This library provides implementations of Mutex
, RwLock
, Condvar
and
Once
that are smaller, faster and more flexible than those in the Rust
standard library. It also exposes a low-level API for creating your own
efficient synchronization primitives.
The primitives provided by this library have several advantages over those in the Rust standard library:
Mutex
, Condvar
and Once
only require 1 byte of storage space, and
RwLock
only requires 1 word of storage space. On the other hand the
standard library primitives require a dynamically allocated Box
to hold
OS-specific synchronization primitives. The small size of Mutex
in
particular encourages the use of fine-grained locks to increase
parallelism.static
global variables. The standard library primitives require
dynamic initialization and thus need to be lazily initialized with
lazy_static!
.To keep these primitives small, all thread queuing and suspending
functionality is offloaded to the parking lot. The idea behind this is
based on the Webkit [WTF::ParkingLot
]
(https://webkit.org/blog/6161/locking-in-webkit/) class, which essentially
consists of a hash table mapping of lock addresses to queues of parked
(sleeping) threads. The Webkit parking lot was itself inspired by Linux
futexes, but it is more
powerful since it allows invoking callbacks while holding a queue lock.
Parking refers to suspending the thread while simultaneously enqueuing it on a queue keyed by some address. Unparking refers to dequeuing a thread from a queue keyed by some address and resuming it. The parking lot API consists of just 3 functions:
rust,ignore
unsafe fn park(key: usize,
validate: &mut FnMut() -> bool,
before_sleep: &mut FnMut(),
timeout: Option<Instant>)
-> bool
This function performs the following steps:
key
.validate
, if it returns false
, unlock the queue and return.before_sleep
.timeout
is reached.true
if we were unparked by another thread, false
otherwise.rust,ignore
unsafe fn unpark_one(key: usize,
callback: &mut FnMut(UnparkResult))
-> UnparkResult
This function will unpark a single thread from the queue associated with
key
. The callback
function is invoked while holding the queue lock but
before the thread is unparked. The UnparkResult
indicates whether the
queue was empty and, if not, whether there are still threads remaining in
the queue.
rust,ignore
unsafe fn unpark_all(key: usize) -> usize
This function will unpark all threads in the queue associated with key
. It
returns the number of threads that were unparked.
Building custom synchronization primitives is very simple since
parking_lot
takes care of all the hard parts for you. The most commmon
case for a custom primitive would be to integrate a Mutex
inside another
data type. Since a mutex only requires 2 bits, it can share space with other
data. For example, one could create an ArcMutex
type that combines the
atomic reference count and the two mutex bits in the same atomic word.
This crate currently requires a nightly Rust compiler.
Add this to your Cargo.toml
:
toml
[dependencies]
parking_lot = "0.1"
and this to your crate root:
rust
extern crate parking_lot;
Licensed under either of
at your option.
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.