gopool

package module
v0.1.0 Latest Latest
Warning

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

Go to latest
Published: Jan 19, 2026 License: MIT Imports: 5 Imported by: 0

README

gopool

gopool is a strand-based goroutine pool for Go.

It provides sequential execution per handle and concurrent execution across handles. If you schedule tasks through the same handle, they will never overlap. If you schedule tasks through different handles, they may run concurrently.

This makes gopool suitable for stateful, order-sensitive workloads where you want concurrency without races.

Design

A pool consists of a fixed number of strands. Each strand owns a task queue and runs tasks one at a time.

A Handle is a stable reference to a strand. Scheduling through the same handle preserves order and exclusivity. Scheduling through different handles allows concurrency.

The pool does not spawn goroutines per task. All goroutines are created upfront.

Guarantees
  • Tasks scheduled via the same handle:

    • Execute sequentially
    • Preserve submission order
    • Never overlap
  • Tasks scheduled via different handles:

    • May execute in parallel
    • Have no ordering guarantees relative to each other
  • Panics inside tasks:

    • Do not crash the pool
    • Are optionally reported via a panic handler
  • Shutdown:

    • Stops accepting new work
    • Drains queued tasks
    • Can be canceled via context
Non-goals
  • Dynamic resizing
  • Task prioritization
  • Automatic retries or restarts
  • Logging or opinionated observability

This pool is intentionally simple and explicit.

Usage

p := gopool.New(4, 16)
defer p.Shutdown(context.Background())

h := p.NewHandle()

_ = h.Schedule(context.Background(), func() {
    // runs sequentially per handle
})
Panic Handling
p := gopool.New(
    4,
    16,
    gopool.WithPanicHandler(func(strand int, value any, stack []byte) {
        log.Printf("panic in strand %d: %v\n%s", strand, value, stack)
    }),
)
Shutdown
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()

if err := p.Shutdown(ctx); err != nil {
    // shutdown was canceled
}

Documentation

Overview

Package gopool provides a strand-based goroutine pool.

The core idea is simple:

  • A Pool owns a fixed number of strands.
  • Each strand executes tasks sequentially.
  • A Handle is a stable reference to a single strand.
  • Tasks scheduled through the same Handle never overlap and preserve submission order.
  • Tasks scheduled through different Handles may run concurrently.

This model is useful for stateful, order-sensitive workloads where per-actor or per-key serialization is required without global locking.

Index

Examples

Constants

This section is empty.

Variables

View Source
var ErrPoolClosed = errors.New("pool is shut down")

ErrPoolClosed is returned when scheduling or shutdown is attempted after the pool has entered draining mode.

Functions

This section is empty.

Types

type Handle

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

Handle represents a stable execution context.

Tasks scheduled through the same Handle are guaranteed to execute sequentially and in submission order.

func (Handle) Schedule

func (h Handle) Schedule(ctx context.Context, task Task) error

Schedule submits a task for execution through the Handle.

The task will execute sequentially with respect to other tasks scheduled through the same Handle.

If the context is canceled before the task is enqueued, Schedule returns the context error.

type Option

type Option func(*Pool)

Option configures a Pool at construction time.

func WithPanicHandler

func WithPanicHandler(h PanicHandler) Option

WithPanicHandler installs a handler invoked when a task panics.

type PanicHandler

type PanicHandler func(strand int, panicValue any, stack []byte)

PanicHandler is invoked when a task panics.

The pool itself does not log, crash, or restart. If no PanicHandler is provided, panics are silently recovered.

type Pool

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

Pool is a fixed-size collection of execution strands.

A Pool creates all goroutines eagerly at construction time. It does not spawn goroutines per task.

Example (Basic)
package main

import (
	"context"
	"fmt"

	"github.com/codegrapple/gopool"
)

func main() {
	ctx := context.Background()

	// Pool with bounded parallelism.
	pool := gopool.New(2, 8)
	defer pool.Shutdown(ctx)

	// Two independent handles, each bound to a single goroutine.
	h1 := pool.NewHandle()
	h2 := pool.NewHandle()

	var (
		seq1, seq2 int
		out1, out2 []int
	)

	// Schedule work concurrently, but observe sequential execution per handle.
	for i := 0; i < 3; i++ {
		h1.Schedule(ctx, func() {
			out1 = append(out1, seq1)
			seq1++
		})

		h2.Schedule(ctx, func() {
			out2 = append(out2, seq2)
			seq2++
		})
	}

	// Ensure all scheduled work has completed.
	pool.Shutdown(ctx)

	fmt.Println(out1)
	fmt.Println(out2)

}
Output:

[0 1 2]
[0 1 2]

func New

func New(size, queue int, opts ...Option) *Pool

New constructs a new Pool.

size controls the number of independent strands. queue controls the per-strand task queue capacity.

Panics if size <= 0 or queue < 0.

func (*Pool) NewHandle

func (p *Pool) NewHandle() Handle

NewHandle returns a Handle bound to a specific strand.

Handles are distributed across strands in round-robin order. The returned Handle is cheap to copy.

func (*Pool) Shutdown

func (p *Pool) Shutdown(ctx context.Context) error

Shutdown transitions the pool into draining mode and waits for all queued tasks to complete.

New tasks are rejected once shutdown begins. Shutdown may be canceled via context.

type Task

type Task func()

Task represents a unit of work executed by the pool.

Jump to

Keyboard shortcuts

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