mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
Implement fcntl options F_GETSIG and F_SETSIG.
These options allow overriding the signal that gets sent to the process when I/O operations are available on the file descriptor, rather than the default `SIGIO` signal. Doing so also populates `siginfo` to contain extra information about which file descriptor caused the event (`si_fd`) and what events happened on it (`si_band`). The logic around which FD is populated within `si_fd` matches Linux's, which means it has some weird edge cases where that value may not actually refer to a file descriptor that is still valid. This CL also ports extra S/R logic regarding async handler in VFS2. Without this, async I/O handlers aren't properly re-registered after S/R. PiperOrigin-RevId: 345436598
This commit is contained in:
committed by
gVisor bot
parent
80552b936d
commit
6f60a2b0a2
@@ -25,6 +25,8 @@ const (
|
||||
F_SETLKW = 7
|
||||
F_SETOWN = 8
|
||||
F_GETOWN = 9
|
||||
F_SETSIG = 10
|
||||
F_GETSIG = 11
|
||||
F_SETOWN_EX = 15
|
||||
F_GETOWN_EX = 16
|
||||
F_DUPFD_CLOEXEC = 1024 + 6
|
||||
|
||||
@@ -251,3 +251,26 @@ func (s *SignalInfo) Arch() uint32 {
|
||||
func (s *SignalInfo) SetArch(val uint32) {
|
||||
usermem.ByteOrder.PutUint32(s.Fields[12:16], val)
|
||||
}
|
||||
|
||||
// Band returns the si_band field.
|
||||
func (s *SignalInfo) Band() int64 {
|
||||
return int64(usermem.ByteOrder.Uint64(s.Fields[0:8]))
|
||||
}
|
||||
|
||||
// SetBand mutates the si_band field.
|
||||
func (s *SignalInfo) SetBand(val int64) {
|
||||
// Note: this assumes the platform uses `long` as `__ARCH_SI_BAND_T`.
|
||||
// On some platforms, which gVisor doesn't support, `__ARCH_SI_BAND_T` is
|
||||
// `int`. See siginfo.h.
|
||||
usermem.ByteOrder.PutUint64(s.Fields[0:8], uint64(val))
|
||||
}
|
||||
|
||||
// FD returns the si_fd field.
|
||||
func (s *SignalInfo) FD() uint32 {
|
||||
return usermem.ByteOrder.Uint32(s.Fields[8:12])
|
||||
}
|
||||
|
||||
// SetFD mutates the si_fd field.
|
||||
func (s *SignalInfo) SetFD(val uint32) {
|
||||
usermem.ByteOrder.PutUint32(s.Fields[8:12], val)
|
||||
}
|
||||
|
||||
@@ -103,8 +103,8 @@ func (fd *regularFileFD) currentFDLocked(ctx context.Context) (*vfs.FileDescript
|
||||
for e, mask := range fd.lowerWaiters {
|
||||
fd.cachedFD.EventUnregister(e)
|
||||
upperFD.EventRegister(e, mask)
|
||||
if ready&mask != 0 {
|
||||
e.Callback.Callback(e)
|
||||
if m := ready & mask; m != 0 {
|
||||
e.Callback.Callback(e, m)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -273,7 +273,7 @@ func (e *EventPoll) ReadEvents(max int) []linux.EpollEvent {
|
||||
//
|
||||
// Callback is called when one of the files we're polling becomes ready. It
|
||||
// moves said file to the readyList if it's currently in the waiting list.
|
||||
func (p *pollEntry) Callback(*waiter.Entry) {
|
||||
func (p *pollEntry) Callback(*waiter.Entry, waiter.EventMask) {
|
||||
e := p.epoll
|
||||
|
||||
e.listsMu.Lock()
|
||||
@@ -306,9 +306,8 @@ func (e *EventPoll) initEntryReadiness(entry *pollEntry) {
|
||||
f.EventRegister(&entry.waiter, entry.mask)
|
||||
|
||||
// Check if the file happens to already be in a ready state.
|
||||
ready := f.Readiness(entry.mask) & entry.mask
|
||||
if ready != 0 {
|
||||
entry.Callback(&entry.waiter)
|
||||
if ready := f.Readiness(entry.mask) & entry.mask; ready != 0 {
|
||||
entry.Callback(&entry.waiter, ready)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,11 +8,13 @@ go_library(
|
||||
visibility = ["//:sandbox"],
|
||||
deps = [
|
||||
"//pkg/abi/linux",
|
||||
"//pkg/sentry/arch",
|
||||
"//pkg/sentry/fs",
|
||||
"//pkg/sentry/kernel",
|
||||
"//pkg/sentry/kernel/auth",
|
||||
"//pkg/sentry/vfs",
|
||||
"//pkg/sync",
|
||||
"//pkg/syserror",
|
||||
"//pkg/waiter",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -17,22 +17,45 @@ package fasync
|
||||
|
||||
import (
|
||||
"gvisor.dev/gvisor/pkg/abi/linux"
|
||||
"gvisor.dev/gvisor/pkg/sentry/arch"
|
||||
"gvisor.dev/gvisor/pkg/sentry/fs"
|
||||
"gvisor.dev/gvisor/pkg/sentry/kernel"
|
||||
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
|
||||
"gvisor.dev/gvisor/pkg/sentry/vfs"
|
||||
"gvisor.dev/gvisor/pkg/sync"
|
||||
"gvisor.dev/gvisor/pkg/syserror"
|
||||
"gvisor.dev/gvisor/pkg/waiter"
|
||||
)
|
||||
|
||||
// New creates a new fs.FileAsync.
|
||||
func New() fs.FileAsync {
|
||||
return &FileAsync{}
|
||||
// Table to convert waiter event masks into si_band siginfo codes.
|
||||
// Taken from fs/fcntl.c:band_table.
|
||||
var bandTable = map[waiter.EventMask]int64{
|
||||
// POLL_IN
|
||||
waiter.EventIn: linux.EPOLLIN | linux.EPOLLRDNORM,
|
||||
// POLL_OUT
|
||||
waiter.EventOut: linux.EPOLLOUT | linux.EPOLLWRNORM | linux.EPOLLWRBAND,
|
||||
// POLL_ERR
|
||||
waiter.EventErr: linux.EPOLLERR,
|
||||
// POLL_PRI
|
||||
waiter.EventPri: linux.EPOLLPRI | linux.EPOLLRDBAND,
|
||||
// POLL_HUP
|
||||
waiter.EventHUp: linux.EPOLLHUP | linux.EPOLLERR,
|
||||
}
|
||||
|
||||
// NewVFS2 creates a new vfs.FileAsync.
|
||||
func NewVFS2() vfs.FileAsync {
|
||||
return &FileAsync{}
|
||||
// New returns a function that creates a new fs.FileAsync with the given file
|
||||
// descriptor.
|
||||
func New(fd int) func() fs.FileAsync {
|
||||
return func() fs.FileAsync {
|
||||
return &FileAsync{fd: fd}
|
||||
}
|
||||
}
|
||||
|
||||
// NewVFS2 returns a function that creates a new vfs.FileAsync with the given
|
||||
// file descriptor.
|
||||
func NewVFS2(fd int) func() vfs.FileAsync {
|
||||
return func() vfs.FileAsync {
|
||||
return &FileAsync{fd: fd}
|
||||
}
|
||||
}
|
||||
|
||||
// FileAsync sends signals when the registered file is ready for IO.
|
||||
@@ -42,6 +65,12 @@ type FileAsync struct {
|
||||
// e is immutable after first use (which is protected by mu below).
|
||||
e waiter.Entry
|
||||
|
||||
// fd is the file descriptor to notify about.
|
||||
// It is immutable, set at allocation time. This matches Linux semantics in
|
||||
// fs/fcntl.c:fasync_helper.
|
||||
// The fd value is passed to the signal recipient in siginfo.si_fd.
|
||||
fd int
|
||||
|
||||
// regMu protects registeration and unregistration actions on e.
|
||||
//
|
||||
// regMu must be held while registration decisions are being made
|
||||
@@ -56,6 +85,10 @@ type FileAsync struct {
|
||||
mu sync.Mutex `state:"nosave"`
|
||||
requester *auth.Credentials
|
||||
registered bool
|
||||
// signal is the signal to deliver upon I/O being available.
|
||||
// The default value ("zero signal") means the default SIGIO signal will be
|
||||
// delivered.
|
||||
signal linux.Signal
|
||||
|
||||
// Only one of the following is allowed to be non-nil.
|
||||
recipientPG *kernel.ProcessGroup
|
||||
@@ -64,10 +97,10 @@ type FileAsync struct {
|
||||
}
|
||||
|
||||
// Callback sends a signal.
|
||||
func (a *FileAsync) Callback(e *waiter.Entry) {
|
||||
func (a *FileAsync) Callback(e *waiter.Entry, mask waiter.EventMask) {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
if !a.registered {
|
||||
a.mu.Unlock()
|
||||
return
|
||||
}
|
||||
t := a.recipientT
|
||||
@@ -80,19 +113,34 @@ func (a *FileAsync) Callback(e *waiter.Entry) {
|
||||
}
|
||||
if t == nil {
|
||||
// No recipient has been registered.
|
||||
a.mu.Unlock()
|
||||
return
|
||||
}
|
||||
c := t.Credentials()
|
||||
// Logic from sigio_perm in fs/fcntl.c.
|
||||
if a.requester.EffectiveKUID == 0 ||
|
||||
permCheck := (a.requester.EffectiveKUID == 0 ||
|
||||
a.requester.EffectiveKUID == c.SavedKUID ||
|
||||
a.requester.EffectiveKUID == c.RealKUID ||
|
||||
a.requester.RealKUID == c.SavedKUID ||
|
||||
a.requester.RealKUID == c.RealKUID {
|
||||
t.SendSignal(kernel.SignalInfoPriv(linux.SIGIO))
|
||||
a.requester.RealKUID == c.RealKUID)
|
||||
if !permCheck {
|
||||
return
|
||||
}
|
||||
a.mu.Unlock()
|
||||
signalInfo := &arch.SignalInfo{
|
||||
Signo: int32(linux.SIGIO),
|
||||
Code: arch.SignalInfoKernel,
|
||||
}
|
||||
if a.signal != 0 {
|
||||
signalInfo.Signo = int32(a.signal)
|
||||
signalInfo.SetFD(uint32(a.fd))
|
||||
var band int64
|
||||
for m, bandCode := range bandTable {
|
||||
if m&mask != 0 {
|
||||
band |= bandCode
|
||||
}
|
||||
}
|
||||
signalInfo.SetBand(band)
|
||||
}
|
||||
t.SendSignal(signalInfo)
|
||||
}
|
||||
|
||||
// Register sets the file which will be monitored for IO events.
|
||||
@@ -186,3 +234,25 @@ func (a *FileAsync) ClearOwner() {
|
||||
a.recipientTG = nil
|
||||
a.recipientPG = nil
|
||||
}
|
||||
|
||||
// Signal returns which signal will be sent to the signal recipient.
|
||||
// A value of zero means the signal to deliver wasn't customized, which means
|
||||
// the default signal (SIGIO) will be delivered.
|
||||
func (a *FileAsync) Signal() linux.Signal {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
return a.signal
|
||||
}
|
||||
|
||||
// SetSignal overrides which signal to send when I/O is available.
|
||||
// The default behavior can be reset by specifying signal zero, which means
|
||||
// to send SIGIO.
|
||||
func (a *FileAsync) SetSignal(signal linux.Signal) error {
|
||||
if signal != 0 && !signal.IsValid() {
|
||||
return syserror.EINVAL
|
||||
}
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
a.signal = signal
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -646,7 +646,7 @@ func Ioctl(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscall
|
||||
if _, err := primitive.CopyInt32In(t, args[2].Pointer(), &set); err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
fSetOwn(t, file, set)
|
||||
fSetOwn(t, int(fd), file, set)
|
||||
return 0, nil, nil
|
||||
|
||||
case linux.FIOGETOWN, linux.SIOCGPGRP:
|
||||
@@ -901,8 +901,8 @@ func fGetOwn(t *kernel.Task, file *fs.File) int32 {
|
||||
//
|
||||
// If who is positive, it represents a PID. If negative, it represents a PGID.
|
||||
// If the PID or PGID is invalid, the owner is silently unset.
|
||||
func fSetOwn(t *kernel.Task, file *fs.File, who int32) error {
|
||||
a := file.Async(fasync.New).(*fasync.FileAsync)
|
||||
func fSetOwn(t *kernel.Task, fd int, file *fs.File, who int32) error {
|
||||
a := file.Async(fasync.New(fd)).(*fasync.FileAsync)
|
||||
if who < 0 {
|
||||
// Check for overflow before flipping the sign.
|
||||
if who-1 > who {
|
||||
@@ -1049,7 +1049,7 @@ func Fcntl(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscall
|
||||
case linux.F_GETOWN:
|
||||
return uintptr(fGetOwn(t, file)), nil, nil
|
||||
case linux.F_SETOWN:
|
||||
return 0, nil, fSetOwn(t, file, args[2].Int())
|
||||
return 0, nil, fSetOwn(t, int(fd), file, args[2].Int())
|
||||
case linux.F_GETOWN_EX:
|
||||
addr := args[2].Pointer()
|
||||
owner := fGetOwnEx(t, file)
|
||||
@@ -1062,7 +1062,7 @@ func Fcntl(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscall
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
a := file.Async(fasync.New).(*fasync.FileAsync)
|
||||
a := file.Async(fasync.New(int(fd))).(*fasync.FileAsync)
|
||||
switch owner.Type {
|
||||
case linux.F_OWNER_TID:
|
||||
task := t.PIDNamespace().TaskWithID(kernel.ThreadID(owner.PID))
|
||||
@@ -1111,6 +1111,12 @@ func Fcntl(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscall
|
||||
}
|
||||
n, err := sz.SetFifoSize(int64(args[2].Int()))
|
||||
return uintptr(n), nil, err
|
||||
case linux.F_GETSIG:
|
||||
a := file.Async(fasync.New(int(fd))).(*fasync.FileAsync)
|
||||
return uintptr(a.Signal()), nil, nil
|
||||
case linux.F_SETSIG:
|
||||
a := file.Async(fasync.New(int(fd))).(*fasync.FileAsync)
|
||||
return 0, nil, a.SetSignal(linux.Signal(args[2].Int()))
|
||||
default:
|
||||
// Everything else is not yet supported.
|
||||
return 0, nil, syserror.EINVAL
|
||||
|
||||
@@ -165,7 +165,7 @@ func Fcntl(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscall
|
||||
ownerType = linux.F_OWNER_PGRP
|
||||
who = -who
|
||||
}
|
||||
return 0, nil, setAsyncOwner(t, file, ownerType, who)
|
||||
return 0, nil, setAsyncOwner(t, int(fd), file, ownerType, who)
|
||||
case linux.F_GETOWN_EX:
|
||||
owner, hasOwner := getAsyncOwner(t, file)
|
||||
if !hasOwner {
|
||||
@@ -179,7 +179,7 @@ func Fcntl(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscall
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
return 0, nil, setAsyncOwner(t, file, owner.Type, owner.PID)
|
||||
return 0, nil, setAsyncOwner(t, int(fd), file, owner.Type, owner.PID)
|
||||
case linux.F_SETPIPE_SZ:
|
||||
pipefile, ok := file.Impl().(*pipe.VFSPipeFD)
|
||||
if !ok {
|
||||
@@ -207,6 +207,16 @@ func Fcntl(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscall
|
||||
return 0, nil, err
|
||||
case linux.F_SETLK, linux.F_SETLKW:
|
||||
return 0, nil, posixLock(t, args, file, cmd)
|
||||
case linux.F_GETSIG:
|
||||
a := file.AsyncHandler()
|
||||
if a == nil {
|
||||
// Default behavior aka SIGIO.
|
||||
return 0, nil, nil
|
||||
}
|
||||
return uintptr(a.(*fasync.FileAsync).Signal()), nil, nil
|
||||
case linux.F_SETSIG:
|
||||
a := file.SetAsyncHandler(fasync.NewVFS2(int(fd))).(*fasync.FileAsync)
|
||||
return 0, nil, a.SetSignal(linux.Signal(args[2].Int()))
|
||||
default:
|
||||
// Everything else is not yet supported.
|
||||
return 0, nil, syserror.EINVAL
|
||||
@@ -241,7 +251,7 @@ func getAsyncOwner(t *kernel.Task, fd *vfs.FileDescription) (ownerEx linux.FOwne
|
||||
}
|
||||
}
|
||||
|
||||
func setAsyncOwner(t *kernel.Task, fd *vfs.FileDescription, ownerType, pid int32) error {
|
||||
func setAsyncOwner(t *kernel.Task, fd int, file *vfs.FileDescription, ownerType, pid int32) error {
|
||||
switch ownerType {
|
||||
case linux.F_OWNER_TID, linux.F_OWNER_PID, linux.F_OWNER_PGRP:
|
||||
// Acceptable type.
|
||||
@@ -249,7 +259,7 @@ func setAsyncOwner(t *kernel.Task, fd *vfs.FileDescription, ownerType, pid int32
|
||||
return syserror.EINVAL
|
||||
}
|
||||
|
||||
a := fd.SetAsyncHandler(fasync.NewVFS2).(*fasync.FileAsync)
|
||||
a := file.SetAsyncHandler(fasync.NewVFS2(fd)).(*fasync.FileAsync)
|
||||
if pid == 0 {
|
||||
a.ClearOwner()
|
||||
return nil
|
||||
|
||||
@@ -100,7 +100,7 @@ func Ioctl(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscall
|
||||
ownerType = linux.F_OWNER_PGRP
|
||||
who = -who
|
||||
}
|
||||
return 0, nil, setAsyncOwner(t, file, ownerType, who)
|
||||
return 0, nil, setAsyncOwner(t, int(fd), file, ownerType, who)
|
||||
}
|
||||
|
||||
ret, err := file.Ioctl(t, t.MemoryManager(), args)
|
||||
|
||||
@@ -204,8 +204,8 @@ func (ep *EpollInstance) AddInterest(file *FileDescription, num int32, event lin
|
||||
file.EventRegister(&epi.waiter, wmask)
|
||||
|
||||
// Check if the file is already ready.
|
||||
if file.Readiness(wmask)&wmask != 0 {
|
||||
epi.Callback(nil)
|
||||
if m := file.Readiness(wmask) & wmask; m != 0 {
|
||||
epi.Callback(nil, m)
|
||||
}
|
||||
|
||||
// Add epi to file.epolls so that it is removed when the last
|
||||
@@ -274,8 +274,8 @@ func (ep *EpollInstance) ModifyInterest(file *FileDescription, num int32, event
|
||||
file.EventRegister(&epi.waiter, wmask)
|
||||
|
||||
// Check if the file is already ready with the new mask.
|
||||
if file.Readiness(wmask)&wmask != 0 {
|
||||
epi.Callback(nil)
|
||||
if m := file.Readiness(wmask) & wmask; m != 0 {
|
||||
epi.Callback(nil, m)
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -311,7 +311,7 @@ func (ep *EpollInstance) DeleteInterest(file *FileDescription, num int32) error
|
||||
}
|
||||
|
||||
// Callback implements waiter.EntryCallback.Callback.
|
||||
func (epi *epollInterest) Callback(*waiter.Entry) {
|
||||
func (epi *epollInterest) Callback(*waiter.Entry, waiter.EventMask) {
|
||||
newReady := false
|
||||
epi.epoll.mu.Lock()
|
||||
if !epi.ready {
|
||||
|
||||
@@ -43,7 +43,7 @@ import (
|
||||
type FileDescription struct {
|
||||
FileDescriptionRefs
|
||||
|
||||
// flagsMu protects statusFlags and asyncHandler below.
|
||||
// flagsMu protects `statusFlags`, `saved`, and `asyncHandler` below.
|
||||
flagsMu sync.Mutex `state:"nosave"`
|
||||
|
||||
// statusFlags contains status flags, "initialized by open(2) and possibly
|
||||
@@ -52,6 +52,11 @@ type FileDescription struct {
|
||||
// access to asyncHandler.
|
||||
statusFlags uint32
|
||||
|
||||
// saved is true after beforeSave is called. This is used to prevent
|
||||
// double-unregistration of asyncHandler. This does not work properly for
|
||||
// save-resume, which is not currently supported in gVisor (see b/26588733).
|
||||
saved bool `state:"nosave"`
|
||||
|
||||
// asyncHandler handles O_ASYNC signal generation. It is set with the
|
||||
// F_SETOWN or F_SETOWN_EX fcntls. For asyncHandler to be used, O_ASYNC must
|
||||
// also be set by fcntl(2).
|
||||
@@ -184,7 +189,7 @@ func (fd *FileDescription) DecRef(ctx context.Context) {
|
||||
}
|
||||
fd.vd.DecRef(ctx)
|
||||
fd.flagsMu.Lock()
|
||||
if fd.statusFlags&linux.O_ASYNC != 0 && fd.asyncHandler != nil {
|
||||
if !fd.saved && fd.statusFlags&linux.O_ASYNC != 0 && fd.asyncHandler != nil {
|
||||
fd.asyncHandler.Unregister(fd)
|
||||
}
|
||||
fd.asyncHandler = nil
|
||||
|
||||
@@ -18,8 +18,10 @@ import (
|
||||
"fmt"
|
||||
"sync/atomic"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/abi/linux"
|
||||
"gvisor.dev/gvisor/pkg/context"
|
||||
"gvisor.dev/gvisor/pkg/refsvfs2"
|
||||
"gvisor.dev/gvisor/pkg/waiter"
|
||||
)
|
||||
|
||||
// FilesystemImplSaveRestoreExtension is an optional extension to
|
||||
@@ -120,5 +122,20 @@ func (mnt *Mount) afterLoad() {
|
||||
func (epi *epollInterest) afterLoad() {
|
||||
// Mark all epollInterests as ready after restore so that the next call to
|
||||
// EpollInstance.ReadEvents() rechecks their readiness.
|
||||
epi.Callback(nil)
|
||||
epi.Callback(nil, waiter.EventMaskFromLinux(epi.mask))
|
||||
}
|
||||
|
||||
// beforeSave is called by stateify.
|
||||
func (fd *FileDescription) beforeSave() {
|
||||
fd.saved = true
|
||||
if fd.statusFlags&linux.O_ASYNC != 0 && fd.asyncHandler != nil {
|
||||
fd.asyncHandler.Unregister(fd)
|
||||
}
|
||||
}
|
||||
|
||||
// afterLoad is called by stateify.
|
||||
func (fd *FileDescription) afterLoad() {
|
||||
if fd.statusFlags&linux.O_ASYNC != 0 && fd.asyncHandler != nil {
|
||||
fd.asyncHandler.Register(fd)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -119,7 +119,10 @@ type EntryCallback interface {
|
||||
// The callback is supposed to perform minimal work, and cannot call
|
||||
// any method on the queue itself because it will be locked while the
|
||||
// callback is running.
|
||||
Callback(e *Entry)
|
||||
//
|
||||
// The mask indicates the events that occurred and that the entry is
|
||||
// interested in.
|
||||
Callback(e *Entry, mask EventMask)
|
||||
}
|
||||
|
||||
// Entry represents a waiter that can be add to the a wait queue. It can
|
||||
@@ -140,7 +143,7 @@ type channelCallback struct {
|
||||
}
|
||||
|
||||
// Callback implements EntryCallback.Callback.
|
||||
func (c *channelCallback) Callback(*Entry) {
|
||||
func (c *channelCallback) Callback(*Entry, EventMask) {
|
||||
select {
|
||||
case c.ch <- struct{}{}:
|
||||
default:
|
||||
@@ -193,8 +196,8 @@ func (q *Queue) EventUnregister(e *Entry) {
|
||||
func (q *Queue) Notify(mask EventMask) {
|
||||
q.mu.RLock()
|
||||
for e := q.list.Front(); e != nil; e = e.Next() {
|
||||
if mask&e.mask != 0 {
|
||||
e.Callback.Callback(e)
|
||||
if m := mask & e.mask; m != 0 {
|
||||
e.Callback.Callback(e, m)
|
||||
}
|
||||
}
|
||||
q.mu.RUnlock()
|
||||
|
||||
@@ -20,12 +20,12 @@ import (
|
||||
)
|
||||
|
||||
type callbackStub struct {
|
||||
f func(e *Entry)
|
||||
f func(e *Entry, m EventMask)
|
||||
}
|
||||
|
||||
// Callback implements EntryCallback.Callback.
|
||||
func (c *callbackStub) Callback(e *Entry) {
|
||||
c.f(e)
|
||||
func (c *callbackStub) Callback(e *Entry, m EventMask) {
|
||||
c.f(e, m)
|
||||
}
|
||||
|
||||
func TestEmptyQueue(t *testing.T) {
|
||||
@@ -36,7 +36,7 @@ func TestEmptyQueue(t *testing.T) {
|
||||
|
||||
// Register then unregister a waiter, then notify the queue.
|
||||
cnt := 0
|
||||
e := Entry{Callback: &callbackStub{func(*Entry) { cnt++ }}}
|
||||
e := Entry{Callback: &callbackStub{func(*Entry, EventMask) { cnt++ }}}
|
||||
q.EventRegister(&e, EventIn)
|
||||
q.EventUnregister(&e)
|
||||
q.Notify(EventIn)
|
||||
@@ -49,7 +49,7 @@ func TestMask(t *testing.T) {
|
||||
// Register a waiter.
|
||||
var q Queue
|
||||
var cnt int
|
||||
e := Entry{Callback: &callbackStub{func(*Entry) { cnt++ }}}
|
||||
e := Entry{Callback: &callbackStub{func(*Entry, EventMask) { cnt++ }}}
|
||||
q.EventRegister(&e, EventIn|EventErr)
|
||||
|
||||
// Notify with an overlapping mask.
|
||||
@@ -101,11 +101,14 @@ func TestConcurrentRegistration(t *testing.T) {
|
||||
for i := 0; i < concurrency; i++ {
|
||||
go func() {
|
||||
var e Entry
|
||||
e.Callback = &callbackStub{func(entry *Entry) {
|
||||
e.Callback = &callbackStub{func(entry *Entry, mask EventMask) {
|
||||
cnt++
|
||||
if entry != &e {
|
||||
t.Errorf("entry = %p, want %p", entry, &e)
|
||||
}
|
||||
if mask != EventIn {
|
||||
t.Errorf("mask = %#x want %#x", mask, EventIn)
|
||||
}
|
||||
}}
|
||||
|
||||
// Wait for notification, then register.
|
||||
@@ -158,11 +161,14 @@ func TestConcurrentNotification(t *testing.T) {
|
||||
// Register waiters.
|
||||
for i := 0; i < waiterCount; i++ {
|
||||
var e Entry
|
||||
e.Callback = &callbackStub{func(entry *Entry) {
|
||||
e.Callback = &callbackStub{func(entry *Entry, mask EventMask) {
|
||||
atomic.AddInt32(&cnt, 1)
|
||||
if entry != &e {
|
||||
t.Errorf("entry = %p, want %p", entry, &e)
|
||||
}
|
||||
if mask != EventIn {
|
||||
t.Errorf("mask = %#x want %#x", mask, EventIn)
|
||||
}
|
||||
}}
|
||||
|
||||
q.EventRegister(&e, EventIn|EventErr)
|
||||
|
||||
@@ -799,8 +799,8 @@ cc_binary(
|
||||
deps = [
|
||||
":socket_test_util",
|
||||
"//test/util:cleanup",
|
||||
"//test/util:epoll_util",
|
||||
"//test/util:eventfd_util",
|
||||
"//test/util:file_descriptor",
|
||||
"//test/util:fs_util",
|
||||
"@com_google_absl//absl/base:core_headers",
|
||||
"@com_google_absl//absl/flags:flag",
|
||||
@@ -811,6 +811,7 @@ cc_binary(
|
||||
"//test/util:multiprocess_util",
|
||||
"//test/util:posix_error",
|
||||
"//test/util:save_util",
|
||||
"//test/util:signal_util",
|
||||
"//test/util:temp_path",
|
||||
"//test/util:test_util",
|
||||
"//test/util:thread_util",
|
||||
|
||||
+419
-71
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user