ctxtool/cancel.go (50 lines of code) (raw):
// Licensed to Elasticsearch B.V. under one or more contributor
// license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright
// ownership. Elasticsearch B.V. licenses this file to you under
// the Apache License, Version 2.0 (the "License"); you may
// not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package ctxtool
import (
"context"
"time"
)
type cancelContext struct {
canceller
}
// AutoCancel collects cancel functions to be executed at the end of the
// function scope.
//
// Example:
// var ac AutoCancel
// defer ac.Cancel()
// ctx := ac.With(context.WithCancel(context.Background()))
// ctx := ac.With(context.WithTimeout(ctx, 5 * time.Second))
// ... // do something with ctx
type AutoCancel struct {
funcs []context.CancelFunc
}
// CancelContext holds a context with its corresponding CancelFunc.
// The cancel func will be called when calling `Cancel` on `CancelContext`.
//
// Although it is discouraged to store context.Context in structs, sometimes it
// might be necessary to do so, because the cancellation needs to be called
// asynchronously, or an interface with a Stop method is required
// (context.Context is not part of the public API). One might still want to use a `context.Context`
// for shutdown handling and propagation in that case.
type CancelContext struct {
context.Context
cancel context.CancelFunc
}
// Cancel calls all registered cancel functions in reverse order.
func (ac *AutoCancel) Cancel() {
for _, fn := range ac.funcs {
defer fn()
}
}
// Add adds a new cancel function to the AutoCancel. The function will be run
// before any other already registered cancel function.
func (ac *AutoCancel) Add(fn context.CancelFunc) {
ac.funcs = append(ac.funcs, fn)
}
// With is used to wrap a Context constructer call that returns a context and a
// cancel function. The cancel function is automatically added to AutoCancel
// and the original context is returned as is.
func (ac *AutoCancel) With(ctx canceller, cancel context.CancelFunc) context.Context {
ac.Add(cancel)
return FromCanceller(ctx)
}
// FromCanceller creates a new context from a canceller. If a value that
// implements contex.Context is passed, then the value will be returned as is.
func FromCanceller(c canceller) context.Context {
if ctx, ok := c.(context.Context); ok {
return ctx
}
return cancelContext{c}
}
func (c cancelContext) Deadline() (deadline time.Time, ok bool) {
return time.Time{}, false
}
func (c cancelContext) Value(key interface{}) interface{} {
return nil
}
// WithCancelContext creates a new CancelContext similar to `context.WithCancel`.
// The `(CancelContext).Cancel()` method must be called in order to clean up associated resources.
func WithCancelContext(parent context.Context) CancelContext {
return WrapCancel(context.WithCancel(parent))
}
// WrapCancel creates a CancelContext from an existing context and the associated cancel function.
//
// Example:
//
// cancelCtx := WrapCancel(context.WithCancel(parent))
func WrapCancel(parent context.Context, cancel context.CancelFunc) CancelContext {
return CancelContext{Context: parent, cancel: cancel}
}
// Cancel calls the underlying cancel function, which marks the associated
// context as cancelled.
func (c CancelContext) Cancel() {
if c.cancel != nil {
c.cancel()
}
}