2016-03-04 22:37:11 +00:00
|
|
|
//! Useful synchronization primitives.
|
2013-12-13 01:27:37 +00:00
|
|
|
//!
|
2018-09-09 11:22:58 +00:00
|
|
|
//! ## The need for synchronization
|
|
|
|
//!
|
2018-10-05 05:50:17 +00:00
|
|
|
//! Conceptually, a Rust program is a series of operations which will
|
|
|
|
//! be executed on a computer. The timeline of events happening in the
|
|
|
|
//! program is consistent with the order of the operations in the code.
|
2018-09-09 11:22:58 +00:00
|
|
|
//!
|
2018-10-05 05:50:17 +00:00
|
|
|
//! Consider the following code, operating on some global static variables:
|
2018-09-09 11:22:58 +00:00
|
|
|
//!
|
|
|
|
//! ```rust
|
2018-09-16 09:56:44 +00:00
|
|
|
//! static mut A: u32 = 0;
|
|
|
|
//! static mut B: u32 = 0;
|
|
|
|
//! static mut C: u32 = 0;
|
|
|
|
//!
|
|
|
|
//! fn main() {
|
|
|
|
//! unsafe {
|
|
|
|
//! A = 3;
|
|
|
|
//! B = 4;
|
|
|
|
//! A = A + B;
|
|
|
|
//! C = B;
|
|
|
|
//! println!("{} {} {}", A, B, C);
|
|
|
|
//! C = A;
|
|
|
|
//! }
|
|
|
|
//! }
|
2018-09-09 11:22:58 +00:00
|
|
|
//! ```
|
|
|
|
//!
|
2018-10-05 05:50:17 +00:00
|
|
|
//! It appears as if some variables stored in memory are changed, an addition
|
|
|
|
//! is performed, result is stored in `A` and the variable `C` is
|
|
|
|
//! modified twice.
|
|
|
|
//!
|
2018-09-09 11:22:58 +00:00
|
|
|
//! When only a single thread is involved, the results are as expected:
|
|
|
|
//! the line `7 4 4` gets printed.
|
|
|
|
//!
|
2018-09-27 17:25:04 +00:00
|
|
|
//! As for what happens behind the scenes, when optimizations are enabled the
|
|
|
|
//! final generated machine code might look very different from the code:
|
2018-09-09 11:22:58 +00:00
|
|
|
//!
|
2018-09-27 17:25:04 +00:00
|
|
|
//! - The first store to `C` might be moved before the store to `A` or `B`,
|
|
|
|
//! _as if_ we had written `C = 4; A = 3; B = 4`.
|
2018-09-09 11:22:58 +00:00
|
|
|
//!
|
2018-09-27 17:25:04 +00:00
|
|
|
//! - Assignment of `A + B` to `A` might be removed, since the sum can be stored
|
|
|
|
//! in a temporary location until it gets printed, with the global variable
|
|
|
|
//! never getting updated.
|
2018-09-09 11:22:58 +00:00
|
|
|
//!
|
2018-10-05 05:50:17 +00:00
|
|
|
//! - The final result could be determined just by looking at the code
|
|
|
|
//! at compile time, so [constant folding] might turn the whole
|
|
|
|
//! block into a simple `println!("7 4 4")`.
|
2018-09-09 11:22:58 +00:00
|
|
|
//!
|
2018-10-05 05:50:17 +00:00
|
|
|
//! The compiler is allowed to perform any combination of these
|
|
|
|
//! optimizations, as long as the final optimized code, when executed,
|
|
|
|
//! produces the same results as the one without optimizations.
|
2018-09-09 11:22:58 +00:00
|
|
|
//!
|
2018-10-05 05:50:17 +00:00
|
|
|
//! Due to the [concurrency] involved in modern computers, assumptions
|
|
|
|
//! about the program's execution order are often wrong. Access to
|
|
|
|
//! global variables can lead to nondeterministic results, **even if**
|
|
|
|
//! compiler optimizations are disabled, and it is **still possible**
|
|
|
|
//! to introduce synchronization bugs.
|
2018-09-09 11:22:58 +00:00
|
|
|
//!
|
|
|
|
//! Note that thanks to Rust's safety guarantees, accessing global (static)
|
|
|
|
//! variables requires `unsafe` code, assuming we don't use any of the
|
|
|
|
//! synchronization primitives in this module.
|
|
|
|
//!
|
|
|
|
//! [constant folding]: https://en.wikipedia.org/wiki/Constant_folding
|
2018-09-28 07:59:45 +00:00
|
|
|
//! [concurrency]: https://en.wikipedia.org/wiki/Concurrency_(computer_science)
|
2018-09-09 11:22:58 +00:00
|
|
|
//!
|
|
|
|
//! ## Out-of-order execution
|
|
|
|
//!
|
|
|
|
//! Instructions can execute in a different order from the one we define, due to
|
|
|
|
//! various reasons:
|
|
|
|
//!
|
2018-10-05 05:50:17 +00:00
|
|
|
//! - The **compiler** reordering instructions: If the compiler can issue an
|
2018-09-09 11:22:58 +00:00
|
|
|
//! instruction at an earlier point, it will try to do so. For example, it
|
|
|
|
//! might hoist memory loads at the top of a code block, so that the CPU can
|
|
|
|
//! start [prefetching] the values from memory.
|
|
|
|
//!
|
2018-09-27 17:25:04 +00:00
|
|
|
//! In single-threaded scenarios, this can cause issues when writing
|
|
|
|
//! signal handlers or certain kinds of low-level code.
|
2018-09-09 11:22:58 +00:00
|
|
|
//! Use [compiler fences] to prevent this reordering.
|
|
|
|
//!
|
2018-10-05 05:50:17 +00:00
|
|
|
//! - A **single processor** executing instructions [out-of-order]:
|
|
|
|
//! Modern CPUs are capable of [superscalar] execution,
|
2018-11-27 02:59:49 +00:00
|
|
|
//! i.e., multiple instructions might be executing at the same time,
|
2018-10-05 05:50:17 +00:00
|
|
|
//! even though the machine code describes a sequential process.
|
2018-09-09 11:22:58 +00:00
|
|
|
//!
|
|
|
|
//! This kind of reordering is handled transparently by the CPU.
|
|
|
|
//!
|
2018-10-05 05:50:17 +00:00
|
|
|
//! - A **multiprocessor** system executing multiple hardware threads
|
|
|
|
//! at the same time: In multi-threaded scenarios, you can use two
|
|
|
|
//! kinds of primitives to deal with synchronization:
|
2018-10-22 16:21:55 +00:00
|
|
|
//! - [memory fences] to ensure memory accesses are made visible to
|
2018-10-05 05:50:17 +00:00
|
|
|
//! other CPUs in the right order.
|
|
|
|
//! - [atomic operations] to ensure simultaneous access to the same
|
|
|
|
//! memory location doesn't lead to undefined behavior.
|
2018-09-09 11:22:58 +00:00
|
|
|
//!
|
|
|
|
//! [prefetching]: https://en.wikipedia.org/wiki/Cache_prefetching
|
2018-09-27 19:12:09 +00:00
|
|
|
//! [compiler fences]: crate::sync::atomic::compiler_fence
|
2018-09-09 11:22:58 +00:00
|
|
|
//! [out-of-order]: https://en.wikipedia.org/wiki/Out-of-order_execution
|
|
|
|
//! [superscalar]: https://en.wikipedia.org/wiki/Superscalar_processor
|
2018-09-27 19:12:09 +00:00
|
|
|
//! [memory fences]: crate::sync::atomic::fence
|
|
|
|
//! [atomic operations]: crate::sync::atomic
|
2018-09-09 11:22:58 +00:00
|
|
|
//!
|
|
|
|
//! ## Higher-level synchronization objects
|
|
|
|
//!
|
|
|
|
//! Most of the low-level synchronization primitives are quite error-prone and
|
|
|
|
//! inconvenient to use, which is why the standard library also exposes some
|
|
|
|
//! higher-level synchronization objects.
|
|
|
|
//!
|
2018-10-05 05:50:17 +00:00
|
|
|
//! These abstractions can be built out of lower-level primitives.
|
|
|
|
//! For efficiency, the sync objects in the standard library are usually
|
|
|
|
//! implemented with help from the operating system's kernel, which is
|
|
|
|
//! able to reschedule the threads while they are blocked on acquiring
|
|
|
|
//! a lock.
|
|
|
|
//!
|
|
|
|
//! The following is an overview of the available synchronization
|
|
|
|
//! objects:
|
|
|
|
//!
|
|
|
|
//! - [`Arc`]: Atomically Reference-Counted pointer, which can be used
|
|
|
|
//! in multithreaded environments to prolong the lifetime of some
|
|
|
|
//! data until all the threads have finished using it.
|
|
|
|
//!
|
|
|
|
//! - [`Barrier`]: Ensures multiple threads will wait for each other
|
|
|
|
//! to reach a point in the program, before continuing execution all
|
|
|
|
//! together.
|
|
|
|
//!
|
|
|
|
//! - [`Condvar`]: Condition Variable, providing the ability to block
|
|
|
|
//! a thread while waiting for an event to occur.
|
2018-09-09 11:22:58 +00:00
|
|
|
//!
|
2018-10-05 05:50:17 +00:00
|
|
|
//! - [`mpsc`]: Multi-producer, single-consumer queues, used for
|
|
|
|
//! message-based communication. Can provide a lightweight
|
|
|
|
//! inter-thread synchronisation mechanism, at the cost of some
|
|
|
|
//! extra memory.
|
2018-09-09 11:22:58 +00:00
|
|
|
//!
|
2018-10-05 05:50:17 +00:00
|
|
|
//! - [`Mutex`]: Mutual Exclusion mechanism, which ensures that at
|
|
|
|
//! most one thread at a time is able to access some data.
|
2018-09-09 11:22:58 +00:00
|
|
|
//!
|
2018-10-05 05:50:17 +00:00
|
|
|
//! - [`Once`]: Used for thread-safe, one-time initialization of a
|
|
|
|
//! global variable.
|
2018-09-09 11:22:58 +00:00
|
|
|
//!
|
2018-10-05 05:50:17 +00:00
|
|
|
//! - [`RwLock`]: Provides a mutual exclusion mechanism which allows
|
|
|
|
//! multiple readers at the same time, while allowing only one
|
|
|
|
//! writer at a time. In some cases, this can be more efficient than
|
|
|
|
//! a mutex.
|
2018-09-09 11:22:58 +00:00
|
|
|
//!
|
2018-10-05 05:50:17 +00:00
|
|
|
//! [`Arc`]: crate::sync::Arc
|
|
|
|
//! [`Barrier`]: crate::sync::Barrier
|
|
|
|
//! [`Condvar`]: crate::sync::Condvar
|
|
|
|
//! [`mpsc`]: crate::sync::mpsc
|
2018-09-27 19:12:09 +00:00
|
|
|
//! [`Mutex`]: crate::sync::Mutex
|
2018-10-05 05:50:17 +00:00
|
|
|
//! [`Once`]: crate::sync::Once
|
2018-09-27 19:12:09 +00:00
|
|
|
//! [`RwLock`]: crate::sync::RwLock
|
2013-12-13 01:27:37 +00:00
|
|
|
|
2015-01-24 05:48:20 +00:00
|
|
|
#![stable(feature = "rust1", since = "1.0.0")]
|
2014-07-01 00:22:40 +00:00
|
|
|
|
2015-11-16 16:54:28 +00:00
|
|
|
#[stable(feature = "rust1", since = "1.0.0")]
|
2018-06-15 02:07:09 +00:00
|
|
|
pub use alloc_crate::sync::{Arc, Weak};
|
2015-11-16 16:54:28 +00:00
|
|
|
#[stable(feature = "rust1", since = "1.0.0")]
|
2015-08-17 20:56:55 +00:00
|
|
|
pub use core::sync::atomic;
|
2014-08-04 22:42:36 +00:00
|
|
|
|
2015-11-16 16:54:28 +00:00
|
|
|
#[stable(feature = "rust1", since = "1.0.0")]
|
2015-04-03 21:46:54 +00:00
|
|
|
pub use self::barrier::{Barrier, BarrierWaitResult};
|
2015-11-16 16:54:28 +00:00
|
|
|
#[stable(feature = "rust1", since = "1.0.0")]
|
2016-07-07 18:46:09 +00:00
|
|
|
pub use self::condvar::{Condvar, WaitTimeoutResult};
|
2015-11-16 16:54:28 +00:00
|
|
|
#[stable(feature = "rust1", since = "1.0.0")]
|
2016-07-07 18:46:09 +00:00
|
|
|
pub use self::mutex::{Mutex, MutexGuard};
|
2015-11-16 16:54:28 +00:00
|
|
|
#[stable(feature = "rust1", since = "1.0.0")]
|
2019-07-17 08:34:34 +00:00
|
|
|
#[allow(deprecated)]
|
2016-05-11 19:30:20 +00:00
|
|
|
pub use self::once::{Once, OnceState, ONCE_INIT};
|
2015-11-16 16:54:28 +00:00
|
|
|
#[stable(feature = "rust1", since = "1.0.0")]
|
2021-04-21 08:05:11 +00:00
|
|
|
pub use self::poison::{LockResult, PoisonError, TryLockError, TryLockResult};
|
2019-12-22 22:42:04 +00:00
|
|
|
#[stable(feature = "rust1", since = "1.0.0")]
|
2021-04-21 08:05:11 +00:00
|
|
|
pub use self::rwlock::{RwLock, RwLockReadGuard, RwLockWriteGuard};
|
2014-06-07 18:13:26 +00:00
|
|
|
|
2014-12-23 19:53:35 +00:00
|
|
|
pub mod mpsc;
|
|
|
|
|
std: Rewrite the `sync` module
This commit is a reimplementation of `std::sync` to be based on the
system-provided primitives wherever possible. The previous implementation was
fundamentally built on top of channels, and as part of the runtime reform it has
become clear that this is not the level of abstraction that the standard level
should be providing. This rewrite aims to provide as thin of a shim as possible
on top of the system primitives in order to make them safe.
The overall interface of the `std::sync` module has in general not changed, but
there are a few important distinctions, highlighted below:
* The condition variable type, `Condvar`, has been separated out of a `Mutex`.
A condition variable is now an entirely separate type. This separation
benefits users who only use one mutex, and provides a clearer distinction of
who's responsible for managing condition variables (the application).
* All of `Condvar`, `Mutex`, and `RWLock` are now directly built on top of
system primitives rather than using a custom implementation. The `Once`,
`Barrier`, and `Semaphore` types are still built upon these abstractions of
the system primitives.
* The `Condvar`, `Mutex`, and `RWLock` types all have a new static type and
constant initializer corresponding to them. These are provided primarily for C
FFI interoperation, but are often useful to otherwise simply have a global
lock. The types, however, will leak memory unless `destroy()` is called on
them, which is clearly documented.
* The `Condvar` implementation for an `RWLock` write lock has been removed. This
may be added back in the future with a userspace implementation, but this
commit is focused on exposing the system primitives first.
* The fundamental architecture of this design is to provide two separate layers.
The first layer is that exposed by `sys_common` which is a cross-platform
bare-metal abstraction of the system synchronization primitives. No attempt is
made at making this layer safe, and it is quite unsafe to use! It is currently
not exported as part of the API of the standard library, but the stabilization
of the `sys` module will ensure that these will be exposed in time. The
purpose of this layer is to provide the core cross-platform abstractions if
necessary to implementors.
The second layer is the layer provided by `std::sync` which is intended to be
the thinnest possible layer on top of `sys_common` which is entirely safe to
use. There are a few concerns which need to be addressed when making these
system primitives safe:
* Once used, the OS primitives can never be **moved**. This means that they
essentially need to have a stable address. The static primitives use
`&'static self` to enforce this, and the non-static primitives all use a
`Box` to provide this guarantee.
* Poisoning is leveraged to ensure that invalid data is not accessible from
other tasks after one has panicked.
In addition to these overall blanket safety limitations, each primitive has a
few restrictions of its own:
* Mutexes and rwlocks can only be unlocked from the same thread that they
were locked by. This is achieved through RAII lock guards which cannot be
sent across threads.
* Mutexes and rwlocks can only be unlocked if they were previously locked.
This is achieved by not exposing an unlocking method.
* A condition variable can only be waited on with a locked mutex. This is
achieved by requiring a `MutexGuard` in the `wait()` method.
* A condition variable cannot be used concurrently with more than one mutex.
This is guaranteed by dynamically binding a condition variable to
precisely one mutex for its entire lifecycle. This restriction may be able
to be relaxed in the future (a mutex is unbound when no threads are
waiting on the condvar), but for now it is sufficient to guarantee safety.
* Condvars now support timeouts for their blocking operations. The
implementation for these operations is provided by the system.
Due to the modification of the `Condvar` API, removal of the `std::sync::mutex`
API, and reimplementation, this is a breaking change. Most code should be fairly
easy to port using the examples in the documentation of these primitives.
[breaking-change]
Closes #17094
Closes #18003
2014-11-24 19:16:40 +00:00
|
|
|
mod barrier;
|
|
|
|
mod condvar;
|
|
|
|
mod mutex;
|
|
|
|
mod once;
|
2021-04-21 08:05:11 +00:00
|
|
|
mod poison;
|
std: Rewrite the `sync` module
This commit is a reimplementation of `std::sync` to be based on the
system-provided primitives wherever possible. The previous implementation was
fundamentally built on top of channels, and as part of the runtime reform it has
become clear that this is not the level of abstraction that the standard level
should be providing. This rewrite aims to provide as thin of a shim as possible
on top of the system primitives in order to make them safe.
The overall interface of the `std::sync` module has in general not changed, but
there are a few important distinctions, highlighted below:
* The condition variable type, `Condvar`, has been separated out of a `Mutex`.
A condition variable is now an entirely separate type. This separation
benefits users who only use one mutex, and provides a clearer distinction of
who's responsible for managing condition variables (the application).
* All of `Condvar`, `Mutex`, and `RWLock` are now directly built on top of
system primitives rather than using a custom implementation. The `Once`,
`Barrier`, and `Semaphore` types are still built upon these abstractions of
the system primitives.
* The `Condvar`, `Mutex`, and `RWLock` types all have a new static type and
constant initializer corresponding to them. These are provided primarily for C
FFI interoperation, but are often useful to otherwise simply have a global
lock. The types, however, will leak memory unless `destroy()` is called on
them, which is clearly documented.
* The `Condvar` implementation for an `RWLock` write lock has been removed. This
may be added back in the future with a userspace implementation, but this
commit is focused on exposing the system primitives first.
* The fundamental architecture of this design is to provide two separate layers.
The first layer is that exposed by `sys_common` which is a cross-platform
bare-metal abstraction of the system synchronization primitives. No attempt is
made at making this layer safe, and it is quite unsafe to use! It is currently
not exported as part of the API of the standard library, but the stabilization
of the `sys` module will ensure that these will be exposed in time. The
purpose of this layer is to provide the core cross-platform abstractions if
necessary to implementors.
The second layer is the layer provided by `std::sync` which is intended to be
the thinnest possible layer on top of `sys_common` which is entirely safe to
use. There are a few concerns which need to be addressed when making these
system primitives safe:
* Once used, the OS primitives can never be **moved**. This means that they
essentially need to have a stable address. The static primitives use
`&'static self` to enforce this, and the non-static primitives all use a
`Box` to provide this guarantee.
* Poisoning is leveraged to ensure that invalid data is not accessible from
other tasks after one has panicked.
In addition to these overall blanket safety limitations, each primitive has a
few restrictions of its own:
* Mutexes and rwlocks can only be unlocked from the same thread that they
were locked by. This is achieved through RAII lock guards which cannot be
sent across threads.
* Mutexes and rwlocks can only be unlocked if they were previously locked.
This is achieved by not exposing an unlocking method.
* A condition variable can only be waited on with a locked mutex. This is
achieved by requiring a `MutexGuard` in the `wait()` method.
* A condition variable cannot be used concurrently with more than one mutex.
This is guaranteed by dynamically binding a condition variable to
precisely one mutex for its entire lifecycle. This restriction may be able
to be relaxed in the future (a mutex is unbound when no threads are
waiting on the condvar), but for now it is sufficient to guarantee safety.
* Condvars now support timeouts for their blocking operations. The
implementation for these operations is provided by the system.
Due to the modification of the `Condvar` API, removal of the `std::sync::mutex`
API, and reimplementation, this is a breaking change. Most code should be fairly
easy to port using the examples in the documentation of these primitives.
[breaking-change]
Closes #17094
Closes #18003
2014-11-24 19:16:40 +00:00
|
|
|
mod rwlock;
|