| use crate::os::xous::ffi::{blocking_scalar, scalar}; |
| use crate::os::xous::services::{ticktimer_server, TicktimerScalar}; |
| use crate::pin::Pin; |
| use crate::ptr; |
| use crate::sync::atomic::{ |
| AtomicI8, |
| Ordering::{Acquire, Release}, |
| }; |
| use crate::time::Duration; |
| |
| const NOTIFIED: i8 = 1; |
| const EMPTY: i8 = 0; |
| const PARKED: i8 = -1; |
| |
| pub struct Parker { |
| state: AtomicI8, |
| } |
| |
| impl Parker { |
| pub unsafe fn new_in_place(parker: *mut Parker) { |
| unsafe { parker.write(Parker { state: AtomicI8::new(EMPTY) }) } |
| } |
| |
| fn index(&self) -> usize { |
| ptr::from_ref(self).addr() |
| } |
| |
| pub unsafe fn park(self: Pin<&Self>) { |
| // Change NOTIFIED to EMPTY and EMPTY to PARKED. |
| let state = self.state.fetch_sub(1, Acquire); |
| if state == NOTIFIED { |
| return; |
| } |
| |
| // The state was set to PARKED. Wait until the `unpark` wakes us up. |
| blocking_scalar( |
| ticktimer_server(), |
| TicktimerScalar::WaitForCondition(self.index(), 0).into(), |
| ) |
| .expect("failed to send WaitForCondition command"); |
| |
| self.state.swap(EMPTY, Acquire); |
| } |
| |
| pub unsafe fn park_timeout(self: Pin<&Self>, timeout: Duration) { |
| // Change NOTIFIED to EMPTY and EMPTY to PARKED. |
| let state = self.state.fetch_sub(1, Acquire); |
| if state == NOTIFIED { |
| return; |
| } |
| |
| // A value of zero indicates an indefinite wait. Clamp the number of |
| // milliseconds to the allowed range. |
| let millis = usize::max(timeout.as_millis().try_into().unwrap_or(usize::MAX), 1); |
| |
| let was_timeout = blocking_scalar( |
| ticktimer_server(), |
| TicktimerScalar::WaitForCondition(self.index(), millis).into(), |
| ) |
| .expect("failed to send WaitForCondition command")[0] |
| != 0; |
| |
| let state = self.state.swap(EMPTY, Acquire); |
| if was_timeout && state == NOTIFIED { |
| // The state was set to NOTIFIED after we returned from the wait |
| // but before we reset the state. Therefore, a wakeup is on its |
| // way, which we need to consume here. |
| // NOTICE: this is a priority hole. |
| blocking_scalar( |
| ticktimer_server(), |
| TicktimerScalar::WaitForCondition(self.index(), 0).into(), |
| ) |
| .expect("failed to send WaitForCondition command"); |
| } |
| } |
| |
| pub fn unpark(self: Pin<&Self>) { |
| let state = self.state.swap(NOTIFIED, Release); |
| if state == PARKED { |
| // The thread is parked, wake it up. |
| blocking_scalar( |
| ticktimer_server(), |
| TicktimerScalar::NotifyCondition(self.index(), 1).into(), |
| ) |
| .expect("failed to send NotifyCondition command"); |
| } |
| } |
| } |
| |
| impl Drop for Parker { |
| fn drop(&mut self) { |
| scalar(ticktimer_server(), TicktimerScalar::FreeCondition(self.index()).into()).ok(); |
| } |
| } |