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 }