Add project documentation and licensing
[go-signalcontext.git] / context.go
index 1bd24d3..5ed10d5 100644 (file)
@@ -9,7 +9,8 @@ import (
 )
 
 // A Context is an implementation of the context.Context interface which
-// completes when a signal (e.g. os.Interrupt) is received.
+// cancels when an operating system signal (e.g. os.Interrupt) is
+// received.
 //
 // Contexts should be created via the UntilSignal function.
 type Context struct {
@@ -23,19 +24,20 @@ type Context struct {
        c chan os.Signal
 }
 
-// UntilSignal returns a new Context which will complete when the parent
-// does or when any of the specified signals are received.
+// UntilSignal returns a new Context which will cancel when the parent
+// does or when any of the specified operating system signals are
+// received.
 func UntilSignal(parent context.Context, sig ...os.Signal) *Context {
        ctx := new(Context)
        ctx.parent = parent
-       ctx.done = make(chan struct{})
 
        if err := parent.Err(); err != nil {
-               close(ctx.done)
+               ctx.done = alreadyclosed
                ctx.err = err
                return ctx
        }
 
+       ctx.done = make(chan struct{})
        ctx.c = make(chan os.Signal, 1)
        signal.Notify(ctx.c, sig...)
        go ctx.wait(sig...)
@@ -63,8 +65,10 @@ func (s *Context) wait(sig ...os.Signal) {
        close(s.done)
 }
 
-// Cancel cancels this context, if it hasn’t already been completed. (If
-// it has, this is safe but has no effect.)
+// Cancel cancels this context, if it hasn’t already been. (If it has,
+// this is safe but has no effect.) Canceling this context releases
+// resources associated with it and stops listening for the configured
+// operating system signals.
 func (s *Context) Cancel() {
        s.m.Lock()
        if s.c != nil {
@@ -83,23 +87,35 @@ func (s *Context) Deadline() (time.Time, bool) {
        return s.parent.Deadline()
 }
 
-// Value implements context.Context; any value is that of its parent.
+// Value implements context.Context; any Context value is that of its
+// parent.
 func (s *Context) Value(key interface{}) interface{} {
        return s.parent.Value(key)
 }
 
-// Done implements context.Context.
+// Done returns a channel that's closed when work done on behalf of this
+// context should be canceled, either because the parent finished or
+// because one of the configured operating system signals was received.
 func (s *Context) Done() <-chan struct{} {
        return s.done
 }
 
 // Err implements context.Context; it returns context.Canceled if the
-// context was canceled; an Error if the context completed due to a
-// signal; the parent’s error if the parent was done before either of
-// those; or nil if the context is not yet done.
+// context was canceled by its Cancel method; an Error if the context
+// canceled due to a operating system signal; the parent’s error if the
+// parent canceled before either of those; or nil if the context is not
+// yet canceled.
 func (s *Context) Err() error {
        s.m.Lock()
        err := s.err
        s.m.Unlock()
        return err
 }
+
+// Reuse the same channel for all Contexts which begin life already
+// canceled.
+var alreadyclosed = make(chan struct{})
+
+func init() {
+       close(alreadyclosed)
+}