-
Notifications
You must be signed in to change notification settings - Fork 18k
context: define that Err() == nil before Done is closed #19856
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
CC @Sajmani |
Maybe instead we should define that until Done, Err returns nil. Are there any implementations where that's not true? Put a different way, what problem are you trying to solve? The solution would cause pain for (some) people. What's the benefit? |
That would probably be better, since it appears to be a common mis-idiom, but I think that would also more explicitly break the compatibility promise since valid existing The problem I'm trying to solve is that I was working with a dependency which was cancelling early |
Yes, my main concern with changing the spec in this way is invalidating
existing implementations.
…On Wed, Apr 5, 2017 at 5:26 PM Alexis Hunt ***@***.***> wrote:
That would probably be better, since it appears to be a common mis-idiom,
but I think that would also more explicitly break the compatibility promise
since valid existing Context implementations would become invalid.
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#19856 (comment)>, or mute
the thread
<https://github.com/notifications/unsubscribe-auth/AJSK3QNVxGbrkhj0DHsqt7jSgTjEeOgfks5rtAcTgaJpZM4M0trn>
.
|
I can only find five Context implementations in go-corpus v0.01:
I searched for ' Done() <-chan struct{}' and sifted through all the non-context uses of that. All these implementations follow the 'Err is nil until Done' rule. I suspect the main implementations do too, but I haven't checked. All evidence for changing
to
and not non-nil. |
This would be a better solution from my view; I didn't suggest it because I assumed it was an unacceptable breaking change. |
I also looked in the go-corpus for checks of Err() against nil and they are all over the place (I've filtered out vendored copies of these, which would also need to be updated, and I've also exclude *_test.go files). It's telling that we can't even get this right in our own code (first two): cloud.google.com/go/bigtable/internal/gax/invoke.go
golang.org/x/crypto/acme/autocert/cache.go
github.com/coreos/etcd/client/client.go
github.com/coreos/etcd/clientv3/client.go
github.com/coreos/etcd/mvcc/kvstore.go
github.com/coreos/etcd/tools/benchmark/cmd/watch_get.go
github.com/docker/docker/daemon/cluster/cluster.go
github.com/docker/docker/daemon/cluster/executor/container/adapter.go
github.com/docker/docker/daemon/cluster/executor/container/controller.go
github.com/docker/swarmkit/node/node.go
|
CL https://golang.org/cl/40291 mentions this issue. |
I think your corpus results argue more that this should be a vet check than that we should change the definition of the interface. Even with the change, many (all?) of the examples you found should be waiting on |
@quentinmit, no they are not trying to wait, they are only doing a polling check. What they "should" be doing is:
instead of |
CL https://golang.org/cl/40396 mentions this issue. |
Context.Err() is not valid before Context.Done(). Updates golang/go#19856 Change-Id: I7605bb227bfc4cb542ef3db49870d4928ce704d1 Reviewed-on: https://go-review.googlesource.com/40396 Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Alex Vaghin <ddos@google.com>
In addition to the definition change, how about a one-shot stderr complaint from package context if Err() is called before Done() is closed? |
I don't think so, for two reasons: (1) if we change the definition, then it's fine to call Err before Done, and (2) Go programs don't chatter to standard error except for very serious problems. |
See also #14292 (comment). The scalability implications (introducing a contention point due to the need to synchronize |
But someone doing the correct behavior is already synchronizing when
receiving on `Done()`.
I don't know how channels are implemented, but if this is cheaper than,
say, a mutex check (e.g. if closed channels can be checked with an atomic
load rather than a lock), then Context implementers could use `Done()` as
the synchronization for `Err()`, so there should be little cost for correct
programs.
And if I understand the memory model correctly, an implementation that
isn't currently synchronizing `Err()` is erroneous, because a change to its
value may not be observed by a goroutine that does not call `Done()` to
synchronize with the `close`.
…On Wed, Apr 12, 2017, 16:45 Bryan C. Mills, ***@***.***> wrote:
See also #14292 (comment)
<#14292 (comment)>.
The scalability implications (introducing a contention point due to the
need to synchronize Err) might at least merit a lint check.
—
You are receiving this because you authored the thread.
Reply to this email directly, view it on GitHub
<#19856 (comment)>, or mute
the thread
<https://github.com/notifications/unsubscribe-auth/AT4HVTEPofYioncqFO44rqu3g88p2LLVks5rvTgFgaJpZM4M0trn>
.
|
@alercah You're correct that |
Thinking about this some more, the appropriate lint warning would probably be something like: That condition may be a bit advanced for |
Over on CL 40291, @Sajmani and @ianlancetaylor were discussing ctx.Err() check as being racy. Moving discussion here. At best the check err := ctx.Err(); err != nil is only half-racy: if err != nil then you know that's not going to change. If err == nil, then it might change the next moment, but that's OK and fundamental to the fact that contexts are for notification of cancellation. There's always a propagation delay between the top-level request for cancellation and the pieces of the computation noticing that request. That's not really a race. I think it's perfectly fine to do such a check before kicking off some expensive computation, or even at intervals during such a computation. As for how to do that, I guess we could go to some trouble that finds places where code says:
and helpfully remind people that they should instead be writing:
That doesn't seem like an improvement though. Better to redefine ctx.Err() to match existing usage, implementations, and expectations. |
"There's always a propagation delay between the top-level request for cancellation and the pieces of the computation noticing that request." I agree. For lengthy computations done in-process, this is the correct idiom and desirable behaviour. Because of the cooperative cancellation model in Go, there is no way to actually directly interrupt a computation short of quitting the process. A goroutine cannot simultaneously be block on The only time you're going to get an immediate response on a cancellation is when the goroutine checking cancellation has nothing else to do because it otherwise blocked. In that case it will |
Sameer is happy with this and I haven't seen any strong arguments against, so I'm going to accept this proposal. |
Currently,
context.emptyContext
(the type ofcontext.Background()
andcontext.TODO()
) has anErr()
ofnil
. This is fine, since the context also hasnil
Done()
and so the value ofErr()
is unspecified.However, much code ends up erroneously expecting
Err()
to benil
beforeDone()
is closed (if ever). IfemptyContext
were to give a non-nil
Err()
, which is permitted in the interface definition (since, again, it is unspecified), then this incorrect code would likely be caught more frequently. Since there is no requirement thatErr()
even becontext.Canceled
orcontext.DeadlineExceeded
in this case, we could makeemptyContext.Err()
return something likefmt.Errorf("checking %s.Err() without checking %s.Done() first", ctx, ctx)
to helpfully inform users of the mistake they are making.This would possibly break existing code, but only code that was already incorrect. If necessary, an alternate context could be provided which preserves the current behavior for people unwilling or unable to correct their code.
The text was updated successfully, but these errors were encountered: