mirror of
https://github.com/ipfs/kubo.git
synced 2026-02-21 10:27:46 +08:00
remove goprocess from godeps, use gx vendored one
License: MIT Signed-off-by: Jeromy <jeromyj@gmail.com>
This commit is contained in:
parent
b9d055c048
commit
3faedb5208
3
Godeps/_workspace/src/github.com/ipfs/go-datastore/leveldb/datastore.go
generated
vendored
3
Godeps/_workspace/src/github.com/ipfs/go-datastore/leveldb/datastore.go
generated
vendored
@ -3,10 +3,11 @@ package leveldb
|
||||
import (
|
||||
ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore"
|
||||
dsq "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore/query"
|
||||
"github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess"
|
||||
"github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/syndtr/goleveldb/leveldb"
|
||||
"github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/syndtr/goleveldb/leveldb/opt"
|
||||
"github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/syndtr/goleveldb/leveldb/util"
|
||||
|
||||
"gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess"
|
||||
)
|
||||
|
||||
type datastore struct {
|
||||
|
||||
2
Godeps/_workspace/src/github.com/ipfs/go-datastore/query/query.go
generated
vendored
2
Godeps/_workspace/src/github.com/ipfs/go-datastore/query/query.go
generated
vendored
@ -1,7 +1,7 @@
|
||||
package query
|
||||
|
||||
import (
|
||||
goprocess "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess"
|
||||
goprocess "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess"
|
||||
)
|
||||
|
||||
/*
|
||||
|
||||
10
Godeps/_workspace/src/github.com/jbenet/goprocess/.travis.yml
generated
vendored
10
Godeps/_workspace/src/github.com/jbenet/goprocess/.travis.yml
generated
vendored
@ -1,10 +0,0 @@
|
||||
sudo: false
|
||||
|
||||
language: go
|
||||
|
||||
go:
|
||||
- 1.3
|
||||
- 1.4
|
||||
|
||||
script:
|
||||
- go test -race -cpu=5 -v ./...
|
||||
21
Godeps/_workspace/src/github.com/jbenet/goprocess/LICENSE
generated
vendored
21
Godeps/_workspace/src/github.com/jbenet/goprocess/LICENSE
generated
vendored
@ -1,21 +0,0 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2014 Juan Batiz-Benet
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
132
Godeps/_workspace/src/github.com/jbenet/goprocess/README.md
generated
vendored
132
Godeps/_workspace/src/github.com/jbenet/goprocess/README.md
generated
vendored
@ -1,132 +0,0 @@
|
||||
# goprocess - lifecycles in go
|
||||
|
||||
[](https://travis-ci.org/jbenet/goprocess)
|
||||
|
||||
(Based on https://github.com/jbenet/go-ctxgroup)
|
||||
|
||||
- Godoc: https://godoc.org/github.com/jbenet/goprocess
|
||||
|
||||
`goprocess` introduces a way to manage process lifecycles in go. It is
|
||||
much like [go.net/context](https://godoc.org/code.google.com/p/go.net/context)
|
||||
(it actually uses a Context), but it is more like a Context-WaitGroup hybrid.
|
||||
`goprocess` is about being able to start and stop units of work, which may
|
||||
receive `Close` signals from many clients. Think of it like a UNIX process
|
||||
tree, but inside go.
|
||||
|
||||
`goprocess` seeks to minimally affect your objects, so you can use it
|
||||
with both embedding or composition. At the heart of `goprocess` is the
|
||||
`Process` interface:
|
||||
|
||||
```Go
|
||||
// Process is the basic unit of work in goprocess. It defines a computation
|
||||
// with a lifecycle:
|
||||
// - running (before calling Close),
|
||||
// - closing (after calling Close at least once),
|
||||
// - closed (after Close returns, and all teardown has _completed_).
|
||||
//
|
||||
// More specifically, it fits this:
|
||||
//
|
||||
// p := WithTeardown(tf) // new process is created, it is now running.
|
||||
// p.AddChild(q) // can register children **before** Closing.
|
||||
// go p.Close() // blocks until done running teardown func.
|
||||
// <-p.Closing() // would now return true.
|
||||
// <-p.childrenDone() // wait on all children to be done
|
||||
// p.teardown() // runs the user's teardown function tf.
|
||||
// p.Close() // now returns, with error teardown returned.
|
||||
// <-p.Closed() // would now return true.
|
||||
//
|
||||
// Processes can be arranged in a process "tree", where children are
|
||||
// automatically Closed if their parents are closed. (Note, it is actually
|
||||
// a Process DAG, children may have multiple parents). A process may also
|
||||
// optionally wait for another to fully Close before beginning to Close.
|
||||
// This makes it easy to ensure order of operations and proper sequential
|
||||
// teardown of resurces. For example:
|
||||
//
|
||||
// p1 := goprocess.WithTeardown(func() error {
|
||||
// fmt.Println("closing 1")
|
||||
// })
|
||||
// p2 := goprocess.WithTeardown(func() error {
|
||||
// fmt.Println("closing 2")
|
||||
// })
|
||||
// p3 := goprocess.WithTeardown(func() error {
|
||||
// fmt.Println("closing 3")
|
||||
// })
|
||||
//
|
||||
// p1.AddChild(p2)
|
||||
// p2.AddChild(p3)
|
||||
//
|
||||
//
|
||||
// go p1.Close()
|
||||
// go p2.Close()
|
||||
// go p3.Close()
|
||||
//
|
||||
// // Output:
|
||||
// // closing 3
|
||||
// // closing 2
|
||||
// // closing 1
|
||||
//
|
||||
// Process is modelled after the UNIX processes group idea, and heavily
|
||||
// informed by sync.WaitGroup and go.net/context.Context.
|
||||
//
|
||||
// In the function documentation of this interface, `p` always refers to
|
||||
// the self Process.
|
||||
type Process interface {
|
||||
|
||||
// WaitFor makes p wait for q before exiting. Thus, p will _always_ close
|
||||
// _after_ q. Note well: a waiting cycle is deadlock.
|
||||
//
|
||||
// If q is already Closed, WaitFor calls p.Close()
|
||||
// If p is already Closing or Closed, WaitFor panics. This is the same thing
|
||||
// as calling Add(1) _after_ calling Done() on a wait group. Calling WaitFor
|
||||
// on an already-closed process is a programming error likely due to bad
|
||||
// synchronization
|
||||
WaitFor(q Process)
|
||||
|
||||
// AddChildNoWait registers child as a "child" of Process. As in UNIX,
|
||||
// when parent is Closed, child is Closed -- child may Close beforehand.
|
||||
// This is the equivalent of calling:
|
||||
//
|
||||
// go func(parent, child Process) {
|
||||
// <-parent.Closing()
|
||||
// child.Close()
|
||||
// }(p, q)
|
||||
//
|
||||
// Note: the naming of functions is `AddChildNoWait` and `AddChild` (instead
|
||||
// of `AddChild` and `AddChildWaitFor`) because:
|
||||
// - it is the more common operation,
|
||||
// - explicitness is helpful in the less common case (no waiting), and
|
||||
// - usual "child" semantics imply parent Processes should wait for children.
|
||||
AddChildNoWait(q Process)
|
||||
|
||||
// AddChild is the equivalent of calling:
|
||||
// parent.AddChildNoWait(q)
|
||||
// parent.WaitFor(q)
|
||||
AddChild(q Process)
|
||||
|
||||
// Go creates a new process, adds it as a child, and spawns the ProcessFunc f
|
||||
// in its own goroutine. It is equivalent to:
|
||||
//
|
||||
// GoChild(p, f)
|
||||
//
|
||||
// It is useful to construct simple asynchronous workers, children of p.
|
||||
Go(f ProcessFunc) Process
|
||||
|
||||
// Close ends the process. Close blocks until the process has completely
|
||||
// shut down, and any teardown has run _exactly once_. The returned error
|
||||
// is available indefinitely: calling Close twice returns the same error.
|
||||
// If the process has already been closed, Close returns immediately.
|
||||
Close() error
|
||||
|
||||
// Closing is a signal to wait upon. The returned channel is closed
|
||||
// _after_ Close has been called at least once, but teardown may or may
|
||||
// not be done yet. The primary use case of Closing is for children who
|
||||
// need to know when a parent is shutting down, and therefore also shut
|
||||
// down.
|
||||
Closing() <-chan struct{}
|
||||
|
||||
// Closed is a signal to wait upon. The returned channel is closed
|
||||
// _after_ Close has completed; teardown has finished. The primary use case
|
||||
// of Closed is waiting for a Process to Close without _causing_ the Close.
|
||||
Closed() <-chan struct{}
|
||||
}
|
||||
```
|
||||
110
Godeps/_workspace/src/github.com/jbenet/goprocess/context/context.go
generated
vendored
110
Godeps/_workspace/src/github.com/jbenet/goprocess/context/context.go
generated
vendored
@ -1,110 +0,0 @@
|
||||
package goprocessctx
|
||||
|
||||
import (
|
||||
goprocess "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess"
|
||||
context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context"
|
||||
)
|
||||
|
||||
// WithContext constructs and returns a Process that respects
|
||||
// given context. It is the equivalent of:
|
||||
//
|
||||
// func ProcessWithContext(ctx context.Context) goprocess.Process {
|
||||
// p := goprocess.WithParent(goprocess.Background())
|
||||
// CloseAfterContext(p, ctx)
|
||||
// return p
|
||||
// }
|
||||
//
|
||||
func WithContext(ctx context.Context) goprocess.Process {
|
||||
p := goprocess.WithParent(goprocess.Background())
|
||||
CloseAfterContext(p, ctx)
|
||||
return p
|
||||
}
|
||||
|
||||
// WithContextAndTeardown is a helper function to set teardown at initiation
|
||||
// of WithContext
|
||||
func WithContextAndTeardown(ctx context.Context, tf goprocess.TeardownFunc) goprocess.Process {
|
||||
p := goprocess.WithTeardown(tf)
|
||||
CloseAfterContext(p, ctx)
|
||||
return p
|
||||
}
|
||||
|
||||
// WaitForContext makes p WaitFor ctx. When Closing, p waits for
|
||||
// ctx.Done(), before being Closed(). It is simply:
|
||||
//
|
||||
// p.WaitFor(goprocess.WithContext(ctx))
|
||||
//
|
||||
func WaitForContext(ctx context.Context, p goprocess.Process) {
|
||||
p.WaitFor(WithContext(ctx))
|
||||
}
|
||||
|
||||
// CloseAfterContext schedules the process to close after the given
|
||||
// context is done. It is the equivalent of:
|
||||
//
|
||||
// func CloseAfterContext(p goprocess.Process, ctx context.Context) {
|
||||
// go func() {
|
||||
// <-ctx.Done()
|
||||
// p.Close()
|
||||
// }()
|
||||
// }
|
||||
//
|
||||
func CloseAfterContext(p goprocess.Process, ctx context.Context) {
|
||||
if p == nil {
|
||||
panic("nil Process")
|
||||
}
|
||||
if ctx == nil {
|
||||
panic("nil Context")
|
||||
}
|
||||
|
||||
// context.Background(). if ctx.Done() is nil, it will never be done.
|
||||
// we check for this to avoid wasting a goroutine forever.
|
||||
if ctx.Done() == nil {
|
||||
return
|
||||
}
|
||||
|
||||
go func() {
|
||||
<-ctx.Done()
|
||||
p.Close()
|
||||
}()
|
||||
}
|
||||
|
||||
// WithProcessClosing returns a context.Context derived from ctx that
|
||||
// is cancelled as p is Closing (after: <-p.Closing()). It is simply:
|
||||
//
|
||||
// func WithProcessClosing(ctx context.Context, p goprocess.Process) context.Context {
|
||||
// ctx, cancel := context.WithCancel(ctx)
|
||||
// go func() {
|
||||
// <-p.Closing()
|
||||
// cancel()
|
||||
// }()
|
||||
// return ctx
|
||||
// }
|
||||
//
|
||||
func WithProcessClosing(ctx context.Context, p goprocess.Process) context.Context {
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
go func() {
|
||||
<-p.Closing()
|
||||
cancel()
|
||||
}()
|
||||
return ctx
|
||||
}
|
||||
|
||||
// WithProcessClosed returns a context.Context that is cancelled
|
||||
// after Process p is Closed. It is the equivalent of:
|
||||
//
|
||||
// func WithProcessClosed(ctx context.Context, p goprocess.Process) context.Context {
|
||||
// ctx, cancel := context.WithCancel(ctx)
|
||||
// go func() {
|
||||
// <-p.Closed()
|
||||
// cancel()
|
||||
// }()
|
||||
// return ctx
|
||||
// }
|
||||
//
|
||||
func WithProcessClosed(ctx context.Context, p goprocess.Process) context.Context {
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
go func() {
|
||||
<-p.Closed()
|
||||
cancel()
|
||||
}()
|
||||
return ctx
|
||||
}
|
||||
59
Godeps/_workspace/src/github.com/jbenet/goprocess/context/derive.go
generated
vendored
59
Godeps/_workspace/src/github.com/jbenet/goprocess/context/derive.go
generated
vendored
@ -1,59 +0,0 @@
|
||||
package goprocessctx
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
goprocess "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess"
|
||||
"gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context"
|
||||
)
|
||||
|
||||
const (
|
||||
closing = iota
|
||||
closed
|
||||
)
|
||||
|
||||
type procContext struct {
|
||||
done <-chan struct{}
|
||||
which int
|
||||
}
|
||||
|
||||
// OnClosingContext derives a context from a given goprocess that will
|
||||
// be 'Done' when the process is closing
|
||||
func OnClosingContext(p goprocess.Process) context.Context {
|
||||
return &procContext{
|
||||
done: p.Closing(),
|
||||
which: closing,
|
||||
}
|
||||
}
|
||||
|
||||
// OnClosedContext derives a context from a given goprocess that will
|
||||
// be 'Done' when the process is closed
|
||||
func OnClosedContext(p goprocess.Process) context.Context {
|
||||
return &procContext{
|
||||
done: p.Closed(),
|
||||
which: closed,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *procContext) Done() <-chan struct{} {
|
||||
return c.done
|
||||
}
|
||||
|
||||
func (c *procContext) Deadline() (time.Time, bool) {
|
||||
return time.Time{}, false
|
||||
}
|
||||
|
||||
func (c *procContext) Err() error {
|
||||
if c.which == closing {
|
||||
return errors.New("process closing")
|
||||
} else if c.which == closed {
|
||||
return errors.New("process closed")
|
||||
} else {
|
||||
panic("unrecognized process context type")
|
||||
}
|
||||
}
|
||||
|
||||
func (c *procContext) Value(key interface{}) interface{} {
|
||||
return nil
|
||||
}
|
||||
37
Godeps/_workspace/src/github.com/jbenet/goprocess/example_test.go
generated
vendored
37
Godeps/_workspace/src/github.com/jbenet/goprocess/example_test.go
generated
vendored
@ -1,37 +0,0 @@
|
||||
package goprocess_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess"
|
||||
)
|
||||
|
||||
func ExampleGo() {
|
||||
p := goprocess.Go(func(p goprocess.Process) {
|
||||
ticker := time.Tick(200 * time.Millisecond)
|
||||
for {
|
||||
select {
|
||||
case <-ticker:
|
||||
fmt.Println("tick")
|
||||
case <-p.Closing():
|
||||
fmt.Println("closing")
|
||||
return
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
<-time.After(1100 * time.Millisecond)
|
||||
p.Close()
|
||||
fmt.Println("closed")
|
||||
<-time.After(100 * time.Millisecond)
|
||||
|
||||
// Output:
|
||||
// tick
|
||||
// tick
|
||||
// tick
|
||||
// tick
|
||||
// tick
|
||||
// closing
|
||||
// closed
|
||||
}
|
||||
283
Godeps/_workspace/src/github.com/jbenet/goprocess/goprocess.go
generated
vendored
283
Godeps/_workspace/src/github.com/jbenet/goprocess/goprocess.go
generated
vendored
@ -1,283 +0,0 @@
|
||||
// Package goprocess introduces a Process abstraction that allows simple
|
||||
// organization, and orchestration of work. It is much like a WaitGroup,
|
||||
// and much like a context.Context, but also ensures safe **exactly-once**,
|
||||
// and well-ordered teardown semantics.
|
||||
package goprocess
|
||||
|
||||
import (
|
||||
"os"
|
||||
"os/signal"
|
||||
)
|
||||
|
||||
// Process is the basic unit of work in goprocess. It defines a computation
|
||||
// with a lifecycle:
|
||||
// - running (before calling Close),
|
||||
// - closing (after calling Close at least once),
|
||||
// - closed (after Close returns, and all teardown has _completed_).
|
||||
//
|
||||
// More specifically, it fits this:
|
||||
//
|
||||
// p := WithTeardown(tf) // new process is created, it is now running.
|
||||
// p.AddChild(q) // can register children **before** Closed().
|
||||
// go p.Close() // blocks until done running teardown func.
|
||||
// <-p.Closing() // would now return true.
|
||||
// <-p.childrenDone() // wait on all children to be done
|
||||
// p.teardown() // runs the user's teardown function tf.
|
||||
// p.Close() // now returns, with error teardown returned.
|
||||
// <-p.Closed() // would now return true.
|
||||
//
|
||||
// Processes can be arranged in a process "tree", where children are
|
||||
// automatically Closed if their parents are closed. (Note, it is actually
|
||||
// a Process DAG, children may have multiple parents). A process may also
|
||||
// optionally wait for another to fully Close before beginning to Close.
|
||||
// This makes it easy to ensure order of operations and proper sequential
|
||||
// teardown of resurces. For example:
|
||||
//
|
||||
// p1 := goprocess.WithTeardown(func() error {
|
||||
// fmt.Println("closing 1")
|
||||
// })
|
||||
// p2 := goprocess.WithTeardown(func() error {
|
||||
// fmt.Println("closing 2")
|
||||
// })
|
||||
// p3 := goprocess.WithTeardown(func() error {
|
||||
// fmt.Println("closing 3")
|
||||
// })
|
||||
//
|
||||
// p1.AddChild(p2)
|
||||
// p2.AddChild(p3)
|
||||
//
|
||||
//
|
||||
// go p1.Close()
|
||||
// go p2.Close()
|
||||
// go p3.Close()
|
||||
//
|
||||
// // Output:
|
||||
// // closing 3
|
||||
// // closing 2
|
||||
// // closing 1
|
||||
//
|
||||
// Process is modelled after the UNIX processes group idea, and heavily
|
||||
// informed by sync.WaitGroup and go.net/context.Context.
|
||||
//
|
||||
// In the function documentation of this interface, `p` always refers to
|
||||
// the self Process.
|
||||
type Process interface {
|
||||
|
||||
// WaitFor makes p wait for q before exiting. Thus, p will _always_ close
|
||||
// _after_ q. Note well: a waiting cycle is deadlock.
|
||||
//
|
||||
// If q is already Closed, WaitFor calls p.Close()
|
||||
// If p is already Closing or Closed, WaitFor panics. This is the same thing
|
||||
// as calling Add(1) _after_ calling Done() on a wait group. Calling WaitFor
|
||||
// on an already-closed process is a programming error likely due to bad
|
||||
// synchronization
|
||||
WaitFor(q Process)
|
||||
|
||||
// AddChildNoWait registers child as a "child" of Process. As in UNIX,
|
||||
// when parent is Closed, child is Closed -- child may Close beforehand.
|
||||
// This is the equivalent of calling:
|
||||
//
|
||||
// go func(parent, child Process) {
|
||||
// <-parent.Closing()
|
||||
// child.Close()
|
||||
// }(p, q)
|
||||
//
|
||||
// Note: the naming of functions is `AddChildNoWait` and `AddChild` (instead
|
||||
// of `AddChild` and `AddChildWaitFor`) because:
|
||||
// - it is the more common operation,
|
||||
// - explicitness is helpful in the less common case (no waiting), and
|
||||
// - usual "child" semantics imply parent Processes should wait for children.
|
||||
AddChildNoWait(q Process)
|
||||
|
||||
// AddChild is the equivalent of calling:
|
||||
// parent.AddChildNoWait(q)
|
||||
// parent.WaitFor(q)
|
||||
AddChild(q Process)
|
||||
|
||||
// Go is much like `go`, as it runs a function in a newly spawned goroutine.
|
||||
// The neat part of Process.Go is that the Process object you call it on will:
|
||||
// * construct a child Process, and call AddChild(child) on it
|
||||
// * spawn a goroutine, and call the given function
|
||||
// * Close the child when the function exits.
|
||||
// This way, you can rest assured each goroutine you spawn has its very own
|
||||
// Process context, and that it will be closed when the function exits.
|
||||
// It is the function's responsibility to respect the Closing of its Process,
|
||||
// namely it should exit (return) when <-Closing() is ready. It is basically:
|
||||
//
|
||||
// func (p Process) Go(f ProcessFunc) Process {
|
||||
// child := WithParent(p)
|
||||
// go func () {
|
||||
// f(child)
|
||||
// child.Close()
|
||||
// }()
|
||||
// }
|
||||
//
|
||||
// It is useful to construct simple asynchronous workers, children of p.
|
||||
Go(f ProcessFunc) Process
|
||||
|
||||
// SetTeardown sets the process's teardown to tf.
|
||||
SetTeardown(tf TeardownFunc)
|
||||
|
||||
// Close ends the process. Close blocks until the process has completely
|
||||
// shut down, and any teardown has run _exactly once_. The returned error
|
||||
// is available indefinitely: calling Close twice returns the same error.
|
||||
// If the process has already been closed, Close returns immediately.
|
||||
Close() error
|
||||
|
||||
// CloseAfterChildren calls Close _after_ its children have Closed
|
||||
// normally (i.e. it _does not_ attempt to close them).
|
||||
CloseAfterChildren() error
|
||||
|
||||
// Closing is a signal to wait upon. The returned channel is closed
|
||||
// _after_ Close has been called at least once, but teardown may or may
|
||||
// not be done yet. The primary use case of Closing is for children who
|
||||
// need to know when a parent is shutting down, and therefore also shut
|
||||
// down.
|
||||
Closing() <-chan struct{}
|
||||
|
||||
// Closed is a signal to wait upon. The returned channel is closed
|
||||
// _after_ Close has completed; teardown has finished. The primary use case
|
||||
// of Closed is waiting for a Process to Close without _causing_ the Close.
|
||||
Closed() <-chan struct{}
|
||||
|
||||
// Err waits until the process is closed, and then returns any error that
|
||||
// occurred during shutdown.
|
||||
Err() error
|
||||
}
|
||||
|
||||
// TeardownFunc is a function used to cleanup state at the end of the
|
||||
// lifecycle of a Process.
|
||||
type TeardownFunc func() error
|
||||
|
||||
// ProcessFunc is a function that takes a process. Its main use case is goprocess.Go,
|
||||
// which spawns a ProcessFunc in its own goroutine, and returns a corresponding
|
||||
// Process object.
|
||||
type ProcessFunc func(proc Process)
|
||||
|
||||
var nilProcessFunc = func(Process) {}
|
||||
|
||||
// Go is much like `go`: it runs a function in a newly spawned goroutine. The neat
|
||||
// part of Go is that it provides Process object to communicate between the
|
||||
// function and the outside world. Thus, callers can easily WaitFor, or Close the
|
||||
// function. It is the function's responsibility to respect the Closing of its Process,
|
||||
// namely it should exit (return) when <-Closing() is ready. It is simply:
|
||||
//
|
||||
// func Go(f ProcessFunc) Process {
|
||||
// p := WithParent(Background())
|
||||
// p.Go(f)
|
||||
// return p
|
||||
// }
|
||||
//
|
||||
// Note that a naive implementation of Go like the following would not work:
|
||||
//
|
||||
// func Go(f ProcessFunc) Process {
|
||||
// return Background().Go(f)
|
||||
// }
|
||||
//
|
||||
// This is because having the process you
|
||||
func Go(f ProcessFunc) Process {
|
||||
// return GoChild(Background(), f)
|
||||
|
||||
// we use two processes, one for communication, and
|
||||
// one for ensuring we wait on the function (unclosable from the outside).
|
||||
p := newProcess(nil)
|
||||
waitFor := newProcess(nil)
|
||||
p.WaitFor(waitFor) // prevent p from closing
|
||||
go func() {
|
||||
f(p)
|
||||
waitFor.Close() // allow p to close.
|
||||
p.Close() // ensure p closes.
|
||||
}()
|
||||
return p
|
||||
}
|
||||
|
||||
// GoChild is like Go, but it registers the returned Process as a child of parent,
|
||||
// **before** spawning the goroutine, which ensures proper synchronization with parent.
|
||||
// It is somewhat like
|
||||
//
|
||||
// func GoChild(parent Process, f ProcessFunc) Process {
|
||||
// p := WithParent(parent)
|
||||
// p.Go(f)
|
||||
// return p
|
||||
// }
|
||||
//
|
||||
// And it is similar to the classic WaitGroup use case:
|
||||
//
|
||||
// func WaitGroupGo(wg sync.WaitGroup, child func()) {
|
||||
// wg.Add(1)
|
||||
// go func() {
|
||||
// child()
|
||||
// wg.Done()
|
||||
// }()
|
||||
// }
|
||||
//
|
||||
func GoChild(parent Process, f ProcessFunc) Process {
|
||||
p := WithParent(parent)
|
||||
p.Go(f)
|
||||
return p
|
||||
}
|
||||
|
||||
// Spawn is an alias of `Go`. In many contexts, Spawn is a
|
||||
// well-known Process launching word, which fits our use case.
|
||||
var Spawn = Go
|
||||
|
||||
// SpawnChild is an alias of `GoChild`. In many contexts, Spawn is a
|
||||
// well-known Process launching word, which fits our use case.
|
||||
var SpawnChild = GoChild
|
||||
|
||||
// WithTeardown constructs and returns a Process with a TeardownFunc.
|
||||
// TeardownFunc tf will be called **exactly-once** when Process is
|
||||
// Closing, after all Children have fully closed, and before p is Closed.
|
||||
// In fact, Process p will not be Closed until tf runs and exits.
|
||||
// See lifecycle in Process doc.
|
||||
func WithTeardown(tf TeardownFunc) Process {
|
||||
if tf == nil {
|
||||
panic("nil tf TeardownFunc")
|
||||
}
|
||||
return newProcess(tf)
|
||||
}
|
||||
|
||||
// WithParent constructs and returns a Process with a given parent.
|
||||
func WithParent(parent Process) Process {
|
||||
if parent == nil {
|
||||
panic("nil parent Process")
|
||||
}
|
||||
q := newProcess(nil)
|
||||
parent.AddChild(q)
|
||||
return q
|
||||
}
|
||||
|
||||
// WithSignals returns a Process that will Close() when any given signal fires.
|
||||
// This is useful to bind Process trees to syscall.SIGTERM, SIGKILL, etc.
|
||||
func WithSignals(sig ...os.Signal) Process {
|
||||
p := WithParent(Background())
|
||||
c := make(chan os.Signal)
|
||||
signal.Notify(c, sig...)
|
||||
go func() {
|
||||
<-c
|
||||
signal.Stop(c)
|
||||
p.Close()
|
||||
}()
|
||||
return p
|
||||
}
|
||||
|
||||
// Background returns the "background" Process: a statically allocated
|
||||
// process that can _never_ close. It also never enters Closing() state.
|
||||
// Calling Background().Close() will hang indefinitely.
|
||||
func Background() Process {
|
||||
return background
|
||||
}
|
||||
|
||||
// background is the background process
|
||||
var background = &unclosable{Process: newProcess(nil)}
|
||||
|
||||
// unclosable is a process that _cannot_ be closed. calling Close simply hangs.
|
||||
type unclosable struct {
|
||||
Process
|
||||
}
|
||||
|
||||
func (p *unclosable) Close() error {
|
||||
var hang chan struct{}
|
||||
<-hang // hang forever
|
||||
return nil
|
||||
}
|
||||
638
Godeps/_workspace/src/github.com/jbenet/goprocess/goprocess_test.go
generated
vendored
638
Godeps/_workspace/src/github.com/jbenet/goprocess/goprocess_test.go
generated
vendored
@ -1,638 +0,0 @@
|
||||
package goprocess
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"runtime"
|
||||
"syscall"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
type tree struct {
|
||||
Process
|
||||
c []tree
|
||||
}
|
||||
|
||||
func setupHierarchy(p Process) tree {
|
||||
t := func(n Process, ts ...tree) tree {
|
||||
return tree{n, ts}
|
||||
}
|
||||
|
||||
a := WithParent(p)
|
||||
b1 := WithParent(a)
|
||||
b2 := WithParent(a)
|
||||
c1 := WithParent(b1)
|
||||
c2 := WithParent(b1)
|
||||
c3 := WithParent(b2)
|
||||
c4 := WithParent(b2)
|
||||
|
||||
return t(a, t(b1, t(c1), t(c2)), t(b2, t(c3), t(c4)))
|
||||
}
|
||||
|
||||
func TestClosingClosed(t *testing.T) {
|
||||
|
||||
bWait := make(chan struct{})
|
||||
a := WithParent(Background())
|
||||
a.Go(func(proc Process) {
|
||||
<-bWait
|
||||
})
|
||||
|
||||
Q := make(chan string, 3)
|
||||
|
||||
go func() {
|
||||
<-a.Closing()
|
||||
Q <- "closing"
|
||||
bWait <- struct{}{}
|
||||
}()
|
||||
|
||||
go func() {
|
||||
<-a.Closed()
|
||||
Q <- "closed"
|
||||
}()
|
||||
|
||||
go func() {
|
||||
a.Close()
|
||||
Q <- "closed"
|
||||
}()
|
||||
|
||||
if q := <-Q; q != "closing" {
|
||||
t.Error("order incorrect. closing not first")
|
||||
}
|
||||
if q := <-Q; q != "closed" {
|
||||
t.Error("order incorrect. closing not first")
|
||||
}
|
||||
if q := <-Q; q != "closed" {
|
||||
t.Error("order incorrect. closing not first")
|
||||
}
|
||||
}
|
||||
|
||||
func TestChildFunc(t *testing.T) {
|
||||
a := WithParent(Background())
|
||||
|
||||
wait1 := make(chan struct{})
|
||||
wait2 := make(chan struct{})
|
||||
wait3 := make(chan struct{})
|
||||
wait4 := make(chan struct{})
|
||||
|
||||
a.Go(func(process Process) {
|
||||
wait1 <- struct{}{}
|
||||
<-wait2
|
||||
wait3 <- struct{}{}
|
||||
})
|
||||
|
||||
go func() {
|
||||
a.Close()
|
||||
wait4 <- struct{}{}
|
||||
}()
|
||||
|
||||
<-wait1
|
||||
select {
|
||||
case <-wait3:
|
||||
t.Error("should not be closed yet")
|
||||
case <-wait4:
|
||||
t.Error("should not be closed yet")
|
||||
case <-a.Closed():
|
||||
t.Error("should not be closed yet")
|
||||
default:
|
||||
}
|
||||
|
||||
wait2 <- struct{}{}
|
||||
|
||||
select {
|
||||
case <-wait3:
|
||||
case <-time.After(time.Second):
|
||||
t.Error("should be closed now")
|
||||
}
|
||||
|
||||
select {
|
||||
case <-wait4:
|
||||
case <-time.After(time.Second):
|
||||
t.Error("should be closed now")
|
||||
}
|
||||
}
|
||||
|
||||
func TestTeardownCalledOnce(t *testing.T) {
|
||||
a := setupHierarchy(Background())
|
||||
|
||||
onlyOnce := func() func() error {
|
||||
count := 0
|
||||
return func() error {
|
||||
count++
|
||||
if count > 1 {
|
||||
t.Error("called", count, "times")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
a.SetTeardown(onlyOnce())
|
||||
a.c[0].SetTeardown(onlyOnce())
|
||||
a.c[0].c[0].SetTeardown(onlyOnce())
|
||||
a.c[0].c[1].SetTeardown(onlyOnce())
|
||||
a.c[1].SetTeardown(onlyOnce())
|
||||
a.c[1].c[0].SetTeardown(onlyOnce())
|
||||
a.c[1].c[1].SetTeardown(onlyOnce())
|
||||
|
||||
a.c[0].c[0].Close()
|
||||
a.c[0].c[0].Close()
|
||||
a.c[0].c[0].Close()
|
||||
a.c[0].c[0].Close()
|
||||
a.c[0].Close()
|
||||
a.c[0].Close()
|
||||
a.c[0].Close()
|
||||
a.c[0].Close()
|
||||
a.Close()
|
||||
a.Close()
|
||||
a.Close()
|
||||
a.Close()
|
||||
a.c[1].Close()
|
||||
a.c[1].Close()
|
||||
a.c[1].Close()
|
||||
a.c[1].Close()
|
||||
}
|
||||
|
||||
func TestOnClosedAll(t *testing.T) {
|
||||
|
||||
Q := make(chan string, 10)
|
||||
p := WithParent(Background())
|
||||
a := setupHierarchy(p)
|
||||
|
||||
go onClosedStr(Q, "0", a.c[0])
|
||||
go onClosedStr(Q, "10", a.c[1].c[0])
|
||||
go onClosedStr(Q, "", a)
|
||||
go onClosedStr(Q, "00", a.c[0].c[0])
|
||||
go onClosedStr(Q, "1", a.c[1])
|
||||
go onClosedStr(Q, "01", a.c[0].c[1])
|
||||
go onClosedStr(Q, "11", a.c[1].c[1])
|
||||
|
||||
go p.Close()
|
||||
|
||||
testStrs(t, Q, "00", "01", "10", "11", "0", "1", "")
|
||||
testStrs(t, Q, "00", "01", "10", "11", "0", "1", "")
|
||||
testStrs(t, Q, "00", "01", "10", "11", "0", "1", "")
|
||||
testStrs(t, Q, "00", "01", "10", "11", "0", "1", "")
|
||||
testStrs(t, Q, "00", "01", "10", "11", "0", "1", "")
|
||||
testStrs(t, Q, "00", "01", "10", "11", "0", "1", "")
|
||||
}
|
||||
|
||||
func TestOnClosedLeaves(t *testing.T) {
|
||||
|
||||
Q := make(chan string, 10)
|
||||
p := WithParent(Background())
|
||||
a := setupHierarchy(p)
|
||||
|
||||
go onClosedStr(Q, "0", a.c[0])
|
||||
go onClosedStr(Q, "10", a.c[1].c[0])
|
||||
go onClosedStr(Q, "", a)
|
||||
go onClosedStr(Q, "00", a.c[0].c[0])
|
||||
go onClosedStr(Q, "1", a.c[1])
|
||||
go onClosedStr(Q, "01", a.c[0].c[1])
|
||||
go onClosedStr(Q, "11", a.c[1].c[1])
|
||||
|
||||
go a.c[0].Close()
|
||||
testStrs(t, Q, "00", "01", "0")
|
||||
testStrs(t, Q, "00", "01", "0")
|
||||
testStrs(t, Q, "00", "01", "0")
|
||||
|
||||
go a.c[1].Close()
|
||||
testStrs(t, Q, "10", "11", "1")
|
||||
testStrs(t, Q, "10", "11", "1")
|
||||
testStrs(t, Q, "10", "11", "1")
|
||||
|
||||
go p.Close()
|
||||
testStrs(t, Q, "")
|
||||
}
|
||||
|
||||
func TestWaitFor(t *testing.T) {
|
||||
|
||||
Q := make(chan string, 5)
|
||||
a := WithParent(Background())
|
||||
b := WithParent(Background())
|
||||
c := WithParent(Background())
|
||||
d := WithParent(Background())
|
||||
e := WithParent(Background())
|
||||
|
||||
go onClosedStr(Q, "a", a)
|
||||
go onClosedStr(Q, "b", b)
|
||||
go onClosedStr(Q, "c", c)
|
||||
go onClosedStr(Q, "d", d)
|
||||
go onClosedStr(Q, "e", e)
|
||||
|
||||
testNone(t, Q)
|
||||
a.WaitFor(b)
|
||||
a.WaitFor(c)
|
||||
b.WaitFor(d)
|
||||
e.WaitFor(d)
|
||||
testNone(t, Q)
|
||||
|
||||
go a.Close() // should do nothing.
|
||||
testNone(t, Q)
|
||||
|
||||
go e.Close()
|
||||
testNone(t, Q)
|
||||
|
||||
d.Close()
|
||||
testStrs(t, Q, "d", "e")
|
||||
testStrs(t, Q, "d", "e")
|
||||
|
||||
c.Close()
|
||||
testStrs(t, Q, "c")
|
||||
|
||||
b.Close()
|
||||
testStrs(t, Q, "a", "b")
|
||||
testStrs(t, Q, "a", "b")
|
||||
}
|
||||
|
||||
func TestAddChildNoWait(t *testing.T) {
|
||||
|
||||
Q := make(chan string, 5)
|
||||
a := WithParent(Background())
|
||||
b := WithParent(Background())
|
||||
c := WithParent(Background())
|
||||
d := WithParent(Background())
|
||||
e := WithParent(Background())
|
||||
|
||||
go onClosedStr(Q, "a", a)
|
||||
go onClosedStr(Q, "b", b)
|
||||
go onClosedStr(Q, "c", c)
|
||||
go onClosedStr(Q, "d", d)
|
||||
go onClosedStr(Q, "e", e)
|
||||
|
||||
testNone(t, Q)
|
||||
a.AddChildNoWait(b)
|
||||
a.AddChildNoWait(c)
|
||||
b.AddChildNoWait(d)
|
||||
e.AddChildNoWait(d)
|
||||
testNone(t, Q)
|
||||
|
||||
b.Close()
|
||||
testStrs(t, Q, "b", "d")
|
||||
testStrs(t, Q, "b", "d")
|
||||
|
||||
a.Close()
|
||||
testStrs(t, Q, "a", "c")
|
||||
testStrs(t, Q, "a", "c")
|
||||
|
||||
e.Close()
|
||||
testStrs(t, Q, "e")
|
||||
}
|
||||
|
||||
func TestAddChild(t *testing.T) {
|
||||
|
||||
a := WithParent(Background())
|
||||
b := WithParent(Background())
|
||||
c := WithParent(Background())
|
||||
d := WithParent(Background())
|
||||
e := WithParent(Background())
|
||||
Q := make(chan string, 5)
|
||||
|
||||
go onClosedStr(Q, "a", a)
|
||||
go onClosedStr(Q, "b", b)
|
||||
go onClosedStr(Q, "c", c)
|
||||
go onClosedStr(Q, "d", d)
|
||||
go onClosedStr(Q, "e", e)
|
||||
|
||||
testNone(t, Q)
|
||||
a.AddChild(b)
|
||||
a.AddChild(c)
|
||||
b.AddChild(d)
|
||||
e.AddChild(d)
|
||||
testNone(t, Q)
|
||||
|
||||
go b.Close()
|
||||
d.Close()
|
||||
testStrs(t, Q, "b", "d")
|
||||
testStrs(t, Q, "b", "d")
|
||||
|
||||
go a.Close()
|
||||
c.Close()
|
||||
testStrs(t, Q, "a", "c")
|
||||
testStrs(t, Q, "a", "c")
|
||||
|
||||
e.Close()
|
||||
testStrs(t, Q, "e")
|
||||
}
|
||||
|
||||
func TestGoChildrenClose(t *testing.T) {
|
||||
|
||||
var a, b, c, d, e Process
|
||||
var ready = make(chan struct{})
|
||||
var bWait = make(chan struct{})
|
||||
var cWait = make(chan struct{})
|
||||
var dWait = make(chan struct{})
|
||||
var eWait = make(chan struct{})
|
||||
|
||||
a = WithParent(Background())
|
||||
a.Go(func(p Process) {
|
||||
b = p
|
||||
b.Go(func(p Process) {
|
||||
c = p
|
||||
ready <- struct{}{}
|
||||
<-cWait
|
||||
})
|
||||
ready <- struct{}{}
|
||||
<-bWait
|
||||
})
|
||||
a.Go(func(p Process) {
|
||||
d = p
|
||||
d.Go(func(p Process) {
|
||||
e = p
|
||||
ready <- struct{}{}
|
||||
<-eWait
|
||||
})
|
||||
ready <- struct{}{}
|
||||
<-dWait
|
||||
})
|
||||
|
||||
<-ready
|
||||
<-ready
|
||||
<-ready
|
||||
<-ready
|
||||
|
||||
Q := make(chan string, 5)
|
||||
|
||||
go onClosedStr(Q, "a", a)
|
||||
go onClosedStr(Q, "b", b)
|
||||
go onClosedStr(Q, "c", c)
|
||||
go onClosedStr(Q, "d", d)
|
||||
go onClosedStr(Q, "e", e)
|
||||
|
||||
testNone(t, Q)
|
||||
go a.Close()
|
||||
testNone(t, Q)
|
||||
|
||||
bWait <- struct{}{} // relase b
|
||||
go b.Close()
|
||||
testNone(t, Q)
|
||||
|
||||
cWait <- struct{}{} // relase c
|
||||
<-c.Closed()
|
||||
<-b.Closed()
|
||||
testStrs(t, Q, "b", "c")
|
||||
testStrs(t, Q, "b", "c")
|
||||
|
||||
eWait <- struct{}{} // release e
|
||||
<-e.Closed()
|
||||
testStrs(t, Q, "e")
|
||||
|
||||
dWait <- struct{}{} // releasse d
|
||||
<-d.Closed()
|
||||
<-a.Closed()
|
||||
testStrs(t, Q, "a", "d")
|
||||
testStrs(t, Q, "a", "d")
|
||||
}
|
||||
|
||||
func TestCloseAfterChildren(t *testing.T) {
|
||||
|
||||
var a, b, c, d, e Process
|
||||
|
||||
var ready = make(chan struct{})
|
||||
|
||||
a = WithParent(Background())
|
||||
a.Go(func(p Process) {
|
||||
b = p
|
||||
b.Go(func(p Process) {
|
||||
c = p
|
||||
ready <- struct{}{}
|
||||
<-p.Closing() // wait till we're told to close (parents mustnt)
|
||||
})
|
||||
ready <- struct{}{}
|
||||
// <-p.Closing() // will CloseAfterChildren
|
||||
})
|
||||
a.Go(func(p Process) {
|
||||
d = p
|
||||
d.Go(func(p Process) {
|
||||
e = p
|
||||
ready <- struct{}{}
|
||||
<-p.Closing() // wait till we're told to close (parents mustnt)
|
||||
})
|
||||
ready <- struct{}{}
|
||||
<-p.Closing()
|
||||
})
|
||||
|
||||
<-ready
|
||||
<-ready
|
||||
<-ready
|
||||
<-ready
|
||||
|
||||
Q := make(chan string, 5)
|
||||
|
||||
go onClosedStr(Q, "a", a)
|
||||
go onClosedStr(Q, "b", b)
|
||||
go onClosedStr(Q, "c", c)
|
||||
go onClosedStr(Q, "d", d)
|
||||
go onClosedStr(Q, "e", e)
|
||||
|
||||
aDone := make(chan struct{})
|
||||
bDone := make(chan struct{})
|
||||
|
||||
t.Log("test none when waiting on a")
|
||||
testNone(t, Q)
|
||||
go func() {
|
||||
a.CloseAfterChildren()
|
||||
aDone <- struct{}{}
|
||||
}()
|
||||
testNone(t, Q)
|
||||
|
||||
t.Log("test none when waiting on b")
|
||||
go func() {
|
||||
b.CloseAfterChildren()
|
||||
bDone <- struct{}{}
|
||||
}()
|
||||
testNone(t, Q)
|
||||
|
||||
c.Close()
|
||||
<-bDone
|
||||
<-b.Closed()
|
||||
testStrs(t, Q, "b", "c")
|
||||
testStrs(t, Q, "b", "c")
|
||||
|
||||
e.Close()
|
||||
testStrs(t, Q, "e")
|
||||
|
||||
d.Close()
|
||||
<-aDone
|
||||
<-a.Closed()
|
||||
testStrs(t, Q, "a", "d")
|
||||
testStrs(t, Q, "a", "d")
|
||||
}
|
||||
|
||||
func TestGoClosing(t *testing.T) {
|
||||
|
||||
var ready = make(chan struct{})
|
||||
a := WithParent(Background())
|
||||
a.Go(func(p Process) {
|
||||
|
||||
// this should be fine.
|
||||
a.Go(func(p Process) {
|
||||
ready <- struct{}{}
|
||||
})
|
||||
|
||||
// set a to close. should not fully close until after this func returns.
|
||||
go a.Close()
|
||||
|
||||
// wait until a is marked as closing
|
||||
<-a.Closing()
|
||||
|
||||
// this should also be fine.
|
||||
a.Go(func(p Process) {
|
||||
|
||||
select {
|
||||
case <-p.Closing():
|
||||
// p should be marked as closing
|
||||
default:
|
||||
t.Error("not marked closing when it should be.")
|
||||
}
|
||||
|
||||
ready <- struct{}{}
|
||||
})
|
||||
|
||||
ready <- struct{}{}
|
||||
})
|
||||
|
||||
<-ready
|
||||
<-ready
|
||||
<-ready
|
||||
}
|
||||
|
||||
func TestBackground(t *testing.T) {
|
||||
// test it hangs indefinitely:
|
||||
b := Background()
|
||||
go b.Close()
|
||||
|
||||
select {
|
||||
case <-b.Closing():
|
||||
t.Error("b.Closing() closed :(")
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
func TestWithSignals(t *testing.T) {
|
||||
p := WithSignals(syscall.SIGABRT)
|
||||
testNotClosed(t, p)
|
||||
|
||||
syscall.Kill(syscall.Getpid(), syscall.SIGABRT)
|
||||
testClosed(t, p)
|
||||
}
|
||||
|
||||
func TestMemoryLeak(t *testing.T) {
|
||||
iters := 100
|
||||
fanout := 10
|
||||
P := newProcess(nil)
|
||||
var memories []float32
|
||||
|
||||
measure := func(str string) float32 {
|
||||
s := new(runtime.MemStats)
|
||||
runtime.ReadMemStats(s)
|
||||
//fmt.Printf("%d ", s.HeapObjects)
|
||||
//fmt.Printf("%d ", len(P.children))
|
||||
//fmt.Printf("%d ", runtime.NumGoroutine())
|
||||
//fmt.Printf("%s: %dk\n", str, s.HeapAlloc/1000)
|
||||
return float32(s.HeapAlloc) / 1000
|
||||
}
|
||||
|
||||
spawn := func() []Process {
|
||||
var ps []Process
|
||||
// Spawn processes
|
||||
for i := 0; i < fanout; i++ {
|
||||
p := WithParent(P)
|
||||
ps = append(ps, p)
|
||||
|
||||
for i := 0; i < fanout; i++ {
|
||||
p2 := WithParent(p)
|
||||
ps = append(ps, p2)
|
||||
|
||||
for i := 0; i < fanout; i++ {
|
||||
p3 := WithParent(p2)
|
||||
ps = append(ps, p3)
|
||||
}
|
||||
}
|
||||
}
|
||||
return ps
|
||||
}
|
||||
|
||||
// Read initial memory stats
|
||||
measure("initial")
|
||||
for i := 0; i < iters; i++ {
|
||||
ps := spawn()
|
||||
//measure("alloc") // read after alloc
|
||||
|
||||
// Close all processes
|
||||
for _, p := range ps {
|
||||
p.Close()
|
||||
<-p.Closed()
|
||||
}
|
||||
ps = nil
|
||||
|
||||
//measure("dealloc") // read after dealloc, but before gc
|
||||
|
||||
// wait until all/most goroutines finish
|
||||
<-time.After(time.Millisecond)
|
||||
|
||||
// Run GC
|
||||
runtime.GC()
|
||||
memories = append(memories, measure("gc")) // read after gc
|
||||
}
|
||||
|
||||
memoryInit := memories[10]
|
||||
percentGrowth := 100 * (memories[len(memories)-1] - memoryInit) / memoryInit
|
||||
fmt.Printf("Memory growth after %d iteration with each %d processes: %.2f%% after %dk\n", iters, fanout*fanout*fanout, percentGrowth, int(memoryInit))
|
||||
|
||||
}
|
||||
|
||||
func testClosing(t *testing.T, p Process) {
|
||||
select {
|
||||
case <-p.Closing():
|
||||
case <-time.After(50 * time.Millisecond):
|
||||
t.Fatal("should be closing")
|
||||
}
|
||||
}
|
||||
|
||||
func testNotClosing(t *testing.T, p Process) {
|
||||
select {
|
||||
case <-p.Closing():
|
||||
t.Fatal("should not be closing")
|
||||
case <-p.Closed():
|
||||
t.Fatal("should not be closed")
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
func testClosed(t *testing.T, p Process) {
|
||||
select {
|
||||
case <-p.Closed():
|
||||
case <-time.After(50 * time.Millisecond):
|
||||
t.Fatal("should be closed")
|
||||
}
|
||||
}
|
||||
|
||||
func testNotClosed(t *testing.T, p Process) {
|
||||
select {
|
||||
case <-p.Closed():
|
||||
t.Fatal("should not be closed")
|
||||
case <-time.After(50 * time.Millisecond):
|
||||
}
|
||||
}
|
||||
|
||||
func testNone(t *testing.T, c <-chan string) {
|
||||
select {
|
||||
case out := <-c:
|
||||
t.Fatal("none should be closed", out)
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
func testStrs(t *testing.T, Q <-chan string, ss ...string) {
|
||||
s1 := <-Q
|
||||
for _, s2 := range ss {
|
||||
if s1 == s2 {
|
||||
return
|
||||
}
|
||||
}
|
||||
t.Error("context not in group:", s1, ss)
|
||||
}
|
||||
|
||||
func onClosedStr(Q chan<- string, s string, p Process) {
|
||||
<-p.Closed()
|
||||
Q <- s
|
||||
}
|
||||
271
Godeps/_workspace/src/github.com/jbenet/goprocess/impl-mutex.go
generated
vendored
271
Godeps/_workspace/src/github.com/jbenet/goprocess/impl-mutex.go
generated
vendored
@ -1,271 +0,0 @@
|
||||
package goprocess
|
||||
|
||||
import (
|
||||
"sync"
|
||||
)
|
||||
|
||||
// process implements Process
|
||||
type process struct {
|
||||
children map[*processLink]struct{} // process to close with us
|
||||
waitfors map[*processLink]struct{} // process to only wait for
|
||||
waiters []*processLink // processes that wait for us. for gc.
|
||||
|
||||
teardown TeardownFunc // called to run the teardown logic.
|
||||
waiting chan struct{} // closed when CloseAfterChildrenClosed is called.
|
||||
closing chan struct{} // closed once close starts.
|
||||
closed chan struct{} // closed once close is done.
|
||||
closeErr error // error to return to clients of Close()
|
||||
|
||||
sync.Mutex
|
||||
}
|
||||
|
||||
// newProcess constructs and returns a Process.
|
||||
// It will call tf TeardownFunc exactly once:
|
||||
// **after** all children have fully Closed,
|
||||
// **after** entering <-Closing(), and
|
||||
// **before** <-Closed().
|
||||
func newProcess(tf TeardownFunc) *process {
|
||||
return &process{
|
||||
teardown: tf,
|
||||
closed: make(chan struct{}),
|
||||
closing: make(chan struct{}),
|
||||
waitfors: make(map[*processLink]struct{}),
|
||||
children: make(map[*processLink]struct{}),
|
||||
}
|
||||
}
|
||||
|
||||
func (p *process) WaitFor(q Process) {
|
||||
if q == nil {
|
||||
panic("waiting for nil process")
|
||||
}
|
||||
|
||||
p.Lock()
|
||||
|
||||
select {
|
||||
case <-p.Closed():
|
||||
panic("Process cannot wait after being closed")
|
||||
default:
|
||||
}
|
||||
|
||||
pl := newProcessLink(p, q)
|
||||
p.waitfors[pl] = struct{}{}
|
||||
p.Unlock()
|
||||
go pl.AddToChild()
|
||||
}
|
||||
|
||||
func (p *process) AddChildNoWait(child Process) {
|
||||
if child == nil {
|
||||
panic("adding nil child process")
|
||||
}
|
||||
|
||||
p.Lock()
|
||||
|
||||
select {
|
||||
case <-p.Closed():
|
||||
panic("Process cannot add children after being closed")
|
||||
case <-p.Closing():
|
||||
go child.Close()
|
||||
default:
|
||||
}
|
||||
|
||||
pl := newProcessLink(p, child)
|
||||
p.children[pl] = struct{}{}
|
||||
p.Unlock()
|
||||
go pl.AddToChild()
|
||||
}
|
||||
|
||||
func (p *process) AddChild(child Process) {
|
||||
if child == nil {
|
||||
panic("adding nil child process")
|
||||
}
|
||||
|
||||
p.Lock()
|
||||
|
||||
select {
|
||||
case <-p.Closed():
|
||||
panic("Process cannot add children after being closed")
|
||||
case <-p.Closing():
|
||||
go child.Close()
|
||||
default:
|
||||
}
|
||||
|
||||
pl := newProcessLink(p, child)
|
||||
if p.waitfors != nil { // if p.waitfors hasn't been set nil
|
||||
p.waitfors[pl] = struct{}{}
|
||||
}
|
||||
if p.children != nil { // if p.children hasn't been set nil
|
||||
p.children[pl] = struct{}{}
|
||||
}
|
||||
p.Unlock()
|
||||
go pl.AddToChild()
|
||||
}
|
||||
|
||||
func (p *process) Go(f ProcessFunc) Process {
|
||||
child := newProcess(nil)
|
||||
waitFor := newProcess(nil)
|
||||
child.WaitFor(waitFor) // prevent child from closing
|
||||
|
||||
// add child last, to prevent a closing parent from
|
||||
// closing all of them prematurely, before running the func.
|
||||
p.AddChild(child)
|
||||
go func() {
|
||||
f(child)
|
||||
waitFor.Close() // allow child to close.
|
||||
child.CloseAfterChildren() // close to tear down.
|
||||
}()
|
||||
return child
|
||||
}
|
||||
|
||||
// SetTeardown to assign a teardown function
|
||||
func (p *process) SetTeardown(tf TeardownFunc) {
|
||||
if tf == nil {
|
||||
panic("cannot set nil TeardownFunc")
|
||||
}
|
||||
|
||||
p.Lock()
|
||||
if p.teardown != nil {
|
||||
panic("cannot SetTeardown twice")
|
||||
}
|
||||
|
||||
p.teardown = tf
|
||||
select {
|
||||
case <-p.Closed():
|
||||
p.closeErr = tf()
|
||||
default:
|
||||
}
|
||||
p.Unlock()
|
||||
}
|
||||
|
||||
// Close is the external close function.
|
||||
// it's a wrapper around internalClose that waits on Closed()
|
||||
func (p *process) Close() error {
|
||||
p.Lock()
|
||||
|
||||
// if already closing, or closed, get out. (but wait!)
|
||||
select {
|
||||
case <-p.Closing():
|
||||
p.Unlock()
|
||||
<-p.Closed()
|
||||
return p.closeErr
|
||||
default:
|
||||
}
|
||||
|
||||
p.doClose()
|
||||
p.Unlock()
|
||||
return p.closeErr
|
||||
}
|
||||
|
||||
func (p *process) Closing() <-chan struct{} {
|
||||
return p.closing
|
||||
}
|
||||
|
||||
func (p *process) Closed() <-chan struct{} {
|
||||
return p.closed
|
||||
}
|
||||
|
||||
func (p *process) Err() error {
|
||||
<-p.Closed()
|
||||
return p.closeErr
|
||||
}
|
||||
|
||||
// the _actual_ close process.
|
||||
func (p *process) doClose() {
|
||||
// this function is only be called once (protected by p.Lock()).
|
||||
// and it will panic (on closing channels) otherwise.
|
||||
|
||||
close(p.closing) // signal that we're shutting down (Closing)
|
||||
|
||||
for len(p.children) > 0 || len(p.waitfors) > 0 {
|
||||
for plc, _ := range p.children {
|
||||
child := plc.Child()
|
||||
if child != nil { // check because child may already have been removed.
|
||||
go child.Close() // force all children to shut down
|
||||
}
|
||||
plc.ParentClear()
|
||||
}
|
||||
p.children = nil // clear them. release memory.
|
||||
|
||||
// we must be careful not to iterate over waitfors directly, as it may
|
||||
// change under our feet.
|
||||
wf := p.waitfors
|
||||
p.waitfors = nil // clear them. release memory.
|
||||
for w, _ := range wf {
|
||||
// Here, we wait UNLOCKED, so that waitfors who are in the middle of
|
||||
// adding a child to us can finish. we will immediately close the child.
|
||||
p.Unlock()
|
||||
<-w.ChildClosed() // wait till all waitfors are fully closed (before teardown)
|
||||
p.Lock()
|
||||
w.ParentClear()
|
||||
}
|
||||
}
|
||||
|
||||
if p.teardown != nil {
|
||||
p.closeErr = p.teardown() // actually run the close logic (ok safe to teardown)
|
||||
}
|
||||
close(p.closed) // signal that we're shut down (Closed)
|
||||
|
||||
// go remove all the parents from the process links. optimization.
|
||||
go func(waiters []*processLink) {
|
||||
for _, pl := range waiters {
|
||||
pl.ClearChild()
|
||||
pr, ok := pl.Parent().(*process)
|
||||
if !ok {
|
||||
// parent has already been called to close
|
||||
continue
|
||||
}
|
||||
pr.Lock()
|
||||
delete(pr.waitfors, pl)
|
||||
delete(pr.children, pl)
|
||||
pr.Unlock()
|
||||
}
|
||||
}(p.waiters) // pass in so
|
||||
p.waiters = nil // clear them. release memory.
|
||||
}
|
||||
|
||||
// We will only wait on the children we have now.
|
||||
// We will not wait on children added subsequently.
|
||||
// this may change in the future.
|
||||
func (p *process) CloseAfterChildren() error {
|
||||
p.Lock()
|
||||
select {
|
||||
case <-p.Closed():
|
||||
p.Unlock()
|
||||
return p.Close() // get error. safe, after p.Closed()
|
||||
case <-p.waiting: // already called it.
|
||||
p.Unlock()
|
||||
<-p.Closed()
|
||||
return p.Close() // get error. safe, after p.Closed()
|
||||
default:
|
||||
}
|
||||
p.Unlock()
|
||||
|
||||
// here only from one goroutine.
|
||||
|
||||
nextToWaitFor := func() Process {
|
||||
p.Lock()
|
||||
defer p.Unlock()
|
||||
for e, _ := range p.waitfors {
|
||||
c := e.Child()
|
||||
if c == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
select {
|
||||
case <-c.Closed():
|
||||
default:
|
||||
return c
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// wait for all processes we're waiting for are closed.
|
||||
// the semantics here are simple: we will _only_ close
|
||||
// if there are no processes currently waiting for.
|
||||
for next := nextToWaitFor(); next != nil; next = nextToWaitFor() {
|
||||
<-next.Closed()
|
||||
}
|
||||
|
||||
// YAY! we're done. close
|
||||
return p.Close()
|
||||
}
|
||||
121
Godeps/_workspace/src/github.com/jbenet/goprocess/link.go
generated
vendored
121
Godeps/_workspace/src/github.com/jbenet/goprocess/link.go
generated
vendored
@ -1,121 +0,0 @@
|
||||
package goprocess
|
||||
|
||||
import (
|
||||
"sync"
|
||||
)
|
||||
|
||||
// closedCh is an alread-closed channel. used to return
|
||||
// in cases where we already know a channel is closed.
|
||||
var closedCh chan struct{}
|
||||
|
||||
func init() {
|
||||
closedCh = make(chan struct{})
|
||||
close(closedCh)
|
||||
}
|
||||
|
||||
// a processLink is an internal bookkeeping datastructure.
|
||||
// it's used to form a relationship between two processes.
|
||||
// It is mostly for keeping memory usage down (letting
|
||||
// children close and be garbage-collected).
|
||||
type processLink struct {
|
||||
// guards all fields.
|
||||
// DO NOT HOLD while holding process locks.
|
||||
// it may be slow, and could deadlock if not careful.
|
||||
sync.Mutex
|
||||
parent Process
|
||||
child Process
|
||||
}
|
||||
|
||||
func newProcessLink(p, c Process) *processLink {
|
||||
return &processLink{
|
||||
parent: p,
|
||||
child: c,
|
||||
}
|
||||
}
|
||||
|
||||
// Closing returns whether the child is closing
|
||||
func (pl *processLink) ChildClosing() <-chan struct{} {
|
||||
// grab a hold of it, and unlock, as .Closing may block.
|
||||
pl.Lock()
|
||||
child := pl.child
|
||||
pl.Unlock()
|
||||
|
||||
if child == nil { // already closed? memory optimization.
|
||||
return closedCh
|
||||
}
|
||||
return child.Closing()
|
||||
}
|
||||
|
||||
func (pl *processLink) ChildClosed() <-chan struct{} {
|
||||
// grab a hold of it, and unlock, as .Closed may block.
|
||||
pl.Lock()
|
||||
child := pl.child
|
||||
pl.Unlock()
|
||||
|
||||
if child == nil { // already closed? memory optimization.
|
||||
return closedCh
|
||||
}
|
||||
return child.Closed()
|
||||
}
|
||||
|
||||
func (pl *processLink) ChildClose() {
|
||||
// grab a hold of it, and unlock, as .Closed may block.
|
||||
pl.Lock()
|
||||
child := pl.child
|
||||
pl.Unlock()
|
||||
|
||||
if child != nil { // already closed? memory optimization.
|
||||
child.Close()
|
||||
}
|
||||
}
|
||||
|
||||
func (pl *processLink) ClearChild() {
|
||||
pl.Lock()
|
||||
pl.child = nil
|
||||
pl.Unlock()
|
||||
}
|
||||
|
||||
func (pl *processLink) ParentClear() {
|
||||
pl.Lock()
|
||||
pl.parent = nil
|
||||
pl.Unlock()
|
||||
}
|
||||
|
||||
func (pl *processLink) Child() Process {
|
||||
pl.Lock()
|
||||
defer pl.Unlock()
|
||||
return pl.child
|
||||
}
|
||||
|
||||
func (pl *processLink) Parent() Process {
|
||||
pl.Lock()
|
||||
defer pl.Unlock()
|
||||
return pl.parent
|
||||
}
|
||||
|
||||
func (pl *processLink) AddToChild() {
|
||||
cp := pl.Child()
|
||||
|
||||
// is it a *process ? if not... panic.
|
||||
c, ok := cp.(*process)
|
||||
if !ok {
|
||||
panic("goprocess does not yet support other process impls.")
|
||||
}
|
||||
|
||||
// first, is it Closed?
|
||||
c.Lock()
|
||||
select {
|
||||
case <-c.Closed():
|
||||
c.Unlock()
|
||||
|
||||
// already closed. must not add.
|
||||
// we must clear it, though. do so without the lock.
|
||||
pl.ClearChild()
|
||||
return
|
||||
|
||||
default:
|
||||
// put the process link into q's waiters
|
||||
c.waiters = append(c.waiters, pl)
|
||||
c.Unlock()
|
||||
}
|
||||
}
|
||||
4
Godeps/_workspace/src/github.com/jbenet/goprocess/periodic/README.md
generated
vendored
4
Godeps/_workspace/src/github.com/jbenet/goprocess/periodic/README.md
generated
vendored
@ -1,4 +0,0 @@
|
||||
# goprocess/periodic - periodic process creation
|
||||
|
||||
- goprocess: https://github.com/jbenet/goprocess
|
||||
- Godoc: https://godoc.org/github.com/jbenet/goprocess/periodic
|
||||
85
Godeps/_workspace/src/github.com/jbenet/goprocess/periodic/examples_test.go
generated
vendored
85
Godeps/_workspace/src/github.com/jbenet/goprocess/periodic/examples_test.go
generated
vendored
@ -1,85 +0,0 @@
|
||||
package periodicproc_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
goprocess "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess"
|
||||
periodicproc "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess/periodic"
|
||||
)
|
||||
|
||||
func ExampleEvery() {
|
||||
tock := make(chan struct{})
|
||||
|
||||
i := 0
|
||||
p := periodicproc.Every(time.Second, func(proc goprocess.Process) {
|
||||
tock <- struct{}{}
|
||||
fmt.Printf("hello %d\n", i)
|
||||
i++
|
||||
})
|
||||
|
||||
<-tock
|
||||
<-tock
|
||||
<-tock
|
||||
p.Close()
|
||||
|
||||
// Output:
|
||||
// hello 0
|
||||
// hello 1
|
||||
// hello 2
|
||||
}
|
||||
|
||||
func ExampleTick() {
|
||||
p := periodicproc.Tick(time.Second, func(proc goprocess.Process) {
|
||||
fmt.Println("tick")
|
||||
})
|
||||
|
||||
<-time.After(3*time.Second + 500*time.Millisecond)
|
||||
p.Close()
|
||||
|
||||
// Output:
|
||||
// tick
|
||||
// tick
|
||||
// tick
|
||||
}
|
||||
|
||||
func ExampleTickGo() {
|
||||
|
||||
// with TickGo, execution is not rate limited,
|
||||
// there can be many in-flight simultaneously
|
||||
|
||||
wait := make(chan struct{})
|
||||
p := periodicproc.TickGo(time.Second, func(proc goprocess.Process) {
|
||||
fmt.Println("tick")
|
||||
<-wait
|
||||
})
|
||||
|
||||
<-time.After(3*time.Second + 500*time.Millisecond)
|
||||
|
||||
wait <- struct{}{}
|
||||
wait <- struct{}{}
|
||||
wait <- struct{}{}
|
||||
p.Close() // blocks us until all children are closed.
|
||||
|
||||
// Output:
|
||||
// tick
|
||||
// tick
|
||||
// tick
|
||||
}
|
||||
|
||||
func ExampleOnSignal() {
|
||||
sig := make(chan struct{})
|
||||
p := periodicproc.OnSignal(sig, func(proc goprocess.Process) {
|
||||
fmt.Println("fire!")
|
||||
})
|
||||
|
||||
sig <- struct{}{}
|
||||
sig <- struct{}{}
|
||||
sig <- struct{}{}
|
||||
p.Close()
|
||||
|
||||
// Output:
|
||||
// fire!
|
||||
// fire!
|
||||
// fire!
|
||||
}
|
||||
232
Godeps/_workspace/src/github.com/jbenet/goprocess/periodic/periodic.go
generated
vendored
232
Godeps/_workspace/src/github.com/jbenet/goprocess/periodic/periodic.go
generated
vendored
@ -1,232 +0,0 @@
|
||||
// Package periodic is part of github.com/jbenet/goprocess.
|
||||
// It provides a simple periodic processor that calls a function
|
||||
// periodically based on some options.
|
||||
//
|
||||
// For example:
|
||||
//
|
||||
// // use a time.Duration
|
||||
// p := periodicproc.Every(time.Second, func(proc goprocess.Process) {
|
||||
// fmt.Printf("the time is %s and all is well", time.Now())
|
||||
// })
|
||||
//
|
||||
// <-time.After(5*time.Second)
|
||||
// p.Close()
|
||||
//
|
||||
// // use a time.Time channel (like time.Ticker)
|
||||
// p := periodicproc.Tick(time.Tick(time.Second), func(proc goprocess.Process) {
|
||||
// fmt.Printf("the time is %s and all is well", time.Now())
|
||||
// })
|
||||
//
|
||||
// <-time.After(5*time.Second)
|
||||
// p.Close()
|
||||
//
|
||||
// // or arbitrary signals
|
||||
// signal := make(chan struct{})
|
||||
// p := periodicproc.OnSignal(signal, func(proc goprocess.Process) {
|
||||
// fmt.Printf("the time is %s and all is well", time.Now())
|
||||
// })
|
||||
//
|
||||
// signal<- struct{}{}
|
||||
// signal<- struct{}{}
|
||||
// <-time.After(5 * time.Second)
|
||||
// signal<- struct{}{}
|
||||
// p.Close()
|
||||
//
|
||||
package periodicproc
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
gp "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess"
|
||||
)
|
||||
|
||||
// Every calls the given ProcessFunc at periodic intervals. Internally, it uses
|
||||
// <-time.After(interval), so it will have the behavior of waiting _at least_
|
||||
// interval in between calls. If you'd prefer the time.Ticker behavior, use
|
||||
// periodicproc.Tick instead.
|
||||
// This is sequentially rate limited, only one call will be in-flight at a time.
|
||||
func Every(interval time.Duration, procfunc gp.ProcessFunc) gp.Process {
|
||||
return gp.Go(func(proc gp.Process) {
|
||||
for {
|
||||
select {
|
||||
case <-time.After(interval):
|
||||
select {
|
||||
case <-proc.Go(procfunc).Closed(): // spin it out as a child, and wait till it's done.
|
||||
case <-proc.Closing(): // we're told to close
|
||||
return
|
||||
}
|
||||
case <-proc.Closing(): // we're told to close
|
||||
return
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// EveryGo calls the given ProcessFunc at periodic intervals. Internally, it uses
|
||||
// <-time.After(interval)
|
||||
// This is not rate limited, multiple calls could be in-flight at the same time.
|
||||
func EveryGo(interval time.Duration, procfunc gp.ProcessFunc) gp.Process {
|
||||
return gp.Go(func(proc gp.Process) {
|
||||
for {
|
||||
select {
|
||||
case <-time.After(interval):
|
||||
proc.Go(procfunc)
|
||||
case <-proc.Closing(): // we're told to close
|
||||
return
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// Tick constructs a ticker with interval, and calls the given ProcessFunc every
|
||||
// time the ticker fires.
|
||||
// This is sequentially rate limited, only one call will be in-flight at a time.
|
||||
//
|
||||
// p := periodicproc.Tick(time.Second, func(proc goprocess.Process) {
|
||||
// fmt.Println("fire!")
|
||||
// })
|
||||
//
|
||||
// <-time.After(3 * time.Second)
|
||||
// p.Close()
|
||||
//
|
||||
// // Output:
|
||||
// // fire!
|
||||
// // fire!
|
||||
// // fire!
|
||||
func Tick(interval time.Duration, procfunc gp.ProcessFunc) gp.Process {
|
||||
return gp.Go(func(proc gp.Process) {
|
||||
ticker := time.NewTicker(interval)
|
||||
callOnTicker(ticker.C, procfunc)(proc)
|
||||
ticker.Stop()
|
||||
})
|
||||
}
|
||||
|
||||
// TickGo constructs a ticker with interval, and calls the given ProcessFunc every
|
||||
// time the ticker fires.
|
||||
// This is not rate limited, multiple calls could be in-flight at the same time.
|
||||
//
|
||||
// p := periodicproc.TickGo(time.Second, func(proc goprocess.Process) {
|
||||
// fmt.Println("fire!")
|
||||
// <-time.After(10 * time.Second) // will not block sequential execution
|
||||
// })
|
||||
//
|
||||
// <-time.After(3 * time.Second)
|
||||
// p.Close()
|
||||
//
|
||||
// // Output:
|
||||
// // fire!
|
||||
// // fire!
|
||||
// // fire!
|
||||
func TickGo(interval time.Duration, procfunc gp.ProcessFunc) gp.Process {
|
||||
return gp.Go(func(proc gp.Process) {
|
||||
ticker := time.NewTicker(interval)
|
||||
goCallOnTicker(ticker.C, procfunc)(proc)
|
||||
ticker.Stop()
|
||||
})
|
||||
}
|
||||
|
||||
// Ticker calls the given ProcessFunc every time the ticker fires.
|
||||
// This is sequentially rate limited, only one call will be in-flight at a time.
|
||||
func Ticker(ticker <-chan time.Time, procfunc gp.ProcessFunc) gp.Process {
|
||||
return gp.Go(callOnTicker(ticker, procfunc))
|
||||
}
|
||||
|
||||
// TickerGo calls the given ProcessFunc every time the ticker fires.
|
||||
// This is not rate limited, multiple calls could be in-flight at the same time.
|
||||
func TickerGo(ticker <-chan time.Time, procfunc gp.ProcessFunc) gp.Process {
|
||||
return gp.Go(goCallOnTicker(ticker, procfunc))
|
||||
}
|
||||
|
||||
func callOnTicker(ticker <-chan time.Time, pf gp.ProcessFunc) gp.ProcessFunc {
|
||||
return func(proc gp.Process) {
|
||||
for {
|
||||
select {
|
||||
case <-ticker:
|
||||
select {
|
||||
case <-proc.Go(pf).Closed(): // spin it out as a child, and wait till it's done.
|
||||
case <-proc.Closing(): // we're told to close
|
||||
return
|
||||
}
|
||||
case <-proc.Closing(): // we're told to close
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func goCallOnTicker(ticker <-chan time.Time, pf gp.ProcessFunc) gp.ProcessFunc {
|
||||
return func(proc gp.Process) {
|
||||
for {
|
||||
select {
|
||||
case <-ticker:
|
||||
proc.Go(pf)
|
||||
case <-proc.Closing(): // we're told to close
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// OnSignal calls the given ProcessFunc every time the signal fires, and waits for it to exit.
|
||||
// This is sequentially rate limited, only one call will be in-flight at a time.
|
||||
//
|
||||
// sig := make(chan struct{})
|
||||
// p := periodicproc.OnSignal(sig, func(proc goprocess.Process) {
|
||||
// fmt.Println("fire!")
|
||||
// <-time.After(time.Second) // delays sequential execution by 1 second
|
||||
// })
|
||||
//
|
||||
// sig<- struct{}
|
||||
// sig<- struct{}
|
||||
// sig<- struct{}
|
||||
//
|
||||
// // Output:
|
||||
// // fire!
|
||||
// // fire!
|
||||
// // fire!
|
||||
func OnSignal(sig <-chan struct{}, procfunc gp.ProcessFunc) gp.Process {
|
||||
return gp.Go(func(proc gp.Process) {
|
||||
for {
|
||||
select {
|
||||
case <-sig:
|
||||
select {
|
||||
case <-proc.Go(procfunc).Closed(): // spin it out as a child, and wait till it's done.
|
||||
case <-proc.Closing(): // we're told to close
|
||||
return
|
||||
}
|
||||
case <-proc.Closing(): // we're told to close
|
||||
return
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// OnSignalGo calls the given ProcessFunc every time the signal fires.
|
||||
// This is not rate limited, multiple calls could be in-flight at the same time.
|
||||
//
|
||||
// sig := make(chan struct{})
|
||||
// p := periodicproc.OnSignalGo(sig, func(proc goprocess.Process) {
|
||||
// fmt.Println("fire!")
|
||||
// <-time.After(time.Second) // wont block execution
|
||||
// })
|
||||
//
|
||||
// sig<- struct{}
|
||||
// sig<- struct{}
|
||||
// sig<- struct{}
|
||||
//
|
||||
// // Output:
|
||||
// // fire!
|
||||
// // fire!
|
||||
// // fire!
|
||||
func OnSignalGo(sig <-chan struct{}, procfunc gp.ProcessFunc) gp.Process {
|
||||
return gp.Go(func(proc gp.Process) {
|
||||
for {
|
||||
select {
|
||||
case <-sig:
|
||||
proc.Go(procfunc)
|
||||
case <-proc.Closing(): // we're told to close
|
||||
return
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
260
Godeps/_workspace/src/github.com/jbenet/goprocess/periodic/periodic_test.go
generated
vendored
260
Godeps/_workspace/src/github.com/jbenet/goprocess/periodic/periodic_test.go
generated
vendored
@ -1,260 +0,0 @@
|
||||
package periodicproc
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
gp "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess"
|
||||
ci "gx/ipfs/QmZcUXuzsUSvxNj9pmU112V8L5kGUFMTYCdFcAbQ3Zj5cp/go-cienv"
|
||||
)
|
||||
|
||||
var (
|
||||
grace = time.Millisecond * 5
|
||||
interval = time.Millisecond * 10
|
||||
timeout = time.Second * 5
|
||||
)
|
||||
|
||||
func init() {
|
||||
if ci.IsRunning() {
|
||||
grace = time.Millisecond * 500
|
||||
interval = time.Millisecond * 1000
|
||||
timeout = time.Second * 15
|
||||
}
|
||||
}
|
||||
|
||||
func between(min, diff, max time.Duration) bool {
|
||||
return min <= diff && diff <= max
|
||||
}
|
||||
|
||||
func testBetween(t *testing.T, min, diff, max time.Duration) {
|
||||
if !between(min, diff, max) {
|
||||
t.Error("time diff incorrect:", min, diff, max)
|
||||
}
|
||||
}
|
||||
|
||||
type intervalFunc func(times chan<- time.Time, wait <-chan struct{}) (proc gp.Process)
|
||||
|
||||
func testSeq(t *testing.T, toTest intervalFunc) {
|
||||
t.Parallel()
|
||||
|
||||
last := time.Now()
|
||||
times := make(chan time.Time, 10)
|
||||
p := toTest(times, nil)
|
||||
|
||||
for i := 0; i < 5; i++ {
|
||||
next := <-times
|
||||
testBetween(t, interval-grace, next.Sub(last), interval+grace)
|
||||
last = next
|
||||
}
|
||||
|
||||
go p.Close()
|
||||
select {
|
||||
case <-p.Closed():
|
||||
case <-time.After(timeout):
|
||||
t.Error("proc failed to close")
|
||||
}
|
||||
}
|
||||
|
||||
func testSeqWait(t *testing.T, toTest intervalFunc) {
|
||||
t.Parallel()
|
||||
|
||||
last := time.Now()
|
||||
times := make(chan time.Time, 10)
|
||||
wait := make(chan struct{})
|
||||
p := toTest(times, wait)
|
||||
|
||||
for i := 0; i < 5; i++ {
|
||||
next := <-times
|
||||
testBetween(t, interval-grace, next.Sub(last), interval+grace)
|
||||
|
||||
<-time.After(interval * 2) // make it wait.
|
||||
last = time.Now() // make it now (sequential)
|
||||
wait <- struct{}{} // release it.
|
||||
}
|
||||
|
||||
go p.Close()
|
||||
|
||||
select {
|
||||
case <-p.Closed():
|
||||
case <-time.After(timeout):
|
||||
t.Error("proc failed to close")
|
||||
}
|
||||
}
|
||||
|
||||
func testSeqNoWait(t *testing.T, toTest intervalFunc) {
|
||||
t.Parallel()
|
||||
|
||||
last := time.Now()
|
||||
times := make(chan time.Time, 10)
|
||||
wait := make(chan struct{})
|
||||
p := toTest(times, wait)
|
||||
|
||||
for i := 0; i < 5; i++ {
|
||||
next := <-times
|
||||
testBetween(t, 0, next.Sub(last), interval+grace) // min of 0
|
||||
|
||||
<-time.After(interval * 2) // make it wait.
|
||||
last = time.Now() // make it now (sequential)
|
||||
wait <- struct{}{} // release it.
|
||||
}
|
||||
|
||||
go p.Close()
|
||||
|
||||
end:
|
||||
select {
|
||||
case wait <- struct{}{}: // drain any extras.
|
||||
goto end
|
||||
case <-p.Closed():
|
||||
case <-time.After(timeout):
|
||||
t.Error("proc failed to close")
|
||||
}
|
||||
}
|
||||
|
||||
func testParallel(t *testing.T, toTest intervalFunc) {
|
||||
t.Parallel()
|
||||
|
||||
last := time.Now()
|
||||
times := make(chan time.Time, 10)
|
||||
wait := make(chan struct{})
|
||||
p := toTest(times, wait)
|
||||
|
||||
for i := 0; i < 5; i++ {
|
||||
next := <-times
|
||||
testBetween(t, interval-grace, next.Sub(last), interval+grace)
|
||||
last = next
|
||||
|
||||
<-time.After(interval * 2) // make it wait.
|
||||
wait <- struct{}{} // release it.
|
||||
}
|
||||
|
||||
go p.Close()
|
||||
|
||||
end:
|
||||
select {
|
||||
case wait <- struct{}{}: // drain any extras.
|
||||
goto end
|
||||
case <-p.Closed():
|
||||
case <-time.After(timeout):
|
||||
t.Error("proc failed to close")
|
||||
}
|
||||
}
|
||||
|
||||
func TestEverySeq(t *testing.T) {
|
||||
testSeq(t, func(times chan<- time.Time, wait <-chan struct{}) (proc gp.Process) {
|
||||
return Every(interval, func(proc gp.Process) {
|
||||
times <- time.Now()
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func TestEverySeqWait(t *testing.T) {
|
||||
testSeqWait(t, func(times chan<- time.Time, wait <-chan struct{}) (proc gp.Process) {
|
||||
return Every(interval, func(proc gp.Process) {
|
||||
times <- time.Now()
|
||||
select {
|
||||
case <-wait:
|
||||
case <-proc.Closing():
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func TestEveryGoSeq(t *testing.T) {
|
||||
testSeq(t, func(times chan<- time.Time, wait <-chan struct{}) (proc gp.Process) {
|
||||
return EveryGo(interval, func(proc gp.Process) {
|
||||
times <- time.Now()
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func TestEveryGoSeqParallel(t *testing.T) {
|
||||
testParallel(t, func(times chan<- time.Time, wait <-chan struct{}) (proc gp.Process) {
|
||||
return EveryGo(interval, func(proc gp.Process) {
|
||||
times <- time.Now()
|
||||
select {
|
||||
case <-wait:
|
||||
case <-proc.Closing():
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func TestTickSeq(t *testing.T) {
|
||||
testSeq(t, func(times chan<- time.Time, wait <-chan struct{}) (proc gp.Process) {
|
||||
return Tick(interval, func(proc gp.Process) {
|
||||
times <- time.Now()
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func TestTickSeqNoWait(t *testing.T) {
|
||||
testSeqNoWait(t, func(times chan<- time.Time, wait <-chan struct{}) (proc gp.Process) {
|
||||
return Tick(interval, func(proc gp.Process) {
|
||||
times <- time.Now()
|
||||
select {
|
||||
case <-wait:
|
||||
case <-proc.Closing():
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func TestTickGoSeq(t *testing.T) {
|
||||
testSeq(t, func(times chan<- time.Time, wait <-chan struct{}) (proc gp.Process) {
|
||||
return TickGo(interval, func(proc gp.Process) {
|
||||
times <- time.Now()
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func TestTickGoSeqParallel(t *testing.T) {
|
||||
testParallel(t, func(times chan<- time.Time, wait <-chan struct{}) (proc gp.Process) {
|
||||
return TickGo(interval, func(proc gp.Process) {
|
||||
times <- time.Now()
|
||||
select {
|
||||
case <-wait:
|
||||
case <-proc.Closing():
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func TestTickerSeq(t *testing.T) {
|
||||
testSeq(t, func(times chan<- time.Time, wait <-chan struct{}) (proc gp.Process) {
|
||||
return Ticker(time.Tick(interval), func(proc gp.Process) {
|
||||
times <- time.Now()
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func TestTickerSeqNoWait(t *testing.T) {
|
||||
testSeqNoWait(t, func(times chan<- time.Time, wait <-chan struct{}) (proc gp.Process) {
|
||||
return Ticker(time.Tick(interval), func(proc gp.Process) {
|
||||
times <- time.Now()
|
||||
select {
|
||||
case <-wait:
|
||||
case <-proc.Closing():
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func TestTickerGoSeq(t *testing.T) {
|
||||
testSeq(t, func(times chan<- time.Time, wait <-chan struct{}) (proc gp.Process) {
|
||||
return TickerGo(time.Tick(interval), func(proc gp.Process) {
|
||||
times <- time.Now()
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func TestTickerGoParallel(t *testing.T) {
|
||||
testParallel(t, func(times chan<- time.Time, wait <-chan struct{}) (proc gp.Process) {
|
||||
return TickerGo(time.Tick(interval), func(proc gp.Process) {
|
||||
times <- time.Now()
|
||||
select {
|
||||
case <-wait:
|
||||
case <-proc.Closing():
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
4
Godeps/_workspace/src/github.com/jbenet/goprocess/ratelimit/README.md
generated
vendored
4
Godeps/_workspace/src/github.com/jbenet/goprocess/ratelimit/README.md
generated
vendored
@ -1,4 +0,0 @@
|
||||
# goprocess/ratelimit - ratelimit children creation
|
||||
|
||||
- goprocess: https://github.com/jbenet/goprocess
|
||||
- Godoc: https://godoc.org/github.com/jbenet/goprocess/ratelimit
|
||||
68
Godeps/_workspace/src/github.com/jbenet/goprocess/ratelimit/ratelimit.go
generated
vendored
68
Godeps/_workspace/src/github.com/jbenet/goprocess/ratelimit/ratelimit.go
generated
vendored
@ -1,68 +0,0 @@
|
||||
// Package ratelimit is part of github.com/jbenet/goprocess.
|
||||
// It provides a simple process that ratelimits child creation.
|
||||
// This is done internally with a channel/semaphore.
|
||||
// So the call `RateLimiter.LimitedGo` may block until another
|
||||
// child is Closed().
|
||||
package ratelimit
|
||||
|
||||
import (
|
||||
process "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess"
|
||||
)
|
||||
|
||||
// RateLimiter limits the spawning of children. It does so
|
||||
// with an internal semaphore. Note that Go will continue
|
||||
// to be the unlimited process.Process.Go, and ONLY the
|
||||
// added function `RateLimiter.LimitedGo` will honor the
|
||||
// limit. This is to improve readability and avoid confusion
|
||||
// for the reader, particularly if code changes over time.
|
||||
type RateLimiter struct {
|
||||
process.Process
|
||||
|
||||
limiter chan struct{}
|
||||
}
|
||||
|
||||
func NewRateLimiter(parent process.Process, limit int) *RateLimiter {
|
||||
proc := process.WithParent(parent)
|
||||
return &RateLimiter{Process: proc, limiter: LimitChan(limit)}
|
||||
}
|
||||
|
||||
// LimitedGo creates a new process, adds it as a child, and spawns the
|
||||
// ProcessFunc f in its own goroutine, but may block according to the
|
||||
// internal rate limit. It is equivalent to:
|
||||
//
|
||||
// func(f process.ProcessFunc) {
|
||||
// <-limitch
|
||||
// p.Go(func (child process.Process) {
|
||||
// f(child)
|
||||
// f.Close() // make sure its children close too!
|
||||
// limitch<- struct{}{}
|
||||
// })
|
||||
/// }
|
||||
//
|
||||
// It is useful to construct simple asynchronous workers, children of p,
|
||||
// and rate limit their creation, to avoid spinning up too many, too fast.
|
||||
// This is great for providing backpressure to producers.
|
||||
func (rl *RateLimiter) LimitedGo(f process.ProcessFunc) {
|
||||
|
||||
<-rl.limiter
|
||||
p := rl.Go(f)
|
||||
|
||||
// this <-closed() is here because the child may have spawned
|
||||
// children of its own, and our rate limiter should capture that.
|
||||
go func() {
|
||||
<-p.Closed()
|
||||
rl.limiter <- struct{}{}
|
||||
}()
|
||||
}
|
||||
|
||||
// LimitChan returns a rate-limiting channel. it is the usual, simple,
|
||||
// golang-idiomatic rate-limiting semaphore. This function merely
|
||||
// initializes it with certain buffer size, and sends that many values,
|
||||
// so it is ready to be used.
|
||||
func LimitChan(limit int) chan struct{} {
|
||||
limitch := make(chan struct{}, limit)
|
||||
for i := 0; i < limit; i++ {
|
||||
limitch <- struct{}{}
|
||||
}
|
||||
return limitch
|
||||
}
|
||||
98
Godeps/_workspace/src/github.com/jbenet/goprocess/ratelimit/ratelimit_test.go
generated
vendored
98
Godeps/_workspace/src/github.com/jbenet/goprocess/ratelimit/ratelimit_test.go
generated
vendored
@ -1,98 +0,0 @@
|
||||
package ratelimit
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
process "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess"
|
||||
)
|
||||
|
||||
func TestRateLimitLimitedGoBlocks(t *testing.T) {
|
||||
numChildren := 6
|
||||
|
||||
t.Logf("create a rate limiter with limit of %d", numChildren/2)
|
||||
rl := NewRateLimiter(process.Background(), numChildren/2)
|
||||
|
||||
doneSpawning := make(chan struct{})
|
||||
childClosing := make(chan struct{})
|
||||
|
||||
t.Log("spawn 6 children with LimitedGo.")
|
||||
go func() {
|
||||
for i := 0; i < numChildren; i++ {
|
||||
rl.LimitedGo(func(child process.Process) {
|
||||
// hang until we drain childClosing
|
||||
childClosing <- struct{}{}
|
||||
})
|
||||
t.Logf("spawned %d", i)
|
||||
}
|
||||
close(doneSpawning)
|
||||
}()
|
||||
|
||||
t.Log("should have blocked.")
|
||||
select {
|
||||
case <-doneSpawning:
|
||||
t.Error("did not block")
|
||||
case <-time.After(time.Millisecond): // for scheduler
|
||||
t.Log("blocked")
|
||||
}
|
||||
|
||||
t.Logf("drain %d children so they close", numChildren/2)
|
||||
for i := 0; i < numChildren/2; i++ {
|
||||
t.Logf("closing %d", i)
|
||||
<-childClosing // consume child cloing
|
||||
t.Logf("closed %d", i)
|
||||
}
|
||||
|
||||
t.Log("should be done spawning.")
|
||||
select {
|
||||
case <-doneSpawning:
|
||||
case <-time.After(100 * time.Millisecond): // for scheduler
|
||||
t.Error("still blocked...")
|
||||
}
|
||||
|
||||
t.Logf("drain %d children so they close", numChildren/2)
|
||||
for i := 0; i < numChildren/2; i++ {
|
||||
<-childClosing
|
||||
t.Logf("closed %d", i)
|
||||
}
|
||||
|
||||
rl.Close() // ensure everyone's closed.
|
||||
}
|
||||
|
||||
func TestRateLimitGoDoesntBlock(t *testing.T) {
|
||||
numChildren := 6
|
||||
|
||||
t.Logf("create a rate limiter with limit of %d", numChildren/2)
|
||||
rl := NewRateLimiter(process.Background(), numChildren/2)
|
||||
|
||||
doneSpawning := make(chan struct{})
|
||||
childClosing := make(chan struct{})
|
||||
|
||||
t.Log("spawn 6 children with usual Process.Go.")
|
||||
go func() {
|
||||
for i := 0; i < numChildren; i++ {
|
||||
rl.Go(func(child process.Process) {
|
||||
// hang until we drain childClosing
|
||||
childClosing <- struct{}{}
|
||||
})
|
||||
t.Logf("spawned %d", i)
|
||||
}
|
||||
close(doneSpawning)
|
||||
}()
|
||||
|
||||
t.Log("should not have blocked.")
|
||||
select {
|
||||
case <-doneSpawning:
|
||||
t.Log("did not block")
|
||||
case <-time.After(100 * time.Millisecond): // for scheduler
|
||||
t.Error("process.Go blocked. it should not.")
|
||||
}
|
||||
|
||||
t.Log("drain children so they close")
|
||||
for i := 0; i < numChildren; i++ {
|
||||
<-childClosing
|
||||
t.Logf("closed %d", i)
|
||||
}
|
||||
|
||||
rl.Close() // ensure everyone's closed.
|
||||
}
|
||||
@ -7,7 +7,6 @@ import (
|
||||
"os/signal"
|
||||
"path/filepath"
|
||||
|
||||
process "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess"
|
||||
homedir "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/mitchellh/go-homedir"
|
||||
fsnotify "github.com/ipfs/go-ipfs/Godeps/_workspace/src/gopkg.in/fsnotify.v1"
|
||||
commands "github.com/ipfs/go-ipfs/commands"
|
||||
@ -16,6 +15,7 @@ import (
|
||||
coreunix "github.com/ipfs/go-ipfs/core/coreunix"
|
||||
config "github.com/ipfs/go-ipfs/repo/config"
|
||||
fsrepo "github.com/ipfs/go-ipfs/repo/fsrepo"
|
||||
process "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess"
|
||||
context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context"
|
||||
)
|
||||
|
||||
|
||||
@ -15,9 +15,9 @@ import (
|
||||
inet "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/net"
|
||||
peer "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/peer"
|
||||
|
||||
goprocess "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess"
|
||||
procctx "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess/context"
|
||||
periodicproc "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess/periodic"
|
||||
goprocess "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess"
|
||||
procctx "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess/context"
|
||||
periodicproc "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess/periodic"
|
||||
context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context"
|
||||
)
|
||||
|
||||
|
||||
@ -7,7 +7,6 @@ import (
|
||||
|
||||
ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore"
|
||||
dsync "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore/sync"
|
||||
goprocessctx "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess/context"
|
||||
bstore "github.com/ipfs/go-ipfs/blocks/blockstore"
|
||||
key "github.com/ipfs/go-ipfs/blocks/key"
|
||||
bserv "github.com/ipfs/go-ipfs/blockservice"
|
||||
@ -17,6 +16,7 @@ import (
|
||||
pin "github.com/ipfs/go-ipfs/pin"
|
||||
repo "github.com/ipfs/go-ipfs/repo"
|
||||
cfg "github.com/ipfs/go-ipfs/repo/config"
|
||||
goprocessctx "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess/context"
|
||||
ci "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/crypto"
|
||||
peer "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/peer"
|
||||
context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context"
|
||||
|
||||
@ -18,9 +18,9 @@ import (
|
||||
|
||||
ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore"
|
||||
b58 "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-base58"
|
||||
goprocess "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess"
|
||||
mamask "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/whyrusleeping/multiaddr-filter"
|
||||
diag "github.com/ipfs/go-ipfs/diagnostics"
|
||||
goprocess "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess"
|
||||
ma "gx/ipfs/QmR3JkmZBKYXgNMNsNZawm914455Qof3PEopwuVSeXG7aV/go-multiaddr"
|
||||
ic "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/crypto"
|
||||
discovery "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/discovery"
|
||||
|
||||
@ -10,8 +10,8 @@ import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess"
|
||||
core "github.com/ipfs/go-ipfs/core"
|
||||
"gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess"
|
||||
ma "gx/ipfs/QmR3JkmZBKYXgNMNsNZawm914455Qof3PEopwuVSeXG7aV/go-multiaddr"
|
||||
manet "gx/ipfs/QmYtzQmUwPFGxjCXctJ8e6GXS8sYfoXy2pdeMbS5SFWqRi/go-multiaddr-net"
|
||||
logging "gx/ipfs/Qmazh5oNUVsDZTs2g59rq8aYQqwpss8tcUWQzor5sCCEuH/go-log"
|
||||
|
||||
@ -8,8 +8,6 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
process "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess"
|
||||
procctx "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess/context"
|
||||
blocks "github.com/ipfs/go-ipfs/blocks"
|
||||
blockstore "github.com/ipfs/go-ipfs/blocks/blockstore"
|
||||
key "github.com/ipfs/go-ipfs/blocks/key"
|
||||
@ -20,6 +18,8 @@ import (
|
||||
notifications "github.com/ipfs/go-ipfs/exchange/bitswap/notifications"
|
||||
wantlist "github.com/ipfs/go-ipfs/exchange/bitswap/wantlist"
|
||||
"github.com/ipfs/go-ipfs/thirdparty/delay"
|
||||
process "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess"
|
||||
procctx "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess/context"
|
||||
peer "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/peer"
|
||||
context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context"
|
||||
logging "gx/ipfs/Qmazh5oNUVsDZTs2g59rq8aYQqwpss8tcUWQzor5sCCEuH/go-log"
|
||||
|
||||
@ -3,8 +3,8 @@ package bitswap
|
||||
import (
|
||||
"time"
|
||||
|
||||
process "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess"
|
||||
procctx "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess/context"
|
||||
process "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess"
|
||||
procctx "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess/context"
|
||||
context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context"
|
||||
|
||||
key "github.com/ipfs/go-ipfs/blocks/key"
|
||||
|
||||
@ -11,7 +11,8 @@ import (
|
||||
|
||||
"github.com/ipfs/go-ipfs/Godeps/_workspace/src/bazil.org/fuse"
|
||||
"github.com/ipfs/go-ipfs/Godeps/_workspace/src/bazil.org/fuse/fs"
|
||||
"github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess"
|
||||
|
||||
goprocess "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess"
|
||||
)
|
||||
|
||||
var ErrNotMounted = errors.New("not mounted")
|
||||
|
||||
@ -8,8 +8,7 @@ import (
|
||||
"runtime"
|
||||
"time"
|
||||
|
||||
goprocess "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess"
|
||||
|
||||
goprocess "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess"
|
||||
logging "gx/ipfs/Qmazh5oNUVsDZTs2g59rq8aYQqwpss8tcUWQzor5sCCEuH/go-log"
|
||||
)
|
||||
|
||||
|
||||
@ -15,8 +15,8 @@ import (
|
||||
|
||||
proto "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/gogo/protobuf/proto"
|
||||
ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore"
|
||||
goprocess "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess"
|
||||
gpctx "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess/context"
|
||||
goprocess "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess"
|
||||
gpctx "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess/context"
|
||||
context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context"
|
||||
logging "gx/ipfs/Qmazh5oNUVsDZTs2g59rq8aYQqwpss8tcUWQzor5sCCEuH/go-log"
|
||||
)
|
||||
|
||||
@ -5,7 +5,7 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
goprocess "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess"
|
||||
goprocess "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess"
|
||||
context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context"
|
||||
|
||||
"github.com/ipfs/go-ipfs/core"
|
||||
|
||||
@ -16,6 +16,11 @@
|
||||
"name": "go-net",
|
||||
"hash": "QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt",
|
||||
"version": "0.0.0"
|
||||
},
|
||||
{
|
||||
"name": "goprocess",
|
||||
"hash": "QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn",
|
||||
"version": "0.0.0"
|
||||
}
|
||||
],
|
||||
"language": "go",
|
||||
|
||||
@ -22,8 +22,8 @@ import (
|
||||
|
||||
proto "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/gogo/protobuf/proto"
|
||||
ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore"
|
||||
goprocess "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess"
|
||||
goprocessctx "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess/context"
|
||||
goprocess "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess"
|
||||
goprocessctx "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess/context"
|
||||
context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context"
|
||||
)
|
||||
|
||||
|
||||
@ -12,8 +12,8 @@ import (
|
||||
u "github.com/ipfs/go-ipfs/util"
|
||||
peer "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/peer"
|
||||
|
||||
goprocess "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess"
|
||||
periodicproc "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess/periodic"
|
||||
goprocess "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess"
|
||||
periodicproc "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess/periodic"
|
||||
context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context"
|
||||
)
|
||||
|
||||
|
||||
@ -3,9 +3,9 @@ package dht
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess"
|
||||
goprocessctx "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess/context"
|
||||
key "github.com/ipfs/go-ipfs/blocks/key"
|
||||
goprocess "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess"
|
||||
goprocessctx "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess/context"
|
||||
peer "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/peer"
|
||||
|
||||
context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context"
|
||||
|
||||
@ -13,8 +13,8 @@ import (
|
||||
queue "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/peer/queue"
|
||||
logging "gx/ipfs/Qmazh5oNUVsDZTs2g59rq8aYQqwpss8tcUWQzor5sCCEuH/go-log"
|
||||
|
||||
process "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess"
|
||||
ctxproc "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess/context"
|
||||
process "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess"
|
||||
ctxproc "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess/context"
|
||||
context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context"
|
||||
)
|
||||
|
||||
|
||||
4
thirdparty/notifier/notifier.go
vendored
4
thirdparty/notifier/notifier.go
vendored
@ -6,8 +6,8 @@ package notifier
|
||||
import (
|
||||
"sync"
|
||||
|
||||
process "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess"
|
||||
ratelimit "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess/ratelimit"
|
||||
process "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess"
|
||||
ratelimit "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess/ratelimit"
|
||||
)
|
||||
|
||||
// Notifiee is a generic interface. Clients implement
|
||||
|
||||
Loading…
Reference in New Issue
Block a user