forked from TrueCloudLab/restic
Vendor gopkg.in/tomb.v2
This commit is contained in:
parent
1af96fc6dd
commit
c703d21d55
9 changed files with 961 additions and 1 deletions
8
Gopkg.lock
generated
8
Gopkg.lock
generated
|
@ -235,6 +235,12 @@
|
|||
revision = "150dc57a1b433e64154302bdc40b6bb8aefa313a"
|
||||
version = "v1.0.0"
|
||||
|
||||
[[projects]]
|
||||
branch = "v2"
|
||||
name = "gopkg.in/tomb.v2"
|
||||
packages = ["."]
|
||||
revision = "d5d1b5820637886def9eef33e03a27a9f166942c"
|
||||
|
||||
[[projects]]
|
||||
name = "gopkg.in/yaml.v2"
|
||||
packages = ["."]
|
||||
|
@ -244,6 +250,6 @@
|
|||
[solve-meta]
|
||||
analyzer-name = "dep"
|
||||
analyzer-version = 1
|
||||
inputs-digest = "a270b39954b9dad18c46f097be5816ca58ae1f387940b673b387d30934ce4ed4"
|
||||
inputs-digest = "44a8f2ed127a6eaa38c1449b97d298fc703c961617bd93565b89bcc6c9a41483"
|
||||
solver-name = "gps-cdcl"
|
||||
solver-version = 1
|
||||
|
|
29
vendor/gopkg.in/tomb.v2/LICENSE
generated
vendored
Normal file
29
vendor/gopkg.in/tomb.v2/LICENSE
generated
vendored
Normal file
|
@ -0,0 +1,29 @@
|
|||
tomb - support for clean goroutine termination in Go.
|
||||
|
||||
Copyright (c) 2010-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.
|
4
vendor/gopkg.in/tomb.v2/README.md
generated
vendored
Normal file
4
vendor/gopkg.in/tomb.v2/README.md
generated
vendored
Normal file
|
@ -0,0 +1,4 @@
|
|||
Installation and usage
|
||||
----------------------
|
||||
|
||||
See [gopkg.in/tomb.v2](https://gopkg.in/tomb.v2) for documentation and usage details.
|
74
vendor/gopkg.in/tomb.v2/context.go
generated
vendored
Normal file
74
vendor/gopkg.in/tomb.v2/context.go
generated
vendored
Normal file
|
@ -0,0 +1,74 @@
|
|||
// +build go1.7
|
||||
|
||||
package tomb
|
||||
|
||||
import (
|
||||
"context"
|
||||
)
|
||||
|
||||
// WithContext returns a new tomb that is killed when the provided parent
|
||||
// context is canceled, and a copy of parent with a replaced Done channel
|
||||
// that is closed when either the tomb is dying or the parent is canceled.
|
||||
// The returned context may also be obtained via the tomb's Context method.
|
||||
func WithContext(parent context.Context) (*Tomb, context.Context) {
|
||||
var t Tomb
|
||||
t.init()
|
||||
if parent.Done() != nil {
|
||||
go func() {
|
||||
select {
|
||||
case <-t.Dying():
|
||||
case <-parent.Done():
|
||||
t.Kill(parent.Err())
|
||||
}
|
||||
}()
|
||||
}
|
||||
t.parent = parent
|
||||
child, cancel := context.WithCancel(parent)
|
||||
t.addChild(parent, child, cancel)
|
||||
return &t, child
|
||||
}
|
||||
|
||||
// Context returns a context that is a copy of the provided parent context with
|
||||
// a replaced Done channel that is closed when either the tomb is dying or the
|
||||
// parent is cancelled.
|
||||
//
|
||||
// If parent is nil, it defaults to the parent provided via WithContext, or an
|
||||
// empty background parent if the tomb wasn't created via WithContext.
|
||||
func (t *Tomb) Context(parent context.Context) context.Context {
|
||||
t.init()
|
||||
t.m.Lock()
|
||||
defer t.m.Unlock()
|
||||
|
||||
if parent == nil {
|
||||
if t.parent == nil {
|
||||
t.parent = context.Background()
|
||||
}
|
||||
parent = t.parent.(context.Context)
|
||||
}
|
||||
|
||||
if child, ok := t.child[parent]; ok {
|
||||
return child.context.(context.Context)
|
||||
}
|
||||
|
||||
child, cancel := context.WithCancel(parent)
|
||||
t.addChild(parent, child, cancel)
|
||||
return child
|
||||
}
|
||||
|
||||
func (t *Tomb) addChild(parent context.Context, child context.Context, cancel func()) {
|
||||
if t.reason != ErrStillAlive {
|
||||
cancel()
|
||||
return
|
||||
}
|
||||
if t.child == nil {
|
||||
t.child = make(map[interface{}]childContext)
|
||||
}
|
||||
t.child[parent] = childContext{child, cancel, child.Done()}
|
||||
for parent, child := range t.child {
|
||||
select {
|
||||
case <-child.done:
|
||||
delete(t.child, parent)
|
||||
default:
|
||||
}
|
||||
}
|
||||
}
|
74
vendor/gopkg.in/tomb.v2/context16.go
generated
vendored
Normal file
74
vendor/gopkg.in/tomb.v2/context16.go
generated
vendored
Normal file
|
@ -0,0 +1,74 @@
|
|||
// +build !go1.7
|
||||
|
||||
package tomb
|
||||
|
||||
import (
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
// WithContext returns a new tomb that is killed when the provided parent
|
||||
// context is canceled, and a copy of parent with a replaced Done channel
|
||||
// that is closed when either the tomb is dying or the parent is canceled.
|
||||
// The returned context may also be obtained via the tomb's Context method.
|
||||
func WithContext(parent context.Context) (*Tomb, context.Context) {
|
||||
var t Tomb
|
||||
t.init()
|
||||
if parent.Done() != nil {
|
||||
go func() {
|
||||
select {
|
||||
case <-t.Dying():
|
||||
case <-parent.Done():
|
||||
t.Kill(parent.Err())
|
||||
}
|
||||
}()
|
||||
}
|
||||
t.parent = parent
|
||||
child, cancel := context.WithCancel(parent)
|
||||
t.addChild(parent, child, cancel)
|
||||
return &t, child
|
||||
}
|
||||
|
||||
// Context returns a context that is a copy of the provided parent context with
|
||||
// a replaced Done channel that is closed when either the tomb is dying or the
|
||||
// parent is cancelled.
|
||||
//
|
||||
// If parent is nil, it defaults to the parent provided via WithContext, or an
|
||||
// empty background parent if the tomb wasn't created via WithContext.
|
||||
func (t *Tomb) Context(parent context.Context) context.Context {
|
||||
t.init()
|
||||
t.m.Lock()
|
||||
defer t.m.Unlock()
|
||||
|
||||
if parent == nil {
|
||||
if t.parent == nil {
|
||||
t.parent = context.Background()
|
||||
}
|
||||
parent = t.parent.(context.Context)
|
||||
}
|
||||
|
||||
if child, ok := t.child[parent]; ok {
|
||||
return child.context.(context.Context)
|
||||
}
|
||||
|
||||
child, cancel := context.WithCancel(parent)
|
||||
t.addChild(parent, child, cancel)
|
||||
return child
|
||||
}
|
||||
|
||||
func (t *Tomb) addChild(parent context.Context, child context.Context, cancel func()) {
|
||||
if t.reason != ErrStillAlive {
|
||||
cancel()
|
||||
return
|
||||
}
|
||||
if t.child == nil {
|
||||
t.child = make(map[interface{}]childContext)
|
||||
}
|
||||
t.child[parent] = childContext{child, cancel, child.Done()}
|
||||
for parent, child := range t.child {
|
||||
select {
|
||||
case <-child.done:
|
||||
delete(t.child, parent)
|
||||
default:
|
||||
}
|
||||
}
|
||||
}
|
177
vendor/gopkg.in/tomb.v2/context16_test.go
generated
vendored
Normal file
177
vendor/gopkg.in/tomb.v2/context16_test.go
generated
vendored
Normal file
|
@ -0,0 +1,177 @@
|
|||
// +build !go1.7
|
||||
|
||||
package tomb_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
|
||||
"gopkg.in/tomb.v2"
|
||||
)
|
||||
|
||||
func TestWithContext(t *testing.T) {
|
||||
parent1, cancel1 := context.WithCancel(context.Background())
|
||||
|
||||
tb, child1 := tomb.WithContext(parent1)
|
||||
|
||||
if !tb.Alive() {
|
||||
t.Fatalf("WithContext returned dead tomb")
|
||||
}
|
||||
if tb.Context(parent1) != child1 {
|
||||
t.Fatalf("Context returned different context for same parent")
|
||||
}
|
||||
if tb.Context(nil) != child1 {
|
||||
t.Fatalf("Context returned different context for nil parent")
|
||||
}
|
||||
select {
|
||||
case <-child1.Done():
|
||||
t.Fatalf("Tomb's child context was born dead")
|
||||
default:
|
||||
}
|
||||
|
||||
parent2, cancel2 := context.WithCancel(context.WithValue(context.Background(), "parent", "parent2"))
|
||||
child2 := tb.Context(parent2)
|
||||
|
||||
if tb.Context(parent2) != child2 {
|
||||
t.Fatalf("Context returned different context for same parent")
|
||||
}
|
||||
if child2.Value("parent") != "parent2" {
|
||||
t.Fatalf("Child context didn't inherit its parent's properties")
|
||||
}
|
||||
select {
|
||||
case <-child2.Done():
|
||||
t.Fatalf("Tomb's child context was born dead")
|
||||
default:
|
||||
}
|
||||
|
||||
cancel2()
|
||||
|
||||
select {
|
||||
case <-child2.Done():
|
||||
case <-time.After(5 * time.Second):
|
||||
t.Fatalf("Tomb's child context didn't die after parent was canceled")
|
||||
}
|
||||
if !tb.Alive() {
|
||||
t.Fatalf("Canceling unrelated parent context killed tomb")
|
||||
}
|
||||
|
||||
parent3 := context.WithValue(context.Background(), "parent", "parent3")
|
||||
child3 := tb.Context(parent3)
|
||||
|
||||
if child3.Value("parent") != "parent3" {
|
||||
t.Fatalf("Child context didn't inherit its parent's properties")
|
||||
}
|
||||
select {
|
||||
case <-child3.Done():
|
||||
t.Fatalf("Tomb's child context was born dead")
|
||||
default:
|
||||
}
|
||||
|
||||
cancel1()
|
||||
|
||||
select {
|
||||
case <-tb.Dying():
|
||||
case <-time.After(5 * time.Second):
|
||||
t.Fatalf("Canceling parent context did not kill tomb")
|
||||
}
|
||||
|
||||
if tb.Err() != context.Canceled {
|
||||
t.Fatalf("tomb should be %v, got %v", context.Canceled, tb.Err())
|
||||
}
|
||||
|
||||
if tb.Context(parent1) == child1 || tb.Context(parent3) == child3 {
|
||||
t.Fatalf("Tomb is dead and shouldn't be tracking children anymore")
|
||||
}
|
||||
select {
|
||||
case <-child3.Done():
|
||||
case <-time.After(5 * time.Second):
|
||||
t.Fatalf("Child context didn't die after tomb's death")
|
||||
}
|
||||
|
||||
parent4 := context.WithValue(context.Background(), "parent", "parent4")
|
||||
child4 := tb.Context(parent4)
|
||||
|
||||
select {
|
||||
case <-child4.Done():
|
||||
case <-time.After(5 * time.Second):
|
||||
t.Fatalf("Child context should be born canceled")
|
||||
}
|
||||
|
||||
childnil := tb.Context(nil)
|
||||
select {
|
||||
case <-childnil.Done():
|
||||
default:
|
||||
t.Fatalf("Child context should be born canceled")
|
||||
}
|
||||
}
|
||||
|
||||
func TestContextNoParent(t *testing.T) {
|
||||
var tb tomb.Tomb
|
||||
|
||||
parent2, cancel2 := context.WithCancel(context.WithValue(context.Background(), "parent", "parent2"))
|
||||
child2 := tb.Context(parent2)
|
||||
|
||||
if tb.Context(parent2) != child2 {
|
||||
t.Fatalf("Context returned different context for same parent")
|
||||
}
|
||||
if child2.Value("parent") != "parent2" {
|
||||
t.Fatalf("Child context didn't inherit its parent's properties")
|
||||
}
|
||||
select {
|
||||
case <-child2.Done():
|
||||
t.Fatalf("Tomb's child context was born dead")
|
||||
default:
|
||||
}
|
||||
|
||||
cancel2()
|
||||
|
||||
select {
|
||||
case <-child2.Done():
|
||||
default:
|
||||
t.Fatalf("Tomb's child context didn't die after parent was canceled")
|
||||
}
|
||||
if !tb.Alive() {
|
||||
t.Fatalf("Canceling unrelated parent context killed tomb")
|
||||
}
|
||||
|
||||
parent3 := context.WithValue(context.Background(), "parent", "parent3")
|
||||
child3 := tb.Context(parent3)
|
||||
|
||||
if child3.Value("parent") != "parent3" {
|
||||
t.Fatalf("Child context didn't inherit its parent's properties")
|
||||
}
|
||||
select {
|
||||
case <-child3.Done():
|
||||
t.Fatalf("Tomb's child context was born dead")
|
||||
default:
|
||||
}
|
||||
|
||||
tb.Kill(nil)
|
||||
|
||||
if tb.Context(parent3) == child3 {
|
||||
t.Fatalf("Tomb is dead and shouldn't be tracking children anymore")
|
||||
}
|
||||
select {
|
||||
case <-child3.Done():
|
||||
default:
|
||||
t.Fatalf("Child context didn't die after tomb's death")
|
||||
}
|
||||
|
||||
parent4 := context.WithValue(context.Background(), "parent", "parent4")
|
||||
child4 := tb.Context(parent4)
|
||||
|
||||
select {
|
||||
case <-child4.Done():
|
||||
default:
|
||||
t.Fatalf("Child context should be born canceled")
|
||||
}
|
||||
|
||||
childnil := tb.Context(nil)
|
||||
select {
|
||||
case <-childnil.Done():
|
||||
default:
|
||||
t.Fatalf("Child context should be born canceled")
|
||||
}
|
||||
}
|
176
vendor/gopkg.in/tomb.v2/context_test.go
generated
vendored
Normal file
176
vendor/gopkg.in/tomb.v2/context_test.go
generated
vendored
Normal file
|
@ -0,0 +1,176 @@
|
|||
// +build go1.7
|
||||
|
||||
package tomb_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"gopkg.in/tomb.v2"
|
||||
)
|
||||
|
||||
func TestWithContext(t *testing.T) {
|
||||
parent1, cancel1 := context.WithCancel(context.Background())
|
||||
|
||||
tb, child1 := tomb.WithContext(parent1)
|
||||
|
||||
if !tb.Alive() {
|
||||
t.Fatalf("WithContext returned dead tomb")
|
||||
}
|
||||
if tb.Context(parent1) != child1 {
|
||||
t.Fatalf("Context returned different context for same parent")
|
||||
}
|
||||
if tb.Context(nil) != child1 {
|
||||
t.Fatalf("Context returned different context for nil parent")
|
||||
}
|
||||
select {
|
||||
case <-child1.Done():
|
||||
t.Fatalf("Tomb's child context was born dead")
|
||||
default:
|
||||
}
|
||||
|
||||
parent2, cancel2 := context.WithCancel(context.WithValue(context.Background(), "parent", "parent2"))
|
||||
child2 := tb.Context(parent2)
|
||||
|
||||
if tb.Context(parent2) != child2 {
|
||||
t.Fatalf("Context returned different context for same parent")
|
||||
}
|
||||
if child2.Value("parent") != "parent2" {
|
||||
t.Fatalf("Child context didn't inherit its parent's properties")
|
||||
}
|
||||
select {
|
||||
case <-child2.Done():
|
||||
t.Fatalf("Tomb's child context was born dead")
|
||||
default:
|
||||
}
|
||||
|
||||
cancel2()
|
||||
|
||||
select {
|
||||
case <-child2.Done():
|
||||
default:
|
||||
t.Fatalf("Tomb's child context didn't die after parent was canceled")
|
||||
}
|
||||
if !tb.Alive() {
|
||||
t.Fatalf("Canceling unrelated parent context killed tomb")
|
||||
}
|
||||
|
||||
parent3 := context.WithValue(context.Background(), "parent", "parent3")
|
||||
child3 := tb.Context(parent3)
|
||||
|
||||
if child3.Value("parent") != "parent3" {
|
||||
t.Fatalf("Child context didn't inherit its parent's properties")
|
||||
}
|
||||
select {
|
||||
case <-child3.Done():
|
||||
t.Fatalf("Tomb's child context was born dead")
|
||||
default:
|
||||
}
|
||||
|
||||
cancel1()
|
||||
|
||||
select {
|
||||
case <-tb.Dying():
|
||||
case <-time.After(5 * time.Second):
|
||||
t.Fatalf("Canceling parent context did not kill tomb")
|
||||
}
|
||||
|
||||
if tb.Err() != context.Canceled {
|
||||
t.Fatalf("tomb should be %v, got %v", context.Canceled, tb.Err())
|
||||
}
|
||||
|
||||
if tb.Context(parent1) == child1 || tb.Context(parent3) == child3 {
|
||||
t.Fatalf("Tomb is dead and shouldn't be tracking children anymore")
|
||||
}
|
||||
select {
|
||||
case <-child3.Done():
|
||||
default:
|
||||
t.Fatalf("Child context didn't die after tomb's death")
|
||||
}
|
||||
|
||||
parent4 := context.WithValue(context.Background(), "parent", "parent4")
|
||||
child4 := tb.Context(parent4)
|
||||
|
||||
select {
|
||||
case <-child4.Done():
|
||||
default:
|
||||
t.Fatalf("Child context should be born canceled")
|
||||
}
|
||||
|
||||
childnil := tb.Context(nil)
|
||||
select {
|
||||
case <-childnil.Done():
|
||||
default:
|
||||
t.Fatalf("Child context should be born canceled")
|
||||
}
|
||||
}
|
||||
|
||||
func TestContextNoParent(t *testing.T) {
|
||||
var tb tomb.Tomb
|
||||
|
||||
parent2, cancel2 := context.WithCancel(context.WithValue(context.Background(), "parent", "parent2"))
|
||||
child2 := tb.Context(parent2)
|
||||
|
||||
if tb.Context(parent2) != child2 {
|
||||
t.Fatalf("Context returned different context for same parent")
|
||||
}
|
||||
if child2.Value("parent") != "parent2" {
|
||||
t.Fatalf("Child context didn't inherit its parent's properties")
|
||||
}
|
||||
select {
|
||||
case <-child2.Done():
|
||||
t.Fatalf("Tomb's child context was born dead")
|
||||
default:
|
||||
}
|
||||
|
||||
cancel2()
|
||||
|
||||
select {
|
||||
case <-child2.Done():
|
||||
default:
|
||||
t.Fatalf("Tomb's child context didn't die after parent was canceled")
|
||||
}
|
||||
if !tb.Alive() {
|
||||
t.Fatalf("Canceling unrelated parent context killed tomb")
|
||||
}
|
||||
|
||||
parent3 := context.WithValue(context.Background(), "parent", "parent3")
|
||||
child3 := tb.Context(parent3)
|
||||
|
||||
if child3.Value("parent") != "parent3" {
|
||||
t.Fatalf("Child context didn't inherit its parent's properties")
|
||||
}
|
||||
select {
|
||||
case <-child3.Done():
|
||||
t.Fatalf("Tomb's child context was born dead")
|
||||
default:
|
||||
}
|
||||
|
||||
tb.Kill(nil)
|
||||
|
||||
if tb.Context(parent3) == child3 {
|
||||
t.Fatalf("Tomb is dead and shouldn't be tracking children anymore")
|
||||
}
|
||||
select {
|
||||
case <-child3.Done():
|
||||
default:
|
||||
t.Fatalf("Child context didn't die after tomb's death")
|
||||
}
|
||||
|
||||
parent4 := context.WithValue(context.Background(), "parent", "parent4")
|
||||
child4 := tb.Context(parent4)
|
||||
|
||||
select {
|
||||
case <-child4.Done():
|
||||
default:
|
||||
t.Fatalf("Child context should be born canceled")
|
||||
}
|
||||
|
||||
childnil := tb.Context(nil)
|
||||
select {
|
||||
case <-childnil.Done():
|
||||
default:
|
||||
t.Fatalf("Child context should be born canceled")
|
||||
}
|
||||
}
|
237
vendor/gopkg.in/tomb.v2/tomb.go
generated
vendored
Normal file
237
vendor/gopkg.in/tomb.v2/tomb.go
generated
vendored
Normal file
|
@ -0,0 +1,237 @@
|
|||
// 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 handles clean goroutine tracking and termination.
|
||||
//
|
||||
// The zero value of a Tomb is ready to handle the creation of a tracked
|
||||
// goroutine via its Go method, and then any tracked goroutine may call
|
||||
// the Go method again to create additional tracked goroutines at
|
||||
// any point.
|
||||
//
|
||||
// If any of the tracked goroutines returns a non-nil error, or the
|
||||
// Kill or Killf method is called by any goroutine in the system (tracked
|
||||
// or not), the tomb Err is set, Alive is set to false, and the Dying
|
||||
// channel is closed to flag that all tracked goroutines are supposed
|
||||
// to willingly terminate as soon as possible.
|
||||
//
|
||||
// Once all tracked goroutines terminate, the Dead channel is closed,
|
||||
// and Wait unblocks and returns the first non-nil error presented
|
||||
// to the tomb via a result or an explicit Kill or Killf method call,
|
||||
// or nil if there were no errors.
|
||||
//
|
||||
// It is okay to create further goroutines via the Go method while
|
||||
// the tomb is in a dying state. The final dead state is only reached
|
||||
// once all tracked goroutines terminate, at which point calling
|
||||
// the Go method again will cause a runtime panic.
|
||||
//
|
||||
// Tracked functions and methods that are still running while the tomb
|
||||
// is in dying state may choose to return ErrDying as their error value.
|
||||
// This preserves the well established non-nil error convention, but is
|
||||
// understood by the tomb as a clean termination. The Err and Wait
|
||||
// methods will still return nil if all observed errors were either
|
||||
// nil or ErrDying.
|
||||
//
|
||||
// For background and a detailed example, see the following blog post:
|
||||
//
|
||||
// http://blog.labix.org/2011/10/09/death-of-goroutines-under-control
|
||||
//
|
||||
package tomb
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// A Tomb tracks the lifecycle of one or more goroutines as alive,
|
||||
// dying or dead, and the reason for their death.
|
||||
//
|
||||
// See the package documentation for details.
|
||||
type Tomb struct {
|
||||
m sync.Mutex
|
||||
alive int
|
||||
dying chan struct{}
|
||||
dead chan struct{}
|
||||
reason error
|
||||
|
||||
// context.Context is available in Go 1.7+.
|
||||
parent interface{}
|
||||
child map[interface{}]childContext
|
||||
}
|
||||
|
||||
type childContext struct {
|
||||
context interface{}
|
||||
cancel func()
|
||||
done <-chan struct{}
|
||||
}
|
||||
|
||||
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
|
||||
// all goroutines have finished running.
|
||||
func (t *Tomb) Dead() <-chan struct{} {
|
||||
t.init()
|
||||
return t.dead
|
||||
}
|
||||
|
||||
// Dying returns the channel that can be used to wait until
|
||||
// t.Kill is called.
|
||||
func (t *Tomb) Dying() <-chan struct{} {
|
||||
t.init()
|
||||
return t.dying
|
||||
}
|
||||
|
||||
// Wait blocks until all goroutines have finished running, and
|
||||
// then returns the reason for their death.
|
||||
func (t *Tomb) Wait() error {
|
||||
t.init()
|
||||
<-t.dead
|
||||
t.m.Lock()
|
||||
reason := t.reason
|
||||
t.m.Unlock()
|
||||
return reason
|
||||
}
|
||||
|
||||
// Go runs f in a new goroutine and tracks its termination.
|
||||
//
|
||||
// If f returns a non-nil error, t.Kill is called with that
|
||||
// error as the death reason parameter.
|
||||
//
|
||||
// It is f's responsibility to monitor the tomb and return
|
||||
// appropriately once it is in a dying state.
|
||||
//
|
||||
// It is safe for the f function to call the Go method again
|
||||
// to create additional tracked goroutines. Once all tracked
|
||||
// goroutines return, the Dead channel is closed and the
|
||||
// Wait method unblocks and returns the death reason.
|
||||
//
|
||||
// Calling the Go method after all tracked goroutines return
|
||||
// causes a runtime panic. For that reason, calling the Go
|
||||
// method a second time out of a tracked goroutine is unsafe.
|
||||
func (t *Tomb) Go(f func() error) {
|
||||
t.init()
|
||||
t.m.Lock()
|
||||
defer t.m.Unlock()
|
||||
select {
|
||||
case <-t.dead:
|
||||
panic("tomb.Go called after all goroutines terminated")
|
||||
default:
|
||||
}
|
||||
t.alive++
|
||||
go t.run(f)
|
||||
}
|
||||
|
||||
func (t *Tomb) run(f func() error) {
|
||||
err := f()
|
||||
t.m.Lock()
|
||||
defer t.m.Unlock()
|
||||
t.alive--
|
||||
if t.alive == 0 || err != nil {
|
||||
t.kill(err)
|
||||
if t.alive == 0 {
|
||||
close(t.dead)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Kill puts the tomb in a dying state for the given reason,
|
||||
// closes the Dying channel, and sets Alive to false.
|
||||
//
|
||||
// Althoguh Kill may be called multiple times, only the first
|
||||
// non-nil error is recorded as the death reason.
|
||||
//
|
||||
// If reason is ErrDying, the previous reason isn't replaced
|
||||
// even if 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()
|
||||
t.kill(reason)
|
||||
}
|
||||
|
||||
func (t *Tomb) kill(reason error) {
|
||||
if reason == ErrStillAlive {
|
||||
panic("tomb: Kill with ErrStillAlive")
|
||||
}
|
||||
if reason == ErrDying {
|
||||
if t.reason == ErrStillAlive {
|
||||
panic("tomb: Kill with ErrDying while still alive")
|
||||
}
|
||||
return
|
||||
}
|
||||
if t.reason == ErrStillAlive {
|
||||
t.reason = reason
|
||||
close(t.dying)
|
||||
for _, child := range t.child {
|
||||
child.cancel()
|
||||
}
|
||||
t.child = nil
|
||||
return
|
||||
}
|
||||
if t.reason == nil {
|
||||
t.reason = reason
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Killf calls the Kill method with an error built providing the received
|
||||
// parameters 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 death reason, or ErrStillAlive if the tomb
|
||||
// is not in a dying or dead state.
|
||||
func (t *Tomb) Err() (reason error) {
|
||||
t.init()
|
||||
t.m.Lock()
|
||||
reason = t.reason
|
||||
t.m.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
// Alive returns true if the tomb is not in a dying or dead state.
|
||||
func (t *Tomb) Alive() bool {
|
||||
return t.Err() == ErrStillAlive
|
||||
}
|
183
vendor/gopkg.in/tomb.v2/tomb_test.go
generated
vendored
Normal file
183
vendor/gopkg.in/tomb.v2/tomb_test.go
generated
vendored
Normal file
|
@ -0,0 +1,183 @@
|
|||
package tomb_test
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"gopkg.in/tomb.v2"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func nothing() error { return nil }
|
||||
|
||||
func TestNewTomb(t *testing.T) {
|
||||
tb := &tomb.Tomb{}
|
||||
checkState(t, tb, false, false, tomb.ErrStillAlive)
|
||||
}
|
||||
|
||||
func TestGo(t *testing.T) {
|
||||
tb := &tomb.Tomb{}
|
||||
alive := make(chan bool)
|
||||
tb.Go(func() error {
|
||||
alive <- true
|
||||
tb.Go(func() error {
|
||||
alive <- true
|
||||
<-tb.Dying()
|
||||
return nil
|
||||
})
|
||||
<-tb.Dying()
|
||||
return nil
|
||||
})
|
||||
<-alive
|
||||
<-alive
|
||||
checkState(t, tb, false, false, tomb.ErrStillAlive)
|
||||
tb.Kill(nil)
|
||||
tb.Wait()
|
||||
checkState(t, tb, true, true, nil)
|
||||
}
|
||||
|
||||
func TestGoErr(t *testing.T) {
|
||||
first := errors.New("first error")
|
||||
second := errors.New("first error")
|
||||
tb := &tomb.Tomb{}
|
||||
alive := make(chan bool)
|
||||
tb.Go(func() error {
|
||||
alive <- true
|
||||
tb.Go(func() error {
|
||||
alive <- true
|
||||
return first
|
||||
})
|
||||
<-tb.Dying()
|
||||
return second
|
||||
})
|
||||
<-alive
|
||||
<-alive
|
||||
tb.Wait()
|
||||
checkState(t, tb, true, true, first)
|
||||
}
|
||||
|
||||
func TestGoPanic(t *testing.T) {
|
||||
// ErrDying being used properly, after a clean death.
|
||||
tb := &tomb.Tomb{}
|
||||
tb.Go(nothing)
|
||||
tb.Wait()
|
||||
defer func() {
|
||||
err := recover()
|
||||
if err != "tomb.Go called after all goroutines terminated" {
|
||||
t.Fatalf("Wrong panic on post-death tomb.Go call: %v", err)
|
||||
}
|
||||
checkState(t, tb, true, true, nil)
|
||||
}()
|
||||
tb.Go(nothing)
|
||||
}
|
||||
|
||||
func TestKill(t *testing.T) {
|
||||
// a nil reason flags the goroutine as dying
|
||||
tb := &tomb.Tomb{}
|
||||
tb.Kill(nil)
|
||||
checkState(t, tb, true, false, nil)
|
||||
|
||||
// a non-nil reason now will override Kill
|
||||
err := errors.New("some error")
|
||||
tb.Kill(err)
|
||||
checkState(t, tb, true, false, err)
|
||||
|
||||
// another non-nil reason won't replace the first one
|
||||
tb.Kill(errors.New("ignore me"))
|
||||
checkState(t, tb, true, false, err)
|
||||
|
||||
tb.Go(nothing)
|
||||
tb.Wait()
|
||||
checkState(t, tb, true, true, err)
|
||||
}
|
||||
|
||||
func TestKillf(t *testing.T) {
|
||||
tb := &tomb.Tomb{}
|
||||
|
||||
err := tb.Killf("BO%s", "OM")
|
||||
if s := err.Error(); s != "BOOM" {
|
||||
t.Fatalf(`Killf("BO%s", "OM"): want "BOOM", got %q`, s)
|
||||
}
|
||||
checkState(t, tb, true, false, err)
|
||||
|
||||
// another non-nil reason won't replace the first one
|
||||
tb.Killf("ignore me")
|
||||
checkState(t, tb, true, false, err)
|
||||
|
||||
tb.Go(nothing)
|
||||
tb.Wait()
|
||||
checkState(t, tb, true, true, err)
|
||||
}
|
||||
|
||||
func TestErrDying(t *testing.T) {
|
||||
// ErrDying being used properly, after a clean death.
|
||||
tb := &tomb.Tomb{}
|
||||
tb.Kill(nil)
|
||||
tb.Kill(tomb.ErrDying)
|
||||
checkState(t, tb, true, false, nil)
|
||||
|
||||
// ErrDying being used properly, after an errorful death.
|
||||
err := errors.New("some error")
|
||||
tb.Kill(err)
|
||||
tb.Kill(tomb.ErrDying)
|
||||
checkState(t, tb, true, false, err)
|
||||
|
||||
// ErrDying being used badly, with an alive tomb.
|
||||
tb = &tomb.Tomb{}
|
||||
defer func() {
|
||||
err := recover()
|
||||
if err != "tomb: Kill with ErrDying while still alive" {
|
||||
t.Fatalf("Wrong panic on Kill(ErrDying): %v", err)
|
||||
}
|
||||
checkState(t, tb, false, false, tomb.ErrStillAlive)
|
||||
}()
|
||||
tb.Kill(tomb.ErrDying)
|
||||
}
|
||||
|
||||
func TestKillErrStillAlivePanic(t *testing.T) {
|
||||
tb := &tomb.Tomb{}
|
||||
defer func() {
|
||||
err := recover()
|
||||
if err != "tomb: Kill with ErrStillAlive" {
|
||||
t.Fatalf("Wrong panic on Kill(ErrStillAlive): %v", err)
|
||||
}
|
||||
checkState(t, tb, false, false, tomb.ErrStillAlive)
|
||||
}()
|
||||
tb.Kill(tomb.ErrStillAlive)
|
||||
}
|
||||
|
||||
func checkState(t *testing.T, tb *tomb.Tomb, wantDying, wantDead bool, wantErr error) {
|
||||
select {
|
||||
case <-tb.Dying():
|
||||
if !wantDying {
|
||||
t.Error("<-Dying: should block")
|
||||
}
|
||||
default:
|
||||
if wantDying {
|
||||
t.Error("<-Dying: should not block")
|
||||
}
|
||||
}
|
||||
seemsDead := false
|
||||
select {
|
||||
case <-tb.Dead():
|
||||
if !wantDead {
|
||||
t.Error("<-Dead: should block")
|
||||
}
|
||||
seemsDead = true
|
||||
default:
|
||||
if wantDead {
|
||||
t.Error("<-Dead: should not block")
|
||||
}
|
||||
}
|
||||
if err := tb.Err(); err != wantErr {
|
||||
t.Errorf("Err: want %#v, got %#v", wantErr, err)
|
||||
}
|
||||
if wantDead && seemsDead {
|
||||
waitErr := tb.Wait()
|
||||
switch {
|
||||
case waitErr == tomb.ErrStillAlive:
|
||||
t.Errorf("Wait should not return ErrStillAlive")
|
||||
case !reflect.DeepEqual(waitErr, wantErr):
|
||||
t.Errorf("Wait: want %#v, got %#v", wantErr, waitErr)
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue