conc

package module
v0.5.0 Latest Latest
Warning

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

Go to latest
Published: Mar 4, 2025 License: MIT Imports: 7 Imported by: 1

README ΒΆ

structured concurrency schema

conc - Structured concurrency for Go

Go doc go report card license card PRs welcome card Go version card

conc is a structured concurrency library for Go that provides a safer, more intuitive approach to concurrent programming.

By emphasizing proper resource management, error handling, and execution flow, conc helps developers write concurrent code that is less error-prone, easier to reason about, and aligned with established best practices.

go get github.com/negrel/conc

Predictable code flow

conc is based on nursery as described in this blog post.

Idea behind nursery is that routines are scoped to a block that returns when all goroutines are done. This way, code flow remains sequential outside before and after the block. Here is an example:

func main() {
	conc.Block(func(n conc.Nursery) error {
		// Spawn a goroutine.
		n.Go(func() error {
			return nil
		})

		// Spawn another goroutine.
		n.Go(func() error {
			time.Sleep(2 * time.Second)
			return nil
		})

		// Sleep before returning.
		time.Sleep(time.Second)
		return nil
	})
	// Once block returns (here after 2 seconds), you're guaranteed that there is no
	// goroutine leak.
	// ...
}

Here is the definition of the Nursery interface:

type Nursery interface {
	context.Context
	Go(func() error)
}

It is a simple extension to context.Context that allows spawning routines.

Block and Nursery are the core of the entire library.

Explicit goroutines "leak"

Now, let's say you want to write a function spawning routines that outlives it. You can pass Nursery as a parameter making the leak explicit:

func workHard(n conc.Nursery) {
	n.Go(func() error {
		return longWork(n)
	})

	n.Go(func() error {
		return longerWork(n)
	})
}

And more...

  • Graceful panics and errors handling
  • Context integration for deadlines and cancellation
  • Limit maximum number of goroutine used by a block
  • Goroutines are pool allocated to improve efficiency
  • Dependency free

Performance

Here, an operation means spawning 100 goroutines:

$ cd bench/
$ go test -v -bench=./... -benchmem ./...
goos: linux
goarch: amd64
pkg: github.com/negrel/conc/bench
cpu: AMD Ryzen 7 7840U w/ Radeon  780M Graphics
BenchmarkNursery
BenchmarkNursery/EmptyBlock
BenchmarkNursery/EmptyBlock-16   1367210               914.3 ns/op           625 B/op         11 allocs/op
BenchmarkNursery/WithRoutines/NoWork
BenchmarkNursery/WithRoutines/NoWork-16                    26823             43909 ns/op            1514 B/op         65 allocs/op
BenchmarkNursery/WithRoutines/Nested/NoWork
BenchmarkNursery/WithRoutines/Nested/NoWork-16             18189             69416 ns/op            3623 B/op        147 allocs/op
BenchmarkNursery/WithRoutines/1msWork
BenchmarkNursery/WithRoutines/1msWork-16                     940           1300306 ns/op           11941 B/op        211 allocs/op
BenchmarkNursery/WithRoutines/1-10msWork
BenchmarkNursery/WithRoutines/1-10msWork-16                  123           9681105 ns/op           12427 B/op        292 allocs/op
BenchmarkNursery/WithRoutines/Error
BenchmarkNursery/WithRoutines/Error-16                     26767             44257 ns/op            1485 B/op         65 allocs/op
BenchmarkSourceGraphConc
BenchmarkSourceGraphConc/EmptyPool
BenchmarkSourceGraphConc/EmptyPool-16                   13450243                85.45 ns/op          176 B/op          2 allocs/op
BenchmarkSourceGraphConc/WithRoutines/NoWork
BenchmarkSourceGraphConc/WithRoutines/NoWork-16            48522             23928 ns/op            1835 B/op         84 allocs/op
BenchmarkSourceGraphConc/WithRoutines/1msWork
BenchmarkSourceGraphConc/WithRoutines/1msWork-16                     966           1239140 ns/op           13776 B/op        302 allocs/op
BenchmarkSourceGraphConc/WithRoutines/1-10msWork
BenchmarkSourceGraphConc/WithRoutines/1-10msWork-16                  123           9625635 ns/op           14030 B/op        372 allocs/op
BenchmarkGo
BenchmarkGo/WithRoutines/NoWork
BenchmarkGo/WithRoutines/NoWork-16                                 84024             14240 ns/op            1600 B/op        100 allocs/op
BenchmarkGo/WithRoutines/Nested/NoWork
BenchmarkGo/WithRoutines/Nested/NoWork-16                          81360             14497 ns/op            3199 B/op        199 allocs/op
BenchmarkGo/WithRoutines/1msWork
BenchmarkGo/WithRoutines/1msWork-16                                59904             18651 ns/op           11215 B/op        200 allocs/op
BenchmarkGo/WithRoutines/1-10msWork
BenchmarkGo/WithRoutines/1-10msWork-16                             51703             21088 ns/op           11069 B/op        190 allocs/op
PASS
ok      github.com/negrel/conc/bench    22.347s

Contributing

If you want to contribute to conc to add a feature or improve the code contact me at [email protected], open an issue or make a pull request.

🌠 Show your support

Please give a ⭐ if this project helped you!

buy me a coffee

πŸ“œ License

MIT Β© Alexandre Negrel

Documentation ΒΆ

Index ΒΆ

Constants ΒΆ

This section is empty.

Variables ΒΆ

This section is empty.

Functions ΒΆ

func All ΒΆ

func All[T any](jobs []Job[T], opts ...BlockOption) ([]T, error)

All executes all jobs in separate goroutines and stores each result in the returned slice.

func Block ΒΆ

func Block(block func(n Nursery) error, opts ...BlockOption) (err error)

Block starts a nursery block that returns when all goroutines have returned. If a goroutine returns an error, it is returned after context is canceled unless a custom error handler is provided. In case of a panic context is canceled and panic is immediately forwarded without waiting for other goroutines to handle context cancellation. Error returned by block closure always trigger a context cancellation and is returned if it occurs before a default goroutine error handler is called. Calling Nursery.Go() after end of block always panic. Calling Nursery.Go after context is canceled still runs the provided function, you're responsible for handling cancellation.

func IsDone ΒΆ added in v0.5.0

func IsDone(ctx context.Context) bool

IsDone returns whether provided context is done.

func Map ΒΆ

func Map[T any, V any](input []T, f func(context.Context, T) (V, error), opts ...BlockOption) ([]V, error)

Map applies f to each element of input and returns a new slice containing mapped results.

func Map2 ΒΆ

func Map2[K comparable, V any](input map[K]V, f func(context.Context, K, V) (K, V, error), opts ...BlockOption) (map[K]V, error)

Map2 applies f to each key, value pair of input and returns a new slice containing mapped results.

func MapInPlace ΒΆ

func MapInPlace[T any](input []T, f func(context.Context, T) (T, error), opts ...BlockOption) ([]T, error)

MapInPlace applies f to each element of input and returns modified input slice.

func Race ΒΆ added in v0.4.0

func Race[T any](jobs []Job[T], opts ...BlockOption) (T, error)

Race executes all jobs in separate goroutines and returns first result. Remaining goroutines are canceled.

func Range ΒΆ

func Range[T any](seq iter.Seq[T], block func(context.Context, T) error, opts ...BlockOption) error

Range iterates over a sequence and pass each value to a separate goroutine.

func Range2 ΒΆ

func Range2[K, V any](seq iter.Seq2[K, V], block func(context.Context, K, V) error, opts ...BlockOption) error

Range2 is the same as Range except it uses a iter.Seq2 instead of iter.Seq.

func Sleep ΒΆ

func Sleep(ctx context.Context, d time.Duration)

Sleep is an alternative to time.Sleep that returns once d time is elapsed or context is done.

Types ΒΆ

type BlockOption ΒΆ

type BlockOption func(cfg *nursery)

BlockOption define an option function applied to a nursery block.

func WithCollectErrors ΒΆ added in v0.4.0

func WithCollectErrors(errors *[]error) BlockOption

WithCollectErrors returns a nursery block option that sets error handler to collect goroutine errors into provided error slice. Provided error slice must not be read and write until end of block.

func WithContext ΒΆ

func WithContext(ctx context.Context) BlockOption

WithContext returns a nursery block option that replaces nursery context with the given one.

func WithDeadline ΒΆ

func WithDeadline(d time.Time) BlockOption

WithDeadline returns a nursery block option that wraps nursery context with a new one that will be canceled at `d`.

func WithErrorHandler ΒΆ

func WithErrorHandler(handler func(error)) BlockOption

WithErrorHandler returns a nursery block option that adds an error handler to the block. Provided error handler is executed in the goroutine that returned the error.

func WithIgnoreErrors ΒΆ added in v0.4.0

func WithIgnoreErrors() BlockOption

WithIgnoreErrors returns a nursery block option that sets error handler to a noop function.

func WithMaxGoroutines ΒΆ

func WithMaxGoroutines(max int) BlockOption

WithMaxGoroutines returns a nursery block option that limits the maximum number of goroutine running concurrently. If max is zero, number of goroutine is unlimited. This function panics if max is negative.

func WithTimeout ΒΆ

func WithTimeout(timeout time.Duration) BlockOption

WithTimeout returns a nursery block option that wraps nursery context with a new one that timeout after the given duration.

type GoroutinePanic ΒΆ

type GoroutinePanic struct {
	Value any
	Stack string
}

GoroutinePanic holds value from a recovered panic along a stacktrace.

func (GoroutinePanic) Error ΒΆ

func (gp GoroutinePanic) Error() string

Error implements error.

func (GoroutinePanic) String ΒΆ

func (gp GoroutinePanic) String() string

String implements fmt.Stringer.

func (GoroutinePanic) Unwrap ΒΆ

func (gp GoroutinePanic) Unwrap() error

Unwrap unwrap underlying error.

type Job ΒΆ

type Job[T any] func(context.Context) (T, error)

type Nursery ΒΆ

type Nursery interface {
	context.Context

	// Executes provided [Routine] as soon as possible in a separate goroutine.
	Go(Routine)
}

Nursery is a supervisor that executes goroutines and manages their lifecycle. It embeds a context.Context to provide cancellation and deadlines to all spawned goroutines. When the nursery's context is canceled, all goroutines are signaled to stop via the context cancellation.

type Routine ΒΆ added in v0.4.0

type Routine = func() error

Routine define a function executed in its own goroutine.

Jump to

Keyboard shortcuts

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