epoll: notify waiters if ReadEvents adds events

ReadEvents is racy with concurrent notifications and it has a code path that
can add events to the ready queue. In such cases, it has to notify waiters.

Users of ReadEvents() registers for events and then call ReadEvents. They
expect that if ReadEvents returns nothing, they will get a notification when
there will be something to read.

PiperOrigin-RevId: 438449859
This commit is contained in:
Andrei Vagin
2022-03-30 18:46:51 -07:00
committed by gVisor bot
parent 5d9d852f37
commit fcd82c8ebf
2 changed files with 20 additions and 0 deletions
+10
View File
@@ -212,6 +212,7 @@ func (e *EventPoll) eventsAvailable() bool {
return false
}
defer func() {
notify := true
e.listsMu.Lock()
e.readyList.PushFrontList(&readyList)
var next *pollEntry
@@ -221,12 +222,16 @@ func (e *EventPoll) eventsAvailable() bool {
// entry.NotifyEvent() was called while we were running.
waitingList.Remove(entry)
e.readyList.PushBack(entry)
notify = true
} else {
entry.curList = &e.waitingList
}
}
e.waitingList.PushBackList(&waitingList)
e.listsMu.Unlock()
if notify {
e.Notify(waiter.ReadableEvents)
}
}()
for it := readyList.Front(); it != nil; {
@@ -287,6 +292,7 @@ func (e *EventPoll) ReadEvents(max int) []linux.EpollEvent {
return nil
}
defer func() {
notify := false
e.listsMu.Lock()
e.readyList.PushFrontList(&readyList)
var next *pollEntry
@@ -296,6 +302,7 @@ func (e *EventPoll) ReadEvents(max int) []linux.EpollEvent {
// entry.NotifyEvent() was called while we were running.
waitingList.Remove(entry)
e.readyList.PushBack(entry)
notify = true
} else {
entry.curList = &e.waitingList
}
@@ -307,6 +314,9 @@ func (e *EventPoll) ReadEvents(max int) []linux.EpollEvent {
}
e.disabledList.PushBackList(&disabledList)
e.listsMu.Unlock()
if notify {
e.Notify(waiter.ReadableEvents)
}
}()
// Go through all entries we believe may be ready.
+10
View File
@@ -170,6 +170,7 @@ func (ep *EpollInstance) Readiness(mask waiter.EventMask) waiter.EventMask {
return 0
}
defer func() {
notify := false
ep.readyMu.Lock()
ep.ready.PushFrontList(&ready)
var next *epollInterest
@@ -179,11 +180,15 @@ func (ep *EpollInstance) Readiness(mask waiter.EventMask) waiter.EventMask {
// epi.NotifyEvent() was called while we were running.
notReady.Remove(epi)
ep.ready.PushBack(epi)
notify = true
} else {
epi.ready = false
}
}
ep.readyMu.Unlock()
if notify {
ep.q.Notify(waiter.ReadableEvents)
}
}()
var next *epollInterest
@@ -428,6 +433,7 @@ func (ep *EpollInstance) ReadEvents(events []linux.EpollEvent, maxEvents int) []
return nil
}
defer func() {
notify := false
ep.readyMu.Lock()
// epollInterests that we never checked are re-inserted at the start of
// ep.ready. epollInterests that were ready are re-inserted at the end
@@ -440,12 +446,16 @@ func (ep *EpollInstance) ReadEvents(events []linux.EpollEvent, maxEvents int) []
// epi.NotifyEvent() was called while we were running.
notReady.Remove(epi)
ep.ready.PushBack(epi)
notify = true
} else {
epi.ready = false
}
}
ep.ready.PushBackList(&requeue)
ep.readyMu.Unlock()
if notify {
ep.q.Notify(waiter.ReadableEvents)
}
}()
i := 0