goutil

package
v0.1.2 Latest Latest
Warning

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

Go to latest
Published: May 4, 2026 License: Apache-2.0 Imports: 4 Imported by: 0

README

goutil

English | 中文

Run goroutines safely with built-in panic recovery.

Features

  • Panic Recovery: Automatically captures panics in goroutines to prevent crashes
  • Global Callback: Provides OnPanic callback for logging, metrics, or alerting
  • Context Control: Supports flexible context cancellation modes (inherit/detach)
  • Return Value Support: Captures return values and errors from goroutines
  • Synchronization: Provides Wait() method to wait for goroutine completion

Usage Examples

Basic Usage
package main

import (
    "context"
    "fmt"
    "time"
    
    "github.com/go-spring/stdlib/goutil"
)

func main() {
    // Launch a goroutine with panic recovery
    status := goutil.Go(context.Background(), func(ctx context.Context) {
        fmt.Println("goroutine is running...")
        time.Sleep(100 * time.Millisecond)
        fmt.Println("goroutine completed")
    }, goutil.InheritCancel)
    
    // Wait for goroutine to complete
    status.Wait()
}
Panic Recovery and Custom Handling
// Customize panic handling logic (e.g., logging, monitoring)
goutil.OnPanic = func(ctx context.Context, info goutil.PanicInfo) {
    // ctx can carry request ID, trace info, etc.
    log.Printf("[PANIC] recovered panic: %v\nStack trace:\n%s", info.Panic, info.Stack)
}

// Program won't crash even if panic occurs
goutil.Go(context.Background(), func(ctx context.Context) {
    panic("something went wrong!")
}, goutil.InheritCancel).Wait()
Context Cancellation Modes

InheritCancel (default): Child goroutine inherits parent context's cancellation

ctx, cancel := context.WithCancel(context.Background())

goutil.Go(ctx, func(ctx context.Context) {
    select {
    case <-time.After(time.Second):
        fmt.Println("task completed")
    case <-ctx.Done():
        fmt.Println("task cancelled")
    }
}, goutil.InheritCancel)

// Cancel context after 50ms
time.Sleep(50 * time.Millisecond)
cancel() // child goroutine receives cancellation signal

DetachCancel: Child goroutine不受 parent context cancellation

ctx, cancel := context.WithCancel(context.Background())

goutil.Go(ctx, func(ctx context.Context) {
    // This goroutine continues even if parent context is cancelled
    time.Sleep(time.Second)
    fmt.Println("task completed, unaffected by parent context cancellation")
}, goutil.DetachCancel)

cancel() // does not affect child goroutine
Getting Return Values (GoValue)
result := goutil.GoValue(context.Background(), func(ctx context.Context) (int, error) {
    // Simulate time-consuming operation
    time.Sleep(100 * time.Millisecond)
    return 42, nil
}, goutil.InheritCancel)

// Wait and get result
value, err := result.Wait()
if err != nil {
    log.Printf("error: %v", err)
    return
}
fmt.Printf("result: %d\n", value)
Panic Converted to Error
value, err := goutil.GoValue(context.Background(), func(ctx context.Context) (string, error) {
    panic("runtime error")
}, goutil.InheritCancel).Wait()

// value is empty string (zero value of type T)
// err contains panic info and stack trace
fmt.Printf("value: %q, error: %v\n", value, err)

Important Notes

1. Context Cancellation is Cooperative

Goroutines don't automatically respond to context cancellation; you must check explicitly in the function.

Wrong: Does not respond to cancellation

goutil.Go(ctx, func(ctx context.Context) {
    time.Sleep(time.Hour) // continues even if ctx is cancelled
}, goutil.InheritCancel)

Right: Actively checks cancellation

goutil.Go(ctx, func(ctx context.Context) {
    select {
    case <-time.After(time.Hour):
        // complete task
    case <-ctx.Done():
        // cleanup and exit
        return
    }
}, goutil.InheritCancel)
2. Defer Always Executes

Even when a panic occurs, defer statements execute normally:

goutil.Go(context.Background(), func(ctx context.Context) {
    file, _ := os.Open("data.txt")
    defer file.Close() // executes even on panic
    
    processData(file) // may panic
}, goutil.InheritCancel)

Typical Use Cases

1. Web Server Background Tasks
http.HandleFunc("/upload", func(w http.ResponseWriter, r *http.Request) {
    // Asynchronously process uploaded file
    goutil.Go(r.Context(), func(ctx context.Context) {
        // process file...
        // won't crash server even if panic occurs
    }, goutil.DetachCancel)
    
    w.WriteHeader(http.StatusAccepted)
})
2. Scheduled Tasks
go func() {
    ticker := time.NewTicker(time.Minute)
    for range ticker.C {
        goutil.Go(context.Background(), func(ctx context.Context) {
            runScheduledTask(ctx)
        }, goutil.InheritCancel)
    }
}()
3. Batch Concurrent Processing
func ProcessBatch(items []Item) error {
    var wg sync.WaitGroup
    results := make(chan Result, len(items))
    
    for _, item := range items {
        wg.Add(1)
        go func(it Item) {
            defer wg.Done()
            res, err := goutil.GoValue(context.Background(), func(ctx context.Context) (Result, error) {
                return processItem(it), nil
            }, goutil.InheritCancel).Wait()
            
            if err != nil {
                log.Printf("processing failed: %v", err)
                return
            }
            results <- res
        }(item)
    }
    
    wg.Wait()
    close(results)
    return nil
}

Comparison with Other Approaches

Approach Panic Recovery Context Control Return Values Synchronization
go func()
errgroup.Group
goutil

License

Apache License 2.0

Documentation

Overview

Package goutil provides utilities for running goroutines safely with built-in panic recovery.

In Go, a panic that occurs inside a goroutine will terminate the entire process if it is not recovered. This package wraps goroutine execution with a deferred recover handler to prevent such crashes.

When a panic is recovered, a global OnPanic callback is invoked, allowing applications to log the panic, emit metrics, or trigger alerts. This makes failures in concurrent code easier to observe and diagnose.

Index

Constants

This section is empty.

Variables

View Source
var OnPanic = func(ctx context.Context, info PanicInfo) {
	fmt.Printf("[PANIC] %v\n%s\n", info.Panic, info.Stack)
}

OnPanic is a global callback invoked whenever a panic is recovered inside a goroutine launched by this package.

By default, it prints the panic value and stack trace to stdout. Applications may override this function during initialization to provide custom logging, metrics, or alerting behavior.

Functions

This section is empty.

Types

type CancelMode added in v0.0.11

type CancelMode int

CancelMode controls how the context passed to a goroutine handles cancellation relative to its parent context.

const (
	// InheritCancel means the goroutine receives the original context
	// and therefore inherits its cancellation and deadline.
	InheritCancel CancelMode = iota

	// DetachCancel means the goroutine receives a context created with
	// context.WithoutCancel, so cancellation of the parent context does
	// not propagate to the goroutine.
	DetachCancel
)

type GoValueFunc

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

type PanicInfo

type PanicInfo struct {
	Panic any
	Stack []byte
}

PanicInfo contains information captured when a panic is recovered.

type Status

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

Status represents the lifecycle of a goroutine launched by the Go function. It provides a synchronization point to wait for the goroutine to finish.

func Go

func Go(ctx context.Context, f func(ctx context.Context), mode CancelMode) *Status

Go launches a new goroutine to execute f with panic recovery enabled.

Any panic raised during execution of f is recovered, and the global OnPanic handler is invoked if it is not nil.

The provided context is passed to both f and OnPanic. Context cancellation is cooperative: the goroutine will NOT stop automatically when ctx is canceled. The function f must observe ctx.Done() and return explicitly.

If mode is DetachCancel, f receives a context derived using context.WithoutCancel. In that case, cancellation and deadlines of the parent context will not propagate to the goroutine.

func (*Status) Wait

func (s *Status) Wait()

Wait blocks until the associated goroutine completes.

type ValueStatus

type ValueStatus[T any] struct {
	// contains filtered or unexported fields
}

ValueStatus represents the execution status of a goroutine that returns a value and an error.

func GoValue

func GoValue[T any](ctx context.Context, f GoValueFunc[T], mode CancelMode) *ValueStatus[T]

GoValue launches a new goroutine to execute f, capturing its returned value and error, with panic recovery enabled.

If f panics, the panic is recovered, reported via OnPanic, and converted into an error that is returned by Wait. In this case, the returned value is the zero value of T.

As with Go, context cancellation is cooperative: f must observe ctx.Done() if early termination is required.

If mode is DetachCancel, f receives a context derived using context.WithoutCancel. In that case, cancellation and deadlines of the parent context will not propagate to the goroutine.

func (*ValueStatus[T]) Wait

func (s *ValueStatus[T]) Wait() (T, error)

Wait blocks until the goroutine completes and returns the produced value and error.

If a panic occurred during execution, the returned error will describe the recovered panic.

Jump to

Keyboard shortcuts

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