Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I sleep with responsive context cancelation?

Tags:

go

In Go, I want to time.Sleep for some time (e.g. waiting between retries), but want to return quickly if the context gets canceled (not just from a deadline, but also manually).

What is the right or best way to do that? Thanks!

like image 547
Aseem Kishore Avatar asked Mar 13 '19 05:03

Aseem Kishore


People also ask

What is CTX done?

<-ctx. Done() once the Done channel is closed, the case <-ctx. Done(): is selected. Once this happens, the function should abandon work and prepare to return. That means you should close any open pipes, free resources and return form the function.

When should you cancel context?

Request. Context method returns a context associated with the request. That context is canceled if the HTTP client disconnects or cancels the HTTP request (possible in HTTP/2).


2 Answers

You can use select to acheive this:

package main

import (
    "fmt"
    "time"
    "context"
)

func main() {
    fmt.Println("Hello, playground")
    ctx, cancel := context.WithCancel(context.Background())
    defer cancel()
    go func(){
        t := time.Now()
        select{
        case <-ctx.Done(): //context cancelled
        case <-time.After(2 * time.Second): //timeout
        }
        fmt.Printf("here after: %v\n", time.Since(t))
    }()

    cancel() //cancel manually, comment out to see timeout kick in
    time.Sleep(3 * time.Second)
    fmt.Println("done")

}

Here is the Go-playground link

like image 175
Saurav Prakash Avatar answered Oct 04 '22 03:10

Saurav Prakash


Here is a sleepContext function that you can use instead of time.Sleep:

func sleepContext(ctx context.Context, delay time.Duration) {
    select {
    case <-ctx.Done():
    case <-time.After(delay):
    }
}

And some sample usage (full runnable code on the Go Playground):

func main() {
    ctx := context.Background()

    fmt.Println(time.Now())
    sleepContext(ctx, 1*time.Second)
    fmt.Println(time.Now())

    ctxTimeout, cancel := context.WithTimeout(ctx, 500*time.Millisecond)
    sleepContext(ctxTimeout, 1*time.Second)
    cancel()
    fmt.Println(time.Now())
}
like image 34
dolmen Avatar answered Oct 04 '22 04:10

dolmen