github.com/pingcap/tiflow@v0.0.0-20240520035814-5bf52d54e205/pkg/chann/chann.go (about) 1 // Copyright 2022 PingCAP, Inc. 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 // See the License for the specific language governing permissions and 12 // limitations under the License. 13 // 14 // ============================================================ 15 // Forked from https://github.com/golang-design/chann. 16 // Copyright 2021 The golang.design Initiative Authors. 17 // All rights reserved. Use of this source code is governed 18 // by a MIT license that can be found in the LICENSE file. 19 // 20 // Written by Changkun Ou <changkun.de> 21 22 // Package chann providesa a unified channel package. 23 // 24 // The package is compatible with existing buffered and unbuffered 25 // channels. For example, in Go, to create a buffered or unbuffered 26 // channel, one uses built-in function `make` to create a channel: 27 // 28 // ch := make(chan int) // unbuffered channel 29 // ch := make(chan int, 42) // or buffered channel 30 // 31 // However, all these channels have a finite capacity for caching, and 32 // it is impossible to create a channel with unlimited capacity, namely, 33 // an unbounded channel. 34 // 35 // This package provides the ability to create all possible types of 36 // channels. To create an unbuffered or a buffered channel: 37 // 38 // ch := chann.New[int](chann.Cap(0)) // unbuffered channel 39 // ch := chann.New[int](chann.Cap(42)) // or buffered channel 40 // 41 // More importantly, when the capacity of the channel is unspecified, 42 // or provided as negative values, the created channel is an unbounded 43 // channel: 44 // 45 // ch := chann.New[int]() // unbounded channel 46 // ch := chann.New[int](chann.Cap(-42)) // or unbounded channel 47 // 48 // Furthermore, all channels provides methods to send (In()), 49 // receive (Out()), and close (Close()). 50 // 51 // An unbounded channel is not a buffered channel with infinite capacity, 52 // and they have different memory model semantics in terms of receiving 53 // a value: The recipient of a buffered channel is immediately available 54 // after a send is complete. However, the recipient of an unbounded channel 55 // may be available within a bounded time frame after a send is complete. 56 // 57 // Note that to close a channel, must use Close() method instead of the 58 // language built-in method 59 // Two additional methods: ApproxLen and Cap returns the current status 60 // of the channel: an approximation of the current length of the channel, 61 // as well as the current capacity of the channel. 62 // 63 // See https://golang.design/research/ultimate-channel to understand 64 // the motivation of providing this package and the possible use cases 65 // with this package. 66 package chann 67 68 import ( 69 "sync/atomic" 70 ) 71 72 // Opt represents an option to configure the created channel. The current possible 73 // option is Cap. 74 type Opt func(*config) 75 76 // Cap is the option to configure the capacity of a creating buffer. 77 // if the provided number is 0, Cap configures the creating buffer to a 78 // unbuffered channel; if the provided number is a positive integer, then 79 // Cap configures the creating buffer to a buffered channel with the given 80 // number of capacity for caching. If n is a negative integer, then it 81 // configures the creating channel to become an unbounded channel. 82 func Cap(n int) Opt { 83 return func(s *config) { 84 switch { 85 case n == 0: 86 s.cap = int64(0) 87 s.typ = unbuffered 88 case n > 0: 89 s.cap = int64(n) 90 s.typ = buffered 91 default: 92 s.cap = int64(-1) 93 s.typ = unbounded 94 } 95 } 96 } 97 98 // Chann is a generic channel abstraction that can be either buffered, 99 // unbuffered, or unbounded. To create a new channel, use New to allocate 100 // one, and use Cap to configure the capacity of the channel. 101 type Chann[T any] struct { 102 q []T 103 in, out chan T 104 close chan struct{} 105 cfg *config 106 } 107 108 // New returns a Chann that may represent a buffered, an unbuffered or 109 // an unbounded channel. To configure the type of the channel, one may 110 // pass Cap as the argument of this function. 111 // 112 // By default, or without specification, the function returns an unbounded 113 // channel which has unlimited capacity. 114 // 115 // ch := chann.New[float64]() 116 // or 117 // ch := chann.New[float64](chann.Cap(-1)) 118 // 119 // If the chann.Cap specified a non-negative integer, the returned channel 120 // is either unbuffered (0) or buffered (positive). 121 // 122 // Note that although the input arguments are specified as variadic parameter 123 // list, however, the function panics if there is more than one option is 124 // provided. 125 // DEPRECATED: use NewAutoDrainChann instead. 126 func New[T any](opts ...Opt) *Chann[T] { 127 cfg := &config{ 128 cap: -1, len: 0, 129 typ: unbounded, 130 } 131 132 if len(opts) > 1 { 133 panic("chann: too many arguments") 134 } 135 for _, o := range opts { 136 o(cfg) 137 } 138 ch := &Chann[T]{cfg: cfg, close: make(chan struct{})} 139 switch ch.cfg.typ { 140 case unbuffered: 141 ch.in = make(chan T) 142 ch.out = ch.in 143 case buffered: 144 ch.in = make(chan T, ch.cfg.cap) 145 ch.out = ch.in 146 case unbounded: 147 ch.in = make(chan T, 16) 148 ch.out = make(chan T, 16) 149 go ch.unboundedProcessing() 150 } 151 return ch 152 } 153 154 // In returns the send channel of the given Chann, which can be used to 155 // send values to the channel. If one closes the channel using close(), 156 // it will result in a runtime panic. Instead, use Close() method. 157 func (ch *Chann[T]) In() chan<- T { return ch.in } 158 159 // Out returns the receive channel of the given Chann, which can be used 160 // to receive values from the channel. 161 func (ch *Chann[T]) Out() <-chan T { return ch.out } 162 163 // Close closes the channel gracefully. 164 // DEPRECATED: use CloseAndDrain instead. 165 func (ch *Chann[T]) Close() { 166 switch ch.cfg.typ { 167 case buffered, unbuffered: 168 close(ch.in) 169 close(ch.close) 170 default: 171 ch.close <- struct{}{} 172 } 173 } 174 175 // unboundedProcessing is a processing loop that implements unbounded 176 // channel semantics. 177 func (ch *Chann[T]) unboundedProcessing() { 178 var nilT T 179 180 for { 181 select { 182 case e, ok := <-ch.in: 183 if !ok { 184 panic("chann: send-only channel ch.In() closed unexpectedly") 185 } 186 atomic.AddInt64(&ch.cfg.len, 1) 187 ch.q = append(ch.q, e) 188 case <-ch.close: 189 ch.unboundedTerminate() 190 return 191 } 192 193 for len(ch.q) > 0 { 194 select { 195 case ch.out <- ch.q[0]: 196 atomic.AddInt64(&ch.cfg.len, -1) 197 ch.q[0] = nilT 198 ch.q = ch.q[1:] 199 case e, ok := <-ch.in: 200 if !ok { 201 panic("chann: send-only channel ch.In() closed unexpectedly") 202 } 203 atomic.AddInt64(&ch.cfg.len, 1) 204 ch.q = append(ch.q, e) 205 case <-ch.close: 206 ch.unboundedTerminate() 207 return 208 } 209 } 210 ch.q = nil 211 } 212 } 213 214 // unboundedTerminate terminates the unbounde channel's processing loop 215 // and make sure all unprocessed elements be consumed if there is 216 // a pending receiver. 217 func (ch *Chann[T]) unboundedTerminate() { 218 var zeroT T 219 220 close(ch.in) 221 for e := range ch.in { 222 ch.q = append(ch.q, e) 223 } 224 for len(ch.q) > 0 { 225 // NOTICE: If no receiver is receiving the element, it will be blocked. 226 // So the consumer have to deal with all the elements in the queue. 227 ch.out <- ch.q[0] 228 ch.q[0] = zeroT // de-reference earlier to help GC 229 ch.q = ch.q[1:] 230 } 231 close(ch.out) 232 close(ch.close) 233 } 234 235 // isClose reports the close status of a channel. 236 func (ch *Chann[T]) isClosed() bool { 237 select { 238 case <-ch.close: 239 return true 240 default: 241 return false 242 } 243 } 244 245 // Len returns an approximation of the length of the channel. 246 // 247 // Note that in a concurrent scenario, the returned length of a channel 248 // may never be accurate. Hence the function is named with an Approx prefix. 249 func (ch *Chann[T]) Len() int { 250 switch ch.cfg.typ { 251 case buffered, unbuffered: 252 return len(ch.in) 253 default: 254 return int(atomic.LoadInt64(&ch.cfg.len)) + len(ch.in) + len(ch.out) 255 } 256 } 257 258 // Cap returns the capacity of the channel. 259 func (ch *Chann[T]) Cap() int { 260 switch ch.cfg.typ { 261 case buffered, unbuffered: 262 return cap(ch.in) 263 default: 264 return int(atomic.LoadInt64(&ch.cfg.cap)) + cap(ch.in) + cap(ch.out) 265 } 266 } 267 268 type chanType int 269 270 const ( 271 unbuffered chanType = iota 272 buffered 273 unbounded 274 ) 275 276 type config struct { 277 typ chanType 278 len, cap int64 279 }