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  }