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  }