Description
After almost a decade, I have finally implemented #8898, special-casing the channels used for timers (or maybe the timers used for channels) so that the timers are not in the timer heap when there are no channel operations pending on them. This means that the channels and timers can be GC'ed once they are no longer referenced, without having to wait for a timer to expire or explicitly Stop the timer. (By timer here I mean the data structure used by time.After, time.NewTimer, and time.NewTicker.)
This raises a question: do we want to guarantee that all future versions of Go will provide this behavior, so that code can rely on not needing to call the Stop methods? I think we probably should make that guarantee.
I propose we land my CL and document that After can be used without concern for GC and that NewTimer and NewTicker can be used without concern for deferring Stop just for GC. At the moment the doc comment for After describes the problem and basically says "don't use this function a lot". If we accept this proposal, we can remove that text. NewTimer and NewTicker do not mention needing to call Stop, although it is implied by After's doc comment. If we accept this proposal, we can document that Stop is not necessary for GC, so that people can stop calling Stop.
Activity
gopherbot commentedon Jul 23, 2023
Change https://go.dev/cl/512355 mentions this issue:
time: garbage collect unstopped Tickers and Timers
bcmills commentedon Jul 24, 2023
This seems like it would require other Go implementations (such as gccgo and TinyGo) to implement those same special cases. Are we sure that's viable for them?
bcmills commentedon Jul 24, 2023
That said, I think we basically have to do this to avoid the portability pitfall I mentioned in #8898 (comment). I don't want to be in a long-term state where it's ok to omit the
Stop
only if you know that your code will never be compiled with gccgo or TinyGo.rsc commentedon Jul 24, 2023
I can't see why it wouldn't be a viable change for gccgo or TinyGo. The change is not terribly complex: it just requires recognizing timers supporting channels and turning them on/off around blocking operations on those channels. See https://go.dev/cl/512355. Also, we have never held Go back before for secondary implementations to catch up, and I am reluctant to start doing that now.
In the second comment, I am not sure what "this" is in "basically have to do this".
bcmills commentedon Jul 24, 2023
This proposal. That is: since the mainline implementation no longer requires a
Stop
, we need to require secondary implementations to provide the same behavior in order to avoid arguments about whether a givenStop
is necessary (for portability) or redundant in code outside of the standard library.rsc commentedon Jul 24, 2023
I see. I completely agree that if the CL is submitted then we have to accept the proposal. This proposal is really "should we submit the CL?"
rsc commentedon Jul 26, 2023
This proposal has been added to the active column of the proposals project
and will now be reviewed at the weekly proposal review meetings.
— rsc for the proposal review group
ChrisHines commentedon Aug 1, 2023
@rsc If I understand the implementation correctly a tight for-select loop including a timer channel will cause frequent resorting of timer heaps. Is that a performance concern? Has it been measured?
20 remaining items
aykevl commentedon Sep 2, 2023
Finally got to this issue (holiday and all that).
So right now we use the standard library time package and implement the runtime side of it in our custom runtime. That implementation isn't great: it basically calls the provided function from inside the scheduler so any blocking call is probably going to mess things up. Consider it a work in progress.
Regarding the actual issue: it appears to require a bunch of special casing for timer channels. Maybe we'll implement that some day, but I can't promise that because it grows the runtime size even when timers aren't used and I very much would like to keep the runtime size as low as reasonably possible (while implementing the Go spec, of course).
(At the same time, the current design of the TinyGo runtime should garbage collect deadlocked goroutines as a side effect of how the scheduler works. I didn't actually test this though. AFAIK the main Go runtime doesn't do this. Just to show a different design results in different things being trivial or difficult).
time: garbage collect unstopped Tickers and Timers