mirror of
https://github.com/golang/net.git
synced 2026-03-31 10:27:08 +09:00
go.net/context: define an emptyCtx type for Background and TODO.
This is the first step in splitting the various context implementations into smaller types. Add a test that creates a large, random stack of Contexts and tests that they work properly. LGTM=crawshaw R=crawshaw CC=golang-codereviews https://golang.org/cl/115350044
This commit is contained in:
@@ -183,8 +183,38 @@ func (c *ctx) Value(key interface{}) interface{} {
|
||||
return nil
|
||||
}
|
||||
|
||||
// The background context for this process.
|
||||
var background = newCtx(nil, neverCanceled)
|
||||
type emptyCtx int
|
||||
|
||||
func (emptyCtx) Deadline() (deadline time.Time, ok bool) {
|
||||
return
|
||||
}
|
||||
|
||||
func (emptyCtx) Done() <-chan struct{} {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (emptyCtx) Err() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (emptyCtx) Value(key interface{}) interface{} {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n emptyCtx) String() string {
|
||||
switch n {
|
||||
case background:
|
||||
return "context.Background"
|
||||
case todo:
|
||||
return "context.TODO"
|
||||
}
|
||||
return "unknown empty Context"
|
||||
}
|
||||
|
||||
const (
|
||||
background emptyCtx = 1
|
||||
todo emptyCtx = 2
|
||||
)
|
||||
|
||||
// Background returns a non-nil, empty Context. It is never canceled, has no
|
||||
// values, and has no deadline. It is typically used by the main function,
|
||||
@@ -200,7 +230,7 @@ func Background() Context {
|
||||
// parameter). TODO is recognized by static analysis tools that determine
|
||||
// whether Contexts are propagated correctly in a program.
|
||||
func TODO() Context {
|
||||
return Background()
|
||||
return todo
|
||||
}
|
||||
|
||||
// A CancelFunc tells an operation to abandon its work.
|
||||
|
||||
@@ -6,6 +6,7 @@ package context
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"runtime"
|
||||
"sync"
|
||||
"testing"
|
||||
@@ -28,6 +29,24 @@ func TestBackground(t *testing.T) {
|
||||
t.Errorf("<-c.Done() == %v want nothing (it should block)", x)
|
||||
default:
|
||||
}
|
||||
if s := fmt.Sprint(c); s != "context.Background" {
|
||||
t.Errorf(`Background.String = %q want "context.Background"`, s)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTODO(t *testing.T) {
|
||||
c := TODO()
|
||||
if c == nil {
|
||||
t.Fatalf("TODO returned nil")
|
||||
}
|
||||
select {
|
||||
case x := <-c.Done():
|
||||
t.Errorf("<-c.Done() == %v want nothing (it should block)", x)
|
||||
default:
|
||||
}
|
||||
if s := fmt.Sprint(c); s != "context.TODO" {
|
||||
t.Errorf(`TODO.String = %q want "context.TODO"`, s)
|
||||
}
|
||||
}
|
||||
|
||||
func TestWithCancel(t *testing.T) {
|
||||
@@ -429,3 +448,79 @@ func TestInterlockedCancels(t *testing.T) {
|
||||
t.Fatalf("timed out waiting for child.Done(); stacks:\n%s", buf[:n])
|
||||
}
|
||||
}
|
||||
|
||||
func TestLayersCancel(t *testing.T) {
|
||||
testLayers(t, time.Now().UnixNano(), false)
|
||||
}
|
||||
|
||||
func TestLayersTimeout(t *testing.T) {
|
||||
testLayers(t, time.Now().UnixNano(), true)
|
||||
}
|
||||
|
||||
func testLayers(t *testing.T, seed int64, testTimeout bool) {
|
||||
rand.Seed(seed)
|
||||
errorf := func(format string, a ...interface{}) {
|
||||
t.Errorf(fmt.Sprintf("seed=%d: %s", seed, format), a...)
|
||||
}
|
||||
const (
|
||||
timeout = 200 * time.Millisecond
|
||||
minLayers = 30
|
||||
)
|
||||
type value int
|
||||
var (
|
||||
vals []*value
|
||||
cancels []CancelFunc
|
||||
numTimers int
|
||||
ctx = Background()
|
||||
)
|
||||
for i := 0; i < minLayers || numTimers == 0 || len(cancels) == 0 || len(vals) == 0; i++ {
|
||||
switch rand.Intn(3) {
|
||||
case 0:
|
||||
v := new(value)
|
||||
t.Logf("WithValue(%p, %p)", v, v)
|
||||
ctx = WithValue(ctx, v, v)
|
||||
vals = append(vals, v)
|
||||
case 1:
|
||||
var cancel CancelFunc
|
||||
t.Logf("WithCancel")
|
||||
ctx, cancel = WithCancel(ctx)
|
||||
cancels = append(cancels, cancel)
|
||||
case 2:
|
||||
var cancel CancelFunc
|
||||
t.Logf("WithTimeout")
|
||||
ctx, cancel = WithTimeout(ctx, timeout)
|
||||
cancels = append(cancels, cancel)
|
||||
numTimers++
|
||||
}
|
||||
}
|
||||
checkValues := func(when string) {
|
||||
for _, key := range vals {
|
||||
if val := ctx.Value(key).(*value); key != val {
|
||||
errorf("%s: ctx.Value(%p) = %p want %p", when, key, val, key)
|
||||
}
|
||||
}
|
||||
}
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
errorf("ctx should not be canceled yet")
|
||||
default:
|
||||
}
|
||||
checkValues("before cancel")
|
||||
if testTimeout {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
case <-time.After(timeout + timeout/10):
|
||||
errorf("ctx should have timed out")
|
||||
}
|
||||
checkValues("after timeout")
|
||||
} else {
|
||||
cancel := cancels[rand.Intn(len(cancels))]
|
||||
cancel()
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
default:
|
||||
errorf("ctx should be canceled")
|
||||
}
|
||||
checkValues("after cancel")
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user