github.com/nicocha30/gvisor-ligolo@v0.0.0-20230726075806-989fa2c0a413/pkg/context/context.go (about)

     1  // Copyright 2018 The gVisor Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  // Package context defines an internal context type.
    16  //
    17  // The given Context conforms to the standard Go context, but mandates
    18  // additional methods that are specific to the kernel internals. Note however,
    19  // that the Context described by this package carries additional constraints
    20  // regarding concurrent access and retaining beyond the scope of a call.
    21  //
    22  // See the Context type for complete details.
    23  package context
    24  
    25  import (
    26  	"context"
    27  	"errors"
    28  	"sync"
    29  	"time"
    30  
    31  	"github.com/nicocha30/gvisor-ligolo/pkg/log"
    32  	"github.com/nicocha30/gvisor-ligolo/pkg/waiter"
    33  )
    34  
    35  // Blocker represents an object with control flow hooks.
    36  //
    37  // These may be used to perform blocking operations, sleep or otherwise
    38  // wait, since there may be asynchronous events that require processing.
    39  type Blocker interface {
    40  	// Interrupt interrupts any Block operations.
    41  	Interrupt()
    42  
    43  	// Interrupted notes whether this context is Interrupted.
    44  	Interrupted() bool
    45  
    46  	// BlockOn blocks until one of the previously registered events occurs,
    47  	// or some external interrupt (cancellation).
    48  	//
    49  	// The return value should indicate whether the wake-up occurred as a
    50  	// result of the requested event (versus an external interrupt).
    51  	BlockOn(waiter.Waitable, waiter.EventMask) bool
    52  
    53  	// Block blocks until an event is received from C, or some external
    54  	// interrupt. It returns nil if an event is received from C and an err if t
    55  	// is interrupted.
    56  	Block(C <-chan struct{}) error
    57  
    58  	// BlockWithTimeoutOn blocks until either the conditions of Block are
    59  	// satisfied, or the timeout is hit. Note that deadlines are not supported
    60  	// since the notion of "with respect to what clock" is not resolved.
    61  	//
    62  	// The return value is per BlockOn.
    63  	BlockWithTimeoutOn(waiter.Waitable, waiter.EventMask, time.Duration) (time.Duration, bool)
    64  
    65  	// UninterruptibleSleepStart indicates the beginning of an uninterruptible
    66  	// sleep state (equivalent to Linux's TASK_UNINTERRUPTIBLE). If deactivate
    67  	// is true and the Context represents a Task, the Task's AddressSpace is
    68  	// deactivated.
    69  	UninterruptibleSleepStart(deactivate bool)
    70  
    71  	// UninterruptibleSleepFinish indicates the end of an uninterruptible sleep
    72  	// state that was begun by a previous call to UninterruptibleSleepStart. If
    73  	// activate is true and the Context represents a Task, the Task's
    74  	// AddressSpace is activated. Normally activate is the same value as the
    75  	// deactivate parameter passed to UninterruptibleSleepStart.
    76  	UninterruptibleSleepFinish(activate bool)
    77  }
    78  
    79  // NoTask is an implementation of Blocker that does not block.
    80  type NoTask struct {
    81  	cancel chan struct{}
    82  }
    83  
    84  // Interrupt implements Blocker.Interrupt.
    85  func (nt *NoTask) Interrupt() {
    86  	select {
    87  	case nt.cancel <- struct{}{}:
    88  	default:
    89  	}
    90  }
    91  
    92  // Interrupted implements Blocker.Interrupted.
    93  func (nt *NoTask) Interrupted() bool {
    94  	return nt.cancel != nil && len(nt.cancel) > 0
    95  }
    96  
    97  // Block implements Blocker.Block.
    98  func (nt *NoTask) Block(C <-chan struct{}) error {
    99  	if nt.cancel == nil {
   100  		nt.cancel = make(chan struct{}, 1)
   101  	}
   102  	select {
   103  	case <-nt.cancel:
   104  		return errors.New("interrupted system call") // Interrupted.
   105  	case <-C:
   106  		return nil
   107  	}
   108  }
   109  
   110  // BlockOn implements Blocker.BlockOn.
   111  func (nt *NoTask) BlockOn(w waiter.Waitable, mask waiter.EventMask) bool {
   112  	if nt.cancel == nil {
   113  		nt.cancel = make(chan struct{}, 1)
   114  	}
   115  	e, ch := waiter.NewChannelEntry(mask)
   116  	w.EventRegister(&e)
   117  	defer w.EventUnregister(&e)
   118  	select {
   119  	case <-nt.cancel:
   120  		return false // Interrupted.
   121  	case _, ok := <-ch:
   122  		return ok
   123  	}
   124  }
   125  
   126  // BlockWithTimeoutOn implements Blocker.BlockWithTimeoutOn.
   127  func (nt *NoTask) BlockWithTimeoutOn(w waiter.Waitable, mask waiter.EventMask, duration time.Duration) (time.Duration, bool) {
   128  	if nt.cancel == nil {
   129  		nt.cancel = make(chan struct{}, 1)
   130  	}
   131  	e, ch := waiter.NewChannelEntry(mask)
   132  	w.EventRegister(&e)
   133  	defer w.EventUnregister(&e)
   134  	start := time.Now() // In system time.
   135  	t := time.AfterFunc(duration, func() { ch <- struct{}{} })
   136  	select {
   137  	case <-nt.cancel:
   138  		return time.Since(start), false // Interrupted.
   139  	case _, ok := <-ch:
   140  		if ok && t.Stop() {
   141  			// Timer never fired.
   142  			return time.Since(start), ok
   143  		}
   144  		// Timer fired, remain is zero.
   145  		return time.Duration(0), ok
   146  	}
   147  }
   148  
   149  // UninterruptibleSleepStart implmenents Blocker.UninterruptedSleepStart.
   150  func (*NoTask) UninterruptibleSleepStart(bool) {}
   151  
   152  // UninterruptibleSleepFinish implmenents Blocker.UninterruptibleSleepFinish.
   153  func (*NoTask) UninterruptibleSleepFinish(bool) {}
   154  
   155  // Context represents a thread of execution (hereafter "goroutine" to reflect
   156  // Go idiosyncrasy). It carries state associated with the goroutine across API
   157  // boundaries.
   158  //
   159  // While Context exists for essentially the same reasons as Go's standard
   160  // context.Context, the standard type represents the state of an operation
   161  // rather than that of a goroutine. This is a critical distinction:
   162  //
   163  //   - Unlike context.Context, which "may be passed to functions running in
   164  //     different goroutines", it is *not safe* to use the same Context in multiple
   165  //     concurrent goroutines.
   166  //
   167  //   - It is *not safe* to retain a Context passed to a function beyond the scope
   168  //     of that function call.
   169  //
   170  // In both cases, values extracted from the Context should be used instead.
   171  type Context interface {
   172  	context.Context
   173  	log.Logger
   174  	Blocker
   175  }
   176  
   177  // logContext implements basic logging.
   178  type logContext struct {
   179  	NoTask
   180  	log.Logger
   181  	context.Context
   182  }
   183  
   184  // bgContext is the context returned by context.Background.
   185  var bgContext Context
   186  var bgOnce sync.Once
   187  
   188  // Background returns an empty context using the default logger.
   189  // Generally, one should use the Task as their context when available, or avoid
   190  // having to use a context in places where a Task is unavailable.
   191  //
   192  // Using a Background context for tests is fine, as long as no values are
   193  // needed from the context in the tested code paths.
   194  //
   195  // The global log.SetTarget() must be called before context.Background()
   196  func Background() Context {
   197  	bgOnce.Do(func() {
   198  		bgContext = &logContext{
   199  			Context: context.Background(),
   200  			Logger:  log.Log(),
   201  		}
   202  	})
   203  	return bgContext
   204  }
   205  
   206  // WithValue returns a copy of parent in which the value associated with key is
   207  // val.
   208  func WithValue(parent Context, key, val any) Context {
   209  	return &withValue{
   210  		Context: parent,
   211  		key:     key,
   212  		val:     val,
   213  	}
   214  }
   215  
   216  type withValue struct {
   217  	Context
   218  	key any
   219  	val any
   220  }
   221  
   222  // Value implements Context.Value.
   223  func (ctx *withValue) Value(key any) any {
   224  	if key == ctx.key {
   225  		return ctx.val
   226  	}
   227  	return ctx.Context.Value(key)
   228  }