Files
RedBear-OS/src/sync/cond.rs
T
Red Bear OS 202e3eb500 relibc: implement pthread_cond_init with monotonic clock
Added clock: u8 field to Cond struct that stores the clock
setting (CLOCK_REALTIME or CLOCK_MONOTONIC) per pthread_condattr.
pthread_cond_init now properly sets the clock attribute.
timedwait uses the stored clock instead of hardcoding CLOCK_REALTIME.

Previously pthread_cond_init with CLOCK_MONOTONIC would log TODO
and use the wrong clock, causing condition variable timeouts to
behave incorrectly.

pthread_cond_t size updated from 8 to 12 bytes to accommodate
the new field. Cross-referenced with POSIX pthread_cond_init(3)
and Linux glibc nptl pthread_cond_init.c.
2026-07-08 19:58:46 +03:00

148 lines
4.5 KiB
Rust

// Used design from https://www.remlab.net/op/futex-condvar.shtml
use crate::{
error::Errno,
header::{
bits_timespec::timespec,
errno::{EINVAL, ETIMEDOUT},
pthread::*,
time::{CLOCK_MONOTONIC, CLOCK_REALTIME, timespec_realtime_to_monotonic},
},
platform::types::clockid_t,
};
use core::sync::atomic::{AtomicU32 as AtomicUint, Ordering};
#[derive(Clone, Copy)]
pub struct CondAttr {
pub clock: clockid_t,
pub pshared: i32,
}
impl Default for CondAttr {
fn default() -> Self {
Self {
// defaults according to POSIX
clock: CLOCK_REALTIME, // for timedwait
pshared: PTHREAD_PROCESS_PRIVATE, // TODO
}
}
}
pub struct Cond {
pub cur: AtomicUint,
pub prev: AtomicUint,
/// Clock used for timedwait, per pthread_condattr_setclock.
/// CLOCK_REALTIME (0) or CLOCK_MONOTONIC (1). Set once at init.
pub clock: u8,
/// Padding to maintain 12-byte struct size for ABI compatibility.
pub _pad: [u8; 3],
}
type Result<T, E = Errno> = core::result::Result<T, E>;
impl Default for Cond {
fn default() -> Self {
Self::new()
}
}
impl Cond {
pub fn new() -> Self {
Self {
cur: AtomicUint::new(0),
prev: AtomicUint::new(0),
clock: CLOCK_REALTIME as u8,
_pad: [0; 3],
}
}
fn wake(&self, count: i32) -> Result<(), Errno> {
// This is formally correct as long as we don't have more than u32::MAX threads.
let prev = self.prev.load(Ordering::Relaxed);
self.cur.store(prev.wrapping_add(1), Ordering::Relaxed);
crate::sync::futex_wake(&self.cur, count);
Ok(())
}
pub fn broadcast(&self) -> Result<(), Errno> {
self.wake(i32::MAX)
}
pub fn signal(&self) -> Result<(), Errno> {
// POSIX requires pthread_cond_signal to wake AT LEAST ONE waiter that
// is currently waiting on the condition variable, but it must not
// wake all waiters (that is pthread_cond_broadcast semantics).
// Wake exactly one via FUTEX_WAKE with count=1. Using broadcast() here
// was a thundering-herd bug: every cond_signal woke every waiter on
// every CPU. Fixed 2026-07-02 (Red Bear OS multi-threading plan,
// Phase 0a).
self.wake(1)
}
pub fn clockwait(
&self,
mutex: &RlctMutex,
timeout: &timespec,
clock_id: clockid_t,
) -> Result<(), Errno> {
let relative = match clock_id {
// FUTEX expect monotonic clock
CLOCK_MONOTONIC => timeout.clone(),
CLOCK_REALTIME => timespec_realtime_to_monotonic(timeout.clone())?,
_ => return Err(Errno(EINVAL)),
};
self.wait_inner(mutex, Some(&relative))
}
pub fn timedwait(&self, mutex: &RlctMutex, timeout: &timespec) -> Result<(), Errno> {
let clock_id = self.clock as clockid_t;
self.clockwait(mutex, timeout, clock_id)
}
fn wait_inner(&self, mutex: &RlctMutex, timeout: Option<&timespec>) -> Result<(), Errno> {
self.wait_inner_generic(|| mutex.unlock(), || mutex.lock(), timeout)
}
pub fn wait_inner_typedmutex<'lock, T>(
&self,
guard: crate::sync::MutexGuard<'lock, T>,
) -> crate::sync::MutexGuard<'lock, T> {
let mut newguard = None;
let lock = guard.mutex;
self.wait_inner_generic(
move || {
drop(guard);
Ok(())
},
|| {
newguard = Some(lock.lock());
Ok(())
},
None,
)
.unwrap();
newguard.unwrap()
}
// TODO: FUTEX_REQUEUE
fn wait_inner_generic(
&self,
unlock: impl FnOnce() -> Result<()>,
lock: impl FnOnce() -> Result<()>,
deadline: Option<&timespec>,
) -> Result<(), Errno> {
// TODO: Error checking for certain types (i.e. robust and errorcheck) of mutexes, e.g. if the
// mutex is not locked.
let current = self.cur.load(Ordering::Relaxed);
self.prev.store(current, Ordering::Relaxed);
unlock()?;
let futex_r = crate::sync::futex_wait(&self.cur, current, deadline);
lock()?;
match futex_r {
super::FutexWaitResult::Waited => Ok(()),
super::FutexWaitResult::Stale => Ok(()),
super::FutexWaitResult::TimedOut => Err(Errno(ETIMEDOUT)),
}
}
pub fn wait(&self, mutex: &RlctMutex) -> Result<(), Errno> {
self.wait_inner(mutex, None)
}
}