In cases like #308, there can be a desire to ensure that a recorder flushes itself on process exit. As we install the recorder globally, such that it is leaked to provide the ability to acquire a static reference to it, recorders cannot normally participate in the typical Drop logic as objects are cleaned up before process exit.
Solution
This PR introduces a generic helper -- RecoverableRecorder<R> -- that allows for installing a wrapped version of the recorder, and acts as a drop guard to allow either manually "recovering" the recorder, or recovering it on drop such that the recorder's drop logic can also be called.
We wrap the recorder in an Arc<T>, and install a custom recorder wrapper that holds a Weak<T> reference. This allows the global recorder to provide access to the inner recorder without taking ownership of it.
When the caller manually seeks to recover the recorder, they call RecoverableRecorder::into_inner which attempts to unwrap the Arc<T> until there are no active references other than from the drop guard, which gives back the original recorder. If the drop guard is simply dropped, the original recorder too is dropped as soon as all active references to it have dropped, following the normal behavior of Arc<T>.
This does come with a small performance overhead, as every call through the weakly-held Recorder implementation must clone the Arc<T> before proxying the given method call, but it should be fairly minimal in practice. If performance is a huge concern, a recorder should implement its own drop guard for controlling flushing on process exit, but this is simply meant to be a generic solution when the application has no control over the recorder implementation.
Additionally, I've stripped out all of the atomic_cas and set_recorder_racy bits, as all illusions of metrics being no_std/embedded-compatible have sort of faded at this point, and it was just an opportune time to strip out the last vestiges.
Context
In cases like #308, there can be a desire to ensure that a recorder flushes itself on process exit. As we install the recorder globally, such that it is leaked to provide the ability to acquire a static reference to it, recorders cannot normally participate in the typical
Drop
logic as objects are cleaned up before process exit.Solution
This PR introduces a generic helper --
RecoverableRecorder<R>
-- that allows for installing a wrapped version of the recorder, and acts as a drop guard to allow either manually "recovering" the recorder, or recovering it on drop such that the recorder's drop logic can also be called.We wrap the recorder in an
Arc<T>
, and install a custom recorder wrapper that holds aWeak<T>
reference. This allows the global recorder to provide access to the inner recorder without taking ownership of it.When the caller manually seeks to recover the recorder, they call
RecoverableRecorder::into_inner
which attempts to unwrap theArc<T>
until there are no active references other than from the drop guard, which gives back the original recorder. If the drop guard is simply dropped, the original recorder too is dropped as soon as all active references to it have dropped, following the normal behavior ofArc<T>
.This does come with a small performance overhead, as every call through the weakly-held
Recorder
implementation must clone theArc<T>
before proxying the given method call, but it should be fairly minimal in practice. If performance is a huge concern, a recorder should implement its own drop guard for controlling flushing on process exit, but this is simply meant to be a generic solution when the application has no control over the recorder implementation.Additionally, I've stripped out all of the
atomic_cas
andset_recorder_racy
bits, as all illusions ofmetrics
beingno_std
/embedded-compatible have sort of faded at this point, and it was just an opportune time to strip out the last vestiges.