concurrent/copy.rs
1// Copyright 2026 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use core::cell::UnsafeCell;
6use core::sync::atomic::{Ordering, fence};
7use zerocopy::{FromBytes, Immutable, IntoBytes};
8
9use crate::common::{SYNC_OPT_FENCE, SYNC_OPT_NONE};
10
11pub use internal::MAX_TRANSFER_GRANULARITY;
12
13use internal::{COPY_DIR_FROM, COPY_DIR_TO, MaxTransferAligned, well_defined_copy};
14
15mod internal {
16 use core::sync::atomic::{AtomicU8, AtomicU16, AtomicU32, AtomicU64, Ordering};
17
18 use crate::common::{SYNC_OPT_ACQ_REL_OPS, SYNC_OPT_NONE};
19
20 pub const MAX_TRANSFER_GRANULARITY: usize = size_of::<u64>();
21 pub(super) const COPY_DIR_TO: u8 = 0;
22 pub(super) const COPY_DIR_FROM: u8 = 1;
23
24 #[derive(Clone, Copy, Debug, PartialEq, Eq)]
25 pub(super) enum MaxTransferAligned {
26 No,
27 Yes,
28 }
29
30 pub(super) trait TransferElement: Copy {
31 /// # Safety
32 ///
33 /// `dst` must be valid for writes of `size_of::<Self>()` bytes, naturally
34 /// aligned for `Self`, and not concurrently accessed with non-atomic operations.
35 unsafe fn atomic_store(dst: *mut Self, val: Self, order: Ordering);
36
37 /// # Safety
38 ///
39 /// `src` must be valid for reads of `size_of::<Self>()` bytes, naturally
40 /// aligned for `Self`, and not concurrently accessed with non-atomic operations.
41 unsafe fn atomic_load(src: *const Self, order: Ordering) -> Self;
42 }
43
44 macro_rules! impl_transfer_element {
45 ($int:ty, $atomic:ty) => {
46 impl TransferElement for $int {
47 #[inline(always)]
48 unsafe fn atomic_store(dst: *mut Self, val: Self, order: Ordering) {
49 // SAFETY: The caller guarantees `dst` is valid for writes, naturally
50 // aligned, and not concurrently accessed with non-atomic operations.
51 unsafe { <$atomic>::from_ptr(dst).store(val, order) }
52 }
53
54 #[inline(always)]
55 unsafe fn atomic_load(src: *const Self, order: Ordering) -> Self {
56 // SAFETY: The caller guarantees `src` is valid for reads, naturally
57 // aligned, and not concurrently accessed with non-atomic operations.
58 unsafe { <$atomic>::from_ptr(src.cast_mut()).load(order) }
59 }
60 }
61 };
62 }
63
64 impl_transfer_element!(u8, AtomicU8);
65 impl_transfer_element!(u16, AtomicU16);
66 impl_transfer_element!(u32, AtomicU32);
67 impl_transfer_element!(u64, AtomicU64);
68
69 #[inline(always)]
70 pub(super) unsafe fn copy_element<T: TransferElement, const DIR: u8>(
71 dst: *mut u8,
72 src: *const u8,
73 offset_bytes: usize,
74 order: Ordering,
75 ) {
76 // SAFETY: The caller guarantees `src + offset_bytes` and `dst + offset_bytes`
77 // remain in bounds and are naturally aligned for `T`.
78 let src = unsafe { src.add(offset_bytes) }.cast::<T>();
79 // SAFETY: As above.
80 let dst = unsafe { dst.add(offset_bytes) }.cast::<T>();
81
82 debug_assert!(
83 ((DIR == COPY_DIR_TO) && matches!(order, Ordering::Relaxed | Ordering::Release))
84 || ((DIR == COPY_DIR_FROM)
85 && matches!(order, Ordering::Relaxed | Ordering::Acquire))
86 );
87
88 if const { DIR == COPY_DIR_TO } {
89 // SAFETY: The caller guarantees `dst` is valid for atomic stores and `src` is valid
90 // for reads of `T`.
91 unsafe { T::atomic_store(dst, *src, order) };
92 } else {
93 // SAFETY: The caller guarantees `src` is valid for atomic loads and `dst` is valid
94 // for writes of `T`.
95 unsafe { *dst = T::atomic_load(src, order) };
96 }
97 }
98
99 #[inline]
100 pub(super) unsafe fn well_defined_copy<const DIR: u8, const SYNC_OPT: u8>(
101 dst: *mut u8,
102 src: *const u8,
103 size_bytes: usize,
104 max_transfer_aligned: MaxTransferAligned,
105 ) {
106 // To keep life simple, we demand that both the source and the destination
107 // have the same alignment relative to our max transfer granularity.
108 debug_assert!(
109 ((src as usize) & (MAX_TRANSFER_GRANULARITY - 1))
110 == ((dst as usize) & (MAX_TRANSFER_GRANULARITY - 1)),
111 "src {:p} dst {:p} granularity {}",
112 src,
113 dst,
114 MAX_TRANSFER_GRANULARITY
115 );
116
117 // In debug builds, make sure that src and dst obey the specified
118 // worst case alignment.
119 //
120 // TODO(johngro): Consider demoting these asserts to existing only in a
121 // super-duper-debug build, if we ever have such a thing.
122 debug_assert!(
123 (max_transfer_aligned == MaxTransferAligned::No)
124 || ((src as usize) & (MAX_TRANSFER_GRANULARITY - 1)) == 0
125 );
126 debug_assert!(
127 (max_transfer_aligned == MaxTransferAligned::No)
128 || ((dst as usize) & (MAX_TRANSFER_GRANULARITY - 1)) == 0
129 );
130
131 // Sync options at this point should be either to use Acquire/Release on the
132 // options, or to simply use relaxed. Use of fences should have been handled
133 // at the inline wrapper level.
134 assert!((SYNC_OPT == SYNC_OPT_ACQ_REL_OPS) || (SYNC_OPT == SYNC_OPT_NONE));
135
136 if size_bytes == 0 {
137 return;
138 }
139
140 let order = if SYNC_OPT == SYNC_OPT_NONE {
141 Ordering::Relaxed
142 } else if DIR == COPY_DIR_TO {
143 Ordering::Release
144 } else {
145 Ordering::Acquire
146 };
147
148 // Start by bringing our pointer to 8 byte alignment. Skip any steps which
149 // are not required based on our specified worst case alignment.
150 let mut offset_bytes: usize = 0;
151 if max_transfer_aligned == MaxTransferAligned::No {
152 if ((src as usize + offset_bytes) & 1) != 0 && (size_bytes - offset_bytes) >= 1 {
153 // SAFETY: Both buffers are valid for `size_bytes` bytes and share the same
154 // alignment relative to `MAX_TRANSFER_GRANULARITY`.
155 unsafe { copy_element::<u8, DIR>(dst, src, offset_bytes, order) };
156 offset_bytes += 1;
157 }
158 if ((src as usize + offset_bytes) & 2) != 0 && (size_bytes - offset_bytes) >= 2 {
159 // SAFETY: Both buffers are valid for `size_bytes` bytes and 2-byte aligned
160 // at `offset_bytes`.
161 unsafe { copy_element::<u16, DIR>(dst, src, offset_bytes, order) };
162 offset_bytes += 2;
163 }
164 if ((src as usize + offset_bytes) & 4) != 0 && (size_bytes - offset_bytes) >= 4 {
165 // SAFETY: Both buffers are valid for `size_bytes` bytes and 4-byte aligned
166 // at `offset_bytes`.
167 unsafe { copy_element::<u32, DIR>(dst, src, offset_bytes, order) };
168 offset_bytes += 4;
169 }
170 }
171
172 // Now copy the bulk portion of the data using 64 bit transfers.
173 const { assert!(MAX_TRANSFER_GRANULARITY == size_of::<u64>()) };
174 while offset_bytes + size_of::<u64>() <= size_bytes {
175 // SAFETY: Both buffers are valid for `size_bytes` bytes and 8-byte aligned
176 // at `offset_bytes`.
177 unsafe { copy_element::<u64, DIR>(dst, src, offset_bytes, order) };
178 offset_bytes += size_of::<u64>();
179 }
180
181 // If there is anything left to do, take care of the remainder using smaller
182 // transfers.
183 let remainder = size_bytes - offset_bytes;
184 if remainder > 0 {
185 // SAFETY: Both buffers are valid for `size_bytes` bytes and sufficiently aligned
186 // at `offset_bytes` for each transfer width.
187 unsafe {
188 match remainder {
189 1 => copy_element::<u8, DIR>(dst, src, offset_bytes, order),
190 2 => copy_element::<u16, DIR>(dst, src, offset_bytes, order),
191 3 => {
192 copy_element::<u16, DIR>(dst, src, offset_bytes, order);
193 copy_element::<u8, DIR>(dst, src, offset_bytes + 2, order);
194 }
195 4 => copy_element::<u32, DIR>(dst, src, offset_bytes, order),
196 5 => {
197 copy_element::<u32, DIR>(dst, src, offset_bytes, order);
198 copy_element::<u8, DIR>(dst, src, offset_bytes + 4, order);
199 }
200 6 => {
201 copy_element::<u32, DIR>(dst, src, offset_bytes, order);
202 copy_element::<u16, DIR>(dst, src, offset_bytes + 4, order);
203 }
204 7 => {
205 copy_element::<u32, DIR>(dst, src, offset_bytes, order);
206 copy_element::<u16, DIR>(dst, src, offset_bytes + 4, order);
207 copy_element::<u8, DIR>(dst, src, offset_bytes + 6, order);
208 }
209 _ => debug_assert!(false),
210 }
211 }
212 }
213 }
214}
215
216/// Copy `size_bytes` bytes from `src` to `dst` using atomic store operations to move
217/// the element into `dst` so that the behavior of the system is always well
218/// defined, even if there is a `well_defined_copy_from` operation reading from the
219/// memory pointed to by `dst` concurrent with this `well_defined_copy_to` operation.
220///
221/// `well_defined_copy_to` has `memcpy` semantics, not `memmove` semantics. In other
222/// words, it is illegal for `src` or `dst` to overlap in any way.
223///
224/// While it is not required, by default, that `src` and `dst` have any specific
225/// alignment, both `src` and `dst` *must* have the _same_ alignment.
226///
227/// IOW: (`src` & 0x7) *must* equal (`dst` & 0x7)
228///
229/// # Const Generic Args
230///
231/// `SYNC_OPT`
232/// Controls the options for memory order synchronization. See the comments on
233/// [`crate::common::SyncOpt`] for details.
234///
235/// `WORST_CASE_ALIGNMENT`
236/// An explicit guarantee of the worst case alignment that `src`/`dst` will obey.
237/// When this alignment guarantee is greater than or equal to the maximum
238/// internal transfer granularity of 64 bits, the initial explicit alignment step
239/// of the operation can be optimized away for a minor performance gain.
240///
241/// # Safety
242///
243/// * `src` and `dst` must be valid for reads and writes (respectively) of `size_bytes`
244/// bytes, must not overlap, and must have the same alignment modulo 8.
245/// * Both pointers must be aligned to at least `WORST_CASE_ALIGNMENT`.
246/// * `dst` must only be accessed concurrently through well-defined copy operations,
247/// and `src` must not be concurrently mutated.
248#[inline]
249pub unsafe fn well_defined_copy_to<const SYNC_OPT: u8, const WORST_CASE_ALIGNMENT: usize>(
250 dst: *mut u8,
251 src: *const u8,
252 size_bytes: usize,
253) {
254 const {
255 assert!(
256 WORST_CASE_ALIGNMENT.is_power_of_two(),
257 "WORST_CASE_ALIGNMENT must be a power of 2"
258 );
259 };
260 let mta = if WORST_CASE_ALIGNMENT >= MAX_TRANSFER_GRANULARITY {
261 MaxTransferAligned::Yes
262 } else {
263 MaxTransferAligned::No
264 };
265
266 if const { SYNC_OPT == SYNC_OPT_FENCE } {
267 fence(Ordering::Release);
268 // SAFETY: The caller guarantees the safety preconditions of `well_defined_copy`.
269 unsafe { well_defined_copy::<COPY_DIR_TO, SYNC_OPT_NONE>(dst, src, size_bytes, mta) };
270 } else {
271 // SAFETY: The caller guarantees the safety preconditions of `well_defined_copy`.
272 unsafe { well_defined_copy::<COPY_DIR_TO, SYNC_OPT>(dst, src, size_bytes, mta) };
273 }
274}
275
276/// Copy `size_bytes` bytes from `src` to `dst` using atomic load operations to load the
277/// element from `src` so that the behavior of the system is always well defined,
278/// even if there is a `well_defined_copy_to` operation writing to the memory pointed
279/// to by `src` concurrent with this `well_defined_copy_from` operation.
280///
281/// `well_defined_copy_from` has `memcpy` semantics, not `memmove` semantics. In other
282/// words, it is illegal for `src` or `dst` to overlap in any way.
283///
284/// While it is not required, by default, that `src` and `dst` have any specific
285/// alignment, both `src` and `dst` *must* have the _same_ alignment.
286///
287/// IOW: (`src` & 0x7) *must* equal (`dst` & 0x7)
288///
289/// # Const Generic Args
290///
291/// `SYNC_OPT`
292/// Controls the options for memory order synchronization. See the comments on
293/// [`crate::common::SyncOpt`] for details.
294///
295/// `WORST_CASE_ALIGNMENT`
296/// An explicit guarantee of the worst case alignment that `src`/`dst` will obey.
297/// When this alignment guarantee is greater than or equal to the maximum
298/// internal transfer granularity of 64 bits, the initial explicit alignment step
299/// of the operation can be optimized away for a minor performance gain.
300///
301/// # Safety
302///
303/// * `src` and `dst` must be valid for reads and writes (respectively) of `size_bytes`
304/// bytes, must not overlap, and must have the same alignment modulo 8.
305/// * Both pointers must be aligned to at least `WORST_CASE_ALIGNMENT`.
306/// * `src` must only be accessed concurrently through well-defined copy operations,
307/// and `dst` must not be concurrently accessed.
308#[inline]
309pub unsafe fn well_defined_copy_from<const SYNC_OPT: u8, const WORST_CASE_ALIGNMENT: usize>(
310 dst: *mut u8,
311 src: *const u8,
312 size_bytes: usize,
313) {
314 const {
315 assert!(
316 WORST_CASE_ALIGNMENT.is_power_of_two(),
317 "WORST_CASE_ALIGNMENT must be a power of 2"
318 );
319 };
320 let mta = if WORST_CASE_ALIGNMENT >= MAX_TRANSFER_GRANULARITY {
321 MaxTransferAligned::Yes
322 } else {
323 MaxTransferAligned::No
324 };
325
326 if const { SYNC_OPT == SYNC_OPT_FENCE } {
327 // SAFETY: The caller guarantees the safety preconditions of `well_defined_copy`.
328 unsafe { well_defined_copy::<COPY_DIR_FROM, SYNC_OPT_NONE>(dst, src, size_bytes, mta) };
329 fence(Ordering::Acquire);
330 } else {
331 // SAFETY: The caller guarantees the safety preconditions of `well_defined_copy`.
332 unsafe { well_defined_copy::<COPY_DIR_FROM, SYNC_OPT>(dst, src, size_bytes, mta) };
333 }
334}
335
336/// Wrapper for transferring trivially copyable data into and out of shared
337/// memory using well-defined atomic operations.
338///
339/// Users wrap a type `T` in `WellDefinedCopyable<T>` and use [`Self::update`]
340/// and [`Self::read`] to copy data into and out of the contained `T` instance,
341/// respectively. These methods deliberately restrict access to the underlying
342/// storage so transfers occur through the lowest-level well-defined copy
343/// functions.
344///
345/// `T` must implement [`Copy`], [`FromBytes`], [`IntoBytes`], and [`Immutable`]
346/// to guarantee that it has no uninitialized padding bytes and that any byte
347/// pattern observed during a concurrent transfer is a valid representation of
348/// `T`. In addition, `align_of::<T>()` must be at least
349/// [`MAX_TRANSFER_GRANULARITY`] (8 bytes) so that source and destination
350/// buffers always share identical alignment modulo 8.
351#[repr(transparent)]
352pub struct WellDefinedCopyable<T: Copy + FromBytes + IntoBytes + Immutable> {
353 // `Clone` and `Copy` are intentionally not implemented so shared instances
354 // cannot be copied non-atomically.
355 pub(crate) instance: UnsafeCell<T>,
356}
357
358// SAFETY: Every concurrent access to `instance` is performed using atomic operations,
359// and `T: Send` allows values to be transferred across threads.
360unsafe impl<T: Copy + FromBytes + IntoBytes + Immutable + Send> Sync for WellDefinedCopyable<T> {}
361
362impl<T: Copy + FromBytes + IntoBytes + Immutable> WellDefinedCopyable<T> {
363 const VALID_TYPE: () = {
364 assert!(
365 size_of::<Self>() == size_of::<T>(),
366 "WellDefinedCopyable<T> must be the same size as T"
367 );
368 assert!(
369 align_of::<Self>() == align_of::<T>(),
370 "WellDefinedCopyable<T> must have the same alignment as T"
371 );
372 assert!(
373 align_of::<T>() >= MAX_TRANSFER_GRANULARITY,
374 "T must have alignment >= MAX_TRANSFER_GRANULARITY"
375 );
376 };
377
378 /// Creates a new `WellDefinedCopyable` wrapping `instance`.
379 #[inline]
380 pub const fn new(instance: T) -> Self {
381 let () = Self::VALID_TYPE;
382 Self { instance: UnsafeCell::new(instance) }
383 }
384
385 /// Read from the wrapped object into the destination buffer provided by the caller.
386 #[inline]
387 pub fn read<const SYNC_OPT: u8>(&self, dst: &mut T) {
388 let () = Self::VALID_TYPE;
389 // SAFETY: `dst` and `self.instance` are valid for `size_of::<T>()` bytes,
390 // non-overlapping, aligned to at least `MAX_TRANSFER_GRANULARITY` (and thus
391 // share the same alignment modulo 8), and `self.instance` is only accessed
392 // atomically.
393 unsafe {
394 well_defined_copy_from::<SYNC_OPT, MAX_TRANSFER_GRANULARITY>(
395 core::ptr::from_mut(dst).cast::<u8>(),
396 self.instance.get().cast_const().cast::<u8>(),
397 size_of::<T>(),
398 );
399 }
400 }
401
402 /// Update the wrapped object from the source buffer provided by the caller.
403 #[inline]
404 pub fn update<const SYNC_OPT: u8>(&self, src: &T) {
405 let () = Self::VALID_TYPE;
406 // SAFETY: `self.instance` and `src` are valid for `size_of::<T>()` bytes,
407 // non-overlapping, aligned to at least `MAX_TRANSFER_GRANULARITY` (and thus
408 // share the same alignment modulo 8), and `self.instance` is only accessed
409 // atomically.
410 unsafe {
411 well_defined_copy_to::<SYNC_OPT, MAX_TRANSFER_GRANULARITY>(
412 self.instance.get().cast::<u8>(),
413 core::ptr::from_ref(src).cast::<u8>(),
414 size_of::<T>(),
415 );
416 }
417 }
418
419 /// WARNING: There be dragons here!
420 ///
421 /// `unsynchronized_get` returns a raw pointer providing direct read-only
422 /// access to the underlying instance of `T`. Dereferencing the pointer is
423 /// _only_ safe if the user can guarantee that no write operations may be
424 /// concurrently performed against the storage while reading the instance.
425 ///
426 /// One example of a legitimate use of this method might be when a user is
427 /// operating in the write exclusive portion of a sequence lock. They are
428 /// guaranteed to be the only potential writer of the wrapped object, so while
429 /// it is still important that they continue to use `update` when they wish to
430 /// mutate their instance of `T`, it is OK for them to read `T` directly without
431 /// using `read` as this will not cause any undefined behavior when done
432 /// concurrently with other readers in the system.
433 ///
434 /// Returning a raw pointer `*const T` rather than a reference `&T` avoids
435 /// Rust's aliasing requirement that the pointee remain immutable for the
436 /// entire lifetime of a reference, matching C++ where holding a reference
437 /// across concurrent writes is permitted as long as it is not read during a
438 /// write.
439 #[inline]
440 #[must_use]
441 pub const fn unsynchronized_get(&self) -> *const T {
442 self.instance.get().cast_const()
443 }
444}
445
446impl<T: Copy + FromBytes + IntoBytes + Immutable + Default> Default for WellDefinedCopyable<T> {
447 fn default() -> Self {
448 Self::new(T::default())
449 }
450}
451
452zr::static_assert!(size_of::<WellDefinedCopyable<u64>>() == size_of::<u64>());
453zr::static_assert!(align_of::<WellDefinedCopyable<u64>>() == align_of::<u64>());
454zr::static_assert!(size_of::<WellDefinedCopyable<[u64; 8]>>() == 64);
455zr::static_assert!(align_of::<WellDefinedCopyable<[u64; 8]>>() == align_of::<u64>());
456
457#[cfg(test)]
458mod tests {
459 use super::*;
460 use crate::common::SYNC_OPT_ACQ_REL_OPS;
461
462 const TEST_BUFFER_SIZE: usize = 256;
463
464 trait TestObj:
465 Copy + Default + core::fmt::Debug + PartialEq + Eq + FromBytes + IntoBytes + Immutable
466 {
467 type Inner: Copy + Default + core::fmt::Debug + PartialEq + Eq;
468 fn new(val: Self::Inner) -> Self;
469 fn val(&self) -> Self::Inner;
470 }
471
472 macro_rules! define_simple_obj {
473 ($name:ident, $inner:ty, $pad_len:expr) => {
474 #[repr(C, align(8))]
475 #[derive(
476 Clone, Copy, Debug, Default, PartialEq, Eq, FromBytes, IntoBytes, Immutable,
477 )]
478 struct $name {
479 val: $inner,
480 _pad: [u8; $pad_len],
481 }
482
483 impl TestObj for $name {
484 type Inner = $inner;
485 fn new(val: $inner) -> Self {
486 Self { val, _pad: [0; $pad_len] }
487 }
488 fn val(&self) -> $inner {
489 self.val
490 }
491 }
492 };
493 }
494
495 define_simple_obj!(SimpleObjU8, u8, 7);
496 define_simple_obj!(SimpleObjU16, u16, 6);
497 define_simple_obj!(SimpleObjU32, u32, 4);
498 define_simple_obj!(SimpleObjU64, u64, 0);
499
500 #[repr(align(8))]
501 struct TestBuffer([u8; TEST_BUFFER_SIZE]);
502
503 impl TestBuffer {
504 const fn new() -> Self {
505 Self([0; TEST_BUFFER_SIZE])
506 }
507 }
508
509 struct Rng(u64);
510
511 impl Rng {
512 const CONST_SEED: u64 = 0xa5f0_84a2_c3de_6b75;
513
514 const fn new() -> Self {
515 Self(Self::CONST_SEED)
516 }
517
518 fn next(&mut self) -> u64 {
519 let mut x = self.0;
520 x ^= x >> 12;
521 x ^= x << 25;
522 x ^= x >> 27;
523 self.0 = x;
524 x.wrapping_mul(0x2545_f491_4f6c_dd1d)
525 }
526
527 fn next_u8(&mut self) -> u8 {
528 self.next() as u8
529 }
530 }
531
532 struct ConcurrentCopyFixture {
533 src: TestBuffer,
534 dst: TestBuffer,
535 generator: Rng,
536 }
537
538 impl ConcurrentCopyFixture {
539 fn new() -> Self {
540 let mut fixture =
541 Self { src: TestBuffer::new(), dst: TestBuffer::new(), generator: Rng::new() };
542 fixture.reset_buffer();
543 fixture
544 }
545
546 fn reset_buffer(&mut self) {
547 for i in 0..TEST_BUFFER_SIZE {
548 self.dst.0[i] = self.generator.next_u8();
549 self.src.0[i] = !self.dst.0[i];
550 }
551 }
552
553 fn do_wrapper_copy_test<O: TestObj, const SYNC_OPT: u8>(val: O::Inner) {
554 let wrapped = WellDefinedCopyable::<O>::default();
555 {
556 let unwrapped = O::new(val);
557 assert_eq!(val, unwrapped.val());
558 // SAFETY: Single-threaded test with exclusive access to `wrapped`.
559 assert_eq!(O::Inner::default(), unsafe { (*wrapped.unsynchronized_get()).val() });
560
561 wrapped.update::<SYNC_OPT>(&unwrapped);
562
563 assert_eq!(val, unwrapped.val());
564 // SAFETY: Single-threaded test with exclusive access to `wrapped`.
565 assert_eq!(val, unsafe { (*wrapped.unsynchronized_get()).val() });
566 }
567
568 {
569 let mut unwrapped = O::default();
570 assert_eq!(O::Inner::default(), unwrapped.val());
571 // SAFETY: Single-threaded test with exclusive access to `wrapped`.
572 assert_eq!(val, unsafe { (*wrapped.unsynchronized_get()).val() });
573
574 wrapped.read::<SYNC_OPT>(&mut unwrapped);
575
576 assert_eq!(val, unwrapped.val());
577 // SAFETY: Single-threaded test with exclusive access to `wrapped`.
578 assert_eq!(val, unsafe { (*wrapped.unsynchronized_get()).val() });
579 }
580 }
581
582 fn do_wrapper_test<O: TestObj>(val: O::Inner) {
583 // Default Construction
584 {
585 let wrapped = WellDefinedCopyable::<O>::default();
586 // SAFETY: Single-threaded test with exclusive access to `wrapped`.
587 assert_eq!(O::Inner::default(), unsafe { (*wrapped.unsynchronized_get()).val() });
588 }
589
590 // Explicit Construction
591 {
592 let wrapped = WellDefinedCopyable::new(O::new(val));
593 // SAFETY: Single-threaded test with exclusive access to `wrapped`.
594 assert_eq!(val, unsafe { (*wrapped.unsynchronized_get()).val() });
595 }
596
597 // Copy with various sync options.
598 Self::do_wrapper_copy_test::<O, SYNC_OPT_ACQ_REL_OPS>(val);
599 Self::do_wrapper_copy_test::<O, SYNC_OPT_FENCE>(val);
600 Self::do_wrapper_copy_test::<O, SYNC_OPT_NONE>(val);
601 }
602 }
603
604 #[test]
605 fn test_copy_to() {
606 let mut fixture = ConcurrentCopyFixture::new();
607
608 assert_eq!(fixture.src.0.len(), fixture.dst.0.len());
609
610 // Test all of the combinations of alignment at the start and end of the operation.
611 for offset in 0..size_of::<u64>() {
612 for remainder in 1..=size_of::<u64>() {
613 let op_len = fixture.src.0.len() - offset - (size_of::<u64>() - remainder);
614
615 assert!(op_len + offset <= fixture.src.0.len());
616
617 // Perform a copy-to using release semantics for each element transfer,
618 // and no fence, then check the results.
619 fixture.reset_buffer();
620 // SAFETY: `src` and `dst` are distinct, valid for `op_len` bytes at `offset`,
621 // and share the same alignment modulo 8.
622 unsafe {
623 well_defined_copy_to::<SYNC_OPT_ACQ_REL_OPS, 1>(
624 fixture.dst.0.as_mut_ptr().add(offset),
625 fixture.src.0.as_ptr().add(offset),
626 op_len,
627 );
628 }
629 assert_eq!(
630 &fixture.dst.0[offset..offset + op_len],
631 &fixture.src.0[offset..offset + op_len]
632 );
633
634 // Same test, but this time use a release fence at the start of the
635 // operation, and relaxed atomic semantics on the individual element
636 // transfers.
637 fixture.reset_buffer();
638 // SAFETY: As above.
639 unsafe {
640 well_defined_copy_to::<SYNC_OPT_FENCE, 1>(
641 fixture.dst.0.as_mut_ptr().add(offset),
642 fixture.src.0.as_ptr().add(offset),
643 op_len,
644 );
645 }
646 assert_eq!(
647 &fixture.dst.0[offset..offset + op_len],
648 &fixture.src.0[offset..offset + op_len]
649 );
650
651 // Same test, but this time do not use either a fence or release semantics
652 // on each element. Instead, simply do everything with relaxed atomic
653 // stores.
654 fixture.reset_buffer();
655 // SAFETY: As above.
656 unsafe {
657 well_defined_copy_to::<SYNC_OPT_NONE, 1>(
658 fixture.dst.0.as_mut_ptr().add(offset),
659 fixture.src.0.as_ptr().add(offset),
660 op_len,
661 );
662 }
663 assert_eq!(
664 &fixture.dst.0[offset..offset + op_len],
665 &fixture.src.0[offset..offset + op_len]
666 );
667 }
668 }
669
670 // Finally, perform one more test using each of the fence options, but
671 // guaranteeing that we have at least uint64_t alignment.
672 assert_eq!(fixture.dst.0.as_ptr() as usize & (MAX_TRANSFER_GRANULARITY - 1), 0);
673 assert_eq!(fixture.src.0.as_ptr() as usize & (MAX_TRANSFER_GRANULARITY - 1), 0);
674
675 // Release on the ops.
676 fixture.reset_buffer();
677 // SAFETY: Both buffers are 8-byte aligned and valid for `TEST_BUFFER_SIZE` bytes.
678 unsafe {
679 well_defined_copy_to::<SYNC_OPT_ACQ_REL_OPS, MAX_TRANSFER_GRANULARITY>(
680 fixture.dst.0.as_mut_ptr(),
681 fixture.src.0.as_ptr(),
682 fixture.dst.0.len(),
683 );
684 }
685 assert_eq!(fixture.dst.0, fixture.src.0);
686
687 // Use a release fence before the transfer.
688 fixture.reset_buffer();
689 // SAFETY: As above.
690 unsafe {
691 well_defined_copy_to::<SYNC_OPT_FENCE, MAX_TRANSFER_GRANULARITY>(
692 fixture.dst.0.as_mut_ptr(),
693 fixture.src.0.as_ptr(),
694 fixture.dst.0.len(),
695 );
696 }
697 assert_eq!(fixture.dst.0, fixture.src.0);
698
699 // Relaxed atomics on the ops, no fence.
700 fixture.reset_buffer();
701 // SAFETY: As above.
702 unsafe {
703 well_defined_copy_to::<SYNC_OPT_NONE, MAX_TRANSFER_GRANULARITY>(
704 fixture.dst.0.as_mut_ptr(),
705 fixture.src.0.as_ptr(),
706 fixture.dst.0.len(),
707 );
708 }
709 assert_eq!(fixture.dst.0, fixture.src.0);
710 }
711
712 #[test]
713 fn test_copy_from() {
714 let mut fixture = ConcurrentCopyFixture::new();
715
716 assert_eq!(fixture.src.0.len(), fixture.dst.0.len());
717
718 // Test all of the combinations of alignment at the start and end of the operation.
719 for offset in 0..size_of::<u64>() {
720 for remainder in 1..=size_of::<u64>() {
721 let op_len = fixture.src.0.len() - offset - (size_of::<u64>() - remainder);
722
723 assert!(op_len + offset <= fixture.src.0.len());
724
725 // Perform a copy-from using acquire semantics for each element transfer,
726 // and no fence, then check the results.
727 fixture.reset_buffer();
728 // SAFETY: `src` and `dst` are distinct, valid for `op_len` bytes at `offset`,
729 // and share the same alignment modulo 8.
730 unsafe {
731 well_defined_copy_from::<SYNC_OPT_ACQ_REL_OPS, 1>(
732 fixture.dst.0.as_mut_ptr().add(offset),
733 fixture.src.0.as_ptr().add(offset),
734 op_len,
735 );
736 }
737 assert_eq!(
738 &fixture.dst.0[offset..offset + op_len],
739 &fixture.src.0[offset..offset + op_len]
740 );
741
742 // Same test, but this time use an acquire fence at the end of the
743 // operation, and relaxed atomic semantics on the individual element
744 // transfers.
745 fixture.reset_buffer();
746 // SAFETY: As above.
747 unsafe {
748 well_defined_copy_from::<SYNC_OPT_FENCE, 1>(
749 fixture.dst.0.as_mut_ptr().add(offset),
750 fixture.src.0.as_ptr().add(offset),
751 op_len,
752 );
753 }
754 assert_eq!(
755 &fixture.dst.0[offset..offset + op_len],
756 &fixture.src.0[offset..offset + op_len]
757 );
758
759 // Same test, but this time do not use either a fence or acquire semantics
760 // on each element. Instead, simply do everything with relaxed atomic
761 // loads.
762 fixture.reset_buffer();
763 // SAFETY: As above.
764 unsafe {
765 well_defined_copy_from::<SYNC_OPT_NONE, 1>(
766 fixture.dst.0.as_mut_ptr().add(offset),
767 fixture.src.0.as_ptr().add(offset),
768 op_len,
769 );
770 }
771 assert_eq!(
772 &fixture.dst.0[offset..offset + op_len],
773 &fixture.src.0[offset..offset + op_len]
774 );
775 }
776 }
777
778 // Finally, perform one more test using each of the fence options, but
779 // guaranteeing that we have at least uint64_t alignment.
780 assert_eq!(fixture.dst.0.as_ptr() as usize & (MAX_TRANSFER_GRANULARITY - 1), 0);
781 assert_eq!(fixture.src.0.as_ptr() as usize & (MAX_TRANSFER_GRANULARITY - 1), 0);
782
783 // Acquire on the ops.
784 fixture.reset_buffer();
785 // SAFETY: Both buffers are 8-byte aligned and valid for `TEST_BUFFER_SIZE` bytes.
786 unsafe {
787 well_defined_copy_from::<SYNC_OPT_ACQ_REL_OPS, MAX_TRANSFER_GRANULARITY>(
788 fixture.dst.0.as_mut_ptr(),
789 fixture.src.0.as_ptr(),
790 fixture.dst.0.len(),
791 );
792 }
793 assert_eq!(fixture.dst.0, fixture.src.0);
794
795 // Use an acquire fence after the transfer.
796 fixture.reset_buffer();
797 // SAFETY: As above.
798 unsafe {
799 well_defined_copy_from::<SYNC_OPT_FENCE, MAX_TRANSFER_GRANULARITY>(
800 fixture.dst.0.as_mut_ptr(),
801 fixture.src.0.as_ptr(),
802 fixture.dst.0.len(),
803 );
804 }
805 assert_eq!(fixture.dst.0, fixture.src.0);
806
807 // Relaxed atomics on the ops, no fence.
808 fixture.reset_buffer();
809 // SAFETY: As above.
810 unsafe {
811 well_defined_copy_from::<SYNC_OPT_NONE, MAX_TRANSFER_GRANULARITY>(
812 fixture.dst.0.as_mut_ptr(),
813 fixture.src.0.as_ptr(),
814 fixture.dst.0.len(),
815 );
816 }
817 assert_eq!(fixture.dst.0, fixture.src.0);
818 }
819
820 #[test]
821 fn test_wrapper_copy() {
822 ConcurrentCopyFixture::do_wrapper_test::<SimpleObjU8>(0xA5);
823 ConcurrentCopyFixture::do_wrapper_test::<SimpleObjU16>(0xA55A);
824 ConcurrentCopyFixture::do_wrapper_test::<SimpleObjU32>(0xA55A_1234);
825 ConcurrentCopyFixture::do_wrapper_test::<SimpleObjU64>(0xA55A_1234_DEAD_BEEF);
826 }
827
828 #[test]
829 fn test_small_copies() {
830 let mut fixture = ConcurrentCopyFixture::new();
831
832 // Test all combinations of starting alignment (0..8) and small transfer lengths
833 // (0..=16), including short transfers that do not reach the next 8-byte boundary.
834 for offset in 0..size_of::<u64>() {
835 for op_len in 0..=16 {
836 fixture.reset_buffer();
837 let before = fixture.dst.0;
838
839 // SAFETY: `src` and `dst` are distinct, valid for `op_len` bytes at `offset`,
840 // and share the same alignment modulo 8.
841 unsafe {
842 well_defined_copy_to::<SYNC_OPT_ACQ_REL_OPS, 1>(
843 fixture.dst.0.as_mut_ptr().add(offset),
844 fixture.src.0.as_ptr().add(offset),
845 op_len,
846 );
847 }
848 assert_eq!(
849 &fixture.dst.0[offset..offset + op_len],
850 &fixture.src.0[offset..offset + op_len]
851 );
852 assert_eq!(&fixture.dst.0[..offset], &before[..offset]);
853 assert_eq!(&fixture.dst.0[offset + op_len..], &before[offset + op_len..]);
854
855 fixture.reset_buffer();
856 let before = fixture.dst.0;
857
858 // SAFETY: As above.
859 unsafe {
860 well_defined_copy_from::<SYNC_OPT_ACQ_REL_OPS, 1>(
861 fixture.dst.0.as_mut_ptr().add(offset),
862 fixture.src.0.as_ptr().add(offset),
863 op_len,
864 );
865 }
866 assert_eq!(
867 &fixture.dst.0[offset..offset + op_len],
868 &fixture.src.0[offset..offset + op_len]
869 );
870 assert_eq!(&fixture.dst.0[..offset], &before[..offset]);
871 assert_eq!(&fixture.dst.0[offset + op_len..], &before[offset + op_len..]);
872 }
873 }
874 }
875}