irox_tools/sync/
exchange.rs

1// SPDX-License-Identifier: MIT
2// Copyright 2025 IROX Contributors
3//
4
5extern crate alloc;
6
7use alloc::sync::Arc;
8use core::sync::atomic::{AtomicBool, Ordering};
9use std::sync::Mutex;
10
11///
12/// Faster exchange operation than a simple mutex that uses a flag to indicate if new data is available.
13pub struct Exchanger<T> {
14    new_data: Arc<AtomicBool>,
15    data: Arc<Mutex<Option<T>>>,
16}
17impl<T> Default for Exchanger<T> {
18    fn default() -> Exchanger<T> {
19        Self {
20            new_data: Arc::new(AtomicBool::new(false)),
21            data: Arc::new(Mutex::new(None)),
22        }
23    }
24}
25impl<T> Clone for Exchanger<T> {
26    fn clone(&self) -> Exchanger<T> {
27        Self {
28            new_data: self.new_data.clone(),
29            data: self.data.clone(),
30        }
31    }
32}
33impl<T> Exchanger<T> {
34    /// Returns the value of the new data available flag
35    pub fn new_data_available(&self) -> bool {
36        self.new_data.load(Ordering::Relaxed)
37    }
38    /// Assert the new data available flag
39    pub fn set_data_changed(&self) {
40        self.new_data.store(true, Ordering::Relaxed);
41    }
42
43    /// Replaces any data in the exchanger with the provided data.  Asserts the data has been changed.
44    pub fn replace_data(&self, new_data: T) -> Option<T> {
45        let mut old = None;
46        if let Ok(mut lock) = self.data.lock() {
47            old = lock.replace(new_data);
48        }
49        self.new_data.store(true, Ordering::Relaxed);
50        old
51    }
52    /// Takes any data within the exchanger.  Deasserts the new data flag.
53    pub fn take_data(&self) -> Option<T> {
54        if !self.new_data_available() {
55            return None;
56        }
57        self.new_data.store(false, Ordering::Relaxed);
58        if let Ok(mut lock) = self.data.lock() {
59            return lock.take();
60        }
61        None
62    }
63}