github.com/Schaudge/grailbase@v0.0.0-20240223061707-44c758a471c0/backgroundcontext/backgroundcontext.go (about)

     1  // Copyright 2019 GRAIL, Inc. All rights reserved.
     2  // Use of this source code is governed by the Apache-2.0
     3  // license that can be found in the LICENSE file.
     4  
     5  // Package backgroundcontext manages the singleton v23 context.  This
     6  // package is not for general use. It it only for packages that (1)
     7  // need to access a background context, and (2) are used both as a
     8  // binary and as a shared library (e.g., in R).
     9  package backgroundcontext
    10  
    11  import (
    12  	"context"
    13  	"sync/atomic"
    14  	"unsafe"
    15  
    16  	v23context "v.io/v23/context"
    17  )
    18  
    19  var ptr unsafe.Pointer
    20  
    21  // Set sets the singleton global context. It should be called at most once,
    22  // usually immediately after a process starts. Calling Set() multiple times will
    23  // cause a panic. Thread safe.
    24  func Set(ctx *v23context.T) {
    25  	if !atomic.CompareAndSwapPointer(&ptr, nil, unsafe.Pointer(ctx)) {
    26  		panic("backgroundcontext.Set called twice")
    27  	}
    28  }
    29  
    30  // T returns the background context set by Set. It panics if Set has not been
    31  // called yet. Thread safe.
    32  func T() *v23context.T {
    33  	p := atomic.LoadPointer(&ptr)
    34  	if p == nil {
    35  		panic("backgroundcontext.Set not yet called")
    36  	}
    37  	return (*v23context.T)(p)
    38  }
    39  
    40  // Get returns a background context: if a v23 context has been set, it is returned;
    41  // otherwise the standard Go background context is returned.
    42  func Get() context.Context {
    43  	if p := (*v23context.T)(atomic.LoadPointer(&ptr)); p != nil {
    44  		return p
    45  	}
    46  	return context.Background()
    47  }
    48  
    49  type wrapped struct {
    50  	context.Context
    51  	v23ctx *v23context.T
    52  }
    53  
    54  func (w *wrapped) Value(key interface{}) interface{} {
    55  	val := w.Context.Value(key)
    56  	if val != nil {
    57  		return val
    58  	}
    59  	return w.v23ctx.Value(key)
    60  }
    61  
    62  // Wrap wraps the provided context, composing it with the defined
    63  // background context, if any. This allows contexts to be wrapped
    64  // so that v23 stubs can get the background context's RPC client.
    65  //
    66  // Cancelations are not forwarded: it is assumed that the set background
    67  // context is never canceled.
    68  //
    69  // BUG: this is very complicated; but it seems required to make
    70  // vanadium play nicely with contexts defined outside of its
    71  // universe.
    72  func Wrap(ctx context.Context) context.Context {
    73  	v23ctx := (*v23context.T)(atomic.LoadPointer(&ptr))
    74  	if v23ctx == nil {
    75  		return ctx
    76  	}
    77  	return &wrapped{ctx, v23ctx}
    78  }