summaryrefslogtreecommitdiffstats
path: root/library/std/src/sys/xous/locks/condvar.rs
blob: 1bb38dfa341541f6622d9f534afc1111697f399e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
use super::mutex::Mutex;
use crate::os::xous::ffi::{blocking_scalar, scalar};
use crate::os::xous::services::ticktimer_server;
use crate::sync::Mutex as StdMutex;
use crate::time::Duration;

// The implementation is inspired by Andrew D. Birrell's paper
// "Implementing Condition Variables with Semaphores"

pub struct Condvar {
    counter: StdMutex<usize>,
}

unsafe impl Send for Condvar {}
unsafe impl Sync for Condvar {}

impl Condvar {
    #[inline]
    #[rustc_const_stable(feature = "const_locks", since = "1.63.0")]
    pub const fn new() -> Condvar {
        Condvar { counter: StdMutex::new(0) }
    }

    pub fn notify_one(&self) {
        let mut counter = self.counter.lock().unwrap();
        if *counter <= 0 {
            return;
        } else {
            *counter -= 1;
        }
        let result = blocking_scalar(
            ticktimer_server(),
            crate::os::xous::services::TicktimerScalar::NotifyCondition(self.index(), 1).into(),
        );
        drop(counter);
        result.expect("failure to send NotifyCondition command");
    }

    pub fn notify_all(&self) {
        let mut counter = self.counter.lock().unwrap();
        if *counter <= 0 {
            return;
        }
        let result = blocking_scalar(
            ticktimer_server(),
            crate::os::xous::services::TicktimerScalar::NotifyCondition(self.index(), *counter)
                .into(),
        );
        *counter = 0;
        drop(counter);

        result.expect("failure to send NotifyCondition command");
    }

    fn index(&self) -> usize {
        self as *const Condvar as usize
    }

    pub unsafe fn wait(&self, mutex: &Mutex) {
        let mut counter = self.counter.lock().unwrap();
        *counter += 1;
        unsafe { mutex.unlock() };
        drop(counter);

        let result = blocking_scalar(
            ticktimer_server(),
            crate::os::xous::services::TicktimerScalar::WaitForCondition(self.index(), 0).into(),
        );
        unsafe { mutex.lock() };

        result.expect("Ticktimer: failure to send WaitForCondition command");
    }

    pub unsafe fn wait_timeout(&self, mutex: &Mutex, dur: Duration) -> bool {
        let mut counter = self.counter.lock().unwrap();
        *counter += 1;
        unsafe { mutex.unlock() };
        drop(counter);

        let mut millis = dur.as_millis() as usize;
        if millis == 0 {
            millis = 1;
        }

        let result = blocking_scalar(
            ticktimer_server(),
            crate::os::xous::services::TicktimerScalar::WaitForCondition(self.index(), millis)
                .into(),
        );
        unsafe { mutex.lock() };

        let result = result.expect("Ticktimer: failure to send WaitForCondition command")[0] == 0;

        // If we awoke due to a timeout, decrement the wake count, as that would not have
        // been done in the `notify()` call.
        if !result {
            *self.counter.lock().unwrap() -= 1;
        }
        result
    }
}

impl Drop for Condvar {
    fn drop(&mut self) {
        scalar(
            ticktimer_server(),
            crate::os::xous::services::TicktimerScalar::FreeCondition(self.index()).into(),
        )
        .ok();
    }
}