Skip to content

Instantly share code, notes, and snippets.

@Hunsin
Created October 27, 2024 07:46
Show Gist options
  • Save Hunsin/81f755b8242da232b9698b583013d46f to your computer and use it in GitHub Desktop.
Save Hunsin/81f755b8242da232b9698b583013d46f to your computer and use it in GitHub Desktop.
Package ctxutil provides some helper functions for manipulating Golang contexts.
package ctxutil
import (
"context"
"os"
"os/signal"
)
// An ErrSignal represents an error triggered by an operating system signal.
type ErrSignal struct {
sig os.Signal
}
func (e *ErrSignal) Error() string {
return "ctxutil: signal received: " + e.sig.String()
}
// WithSignal returns a copy of parent with a new Done channel. The new Done
// channel is closed when any of the given signals is received.
func WithSignal(parent context.Context, sig ...os.Signal) (context.Context, context.CancelFunc) {
ch := make(chan os.Signal, 1)
signal.Notify(ch, sig...)
ctx, cancel := context.WithCancelCause(parent)
go func() {
select {
case <-ctx.Done():
cancel(nil)
case sig := <-ch:
cancel(&ErrSignal{sig})
}
}()
return ctx, func() { cancel(nil) }
}
package ctxutil
import (
"context"
"os"
"syscall"
"testing"
"time"
)
func TestWithSignal(t *testing.T) {
parent := context.Background()
ctx, cancel := WithSignal(parent, syscall.SIGINT)
defer cancel()
p, err := os.FindProcess(os.Getpid())
if err != nil {
t.Fatalf("failed finding current process: %s", err)
}
if err = p.Signal(syscall.SIGINT); err != nil {
t.Fatalf("failed sending signal: %s", err)
}
// wait for a short time to make sure the channel has been closed
time.Sleep(time.Millisecond)
select {
case <-ctx.Done():
// success
default:
t.Error("WithSignal() failed")
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment