github.com/alphadose/zenq/v2@v2.8.4/selector.go (about) 1 package zenq 2 3 import ( 4 "sync/atomic" 5 "unsafe" 6 ) 7 8 // Selectable is an interface for getting selected among many others 9 type Selectable interface { 10 IsClosed() bool 11 EnqueueSelector(*unsafe.Pointer, *any) 12 ReadFromBackLog() (data any) 13 Signal() uint8 14 } 15 16 // Select selects a single element out of multiple ZenQs 17 // A maximum of 127 ZenQs can be selected from at a time owing to the size of int8 type 18 // `nil` is returned if all streams are closed or if a stream gets closed during the selection process 19 func Select(streams ...Selectable) (data any) { 20 numStreams := int8(len(streams) - 1) 21 filter: 22 for idx := int8(0); idx < numStreams; idx++ { 23 if streams[idx] == nil || streams[idx].IsClosed() { 24 for ; numStreams >= 0 && (streams[numStreams] == nil || streams[numStreams].IsClosed()); numStreams-- { 25 } 26 if idx >= numStreams { 27 break filter 28 } 29 streams[idx], streams[numStreams] = streams[numStreams], streams[idx] 30 numStreams-- 31 } 32 } 33 if numStreams < 0 { 34 data = nil 35 return 36 } 37 38 for idx := int8(0); idx <= numStreams; idx++ { 39 if data = streams[idx].ReadFromBackLog(); data != nil { 40 return 41 } 42 } 43 44 g, numSignals, iter := GetG(), uint8(0), int8(0) 45 46 for idx := int8(0); idx <= numStreams; idx++ { 47 streams[idx].EnqueueSelector(&g, &data) 48 } 49 50 retry: 51 for idx := int8(0); idx <= numStreams; idx++ { 52 numSignals += streams[idx].Signal() 53 } 54 55 // might cause deadlock without this case 56 if numSignals == 0 && atomic.LoadPointer(&g) != nil { 57 // wait for some ZenQ to acquire this selector's thread 58 if runtime_canSpin(int(iter)) { 59 iter++ 60 spin(30) 61 } else { 62 mcall(gosched_m) 63 } 64 goto retry 65 } 66 67 // park and wait for notification 68 mcall(fast_park) 69 return 70 }