Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

45 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

go-utils

Go Reference Tests Coverage Go Report Card Go Version

go-utils is a collection of small, reusable Go utilities, with an emphasis on bounded queues and concurrent object reuse. The module includes generic single-threaded, mutex-protected, SPSC, and MPMC queues; a growable or fixed-size slab pool; UUID and size helpers; build information formatting; and a handful of synchronization and command-line helpers.

Install

go get github.com/opencoff/go-utils
import utils "github.com/opencoff/go-utils"

The module requires Go 1.25 or newer. Its go.mod currently selects the Go 1.26 toolchain.

What is included

API Purpose
Q[T] Bounded queue for single-threaded or externally synchronized use
SyncQ[T] Mutex-protected bounded queue
SPSCQ[T] Lock-free queue for exactly one producer and one consumer
MPMCQ[T] Lock-free, linearizable multiple-producer/multiple-consumer queue
Pool[T] Concurrent slab pool with lock-free reuse and serialized growth
Bufpool Legacy channel-backed fixed-size blocking pool
Barrier One-shot goroutine broadcast barrier
UUID UUIDv4 generation and text/byte conversion
ParseSize, HumanizeSize Parse and format binary byte sizes
BuildInfo Human-readable and JSON build metadata
Abbrev Generate unambiguous abbreviations from a word list
Askpass Read and optionally verify a password from the terminal

Bounded queues

All queue operations are non-blocking: Enq returns false when full and Deq returns false when empty. Requested capacities are rounded as needed for the queue's power-of-two ring representation.

Choose the queue according to its concurrency contract:

Queue Producers Consumers Synchronization
Q[T] One or externally synchronized One or externally synchronized None
SyncQ[T] Multiple Multiple Mutex
SPSCQ[T] Exactly one Exactly one Lock-free atomics
MPMCQ[T] Multiple Multiple Lock-free SCQ rings

MPMC queue

q := utils.NewMPMCQ[string](1024)

if !q.Enq("work item") {
	// The bounded queue is full.
}

item, ok := q.Deq()
if ok {
	fmt.Println(item)
}

MPMCQ uses two SCQ rings: one tracks occupied value slots and the other tracks available slots. It is bounded, linearizable, and must not be copied after first use. Len is only a momentary observation while producers and consumers are active.

This is an implementation of the bounded SCQ algorithm published in the refereed proceedings of DISC 2019:

Ruslan Nikolaev, "A Scalable, Portable, and Memory-Efficient Lock-Free FIFO Queue", 33rd International Symposium on Distributed Computing (DISC 2019), LIPIcs 146, Article 28, pp. 28:1–28:16.

SPSC queue

q := utils.NewSPSCQ[Packet](4096)

// Producer goroutine:
for !q.Enq(packet) {
	runtime.Gosched()
}

// Consumer goroutine:
packet, ok := q.Deq()

Only the designated producer may call Enq, and only the designated consumer may call Deq. Use MPMCQ or SyncQ when either side has multiple goroutines.

Call Flush only while a concurrent queue is quiescent.

Slab pool

Pool[T] retains objects in stable backing slabs. Returned objects are zeroed before they enter their originating slab's freelist. Freelist entries are integer offsets rather than pointers, reducing GC scanning and pointer write barriers.

type Message struct {
	Header []byte
	Body   []byte
}

pool := utils.NewPool[Message](1024, 2)

message := pool.Get()
message.Body = append(message.Body, payload...)

// Put clears message back to the zero value.
pool.Put(message)

The first argument is the initial capacity. The second is the growth factor; new slab sizes are rounded to powers of two. Growth is serialized by a mutex, while ordinary Get and Put reuse paths remain lock-free.

If the growth factor is less than one, the pool is fixed-size and Get returns nil when all objects are checked out:

pool := utils.NewPool[Message](128, 0.5)

message := pool.Get()
if message == nil {
	// Fixed pool is exhausted.
	return
}
defer pool.Put(message)

Every non-nil object returned by Get must be passed exactly once to the same pool. Do not pass copied, interior, foreign, or already returned pointers to Put. A Pool must not be copied after first use. Zero-sized element types are not supported.

Pool choices

Type Exhaustion behavior Retention Object reset
Pool[T], growth >= 1 Allocates another slab Stable until pool is unreachable Zeroes on Put
Pool[T], growth < 1 Get returns nil Stable until pool is unreachable Zeroes on Put
sync.Pool May allocate through New Entries may disappear at any GC Caller-managed
Bufpool Get blocks Fixed set of objects Caller-managed

sync.Pool is substantially faster under high contention because it uses per-P local storage, but it is a transient cache rather than a retained, bounded allocator.

Size and UUID helpers

ParseSize accepts binary suffixes such as k, M, G, and their B forms:

size, err := utils.ParseSize("32MB") // 32 * 1024 * 1024
if err != nil {
	return err
}

fmt.Println(utils.HumanizeSize(size)) // 32MB

The MiB spelling is not accepted; use M or MB.

UUID helpers generate RFC 4122 version 4 identifiers and support compact hex and conventional hyphenated forms:

id := utils.NewUUID()
fmt.Println(id.String())

encoded := id.Marshal()
decoded := utils.UnmarshalUUID(encoded)

MakeUUID constructs a UUID from exactly 16 bytes.

Build information

ReadBuildInfo wraps runtime/debug.ReadBuildInfo and exposes readable and JSON representations:

if info, ok := utils.ReadBuildInfo(); ok {
	fmt.Print(info.String())

	data, err := info.JSON()
	if err != nil {
		return err
	}
	fmt.Println(data)
}

Smaller helpers

  • Abbrev maps unique word prefixes back to their full words.
  • Barrier releases all current and future waiters after one Broadcast.
  • Askpass reads a password from standard input without terminal echo and can ask for confirmation.
  • Bufpool creates a fixed set of interface-valued objects; Get blocks when empty and an invalid extra Put panics.

Tests, coverage, and benchmarks

# Unit and concurrency tests
go test ./...

# Race detector and coverage
go test -race -count=1 -covermode=atomic -coverprofile=coverage.out ./...
go tool cover -func=coverage.out
go tool cover -html=coverage.out

# Queue and pool benchmarks
go test -run '^$' -bench 'Benchmark(SPSCQ|MPMCQ|Pool)$' -benchmem .

GitHub Actions runs the race-enabled test suite and enforces at least 90% statement coverage. The BenchmarkPool cases compare the slab allocator with sync.Pool in serial and parallel workloads.

API documentation

The complete exported API and type documentation are available on pkg.go.dev.

Licensing notices are provided in the individual source files.

About

General purpose golang utility functions

Topics

Resources

Stars

6 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages