Home / Go / Context edit

http://go-talks.appspot.com/github.com/dkondratovych/golang-ua-meetup/go-context/ctx.slide#35

type Context interface {
    // Deadline returns the time when work done
    // on behalf of this context should be canceled.
    Deadline() (deadline time.Time, ok bool)
    // Done returns a channel that's closed when work done 
    // on behalf of this context should be canceled.
    Done() <-chan struct{}
    // Err returns a non-nil error value after Done is closed.
    Err() error
    // Value returns the value associated with this context for key
    Value(key interface{}) interface{}
}
func Background() Context
func TODO() Context
func WithCancel(parent Context) (ctx Context, cancel CancelFunc)
func WithDeadline(parent Context, deadline time.Time) (Context, CancelFunc)
func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc)
func WithValue(parent Context, key, val interface{}) Context
// modify context of http.Request
func middleware(h http.HandlerFunc) http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {
        // Use request method context, which returns context, if ctx is nil
        // then creates new background context - context.Background()
        ctx = r.Context()
        // Build context variations on top of background context
        ctx = context.WithValue(ctx, "some_key", "some_value")
        // tctx, cancelFunc := context.WithTimeout(ctx, time.Duration(5 * time.Second))
        // deadline := time.Now().Add(time.Duration(30 * time.Second))
        // dctx, cancelFunc := context.WithDeadline(ctx, deadline)
        // WithContext returns a shallow copy of r with its context changed to ctx.
        r = r.WithContext(ctx)
        h(w, r)
    }
}
// pattern for setting/getting value in a context
type contextKey string

// making it un-exported package variable avoid naming conflics between
// packages
var userContextKey contextKey = "user"

func NewUserContext(ctx context.Context, user *User) context.Context {
    return context.WithValue(ctx, userContextKey, user)
}

func UserFromContext(ctx context.Context) (*User, bool) {
    u, ok := ctx.Value(userContextKey).(*User)
    return u, ok
}

func UserMustFromContext(ctx context.Context) *User {
    u, ok := ctx.Value(userContextKey).(*User)
    if !ok {
        panic("user not found in context")
    }
    return u
}
func handlerRequestWithCancelation(w http.ResponseWriter, r *http.Request) {
    longRunningCalculation := func(ctx context.Context) {
        for i := 0; ; i++ {
            select {
            case <-ctx.Done():
                return
            default:
                time.Sleep(1 * time.Second)
                fmt.Printf("Worker %d \n", i)
            }
        }
    }

    // the context is canceled when the ServeHTTP method returns
    go longRunningCalculation(r.Context())

    // give some time for longRunningCalculation to do some work
    time.Sleep(5 * time.Second)

    io.WriteString(w, "bazinga!")
    return
}
// explicitly cancelling context
func handlerSearchCancel(w http.ResponseWriter, r *http.Request) {
    var ctx context.Context
    var cancel context.CancelFunc

    ctx, cancel = context.WithCancel(r.Context())
    defer cancel()

    // Close context.Done channel in 4 seconds
    go func() {
        time.Sleep(4 * time.Second)
        cancel()
    }()

    select {
    case <-ctx.Done():
        log.Print(ctx.Err())
        return
    case result := <-longRunningCalculation():
        io.WriteString(w, result)
    }

    return
}

How can I use WithTimeout and WithDeadline ?

Context with timeout

ctx, cancel = context.WithTimeout(ctx, time.Duration(7 * time.Second))
defer cancel()

Context with deadline

deadline := time.Now().Add(time.Duration(30 * time.Second))
dctx, cancel := context.WithDeadline(ctx, deadline)
defer cancel()

Pattern here is the same, you have to wait for signal from ctx.Done

select {
case <-ctx.Done():
    log.Print(ctx.Err())
    return
case result := <-longRunningCalculation():
    io.WriteString(w, result)
}
func handlerSearchTimeout(w http.ResponseWriter, r *http.Request) {
    var ctx context.Context
    var cancel context.CancelFunc

    ctx, cancel = context.WithTimeout(r.Context(), time.Duration(2*time.Second))
    defer cancel()

    request, err := http.NewRequest(http.MethodGet, "http://localhost:8181/timeout", nil)
    if err != nil {
        log.Println(err.Error())
        return
    }
    // You can context separately for each request
    request = request.WithContext(ctx)

    client := &http.Client{}
    response, err := client.Do(request)
    // You will get an error "net/http: request canceled" when request timeout exceeds limits
    if err != nil {
        log.Println(err.Error())
        return
    }
    // ......

Go to index of articles.

Share on