gopkg.in/hashicorp/nomad.v0@v0.11.8/lib/circbufwriter/writer.go (about)

     1  package circbufwriter
     2  
     3  import (
     4  	"io"
     5  	"sync"
     6  	"time"
     7  
     8  	"github.com/armon/circbuf"
     9  )
    10  
    11  type circbufWriter struct {
    12  	// circle buffer for data to write
    13  	buf *circbuf.Buffer
    14  
    15  	// error to return from the writer
    16  	err error
    17  
    18  	// bufLock syncronizes access to err and buf
    19  	bufLock sync.Mutex
    20  
    21  	// wrapped writer
    22  	wr io.Writer
    23  
    24  	// signals to flush the buffer
    25  	flushCh chan struct{}
    26  }
    27  
    28  // New created a circle buffered writer that wraps the given writer. The
    29  // bufferSize is the amount of data that will be stored in memory before
    30  // overwriting.
    31  func New(w io.Writer, bufferSize int64) io.WriteCloser {
    32  	buf, _ := circbuf.NewBuffer(bufferSize)
    33  	c := &circbufWriter{
    34  		buf:     buf,
    35  		wr:      w,
    36  		flushCh: make(chan struct{}, 1),
    37  	}
    38  	go c.flushLoop()
    39  	return c
    40  }
    41  
    42  // Write will write the data to the buffer and attempt to flush the buffer to
    43  // the wrapped writer. If the wrapped writer blocks on write, subsequent write
    44  // will be written to the circle buffer.
    45  func (c *circbufWriter) Write(p []byte) (nn int, err error) {
    46  	// If the last write returned an error, return it here. Note there is a
    47  	// small chance of missing an error if multiple writes occure at the same
    48  	// time where the last write nils out the error before it can be returned
    49  	// here.
    50  	c.bufLock.Lock()
    51  	defer c.bufLock.Unlock()
    52  	if c.err != nil {
    53  		return nn, c.err
    54  	}
    55  
    56  	// Write to the buffer
    57  	nn, err = c.buf.Write(p)
    58  
    59  	// Signal to flush the buffer
    60  	select {
    61  	case c.flushCh <- struct{}{}:
    62  	default:
    63  		// flush is blocked
    64  	}
    65  	return nn, err
    66  }
    67  
    68  func (c *circbufWriter) Close() error {
    69  	// Guard against double closing channel
    70  	select {
    71  	case <-c.flushCh:
    72  	default:
    73  		close(c.flushCh)
    74  	}
    75  
    76  	// if the last write errored, it will return here
    77  	c.bufLock.Lock()
    78  	defer c.bufLock.Unlock()
    79  	return c.err
    80  }
    81  
    82  func (c *circbufWriter) flushLoop() {
    83  	// Check buffer every 100ms in case a flush from Write was missed
    84  	ticker := time.NewTicker(time.Millisecond * 100)
    85  	defer ticker.Stop()
    86  	for {
    87  		var err error
    88  		select {
    89  		case _, ok := <-c.flushCh:
    90  			if !ok {
    91  				// Close called, exiting loop
    92  				return
    93  			}
    94  			err = c.flush()
    95  		case <-ticker.C:
    96  			err = c.flush()
    97  		}
    98  
    99  		c.bufLock.Lock()
   100  		c.err = err
   101  		c.bufLock.Unlock()
   102  	}
   103  }
   104  
   105  func (c *circbufWriter) flush() error {
   106  	c.bufLock.Lock()
   107  	b := c.buf.Bytes()
   108  	c.buf.Reset()
   109  	c.bufLock.Unlock()
   110  
   111  	var err error
   112  	if len(b) > 0 {
   113  		_, err = c.wr.Write(b)
   114  	}
   115  	return err
   116  }