github.com/technosophos/deis@v1.7.1-0.20150915173815-f9005256004b/Godeps/_workspace/src/gopkg.in/tomb.v1/tomb.go (about) 1 // Copyright (c) 2011 - Gustavo Niemeyer <gustavo@niemeyer.net> 2 // 3 // All rights reserved. 4 // 5 // Redistribution and use in source and binary forms, with or without 6 // modification, are permitted provided that the following conditions are met: 7 // 8 // * Redistributions of source code must retain the above copyright notice, 9 // this list of conditions and the following disclaimer. 10 // * Redistributions in binary form must reproduce the above copyright notice, 11 // this list of conditions and the following disclaimer in the documentation 12 // and/or other materials provided with the distribution. 13 // * Neither the name of the copyright holder nor the names of its 14 // contributors may be used to endorse or promote products derived from 15 // this software without specific prior written permission. 16 // 17 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 21 // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 22 // EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 23 // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 24 // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 25 // LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 26 // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 27 // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 29 // The tomb package offers a conventional API for clean goroutine termination. 30 // 31 // A Tomb tracks the lifecycle of a goroutine as alive, dying or dead, 32 // and the reason for its death. 33 // 34 // The zero value of a Tomb assumes that a goroutine is about to be 35 // created or already alive. Once Kill or Killf is called with an 36 // argument that informs the reason for death, the goroutine is in 37 // a dying state and is expected to terminate soon. Right before the 38 // goroutine function or method returns, Done must be called to inform 39 // that the goroutine is indeed dead and about to stop running. 40 // 41 // A Tomb exposes Dying and Dead channels. These channels are closed 42 // when the Tomb state changes in the respective way. They enable 43 // explicit blocking until the state changes, and also to selectively 44 // unblock select statements accordingly. 45 // 46 // When the tomb state changes to dying and there's still logic going 47 // on within the goroutine, nested functions and methods may choose to 48 // return ErrDying as their error value, as this error won't alter the 49 // tomb state if provided to the Kill method. This is a convenient way to 50 // follow standard Go practices in the context of a dying tomb. 51 // 52 // For background and a detailed example, see the following blog post: 53 // 54 // http://blog.labix.org/2011/10/09/death-of-goroutines-under-control 55 // 56 // For a more complex code snippet demonstrating the use of multiple 57 // goroutines with a single Tomb, see: 58 // 59 // http://play.golang.org/p/Xh7qWsDPZP 60 // 61 package tomb 62 63 import ( 64 "errors" 65 "fmt" 66 "sync" 67 ) 68 69 // A Tomb tracks the lifecycle of a goroutine as alive, dying or dead, 70 // and the reason for its death. 71 // 72 // See the package documentation for details. 73 type Tomb struct { 74 m sync.Mutex 75 dying chan struct{} 76 dead chan struct{} 77 reason error 78 } 79 80 var ( 81 ErrStillAlive = errors.New("tomb: still alive") 82 ErrDying = errors.New("tomb: dying") 83 ) 84 85 func (t *Tomb) init() { 86 t.m.Lock() 87 if t.dead == nil { 88 t.dead = make(chan struct{}) 89 t.dying = make(chan struct{}) 90 t.reason = ErrStillAlive 91 } 92 t.m.Unlock() 93 } 94 95 // Dead returns the channel that can be used to wait 96 // until t.Done has been called. 97 func (t *Tomb) Dead() <-chan struct{} { 98 t.init() 99 return t.dead 100 } 101 102 // Dying returns the channel that can be used to wait 103 // until t.Kill or t.Done has been called. 104 func (t *Tomb) Dying() <-chan struct{} { 105 t.init() 106 return t.dying 107 } 108 109 // Wait blocks until the goroutine is in a dead state and returns the 110 // reason for its death. 111 func (t *Tomb) Wait() error { 112 t.init() 113 <-t.dead 114 t.m.Lock() 115 reason := t.reason 116 t.m.Unlock() 117 return reason 118 } 119 120 // Done flags the goroutine as dead, and should be called a single time 121 // right before the goroutine function or method returns. 122 // If the goroutine was not already in a dying state before Done is 123 // called, it will be flagged as dying and dead at once with no 124 // error. 125 func (t *Tomb) Done() { 126 t.Kill(nil) 127 close(t.dead) 128 } 129 130 // Kill flags the goroutine as dying for the given reason. 131 // Kill may be called multiple times, but only the first 132 // non-nil error is recorded as the reason for termination. 133 // 134 // If reason is ErrDying, the previous reason isn't replaced 135 // even if it is nil. It's a runtime error to call Kill with 136 // ErrDying if t is not in a dying state. 137 func (t *Tomb) Kill(reason error) { 138 t.init() 139 t.m.Lock() 140 defer t.m.Unlock() 141 if reason == ErrDying { 142 if t.reason == ErrStillAlive { 143 panic("tomb: Kill with ErrDying while still alive") 144 } 145 return 146 } 147 if t.reason == nil || t.reason == ErrStillAlive { 148 t.reason = reason 149 } 150 // If the receive on t.dying succeeds, then 151 // it can only be because we have already closed it. 152 // If it blocks, then we know that it needs to be closed. 153 select { 154 case <-t.dying: 155 default: 156 close(t.dying) 157 } 158 } 159 160 // Killf works like Kill, but builds the reason providing the received 161 // arguments to fmt.Errorf. The generated error is also returned. 162 func (t *Tomb) Killf(f string, a ...interface{}) error { 163 err := fmt.Errorf(f, a...) 164 t.Kill(err) 165 return err 166 } 167 168 // Err returns the reason for the goroutine death provided via Kill 169 // or Killf, or ErrStillAlive when the goroutine is still alive. 170 func (t *Tomb) Err() (reason error) { 171 t.init() 172 t.m.Lock() 173 reason = t.reason 174 t.m.Unlock() 175 return 176 }