github.com/git-amp/amp-sdk-go@v0.7.5/stdlib/task/api.task.go (about)

     1  // Features task.Context, a wrapper for goroutines inspired by a conventional parent-child process model
     2  package task
     3  
     4  import (
     5  	"context"
     6  	"time"
     7  
     8  	"github.com/git-amp/amp-sdk-go/stdlib/log"
     9  )
    10  
    11  // Starts a new Context with no parent Context.
    12  func Start(task *Task) (Context, error) {
    13  	return Context((*ctx)(nil)).StartChild(task)
    14  }
    15  
    16  // Go is a convenience function that starts a new Context that runs the given function -- like starting a goroutine.
    17  //
    18  // If parent == null, then the new Context will have no parent.
    19  func Go(parent Context, label string, fn func(ctx Context)) (Context, error) {
    20  	return parent.StartChild(&Task{
    21  		Label: label,
    22  		OnRun: fn,
    23  	})
    24  }
    25  
    26  // Task is a parameter block used to start a new Context and contains hooks for each stage of the Context's lifecycle.
    27  type Task struct {
    28  
    29  	// If > 0, Context.CloseWhenIdle() will automatically called when the last remaining child is closed or when OnRun() completes, whichever occurs later.
    30  	//
    31  	// This will not enter into effect unless OnRun is given or a child is started.
    32  	IdleClose time.Duration
    33  
    34  	TaskRef        any                     // TaskRef is offered for open-ended use
    35  	Label          string                  // Label is used for logging and debugging
    36  	OnStart        func(ctx Context) error // Blocking fn called in StartChild(). If err, ctx.Close() is called and Go() returns the err and OnRun is never called.
    37  	OnRun          func(ctx Context)       // Async work body. If non-nil, ctx.Close() will be automatically called after OnRun() completes
    38  	OnClosing      func()                  // Called immediately after Close() is first called while self & children are still closing
    39  	OnChildClosing func(child Context)     // Called immediately after the child's OnClosing() is called
    40  	OnClosed       func()                  // Called after Close() and all children have completed Close() (but immediately before Done() is released)
    41  }
    42  
    43  // Context is an expanded form of a context.Context offering, featuring:
    44  //   - integrated logging, removing guesswork of which Context logged what
    45  //   - "child" Contexts such that Close() will cause a Context's children to close
    46  //   - automatic idle-close of Contexts after a period of inactivity
    47  //   - the OnClosing() hook, allowing cleanup to occur  when a Context is closed but before its parent is closed.
    48  //   - PrintTreePeriodically() which visualizes a Context's child tree and is helpful for debugging in large projects.
    49  type Context interface {
    50  	log.Logger
    51  
    52  	// Includes functionality and behavior of a context.Context.
    53  	context.Context
    54  
    55  	// Returns Task.Ref passed into StartChild()
    56  	TaskRef() interface{}
    57  
    58  	// The context's public label
    59  	Label() string
    60  
    61  	// A guaranteed unique ID assigned after Start() is called.
    62  	ContextID() int64
    63  
    64  	// Creates a new child Context with for given Task.
    65  	// If OnStart() returns an error error is encountered, then child.Close() is immediately called and the error is returned.
    66  	StartChild(task *Task) (Context, error)
    67  
    68  	// Convenience function for StartChild() and is equivalent to:
    69  	//
    70  	//      parent.StartChild(label, &Task{
    71  	//  		IdleClose: time.Nanosecond,
    72  	// 	        OnRun: fn,
    73  	//      })
    74  	Go(label string, fn func(ctx Context)) (Context, error)
    75  
    76  	// Appends all currently open/active child Contexts to the given slice and returns the given slice.
    77  	// Naturally, the returned items are back-ward looking as any could close at any time.
    78  	// Context implementations wishing to remain lightweight may opt to not retain a list of children (and just return the given slice as-is).
    79  	GetChildren(in []Context) []Context
    80  
    81  	// Async call that initiates task shutdown and causes all children's Close() to be called.
    82  	// Close can be called multiple times but calls after the first are in effect ignored.
    83  	// First, children get Close() in breath-first order.
    84  	// After all children are done closing, OnClosing(), then OnClosed() are executed.
    85  	Close() error
    86  
    87  	// Inserts a pending Close() on this Context once it is idle after the given delay.
    88  	// Subsequent calls will update the delay but the previously pending delay must run out first.
    89  	// If at the end of the period Task.OnRun() is complete, there are no children, PreventIdleClose() is not in effect, then Close() is called.
    90  	CloseWhenIdle(delay time.Duration)
    91  
    92  	// Ensures that that this Context will not automatically idle-close until the given delay has passed.
    93  	// If previous PreventIdleClose calls were made, the more limiting delay is retained.
    94  	//
    95  	// Returns false if this Context has already been closed.
    96  	PreventIdleClose(delay time.Duration) bool
    97  
    98  	// Signals when Close() has been called.
    99  	// First, Children get Close(),  then OnClosing, then OnClosed are executing
   100  	Closing() <-chan struct{}
   101  
   102  	// Signals when Close() has fully executed, no children remain, and OnClosed() has been completed.
   103  	Done() <-chan struct{}
   104  }