github.com/sandwich-go/boost@v1.3.29/xchan/unbounded_chan.go (about) 1 package xchan 2 3 import ( 4 "context" 5 "sync/atomic" 6 ) 7 8 // UnboundedChan is an unbounded chan. 9 // In is used to write without blocking, which supports multiple writers. 10 // and Out is used to read, which supports multiple readers. 11 // You can close the in channel if you want. 12 type UnboundedChan[T any] struct { 13 bufCount int64 14 In chan<- T // channel for write 15 Out <-chan T // channel for read 16 buffer *RingBuffer[T] // buffer 17 cc *Options 18 } 19 20 // Len 所有待读取的数据的长度 21 func (c UnboundedChan[T]) Len() int { 22 return len(c.In) + c.BufLen() + len(c.Out) 23 } 24 25 // BufLen 获取缓存中的数据的长度,不包含外发Out channel中数据的长度 26 func (c UnboundedChan[T]) BufLen() int { 27 return int(atomic.LoadInt64(&c.bufCount)) 28 } 29 30 // NewUnboundedChan creates the unbounded chan. 31 // in is used to write without blocking, which supports multiple writers. 32 // and out is used to read, which supports multiple readers. 33 // You can close the in channel if you want. 34 func NewUnboundedChan[T any](ctx context.Context, initCapacity int, opts ...Option) *UnboundedChan[T] { 35 return NewUnboundedChanSize[T](ctx, initCapacity, initCapacity, initCapacity, opts...) 36 } 37 38 // NewUnboundedChanSize is like NewUnboundedChan but you can set initial capacity for In, Out, Buffer. 39 func NewUnboundedChanSize[T any](ctx context.Context, initInCapacity, initOutCapacity, initBufCapacity int, opts ...Option) *UnboundedChan[T] { 40 in := make(chan T, initInCapacity) 41 out := make(chan T, initOutCapacity) 42 ch := UnboundedChan[T]{In: in, Out: out, buffer: NewRingBuffer[T](initBufCapacity), cc: NewOptions(opts...)} 43 44 go process(ctx, in, out, &ch) 45 46 return &ch 47 } 48 49 func process[T any](ctx context.Context, in, out chan T, ch *UnboundedChan[T]) { 50 defer close(out) 51 drain := func() { 52 for !ch.buffer.IsEmpty() { 53 select { 54 case out <- ch.buffer.Pop(): 55 atomic.AddInt64(&ch.bufCount, -1) 56 case <-ctx.Done(): 57 return 58 } 59 } 60 61 ch.buffer.Reset() 62 atomic.StoreInt64(&ch.bufCount, 0) 63 } 64 for { 65 select { 66 case <-ctx.Done(): 67 return 68 case val, ok := <-in: 69 if !ok { // in is closed 70 drain() 71 return 72 } 73 74 // make sure values' order 75 // buffer has some values 76 if atomic.LoadInt64(&ch.bufCount) > 0 { 77 ch.buffer.Write(val) 78 newVal := atomic.AddInt64(&ch.bufCount, 1) 79 if ch.cc.CallbackOnBufCount != 0 && newVal > ch.cc.CallbackOnBufCount { 80 ch.cc.Callback(newVal) 81 } 82 } else { 83 // out is not full 84 select { 85 case out <- val: 86 //放入成功,说明out刚才还没有满,buffer中也没有额外的数据待处理,所以回到loop开始 87 continue 88 default: 89 } 90 91 // out is full 92 ch.buffer.Write(val) 93 newVal := atomic.AddInt64(&ch.bufCount, 1) 94 if ch.cc.CallbackOnBufCount != 0 && newVal > ch.cc.CallbackOnBufCount { 95 ch.cc.Callback(newVal) 96 } 97 } 98 99 for !ch.buffer.IsEmpty() { 100 select { 101 case <-ctx.Done(): 102 return 103 case val, ok := <-in: 104 if !ok { // in is closed 105 drain() 106 return 107 } 108 ch.buffer.Write(val) 109 newVal := atomic.AddInt64(&ch.bufCount, 1) 110 if ch.cc.CallbackOnBufCount != 0 && newVal > ch.cc.CallbackOnBufCount { 111 ch.cc.Callback(newVal) 112 } 113 case out <- ch.buffer.Peek(): 114 ch.buffer.Pop() 115 atomic.AddInt64(&ch.bufCount, -1) 116 if ch.buffer.IsEmpty() && ch.buffer.size > ch.buffer.initialSize { // after burst 117 ch.buffer.Reset() 118 atomic.StoreInt64(&ch.bufCount, 0) 119 } 120 } 121 } 122 } 123 } 124 }