top | item 30206800

(no title)

sethvargo | 4 years ago

It really doesn't though. It handles the case where the context might have expired or be cancelled, but there's still a race when entering the select between the ctx.Done() and reading from thingCh. You may end up processing one additional unit of work. In situations where the exit condition is channel-based, this won't work.

Additionally, this would only work if you had one predominant condition and that condition was context-based. If you have multiple ordered conditions upon which you want to exit, I can't think of how you'd express that as a range.

discuss

order

assbuttbuttass|4 years ago

I'm not sure what you mean. There's always going to be a race condition between ctx.Done and thingCh, just depending on whether there's data available. This race condition is unavoidable.

I guess you're thinking of "what if thingCh and ctx.Done activate simultaneously?"

There's no real difference between happening simultaneously and happening one after another.

As for your other point, you can just write code like

    select {
    case x := <-conditionA:
        return x
    default:
    }
    select {
    case x := <-conditionB:
        return x
    default:
    }
    ...
But I've personally never needed code like this.

sethvargo|4 years ago

Also, this isn't semantically correct. In order to ensure that `conditionaA` is _always_ preferred over `conditionB`, you must also check if `conditionA` has received a value inside of `conditionB`:

    select {
    case a := <-conditionA:
        return a
    default:
    }
    select {
    case b := <-conditionB:
      case a := <-conditionA:
          return a
      default:

      return b
    default:
    }

sethvargo|4 years ago

Right, which is noted in the post. That verbosity is, well, verbose. I generally need this in 20% of things I write.

morelisp|4 years ago

Context cancellation propagates (potentially) asynchronously anyway, so if you're relying on something canceling your context and that immediately appearing you already have a bug.

I've written `select { ..., default: }` enough times I also wish it had shorthand syntax - sometimes it's even clearer to range one "primary" channel and lead the code block with that check - but I cannot think of a case where relying on a deterministic select would not have led to a bug.