28
28
//! undefined behavior to perform two concurrent accesses to the same location from different
29
29
//! threads unless both accesses only read from memory. Notice that this explicitly
30
30
//! includes [`read_volatile`] and [`write_volatile`]: Volatile accesses cannot
31
- //! be used for inter-thread synchronization.
31
+ //! be used for inter-thread synchronization, regardless of whether they are acting on
32
+ //! Rust memory or not.
32
33
//! * The result of casting a reference to a pointer is valid for as long as the
33
34
//! underlying allocation is live and no reference (just raw pointers) is used to
34
35
//! access the same memory. That is, reference and pointer accesses cannot be
114
115
//! fully contiguous (i.e., has no "holes"), there is no guarantee that this
115
116
//! will not change in the future.
116
117
//!
118
+ //! Allocations must behave like "normal" memory: in particular, reads must not have
119
+ //! side-effects, and writes must become visible to other threads using the usual synchronization
120
+ //! primitives.
121
+ //!
117
122
//! For any allocation with `base` address, `size`, and a set of
118
123
//! `addresses`, the following are guaranteed:
119
124
//! - For all addresses `a` in `addresses`, `a` is in the range `base .. (base +
@@ -2021,54 +2026,61 @@ pub const unsafe fn write_unaligned<T>(dst: *mut T, src: T) {
2021
2026
}
2022
2027
}
2023
2028
2024
- /// Performs a volatile read of the value from `src` without moving it. This
2025
- /// leaves the memory in `src` unchanged.
2026
- ///
2027
- /// Volatile operations are intended to act on I/O memory, and are guaranteed
2028
- /// to not be elided or reordered by the compiler across other volatile
2029
- /// operations.
2030
- ///
2031
- /// # Notes
2032
- ///
2033
- /// Rust does not currently have a rigorously and formally defined memory model,
2034
- /// so the precise semantics of what "volatile" means here is subject to change
2035
- /// over time. That being said, the semantics will almost always end up pretty
2036
- /// similar to [C11's definition of volatile][c11].
2037
- ///
2038
- /// The compiler shouldn't change the relative order or number of volatile
2039
- /// memory operations. However, volatile memory operations on zero-sized types
2040
- /// (e.g., if a zero-sized type is passed to `read_volatile`) are noops
2041
- /// and may be ignored.
2042
- ///
2043
- /// [c11]: http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf
2029
+ /// Performs a volatile read of the value from `src` without moving it.
2030
+ ///
2031
+ /// Volatile operations are intended to act on I/O memory. As such, they are considered externally
2032
+ /// observable events (just like syscalls, but less opaque), and are guaranteed to not be elided or
2033
+ /// reordered by the compiler across other externally observable events. With this in mind, there
2034
+ /// are two cases of usage that need to be distinguished:
2035
+ ///
2036
+ /// - When a volatile operation is used for memory inside an [allocation], it behaves exactly like
2037
+ /// [`read`], except for the additional guarantee that it won't be elided or reordered (see
2038
+ /// above). This implies that the operation will actually access memory and not e.g. be lowered to
2039
+ /// reusing data from a previous read. Other than that, all the usual rules for memory accesses
2040
+ /// apply (including provenance). In particular, just like in C, whether an operation is volatile
2041
+ /// has no bearing whatsoever on questions involving concurrent accesses from multiple threads.
2042
+ /// Volatile accesses behave exactly like non-atomic accesses in that regard.
2043
+ ///
2044
+ /// - Volatile operations, however, may also be used to access memory that is _outside_ of any Rust
2045
+ /// allocation. In this use-case, the pointer does *not* have to be [valid] for reads. This is
2046
+ /// typically used for CPU and peripheral registers that must be accessed via an I/O memory
2047
+ /// mapping, most commonly at fixed addresses reserved by the hardware. These often have special
2048
+ /// semantics associated to their manipulation, and cannot be used as general purpose memory.
2049
+ /// Here, any address value is possible, including 0 and [`usize::MAX`], so long as the semantics
2050
+ /// of such a read are well-defined by the target hardware. The provenance of the pointer is
2051
+ /// irrelevant, and it can be created with [`without_provenance`]. The access must not trap. It
2052
+ /// can cause side-effects, but those must not affect Rust-allocated memory in any way. This
2053
+ /// access is still not considered [atomic], and as such it cannot be used for inter-thread
2054
+ /// synchronization.
2055
+ ///
2056
+ /// Note that volatile memory operations where T is a zero-sized type are noops and may be ignored.
2057
+ ///
2058
+ /// [allocation]: crate::ptr#allocated-object
2059
+ /// [atomic]: crate::sync::atomic#memory-model-for-atomic-accesses
2044
2060
///
2045
2061
/// # Safety
2046
2062
///
2063
+ /// Like [`read`], `read_volatile` creates a bitwise copy of `T`, regardless of whether `T` is
2064
+ /// [`Copy`]. If `T` is not [`Copy`], using both the returned value and the value at `*src` can
2065
+ /// [violate memory safety][read-ownership]. However, storing non-[`Copy`] types in volatile memory
2066
+ /// is almost certainly incorrect.
2067
+ ///
2047
2068
/// Behavior is undefined if any of the following conditions are violated:
2048
2069
///
2049
- /// * `src` must be [valid] for reads.
2070
+ /// * `src` must be either [valid] for reads, or it must point to memory outside of all Rust
2071
+ /// allocations and reading from that memory must:
2072
+ /// - not trap, and
2073
+ /// - not cause any memory inside a Rust allocation to be modified.
2050
2074
///
2051
2075
/// * `src` must be properly aligned.
2052
2076
///
2053
- /// * `src` must point to a properly initialized value of type `T`.
2054
- ///
2055
- /// Like [`read`], `read_volatile` creates a bitwise copy of `T`, regardless of
2056
- /// whether `T` is [`Copy`]. If `T` is not [`Copy`], using both the returned
2057
- /// value and the value at `*src` can [violate memory safety][read-ownership].
2058
- /// However, storing non-[`Copy`] types in volatile memory is almost certainly
2059
- /// incorrect.
2077
+ /// * Reading from `src` must produce a properly initialized value of type `T`.
2060
2078
///
2061
2079
/// Note that even if `T` has size `0`, the pointer must be properly aligned.
2062
2080
///
2063
2081
/// [valid]: self#safety
2064
2082
/// [read-ownership]: read#ownership-of-the-returned-value
2065
2083
///
2066
- /// Just like in C, whether an operation is volatile has no bearing whatsoever
2067
- /// on questions involving concurrent access from multiple threads. Volatile
2068
- /// accesses behave exactly like non-atomic accesses in that regard. In particular,
2069
- /// a race between a `read_volatile` and any write operation to the same location
2070
- /// is undefined behavior.
2071
- ///
2072
2084
/// # Examples
2073
2085
///
2074
2086
/// Basic usage:
@@ -2090,63 +2102,70 @@ pub unsafe fn read_volatile<T>(src: *const T) -> T {
2090
2102
unsafe {
2091
2103
ub_checks:: assert_unsafe_precondition!(
2092
2104
check_language_ub,
2093
- "ptr::read_volatile requires that the pointer argument is aligned and non-null " ,
2105
+ "ptr::read_volatile requires that the pointer argument is aligned" ,
2094
2106
(
2095
2107
addr: * const ( ) = src as * const ( ) ,
2096
2108
align: usize = align_of:: <T >( ) ,
2097
- is_zst: bool = T :: IS_ZST ,
2098
- ) => ub_checks:: maybe_is_aligned_and_not_null( addr, align, is_zst)
2109
+ ) => ub_checks:: maybe_is_aligned( addr, align)
2099
2110
) ;
2100
2111
intrinsics:: volatile_load ( src)
2101
2112
}
2102
2113
}
2103
2114
2104
- /// Performs a volatile write of a memory location with the given value without
2105
- /// reading or dropping the old value.
2106
- ///
2107
- /// Volatile operations are intended to act on I/O memory, and are guaranteed
2108
- /// to not be elided or reordered by the compiler across other volatile
2109
- /// operations.
2110
- ///
2111
- /// `write_volatile` does not drop the contents of `dst`. This is safe, but it
2112
- /// could leak allocations or resources, so care should be taken not to overwrite
2113
- /// an object that should be dropped.
2114
- ///
2115
- /// Additionally, it does not drop `src`. Semantically, `src` is moved into the
2116
- /// location pointed to by `dst`.
2117
- ///
2118
- /// # Notes
2119
- ///
2120
- /// Rust does not currently have a rigorously and formally defined memory model,
2121
- /// so the precise semantics of what "volatile" means here is subject to change
2122
- /// over time. That being said, the semantics will almost always end up pretty
2123
- /// similar to [C11's definition of volatile][c11].
2124
- ///
2125
- /// The compiler shouldn't change the relative order or number of volatile
2126
- /// memory operations. However, volatile memory operations on zero-sized types
2127
- /// (e.g., if a zero-sized type is passed to `write_volatile`) are noops
2128
- /// and may be ignored.
2129
- ///
2130
- /// [c11]: http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf
2115
+ /// Performs a volatile write of a memory location with the given value without reading or dropping
2116
+ /// the old value.
2117
+ ///
2118
+ /// Volatile operations are intended to act on I/O memory. As such, they are considered externally
2119
+ /// observable events (just like syscalls), and are guaranteed to not be elided or reordered by the
2120
+ /// compiler across other externally observable events. With this in mind, there are two cases of
2121
+ /// usage that need to be distinguished:
2122
+ ///
2123
+ /// - When a volatile operation is used for memory inside an [allocation], it behaves exactly like
2124
+ /// [`write`][write()], except for the additional guarantee that it won't be elided or reordered
2125
+ /// (see above). This implies that the operation will actually access memory and not e.g. be
2126
+ /// lowered to a register access. Other than that, all the usual rules for memory accesses apply
2127
+ /// (including provenance). In particular, just like in C, whether an operation is volatile has no
2128
+ /// bearing whatsoever on questions involving concurrent access from multiple threads. Volatile
2129
+ /// accesses behave exactly like non-atomic accesses in that regard.
2130
+ ///
2131
+ /// - Volatile operations, however, may also be used to access memory that is _outside_ of any Rust
2132
+ /// allocation. In this use-case, the pointer does *not* have to be [valid] for writes. This is
2133
+ /// typically used for CPU and peripheral registers that must be accessed via an I/O memory
2134
+ /// mapping, most commonly at fixed addresses reserved by the hardware. These often have special
2135
+ /// semantics associated to their manipulation, and cannot be used as general purpose memory.
2136
+ /// Here, any address value is possible, including 0 and [`usize::MAX`], so long as the semantics
2137
+ /// of such a write are well-defined by the target hardware. The provenance of the pointer is
2138
+ /// irrelevant, and it can be created with [`without_provenance`]. The access must not trap. It
2139
+ /// can cause side-effects, but those must not affect Rust-allocated memory in any way. This
2140
+ /// access is still not considered [atomic], and as such it cannot be used for inter-thread
2141
+ /// synchronization.
2142
+ ///
2143
+ /// Note that volatile memory operations on zero-sized types (e.g., if a zero-sized type is passed
2144
+ /// to `write_volatile`) are noops and may be ignored.
2145
+ ///
2146
+ /// `write_volatile` does not drop the contents of `dst`. This is safe, but it could leak
2147
+ /// allocations or resources, so care should be taken not to overwrite an object that should be
2148
+ /// dropped when operating on Rust memory. Additionally, it does not drop `src`. Semantically, `src`
2149
+ /// is moved into the location pointed to by `dst`.
2150
+ ///
2151
+ /// [allocation]: crate::ptr#allocated-object
2152
+ /// [atomic]: crate::sync::atomic#memory-model-for-atomic-accesses
2131
2153
///
2132
2154
/// # Safety
2133
2155
///
2134
2156
/// Behavior is undefined if any of the following conditions are violated:
2135
2157
///
2136
- /// * `dst` must be [valid] for writes.
2158
+ /// * `dst` must be either [valid] for writes, or it must point to memory outside of all Rust
2159
+ /// allocations and writing to that memory must:
2160
+ /// - not trap, and
2161
+ /// - not cause any memory inside a Rust allocation to be modified.
2137
2162
///
2138
2163
/// * `dst` must be properly aligned.
2139
2164
///
2140
2165
/// Note that even if `T` has size `0`, the pointer must be properly aligned.
2141
2166
///
2142
2167
/// [valid]: self#safety
2143
2168
///
2144
- /// Just like in C, whether an operation is volatile has no bearing whatsoever
2145
- /// on questions involving concurrent access from multiple threads. Volatile
2146
- /// accesses behave exactly like non-atomic accesses in that regard. In particular,
2147
- /// a race between a `write_volatile` and any other operation (reading or writing)
2148
- /// on the same location is undefined behavior.
2149
- ///
2150
2169
/// # Examples
2151
2170
///
2152
2171
/// Basic usage:
@@ -2170,12 +2189,11 @@ pub unsafe fn write_volatile<T>(dst: *mut T, src: T) {
2170
2189
unsafe {
2171
2190
ub_checks:: assert_unsafe_precondition!(
2172
2191
check_language_ub,
2173
- "ptr::write_volatile requires that the pointer argument is aligned and non-null " ,
2192
+ "ptr::write_volatile requires that the pointer argument is aligned" ,
2174
2193
(
2175
2194
addr: * mut ( ) = dst as * mut ( ) ,
2176
2195
align: usize = align_of:: <T >( ) ,
2177
- is_zst: bool = T :: IS_ZST ,
2178
- ) => ub_checks:: maybe_is_aligned_and_not_null( addr, align, is_zst)
2196
+ ) => ub_checks:: maybe_is_aligned( addr, align)
2179
2197
) ;
2180
2198
intrinsics:: volatile_store ( dst, src) ;
2181
2199
}
0 commit comments