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  }