github.com/metacubex/gvisor@v0.0.0-20240320004321-933faba989ec/pkg/syncevent/receiver.go (about) 1 // Copyright 2020 The gVisor Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package syncevent 16 17 import ( 18 "github.com/metacubex/gvisor/pkg/atomicbitops" 19 ) 20 21 // Receiver is an event sink that holds pending events and invokes a callback 22 // whenever new events become pending. Receiver's methods may be called 23 // concurrently from multiple goroutines. 24 // 25 // Receiver.Init() must be called before first use. 26 type Receiver struct { 27 // pending is the set of pending events. pending is accessed using atomic 28 // memory operations. 29 pending atomicbitops.Uint64 30 31 // cb is notified when new events become pending. cb is immutable after 32 // Init(). 33 cb ReceiverCallback 34 } 35 36 // ReceiverCallback receives callbacks from a Receiver. 37 type ReceiverCallback interface { 38 // NotifyPending is called when the corresponding Receiver has new pending 39 // events. 40 // 41 // NotifyPending is called synchronously from Receiver.Notify(), so 42 // implementations must not take locks that may be held by callers of 43 // Receiver.Notify(). NotifyPending may be called concurrently from 44 // multiple goroutines. 45 NotifyPending() 46 } 47 48 // Init must be called before first use of r. 49 func (r *Receiver) Init(cb ReceiverCallback) { 50 r.cb = cb 51 } 52 53 // Pending returns the set of pending events. 54 func (r *Receiver) Pending() Set { 55 return Set(r.pending.Load()) 56 } 57 58 // Notify sets the given events as pending. 59 func (r *Receiver) Notify(es Set) { 60 p := Set(r.pending.Load()) 61 // Optimization: Skip the atomic CAS on r.pending if all events are 62 // already pending. 63 if p&es == es { 64 return 65 } 66 // When this is uncontended (the common case), CAS is faster than 67 // atomic-OR because the former is inlined and the latter (which we 68 // implement in assembly ourselves) is not. 69 if !r.pending.CompareAndSwap(uint64(p), uint64(p|es)) { 70 // If the CAS fails, fall back to atomic-OR. 71 atomicbitops.OrUint64(&r.pending, uint64(es)) 72 } 73 r.cb.NotifyPending() 74 } 75 76 // Ack unsets the given events as pending. 77 func (r *Receiver) Ack(es Set) { 78 p := Set(r.pending.Load()) 79 // Optimization: Skip the atomic CAS on r.pending if all events are 80 // already not pending. 81 if p&es == 0 { 82 return 83 } 84 // When this is uncontended (the common case), CAS is faster than 85 // atomic-AND because the former is inlined and the latter (which we 86 // implement in assembly ourselves) is not. 87 if !r.pending.CompareAndSwap(uint64(p), uint64(p&^es)) { 88 // If the CAS fails, fall back to atomic-AND. 89 atomicbitops.AndUint64(&r.pending, ^uint64(es)) 90 } 91 } 92 93 // PendingAndAckAll unsets all events as pending and returns the set of 94 // previously-pending events. 95 // 96 // PendingAndAckAll should only be used in preference to a call to Pending 97 // followed by a conditional call to Ack when the caller expects events to be 98 // pending (e.g. after a call to ReceiverCallback.NotifyPending()). 99 func (r *Receiver) PendingAndAckAll() Set { 100 return Set(r.pending.Swap(0)) 101 }