github.com/damirazo/docker@v1.9.0/pkg/broadcaster/buffered.go (about) 1 package broadcaster 2 3 import ( 4 "errors" 5 "io" 6 "sync" 7 ) 8 9 // Buffered keeps track of one or more observers watching the progress 10 // of an operation. For example, if multiple clients are trying to pull an 11 // image, they share a Buffered struct for the download operation. 12 type Buffered struct { 13 sync.Mutex 14 // c is a channel that observers block on, waiting for the operation 15 // to finish. 16 c chan struct{} 17 // cond is a condition variable used to wake up observers when there's 18 // new data available. 19 cond *sync.Cond 20 // history is a buffer of the progress output so far, so a new observer 21 // can catch up. The history is stored as a slice of separate byte 22 // slices, so that if the writer is a WriteFlusher, the flushes will 23 // happen in the right places. 24 history [][]byte 25 // wg is a WaitGroup used to wait for all writes to finish on Close 26 wg sync.WaitGroup 27 // result is the argument passed to the first call of Close, and 28 // returned to callers of Wait 29 result error 30 } 31 32 // NewBuffered returns an initialized Buffered structure. 33 func NewBuffered() *Buffered { 34 b := &Buffered{ 35 c: make(chan struct{}), 36 } 37 b.cond = sync.NewCond(b) 38 return b 39 } 40 41 // closed returns true if and only if the broadcaster has been closed 42 func (broadcaster *Buffered) closed() bool { 43 select { 44 case <-broadcaster.c: 45 return true 46 default: 47 return false 48 } 49 } 50 51 // receiveWrites runs as a goroutine so that writes don't block the Write 52 // function. It writes the new data in broadcaster.history each time there's 53 // activity on the broadcaster.cond condition variable. 54 func (broadcaster *Buffered) receiveWrites(observer io.Writer) { 55 n := 0 56 57 broadcaster.Lock() 58 59 // The condition variable wait is at the end of this loop, so that the 60 // first iteration will write the history so far. 61 for { 62 newData := broadcaster.history[n:] 63 // Make a copy of newData so we can release the lock 64 sendData := make([][]byte, len(newData), len(newData)) 65 copy(sendData, newData) 66 broadcaster.Unlock() 67 68 for len(sendData) > 0 { 69 _, err := observer.Write(sendData[0]) 70 if err != nil { 71 broadcaster.wg.Done() 72 return 73 } 74 n++ 75 sendData = sendData[1:] 76 } 77 78 broadcaster.Lock() 79 80 // If we are behind, we need to catch up instead of waiting 81 // or handling a closure. 82 if len(broadcaster.history) != n { 83 continue 84 } 85 86 // detect closure of the broadcast writer 87 if broadcaster.closed() { 88 broadcaster.Unlock() 89 broadcaster.wg.Done() 90 return 91 } 92 93 broadcaster.cond.Wait() 94 95 // Mutex is still locked as the loop continues 96 } 97 } 98 99 // Write adds data to the history buffer, and also writes it to all current 100 // observers. 101 func (broadcaster *Buffered) Write(p []byte) (n int, err error) { 102 broadcaster.Lock() 103 defer broadcaster.Unlock() 104 105 // Is the broadcaster closed? If so, the write should fail. 106 if broadcaster.closed() { 107 return 0, errors.New("attempted write to a closed broadcaster.Buffered") 108 } 109 110 // Add message in p to the history slice 111 newEntry := make([]byte, len(p), len(p)) 112 copy(newEntry, p) 113 broadcaster.history = append(broadcaster.history, newEntry) 114 115 broadcaster.cond.Broadcast() 116 117 return len(p), nil 118 } 119 120 // Add adds an observer to the broadcaster. The new observer receives the 121 // data from the history buffer, and also all subsequent data. 122 func (broadcaster *Buffered) Add(w io.Writer) error { 123 // The lock is acquired here so that Add can't race with Close 124 broadcaster.Lock() 125 defer broadcaster.Unlock() 126 127 if broadcaster.closed() { 128 return errors.New("attempted to add observer to a closed broadcaster.Buffered") 129 } 130 131 broadcaster.wg.Add(1) 132 go broadcaster.receiveWrites(w) 133 134 return nil 135 } 136 137 // CloseWithError signals to all observers that the operation has finished. Its 138 // argument is a result that should be returned to waiters blocking on Wait. 139 func (broadcaster *Buffered) CloseWithError(result error) { 140 broadcaster.Lock() 141 if broadcaster.closed() { 142 broadcaster.Unlock() 143 return 144 } 145 broadcaster.result = result 146 close(broadcaster.c) 147 broadcaster.cond.Broadcast() 148 broadcaster.Unlock() 149 150 // Don't return until all writers have caught up. 151 broadcaster.wg.Wait() 152 } 153 154 // Close signals to all observers that the operation has finished. It causes 155 // all calls to Wait to return nil. 156 func (broadcaster *Buffered) Close() { 157 broadcaster.CloseWithError(nil) 158 } 159 160 // Wait blocks until the operation is marked as completed by the Close method, 161 // and all writer goroutines have completed. It returns the argument that was 162 // passed to Close. 163 func (broadcaster *Buffered) Wait() error { 164 <-broadcaster.c 165 broadcaster.wg.Wait() 166 return broadcaster.result 167 }