Fragment Description:

context.Context is meant to help timing out or ending early long running operations.
A Context carries a deadline, a cancellation signal, and other values across API boundaries.
Context's methods may be called by multiple goroutines simultaneously.
A note about Context.Value:
it should inform, not control.
'Inform, not control.
This is the primary mantra that I feel should guide if you are using context.Value correctly.
The content of context.Value is for maintainers not users.
It should never be a required input for documented or expected results.
To clarify, if your function can’t behave correctly because of a value that may or may not be inside context.Value, then your API is obscuring required inputs too heavily.
said Jack Lindamood in his excellent article '".


Go Playground

Last update, on 2017, Tue 7 Feb, 17:39:32

/* ... <== see fragment description ... */

package main

import (

type Transaction struct {
    Name string
    ID   int64

func ProcessTransaction(ctx context.Context, Transaction *Transaction) {
    // sign: func (c *valueCtx) Value(key interface{}) interface{}
    // here Value will just inform us about the status of Transaction
    completed := ctx.Value("completed").(chan struct{}) // type assertion
    for {
        select {
        case <-completed:
            fmt.Printf("\nThe Transaction of [%d] has been completed.\n", Transaction.ID)
        case <-ctx.Done():
            // Done() causes: either by a Cancellation or a Deadline being exceeded
            if ctx.Err() == context.Canceled {
                fmt.Printf("\nThe Transaction [%d] has been canceled.\n", Transaction.ID)
            } else if ctx.Err() == context.DeadlineExceeded {
                fmt.Printf("\nThe DELAY for Transaction has expired. The Transaction with ID [%d] is being terminated.\n", Transaction.ID)
            time.Sleep(time.Duration(rand.Int63n(0.15 * 1e9)))
func main() {
    // let's define a signalling channel
    completed := make(chan struct{})
    var ctx context.Context
    var cancel context.CancelFunc
    // let's configure our context
    ctx = context.WithValue(context.Background(), "completed", completed)
    ctx, cancel = context.WithTimeout(ctx, 8*time.Second)
    txn := &Transaction{
        Name: "ourControlledTransaction",
        ID:   20170204}
    // start a goroutine with a context to control it
    go ProcessTransaction(ctx, txn)
    // a command line simple user interface
    fmt.Printf("-----\nThe Transaction [%d] is waiting for your input.\n", txn.ID)
    deadline, ok := ctx.Deadline()
    if ok {
        fmt.Printf("When the DELAY of %q will be passed, the Transaction will be terminated [%d].\n-----\n", deadline.Sub(time.Now()).String(), txn.ID)
    // fmt.Println()
    fmt.Println("Please choose an option:")
    reader := bufio.NewReader(os.Stdin)
    for {
        fmt.Print("\n[P]rocess, [C]ancel: ")
        // capture input from Stdin
        line, err := reader.ReadString('\n')
        if err == nil {
            command := strings.TrimSuffix(line, "\n")
            switch command {
            case "P":
                fmt.Printf("\nThe Transaction [%d] is being processed!\n", txn.ID)
                // let's simulate some work is being done
                time.Sleep(200 * time.Millisecond)
                // let's signal the transaction has been processed and is completed
                completed <- struct{}{}
            case "C":
                fmt.Printf("\nThe Transaction [%d] is being cancelled!\n", txn.ID)
                // let's start the txn...
                time.Sleep(200 * time.Millisecond)
                // let's cancel the txn
                fmt.Printf("Wrong option: %q. Please try again.\n", command)

/* Expected output:
The Transaction [20170204] is waiting for your input.
When the DELAY of "7.999953293s" will be passed, the Transaction will be terminated [20170204].
Please choose an option:
[P]rocess, [C]ancel: P
if P:  The Transaction [20170204] is being processed!
if C:  The Transaction [20170204] is being cancelled!
if other key:  Wrong option: "f". Please try again.
               [P]rocess, [C]ancel:
if nothing:    The DELAY for Transaction has expired. The Transaction with ID [20170204] is being terminated.