Files
bun.sh/src/threading/guarded.zig
taylor.fish 3d361c8b49 Static allocator polymorphism (#22227)
* Define a generic allocator interface to enable static polymorphism for
allocators (see `GenericAllocator` in `src/allocators.zig`). Note that
`std.mem.Allocator` itself is considered a generic allocator.
* Add utilities to `bun.allocators` for working with generic allocators.
* Add a new namespace, `bun.memory`, with basic utilities for working
with memory and objects (`create`, `destroy`, `initDefault`, `deinit`).
* Add `bun.DefaultAllocator`, a zero-sized generic allocator type whose
`allocator` method simply returns `bun.default_allocator`.
* Implement the generic allocator interface in `AllocationScope` and
`MimallocArena`.
* Improve `bun.threading.GuardedValue` (now `bun.threading.Guarded`).
* Improve `bun.safety.AllocPtr` (now `bun.safety.CheckedAllocator`).

(For internal tracking: fixes STAB-1085, STAB-1086, STAB-1087,
STAB-1088, STAB-1089, STAB-1090, STAB-1091)
2025-09-03 15:40:44 -07:00

73 lines
2.5 KiB
Zig

/// A wrapper around a mutex, and a value protected by the mutex.
/// This type uses `bun.threading.Mutex` internally.
pub fn Guarded(comptime Value: type) type {
return GuardedBy(Value, bun.threading.Mutex);
}
/// A wrapper around a mutex, and a value protected by the mutex.
/// `Mutex` should have `lock` and `unlock` methods.
pub fn GuardedBy(comptime Value: type, comptime Mutex: type) type {
return struct {
const Self = @This();
/// The raw value. Don't use this if there might be concurrent accesses.
unsynchronized_value: Value,
#mutex: Mutex,
/// Creates a guarded value with a default-initialized mutex.
pub fn init(value: Value) Self {
return .initWithMutex(value, bun.memory.initDefault(Mutex));
}
/// Creates a guarded value with the given mutex.
pub fn initWithMutex(value: Value, mutex: Mutex) Self {
return .{
.unsynchronized_value = value,
.#mutex = mutex,
};
}
/// Locks the mutex and returns a pointer to the value. Remember to call `unlock`!
pub fn lock(self: *Self) *Value {
self.#mutex.lock();
return &self.unsynchronized_value;
}
/// Unlocks the mutex. Don't use any pointers returned by `lock` after calling this method!
pub fn unlock(self: *Self) void {
self.#mutex.unlock();
}
/// Returns the inner unprotected value.
///
/// You must ensure that no other threads could be concurrently using `self`. This method
/// invalidates `self`, so you must ensure `self` is not used on any thread after calling
/// this method.
pub fn intoUnprotected(self: *Self) Value {
defer self.* = undefined;
bun.memory.deinit(&self.#mutex);
return self.unsynchronized_value;
}
/// Deinitializes the inner value and mutex.
///
/// You must ensure that no other threads could be concurrently using `self`. This method
/// invalidates `self`.
///
/// If neither `Value` nor `Mutex` has a `deinit` method, it is not necessary to call this
/// method.
pub fn deinit(self: *Self) void {
bun.memory.deinit(&self.unsynchronized_value);
bun.memory.deinit(&self.#mutex);
self.* = undefined;
}
};
}
/// Uses `bun.safety.ThreadLock`.
pub fn Debug(comptime Value: type) type {
return GuardedBy(Value, bun.safety.ThreadLock);
}
const bun = @import("bun");