-
Notifications
You must be signed in to change notification settings - Fork 60
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
Allow the creation of a context.Context
that uses a clockwork.Clock
#94
Comments
I'm not sure I understand the race condition in |
That, and having |
Most of the time, the immediately subsequent call to You need a way to ensure the lock is acquired and released only once across the combination of |
I think we can overcome questions 2 and 3 if we are good with the following API calls:
Assuming these are acceptable, the only open question is question 1. |
Regarding type casting, remember that you can also cast to an interface, so you don't necessarily need to check whether the clock is a FakeClock or not - you can just detect the presence of certain methods. errors in stdlib makes heavy use of this feature. Looks pretty janky, but the usage code remains clean and you can always add new types of Clock that opt in to the specialized behaviour https://github.com/golang/go/blob/master/src/errors/wrap.go#L17 So you could have a free function like this (untested) func At(clock Clock, t time.Time) <- chan time.Time {
// Call dedicated thread-safe alternative if available
if c, ok := clock.(interface{ At(t time.Time) <- chan time.Time }); ok {
return c.At(t)
}
// Fall back to näive implementation
return clock.After(clock.Until(t))
} |
Oh man, I admit I totally forgot about that. I think I wrote it off because, as you mention, it looks/feels janky. Great call though. I wanted to touch base on Open Question 1. I am still looking for someone to provide feedback on the lesser of the 2 evils there. If I don't hear strong opinions otherwise I'm going to execute on Option 1, that the child context cancels if/when the parent cancels, just because that is easier to implement. Maybe I can add a custom error to determine if the cancellation was from a fake clock or not. Perhaps that is enough. Actually... as I type that I think that is the cleanest/easiest way forward. I plan to revisit this over the weekend and ideally get a PR out. Thanks for the feedback @jonathangjertsen |
Agree with option 1. I expect a child context to cancel when its parent cancels for any reason, and would be surprised if that didn't happen. If the cancellation is caused by a fake clock, all is well. If it's caused by a real clock, that means you're mixing real and fake time in the same program which is not really meaningful and shouldn't necessarily be supported IMO. |
Please see #92, which I have reopened and implemented the agreements we came to above. Of note:
I'll leave this open for comment for a week or 2 and then merge it & then address #93. Sorry for the delay. Real life and work life have robbed me of spare headspace/cycles the last few weeks. |
This issue was prompted by discussions in PRs #86 & #92. The high level ask is to provide methods to construct a
context.Context
that uses aclockwork.Clock
for deadline determination.This should include the creation of functions analogous to
context.WithDeadline
andcontext.WithTimeout
.Open questions
Context
s usingFakeClock
when their parent is cancelled withcontext.DeadlineExceeded
? The options I am currently aware of are:context.DeadlineExceeded
, possibly due to a deadline from a real clock used by the parent context.context.CancelFunc
).context.WithDeadline
? Specifically, the following code block has a race condition with concurrent users of clock.WithTimeout
variant is easy, becausetime.After
is mirrored withclockwork.Clock.After
, and thus the resulting channel and backing goroutine can be generated with a single a mutex lock. However there is no equivalentAt(t Time) <- chan Time
in thetime
library, and thus we can't add that to theclockwork.Clock
interface.Initial responses to questions / requests for information
I am recusing myself on this question because I find both these options sufficiently distasteful that I don't personally intend to use this feature. @3vilhamster, since you were the initial requester, what behavior would you prefer, and ideally, why is it better than the alternative?
In Add contexts that use FakeClock rather than the system time. #92, I suggested the interface
WithDeadline(parent context.Context, clock *FakeClock, t time.Time) (context.Context, context.CancelFunc)
and its timeout analog. I used aFakeClock
due to the issue mentioned in Question 3 below. The response was twofold:*FakeClock
vs aClock
interface, which I agree with unless there is no alternative.Question: With regard to point 2 above, why is this API "not nice"? Was that just because it required the reference to
FakeClock
? If that were replaced withClock
, would that resolve the issue?I think this will have to be resolved by adding
FakeClock.at(t Time) <- chan Time
(note that it is not exported) so we can build a timer channel inside a single mutex lock and avoid the race condition. We can then discuss typecasting (yuck) vs exposingFakeClock
in the function directly. As I type this, I realize that perhaps typecasting is fine if we just fallback to the standard libraries if the type is not aFakeClock
.The text was updated successfully, but these errors were encountered: