瀏覽代碼

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

Sameer Ajmani 11 年之前
父節點
當前提交
a1f3609ec9
共有 2 個文件被更改,包括 128 次插入3 次删除
  1. 33 3
      context/context.go
  2. 95 0
      context/context_test.go

+ 33 - 3
context/context.go

@@ -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.

+ 95 - 0
context/context_test.go

@@ -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")
+	}
+}