golang.org/x/net@v0.25.1-0.20240516223405-c87a5b62e243/http2/pipe.go (about)

     1  // Copyright 2014 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package http2
     6  
     7  import (
     8  	"errors"
     9  	"io"
    10  	"sync"
    11  )
    12  
    13  // pipe is a goroutine-safe io.Reader/io.Writer pair. It's like
    14  // io.Pipe except there are no PipeReader/PipeWriter halves, and the
    15  // underlying buffer is an interface. (io.Pipe is always unbuffered)
    16  type pipe struct {
    17  	mu       sync.Mutex
    18  	c        sync.Cond     // c.L lazily initialized to &p.mu
    19  	b        pipeBuffer    // nil when done reading
    20  	unread   int           // bytes unread when done
    21  	err      error         // read error once empty. non-nil means closed.
    22  	breakErr error         // immediate read error (caller doesn't see rest of b)
    23  	donec    chan struct{} // closed on error
    24  	readFn   func()        // optional code to run in Read before error
    25  }
    26  
    27  type pipeBuffer interface {
    28  	Len() int
    29  	io.Writer
    30  	io.Reader
    31  }
    32  
    33  // setBuffer initializes the pipe buffer.
    34  // It has no effect if the pipe is already closed.
    35  func (p *pipe) setBuffer(b pipeBuffer) {
    36  	p.mu.Lock()
    37  	defer p.mu.Unlock()
    38  	if p.err != nil || p.breakErr != nil {
    39  		return
    40  	}
    41  	p.b = b
    42  }
    43  
    44  func (p *pipe) Len() int {
    45  	p.mu.Lock()
    46  	defer p.mu.Unlock()
    47  	if p.b == nil {
    48  		return p.unread
    49  	}
    50  	return p.b.Len()
    51  }
    52  
    53  // Read waits until data is available and copies bytes
    54  // from the buffer into p.
    55  func (p *pipe) Read(d []byte) (n int, err error) {
    56  	p.mu.Lock()
    57  	defer p.mu.Unlock()
    58  	if p.c.L == nil {
    59  		p.c.L = &p.mu
    60  	}
    61  	for {
    62  		if p.breakErr != nil {
    63  			return 0, p.breakErr
    64  		}
    65  		if p.b != nil && p.b.Len() > 0 {
    66  			return p.b.Read(d)
    67  		}
    68  		if p.err != nil {
    69  			if p.readFn != nil {
    70  				p.readFn()     // e.g. copy trailers
    71  				p.readFn = nil // not sticky like p.err
    72  			}
    73  			p.b = nil
    74  			return 0, p.err
    75  		}
    76  		p.c.Wait()
    77  	}
    78  }
    79  
    80  var (
    81  	errClosedPipeWrite        = errors.New("write on closed buffer")
    82  	errUninitializedPipeWrite = errors.New("write on uninitialized buffer")
    83  )
    84  
    85  // Write copies bytes from p into the buffer and wakes a reader.
    86  // It is an error to write more data than the buffer can hold.
    87  func (p *pipe) Write(d []byte) (n int, err error) {
    88  	p.mu.Lock()
    89  	defer p.mu.Unlock()
    90  	if p.c.L == nil {
    91  		p.c.L = &p.mu
    92  	}
    93  	defer p.c.Signal()
    94  	if p.err != nil || p.breakErr != nil {
    95  		return 0, errClosedPipeWrite
    96  	}
    97  	// pipe.setBuffer is never invoked, leaving the buffer uninitialized.
    98  	// We shouldn't try to write to an uninitialized pipe,
    99  	// but returning an error is better than panicking.
   100  	if p.b == nil {
   101  		return 0, errUninitializedPipeWrite
   102  	}
   103  	return p.b.Write(d)
   104  }
   105  
   106  // CloseWithError causes the next Read (waking up a current blocked
   107  // Read if needed) to return the provided err after all data has been
   108  // read.
   109  //
   110  // The error must be non-nil.
   111  func (p *pipe) CloseWithError(err error) { p.closeWithError(&p.err, err, nil) }
   112  
   113  // BreakWithError causes the next Read (waking up a current blocked
   114  // Read if needed) to return the provided err immediately, without
   115  // waiting for unread data.
   116  func (p *pipe) BreakWithError(err error) { p.closeWithError(&p.breakErr, err, nil) }
   117  
   118  // closeWithErrorAndCode is like CloseWithError but also sets some code to run
   119  // in the caller's goroutine before returning the error.
   120  func (p *pipe) closeWithErrorAndCode(err error, fn func()) { p.closeWithError(&p.err, err, fn) }
   121  
   122  func (p *pipe) closeWithError(dst *error, err error, fn func()) {
   123  	if err == nil {
   124  		panic("err must be non-nil")
   125  	}
   126  	p.mu.Lock()
   127  	defer p.mu.Unlock()
   128  	if p.c.L == nil {
   129  		p.c.L = &p.mu
   130  	}
   131  	defer p.c.Signal()
   132  	if *dst != nil {
   133  		// Already been done.
   134  		return
   135  	}
   136  	p.readFn = fn
   137  	if dst == &p.breakErr {
   138  		if p.b != nil {
   139  			p.unread += p.b.Len()
   140  		}
   141  		p.b = nil
   142  	}
   143  	*dst = err
   144  	p.closeDoneLocked()
   145  }
   146  
   147  // requires p.mu be held.
   148  func (p *pipe) closeDoneLocked() {
   149  	if p.donec == nil {
   150  		return
   151  	}
   152  	// Close if unclosed. This isn't racy since we always
   153  	// hold p.mu while closing.
   154  	select {
   155  	case <-p.donec:
   156  	default:
   157  		close(p.donec)
   158  	}
   159  }
   160  
   161  // Err returns the error (if any) first set by BreakWithError or CloseWithError.
   162  func (p *pipe) Err() error {
   163  	p.mu.Lock()
   164  	defer p.mu.Unlock()
   165  	if p.breakErr != nil {
   166  		return p.breakErr
   167  	}
   168  	return p.err
   169  }
   170  
   171  // Done returns a channel which is closed if and when this pipe is closed
   172  // with CloseWithError.
   173  func (p *pipe) Done() <-chan struct{} {
   174  	p.mu.Lock()
   175  	defer p.mu.Unlock()
   176  	if p.donec == nil {
   177  		p.donec = make(chan struct{})
   178  		if p.err != nil || p.breakErr != nil {
   179  			// Already hit an error.
   180  			p.closeDoneLocked()
   181  		}
   182  	}
   183  	return p.donec
   184  }