mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
Notify master waiter when all replicas are closed.
Fixes #9951 Fixes #9333 PiperOrigin-RevId: 605462571
This commit is contained in:
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user