-
Notifications
You must be signed in to change notification settings - Fork 17.8k
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
time: Timer.Stop documentation example easily leads to deadlocks #27169
Comments
TL/DR: This is incorrect usage and the documentation kinda mentions it but it takes a while to understand it correctly so while it's documented it could be documented better.
Isn't this incorrect usage because you've already received from The way it actually works is that Stop() returns false in case the timer has already fired which means UNLESS you haven't ALREADY read from it then there's a value in
This prevents the deadlock, sure and it's always safe to do that because if Stop() returns true you enter the default case and if it returns false you enter the default case as well because |
FWIW: This would be an example of proper usage:
|
I believe you don't really need to drain the timer channel for this, calling Timer.Stop will suffice:
You may find such pattern in use in standard library. |
@FMNSSun
Looks scary. What if timer triggers between the |
@palsivertsen No because timer uses a buffered channel with capacity 1 exactly for this reason: that it can't get stuck if there's nobody reading from it. Also... it might make sense in that case to move the |
Cool. I did not know that.
Wouldn't that deadlock if |
@palsivertsen it would but you could set a flag in the |
+1 this issue just cost me an hour :( I also have the keep-alive scenario. Perhaps the the docs should link to this discussion? |
I don't think we want to link to this discussion. Does anyone have specific improvements to suggest? Anyone want to send a pull request? Thanks. |
Some suggestions/thoughts:
|
Change https://golang.org/cl/185245 mentions this issue: |
Updates #27169 Change-Id: I22a6194c06529ba70b1ec648e3188c191224e321 GitHub-Last-Rev: 457b2a6 GitHub-Pull-Request: #32996 Reviewed-on: https://go-review.googlesource.com/c/go/+/185245 Reviewed-by: Rob Pike <[email protected]>
If stateTransTimer has already been stopped (without reset) then drain will deadlock. Avoid this by using a select + default case, as done in golang/go#27169 . Signed-off-by: David Disseldorp <[email protected]>
…eading to a memory leak. The documentation implied something which doesn't seem to actually be true when looked at in the detail: golang/go#27169
* Add missing bytes field to backend response log * (docs) remove unknown utf8 chars and reformat tables * (docs) add response.bytes field to backend log documentation * Add content-length fallback * rm obsolete key * more checks in test * handle timeout cancel select case * fmt * Fix missing token request within custom log context * Fix possible timer deadlock golang/go#27169 * cancel while reading results * fire collected backend logStack at the end endpoint handler has to many exits; even with panic recovers * Add changelog entry Co-authored-by: Alex Schneider <[email protected]>
attempting to drain an already drained timer causes a deadlock and prevents activityTimer from being reset therefore heartbeat pings are not periodically sent. Issue documented here: golang/go#27169
@FMNSSun do you have a source for this? |
The documentation no longer gives this example, because it is no longer necessary. |
…tation example no longer leads to deadlocks, as Timer/Ticker channels not receivable with old values after Stop or Reset returns. golang/go#27169 golang/go#37196 golang/go#14383
…tation example no longer leads to deadlocks, as Timer/Ticker channels not receivable with old values after Stop or Reset returns. golang/go#27169 golang/go#37196 golang/go#14383
…tation example no longer leads to deadlocks, as Timer/Ticker channels not receivable with old values after Stop or Reset returns. golang/go#27169 golang/go#37196 golang/go#14383
…tation example no longer leads to deadlocks, as Timer/Ticker channels not receivable with old values after Stop or Reset returns. golang/go#27169 golang/go#37196 golang/go#14383
…tation example no longer leads to deadlocks, as Timer/Ticker channels not receivable with old values after Stop or Reset returns. golang/go#27169 golang/go#37196 golang/go#14383
I needed timeout functionality for one of my projects, so I looked in the
time
package. My timeouts where fallbacks in case a channel receive took too long. Most of the time the channel would receive before the timeout and I wanted to release the timeout resources when they where no longer needed. Documentation for time.After() says:So I used a time.Timer and according to the documentation for time.Timer.Stop() one should drain the channel if
time.Timer.Stop()
returns false:I later discovered that my threads got stuck on receive like in this playground example when timer where triggered before I called stop:
Wrapping the drain in a select seems to do the trick:
Documentation should make it clear how to safely drain the channel.
The text was updated successfully, but these errors were encountered: