golang.org/x/build@v0.0.0-20240506185731-218518f32b70/livelog/livelog.go (about) 1 // Copyright 2015 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 livelog provides a buffer that can be simultaneously written to by 6 // one writer and read from by many readers. 7 package livelog 8 9 import ( 10 "io" 11 "sync" 12 ) 13 14 const ( 15 // MaxBufferSize is the maximum buffer size, as it is more output than 16 // we expect from reasonable tests. 17 MaxBufferSize = 2 << 20 // 2 MB 18 19 // truncationMessage is added to the end of the log when it reaches the 20 // maximum size. 21 truncationMessage = "\n\n... log truncated ..." 22 23 // maxUserSize is the total user output we can place in the buffer 24 // while still leaving room for the truncation message. 25 maxUserSize = MaxBufferSize - len(truncationMessage) 26 ) 27 28 // Buffer is an io.WriteCloser that provides multiple Readers that each yield 29 // the same data. 30 // 31 // It is safe to Write to a Buffer while Readers consume data. A Buffer has a 32 // maximum size of MaxBufferSize, after which Write will silently drop 33 // additional data and the buffer will contain a truncation note at the end. 34 // 35 // The zero value is a ready-to-use buffer. 36 type Buffer struct { 37 mu sync.Mutex // Guards the fields below. 38 wake *sync.Cond // Created on demand by reader. 39 buf []byte // Length is in the range [0, MaxBufferSize]. 40 eof bool 41 lastID int 42 } 43 44 // Write appends data to the Buffer. 45 // It will wake any blocked Readers. 46 func (b *Buffer) Write(b2 []byte) (int, error) { 47 b.mu.Lock() 48 defer b.mu.Unlock() 49 50 needTrunc := false 51 b2len := len(b2) 52 if len(b.buf) == MaxBufferSize { 53 // b.buf is full and truncationMessage was written. 54 b2 = nil 55 } else if len(b.buf)+b2len > maxUserSize { 56 b2 = b2[:maxUserSize-len(b.buf)] 57 needTrunc = true 58 // After this write, b.buf will reach MaxBufferSize length. 59 } 60 b.buf = append(b.buf, b2...) 61 if needTrunc { 62 b.buf = append(b.buf, []byte(truncationMessage)...) 63 } 64 b.wakeReaders() 65 return b2len, nil 66 } 67 68 // Close signals EOF to all Readers. 69 func (b *Buffer) Close() error { 70 b.mu.Lock() 71 defer b.mu.Unlock() 72 73 b.eof = true 74 b.wakeReaders() 75 return nil 76 } 77 78 // wakeReaders wakes any sleeping readers. 79 // b.mu must be held when calling. 80 func (b *Buffer) wakeReaders() { 81 if b.wake != nil { 82 b.wake.Broadcast() 83 } 84 } 85 86 // Bytes returns a copy of the underlying buffer. 87 func (b *Buffer) Bytes() []byte { 88 b.mu.Lock() 89 defer b.mu.Unlock() 90 91 return append([]byte(nil), b.buf...) 92 } 93 94 // String returns a copy of the underlying buffer as a string. 95 func (b *Buffer) String() string { 96 b.mu.Lock() 97 defer b.mu.Unlock() 98 99 return string(b.buf) 100 } 101 102 // Reader initializes and returns a ReadCloser that will emit the entire buffer. 103 // It is safe to call Read and Close concurrently. 104 func (b *Buffer) Reader() io.ReadCloser { 105 b.mu.Lock() 106 defer b.mu.Unlock() 107 108 b.lastID++ 109 return &reader{buf: b, id: b.lastID} 110 } 111 112 type reader struct { 113 buf *Buffer 114 id int // Read-only. 115 read int // Bytes read; accessed by only the Read method. 116 closed bool // Guarded by buf.mu. 117 } 118 119 func (r *reader) Read(b []byte) (int, error) { 120 r.buf.mu.Lock() 121 defer r.buf.mu.Unlock() 122 123 // Wait for data or writer EOF or reader closed. 124 for len(r.buf.buf) == r.read && !r.buf.eof && !r.closed { 125 if r.buf.wake == nil { 126 r.buf.wake = sync.NewCond(&r.buf.mu) 127 } 128 r.buf.wake.Wait() 129 } 130 // Return EOF if writer reported EOF or this reader is closed. 131 if (len(r.buf.buf) == r.read && r.buf.eof) || r.closed { 132 return 0, io.EOF 133 } 134 // Emit some data. 135 n := copy(b, r.buf.buf[r.read:]) 136 r.read += n 137 return n, nil 138 } 139 140 func (r *reader) Close() error { 141 r.buf.mu.Lock() 142 defer r.buf.mu.Unlock() 143 144 r.closed = true 145 146 // Wake any sleeping readers to unblock a pending read on this reader. 147 // (For other open readers this will be a no-op.) 148 r.buf.wakeReaders() 149 150 return nil 151 }