Description
tl;dr: Timer#unref()
is not very efficient, and is also confusing. This should be fixable.
Timers use pooled handles behind the hood in all normal cases. There is one exception: if a timer has been unreferenced by using Timer#unref()
. (But not _unrefActive
, which pools the timer but also resets it.)
#8372 Is my pervious attempt at optimizing it. As noted in that PR, that method is very odd and IIRC probably also has some behvaior weirdness in slightly-edge-cases.
A better solution is outlined by @misterdjules in that issue. I am describing it below in clearer detail so that other may better understand it. It assumes basic knowledge of our timers algorithm/implementation, most of which can be found in the extensive comments near the top of lib/timers.js
.
From #8372 (comment):
I wanted to experiment with another approach to making unrefed timers use
TimerList
instances, but without the inconsistency of having some of them not useTimersList
.
Each TimersList
could keep a counting tracker of how many unrefed timers it contains. This would be incremented / decremented whenever Timeout#unref()
/ Timeout#ref()
are called, and also when unref timers call their callbacks. Whenever this counter is adjusted the implementation would check if there are unrefed timers, and then either unref or ref the TimerWrap
handle accordingly.
Additionally, it could also cover _unrefActive
, and pool our internal unreferenced timers without needed separate pools, even for the same duration.
The logic behind this revolves around the fact that unrefed handles don't matter if there are still refed handles.
Also, this means we wouldn't need separate _handle
properties on unreferenced timers, which is honestly just a confusing behavior as it stands today.
The major consideration here is whether unrefing and re-refing a libuv handle has much overhead. My recollection was that it did not have significant overhead (from @trevnorris, I think), but that would need to be investigated alongside this. (See below.) Keep in mind timers is potentially hot code for any net request and does actually need to be quite efficient.