Skip to main content

concurrent/
common.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
5/// An enumeration of various synchronization options to use when performing
6/// memory transfer operations with `well_defined_copy_to` / `well_defined_copy_from`.
7///
8/// # AcqRelOps
9/// Use either `Ordering::Acquire` (`CopyFrom`) or `Ordering::Release` (`CopyTo`)
10/// on every atomic load/store operation during the transfer to/from the shared
11/// buffer.
12///
13/// # Fence
14/// Use either an `Ordering::Acquire` thread fence (`CopyFrom`) after the transfer
15/// operation, or an `Ordering::Release` (`CopyTo`) thread fence before the
16/// operation, and `Ordering::Relaxed` for each of the atomic load/store
17/// operations during the transfer.
18///
19/// # None
20/// Simply use `Ordering::Relaxed` for each of the atomic load/store
21/// operations during the transfer. Do not actually introduce any
22/// explicit synchronization behavior.
23///
24/// WARNING: Use cases for this transfer mode tend to be unusual. Users will almost
25/// always want some form of synchronization to take place during their
26/// transfers. One example of where it may be appropriate to use `SyncOpt::None`
27/// might be a situation where users are attempting to observe the state of more
28/// than one object while inside of a sequence lock read transaction, and the
29/// user has decided that it is better to use a thread fence than to use acquire
30/// semantics on each element transferred. Such a sequence might look something
31/// like this:
32///
33/// ```
34/// # use concurrent::{WellDefinedCopyable, SYNC_OPT_FENCE, SYNC_OPT_NONE};
35/// # use zerocopy::{FromBytes, Immutable, IntoBytes};
36/// # #[derive(Copy, Clone, Default, FromBytes, IntoBytes, Immutable)]
37/// # #[repr(C, align(8))]
38/// # struct Foo(u64);
39/// # #[derive(Copy, Clone, Default, FromBytes, IntoBytes, Immutable)]
40/// # #[repr(C, align(8))]
41/// # struct Bar(u64);
42/// # let src_foo1 = WellDefinedCopyable::new(Foo::default());
43/// # let src_foo2 = WellDefinedCopyable::new(Foo::default());
44/// # let src_bar1 = WellDefinedCopyable::new(Bar::default());
45/// # let src_bar2 = WellDefinedCopyable::new(Bar::default());
46/// let mut foo1 = Foo::default();
47/// let mut foo2 = Foo::default();
48/// let mut bar1 = Bar::default();
49/// let mut bar2 = Bar::default();
50/// // ...
51/// src_foo1.read::<SYNC_OPT_NONE>(&mut foo1);
52/// src_foo2.read::<SYNC_OPT_NONE>(&mut foo2);
53/// src_bar1.read::<SYNC_OPT_NONE>(&mut bar1);
54/// src_bar2.read::<SYNC_OPT_FENCE>(&mut bar2);
55/// ```
56///
57/// Note that it is the _last_ transfer operation which includes the fence. In
58/// the case of a `CopyTo` operation (when publishing data) it would be the _first_
59/// operation which included the fence, not the last.
60#[repr(u8)]
61#[derive(Clone, Copy, Debug, PartialEq, Eq)]
62pub enum SyncOpt {
63    AcqRelOps,
64    Fence,
65    None,
66}
67
68impl SyncOpt {
69    pub const fn from_u8(val: u8) -> Self {
70        match val {
71            SYNC_OPT_ACQ_REL_OPS => SyncOpt::AcqRelOps,
72            SYNC_OPT_FENCE => SyncOpt::Fence,
73            SYNC_OPT_NONE => SyncOpt::None,
74            _ => panic!("invalid SyncOpt discriminant"),
75        }
76    }
77}
78
79pub const SYNC_OPT_ACQ_REL_OPS: u8 = SyncOpt::AcqRelOps as u8;
80pub const SYNC_OPT_FENCE: u8 = SyncOpt::Fence as u8;
81pub const SYNC_OPT_NONE: u8 = SyncOpt::None as u8;
82
83#[cfg(test)]
84mod tests {
85    use super::*;
86
87    #[test]
88    fn test_from_u8() {
89        assert_eq!(SyncOpt::from_u8(SYNC_OPT_ACQ_REL_OPS), SyncOpt::AcqRelOps);
90        assert_eq!(SyncOpt::from_u8(SYNC_OPT_FENCE), SyncOpt::Fence);
91        assert_eq!(SyncOpt::from_u8(SYNC_OPT_NONE), SyncOpt::None);
92    }
93
94    #[test]
95    #[should_panic(expected = "invalid SyncOpt discriminant")]
96    fn test_from_u8_invalid() {
97        SyncOpt::from_u8(42);
98    }
99}