MCPcopy Index your code
hub / github.com/alitto/pond

github.com/alitto/pond @v2.7.1

Chat with this repo
repository ↗ · DeepWiki ↗ · release v2.7.1 ↗ · + Follow
292 symbols 1,614 edges 30 files 92 documented · 32% 11 cross-repo links
What it actually does AI analysis from the code graph — generated when you open this
loading…
README

Build status

pond is a minimalistic and high-performance Go library designed to elegantly manage concurrent tasks.

Motivation

This library is meant to provide a simple and idiomatic way to manage concurrency in Go programs. Based on the Worker Pool pattern, it allows running a large number of tasks concurrently while limiting the number of goroutines that are running at the same time. This is useful when you need to limit the number of concurrent operations to avoid resource exhaustion or hitting rate limits.

Some common use cases include: - Processing a large number of tasks concurrently - Limiting the number of concurrent HTTP requests - Limiting the number of concurrent database connections - Sending HTTP requests to a rate-limited API

Features:

  • Zero dependencies
  • Create pools of goroutines that scale automatically based on the number of tasks submitted
  • Limit the number of concurrent tasks running at the same time
  • Worker goroutines are only created when needed and immediately removed when idle (scale to zero)
  • Minimalistic and fluent APIs for:
  • Creating worker pools with maximum number of workers
  • Submitting tasks to a pool and waiting for them to complete
  • Submitting tasks to a pool in a fire-and-forget fashion
  • Submitting a group of tasks and waiting for them to complete or the first error to occur
  • Stopping a worker pool
  • Monitoring pool metrics such as number of running workers, tasks waiting in the queue, etc.
  • Very high performance and efficient resource usage under heavy workloads, even outperforming unbounded goroutines in some scenarios
  • Complete pool metrics such as number of running workers, tasks waiting in the queue and more
  • Configurable parent context to stop all workers when it is cancelled
  • New features in v2:
  • Bounded or Unbounded task queues
  • Submission of tasks that return results
  • Awaitable task completion
  • Type safe APIs for tasks that return errors or results
  • Panics recovery (panics are captured and returned as errors)
  • Subpools with a fraction of the parent pool's maximum number of workers
  • Blocking and non-blocking submission of tasks when the queue is full
  • Dynamic resizing of the pool
  • API reference

Installation

go get -u github.com/alitto/pond/v2

Usage

Submitting tasks to a pool with limited concurrency

package main

import (
    "fmt"

    "github.com/alitto/pond/v2"
)

func main() {

    // Create a pool with limited concurrency
    pool := pond.NewPool(100)

    // Submit 1000 tasks
    for i := 0; i < 1000; i++ {
        i := i
        pool.Submit(func() {
            fmt.Printf("Running task #%d\n", i)
        })
    }

    // Stop the pool and wait for all submitted tasks to complete
    pool.StopAndWait()
}

Submitting tasks that return errors

This feature allows you to submit tasks that return an error. This is useful when you need to handle errors that occur during the execution of a task.

// Create a pool with limited concurrency
pool := pond.NewPool(100)

// Submit a task that returns an error
task := pool.SubmitErr(func() error {
    return errors.New("An error occurred")
})

// Wait for the task to complete and get the error
err := task.Wait()

Submitting tasks that return results

This feature allows you to submit tasks that return a value. This is useful when you need to process the result of a task.

// Create a pool that accepts tasks that return a string and an error
pool := pond.NewResultPool[string](10)

// Submit a task that returns a string
task := pool.Submit(func() (string) {
    return "Hello, World!"
})

// Wait for the task to complete and get the result
result, err := task.Wait()
// result = "Hello, World!" and err = nil

Submitting tasks that return results or errors

This feature allows you to submit tasks that return a value and an error. This is useful when you need to handle errors that occur during the execution of a task.

// Create a concurrency limited pool that accepts tasks that return a string
pool := pond.NewResultPool[string](10)

// Submit a task that returns a string value or an error
task := pool.SubmitErr(func() (string, error) {
    return "Hello, World!", nil
})

// Wait for the task to complete and get the result
result, err := task.Wait()
// result = "Hello, World!" and err = nil

Submitting tasks associated with a context

If you need to submit a task that is associated with a context, you can pass the context directly to the task function.

// Create a pool with limited concurrency
pool := pond.NewPool(10)

// Create a context that can be cancelled
ctx, cancel := context.WithCancel(context.Background())

// Submit a task that is associated with a context
task := pool.SubmitErr(func() error {
    return doSomethingWithCtx(ctx) // Pass the context to the task directly
})

// Wait for the task to complete and get the error.
// If the context is cancelled, the task is stopped and an error is returned.
err := task.Wait()

Submitting a group of related tasks

You can submit a group of tasks that are related to each other. This is useful when you need to execute a group of tasks concurrently and wait for all of them to complete.

// Create a pool with limited concurrency
pool := pond.NewPool(10)

// Create a task group
group := pool.NewGroup()

// Submit a group of tasks
for i := 0; i < 20; i++ {
    i := i
    group.Submit(func() {
        fmt.Printf("Running group task #%d\n", i)
    })
}

// Wait for all tasks in the group to complete
err := group.Wait()

Submitting a group of related tasks associated with a context

You can submit a group of tasks that are linked to a context. This is useful when you need to execute a group of tasks concurrently and stop them when the context is cancelled (e.g. when the parent task is cancelled or times out).

// Create a pool with limited concurrency
pool := pond.NewPool(10)

// Create a context with a 5s timeout
timeout, _ := context.WithTimeout(context.Background(), 5*time.Second)

// Create a task group with a context
group := pool.NewGroupContext(timeout)

// Submit a group of tasks
for i := 0; i < 20; i++ {
    i := i
    group.Submit(func() {
        fmt.Printf("Running group task #%d\n", i)
    })
}

// Wait for all tasks in the group to complete or the timeout to occur, whichever comes first
err := group.Wait()

Submitting a group of related tasks and waiting for the first error

You can submit a group of tasks that are related to each other and wait for the first error to occur. This is useful when you need to execute a group of tasks concurrently and stop the execution if an error occurs.

// Create a pool with limited concurrency
pool := pond.NewPool(10)

// Create a task group
group := pool.NewGroup()

// Submit a group of tasks
for i := 0; i < 20; i++ {
    i := i
    group.SubmitErr(func() error {
        return doSomethingThatCanFail()
    })
}

// Wait for all tasks in the group to complete or the first error to occur
err := group.Wait()

Cancelling tasks immediately

When the first error occurs, tasks that are in the queue will be aborted but any running task will not be disrupted. The call to group.Wait() will not wait for these "in-flight" tasks to complete but they will continue running until completion nonetheless.

If you also need to stop these "in-flight" tasks when the first error occurs, you can reference the group's context (accessible via group.Context()) from any long-running operation carried out within these tasks. Here's an example:

// Create a pool with limited concurrency
pool := pond.NewPool(10)

// Create a task group
group := pool.NewGroup()

// Submit a group of tasks
for i := 0; i < 20; i++ {
    i := i
    group.SubmitErr(func() error {
        // Cancel all in-flight tasks when the first error occurs
        return doSomethingThatCanFailInContext(group.Context())
    })
}

// Wait for all tasks in the group to complete or the first error to occur
err := group.Wait()

Submitting a group of related tasks that return results

You can submit a group of tasks that are related to each other and return results. This is useful when you need to execute a group of tasks concurrently and process the results. Results are returned in the order they were submitted.

// Create a pool with limited concurrency
pool := pond.NewResultPool[string](10)

// Create a task group
group := pool.NewGroup()

// Submit a group of tasks
for i := 0; i < 20; i++ {
    i := i
    group.Submit(func() string {
        return fmt.Sprintf("Task #%d", i)
    })
}

// Wait for all tasks in the group to complete
results, err := group.Wait()
// results = ["Task #0", "Task #1", ..., "Task #19"] and err = nil

Stopping a group of tasks when a context is cancelled

If you need to submit a group of tasks that are associated with a context and stop them when the context is cancelled, you can pass the context directly to the task function.

// Create a pool with limited concurrency
pool := pond.NewPool(10)

// Create a context that can be cancelled
ctx, cancel := context.WithCancel(context.Background())

// Create a task group
group := pool.NewGroupContext(ctx)

// Submit a group of tasks
for i := 0; i < 20; i++ {
    i := i
    group.SubmitErr(func() error {
        return doSomethingWithCtx(ctx) // Pass the context to the task directly
    })
}

// Wait for all tasks in the group to complete.
// If the context is cancelled, all tasks are stopped and the first error is returned.
err := group.Wait()

Using a custom Context at the pool level

Each pool is associated with a context that is used to stop all workers when the pool is stopped. By default, the context is the background context (context.Background()). You can create a custom context and pass it to the pool to stop all workers when the context is cancelled.

// Create a custom context that can be cancelled
customCtx, cancel := context.WithCancel(context.Background())

// This creates a pool that is stopped when customCtx is cancelled 
pool := pond.NewPool(10, pond.WithContext(customCtx))

When a pool-level context is canceled, workers stop accepting new work and any queued tasks are drained so the pool can shut down cleanly without deadlocking. Drained tasks are not executed and resolve with a cancellation error.

Stopping a pool

You can stop a pool using the Stop method. This will stop all workers and prevent new tasks from being submitted. You can also wait for all submitted tasks by calling the Wait method.

// Create a pool with limited concurrency
pool := pond.NewPool(10)

// Submit a task
pool.Submit(func() {
    fmt.Println("Running task")
})

// Stop the pool and wait for all submitted tasks to complete
pool.Stop().Wait()

A shorthand method StopAndWait is also available to stop the pool and wait for all submitted tasks to complete.

// Create a pool with limited concurrency
pool := pond.NewPool(10)

// Submit a task
pool.Submit(func() {
    fmt.Println("Running task")
})

// Stop the pool and wait for all submitted tasks to complete
pool.StopAndWait()

Recovering from panics

By default, panics that occur during the execution of a task are captured and returned as errors. This allows you to recover from panics and handle them gracefully.

// Create a pool with limited concurrency
pool := pond.NewPool(10)

// Submit a task that panics
task := pool.Submit(func() {
    panic("A panic occurred")
})

// Wait for the task to complete and get the error
err := task.Wait()

if err != nil {
    fmt.Printf("Failed to run task: %v", err)
} else {
    fmt.Println("Task completed successfully")
}

If you prefer to keep the default Go runtime behavior (panics crashing the goroutine), you can disable panic interception when creating the pool:

pool := pond.NewPool(10, pond.WithoutPanicRecovery())

pool.Go(func() {
    panic("this panic will crash the goroutine")
})

Subpools (v2)

Subpools are pools that can have a fraction of the parent pool's maximum number of workers. This is useful when you need to create a pool of workers that can be used for a specific task or group of tasks.

// Create a pool with limited concurrency
pool := pond.NewPool(10)

// Create a subpool with a fraction of the parent pool's maximum number of workers
subpool := pool.NewSubpool(5)

// Submit a task to the subpool
subpool.Submit(func() {
    fmt.Println("Running task in subpool")
})

// Stop the subpool and wait for all submitted tasks to complete
subpool.StopAndWait()

Default pool (v2)

The default pool is a global pool that is used when no pool is provided. This is useful when you need to submit tasks but don't want to create a pool explicitly. The default pool does not have a maximum number of workers and scales automatically based on the number of tasks submitted.

// Submit a task to the default pool and wait for it to complete
err := pond.SubmitErr(func() error {
    fmt.Println("Running task in default pool")
    return nil
}).Wait()

if err != nil {
    fmt.Printf("Failed to run task: %v", err)
} else {
    fmt.Println("Task completed successfully")
}

Boun

Extension points exported contracts — how you extend this code

ResultPool (Interface)
ResultPool is a pool that can be used to submit tasks that return a result. [1 implementers]
result.go
BasePool (Interface)
BasePool defines methods common to all pool types. [1 implementers]
pool.go
Task (Interface)
Task represents a task that can be waited on. If the task fails, the error can be retrieved. [1 implementers]
future.go
TaskGroup (Interface)
TaskGroup represents a group of tasks that can be executed concurrently. The group can be waited on to block until all t
group.go
Option (FuncType)
(no doc)
pooloptions.go
ValueFutureResolver (FuncType)
(no doc)
internal/future/value.go
Pool (Interface)
Represents a pool of goroutines that can execute tasks concurrently. [1 implementers]
pool.go
ResultTask (Interface)
ResultTask represents a task that yields a result. If the task fails, the error can be retrieved. [1 implementers]
future.go

Core symbols most depended-on inside this repo

Equal
called by 438
internal/assert/assert.go
Wait
called by 117
future.go
Submit
called by 79
pool.go
NewPool
called by 47
pool.go
StopAndWait
called by 39
pool.go
True
called by 36
internal/assert/assert.go
RunningWorkers
called by 34
pool.go
Done
called by 30
future.go

Shape

Function 133
Method 129
Struct 18
Interface 8
FuncType 4

Languages

Go100%

Modules by API surface

pool.go64 symbols
group.go31 symbols
pool_test.go25 symbols
result.go19 symbols
group_test.go18 symbols
internal/future/composite.go14 symbols
internal/future/composite_test.go12 symbols
subpool_test.go11 symbols
result_test.go10 symbols
future.go9 symbols
internal/future/value.go8 symbols
internal/future/future.go8 symbols

For agents

$ claude mcp add pond \
  -- python -m otcore.mcp_server <graph>

⬇ download graph artifact