use super::Attr;
use core::sync::atomic::{
fence, AtomicU8,
Ordering::{self, Acquire, Relaxed, Release},
};
#[repr(C)]
pub(crate) struct AtomicStatus {
stat: AtomicU8,
rcnt: AtomicU8,
wcnt: AtomicU8,
wexp: u8,
group: u8,
sext: u8,
worker: u16,
}
pub(crate) const STAT_RUN: u8 = 0;
pub(crate) const STAT_JOIN: u8 = 1;
pub(crate) const STAT_RETURN: u8 = 2;
pub(crate) const STAT_FINISH: u8 = 3;
pub(crate) const STAT_ABORT: u8 = 4;
pub(crate) const STAT_TIMEOUT: u8 = 5;
const PRI_SHIFT: u8 = 0;
const YIELD_SHIFT: u8 = 1;
impl AtomicStatus {
pub(crate) fn new(attr: &Attr) -> Self {
Self {
stat: AtomicU8::new(STAT_RUN),
rcnt: AtomicU8::new(1),
wcnt: AtomicU8::new(1),
wexp: 1,
group: attr.group_id,
sext: sext_new(0, PRI_SHIFT, attr.priority != 0),
worker: 0,
}
}
pub(crate) fn test_dec_wake(&self, cnt: u8) -> bool {
let old = self.wcnt.fetch_sub(cnt, Release);
(old - cnt) >= self.wexp
}
pub(crate) fn inc_wake(&self) {
self.wcnt.fetch_add(1, Relaxed);
}
pub(crate) fn test_inc_wake(&self) -> bool {
let cnt = self.wcnt.fetch_add(1, Acquire);
cnt == self.wexp - 1
}
pub(crate) fn wake_expect(&self) -> u8 {
assert!(self.wcnt.load(Relaxed) >= self.wexp);
self.wexp
}
pub(crate) fn set_wake_expect(&mut self, wexp: u8) {
assert!(wexp > 0);
self.wexp = wexp;
}
pub(crate) fn inc_ref(&self) {
let cnt = self.rcnt.fetch_add(1, Relaxed);
assert!(cnt < 254);
assert!(cnt > 0);
}
pub(crate) fn test_dec_ref(&self) -> bool {
if self.rcnt.fetch_sub(1, Release) == 1 {
fence(Acquire);
return true;
}
false
}
pub(crate) fn status(&self, order: Ordering) -> u8 {
self.stat.load(order)
}
pub(crate) fn set_status(&self, new: u8, order: Ordering) {
self.stat.store(new, order);
}
pub(crate) fn cmp_xchg_status(
&self,
current: u8,
new: u8,
set_order: Ordering,
) -> Result<u8, u8> {
self.stat.compare_exchange(current, new, set_order, Relaxed)
}
pub(crate) fn priority(&self) -> u8 {
if sext_get(self.sext, PRI_SHIFT) {
1
} else {
0
}
}
pub(crate) fn group(&self) -> u8 {
self.group
}
pub(crate) fn set_yield(&mut self) {
self.sext = sext_new(self.sext, YIELD_SHIFT, true);
}
pub(crate) fn is_yield(&self) -> bool {
sext_get(self.sext, YIELD_SHIFT)
}
pub(crate) fn get_local(&self) -> Option<u16> {
if (self.worker & 0x8000) > 0 {
return Some(self.worker & !0x8000);
}
None
}
pub(crate) fn set_local(&mut self, id: u16) {
self.worker = id | 0x8000;
}
}
fn sext_new(old: u8, shift: u8, val: bool) -> u8 {
if val {
old | (0x01 << shift)
} else {
old & !(0x01 << shift)
}
}
fn sext_get(sext: u8, shift: u8) -> bool {
(sext & (0x01 << shift)) != 0
}