trpc.group/trpc-go/trpc-go@v1.0.3/http/value_detached_ctx_test.go (about) 1 // 2 // 3 // Tencent is pleased to support the open source community by making tRPC available. 4 // 5 // Copyright (C) 2023 THL A29 Limited, a Tencent company. 6 // All rights reserved. 7 // 8 // If you have downloaded a copy of the tRPC source code from Tencent, 9 // please note that tRPC source code is licensed under the Apache 2.0 License, 10 // A copy of the Apache 2.0 License is included in this file. 11 // 12 // 13 14 package http 15 16 import ( 17 "context" 18 "runtime" 19 "testing" 20 "time" 21 22 "github.com/stretchr/testify/require" 23 ) 24 25 type ctxKey struct{} 26 27 func TestValueDetachedCtxDeadline(t *testing.T) { 28 ctx, cancel := context.WithTimeout(context.Background(), time.Millisecond*10) 29 defer cancel() 30 detachedCtx := detachCtxValue(ctx) 31 deadline, ok := ctx.Deadline() 32 newDeadline, newOk := detachedCtx.Deadline() 33 require.Equal(t, ok, newOk) 34 require.Equal(t, deadline, newDeadline) 35 <-ctx.Done() 36 newDeadline, newOk = detachedCtx.Deadline() 37 require.Equal(t, ok, newOk, "deadline should not change after ctx done") 38 require.Equal(t, deadline, newDeadline, "deadline should not change after ctx done") 39 } 40 41 func TestValueDetachedCtxDone(t *testing.T) { 42 ctx, cancel := context.WithTimeout(context.Background(), time.Millisecond*10) 43 defer cancel() 44 ctx = detachCtxValue(context.WithValue(ctx, ctxKey{}, struct{}{})) 45 require.Nil(t, ctx.Err(), "should not timed out yet") 46 deadline, ok := ctx.Deadline() 47 require.True(t, ok) 48 require.NotEqual(t, deadline, time.Time{}) 49 <-ctx.Done() 50 // Sleep a while to wait ctx done propagate to detached ctx. 51 time.Sleep(time.Millisecond * 20) 52 require.NotNil(t, ctx.Err(), "ctx has timed out") 53 } 54 55 func TestValueDetachedCtxValue(t *testing.T) { 56 ctx, cancel := context.WithCancel(context.Background()) 57 defer cancel() 58 ctx = context.WithValue(ctx, ctxKey{}, struct{}{}) 59 ctx = detachCtxValue(ctx) 60 require.Equal(t, ctx.Value(ctxKey{}), nil) 61 } 62 63 func TestValueDetachedCtxGC(t *testing.T) { 64 newValueCtx := func() (ctx context.Context, valGCed func() bool) { 65 var i int 66 iGCed := make(chan struct{}) 67 runtime.SetFinalizer(&i, func(*int) { 68 close(iGCed) 69 }) 70 return context.WithValue(context.Background(), ctxKey{}, &i), func() bool { 71 // Wait a while before finalizer could run. 72 select { 73 case <-time.After(time.Millisecond * 100): 74 return false 75 case <-iGCed: 76 return true 77 } 78 } 79 } 80 81 ctx, valGCed := newValueCtx() 82 _ = detachCtxValue(ctx) 83 84 // The original ctx is only swept in second GC circle due to go's tri-color GC algorithm. 85 runtime.GC() 86 runtime.GC() 87 require.True(t, valGCed(), "allocated val should be GCed") 88 } 89 90 func TestValueDetachedCtxGCCancelableCtx(t *testing.T) { 91 newValueCtx := func() (ctx context.Context, cancel func(), valGCed func() bool) { 92 var i int 93 iGCed := make(chan struct{}) 94 runtime.SetFinalizer(&i, func(*int) { 95 close(iGCed) 96 }) 97 ctx, cancel = context.WithCancel(context.Background()) 98 return context.WithValue(ctx, ctxKey{}, &i), 99 cancel, 100 func() bool { 101 // Wait a while before finalizer could run. 102 select { 103 case <-time.After(time.Millisecond * 100): 104 return false 105 case <-iGCed: 106 return true 107 } 108 } 109 } 110 111 ctx, cancel, valGCed := newValueCtx() 112 _ = detachCtxValue(ctx) 113 114 // The original ctx is not swept before second GC circle due to go's tri-color GC algorithm. 115 runtime.GC() 116 runtime.GC() 117 require.False(t, valGCed(), "allocated resource can not be GCed before cancel original ctx") 118 119 cancel() 120 runtime.GC() 121 runtime.GC() 122 require.True(t, valGCed(), "allocated resource should be GCed after cancel original ctx") 123 }