🎩 You're Invited:Meet the Socket team at Black Hat in Las Vegas, August 3-6.RSVP
Sign In

vawter.tech/stopper/v2

Package Overview
Dependencies
Versions
10
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

vawter.tech/stopper/v2

Go Modules
Version
v2.0.0
Version published
Created
Source

Structured, Observable Concurrency for Go

Go Reference codecov

go get vawter.tech/stopper/v2

Package stopper brings structured, observable concurrency to Go programs. It extends the standard library context.Context with a hierarchical task model and two-phase graceful shutdown, providing deep runtime visibility into your application's task tree.

Features

  • Structured concurrency – Tasks created with WithContext form a hierarchy: stopping a parent automatically stops its children, while Len() and Wait() account for tasks across the entire tree.
  • Observability – every Context is associated with a TaskGroup providing real-time visibility into the task hierarchy. Use TaskGroupFrom(ctx), TaskInfoFrom(ctx), and TaskTree(group, out) to inspect active tasks and their states.
  • Always-on runtime/trace – every stopper Context and every task automatically creates a runtime/trace.Task, so the full hierarchy appears in Go execution traces with zero extra code. Built-in middleware in limit and retry annotate blocking waits with trace.StartRegion for concurrency, rate-limit, and retry visibility.
  • Two-phase shutdown – calling Stop() closes the Stopping() channel, giving tasks a window to drain gracefully before the context is canceled (Done()). Use Stopping() in a select or call IsStopping() in a loop to respond to the signal.
  • Generic task adaptors – the package-level Go, GoN, Call, and Defer functions accept any signature from func() to func(stopper.Context) error.
  • Middleware – composable Middleware functions wrap task execution for cross-cutting concerns such as concurrency or rate limiting (see the limit sub-package).
  • Panic recovery – every task launched via Call or Go is wrapped in a default panic handler. If a task panics, the value is recovered and wrapped in a RecoveredError that captures the goroutine stack at the point of the panic. The error is then handled exactly like a returned error (passed to the task's ErrorHandler for Go, or returned to the caller for Call). Use errors.As(err, &re) to extract the RecoveredError and inspect its Stack field or call its String() method for a human-readable trace.
  • Context interop – works with any library that produces custom context.Context values via WithDelegate() and StoppingContext().
  • OS signal integrationStopOnReceive triggers a graceful shutdown when a value arrives on any channel, making it easy to wire up os/signal.Notify.
  • Retry – the retry sub-package provides Middleware for retrying failed tasks. Backoff offers exponential backoff with jitter, and Loop implements a simple synchronous retry.
  • Concurrent sequence processing – the seq sub-package provides bounded-concurrency helpers for iter.Seq and iter.Seq2 sequences. ForEach / ForEach2 apply a callback to every element; Map / Map2 and MapUnordered / MapUnordered2 transform elements into a new iter.Seq2[R, error], preserving or relaxing input order respectively.
  • Test helpers – the linger sub-package detects tasks that fail to exit promptly.

Competitive Analysis

For a detailed comparison of stopper against other Go concurrency libraries (such as errgroup, conc, and oklog/run), see the Competitive Market Analysis.

Quick Start

ctx := stopper.New()

// Launch a background task that responds to the soft-stop signal.
_ = stopper.Go(ctx, func(ctx stopper.Context) {
    for {
        select {
        case <-ctx.Stopping():
            return // begin graceful draining
        case item := <-work:
            process(item)
        }
    }
})

// Graceful shutdown with a one-second grace period.
ctx.Stop(stopper.StopGracePeriod(time.Second))
if err := ctx.Wait(); err != nil {
    log.Fatal(err)
}

Deep Observability

The stopper package provides real-time visibility into your goroutine hierarchy:

ctx := stopper.New(stopper.WithName("api-server"))
_ = stopper.Go(ctx, task1, stopper.TaskName("request-handler"))
_ = stopper.Go(ctx, task2, stopper.TaskName("background-sync"))

// Write a stable, human-readable representation of the task tree to stdout.
if g, ok := stopper.TaskGroupFrom(ctx); ok {
    stopper.TaskTree(g, os.Stdout)
}

Example output:

api-server
├── api-server.request-handler (started 2026-04-08T21:53:00Z) (running)
└── api-server.background-sync (started 2026-04-08T21:53:00Z) (running)

For simple loops that don't need a select, use the boolean shorthand:

_ = stopper.Go(ctx, func(ctx stopper.Context) {
    for !ctx.IsStopping() {
        // Do work.
    }
})

Examples

The package examples demonstrate a variety of patterns:

PatternExample
Simplest usageExample
Feature overviewExample_features
Accept / poll network serverExample_netServer
HTTP server with CallExample_httpServer
Bounded work poolExample_workPool
Channel-based tickerExample_ticker
Deferred cleanupsExampleContext_defer
Nested contextsExampleContext_nesting
Stop on idleExampleContext_stopOnIdle
MiddlewareExampleMiddleware
runtime/trace integrationExampleContext_tracing
Task observability metadataExampleTaskInfo
Task hierarchy treeExampleTaskTree
Retry with exponential backoffExampleBackoff
Concurrency / rate limitingExample
Detecting lingering tasksExampleRecorder
Concurrent ForEachExampleForEach
Concurrent ForEach (key-value)ExampleForEach2
Ordered concurrent MapExampleMap
Ordered concurrent Map (key-value)ExampleMap2
Unordered concurrent MapExampleMapUnordered
Unordered concurrent Map (key-value)ExampleMapUnordered2
Short-circuit processingExampleForEach_shortCircuit
Map with parent contextExampleMap_withContext

Project History

Version 1 of this repository was extracted from github.com/cockroachdb/field-eng-powertools using git filter-repo --subdirectory-filter stopper --path LICENSE by the code's original author.

Version 2 is a complete overhaul of the library's API with a focus on structured, observable concurrency and better composition.

FAQs

Package last updated on 24 Apr 2026

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts