github.com/zignig/go-ipfs@v0.0.0-20141111235910-c9e5fdf55a52/util/ctxcloser/closer.go (about)

     1  package ctxcloser
     2  
     3  import (
     4  	"sync"
     5  
     6  	context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context"
     7  )
     8  
     9  // CloseFunc is a function used to close a ContextCloser
    10  type CloseFunc func() error
    11  
    12  var nilCloseFunc = func() error { return nil }
    13  
    14  // ContextCloser is an interface for services able to be opened and closed.
    15  // It has a parent Context, and Children. But ContextCloser is not a proper
    16  // "tree" like the Context tree. It is more like a Context-WaitGroup hybrid.
    17  // It models a main object with a few children objects -- and, unlike the
    18  // context -- concerns itself with the parent-child closing semantics:
    19  //
    20  // - Can define a CloseFunc (func() error) to be run at Close time.
    21  // - Children call Children().Add(1) to be waited upon
    22  // - Children can select on <-Closing() to know when they should shut down.
    23  // - Close() will wait until all children call Children().Done()
    24  // - <-Closed() signals when the service is completely closed.
    25  //
    26  // ContextCloser can be embedded into the main object itself. In that case,
    27  // the closeFunc (if a member function) has to be set after the struct
    28  // is intialized:
    29  //
    30  //  type service struct {
    31  //  	ContextCloser
    32  //  	net.Conn
    33  //  }
    34  //
    35  //  func (s *service) close() error {
    36  //  	return s.Conn.Close()
    37  //  }
    38  //
    39  //  func newService(ctx context.Context, c net.Conn) *service {
    40  //  	s := &service{c}
    41  //  	s.ContextCloser = NewContextCloser(ctx, s.close)
    42  //  	return s
    43  //  }
    44  //
    45  type ContextCloser interface {
    46  
    47  	// Context is the context of this ContextCloser. It is "sort of" a parent.
    48  	Context() context.Context
    49  
    50  	// Children is a sync.Waitgroup for all children goroutines that should
    51  	// shut down completely before this service is said to be "closed".
    52  	// Follows the semantics of WaitGroup:
    53  	//
    54  	//  Children().Add(1) // add one more dependent child
    55  	//  Children().Done() // child signals it is done
    56  	//
    57  	Children() *sync.WaitGroup
    58  
    59  	// AddCloserChild registers a dependent ContextCloser child. The child will
    60  	// be closed when this parent is closed, and waited upon to finish. It is
    61  	// the functional equivalent of the following:
    62  	//
    63  	//  go func(parent, child ContextCloser) {
    64  	//  	parent.Children().Add(1) // add one more dependent child
    65  	//  	<-parent.Closing()       // wait until parent is closing
    66  	//  	child.Close()            // signal child to close
    67  	//  	parent.Children().Done() // child signals it is done
    68  	//	}(a, b)
    69  	//
    70  	AddCloserChild(c ContextCloser)
    71  
    72  	// Close is a method to call when you wish to stop this ContextCloser
    73  	Close() error
    74  
    75  	// Closing is a signal to wait upon, like Context.Done().
    76  	// It fires when the object should be closing (but hasn't yet fully closed).
    77  	// The primary use case is for child goroutines who need to know when
    78  	// they should shut down. (equivalent to Context().Done())
    79  	Closing() <-chan struct{}
    80  
    81  	// Closed is a method to wait upon, like Context.Done().
    82  	// It fires when the entire object is fully closed.
    83  	// The primary use case is for external listeners who need to know when
    84  	// this object is completly done, and all its children closed.
    85  	Closed() <-chan struct{}
    86  }
    87  
    88  // contextCloser is an OpenCloser with a cancellable context
    89  type contextCloser struct {
    90  	ctx    context.Context
    91  	cancel context.CancelFunc
    92  
    93  	// called to run the close logic.
    94  	closeFunc CloseFunc
    95  
    96  	// closed is released once the close function is done.
    97  	closed chan struct{}
    98  
    99  	// wait group for child goroutines
   100  	children sync.WaitGroup
   101  
   102  	// sync primitive to ensure the close logic is only called once.
   103  	closeOnce sync.Once
   104  
   105  	// error to return to clients of Close().
   106  	closeErr error
   107  }
   108  
   109  // NewContextCloser constructs and returns a ContextCloser. It will call
   110  // cf CloseFunc before its Done() Wait signals fire.
   111  func NewContextCloser(ctx context.Context, cf CloseFunc) ContextCloser {
   112  	if cf == nil {
   113  		cf = nilCloseFunc
   114  	}
   115  	ctx, cancel := context.WithCancel(ctx)
   116  	c := &contextCloser{
   117  		ctx:       ctx,
   118  		cancel:    cancel,
   119  		closeFunc: cf,
   120  		closed:    make(chan struct{}),
   121  	}
   122  
   123  	c.Children().Add(1) // we're a child goroutine, to be waited upon.
   124  	go c.closeOnContextDone()
   125  	return c
   126  }
   127  
   128  func (c *contextCloser) Context() context.Context {
   129  	return c.ctx
   130  }
   131  
   132  func (c *contextCloser) Children() *sync.WaitGroup {
   133  	return &c.children
   134  }
   135  
   136  func (c *contextCloser) AddCloserChild(child ContextCloser) {
   137  	c.children.Add(1)
   138  	go func(parent, child ContextCloser) {
   139  		<-parent.Closing()       // wait until parent is closing
   140  		child.Close()            // signal child to close
   141  		parent.Children().Done() // child signals it is done
   142  	}(c, child)
   143  }
   144  
   145  // Close is the external close function. it's a wrapper around internalClose
   146  // that waits on Closed()
   147  func (c *contextCloser) Close() error {
   148  	c.internalClose()
   149  	<-c.Closed() // wait until we're totally done.
   150  	return c.closeErr
   151  }
   152  
   153  func (c *contextCloser) Closing() <-chan struct{} {
   154  	return c.Context().Done()
   155  }
   156  
   157  func (c *contextCloser) Closed() <-chan struct{} {
   158  	return c.closed
   159  }
   160  
   161  func (c *contextCloser) internalClose() {
   162  	go c.closeOnce.Do(c.closeLogic)
   163  }
   164  
   165  // the _actual_ close process.
   166  func (c *contextCloser) closeLogic() {
   167  	// this function should only be called once (hence the sync.Once).
   168  	// and it will panic at the bottom (on close(c.closed)) otherwise.
   169  
   170  	c.cancel()                 // signal that we're shutting down (Closing)
   171  	c.closeErr = c.closeFunc() // actually run the close logic
   172  	c.children.Wait()          // wait till all children are done.
   173  	close(c.closed)            // signal that we're shut down (Closed)
   174  }
   175  
   176  // if parent context is shut down before we call Close explicitly,
   177  // we need to go through the Close motions anyway. Hence all the sync
   178  // stuff all over the place...
   179  func (c *contextCloser) closeOnContextDone() {
   180  	<-c.Context().Done() // wait until parent (context) is done.
   181  	c.internalClose()
   182  	c.Children().Done()
   183  }