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  }