Skip to main content

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}