github.com/puellanivis/breton@v0.2.16/lib/io/bufpipe/bufpipe.go (about)

     1  // Package bufpipe implements a buffered in-memory pipe where reads will block until data is available.
     2  package bufpipe
     3  
     4  import (
     5  	"bytes"
     6  	"context"
     7  	"io"
     8  	"sync"
     9  )
    10  
    11  // Pipe defines an io.Reader and io.Writer where data given to Write will be buffered until a corresponding Read.
    12  type Pipe struct {
    13  	once sync.Once
    14  	mu   sync.Mutex
    15  
    16  	closed chan struct{}
    17  	ready  chan struct{}
    18  	empty  chan struct{}
    19  
    20  	autoFlush      int
    21  	maxOutstanding int
    22  
    23  	b bytes.Buffer
    24  }
    25  
    26  func (p *Pipe) init() {
    27  	p.closed = make(chan struct{})
    28  	p.ready = make(chan struct{})
    29  
    30  	p.empty = make(chan struct{})
    31  	close(p.empty)
    32  }
    33  
    34  // New returns a new Pipe with the given Options, and will be closed if the given context.Context is canceled.
    35  // If a nil context is given, then no context-dependent closing will be done.
    36  func New(ctx context.Context, opts ...Option) *Pipe {
    37  	p := new(Pipe)
    38  	p.once.Do(p.init)
    39  
    40  	for _, opt := range opts {
    41  		_ = opt(p)
    42  	}
    43  
    44  	if ctx != nil {
    45  		p.CloseOnContext(ctx)
    46  	}
    47  
    48  	return p
    49  }
    50  
    51  // CloseOnContext will close the Pipe if the given context.Context is canceled.
    52  // A single Pipe can be setup to close on multiple different and even independent contexts.
    53  // With great power, comes great responsibility. Use wisely.
    54  func (p *Pipe) CloseOnContext(ctx context.Context) {
    55  	go func() {
    56  		// Watch the context, if it closes, then Close this pipe.
    57  		select {
    58  		case <-ctx.Done():
    59  			p.Close()
    60  		case <-p.closed:
    61  		}
    62  	}()
    63  }
    64  
    65  func (p *Pipe) doEmptyBuffer() error {
    66  	select {
    67  	case <-p.empty:
    68  		// Pipe is already marked as empty, so we don't need to mark it empty again.
    69  	default:
    70  		close(p.empty)
    71  	}
    72  
    73  	select {
    74  	case <-p.closed:
    75  		return io.EOF
    76  	default:
    77  	}
    78  
    79  	// We have to check these separately,
    80  	// because by Go standards, a select picks between ready channels randomly.
    81  	// And we need to ensure these are tested sequentially.
    82  
    83  	select {
    84  	case <-p.ready:
    85  		// The ready channel is closed, so remake it so future Readers will block.
    86  		p.ready = make(chan struct{})
    87  
    88  	default:
    89  		// The ready channel is already reopened, so don't open it again.
    90  	}
    91  
    92  	return nil
    93  }
    94  
    95  // ReadAll blocks until data is available on the buffer,
    96  // then returns a Read of the entire contents of the underlying buffer.
    97  func (p *Pipe) ReadAll() (buf []byte, err error) {
    98  	p.once.Do(p.init)
    99  
   100  	// We want to block here outside of the mutex lock,
   101  	// so we can block waiting for data while at the same time also not holding the mutex.
   102  	// Otherwise, we could not Write to the Pipe!
   103  	<-p.ready
   104  
   105  	p.mu.Lock()
   106  	defer p.mu.Unlock()
   107  
   108  	if p.b.Len() == 0 {
   109  		return nil, p.doEmptyBuffer()
   110  	}
   111  
   112  	buf = make([]byte, p.b.Len())
   113  
   114  	_, err = p.b.Read(buf)
   115  	if err != nil && err != io.EOF {
   116  		return nil, err
   117  	}
   118  
   119  	if p.b.Len() != 0 {
   120  		panic("ReadAll did not empty buffer")
   121  	}
   122  
   123  	_ = p.doEmptyBuffer()
   124  
   125  	return buf, nil
   126  }
   127  
   128  // Read blocks until data is available on the buffer, then performs a locked Read on the underlying buffer.
   129  func (p *Pipe) Read(b []byte) (n int, err error) {
   130  	p.once.Do(p.init)
   131  
   132  	// We want to block here outside of the mutex lock,
   133  	// so we can block waiting for data while at the same time also not holding the mutex.
   134  	// Otherwise, we could not Write to the Pipe!
   135  	<-p.ready
   136  
   137  	p.mu.Lock()
   138  	defer p.mu.Unlock()
   139  
   140  	if p.b.Len() == 0 {
   141  		return 0, p.doEmptyBuffer()
   142  	}
   143  
   144  	n, err = p.b.Read(b)
   145  
   146  	if p.b.Len() == 0 {
   147  		_ = p.doEmptyBuffer()
   148  
   149  		return n, nil
   150  	}
   151  
   152  	return n, err
   153  }
   154  
   155  func (p *Pipe) prewrite() error {
   156  	select {
   157  	case <-p.closed:
   158  		// One cannot write/flush a closed pipe.
   159  		return io.ErrClosedPipe
   160  	default:
   161  	}
   162  
   163  	return nil
   164  }
   165  
   166  // Write performs an locked Write to the underlying buffer.
   167  // If AutoFlush is enabled (the default), it will also unblock any blocked Readers.
   168  func (p *Pipe) Write(b []byte) (n int, err error) {
   169  	p.once.Do(p.init)
   170  	p.mu.Lock()
   171  	defer p.mu.Unlock()
   172  
   173  	if err := p.prewrite(); err != nil {
   174  		return 0, err
   175  	}
   176  
   177  	if p.maxOutstanding > 0 && p.b.Len()+len(b) > p.maxOutstanding {
   178  		if err := p.sync(); err != nil {
   179  			return 0, err
   180  		}
   181  	}
   182  
   183  	n, err = p.b.Write(b)
   184  
   185  	if err == nil {
   186  		if p.autoFlush >= 0 && p.b.Len() > p.autoFlush {
   187  			p.flush()
   188  		}
   189  	}
   190  
   191  	return n, err
   192  }
   193  
   194  func (p *Pipe) flush() {
   195  	select {
   196  	case <-p.ready:
   197  		// Pipe is already marked as ready, so we don't need to mark it ready again.
   198  	default:
   199  		close(p.ready)
   200  	}
   201  }
   202  
   203  // Flush will unblock any blocked Readers.
   204  // This could cause them to read zero bytes of data.
   205  func (p *Pipe) Flush() error {
   206  	p.once.Do(p.init)
   207  	p.mu.Lock()
   208  	defer p.mu.Unlock()
   209  
   210  	if err := p.prewrite(); err != nil {
   211  		return err
   212  	}
   213  
   214  	p.flush()
   215  
   216  	return nil
   217  }
   218  
   219  func (p *Pipe) sync() error {
   220  	// If we only make a new empty channel when we will be watching it,
   221  	// we can avoid channel creation churn on non-syncing pipes.
   222  	if p.b.Len() > 0 {
   223  		select {
   224  		case <-p.empty:
   225  			p.empty = make(chan struct{})
   226  		default:
   227  		}
   228  	}
   229  
   230  	// We will be watching this channel outside of lock,
   231  	// so we have to have a local copy.
   232  	empty := p.empty
   233  
   234  	p.flush() // flush, just to make sure.
   235  	p.mu.Unlock()
   236  
   237  	select {
   238  	case <-empty:
   239  	case <-p.closed:
   240  	}
   241  
   242  	p.mu.Lock()
   243  
   244  	return p.prewrite()
   245  }
   246  
   247  // Sync will unblock any blocked Readers, and block until the internal buffer is empty.
   248  // This could cause them to read zero bytes of data.
   249  func (p *Pipe) Sync() error {
   250  	p.once.Do(p.init)
   251  	p.mu.Lock()
   252  	defer p.mu.Unlock()
   253  
   254  	if err := p.prewrite(); err != nil {
   255  		return err
   256  	}
   257  
   258  	return p.sync()
   259  }
   260  
   261  func (p *Pipe) close() {
   262  	select {
   263  	case <-p.closed:
   264  	default:
   265  		close(p.closed)
   266  	}
   267  }
   268  
   269  // Close will close the Pipe, and unblock any blocked Readers.
   270  func (p *Pipe) Close() error {
   271  	p.once.Do(p.init)
   272  	p.mu.Lock()
   273  	defer p.mu.Unlock()
   274  
   275  	// Order is vitally important here: close then flush.
   276  	p.close()
   277  	p.flush()
   278  
   279  	return nil
   280  }