trpc.group/trpc-go/trpc-go@v1.0.3/http/value_detached_ctx.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  	"sync"
    19  	"time"
    20  )
    21  
    22  // valueDetachedCtx removes all values associated with ctx while
    23  // ensuring the transitivity of ctx timeout/cancel.
    24  // After the original ctx timeout/cancel, valueDetachedCtx must release
    25  // the original ctx to ensure that the resources associated with
    26  // original ctx can be GC normally.
    27  type valueDetachedCtx struct {
    28  	mu  sync.Mutex
    29  	ctx context.Context
    30  }
    31  
    32  // detachCtxValue creates a new valueDetachedCtx from ctx.
    33  func detachCtxValue(ctx context.Context) context.Context {
    34  	if ctx.Done() == nil {
    35  		return context.Background()
    36  	}
    37  	c := valueDetachedCtx{ctx: ctx}
    38  	go func() {
    39  		<-ctx.Done()
    40  		deadline, ok := ctx.Deadline()
    41  		c.mu.Lock()
    42  		c.ctx = &ctxRemnant{
    43  			deadline:    deadline,
    44  			hasDeadline: ok,
    45  			err:         ctx.Err(),
    46  			done:        ctx.Done(),
    47  		}
    48  		c.mu.Unlock()
    49  	}()
    50  	return &c
    51  }
    52  
    53  // Deadline implements the Deadline method of Context.
    54  func (c *valueDetachedCtx) Deadline() (time.Time, bool) {
    55  	c.mu.Lock()
    56  	defer c.mu.Unlock()
    57  	return c.ctx.Deadline()
    58  }
    59  
    60  // Done implements Done method of Context.
    61  func (c *valueDetachedCtx) Done() <-chan struct{} {
    62  	c.mu.Lock()
    63  	defer c.mu.Unlock()
    64  	return c.ctx.Done()
    65  }
    66  
    67  // Err implements Err method of Context.
    68  func (c *valueDetachedCtx) Err() error {
    69  	c.mu.Lock()
    70  	defer c.mu.Unlock()
    71  	return c.ctx.Err()
    72  }
    73  
    74  // Value always returns nil.
    75  func (c *valueDetachedCtx) Value(_ interface{}) interface{} {
    76  	return nil
    77  }
    78  
    79  // ctxRemnant is the remnant of valueDetachedCtx after timeout/cancel,
    80  // retains some information of the original ctx, ensure that the original ctx
    81  // can be GC normally.
    82  type ctxRemnant struct {
    83  	deadline    time.Time
    84  	hasDeadline bool
    85  	err         error
    86  	done        <-chan struct{}
    87  }
    88  
    89  // Deadline returns the saved readline information.
    90  func (c *ctxRemnant) Deadline() (time.Time, bool) {
    91  	return c.deadline, c.hasDeadline
    92  }
    93  
    94  // Done returns saved Done channel.
    95  func (c *ctxRemnant) Done() <-chan struct{} {
    96  	return c.done
    97  }
    98  
    99  // Err returns saved error.
   100  func (c *ctxRemnant) Err() error {
   101  	return c.err
   102  }
   103  
   104  // Value always returns nil.
   105  func (c *ctxRemnant) Value(_ interface{}) interface{} {
   106  	return nil
   107  }