The conc library provides a lightweight, zero-allocation wrapper around Go's standard concurrency primitives to prevent common race conditions and goroutine leaks without sacrificing performance. It achieves this by enforcing strict usage patterns through typed channels and context-aware wrappers that automatically handle cancellation and error propagation.
For example, instead of manually managing a sync.WaitGroup and checking for context cancellation in every loop iteration, you can use conc.Wait to spawn goroutines that automatically stop when the parent context is cancelled. This eliminates the risk of goroutine leaks where a worker continues running after the main program exits.
package main
import (
"context"
"fmt"
"time"
"github.com/stevenle/conc"
)
func main() {
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel()
// conc.Wait ensures all goroutines are tracked and stopped on context cancellation
conc.Wait(ctx, func() {
for {
select {
case <-ctx.Done():
return // Automatically stops when context is cancelled
default:
fmt.Println("Working...")
time.Sleep(500 * time.Millisecond)
}
}
})
// No need for manual WaitGroup or error checking on ctx.Done()
fmt.Println("All workers finished or cancelled safely.")
}
You can also use conc.Map to safely process slices in parallel without manually creating channels or managing synchronization. This function handles the fan-out and fan-in logic, ensuring that if one goroutine panics or the context is cancelled, the entire operation terminates gracefully without leaving dangling goroutines.
package main
import (
"context"
"fmt"
"time"
"github.com/stevenle/conc"
)
func main() {
ctx := context.Background()
numbers := []int{1, 2, 3, 4, 5}
// conc.Map processes items concurrently and returns results in order
results, err := conc.Map(ctx, numbers, func(ctx context.Context, n int) (int, error) {
// Simulate work; if ctx is cancelled, this returns immediately
time.Sleep(100 * time.Millisecond)
if n == 3 {
return 0, fmt.Errorf("simulated error for 3")
}
return n * 2, nil
})
if err != nil {
fmt.Printf("Operation failed: %v\n", err)
} else {
fmt.Printf("Results: %v\n", results)
}
}
The key advantage of conc is that it shifts the burden of concurrency safety from the developer to the library. By wrapping standard patterns, it forces you to pass a context.Context to every operation, ensuring that cancellation signals are respected everywhere. This is particularly useful in long-running services where a single leaked goroutine can eventually exhaust system resources. Unlike the standard library, which requires you to remember to check ctx.Done() in every loop, conc makes this the default behavior, reducing cognitive load and preventing subtle bugs in production code.