Commit 00a3c085 authored by Kirill Smelkov's avatar Kirill Smelkov

.

parent 60125d35
// Copyright (C) 2017 Nexedi SA and Contributors.
// Copyright (C) 2017-2018 Nexedi SA and Contributors.
// Kirill Smelkov <kirr@nexedi.com>
//
// This program is free software: you can Use, Study, Modify and Redistribute
......@@ -27,7 +27,7 @@ import (
"lab.nexedi.com/kirr/go123/xerr"
)
// Task represents currently running operation
// Task represents currently running operation.
type Task struct {
Parent *Task
Name string
......@@ -35,17 +35,18 @@ type Task struct {
type taskKey struct{}
// Running creates new task and returns new context with that task set to current
// Running creates new task and returns new context with that task set to current.
func Running(ctx context.Context, name string) context.Context {
return context.WithValue(ctx, taskKey{}, &Task{Parent: Current(ctx), Name: name})
}
// Runningf is Running cousin with formatting support
// Runningf is Running cousin with formatting support.
func Runningf(ctx context.Context, format string, argv ...interface{}) context.Context {
return Running(ctx, fmt.Sprintf(format, argv...))
}
// Current returns current task represented by context.
//
// if there is no current task - it returns nil.
func Current(ctx context.Context) *Task {
task, _ := ctx.Value(taskKey{}).(*Task)
......@@ -53,6 +54,7 @@ func Current(ctx context.Context) *Task {
}
// ErrContext adds current task name to error on error return.
//
// To work as intended it should be called under defer like this:
//
// func myfunc(ctx, ...) (..., err error) {
......
// Copyright (C) 2017 Nexedi SA and Contributors.
// Copyright (C) 2017-2018 Nexedi SA and Contributors.
// Kirill Smelkov <kirr@nexedi.com>
//
// This program is free software: you can Use, Study, Modify and Redistribute
......@@ -18,6 +18,35 @@
// See https://www.nexedi.com/licensing for rationale and options.
// Package xcontext provides addons to std package context.
//
// Merging contexts
//
// Merge and MergeChan could be handy in situations where spawned job needs to
// be canceled whenever any of 2 contexts becomes done. This frequently arises
// with service methods which accept context as argument, and the service
// itself, on another control line, could be instructed to become
// non-operational. For example:
//
// func (srv *Service) DoSomething(ctx context.Context) error {
// // srv.down is chan struct{} that becomes ready when service is closed.
// ctxDown, down := xcontext.MergeChan(ctx, srv.down)
// defer down()
//
// err := doJob(ctxDown)
// if ctxDown.Err() != nil && ctx.Err() == nil {
// err = ErrDueToServiceDown
// }
//
// ...
// }
//
//
//
// XXX docs:
// - Canceled
// - Merge
//
// - WhenDone
package xcontext
import (
......@@ -26,6 +55,17 @@ import (
"time"
)
// mergeCtx represents 2 context merged into 1.
type mergeCtx struct {
ctx1, ctx2 context.Context
done chan struct{}
doneErr error
cancelCh chan struct{}
cancelOnce sync.Once
}
// Merge merges 2 contexts into 1.
//
// The result context:
......@@ -36,9 +76,6 @@ import (
//
// Canceling this context releases resources associated with it, so code should
// call cancel as soon as the operations running in this Context complete.
//
// XXX let Merge do only merge, not create another cancel; optimize it for
// cases when a source context is not cancellable
func Merge(ctx1, ctx2 context.Context) (context.Context, context.CancelFunc) {
mc := &mergeCtx{
ctx1: ctx1,
......@@ -47,15 +84,6 @@ func Merge(ctx1, ctx2 context.Context) (context.Context, context.CancelFunc) {
cancelCh: make(chan struct{}),
}
/*
// ctx1 will never be canceled?
switch ctx1.Done() {
case nil, context.Background().Done():
bg1 = true
}
// ----//---- same for ctx2?
*/
// if src ctx is already cancelled - make mc cancelled right after creation
//
// this saves goroutine spawn and makes
......@@ -80,16 +108,6 @@ func Merge(ctx1, ctx2 context.Context) (context.Context, context.CancelFunc) {
return mc, mc.cancel
}
type mergeCtx struct {
ctx1, ctx2 context.Context
done chan struct{}
doneErr error
cancelCh chan struct{}
cancelOnce sync.Once
}
// wait waits when .ctx1 or .ctx2 is done and then mark mergeCtx as done
func (mc *mergeCtx) wait() {
select {
......@@ -106,18 +124,21 @@ func (mc *mergeCtx) wait() {
close(mc.done)
}
// cancel sends signal to wait to shutdown
// cancel is the context.CancelFunc returned for mergeCtx by Merge
// cancel sends signal to wait to shutdown.
//
// cancel is the context.CancelFunc returned for mergeCtx by Merge.
func (mc *mergeCtx) cancel() {
mc.cancelOnce.Do(func() {
close(mc.cancelCh)
})
}
// Done implements context.Context .
func (mc *mergeCtx) Done() <-chan struct{} {
return mc.done
}
// Err implements context.Context .
func (mc *mergeCtx) Err() error {
// synchronize on .done to avoid .doneErr read races
select {
......@@ -132,6 +153,7 @@ func (mc *mergeCtx) Err() error {
return mc.doneErr
}
// Deadline implements context.Context .
func (mc *mergeCtx) Deadline() (time.Time, bool) {
d1, ok1 := mc.ctx1.Deadline()
d2, ok2 := mc.ctx2.Deadline()
......@@ -147,6 +169,7 @@ func (mc *mergeCtx) Deadline() (time.Time, bool) {
}
}
// Value implements context.Context .
func (mc *mergeCtx) Value(key interface{}) interface{} {
v := mc.ctx1.Value(key)
if v != nil {
......
// Copyright (C) 2017 Nexedi SA and Contributors.
// Copyright (C) 2017-2018 Nexedi SA and Contributors.
// Kirill Smelkov <kirr@nexedi.com>
//
// This program is free software: you can Use, Study, Modify and Redistribute
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment