Skip to content

Commit ccb7d35

Browse files
committed
docs: further revision of write_volatile
1 parent 5a52202 commit ccb7d35

File tree

1 file changed

+43
-52
lines changed

1 file changed

+43
-52
lines changed

library/core/src/ptr/mod.rs

Lines changed: 43 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1839,72 +1839,63 @@ pub unsafe fn read_volatile<T>(src: *const T) -> T {
18391839
}
18401840
}
18411841

1842-
/// Performs a volatile write of a memory location with the given value without
1843-
/// reading or dropping the old value.
1844-
///
1845-
/// Rust does not currently have a rigorously and formally defined memory model,
1846-
/// so the precise semantics of what "volatile" means here is subject to change
1847-
/// over time. That being said, the semantics will almost always end up pretty
1848-
/// similar to [C11's definition of volatile][c11].
1849-
///
1850-
/// Volatile operations are intended to act on I/O memory, and are guaranteed
1851-
/// to not be elided or reordered by the compiler across other volatile
1852-
/// operations. With this in mind, there are two cases of usage that need to
1853-
/// be distinguished:
1854-
///
1855-
/// - When a volatile operation is used for memory inside an [allocation], all
1856-
/// the typical restrictions of Rust-allocated memory apply, meaning things
1857-
/// like data races and mutable aliasing remain as undefined behavior. In
1858-
/// addition, the volatile rule that the operation won't be elided or
1859-
/// reordered applies, such that the operation will access memory and not e.g.
1860-
/// be lowered to a register access or stack pop. The memory in `src` should
1861-
/// remain unchanged. Just like in C, whether an operation is volatile has no
1862-
/// bearing whatsoever on questions involving concurrent access from multiple
1863-
/// threads. Volatile accesses behave exactly like non-atomic accesses in that
1864-
/// regard. All this is because this kind of target-memory may be used from
1865-
/// safe code at any time, and its validity assumptions must not be violated.
1866-
///
1867-
/// - Volatile operations, however, provide a conditionally valid way to access
1868-
/// memory that is _outside_ of any allocation. The main use-case is CPU and
1869-
/// peripheral registers that must be accessed via an I/O memory mapping, most
1870-
/// commonly at fixed addresses reserved by the hardware. These often have
1871-
/// special semantics associated to their manipulation, and cannot be used as
1872-
/// general purpose memory. Here, any address value is possible, from 0 to
1873-
/// [`usize::MAX`], so long as its semantics are well-defined by the target
1874-
/// hardware. The access is restricted to not trap/interrupt. It can (and
1875-
/// usually will) cause side-effects, but note they shouldn't affect
1876-
/// Rust-allocated memory in any way.
1877-
///
1878-
/// The compiler shouldn't change the relative order or number of volatile
1879-
/// memory operations. However, volatile memory operations on zero-sized types
1880-
/// (e.g., if a zero-sized type is passed to `write_volatile`) are noops
1881-
/// and may be ignored.
1882-
///
1883-
/// `write_volatile` does not drop the contents of `dst`. This is safe, but it
1884-
/// could leak allocations or resources, so care should be taken not to
1885-
/// overwrite an object that should be dropped when operating on Rust memory.
1842+
/// Performs a volatile write of a memory location with the given value without reading or dropping
1843+
/// the old value.
18861844
///
1887-
/// Additionally, it does not drop `src`. Semantically, `src` is moved into the
1888-
/// location pointed to by `dst`.
1845+
/// Rust does not currently have a rigorously and formally defined memory model, so the precise
1846+
/// semantics of what "volatile" means here is subject to change over time. That being said, the
1847+
/// semantics will almost always end up pretty similar to [C11's definition of volatile][c11].
1848+
///
1849+
/// Volatile operations are intended to act on I/O memory. As such, they are considered externally
1850+
/// observable events (just like syscalls), and are guaranteed to not be elided or reordered by the
1851+
/// compiler across other externally observable events. With this in mind, there are two cases of
1852+
/// usage that need to be distinguished:
1853+
///
1854+
/// - When a volatile operation is used for memory inside an [allocation], it behaves exactly like
1855+
/// [`write`], except for the additional guarantee that it won't be elided or reordered (see
1856+
/// above). This implies that the operation will actually access memory and not e.g. be lowered to
1857+
/// a register access or stack pop. Other than that, all the usual rules for memory accesses
1858+
/// apply. In particular, just like in C, whether an operation is volatile has no bearing
1859+
/// whatsoever on questions involving concurrent access from multiple threads. Volatile accesses
1860+
/// behave exactly like non-atomic accesses in that regard.
1861+
///
1862+
/// - Volatile operations, however, may also be used access memory that is _outside_ of any Rust
1863+
/// allocation. The main use-case is CPU and peripheral registers that must be accessed via an I/O
1864+
/// memory mapping, most commonly at fixed addresses reserved by the hardware. These often have
1865+
/// special semantics associated to their manipulation, and cannot be used as general purpose
1866+
/// memory. Here, any address value is possible, including 0 and [`usize::MAX`], so long as the
1867+
/// semantics of such a write are well-defined by the target hardware. The access must not trap.
1868+
/// It can (and usually will) cause side-effects, but those must not affect Rust-allocated memory
1869+
/// in any way. In this use-case, the pointer validity rules in the [module
1870+
/// documentation][mod-docs] may not apply.
1871+
///
1872+
/// Note that volatile memory operations on zero-sized types (e.g., if a zero-sized type is passed
1873+
/// to `write_volatile`) are noops and may be ignored.
1874+
///
1875+
/// `write_volatile` does not drop the contents of `dst`. This is safe, but it could leak
1876+
/// allocations or resources, so care should be taken not to overwrite an object that should be
1877+
/// dropped when operating on Rust memory.
1878+
///
1879+
/// Additionally, it does not drop `src`. Semantically, `src` is moved into the location pointed to
1880+
/// by `dst`.
18891881
///
18901882
/// [c11]: http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf
18911883
/// [allocation]: crate::ptr#allocated-object
1884+
/// [mod-docs]: crate::ptr
18921885
///
18931886
/// # Safety
18941887
///
18951888
/// Behavior is undefined if any of the following conditions are violated:
18961889
///
1897-
/// * `dst` must be writable without trapping.
1890+
/// * `dst` must be either [valid] for writes, or it must point to memory outside of all Rust
1891+
/// allocations and to that memory must:
1892+
/// - not trap, and
1893+
/// - not cause any memory inside a Rust allocation to be modified.
18981894
///
18991895
/// * `dst` must be properly aligned.
19001896
///
19011897
/// * `src` must be a properly initialized value of type `T`.
19021898
///
1903-
/// * If operating on an allocation, no Rust memory outside of `dst` may be
1904-
/// modified.
1905-
///
1906-
/// * If not operating on an allocation, no Rust memory may be affected.
1907-
///
19081899
/// Note that even if `T` has size `0`, the pointer must be properly aligned.
19091900
///
19101901
/// # Examples

0 commit comments

Comments
 (0)