github.com/Andyfoo/golang/x/net@v0.0.0-20190901054642-57c1bf301704/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 err error // read error once empty. non-nil means closed. 21 breakErr error // immediate read error (caller doesn't see rest of b) 22 donec chan struct{} // closed on error 23 readFn func() // optional code to run in Read before error 24 } 25 26 type pipeBuffer interface { 27 Len() int 28 io.Writer 29 io.Reader 30 } 31 32 func (p *pipe) Len() int { 33 p.mu.Lock() 34 defer p.mu.Unlock() 35 if p.b == nil { 36 return 0 37 } 38 return p.b.Len() 39 } 40 41 // Read waits until data is available and copies bytes 42 // from the buffer into p. 43 func (p *pipe) Read(d []byte) (n int, err error) { 44 p.mu.Lock() 45 defer p.mu.Unlock() 46 if p.c.L == nil { 47 p.c.L = &p.mu 48 } 49 for { 50 if p.breakErr != nil { 51 return 0, p.breakErr 52 } 53 if p.b != nil && p.b.Len() > 0 { 54 return p.b.Read(d) 55 } 56 if p.err != nil { 57 if p.readFn != nil { 58 p.readFn() // e.g. copy trailers 59 p.readFn = nil // not sticky like p.err 60 } 61 p.b = nil 62 return 0, p.err 63 } 64 p.c.Wait() 65 } 66 } 67 68 var errClosedPipeWrite = errors.New("write on closed buffer") 69 70 // Write copies bytes from p into the buffer and wakes a reader. 71 // It is an error to write more data than the buffer can hold. 72 func (p *pipe) Write(d []byte) (n int, err error) { 73 p.mu.Lock() 74 defer p.mu.Unlock() 75 if p.c.L == nil { 76 p.c.L = &p.mu 77 } 78 defer p.c.Signal() 79 if p.err != nil { 80 return 0, errClosedPipeWrite 81 } 82 if p.breakErr != nil { 83 return len(d), nil // discard when there is no reader 84 } 85 return p.b.Write(d) 86 } 87 88 // CloseWithError causes the next Read (waking up a current blocked 89 // Read if needed) to return the provided err after all data has been 90 // read. 91 // 92 // The error must be non-nil. 93 func (p *pipe) CloseWithError(err error) { p.closeWithError(&p.err, err, nil) } 94 95 // BreakWithError causes the next Read (waking up a current blocked 96 // Read if needed) to return the provided err immediately, without 97 // waiting for unread data. 98 func (p *pipe) BreakWithError(err error) { p.closeWithError(&p.breakErr, err, nil) } 99 100 // closeWithErrorAndCode is like CloseWithError but also sets some code to run 101 // in the caller's goroutine before returning the error. 102 func (p *pipe) closeWithErrorAndCode(err error, fn func()) { p.closeWithError(&p.err, err, fn) } 103 104 func (p *pipe) closeWithError(dst *error, err error, fn func()) { 105 if err == nil { 106 panic("err must be non-nil") 107 } 108 p.mu.Lock() 109 defer p.mu.Unlock() 110 if p.c.L == nil { 111 p.c.L = &p.mu 112 } 113 defer p.c.Signal() 114 if *dst != nil { 115 // Already been done. 116 return 117 } 118 p.readFn = fn 119 if dst == &p.breakErr { 120 p.b = nil 121 } 122 *dst = err 123 p.closeDoneLocked() 124 } 125 126 // requires p.mu be held. 127 func (p *pipe) closeDoneLocked() { 128 if p.donec == nil { 129 return 130 } 131 // Close if unclosed. This isn't racy since we always 132 // hold p.mu while closing. 133 select { 134 case <-p.donec: 135 default: 136 close(p.donec) 137 } 138 } 139 140 // Err returns the error (if any) first set by BreakWithError or CloseWithError. 141 func (p *pipe) Err() error { 142 p.mu.Lock() 143 defer p.mu.Unlock() 144 if p.breakErr != nil { 145 return p.breakErr 146 } 147 return p.err 148 } 149 150 // Done returns a channel which is closed if and when this pipe is closed 151 // with CloseWithError. 152 func (p *pipe) Done() <-chan struct{} { 153 p.mu.Lock() 154 defer p.mu.Unlock() 155 if p.donec == nil { 156 p.donec = make(chan struct{}) 157 if p.err != nil || p.breakErr != nil { 158 // Already hit an error. 159 p.closeDoneLocked() 160 } 161 } 162 return p.donec 163 }