The progress package provides status updates to the terminal as units of work are incrementally completed.
Incremental calculations retain high-precision, while imposing minimal overhead upon callers.
See the examples directory for examples of the modal API.
Run the example programs to see the UI in action.
Here are a couple of real-world examples:
Features include:
- race-free and lock-free by design:
- concurrency-safe and well-suited to highly-scaled concurrent processing systems
- uses Go concurrency primitives for synchronization
- uses atomic operations to:
- provide lock-free updates of internal state for highly-scaled concurrent workloads
- obviate mutex contention
- minimize memory allocation and impose minimal GC overhead
- enable fast and efficient UI synchronization via comparisons of:
- precise internal progress values via
atomic.Uint32andatomic.Uint64types - progress status strings (
atomic.Pointer[string]in theprogress.Standardtracker;atomic.Uint64in theprogress.Fractiontracker), or - canonicalized (interned) progress status string handles (
unique.Handle[string]guarded byatomic.Valuein theprogress.Uniquetracker)
- precise internal progress values via
- context-aware:
- correctly handles cancellation of the parent context, ensuring a clean exit under reasonable circumstances
- very efficient with minimal memory footprint:
- optimized for very low CPU usage, even at refresh rates beyond human perception
- the use of a background rendering loop throttled at ~60 FPS combined with
atomictypes:- decouples the progress status tracker from the workers processing the workload
- allows workers to report progress updates to the tracker asynchronously
- ensures that UI rendering (which involves I/O and syscalls) never blocks workers
- condenses all I/O operations into a single, atomic system call per frame, minimizing I/O latency
- skips redundant UI redraws, further minimizing I/O and ensuring the terminal is never overwhelmed with I/O
- uses bit-packed
atomic.Uint32types and bitwise operations to further reduce memory allocation and efficiently handle precise updates of internal state
- supports two tracking modes:
- weight-based accumulation: callers specify the total known amount of work (e.g., 100 tasks)
- fractional allocation: callers add the relative share of the total budget as work is discovered (e.g., recursively traversing a directory to process its contents)
- supports multiple progress status tracking implementations which are well-suited to different sets of inputs:
progress.Standard: suitable for mostly unique status updates (usesatomic.Pointer[string])progress.Unique: suitable for mostly repetitive status updates (usesatomic.Valueandunique.Handle[string]to canonicalize (intern) status strings)
- supports multiple progress status formats:
progress.Fraction: writes progress status as a proper fraction (x/y) given a prescribed fixed total units of work (y)progress.Percent: writes only the percentage calculation to the terminal
- transparently handles pipes, redirections, and non-TTY environments
- correctly and efficiently handles UTF-8 status strings passed by callers
- safely handles terminal window resizing by:
- dynamically adapting the layout
- formatting the rendered output accordingly
- ensuring layout and output integrity during concurrent writes to the terminal
- supports various color schemes for rendering the background colors of the progress bar
- the
all_themes.shscript demonstrates all available themes
- the
Limitations:
- the precision of percentage calculations starts to progressively degrade at ~1 quadrillion (1e15) units of work
- a workload capacity limited to 1 quadrillion units still allows for extremely fine-grained budget splitting and / or extremely deep recursion
- handling of terminal resize events on Windows systems is not supported
- truncated status updates may render incorrectly in terminal emulators which lack UTF-8 support
- has not been tested on terminal emulators which lack support for rendering 24-bit colors
See for API documentation and
for technical internal implementation details.
This library originated as an experiement to satisfy the author's curiosity about the practical application of modern, idiomatic Go features (e.g., context for modern handling of cancelation and deadlines across API boundaries, lock-free synchronization and thread-safety via sync/atomic and CAS loops, set canonicalization (interning) via unique and comparable, etc) in a context where concurrency is paramount after a nearly eight-year break from coding Go, back when 1.10 was the latest release.
The implementation was then iteratively optimized well beyond the point of overengineering, for fun and as a learning exercise.