probe

package module
v0.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 12, 2024 License: MIT Imports: 9 Imported by: 0

README

probe

Amplify Security test coverage Go Report Card Go Reference

A modern, zero-dependency goroutine pool. Probe is designed to abstract goroutine synchronization and control flow to enable cleaner concurrent code.

What can you do with Probe?

You can easily create reusable goroutines called Probes to do side channel work with less synchronization code. For example:

p := probe.NewProbe(&probe.ProbeConfig{})
p.WorkChan() <-func() {
    fmt.Println("Hello from Probe!")
}
p.Stop()

You can also create a Probe Pool and run functions on a configurable pool of goroutines:

p := pool.NewPool(&pool.PoolConfig{ Size: 16 })
p.Run(func() {
    fmt.Println("Hello from Probe Pool!")
})
p.Stop()

You can check to see how many Probes in the Pool are idle for monitoring and tuning Pool sizes:

ctrlChan := make(chan struct{})
f := func() {
    <-ctrlChan
}
p := pool.NewPool(&pool.PoolConfig{ Size: 16 })
p.Run(f)
p.Run(f)
// this is a race condition with the Pool work channel, your output may differ
fmt.Println(p.Idle()) // 14

Channels can be used to get results with type safety back from your functions:

f, _ := os.Open("/tmp/test")
b := new(bytes.Buffer)
returnChan := make(chan struct{int; error})
p := probe.NewProbe(&probe.ProbeConfig{})
p.WorkChan() <-func() {
    n, err := f.Read(b)
    returnChan <- struct{int; error}{n, err}
}
r := <-returnChan // access with r.int, r.error

Configuration

Some common configuration scenarios for an individual Probe may be passing in a buffered channel instead of using the default, blocking channel, or passing in a shared context so that all probes are stopped if the context is canceled.

ctx, cancel := context.WithCancel(context.Background())
work := make(chan probe.Runner, 1024)
p := probe.NewProbe(&probe.ProbeConfig{
    Ctx:      ctx,
    WorkChan: work, 
})
work <- func() {
    fmt.Println("Hello from a buffered Probe!")
}
cancel()

Pools may likewise be configured with a Size, Ctx, and BufferSize.

ctx, cancel := context.WithCancel(context.Background())
p := pool.NewPool(&pool.PoolConfig{
    Ctx:        ctx,
    Size:       1024,
    BufferSize: 2048,
})
p.Run(func() {
    fmt.Println("Hello from a custom Pool!")
})
// note that a pool canceled like this cannot be restarted
// useful if unique requests within a larger system each create a new pool
cancel()

Logging

Probe uses the slog.Handler interface for logging to maximize logging compatibility. By default, Probe and Pool use the logging.NoopLogHandler which does not log. For information on building a slog.Handler for your logger of choice, see the slog Handler guide.

Why use a goroutine pool?

Go is excellent at parallelization and includes concurrency and sychronization mechanisms "out of the box." Go also multiplexes goroutines onto system threads for threading efficiency. So, with this in mind, why use a goroutine pool at all?

One, they can keep code cleaner with lower cognitive load for developers. It's easy to conceptualize a single pool of goroutines whereas starting multiple goroutines in different places in code can be difficult to grok.

Two, debugging synchronization issues with a goroutine Pool is easier than many concurrent goroutines that may be started in different places in code. The latter point is especially true when dealing with graceful shutdowns or context cancellation.

Why Probe?

Probe is named after the Protoss Probe unit from the popular video game series Starcraft. These faithful workers are helpful for collecting resources. The name was chosen to be both fitting and fun!

Probe

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Probe

type Probe struct {
	// contains filtered or unexported fields
}

Probe is a helper that runs functions on a separate goroutine.

func NewProbe

func NewProbe(cfg *ProbeConfig) *Probe

NewProbe initializes and returns a new Probe.

func (*Probe) ID

func (p *Probe) ID() string

ID returns the unique identifier of the Probe.

func (*Probe) Idle

func (p *Probe) Idle() bool

Idle returns the status of the Probe: true if the Probe is Working but has no current work to execute.

func (*Probe) Run

func (p *Probe) Run()

Run is the main event loop for the Probe. Run will start a new goroutine.

func (*Probe) Running

func (p *Probe) Running() bool

Running returns the status of the Probe: true if work event loop is running.

func (*Probe) Stop

func (p *Probe) Stop(wait bool)

Stop will stop the Probe from doing further work. Stop blocks if wait is true until current work is complete.

func (*Probe) WorkChan

func (p *Probe) WorkChan() chan Runner

WorkChan returns the channel used for work events.

type ProbeConfig

type ProbeConfig struct {
	LogHandler slog.Handler    // Handler to use for probe logging. If empty, probe.NoopHandler will be used.
	Ctx        context.Context // Context to use for the probe. If empty, context.Background will be used.
	WorkChan   chan Runner     // Channel to use for work. If empty, a new channel will be created.
	RunningCtr *atomic.Int32   // Running counter to increment when this probe is running.
	IdleCtr    *atomic.Int32   // Idle counter to increment when this probe is idle.
	WaitGroup  *sync.WaitGroup // WaitGroup to use for the probe.
}

ProbeConfig is a struct for passing configuration data to a new Probe.

type Runner

type Runner func()

Runner function type.

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL