feat(Slab::take): add exponential backoff when spinning
Slab::take will spin if there are currently outstanding references to
the taken slot. Spinning is not the ideal way to block a thread until
a state changes, since it uses CPU time that might be used by other
threads, and impacts power consumption. However, waiting "properly"
(e.g. with a condvar) here is difficult. If we wanted to give each slot
a Mutex and Condvar pair, we would need two heap allocations per
slot (assuming POSIX pthread_mutex and pthread_cond), plus 16 bytes
for the Rust std::sync::Mutex and std::sync::Condvar types. This is
a lot of additional per-slot overhead.
As a compromise, this commit updates take to spin with an exponential
backoff. Now, every time take spins, it will issue an exponentially
increasing number of std::sync::atomic::spin_loop_hint calls (on x86,
probably a pause instruction), up to 256. Once the backoff has
increased to 256, it will also explicitly yield to the scheduler.
Although this is not as good as actually parking the thread until the
state changes, it doesn't require the allocations of the mutex/condvar
pair, and should make the spin loop a little less hot.
docs(Slab::take): document that take spins
This commit updates the API docs for Slab::take to note explicitly
that the calling thread is blocked by spinning until outstanding
references to the removed slot are dropped. The docs now warn against
using take if references to a slot may last for a long time, and note
that it should only be used when such references are relatively
short-lived.
feat(Slab::take): add exponential backoff when spinning
Slab::take
will spin if there are currently outstanding references to thetake
n slot. Spinning is not the ideal way to block a thread until a state changes, since it uses CPU time that might be used by other threads, and impacts power consumption. However, waiting "properly" (e.g. with a condvar) here is difficult. If we wanted to give each slot aMutex
andCondvar
pair, we would need two heap allocations per slot (assuming POSIXpthread_mutex
andpthread_cond
), plus 16 bytes for the Ruststd::sync::Mutex
andstd::sync::Condvar
types. This is a lot of additional per-slot overhead.As a compromise, this commit updates
take
to spin with an exponential backoff. Now, every timetake
spins, it will issue an exponentially increasing number ofstd::sync::atomic::spin_loop_hint
calls (on x86, probably apause
instruction), up to 256. Once the backoff has increased to 256, it will also explicitly yield to the scheduler. Although this is not as good as actually parking the thread until the state changes, it doesn't require the allocations of the mutex/condvar pair, and should make the spin loop a little less hot.docs(Slab::take): document that
take
spinsThis commit updates the API docs for
Slab::take
to note explicitly that the calling thread is blocked by spinning until outstanding references to the removed slot are dropped. The docs now warn against usingtake
if references to a slot may last for a long time, and note that it should only be used when such references are relatively short-lived.Fixes #20