mirror of
https://github.com/oven-sh/bun
synced 2026-02-09 10:28:47 +00:00
* @min and @max * builtins and some trivial ones * Most of them * more * more! * More Progress * wip * Update tagged_pointer.zig * Update http_client_async.zig * Most of the iterable dir changes * alright * Remove usages of deprecated formatters * 📷 * fmt * Update shimmer.zig * wip * wip * wip * progress * more * Latest * stuck on error * latest * workaround stage2 * wip * Update string_immutable.zig * wip * Migrate `Dirent` and `require("fs')` to use JSC<>Zig bindings * Fix build errors * Fixup most of the test failures * Fix `make headers` * Fix "outside package path" error * Fixup aligned alloc * Add missing file * linux * More linux fixes * use latest peechy * Fix transpiler test failure * Forgot about these * Fixup test failure * Update node-timers.test.ts * [node:htt] Fix `undefined is not an object` error Fixes https://github.com/oven-sh/bun/issues/1618 * Update http.exports.js * Make this test less flaky * fix hashes * Fix hex formatting and zls issues * Download zig version * Update Dockerfile * Update Dockerfile * Update uws * Update Dockerfile * Set llvm version * Update README.md * Update uws * Update Dockerfile * Update io_linux.zig * Update bun.zig * Log output * workaround strange @cInclude error * Make ffi tests better * Don't use cImport * Update c.zig * Update c-bindings.cpp * call setOutputDir * Update Dockerfile * Use a longer name * latest * Update serve.test.ts Co-authored-by: Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com> Co-authored-by: Veikka Tuominen <git@vexu.eu>
121 lines
4.0 KiB
Zig
121 lines
4.0 KiB
Zig
const std = @import("std");
|
|
const Atomic = std.atomic.Atomic;
|
|
const Futex = @import("./futex.zig");
|
|
|
|
// Credit: this is copypasta from @kprotty. Thank you @kprotty!
|
|
pub const Mutex = struct {
|
|
state: Atomic(u32) = Atomic(u32).init(UNLOCKED),
|
|
|
|
const UNLOCKED = 0;
|
|
const LOCKED = 0b01;
|
|
const CONTENDED = 0b11;
|
|
const is_x86 = @import("builtin").target.cpu.arch.isX86();
|
|
|
|
pub fn tryAcquire(self: *Mutex) bool {
|
|
return self.acquireFast(true);
|
|
}
|
|
|
|
pub fn acquire(self: *Mutex) void {
|
|
if (!self.acquireFast(false)) {
|
|
self.acquireSlow();
|
|
}
|
|
}
|
|
|
|
inline fn acquireFast(self: *Mutex, comptime strong: bool) bool {
|
|
// On x86, "lock bts" uses less i-cache & can be faster than "lock cmpxchg" below.
|
|
if (comptime is_x86) {
|
|
return self.state.bitSet(@ctz(@as(u32, LOCKED)), .Acquire) == UNLOCKED;
|
|
}
|
|
|
|
const cas_fn = comptime switch (strong) {
|
|
true => "compareAndSwap",
|
|
else => "tryCompareAndSwap",
|
|
};
|
|
|
|
return @field(self.state, cas_fn)(
|
|
UNLOCKED,
|
|
LOCKED,
|
|
.Acquire,
|
|
.Monotonic,
|
|
) == null;
|
|
}
|
|
|
|
noinline fn acquireSlow(self: *Mutex) void {
|
|
// Spin a little bit on the Mutex state in the hopes that
|
|
// we can acquire it without having to call Futex.wait().
|
|
// Give up spinning if the Mutex is contended.
|
|
// This helps acquire() latency under micro-contention.
|
|
//
|
|
var spin: u8 = 100;
|
|
while (spin > 0) : (spin -= 1) {
|
|
std.atomic.spinLoopHint();
|
|
|
|
switch (self.state.load(.Monotonic)) {
|
|
UNLOCKED => _ = self.state.tryCompareAndSwap(
|
|
UNLOCKED,
|
|
LOCKED,
|
|
.Acquire,
|
|
.Monotonic,
|
|
) orelse return,
|
|
LOCKED => continue,
|
|
CONTENDED => break,
|
|
else => unreachable, // invalid Mutex state
|
|
}
|
|
}
|
|
|
|
// Make sure the state is CONTENDED before sleeping with Futex so release() can wake us up.
|
|
// Transitioning to CONTENDED may also acquire the mutex in the process.
|
|
//
|
|
// If we sleep, we must acquire the Mutex with CONTENDED to ensure that other threads
|
|
// sleeping on the Futex having seen CONTENDED before are eventually woken up by release().
|
|
// This unfortunately ends up in an extra Futex.wake() for the last thread but that's ok.
|
|
while (true) : (Futex.wait(&self.state, CONTENDED, null) catch unreachable) {
|
|
// On x86, "xchg" can be faster than "lock cmpxchg" below.
|
|
if (comptime is_x86) {
|
|
switch (self.state.swap(CONTENDED, .Acquire)) {
|
|
UNLOCKED => return,
|
|
LOCKED, CONTENDED => continue,
|
|
else => unreachable, // invalid Mutex state
|
|
}
|
|
}
|
|
|
|
var state = self.state.load(.Monotonic);
|
|
while (state != CONTENDED) {
|
|
state = switch (state) {
|
|
UNLOCKED => self.state.tryCompareAndSwap(state, CONTENDED, .Acquire, .Monotonic) orelse return,
|
|
LOCKED => self.state.tryCompareAndSwap(state, CONTENDED, .Monotonic, .Monotonic) orelse break,
|
|
CONTENDED => unreachable, // checked above
|
|
else => unreachable, // invalid Mutex state
|
|
};
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn release(self: *Mutex) void {
|
|
switch (self.state.swap(UNLOCKED, .Release)) {
|
|
UNLOCKED => unreachable, // released without being acquired
|
|
LOCKED => {},
|
|
CONTENDED => Futex.wake(&self.state, 1),
|
|
else => unreachable, // invalid Mutex state
|
|
}
|
|
}
|
|
};
|
|
|
|
pub const Lock = struct {
|
|
mutex: Mutex,
|
|
|
|
pub fn init() Lock {
|
|
return Lock{ .mutex = Mutex{} };
|
|
}
|
|
|
|
pub inline fn lock(this: *Lock) void {
|
|
this.mutex.acquire();
|
|
}
|
|
|
|
pub inline fn unlock(this: *Lock) void {
|
|
this.mutex.release();
|
|
}
|
|
};
|
|
|
|
pub fn spinCycle() void {}
|