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 }