back to TILs

Go select without the coin flip

In Go, it’s common to wait on a condition via a channel while also respecting context cancellation. A common idiom looks like:

select {
case <-condition:
	return nil
case <-ctx.Done():
	return ctx.Err()
}

An issue with this is that if you’d prefer that condition (i.e., the non-error case) be treated with precedence, you’re racing with ctx.Done(); select chooses a case at random when multiple cases are ready.

My coworker suggested a solution with a nested select within the ctx.Done() case to re-check condition:

select {
case <-condition:
	return nil
case <-ctx.Done():
	// Re-check condition in case we raced above.
	select {
	case <-condition:
		return nil
	default:
		return ctx.Err()
	}
}

But I don’t care for that nested select. Looking at this Go proposal, it looks like this is the best existing idiom, but I’d love to know if there’s a better way of solving this without the nested-select smell.