github.com/haraldrudell/parl@v0.4.176/nb-chan-send.go (about) 1 /* 2 © 2023–present Harald Rudell <harald.rudell@gmail.com> (https://haraldrudell.github.io/haraldrudell/) 3 ISC License 4 */ 5 6 package parl 7 8 import "math" 9 10 // Send sends a single value on the channel 11 // - non-blocking, thread-safe, panic-free and error-free 12 // - if Close or CloseNow was invoked, items are discarded 13 func (n *NBChan[T]) Send(value T) { 14 if n.isCloseInvoked.IsInvoked() { 15 return // no send after Close(), atomic performance: noop 16 } 17 n.preSend() 18 n.inputLock.Lock() 19 defer n.postSend() 20 21 // if Close or CloseNow was invoked, items are discarded 22 if n.isCloseInvoked.IsInvoked() { 23 return // no send after Close() return: noop 24 } 25 26 // try providing value to thread 27 // - ensures a thread is running if configured 28 // - updates threadProgressRequired 29 if n.tcAlertOrLaunchThreadWithValue(value) { 30 return // value was provided to a thread 31 } 32 33 // save value in [NBChan.inputQueue] 34 if n.inputQueue == nil { 35 n.inputQueue = n.newQueue(1) // will allocation proper size 36 } 37 n.inputQueue = append(n.inputQueue, value) 38 n.inputCapacity.Store(uint64(cap(n.inputQueue))) 39 n.tcAddProgress(1) 40 } 41 42 // Send sends many values non-blocking, thread-safe, panic-free and error-free on the channel 43 // - if values is length 0 or nil, SendMany only returns count and capacity 44 func (n *NBChan[T]) SendMany(values []T) { 45 var valueCount = len(values) 46 if n.isCloseInvoked.IsInvoked() || valueCount == 0 { 47 return // no send after Close(), atomic performance: noop 48 } 49 n.preSend() 50 n.inputLock.Lock() 51 defer n.postSend() 52 53 if n.isCloseInvoked.IsInvoked() { 54 return // no send after Close() return: noop 55 } 56 57 if n.tcAlertOrLaunchThreadWithValue(values[0]) { 58 values = values[1:] 59 valueCount-- 60 if valueCount == 0 { 61 return // one value handed to thread return: complete 62 } 63 } 64 65 // save values in [NBChan.inputQueue] 66 if n.inputQueue == nil { 67 n.inputQueue = n.newQueue(valueCount) 68 } 69 n.inputQueue = append(n.inputQueue, values...) 70 n.inputCapacity.Store(uint64(cap(n.inputQueue))) 71 n.tcAddProgress(valueCount) 72 } 73 74 // preSend registers a Send or SendMany invocation pre-inputLock 75 // - send count is in [NBChan.sends] 76 // - handles [NBChan.sendsWait] that prevents a thread from exiting 77 // during Send SendMany invocations 78 func (n *NBChan[T]) preSend() { 79 if n.sends.Add(1) == 1 { 80 n.sendsWait.HoldWaiters() 81 } 82 } 83 84 // post send is deferred for [NBChan.Send] and [NBChan.SendMany] 85 // - release inputLock 86 // - alert thread if no pending Get and values ar present 87 func (n *NBChan[T]) postSend() { 88 n.inputLock.Unlock() 89 90 // update dataWaitCh 91 n.updateDataAvailable() 92 93 // decrement sends 94 if n.sends.Add(math.MaxUint64) == 0 { 95 n.sendsWait.ReleaseWaiters() 96 } 97 98 // ensure progress 99 for { 100 if isZeroObserved, isGets := n.tcIsDeferredSend(); !isZeroObserved || isGets { 101 // progress not required or 102 // deferred by Get invocations 103 return 104 } else if !n.tcAwaitProgress() { 105 // progress was secured 106 return 107 } else if n.gets.Load() > 0 { 108 // subsequent Send SendMany exist 109 // - after sends decrement, those will arrive at ensure progress 110 return 111 } 112 } 113 } 114 115 // ensureInput allocates or enlarges for [NBChan.SetAllocationSize] 116 func (n *NBChan[T]) ensureInput(size int) (queue []T) { 117 n.inputLock.Lock() 118 defer n.inputLock.Unlock() 119 120 if n.inputQueue != nil { 121 return 122 } 123 n.inputQueue = n.newQueue(size) 124 return 125 } 126 127 // newQueue allocates a new queue slice 128 // - capacity is at least count elements 129 // - the slice is empty 130 func (n *NBChan[T]) newQueue(count int) (queue []T) { 131 132 // determine size 133 var size = int(n.allocationSize.Load()) 134 if size > 0 { 135 if count > size { 136 size = count 137 } 138 } else { 139 size = defaultNBChanSize 140 if count > size { 141 size = count * 2 142 } 143 } 144 145 // return allocated zero-length queue 146 return make([]T, size)[:0] 147 }