Skip to content

Commit

Permalink
try impl
Browse files Browse the repository at this point in the history
  • Loading branch information
Hanaasagi committed Jun 15, 2023
1 parent 2e0b548 commit 7ea5a9e
Show file tree
Hide file tree
Showing 3 changed files with 133 additions and 4 deletions.
76 changes: 72 additions & 4 deletions src/lib.zig
Original file line number Diff line number Diff line change
@@ -1,10 +1,78 @@
const std = @import("std");
const builtin = @import("builtin");
const testing = std.testing;

export fn add(a: i32, b: i32) i32 {
return a + b;
pub fn OnceCell(comptime T: type) type {
if (builtin.single_threaded) {
return @import("./singlethread.zig").OnceCell(T);
} else {
return @import("./multithread.zig").OnceCell(T);
}
}

test "basic add functionality" {
try testing.expect(add(3, 7) == 10);
pub fn Lazy(comptime T: type, comptime f: fn () T) type {
_ = f;
return struct {
cell: OnceCell(T),
};
}

// --------------------------------------------------------------------------------
// Testing
// --------------------------------------------------------------------------------

// var global_number: i32 = 0;
// var global_once = once(incr);

fn init1() i32 {
return 1;
}

fn init2() i32 {
return 2;
}

var c3: i32 = 0;

fn init3() i32 {
c3 += 1;
return c3;
}

var cell1 = OnceCell(i32).init();
var cell2 = OnceCell(i32).init();
var cell3 = OnceCell(i32).init();

test "Once executes its function just once" {
const r1 = cell1.getOrInit(init1);
const r2 = cell1.getOrInit(init1);
const r3 = cell1.getOrInit(init1);

try testing.expect(r1 == 1);
try testing.expectEqual(r1, r2);
try testing.expectEqual(r2, r3);

const a1 = cell2.getOrInit(init2);
const a2 = cell2.getOrInit(init2);
const a3 = cell2.getOrInit(init2);

try testing.expect(a1 == 2);
try testing.expectEqual(a1, a2);
try testing.expectEqual(a2, a3);
}

test "test multithread " {
var threads: [10]std.Thread = undefined;
defer for (threads) |handle| handle.join();

for (&threads) |*handle| {
handle.* = try std.Thread.spawn(.{}, struct {
fn thread_fn(x: u8) void {
_ = x;
_ = cell3.getOrInit(init3);
}
}.thread_fn, .{0});
}

try testing.expectEqual(@as(i32, 1), c3);
}
57 changes: 57 additions & 0 deletions src/multithread.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
const std = @import("std");

pub fn OnceCell(comptime T: type) type {
return struct {
cell: T = undefined,
mutex: std.Thread.Mutex = std.Thread.Mutex{},
done: bool = false,

const Self = @This();

pub fn init() Self {
return Self{};
}

pub fn get(self: Self) ?T {
if (self.isInitialize()) {
return self.cell;
}
return null;
}

// pub fn getPtr(self: Self) ?*T {
// if (self.isInitialize()) {
// return self.cell.ptr;
// }
// return null;
// }

pub fn getOrInit(self: *Self, comptime f: fn () T) T {
// Fast path check
if (self.get()) |value| {
return value;
}

return self.initialize(f);
}

pub fn isInitialize(self: Self) bool {
return @atomicLoad(bool, &self.done, .Acquire);
}

fn initialize(self: *Self, comptime f: fn () T) T {
@setCold(true);

self.mutex.lock();
defer self.mutex.unlock();

// The first thread to acquire the mutex gets to run the initializer

if (!self.done) {
self.cell = f();
defer @atomicStore(bool, &self.done, true, .Release);
}
return self.cell;
}
};
}
4 changes: 4 additions & 0 deletions src/singlethread.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
pub fn OnceCell(comptime T: type) type {
_ = T;
return struct {};
}

0 comments on commit 7ea5a9e

Please sign in to comment.