116
116
//! fully contiguous (i.e., has no "holes"), there is no guarantee that this
117
117
//! will not change in the future.
118
118
//!
119
+ //! Allocated objects must behave like "normal" memory: in particular, reads must not have
120
+ //! side-effects, and writes must become visible to other threads using the usual synchronization
121
+ //! primitives.
122
+ //!
119
123
//! For any allocated object with `base` address, `size`, and a set of
120
124
//! `addresses`, the following are guaranteed:
121
125
//! - For all addresses `a` in `addresses`, `a` is in the range `base .. (base +
@@ -1755,38 +1759,30 @@ pub const unsafe fn write_unaligned<T>(dst: *mut T, src: T) {
1755
1759
/// over time. That being said, the semantics will almost always end up pretty
1756
1760
/// similar to [C11's definition of volatile][c11].
1757
1761
///
1758
- /// Volatile operations are intended to act on I/O memory, and are guaranteed
1759
- /// to not be elided or reordered by the compiler across other volatile
1760
- /// operations. With this in mind, there are two cases of usage that need to
1761
- /// be distinguished:
1762
- ///
1763
- /// - When a volatile operation is used for memory inside an [allocation], all
1764
- /// the typical restrictions of Rust-allocated memory apply, meaning things
1765
- /// like data races and mutable aliasing remain as undefined behavior. In
1766
- /// addition, the volatile rule that the operation won't be elided or
1767
- /// reordered applies, such that the operation will access memory and not e.g.
1768
- /// be lowered to a register access or stack pop. The memory in `src` should
1769
- /// remain unchanged. Just like in C, whether an operation is volatile has no
1770
- /// bearing whatsoever on questions involving concurrent access from multiple
1771
- /// threads. Volatile accesses behave exactly like non-atomic accesses in that
1772
- /// regard. All this is because this kind of target-memory may be used from
1773
- /// safe code at any time, and its validity assumptions must not be violated.
1774
- ///
1775
- /// - Volatile operations, however, provide a conditionally valid way to access
1776
- /// memory that is _outside_ of any allocation. The main use-case is CPU and
1777
- /// peripheral registers that must be accessed via an I/O memory mapping, most
1778
- /// commonly at fixed addresses reserved by the hardware. These often have
1779
- /// special semantics associated to their manipulation, and cannot be used as
1780
- /// general purpose memory. Here, any address value is possible, from 0 to
1781
- /// [`usize::MAX`], so long as its semantics are well-defined by the target
1782
- /// hardware. The access is restricted to not trap/interrupt. It can (and
1783
- /// usually will) cause side-effects, but note they shouldn't affect
1784
- /// Rust-allocated memory in any way.
1785
- ///
1786
- /// The compiler shouldn't change the relative order or number of volatile
1787
- /// memory operations. However, volatile memory operations on zero-sized types
1788
- /// (e.g., if a zero-sized type is passed to `read_volatile`) are noops
1789
- /// and may be ignored.
1762
+ /// Volatile operations are intended to act on I/O memory. As such, they are considered externally
1763
+ /// observable events (just like syscalls), and are guaranteed to not be elided or reordered by the
1764
+ /// compiler across other externally observable events. With this in mind, there are two cases of
1765
+ /// usage that need to be distinguished:
1766
+ ///
1767
+ /// - When a volatile operation is used for memory inside an [allocation], it behaves exactly like
1768
+ /// [`read`], except for the additional guarantee that it won't be elided or reordered (see
1769
+ /// above). This implies that the operation will actually access memory and not e.g. be lowered to
1770
+ /// a register access or stack pop. Other than that, all the usual rules for memory accesses
1771
+ /// apply. In particular, just like in C, whether an operation is volatile has no bearing
1772
+ /// whatsoever on questions involving concurrent access from multiple threads. Volatile accesses
1773
+ /// behave exactly like non-atomic accesses in that regard.
1774
+ ///
1775
+ /// - Volatile operations, however, may also be used access memory that is _outside_ of any Rust
1776
+ /// allocation. The main use-case is CPU and peripheral registers that must be accessed via an I/O
1777
+ /// memory mapping, most commonly at fixed addresses reserved by the hardware. These often have
1778
+ /// special semantics associated to their manipulation, and cannot be used as general purpose
1779
+ /// memory. Here, any address value is possible, including 0 and [`usize::MAX`], so long as the
1780
+ /// semantics of such a read are well-defined by the target hardware. The access must not trap. It
1781
+ /// can (and usually will) cause side-effects, but those must not affect Rust-allocated memory in
1782
+ /// any way.
1783
+ ///
1784
+ /// Note that volatile memory operations on zero-sized types (e.g., if a zero-sized type is passed
1785
+ /// to `read_volatile`) are noops and may be ignored.
1790
1786
///
1791
1787
/// [c11]: http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf
1792
1788
/// [allocation]: crate::ptr#allocated-object
@@ -1795,15 +1791,14 @@ pub const unsafe fn write_unaligned<T>(dst: *mut T, src: T) {
1795
1791
///
1796
1792
/// Behavior is undefined if any of the following conditions are violated:
1797
1793
///
1798
- /// * `src` must be readable without trapping.
1794
+ /// * `src` must be either [valid] for reads, or it must point to memory outside of all Rust
1795
+ /// allocations and reading from that memory must:
1796
+ /// - not trap, and
1797
+ /// - not cause any memory inside a Rust allocation to be modified.
1799
1798
///
1800
1799
/// * `src` must be properly aligned.
1801
1800
///
1802
- /// * No Rust memory may be modified.
1803
- ///
1804
- /// * If operating on an allocation, `src` must point to a properly initialized
1805
- /// value of type `T`, and the safe usage assumptions of the data must be
1806
- /// satisfied.
1801
+ /// * Reading from `src` must produce a properly initialized value of type `T`.
1807
1802
///
1808
1803
/// Like [`read`], `read_volatile` creates a bitwise copy of `T`, regardless of
1809
1804
/// whether `T` is [`Copy`]. If `T` is not [`Copy`], using both the returned
0 commit comments