mirror of https://github.com/containers/podman.git
				
				
				
			
		
			
				
	
	
		
			177 lines
		
	
	
		
			5.7 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			177 lines
		
	
	
		
			5.7 KiB
		
	
	
	
		
			Go
		
	
	
	
// Copyright (c) 2011 - Gustavo Niemeyer <gustavo@niemeyer.net>
 | 
						|
// 
 | 
						|
// All rights reserved.
 | 
						|
// 
 | 
						|
// Redistribution and use in source and binary forms, with or without
 | 
						|
// modification, are permitted provided that the following conditions are met:
 | 
						|
// 
 | 
						|
//     * Redistributions of source code must retain the above copyright notice,
 | 
						|
//       this list of conditions and the following disclaimer.
 | 
						|
//     * Redistributions in binary form must reproduce the above copyright notice,
 | 
						|
//       this list of conditions and the following disclaimer in the documentation
 | 
						|
//       and/or other materials provided with the distribution.
 | 
						|
//     * Neither the name of the copyright holder nor the names of its
 | 
						|
//       contributors may be used to endorse or promote products derived from
 | 
						|
//       this software without specific prior written permission.
 | 
						|
// 
 | 
						|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 | 
						|
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 | 
						|
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 | 
						|
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 | 
						|
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 | 
						|
// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 | 
						|
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 | 
						|
// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 | 
						|
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 | 
						|
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 | 
						|
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | 
						|
 | 
						|
// The tomb package offers a conventional API for clean goroutine termination.
 | 
						|
//
 | 
						|
// A Tomb tracks the lifecycle of a goroutine as alive, dying or dead,
 | 
						|
// and the reason for its death.
 | 
						|
//
 | 
						|
// The zero value of a Tomb assumes that a goroutine is about to be
 | 
						|
// created or already alive. Once Kill or Killf is called with an
 | 
						|
// argument that informs the reason for death, the goroutine is in
 | 
						|
// a dying state and is expected to terminate soon. Right before the
 | 
						|
// goroutine function or method returns, Done must be called to inform
 | 
						|
// that the goroutine is indeed dead and about to stop running.
 | 
						|
//
 | 
						|
// A Tomb exposes Dying and Dead channels. These channels are closed
 | 
						|
// when the Tomb state changes in the respective way. They enable
 | 
						|
// explicit blocking until the state changes, and also to selectively
 | 
						|
// unblock select statements accordingly.
 | 
						|
//
 | 
						|
// When the tomb state changes to dying and there's still logic going
 | 
						|
// on within the goroutine, nested functions and methods may choose to
 | 
						|
// return ErrDying as their error value, as this error won't alter the
 | 
						|
// tomb state if provided to the Kill method. This is a convenient way to
 | 
						|
// follow standard Go practices in the context of a dying tomb.
 | 
						|
//
 | 
						|
// For background and a detailed example, see the following blog post:
 | 
						|
//
 | 
						|
//   http://blog.labix.org/2011/10/09/death-of-goroutines-under-control
 | 
						|
//
 | 
						|
// For a more complex code snippet demonstrating the use of multiple
 | 
						|
// goroutines with a single Tomb, see:
 | 
						|
//
 | 
						|
//   http://play.golang.org/p/Xh7qWsDPZP
 | 
						|
//
 | 
						|
package tomb
 | 
						|
 | 
						|
import (
 | 
						|
	"errors"
 | 
						|
	"fmt"
 | 
						|
	"sync"
 | 
						|
)
 | 
						|
 | 
						|
// A Tomb tracks the lifecycle of a goroutine as alive, dying or dead,
 | 
						|
// and the reason for its death.
 | 
						|
//
 | 
						|
// See the package documentation for details.
 | 
						|
type Tomb struct {
 | 
						|
	m      sync.Mutex
 | 
						|
	dying  chan struct{}
 | 
						|
	dead   chan struct{}
 | 
						|
	reason error
 | 
						|
}
 | 
						|
 | 
						|
var (
 | 
						|
	ErrStillAlive = errors.New("tomb: still alive")
 | 
						|
	ErrDying = errors.New("tomb: dying")
 | 
						|
)
 | 
						|
 | 
						|
func (t *Tomb) init() {
 | 
						|
	t.m.Lock()
 | 
						|
	if t.dead == nil {
 | 
						|
		t.dead = make(chan struct{})
 | 
						|
		t.dying = make(chan struct{})
 | 
						|
		t.reason = ErrStillAlive
 | 
						|
	}
 | 
						|
	t.m.Unlock()
 | 
						|
}
 | 
						|
 | 
						|
// Dead returns the channel that can be used to wait
 | 
						|
// until t.Done has been called.
 | 
						|
func (t *Tomb) Dead() <-chan struct{} {
 | 
						|
	t.init()
 | 
						|
	return t.dead
 | 
						|
}
 | 
						|
 | 
						|
// Dying returns the channel that can be used to wait
 | 
						|
// until t.Kill or t.Done has been called.
 | 
						|
func (t *Tomb) Dying() <-chan struct{} {
 | 
						|
	t.init()
 | 
						|
	return t.dying
 | 
						|
}
 | 
						|
 | 
						|
// Wait blocks until the goroutine is in a dead state and returns the
 | 
						|
// reason for its death.
 | 
						|
func (t *Tomb) Wait() error {
 | 
						|
	t.init()
 | 
						|
	<-t.dead
 | 
						|
	t.m.Lock()
 | 
						|
	reason := t.reason
 | 
						|
	t.m.Unlock()
 | 
						|
	return reason
 | 
						|
}
 | 
						|
 | 
						|
// Done flags the goroutine as dead, and should be called a single time
 | 
						|
// right before the goroutine function or method returns.
 | 
						|
// If the goroutine was not already in a dying state before Done is
 | 
						|
// called, it will be flagged as dying and dead at once with no
 | 
						|
// error.
 | 
						|
func (t *Tomb) Done() {
 | 
						|
	t.Kill(nil)
 | 
						|
	close(t.dead)
 | 
						|
}
 | 
						|
 | 
						|
// Kill flags the goroutine as dying for the given reason.
 | 
						|
// Kill may be called multiple times, but only the first
 | 
						|
// non-nil error is recorded as the reason for termination.
 | 
						|
//
 | 
						|
// If reason is ErrDying, the previous reason isn't replaced
 | 
						|
// even if it is nil. It's a runtime error to call Kill with
 | 
						|
// ErrDying if t is not in a dying state.
 | 
						|
func (t *Tomb) Kill(reason error) {
 | 
						|
	t.init()
 | 
						|
	t.m.Lock()
 | 
						|
	defer t.m.Unlock()
 | 
						|
	if reason == ErrDying {
 | 
						|
		if t.reason == ErrStillAlive {
 | 
						|
			panic("tomb: Kill with ErrDying while still alive")
 | 
						|
		}
 | 
						|
		return
 | 
						|
	}
 | 
						|
	if t.reason == nil || t.reason == ErrStillAlive {
 | 
						|
		t.reason = reason
 | 
						|
	}
 | 
						|
	// If the receive on t.dying succeeds, then
 | 
						|
	// it can only be because we have already closed it.
 | 
						|
	// If it blocks, then we know that it needs to be closed.
 | 
						|
	select {
 | 
						|
	case <-t.dying:
 | 
						|
	default:
 | 
						|
		close(t.dying)
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// Killf works like Kill, but builds the reason providing the received
 | 
						|
// arguments to fmt.Errorf. The generated error is also returned.
 | 
						|
func (t *Tomb) Killf(f string, a ...interface{}) error {
 | 
						|
	err := fmt.Errorf(f, a...)
 | 
						|
	t.Kill(err)
 | 
						|
	return err
 | 
						|
}
 | 
						|
 | 
						|
// Err returns the reason for the goroutine death provided via Kill
 | 
						|
// or Killf, or ErrStillAlive when the goroutine is still alive.
 | 
						|
func (t *Tomb) Err() (reason error) {
 | 
						|
	t.init()
 | 
						|
	t.m.Lock()
 | 
						|
	reason = t.reason
 | 
						|
	t.m.Unlock()
 | 
						|
	return
 | 
						|
}
 |