snapshot: sagernet/gvisor v0.0.0-20250811.0-sing-box-mod.1
Содержимое пина, зафиксированного в go.mod sing-box-lx, одним коммитом без истории. Полная история SagerNet/gvisor — 1.45 ГБ и клонируется в каждой CI-джобе; наша дельта — одна вставка в одну функцию, история для неё не нужна. Module path github.com/sagernet/gvisor сохранён намеренно: на него опирается replace-директива суперпроекта. Патч поверх — отдельным коммитом, чтобы дельта читалась одним git show и переносилась на новый пин копированием. SPECS/TASKS/048-GVISOR_HANDSHAKE_NIL_CRASH
This commit is contained in:
commit
2c4ae3b0a4
712 changed files with 185689 additions and 0 deletions
220
pkg/syncevent/broadcaster.go
Normal file
220
pkg/syncevent/broadcaster.go
Normal file
|
|
@ -0,0 +1,220 @@
|
|||
// Copyright 2020 The gVisor Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package syncevent
|
||||
|
||||
import (
|
||||
"github.com/sagernet/gvisor/pkg/sync"
|
||||
)
|
||||
|
||||
// Broadcaster is an implementation of Source that supports any number of
|
||||
// subscribed Receivers.
|
||||
//
|
||||
// The zero value of Broadcaster is valid and has no subscribed Receivers.
|
||||
// Broadcaster is not copyable by value.
|
||||
//
|
||||
// All Broadcaster methods may be called concurrently from multiple goroutines.
|
||||
type Broadcaster struct {
|
||||
// Broadcaster is implemented as a hash table where keys are assigned by
|
||||
// the Broadcaster and returned as SubscriptionIDs, making it safe to use
|
||||
// the identity function for hashing. The hash table resolves collisions
|
||||
// using linear probing and features Robin Hood insertion and backward
|
||||
// shift deletion in order to support a relatively high load factor
|
||||
// efficiently, which matters since the cost of Broadcast is linear in the
|
||||
// size of the table.
|
||||
|
||||
// mu protects the following fields.
|
||||
mu sync.Mutex
|
||||
|
||||
// Invariants: len(table) is 0 or a power of 2.
|
||||
table []broadcasterSlot
|
||||
|
||||
// load is the number of entries in table with receiver != nil.
|
||||
load int
|
||||
|
||||
lastID SubscriptionID
|
||||
}
|
||||
|
||||
type broadcasterSlot struct {
|
||||
// Invariants: If receiver == nil, then filter == NoEvents and id == 0.
|
||||
// Otherwise, id != 0.
|
||||
receiver *Receiver
|
||||
filter Set
|
||||
id SubscriptionID
|
||||
}
|
||||
|
||||
const (
|
||||
broadcasterMinNonZeroTableSize = 2 // must be a power of 2 > 1
|
||||
|
||||
broadcasterMaxLoadNum = 13
|
||||
broadcasterMaxLoadDen = 16
|
||||
)
|
||||
|
||||
// SubscribeEvents implements Source.SubscribeEvents.
|
||||
func (b *Broadcaster) SubscribeEvents(r *Receiver, filter Set) SubscriptionID {
|
||||
b.mu.Lock()
|
||||
|
||||
// Assign an ID for this subscription.
|
||||
b.lastID++
|
||||
id := b.lastID
|
||||
|
||||
// Expand the table if over the maximum load factor:
|
||||
//
|
||||
// load / len(b.table) > broadcasterMaxLoadNum / broadcasterMaxLoadDen
|
||||
// load * broadcasterMaxLoadDen > broadcasterMaxLoadNum * len(b.table)
|
||||
b.load++
|
||||
if (b.load * broadcasterMaxLoadDen) > (broadcasterMaxLoadNum * len(b.table)) {
|
||||
// Double the number of slots in the new table.
|
||||
newlen := broadcasterMinNonZeroTableSize
|
||||
if len(b.table) != 0 {
|
||||
newlen = 2 * len(b.table)
|
||||
}
|
||||
if newlen <= cap(b.table) {
|
||||
// Reuse excess capacity in the current table, moving entries not
|
||||
// already in their first-probed positions to better ones.
|
||||
newtable := b.table[:newlen]
|
||||
newmask := uint64(newlen - 1)
|
||||
for i := range b.table {
|
||||
if b.table[i].receiver != nil && uint64(b.table[i].id)&newmask != uint64(i) {
|
||||
entry := b.table[i]
|
||||
b.table[i] = broadcasterSlot{}
|
||||
broadcasterTableInsert(newtable, entry.id, entry.receiver, entry.filter)
|
||||
}
|
||||
}
|
||||
b.table = newtable
|
||||
} else {
|
||||
newtable := make([]broadcasterSlot, newlen)
|
||||
// Copy existing entries to the new table.
|
||||
for i := range b.table {
|
||||
if b.table[i].receiver != nil {
|
||||
broadcasterTableInsert(newtable, b.table[i].id, b.table[i].receiver, b.table[i].filter)
|
||||
}
|
||||
}
|
||||
// Switch to the new table.
|
||||
b.table = newtable
|
||||
}
|
||||
}
|
||||
|
||||
broadcasterTableInsert(b.table, id, r, filter)
|
||||
b.mu.Unlock()
|
||||
return id
|
||||
}
|
||||
|
||||
// Preconditions:
|
||||
// - table must not be full.
|
||||
// - len(table) is a power of 2.
|
||||
func broadcasterTableInsert(table []broadcasterSlot, id SubscriptionID, r *Receiver, filter Set) {
|
||||
entry := broadcasterSlot{
|
||||
receiver: r,
|
||||
filter: filter,
|
||||
id: id,
|
||||
}
|
||||
mask := uint64(len(table) - 1)
|
||||
i := uint64(id) & mask
|
||||
disp := uint64(0)
|
||||
for {
|
||||
if table[i].receiver == nil {
|
||||
table[i] = entry
|
||||
return
|
||||
}
|
||||
// If we've been displaced farther from our first-probed slot than the
|
||||
// element stored in this one, swap elements and switch to inserting
|
||||
// the replaced one. (This is Robin Hood insertion.)
|
||||
slotDisp := (i - uint64(table[i].id)) & mask
|
||||
if disp > slotDisp {
|
||||
table[i], entry = entry, table[i]
|
||||
disp = slotDisp
|
||||
}
|
||||
i = (i + 1) & mask
|
||||
disp++
|
||||
}
|
||||
}
|
||||
|
||||
// UnsubscribeEvents implements Source.UnsubscribeEvents.
|
||||
func (b *Broadcaster) UnsubscribeEvents(id SubscriptionID) {
|
||||
b.mu.Lock()
|
||||
|
||||
mask := uint64(len(b.table) - 1)
|
||||
i := uint64(id) & mask
|
||||
for {
|
||||
if b.table[i].id == id {
|
||||
// Found the element to remove. Move all subsequent elements
|
||||
// backward until we either find an empty slot, or an element that
|
||||
// is already in its first-probed slot. (This is backward shift
|
||||
// deletion.)
|
||||
for {
|
||||
next := (i + 1) & mask
|
||||
if b.table[next].receiver == nil {
|
||||
break
|
||||
}
|
||||
if uint64(b.table[next].id)&mask == next {
|
||||
break
|
||||
}
|
||||
b.table[i] = b.table[next]
|
||||
i = next
|
||||
}
|
||||
b.table[i] = broadcasterSlot{}
|
||||
break
|
||||
}
|
||||
i = (i + 1) & mask
|
||||
}
|
||||
|
||||
// If a table 1/4 of the current size would still be at or under the
|
||||
// maximum load factor (i.e. the current table size is at least two
|
||||
// expansions bigger than necessary), halve the size of the table to reduce
|
||||
// the cost of Broadcast. Since we are concerned with iteration time and
|
||||
// not memory usage, reuse the existing slice to reduce future allocations
|
||||
// from table re-expansion.
|
||||
b.load--
|
||||
if len(b.table) > broadcasterMinNonZeroTableSize && (b.load*(4*broadcasterMaxLoadDen)) <= (broadcasterMaxLoadNum*len(b.table)) {
|
||||
newlen := len(b.table) / 2
|
||||
newtable := b.table[:newlen]
|
||||
for i := newlen; i < len(b.table); i++ {
|
||||
if b.table[i].receiver != nil {
|
||||
broadcasterTableInsert(newtable, b.table[i].id, b.table[i].receiver, b.table[i].filter)
|
||||
b.table[i] = broadcasterSlot{}
|
||||
}
|
||||
}
|
||||
b.table = newtable
|
||||
}
|
||||
|
||||
b.mu.Unlock()
|
||||
}
|
||||
|
||||
// Broadcast notifies all Receivers subscribed to the Broadcaster of the subset
|
||||
// of events to which they subscribed. The order in which Receivers are
|
||||
// notified is unspecified.
|
||||
func (b *Broadcaster) Broadcast(events Set) {
|
||||
b.mu.Lock()
|
||||
for i := range b.table {
|
||||
if intersection := events & b.table[i].filter; intersection != 0 {
|
||||
// We don't need to check if broadcasterSlot.receiver is nil, since
|
||||
// if it is then broadcasterSlot.filter is 0.
|
||||
b.table[i].receiver.Notify(intersection)
|
||||
}
|
||||
}
|
||||
b.mu.Unlock()
|
||||
}
|
||||
|
||||
// FilteredEvents returns the set of events for which Broadcast will notify at
|
||||
// least one Receiver, i.e. the union of filters for all subscribed Receivers.
|
||||
func (b *Broadcaster) FilteredEvents() Set {
|
||||
var es Set
|
||||
b.mu.Lock()
|
||||
for i := range b.table {
|
||||
es |= b.table[i].filter
|
||||
}
|
||||
b.mu.Unlock()
|
||||
return es
|
||||
}
|
||||
101
pkg/syncevent/receiver.go
Normal file
101
pkg/syncevent/receiver.go
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
// Copyright 2020 The gVisor Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package syncevent
|
||||
|
||||
import (
|
||||
"github.com/sagernet/gvisor/pkg/atomicbitops"
|
||||
)
|
||||
|
||||
// Receiver is an event sink that holds pending events and invokes a callback
|
||||
// whenever new events become pending. Receiver's methods may be called
|
||||
// concurrently from multiple goroutines.
|
||||
//
|
||||
// Receiver.Init() must be called before first use.
|
||||
type Receiver struct {
|
||||
// pending is the set of pending events. pending is accessed using atomic
|
||||
// memory operations.
|
||||
pending atomicbitops.Uint64
|
||||
|
||||
// cb is notified when new events become pending. cb is immutable after
|
||||
// Init().
|
||||
cb ReceiverCallback
|
||||
}
|
||||
|
||||
// ReceiverCallback receives callbacks from a Receiver.
|
||||
type ReceiverCallback interface {
|
||||
// NotifyPending is called when the corresponding Receiver has new pending
|
||||
// events.
|
||||
//
|
||||
// NotifyPending is called synchronously from Receiver.Notify(), so
|
||||
// implementations must not take locks that may be held by callers of
|
||||
// Receiver.Notify(). NotifyPending may be called concurrently from
|
||||
// multiple goroutines.
|
||||
NotifyPending()
|
||||
}
|
||||
|
||||
// Init must be called before first use of r.
|
||||
func (r *Receiver) Init(cb ReceiverCallback) {
|
||||
r.cb = cb
|
||||
}
|
||||
|
||||
// Pending returns the set of pending events.
|
||||
func (r *Receiver) Pending() Set {
|
||||
return Set(r.pending.Load())
|
||||
}
|
||||
|
||||
// Notify sets the given events as pending.
|
||||
func (r *Receiver) Notify(es Set) {
|
||||
p := Set(r.pending.Load())
|
||||
// Optimization: Skip the atomic CAS on r.pending if all events are
|
||||
// already pending.
|
||||
if p&es == es {
|
||||
return
|
||||
}
|
||||
// When this is uncontended (the common case), CAS is faster than
|
||||
// atomic-OR because the former is inlined and the latter (which we
|
||||
// implement in assembly ourselves) is not.
|
||||
if !r.pending.CompareAndSwap(uint64(p), uint64(p|es)) {
|
||||
// If the CAS fails, fall back to atomic-OR.
|
||||
atomicbitops.OrUint64(&r.pending, uint64(es))
|
||||
}
|
||||
r.cb.NotifyPending()
|
||||
}
|
||||
|
||||
// Ack unsets the given events as pending.
|
||||
func (r *Receiver) Ack(es Set) {
|
||||
p := Set(r.pending.Load())
|
||||
// Optimization: Skip the atomic CAS on r.pending if all events are
|
||||
// already not pending.
|
||||
if p&es == 0 {
|
||||
return
|
||||
}
|
||||
// When this is uncontended (the common case), CAS is faster than
|
||||
// atomic-AND because the former is inlined and the latter (which we
|
||||
// implement in assembly ourselves) is not.
|
||||
if !r.pending.CompareAndSwap(uint64(p), uint64(p&^es)) {
|
||||
// If the CAS fails, fall back to atomic-AND.
|
||||
atomicbitops.AndUint64(&r.pending, ^uint64(es))
|
||||
}
|
||||
}
|
||||
|
||||
// PendingAndAckAll unsets all events as pending and returns the set of
|
||||
// previously-pending events.
|
||||
//
|
||||
// PendingAndAckAll should only be used in preference to a call to Pending
|
||||
// followed by a conditional call to Ack when the caller expects events to be
|
||||
// pending (e.g. after a call to ReceiverCallback.NotifyPending()).
|
||||
func (r *Receiver) PendingAndAckAll() Set {
|
||||
return Set(r.pending.Swap(0))
|
||||
}
|
||||
61
pkg/syncevent/source.go
Normal file
61
pkg/syncevent/source.go
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
// Copyright 2020 The gVisor Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package syncevent
|
||||
|
||||
// Source represents an event source.
|
||||
type Source interface {
|
||||
// SubscribeEvents causes the Source to notify the given Receiver of the
|
||||
// given subset of events.
|
||||
//
|
||||
// Preconditions:
|
||||
// * r != nil.
|
||||
// * The ReceiverCallback for r must not take locks that are ordered
|
||||
// prior to the Source; for example, it cannot call any Source
|
||||
// methods.
|
||||
SubscribeEvents(r *Receiver, filter Set) SubscriptionID
|
||||
|
||||
// UnsubscribeEvents causes the Source to stop notifying the Receiver
|
||||
// subscribed by a previous call to SubscribeEvents that returned the given
|
||||
// SubscriptionID.
|
||||
//
|
||||
// Preconditions: UnsubscribeEvents may be called at most once for any
|
||||
// given SubscriptionID.
|
||||
UnsubscribeEvents(id SubscriptionID)
|
||||
}
|
||||
|
||||
// SubscriptionID identifies a call to Source.SubscribeEvents.
|
||||
type SubscriptionID uint64
|
||||
|
||||
// UnsubscribeAndAck is a convenience function that unsubscribes r from the
|
||||
// given events from src and also clears them from r.
|
||||
func UnsubscribeAndAck(src Source, r *Receiver, filter Set, id SubscriptionID) {
|
||||
src.UnsubscribeEvents(id)
|
||||
r.Ack(filter)
|
||||
}
|
||||
|
||||
// NoopSource implements Source by never sending events to subscribed
|
||||
// Receivers.
|
||||
type NoopSource struct{}
|
||||
|
||||
// SubscribeEvents implements Source.SubscribeEvents.
|
||||
func (NoopSource) SubscribeEvents(*Receiver, Set) SubscriptionID {
|
||||
return 0
|
||||
}
|
||||
|
||||
// UnsubscribeEvents implements Source.UnsubscribeEvents.
|
||||
func (NoopSource) UnsubscribeEvents(SubscriptionID) {
|
||||
}
|
||||
|
||||
// See Broadcaster for a non-noop implementations of Source.
|
||||
32
pkg/syncevent/syncevent.go
Normal file
32
pkg/syncevent/syncevent.go
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
// Copyright 2020 The gVisor Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Package syncevent provides efficient primitives for goroutine
|
||||
// synchronization based on event bitmasks.
|
||||
package syncevent
|
||||
|
||||
// Set is a bitmask where each bit represents a distinct user-defined event.
|
||||
// The event package does not treat any bits in Set specially.
|
||||
type Set uint64
|
||||
|
||||
const (
|
||||
// NoEvents is a Set containing no events.
|
||||
NoEvents = Set(0)
|
||||
|
||||
// AllEvents is a Set containing all possible events.
|
||||
AllEvents = ^Set(0)
|
||||
|
||||
// MaxEvents is the number of distinct events that can be represented by a Set.
|
||||
MaxEvents = 64
|
||||
)
|
||||
3
pkg/syncevent/syncevent_state_autogen.go
Normal file
3
pkg/syncevent/syncevent_state_autogen.go
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
// automatically generated by stateify.
|
||||
|
||||
package syncevent
|
||||
3
pkg/syncevent/syncevent_unsafe_state_autogen.go
Normal file
3
pkg/syncevent/syncevent_unsafe_state_autogen.go
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
// automatically generated by stateify.
|
||||
|
||||
package syncevent
|
||||
197
pkg/syncevent/waiter_unsafe.go
Normal file
197
pkg/syncevent/waiter_unsafe.go
Normal file
|
|
@ -0,0 +1,197 @@
|
|||
// Copyright 2020 The gVisor Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package syncevent
|
||||
|
||||
import (
|
||||
"sync/atomic"
|
||||
"unsafe"
|
||||
|
||||
"github.com/sagernet/gvisor/pkg/sync"
|
||||
)
|
||||
|
||||
// Waiter allows a goroutine to block on pending events received by a Receiver.
|
||||
//
|
||||
// Waiter.Init() must be called before first use.
|
||||
type Waiter struct {
|
||||
r Receiver
|
||||
|
||||
// g is one of:
|
||||
//
|
||||
// - 0: No goroutine is blocking in Wait.
|
||||
//
|
||||
// - preparingG: A goroutine is in Wait preparing to sleep, but hasn't yet
|
||||
// completed waiterUnlock(). Thus the wait can only be interrupted by
|
||||
// replacing the value of g with 0 (the G may not be in state Gwaiting yet,
|
||||
// so we can't call goready.)
|
||||
//
|
||||
// - Otherwise: g is a pointer to the runtime.g in state Gwaiting for the
|
||||
// goroutine blocked in Wait, which can only be woken by calling goready.
|
||||
g uintptr `state:"zerovalue"`
|
||||
}
|
||||
|
||||
const preparingG = 1
|
||||
|
||||
// Init must be called before first use of w.
|
||||
func (w *Waiter) Init() {
|
||||
w.r.Init(w)
|
||||
}
|
||||
|
||||
// Receiver returns the Receiver that receives events that unblock calls to
|
||||
// w.Wait().
|
||||
func (w *Waiter) Receiver() *Receiver {
|
||||
return &w.r
|
||||
}
|
||||
|
||||
// Pending returns the set of pending events.
|
||||
func (w *Waiter) Pending() Set {
|
||||
return w.r.Pending()
|
||||
}
|
||||
|
||||
// Wait blocks until at least one event is pending, then returns the set of
|
||||
// pending events. It does not affect the set of pending events; callers must
|
||||
// call w.Ack() to do so, or use w.WaitAndAck() instead.
|
||||
//
|
||||
// Precondition: Only one goroutine may call any Wait* method at a time.
|
||||
func (w *Waiter) Wait() Set {
|
||||
return w.WaitFor(AllEvents)
|
||||
}
|
||||
|
||||
// WaitFor blocks until at least one event in es is pending, then returns the
|
||||
// set of pending events (including those not in es). It does not affect the
|
||||
// set of pending events; callers must call w.Ack() to do so.
|
||||
//
|
||||
// Precondition: Only one goroutine may call any Wait* method at a time.
|
||||
func (w *Waiter) WaitFor(es Set) Set {
|
||||
for {
|
||||
// Optimization: Skip the atomic store to w.g if an event is already
|
||||
// pending.
|
||||
if p := w.r.Pending(); p&es != NoEvents {
|
||||
return p
|
||||
}
|
||||
|
||||
// Indicate that we're preparing to go to sleep.
|
||||
atomic.StoreUintptr(&w.g, preparingG)
|
||||
|
||||
// If an event is pending, abort the sleep.
|
||||
if p := w.r.Pending(); p&es != NoEvents {
|
||||
atomic.StoreUintptr(&w.g, 0)
|
||||
return p
|
||||
}
|
||||
|
||||
// If w.g is still preparingG (i.e. w.NotifyPending() has not been
|
||||
// called or has not reached atomic.SwapUintptr()), go to sleep until
|
||||
// w.NotifyPending() => goready().
|
||||
sync.Gopark(waiterCommit, unsafe.Pointer(&w.g), sync.WaitReasonSelect, sync.TraceBlockSelect, 0)
|
||||
}
|
||||
}
|
||||
|
||||
//go:norace
|
||||
//go:nosplit
|
||||
func waiterCommit(g uintptr, wg unsafe.Pointer) bool {
|
||||
// The only way this CAS can fail is if a call to Waiter.NotifyPending()
|
||||
// has replaced *wg with nil, in which case we should not sleep.
|
||||
return sync.RaceUncheckedAtomicCompareAndSwapUintptr((*uintptr)(wg), preparingG, g)
|
||||
}
|
||||
|
||||
// Ack marks the given events as not pending.
|
||||
func (w *Waiter) Ack(es Set) {
|
||||
w.r.Ack(es)
|
||||
}
|
||||
|
||||
// WaitAndAckAll blocks until at least one event is pending, then marks all
|
||||
// events as not pending and returns the set of previously-pending events.
|
||||
//
|
||||
// Precondition: Only one goroutine may call any Wait* method at a time.
|
||||
func (w *Waiter) WaitAndAckAll() Set {
|
||||
// Optimization: Skip the atomic store to w.g if an event is already
|
||||
// pending. Call Pending() first since, in the common case that events are
|
||||
// not yet pending, this skips an atomic swap on w.r.pending.
|
||||
if w.r.Pending() != NoEvents {
|
||||
if p := w.r.PendingAndAckAll(); p != NoEvents {
|
||||
return p
|
||||
}
|
||||
}
|
||||
|
||||
for {
|
||||
// Indicate that we're preparing to go to sleep.
|
||||
atomic.StoreUintptr(&w.g, preparingG)
|
||||
|
||||
// If an event is pending, abort the sleep.
|
||||
if w.r.Pending() != NoEvents {
|
||||
if p := w.r.PendingAndAckAll(); p != NoEvents {
|
||||
atomic.StoreUintptr(&w.g, 0)
|
||||
return p
|
||||
}
|
||||
}
|
||||
|
||||
// If w.g is still preparingG (i.e. w.NotifyPending() has not been
|
||||
// called or has not reached atomic.SwapUintptr()), go to sleep until
|
||||
// w.NotifyPending() => goready().
|
||||
sync.Gopark(waiterCommit, unsafe.Pointer(&w.g), sync.WaitReasonSelect, sync.TraceBlockSelect, 0)
|
||||
|
||||
// Check for pending events. We call PendingAndAckAll() directly now since
|
||||
// we only expect to be woken after events become pending.
|
||||
if p := w.r.PendingAndAckAll(); p != NoEvents {
|
||||
return p
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Notify marks the given events as pending, possibly unblocking concurrent
|
||||
// calls to w.Wait() or w.WaitFor().
|
||||
func (w *Waiter) Notify(es Set) {
|
||||
w.r.Notify(es)
|
||||
}
|
||||
|
||||
// NotifyPending implements ReceiverCallback.NotifyPending. Users of Waiter
|
||||
// should not call NotifyPending.
|
||||
func (w *Waiter) NotifyPending() {
|
||||
// Optimization: Skip the atomic swap on w.g if there is no sleeping
|
||||
// goroutine. NotifyPending is called after w.r.Pending() is updated, so
|
||||
// concurrent and future calls to w.Wait() will observe pending events and
|
||||
// abort sleeping.
|
||||
if atomic.LoadUintptr(&w.g) == 0 {
|
||||
return
|
||||
}
|
||||
// Wake a sleeping G, or prevent a G that is preparing to sleep from doing
|
||||
// so. Swap is needed here to ensure that only one call to NotifyPending
|
||||
// calls goready.
|
||||
if g := atomic.SwapUintptr(&w.g, 0); g > preparingG {
|
||||
sync.Goready(g, 0, true /* wakep */)
|
||||
}
|
||||
}
|
||||
|
||||
var waiterPool = sync.Pool{
|
||||
New: func() any {
|
||||
w := &Waiter{}
|
||||
w.Init()
|
||||
return w
|
||||
},
|
||||
}
|
||||
|
||||
// GetWaiter returns an unused Waiter. PutWaiter should be called to release
|
||||
// the Waiter once it is no longer needed.
|
||||
//
|
||||
// Where possible, users should prefer to associate each goroutine that calls
|
||||
// Waiter.Wait() with a distinct pre-allocated Waiter to avoid allocation of
|
||||
// Waiters in hot paths.
|
||||
func GetWaiter() *Waiter {
|
||||
return waiterPool.Get().(*Waiter)
|
||||
}
|
||||
|
||||
// PutWaiter releases an unused Waiter previously returned by GetWaiter.
|
||||
func PutWaiter(w *Waiter) {
|
||||
waiterPool.Put(w)
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue