100-go-mistakes icon indicating copy to clipboard operation
100-go-mistakes copied to clipboard

Context Cancellation Select

Open FarhanSajid1 opened this issue 4 months ago • 0 comments

Describe the mistake

In section 8.6.4 "catching a context cancellation", we have the following solution for handling a context cancellation prior to reading/writing from a channel


func f(ctx context.Context) error {
    // ...
    select {
    case <-ctx.Done():
        return ctx.Err()
    case ch1 <- struct{}{}:
    }
    select {
    case <-ctx.Done():
        return ctx.Err()
    case v := <-ch2:
// ... }
}

Essentially, check if the context is done prior to reading/writing.

This is contradicted by mistake #64 (Expecting deterministic behavior using select and channels)

Which goes into detail about this

_If one or more of the communications can proceed, a single one that can proceed is chosen via a uniform pseudo-random selection. _

Given that, we cannot use the following approach, what if both are able to be read, the go runtime will choose one at random and that could potentially block?

Solution

I've seen the following solution, it's not the best, but it should work

    ticker := time.NewTicker(time.Second)
    defer ticker.Stop()

    for {            
        //select as usual
        select {
        case <-ctx.Done():
            return
        case <-ticker.C:
            //give priority to a possible concurrent Done() event non-blocking way
            select {
              case <-ctx.Done():
              return
            default:
            }
            sendHeartbeat()
        }
    }

As outlined here: https://stackoverflow.com/a/51296312

FarhanSajid1 avatar Oct 24 '24 02:10 FarhanSajid1