Notify master waiter when all replicas are closed.

Fixes #9951
Fixes #9333

PiperOrigin-RevId: 605462571
This commit is contained in:
Ayush Ranjan
2024-02-08 16:41:01 -08:00
committed by gVisor bot
parent ca695a9e08
commit cdededb792
2 changed files with 58 additions and 4 deletions
+13 -4
View File
@@ -180,9 +180,14 @@ func (l *lineDiscipline) setWindowSize(t *kernel.Task, args arch.SyscallArgument
}
func (l *lineDiscipline) masterReadiness() waiter.EventMask {
// We don't have to lock a termios because the default master termios
// is immutable.
return l.inQueue.writeReadiness(&linux.MasterTermios) | l.outQueue.readReadiness(&linux.MasterTermios)
// The master termios is immutable so termiosMu is not needed.
res := l.inQueue.writeReadiness(&linux.MasterTermios) | l.outQueue.readReadiness(&linux.MasterTermios)
l.termiosMu.RLock()
if l.numReplicas == 0 {
res |= waiter.EventHUp
}
l.termiosMu.RUnlock()
return res
}
func (l *lineDiscipline) replicaReadiness() waiter.EventMask {
@@ -285,8 +290,12 @@ func (l *lineDiscipline) replicaOpen() {
// replicaClose is called when a replica file descriptor is closed.
func (l *lineDiscipline) replicaClose() {
l.termiosMu.Lock()
defer l.termiosMu.Unlock()
l.numReplicas--
notify := l.numReplicas == 0
l.termiosMu.Unlock()
if notify {
l.masterWaiter.Notify(waiter.EventHUp)
}
}
// transformer is a helper interface to make it easier to stateify queue.
+45
View File
@@ -20,6 +20,8 @@
#include <signal.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/poll.h>
#include <sys/select.h>
#include <sys/stat.h>
#include <sys/sysmacros.h>
#include <sys/types.h>
@@ -660,6 +662,49 @@ class PtyTest : public ::testing::Test {
FileDescriptor replica_;
};
// NOTE(gvisor.dev/issue/9951): Regression test.
TEST_F(PtyTest, ReplicaCloseNotify) {
// Open a second replica.
FileDescriptor replica2_ = ASSERT_NO_ERRNO_AND_VALUE(OpenReplica(master_));
fd_set read_set;
FD_ZERO(&read_set);
FD_SET(master_.get(), &read_set);
int max_fd = master_.get() + 1;
// Set timeout to 0.2 seconds.
struct timeval tv;
tv.tv_sec = 0;
tv.tv_usec = 200000;
// Ensure that there are no readable events.
FD_ZERO(&read_set);
FD_SET(master_.get(), &read_set);
EXPECT_THAT(select(max_fd, &read_set, NULL, NULL, &tv),
SyscallSucceedsWithValue(0));
// Close the second replica and no readable event should occur.
replica2_.reset();
FD_ZERO(&read_set);
FD_SET(master_.get(), &read_set);
EXPECT_THAT(select(max_fd, &read_set, NULL, NULL, &tv),
SyscallSucceedsWithValue(0));
// Close the last remaining replica and a readable event should occur.
replica_.reset();
FD_ZERO(&read_set);
FD_SET(master_.get(), &read_set);
EXPECT_THAT(select(max_fd, &read_set, NULL, NULL, &tv),
SyscallSucceedsWithValue(1));
EXPECT_TRUE(FD_ISSET(master_.get(), &read_set));
// Check that the right events are occurring.
struct pollfd pfd;
pfd.fd = master_.get();
pfd.events = POLLIN | POLLOUT | POLLRDHUP | POLLRDNORM | POLLWRNORM;
EXPECT_THAT(poll(&pfd, 1, 1), SyscallSucceedsWithValue(1));
EXPECT_EQ(POLLHUP | POLLOUT | POLLWRNORM, pfd.revents);
}
// Master to replica sanity test.
TEST_F(PtyTest, WriteMasterToReplica) {
// N.B. by default, the replica reads nothing until the master writes a