1pub unsafe trait Copyable: Copy {}
32
33pub use zerocopy::IntoBytes;
35unsafe impl<T: Copy + IntoBytes> Copyable for T {}
36
37use crate::loom::sync::atomic::{AtomicUsize, Ordering, Ordering::Relaxed, fence};
38use std::marker::PhantomData;
39use std::mem::{MaybeUninit, size_of};
40
41pub struct SlotItem<T> {
42 cell: AtomicUsize,
43 _t: PhantomData<T>,
44}
45
46pub type Slot<T> = [SlotItem<T>];
47
48fn allocate<T>(n_items: usize) -> Box<[SlotItem<T>]> {
49 std::iter::repeat_with(|| SlotItem {
50 cell: AtomicUsize::new(0),
51 _t: PhantomData,
52 })
53 .take(n_items)
54 .collect::<Vec<_>>()
55 .into_boxed_slice()
56}
57
58const fn required_items<T>() -> usize {
59 size_of::<T>().div_ceil(size_of::<usize>())
60}
61
62pub struct AtomicSlotable;
63use super::Slotable;
64
65impl<T: Copyable> Slotable<T> for AtomicSlotable {
66 type Slot = Slot<T>;
67 type SlotArrayItem = SlotItem<T>;
68
69 fn boxed_uninit_single() -> Box<Self::Slot> {
70 allocate::<T>(required_items::<T>())
71 }
72
73 fn read(slot: &Self::Slot, ordering: Ordering) -> std::mem::MaybeUninit<T> {
74 let n = size_of::<T>() / size_of::<usize>();
75 let mut value = MaybeUninit::<T>::uninit();
76 let tgt = value.as_mut_ptr() as *mut usize;
77 for (i, src) in slot[0..n].iter().enumerate() {
78 let x = src.cell.load(Relaxed);
79 unsafe {
81 tgt.add(i).write_unaligned(x);
82 }
83 }
84 let remain = size_of::<T>() % size_of::<usize>();
85 if remain != 0 {
86 let part = slot[n].cell.load(Relaxed).to_ne_bytes();
87 unsafe {
89 tgt.add(n)
90 .cast::<u8>()
91 .copy_from_nonoverlapping(part.as_ptr(), remain);
92 }
93 }
94 if ordering != Relaxed {
95 fence(ordering);
96 }
97 value
98 }
99
100 fn write(slot: &Self::Slot, value: T, ordering: Ordering) {
101 if ordering != Relaxed {
102 fence(ordering);
103 }
104
105 let n = size_of::<T>() / size_of::<usize>();
106 let src = &value as *const T as *const usize;
107 for (i, tgt) in slot[0..n].iter().enumerate() {
108 let x = unsafe { src.add(i).read_unaligned() };
110 tgt.cell.store(x, Relaxed);
111 }
112 let remain = size_of::<T>() % size_of::<usize>();
113 if remain != 0 {
114 let mut part = [0u8; size_of::<usize>()];
115 unsafe {
117 part.as_mut_ptr()
118 .copy_from_nonoverlapping(src.add(n) as *const u8, remain);
119 }
120 slot[n].cell.store(usize::from_ne_bytes(part), Relaxed);
121 }
122 }
123
124 fn boxed_uninit_multiple(n: usize) -> Box<[Self::SlotArrayItem]> {
125 let req = required_items::<T>() * n;
126 allocate(req)
127 }
128
129 fn index_in_array(items: &[Self::SlotArrayItem], index: usize) -> &Self::Slot {
130 let n = required_items::<T>();
131 &items[index * n..(index + 1) * n]
132 }
133}
134
135crate::impl_channels!(crate::atomic::AtomicSlotable, crate::atomic::Copyable);
136
137#[cfg(test)]
138mod tests {
139 use super::*;
140
141 fn test_rw<T: Copyable + PartialEq + std::fmt::Debug>(value: T) {
142 let slot = AtomicSlotable::boxed_uninit_single();
143 AtomicSlotable::write(&slot, value, Ordering::Relaxed);
144 let readback: T = unsafe { AtomicSlotable::read(&slot, Ordering::Relaxed).assume_init() };
145 assert_eq!(readback, value);
146 }
147
148 #[test]
149 fn read_write_8() {
150 #[derive(Clone, Copy, PartialEq, Debug, IntoBytes)]
151 struct V(u64, u64, u32, u32);
152 test_rw(V(1, 2, 3, 4));
153 }
154
155 #[test]
156 fn read_write_4() {
157 #[derive(Clone, Copy, PartialEq, Debug, IntoBytes)]
158 struct V(u32, u32, u16, u16);
159 test_rw(V(1, 2, 3, 4));
160 }
161
162 #[test]
163 fn read_write_1() {
164 #[derive(Clone, Copy, PartialEq, Debug, IntoBytes)]
165 struct V(u8, u8);
166 test_rw(V(1, 2));
167 }
168}