#[cfg(not(any(windows, target_vendor = "apple")))]
use core::sync::atomic::AtomicU32;
#[cfg(debug_assertions)]
use core::sync::atomic::AtomicU64;
#[cfg(any(debug_assertions, not(any(windows, target_vendor = "apple"))))]
use core::sync::atomic::Ordering;
#[cfg(not(any(windows, target_vendor = "apple")))]
use crate::Futex;
#[derive(Default)]
pub struct Mutex {
pub(crate) impl_: Impl,
}
impl Mutex {
pub const fn new() -> Self {
Self { impl_: Impl::new() }
}
pub fn try_lock(&self) -> bool {
self.impl_.try_lock()
}
pub fn lock(&self) {
self.impl_.lock()
}
pub fn unlock(&self) {
unsafe { Self::unlock_raw(self) }
}
pub(crate) unsafe fn unlock_raw(this: *const Self) {
unsafe { Impl::unlock_raw(&raw const (*this).impl_) }
}
#[inline]
pub fn is_held_by_current_thread(&self) -> bool {
#[cfg(debug_assertions)]
{
self.impl_.locking_thread.load(Ordering::Relaxed) == current_thread_id()
}
#[cfg(not(debug_assertions))]
{
true
}
}
#[inline]
#[must_use = "the mutex unlocks immediately if the guard is dropped"]
pub fn lock_guard(&self) -> MutexGuard {
self.lock();
MutexGuard {
mutex: bun_ptr::BackRef::new(self),
_not_send: core::marker::PhantomData,
}
}
}
pub struct MutexGuard {
mutex: bun_ptr::BackRef<Mutex>,
_not_send: core::marker::PhantomData<*const Mutex>,
}
impl Drop for MutexGuard {
#[inline]
fn drop(&mut self) {
self.mutex.unlock()
}
}
#[cfg(debug_assertions)]
type Impl = DebugImpl;
#[cfg(not(debug_assertions))]
type Impl = ReleaseImpl;
#[cfg(windows)]
pub type ReleaseImpl = WindowsImpl;
#[cfg(target_vendor = "apple")]
pub type ReleaseImpl = DarwinImpl;
#[cfg(not(any(windows, target_vendor = "apple")))]
pub type ReleaseImpl = FutexImpl;
#[cfg(windows)]
#[allow(dead_code)]
pub(crate) type ExternImpl = bun_sys::windows::SRWLOCK;
#[cfg(not(any(windows, target_vendor = "apple")))]
#[allow(dead_code)]
pub(crate) type ExternImpl = u32;
#[cfg(debug_assertions)]
type ThreadId = u64;
#[cfg(debug_assertions)]
#[inline]
fn current_thread_id() -> ThreadId {
crate::current_thread_id()
}
#[cfg(debug_assertions)]
#[derive(Default)]
pub(crate) struct DebugImpl {
pub(crate) locking_thread: AtomicU64,
pub(crate) impl_: ReleaseImpl,
}
#[cfg(debug_assertions)]
impl DebugImpl {
pub(crate) const fn new() -> Self {
Self {
locking_thread: AtomicU64::new(0),
impl_: ReleaseImpl::new(),
}
}
#[inline]
fn try_lock(&self) -> bool {
let locking = self.impl_.try_lock();
if locking {
self.locking_thread
.store(current_thread_id(), Ordering::Relaxed);
}
locking
}
#[inline]
fn lock(&self) {
let current_id = current_thread_id();
if self.locking_thread.load(Ordering::Relaxed) == current_id && current_id != 0 {
panic!("Deadlock detected");
}
self.impl_.lock();
self.locking_thread.store(current_id, Ordering::Relaxed);
}
#[inline]
unsafe fn unlock_raw(this: *const Self) {
unsafe {
debug_assert!((*this).locking_thread.load(Ordering::Relaxed) == current_thread_id());
(*this).locking_thread.store(0, Ordering::Relaxed);
ReleaseImpl::unlock_raw(&raw const (*this).impl_);
}
}
}
#[cfg(windows)]
#[derive(Default)]
pub struct WindowsImpl {
pub(crate) srwlock: core::cell::UnsafeCell<bun_sys::windows::SRWLOCK>,
}
#[cfg(windows)]
unsafe impl Sync for WindowsImpl {}
#[cfg(windows)]
unsafe impl Send for WindowsImpl {}
#[cfg(windows)]
#[link(name = "kernel32")]
unsafe extern "system" {
safe fn AcquireSRWLockExclusive(lock: &core::cell::UnsafeCell<bun_sys::windows::SRWLOCK>);
safe fn TryAcquireSRWLockExclusive(
lock: &core::cell::UnsafeCell<bun_sys::windows::SRWLOCK>,
) -> u8;
}
#[cfg(windows)]
impl WindowsImpl {
pub(crate) const fn new() -> Self {
Self {
srwlock: core::cell::UnsafeCell::new(bun_sys::windows::SRWLOCK_INIT),
}
}
fn try_lock(&self) -> bool {
TryAcquireSRWLockExclusive(&self.srwlock) != 0
}
fn lock(&self) {
AcquireSRWLockExclusive(&self.srwlock)
}
unsafe fn unlock_raw(this: *const Self) {
unsafe {
let srwlock = core::cell::UnsafeCell::raw_get(&raw const (*this).srwlock);
bun_sys::windows::kernel32::ReleaseSRWLockExclusive(srwlock)
}
}
}
#[cfg(target_vendor = "apple")]
#[derive(Default)]
pub struct DarwinImpl {
oul: core::cell::UnsafeCell<OsUnfairLock>,
}
#[cfg(target_vendor = "apple")]
unsafe impl Sync for DarwinImpl {}
#[cfg(target_vendor = "apple")]
unsafe impl Send for DarwinImpl {}
#[cfg(target_vendor = "apple")]
#[repr(C)]
#[derive(Default)]
pub(crate) struct OsUnfairLock {
_opaque: u32,
}
#[cfg(target_vendor = "apple")]
unsafe extern "C" {
safe fn os_unfair_lock_trylock(lock: &core::cell::UnsafeCell<OsUnfairLock>) -> bool;
safe fn os_unfair_lock_lock(lock: &core::cell::UnsafeCell<OsUnfairLock>);
fn os_unfair_lock_unlock(lock: *mut OsUnfairLock);
}
#[cfg(target_vendor = "apple")]
impl DarwinImpl {
pub(crate) const fn new() -> Self {
Self {
oul: core::cell::UnsafeCell::new(OsUnfairLock { _opaque: 0 }),
}
}
fn try_lock(&self) -> bool {
os_unfair_lock_trylock(&self.oul)
}
fn lock(&self) {
os_unfair_lock_lock(&self.oul)
}
unsafe fn unlock_raw(this: *const Self) {
unsafe { os_unfair_lock_unlock(core::cell::UnsafeCell::raw_get(&raw const (*this).oul)) }
}
}
#[cfg(not(any(windows, target_vendor = "apple")))]
#[derive(Default)]
pub struct FutexImpl {
state: AtomicU32,
}
#[cfg(not(any(windows, target_vendor = "apple")))]
impl FutexImpl {
pub(crate) const fn new() -> Self {
Self {
state: AtomicU32::new(0),
}
}
const UNLOCKED: u32 = 0b00;
const LOCKED: u32 = 0b01;
const CONTENDED: u32 = 0b11;
fn lock(&self) {
if !self.try_lock() {
self.lock_slow();
}
}
fn try_lock(&self) -> bool {
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
{
let locked_bit: u32 = Self::LOCKED.trailing_zeros();
return (self.state.fetch_or(1 << locked_bit, Ordering::Acquire) & (1 << locked_bit))
== 0;
}
#[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))]
{
self.state
.compare_exchange_weak(
Self::UNLOCKED,
Self::LOCKED,
Ordering::Acquire,
Ordering::Relaxed,
)
.is_ok()
}
}
#[cold]
fn lock_slow(&self) {
if self.state.load(Ordering::Relaxed) == Self::CONTENDED {
Futex::wait_forever(&self.state, Self::CONTENDED);
}
while self.state.swap(Self::CONTENDED, Ordering::Acquire) != Self::UNLOCKED {
Futex::wait_forever(&self.state, Self::CONTENDED);
}
}
unsafe fn unlock_raw(this: *const Self) {
let state_ptr = unsafe { &raw const (*this).state };
let state = unsafe { (*state_ptr).swap(Self::UNLOCKED, Ordering::Release) };
debug_assert!(state != Self::UNLOCKED);
if state == Self::CONTENDED {
Futex::wake_raw(state_ptr, 1);
}
}
}
#[unsafe(no_mangle)]
pub(crate) unsafe extern "C" fn Bun__lock(ptr: *mut ReleaseImpl) {
unsafe { (*ptr).lock() }
}
#[unsafe(no_mangle)]
pub(crate) unsafe extern "C" fn Bun__unlock(ptr: *mut ReleaseImpl) {
unsafe { ReleaseImpl::unlock_raw(ptr) }
}
#[unsafe(no_mangle)]
pub(crate) static Bun__lock__size: usize = core::mem::size_of::<ReleaseImpl>();