|
const std = @import("../std.zig"); const builtin = @import("builtin"); const assert = std.debug.assert; const testing = std.testing; const mem = std.mem; const Loop = std.event.Loop; |
LockThread-safe async/await lock. Functions which are waiting for the lock are suspended, and are resumed when the lock is released, in order. Allows only one actor to hold the lock. TODO: make this API also work in blocking I/O mode. |
pub const Lock = struct { mutex: std.Thread.Mutex = std.Thread.Mutex{}, head: usize = UNLOCKED, const UNLOCKED = 0; const LOCKED = 1; const global_event_loop = Loop.instance orelse @compileError("std.event.Lock currently only works with event-based I/O"); const Waiter = struct { // forced Waiter alignment to ensure it doesn't clash with LOCKED next: ?*Waiter align(2), tail: *Waiter, node: Loop.NextTickNode, }; |
initLocked() |
pub fn initLocked() Lock { return Lock{ .head = LOCKED }; } |
acquire() |
pub fn acquire(self: *Lock) Held { self.mutex.lock(); // self.head transitions from multiple stages depending on the value: // UNLOCKED -> LOCKED: // acquire Lock ownership when there are no waiters // LOCKED -> |
release() |
pub fn release(self: Held) void { const waiter = blk: { self.lock.mutex.lock(); defer self.lock.mutex.unlock(); // self.head goes through the reverse transition from acquire(): // -> |
Test:std.event.Lock |
test "std.event.Lock" { if (!std.io.is_async) return error.SkipZigTest; // TODO https://github.com/ziglang/zig/issues/1908 if (builtin.single_threaded) return error.SkipZigTest; // TODO https://github.com/ziglang/zig/issues/3251 if (builtin.os.tag == .freebsd) return error.SkipZigTest; var lock = Lock{}; testLock(&lock); const expected_result = [1]i32{3 * @as(i32, @intCast(shared_test_data.len))} ** shared_test_data.len; try testing.expectEqualSlices(i32, &expected_result, &shared_test_data); } fn testLock(lock: *Lock) void { var handle1 = async lockRunner(lock); var handle2 = async lockRunner(lock); var handle3 = async lockRunner(lock); await handle1; await handle2; await handle3; } var shared_test_data = [1]i32{0} ** 10; var shared_test_index: usize = 0; fn lockRunner(lock: *Lock) void { Lock.global_event_loop.yield(); var i: usize = 0; while (i < shared_test_data.len) : (i += 1) { const handle = lock.acquire(); defer handle.release(); shared_test_index = 0; while (shared_test_index < shared_test_data.len) : (shared_test_index += 1) { shared_test_data[shared_test_index] = shared_test_data[shared_test_index] + 1; } } } |
Generated by zstd-browse2 on 2023-11-04 14:12:31 -0400. |