ph_eventing/
event_flags.rs1use core::cell::Cell;
19use core::marker::PhantomData;
20use core::ops::{BitAnd, BitOr, BitOrAssign};
21
22use crate::sync::{AtomicBool, AtomicU32, Ordering};
23
24#[derive(Clone, Copy, Default, Eq, Hash, PartialEq)]
30#[repr(transparent)]
31#[must_use]
32pub struct EventMask(u32);
33
34impl EventMask {
35 pub const EMPTY: Self = Self(0);
37
38 pub const ALL: Self = Self(u32::MAX);
40
41 #[inline(always)]
43 pub const fn from_bits(bits: u32) -> Self {
44 Self(bits)
45 }
46
47 #[inline(always)]
52 #[must_use]
53 pub const fn from_index(index: u32) -> Option<Self> {
54 if index < u32::BITS {
55 Some(Self(1u32 << index))
56 } else {
57 None
58 }
59 }
60
61 #[inline(always)]
63 #[must_use]
64 pub const fn bits(self) -> u32 {
65 self.0
66 }
67
68 #[inline(always)]
70 #[must_use]
71 pub const fn is_empty(self) -> bool {
72 self.0 == 0
73 }
74
75 #[inline(always)]
77 #[must_use]
78 pub const fn contains(self, other: Self) -> bool {
79 self.0 & other.0 == other.0
80 }
81
82 #[inline(always)]
84 #[must_use]
85 pub const fn intersects(self, other: Self) -> bool {
86 self.0 & other.0 != 0
87 }
88}
89
90impl BitOr for EventMask {
91 type Output = Self;
92
93 #[inline(always)]
94 fn bitor(self, rhs: Self) -> Self::Output {
95 Self(self.0 | rhs.0)
96 }
97}
98
99impl BitOrAssign for EventMask {
100 #[inline(always)]
101 fn bitor_assign(&mut self, rhs: Self) {
102 self.0 |= rhs.0;
103 }
104}
105
106impl BitAnd for EventMask {
107 type Output = Self;
108
109 #[inline(always)]
110 fn bitand(self, rhs: Self) -> Self::Output {
111 Self(self.0 & rhs.0)
112 }
113}
114
115impl core::fmt::Debug for EventMask {
116 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
117 write!(f, "EventMask({:#010x})", self.0)
118 }
119}
120
121pub struct EventFlags {
127 pending: AtomicU32,
128 producer_taken: AtomicBool,
129 consumer_taken: AtomicBool,
130}
131
132impl EventFlags {
133 #[cfg(not(loom))]
135 #[must_use]
136 pub const fn new() -> Self {
137 Self {
138 pending: AtomicU32::new(0),
139 producer_taken: AtomicBool::new(false),
140 consumer_taken: AtomicBool::new(false),
141 }
142 }
143
144 #[cfg(loom)]
146 #[must_use]
147 pub fn new() -> Self {
148 Self {
149 pending: AtomicU32::new(0),
150 producer_taken: AtomicBool::new(false),
151 consumer_taken: AtomicBool::new(false),
152 }
153 }
154
155 #[inline]
159 pub fn try_producer(&self) -> Option<Producer<'_>> {
160 if self.producer_taken.swap(true, Ordering::AcqRel) {
161 None
162 } else {
163 Some(Producer {
164 flags: self,
165 _not_sync: PhantomData,
166 })
167 }
168 }
169
170 #[inline]
174 pub fn try_consumer(&self) -> Option<Consumer<'_>> {
175 if self.consumer_taken.swap(true, Ordering::AcqRel) {
176 None
177 } else {
178 Some(Consumer {
179 flags: self,
180 _not_sync: PhantomData,
181 })
182 }
183 }
184}
185
186impl Default for EventFlags {
187 fn default() -> Self {
188 Self::new()
189 }
190}
191
192impl core::fmt::Debug for EventFlags {
198 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
199 f.debug_struct("EventFlags").finish_non_exhaustive()
200 }
201}
202
203pub struct Producer<'a> {
215 flags: &'a EventFlags,
216 _not_sync: PhantomData<Cell<()>>,
217}
218
219impl Producer<'_> {
220 #[inline]
234 pub fn raise(&self, mask: EventMask) {
235 self.flags.pending.fetch_or(mask.bits(), Ordering::Release);
236 }
237}
238
239impl Drop for Producer<'_> {
240 fn drop(&mut self) {
241 self.flags.producer_taken.store(false, Ordering::Release);
242 }
243}
244
245impl core::fmt::Debug for Producer<'_> {
246 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
247 f.debug_struct("event_flags::Producer").finish()
248 }
249}
250
251pub struct Consumer<'a> {
263 flags: &'a EventFlags,
264 _not_sync: PhantomData<Cell<()>>,
265}
266
267impl Consumer<'_> {
268 #[inline]
273 pub fn take_all(&self) -> EventMask {
274 EventMask::from_bits(self.flags.pending.swap(0, Ordering::Acquire))
275 }
276}
277
278impl Drop for Consumer<'_> {
279 fn drop(&mut self) {
280 self.flags.consumer_taken.store(false, Ordering::Release);
281 }
282}
283
284impl core::fmt::Debug for Consumer<'_> {
285 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
286 f.debug_struct("event_flags::Consumer").finish()
287 }
288}
289
290#[cfg(test)]
291mod tests {
292 use super::*;
293
294 const DATA_READY: EventMask = EventMask::from_bits(1 << 0);
295 const OVERFLOW: EventMask = EventMask::from_bits(1 << 1);
296
297 #[test]
298 fn event_flags_object_is_eight_bytes() {
299 assert_eq!(core::mem::size_of::<EventFlags>(), 8);
302 assert_eq!(core::mem::align_of::<EventFlags>(), 4);
303 }
304
305 #[test]
306 fn event_mask_is_an_explicit_panic_free_32_bit_set() {
307 assert_eq!(core::mem::size_of::<EventMask>(), 4);
309 assert!(EventMask::EMPTY.is_empty());
310 assert_eq!(EventMask::ALL.bits(), u32::MAX);
311 assert_eq!(EventMask::from_index(0), Some(DATA_READY));
312 assert_eq!(EventMask::from_index(31).unwrap().bits(), 1 << 31);
313 assert_eq!(EventMask::from_index(32), None);
314 assert_eq!(EventMask::from_index(u32::MAX), None);
315
316 let both = DATA_READY | OVERFLOW;
317 assert!(both.contains(DATA_READY));
318 assert!(both.intersects(OVERFLOW));
319 assert_eq!((both & OVERFLOW).bits(), OVERFLOW.bits());
320 }
321
322 #[test]
323 fn duplicate_raises_coalesce_and_take_clears() {
324 let flags = EventFlags::new();
326 let producer = flags.try_producer().unwrap();
327 let consumer = flags.try_consumer().unwrap();
328
329 producer.raise(DATA_READY);
330 producer.raise(DATA_READY);
331
332 assert_eq!(consumer.take_all(), DATA_READY);
333 assert_eq!(consumer.take_all(), EventMask::EMPTY);
334 }
335
336 #[test]
337 fn multi_bit_and_all_bit_masks_round_trip() {
338 let flags = EventFlags::new();
340 let producer = flags.try_producer().unwrap();
341 let consumer = flags.try_consumer().unwrap();
342
343 producer.raise(DATA_READY | OVERFLOW);
344 assert_eq!(consumer.take_all(), DATA_READY | OVERFLOW);
345 producer.raise(EventMask::ALL);
346 assert_eq!(consumer.take_all(), EventMask::ALL);
347 }
348
349 #[test]
350 fn empty_raise_and_empty_take_are_no_ops() {
351 let flags = EventFlags::new();
353 let producer = flags.try_producer().unwrap();
354 let consumer = flags.try_consumer().unwrap();
355
356 producer.raise(EventMask::EMPTY);
357 assert!(consumer.take_all().is_empty());
358 }
359
360 #[test]
361 fn handles_are_exclusive_and_reusable_after_drop() {
362 let flags = EventFlags::new();
364 let producer = flags.try_producer().unwrap();
365 let consumer = flags.try_consumer().unwrap();
366 assert!(flags.try_producer().is_none());
367 assert!(flags.try_consumer().is_none());
368
369 producer.raise(DATA_READY);
370 drop(producer);
371 drop(consumer);
372
373 let producer = flags.try_producer().expect("producer role released");
374 let consumer = flags.try_consumer().expect("consumer role released");
375 assert_eq!(consumer.take_all(), DATA_READY);
376 producer.raise(OVERFLOW);
377 assert_eq!(consumer.take_all(), OVERFLOW);
378 }
379
380 #[test]
381 fn handles_are_send_and_container_is_sync() {
382 fn assert_send<T: Send>() {}
384 fn assert_sync<T: Sync>() {}
385 assert_send::<Producer<'static>>();
386 assert_send::<Consumer<'static>>();
387 assert_sync::<EventFlags>();
388 }
389
390 #[cfg(not(loom))]
391 #[test]
392 fn const_new_works_in_static_context() {
393 static FLAGS: EventFlags = EventFlags::new();
395 let producer = FLAGS.try_producer().unwrap();
396 let consumer = FLAGS.try_consumer().unwrap();
397 producer.raise(DATA_READY);
398 assert_eq!(consumer.take_all(), DATA_READY);
399 }
400
401 #[cfg(not(loom))]
402 #[test]
403 fn concurrent_raise_and_take_never_loses_the_condition() {
404 use core::sync::atomic::{AtomicBool, Ordering as CoreOrdering};
406
407 let flags = EventFlags::new();
408 let producer = flags.try_producer().unwrap();
409 let consumer = flags.try_consumer().unwrap();
410 let done = AtomicBool::new(false);
411
412 let seen = std::thread::scope(|scope| {
413 let done_for_producer = &done;
414 scope.spawn(move || {
415 for _ in 0..crate::test_support::iterations(100_000) {
416 producer.raise(DATA_READY);
417 }
418 done_for_producer.store(true, CoreOrdering::Release);
419 });
420
421 let done_for_consumer = &done;
422 let taker = scope.spawn(move || {
423 let mut seen = EventMask::EMPTY;
424 while !done_for_consumer.load(CoreOrdering::Acquire) {
425 seen |= consumer.take_all();
426 std::thread::yield_now();
427 }
428 seen | consumer.take_all()
429 });
430
431 taker.join().unwrap()
432 });
433
434 assert_eq!(seen, DATA_READY);
435 }
436
437 #[cfg(not(loom))]
438 #[test]
439 fn observed_raise_publishes_preceding_memory() {
440 use core::sync::atomic::{AtomicU32, Ordering as CoreOrdering};
442
443 let flags = EventFlags::new();
444 let producer = flags.try_producer().unwrap();
445 let consumer = flags.try_consumer().unwrap();
446 let payload = AtomicU32::new(0);
447
448 std::thread::scope(|scope| {
449 let payload_for_producer = &payload;
450 scope.spawn(move || {
451 payload_for_producer.store(0xA5A5_5A5A, CoreOrdering::Relaxed);
452 producer.raise(DATA_READY);
453 });
454
455 while !consumer.take_all().contains(DATA_READY) {
456 std::thread::yield_now();
457 }
458 assert_eq!(payload.load(CoreOrdering::Relaxed), 0xA5A5_5A5A);
459 });
460 }
461}