Commit a1f3609e authored by Sameer Ajmani's avatar Sameer Ajmani

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
parent 8734c452
......@@ -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")
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment