Cancel a launched goroutine

Published May 30, 2022

One of the more challenging tasks in the past few months was for me was to find a way to implement the following feature:

After a set amount of seconds (provided in the form of an environment variable) launch a goroutine which 
sends control data to another service with a way to cancel the whole process in case of an external event.

If you come from perhaps a Java background then this task seems pretty trivial, you “just” have to call the interrupt() function on your thread, but with goroutines this is not an option.

Luckily Go provides a lot of great synchronization primitives which allowed me to produce the following solution:

// Create a derived context from context.Background()
ctx, cancelCtx := context.WithCancel(context.Background())
defer cancelCtx()

go func(ctx context.Context) {
    t := time.NewTimer(time.Duration(seconds) * time.Second)
    select {
        case <-t.C:
            // send the control data to the next service
        case <-ctx.Done()
          return
    }
}(ctx)

The select statement in our goroutine lets us wait on multiple operations and will block until one of the cases can run, which are:

  • time.NewTimer sending the current time on its channel after the duration provided
  • calling the cancelCtx function which closes the Done channel

When the timer expires and we do not cancel the context, we send the data to the next service and cancelling the context causes the goroutine to return and terminate.

P.S. Always close your contexts: https://developer.squareup.com/blog/always-be-closing/