#[cfg(not(windows))]
use core::sync::atomic::AtomicU32;
#[cfg(any(not(windows), debug_assertions))]
use core::sync::atomic::Ordering;
#[cfg(not(windows))]
use crate::Futex;
use crate::Mutex;
use crate::guarded::GuardedLock;
#[derive(Default)]
pub struct Condition {
impl_: Impl,
}
pub type Condvar = Condition;
#[derive(thiserror::Error, strum::IntoStaticStr, Debug, Clone, Copy, PartialEq, Eq)]
pub enum TimeoutError {
#[error("Timeout")]
Timeout,
}
impl From<TimeoutError> for bun_core::Error {
fn from(_: TimeoutError) -> Self {
bun_core::err!("Timeout")
}
}
impl Condition {
pub const fn new() -> Self {
Self { impl_: Impl::new() }
}
pub fn wait(&self, mutex: &Mutex) {
match self.impl_.wait(mutex, None) {
Ok(()) => {}
Err(TimeoutError::Timeout) => unreachable!(), }
}
pub fn timed_wait(&self, mutex: &Mutex, timeout_ns: u64) -> Result<(), TimeoutError> {
self.impl_.wait(mutex, Some(timeout_ns))
}
pub fn signal(&self) {
self.impl_.wake(Notify::One);
}
pub fn broadcast(&self) {
self.impl_.wake(Notify::All);
}
pub fn wait_guarded<T>(&self, guard: &mut GuardedLock<'_, T, Mutex>) {
self.wait(guard.mutex())
}
pub fn timed_wait_guarded<T>(
&self,
guard: &mut GuardedLock<'_, T, Mutex>,
timeout_ns: u64,
) -> Result<(), TimeoutError> {
self.timed_wait(guard.mutex(), timeout_ns)
}
#[inline]
pub fn notify_one(&self) {
self.signal()
}
#[inline]
pub fn notify_all(&self) {
self.broadcast()
}
}
#[cfg(windows)]
type Impl = WindowsImpl;
#[cfg(not(windows))]
type Impl = FutexImpl;
#[derive(PartialEq, Eq, Clone, Copy)]
enum Notify {
One, All, }
#[cfg(windows)]
mod windows_impl {
use super::*;
use bun_sys::windows;
use bun_sys::windows::kernel32;
use bun_core::time::NS_PER_MS;
#[link(name = "kernel32")]
unsafe extern "system" {
safe fn WakeConditionVariable(cv: &core::cell::UnsafeCell<windows::CONDITION_VARIABLE>);
safe fn WakeAllConditionVariable(cv: &core::cell::UnsafeCell<windows::CONDITION_VARIABLE>);
}
pub(super) struct WindowsImpl {
condition: core::cell::UnsafeCell<windows::CONDITION_VARIABLE>,
}
unsafe impl Sync for WindowsImpl {}
unsafe impl Send for WindowsImpl {}
impl Default for WindowsImpl {
fn default() -> Self {
Self::new()
}
}
impl WindowsImpl {
pub(super) const fn new() -> Self {
Self {
condition: core::cell::UnsafeCell::new(windows::CONDITION_VARIABLE_INIT),
}
}
pub(super) fn wait(&self, mutex: &Mutex, timeout: Option<u64>) -> Result<(), TimeoutError> {
let mut timeout_overflowed = false;
let mut timeout_ms: windows::DWORD = windows::INFINITE;
if let Some(timeout_ns) = timeout {
let ms = timeout_ns.saturating_add(NS_PER_MS / 2) / NS_PER_MS;
timeout_ms = windows::DWORD::try_from(ms).unwrap_or(windows::DWORD::MAX);
if timeout_ms == windows::INFINITE {
timeout_overflowed = true;
timeout_ms -= 1;
}
}
#[cfg(debug_assertions)]
{
mutex.impl_.locking_thread.store(0, Ordering::Relaxed);
}
let rc = unsafe {
kernel32::SleepConditionVariableSRW(
self.condition.get(),
#[cfg(debug_assertions)]
{
mutex.impl_.impl_.srwlock.get()
},
#[cfg(not(debug_assertions))]
{
mutex.impl_.srwlock.get()
},
timeout_ms,
0, )
};
#[cfg(debug_assertions)]
{
mutex
.impl_
.locking_thread
.store(crate::current_thread_id(), Ordering::Relaxed);
}
if rc == windows::FALSE {
debug_assert!(windows::GetLastError() == windows::Win32Error::TIMEOUT.0 as u32);
if !timeout_overflowed {
return Err(TimeoutError::Timeout);
}
}
Ok(())
}
pub(super) fn wake(&self, notify: Notify) {
match notify {
Notify::One => WakeConditionVariable(&self.condition),
Notify::All => WakeAllConditionVariable(&self.condition),
}
}
}
}
#[cfg(windows)]
use windows_impl::WindowsImpl;
#[cfg(not(windows))]
#[derive(Default)]
struct FutexImpl {
state: AtomicU32,
epoch: AtomicU32,
}
#[cfg(not(windows))]
impl FutexImpl {
const fn new() -> Self {
Self {
state: AtomicU32::new(0),
epoch: AtomicU32::new(0),
}
}
const ONE_WAITER: u32 = 1;
const WAITER_MASK: u32 = 0xffff;
const ONE_SIGNAL: u32 = 1 << 16;
const SIGNAL_MASK: u32 = 0xffff << 16;
fn wait(&self, mutex: &Mutex, timeout: Option<u64>) -> Result<(), TimeoutError> {
let mut epoch = self.epoch.load(Ordering::Acquire);
let mut state = self.state.fetch_add(Self::ONE_WAITER, Ordering::Relaxed);
debug_assert!(state & Self::WAITER_MASK != Self::WAITER_MASK);
state += Self::ONE_WAITER;
mutex.unlock();
scopeguard::defer! { mutex.lock(); }
let mut futex_deadline = Futex::Deadline::init(timeout);
loop {
match futex_deadline.wait(&self.epoch, epoch) {
Ok(()) => {}
Err(crate::futex::TimeoutError::Timeout) => {
loop {
while state & Self::SIGNAL_MASK != 0 {
let new_state = state - Self::ONE_WAITER - Self::ONE_SIGNAL;
state = match self.state.compare_exchange_weak(
state,
new_state,
Ordering::Acquire,
Ordering::Relaxed,
) {
Ok(_) => return Ok(()),
Err(s) => s,
};
}
let new_state = state - Self::ONE_WAITER;
state = match self.state.compare_exchange_weak(
state,
new_state,
Ordering::Relaxed,
Ordering::Relaxed,
) {
Ok(_) => return Err(TimeoutError::Timeout),
Err(s) => s,
};
}
}
}
epoch = self.epoch.load(Ordering::Acquire);
state = self.state.load(Ordering::Relaxed);
while state & Self::SIGNAL_MASK != 0 {
let new_state = state - Self::ONE_WAITER - Self::ONE_SIGNAL;
state = match self.state.compare_exchange_weak(
state,
new_state,
Ordering::Acquire,
Ordering::Relaxed,
) {
Ok(_) => return Ok(()),
Err(s) => s,
};
}
}
}
fn wake(&self, notify: Notify) {
let mut state = self.state.load(Ordering::Relaxed);
loop {
let waiters = (state & Self::WAITER_MASK) / Self::ONE_WAITER;
let signals = (state & Self::SIGNAL_MASK) / Self::ONE_SIGNAL;
let wakeable = waiters - signals;
if wakeable == 0 {
return;
}
let to_wake = match notify {
Notify::One => 1,
Notify::All => wakeable,
};
let new_state = state + (Self::ONE_SIGNAL * to_wake);
state = match self.state.compare_exchange_weak(
state,
new_state,
Ordering::Release,
Ordering::Relaxed,
) {
Ok(_) => {
let _ = self.epoch.fetch_add(1, Ordering::Release);
Futex::wake(&self.epoch, to_wake);
return;
}
Err(s) => s,
};
}
}
}