From a5b3b52b1b0aeba09ac01f50878ae808050067fd Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Thu, 21 Apr 2022 16:24:37 -0700 Subject: [PATCH] libuv: Threads --- src/libuv/Thread.zig | 88 ++++++++++++++++++++++++++++++++++++++++++++ src/libuv/main.zig | 2 + 2 files changed, 90 insertions(+) create mode 100644 src/libuv/Thread.zig diff --git a/src/libuv/Thread.zig b/src/libuv/Thread.zig new file mode 100644 index 000000000..38e17f768 --- /dev/null +++ b/src/libuv/Thread.zig @@ -0,0 +1,88 @@ +//! Threading implemented by libuv. +const Thread = @This(); + +const std = @import("std"); +const Allocator = std.mem.Allocator; +const testing = std.testing; +const c = @import("c.zig"); +const errors = @import("error.zig"); + +thread: c.uv_thread_t, + +/// Initialize a new thread. +pub fn init( + comptime callback: fn () void, +) !Thread { + const CWrapper = struct { + pub fn wrapper(_: ?*const anyopaque) callconv(.C) void { + @call(.{ .modifier = .always_inline }, callback, .{}); + } + }; + + var res = Thread{ .thread = undefined }; + try errors.convertError(c.uv_thread_create(&res.thread, CWrapper.wrapper, null)); + return res; +} + +/// Initialize a new thread with user data attached. +pub fn initData( + data: anytype, + comptime callback: fn (arg: @TypeOf(data)) void, +) !Thread { + // Comptime stuff to learn more about our data parameter. This is used + // to do the proper casts for the callback. + const Data = @TypeOf(data); + const dataInfo = @typeInfo(Data); + if (dataInfo != .Pointer) @compileError("data must be a pointer type"); + + const CWrapper = struct { + pub fn wrapper(arg: ?*anyopaque) callconv(.C) void { + @call(.{ .modifier = .always_inline }, callback, .{ + @ptrCast(Data, @alignCast(@alignOf(dataInfo.Pointer.child), arg)), + }); + } + }; + + var res: Thread = .{ .thread = undefined }; + try errors.convertError(c.uv_thread_create( + &res.thread, + CWrapper.wrapper, + data, + )); + return res; +} + +pub fn deinit(self: *Thread) void { + self.* = undefined; +} + +pub fn join(self: *Thread) !void { + try errors.convertError(c.uv_thread_join(&self.thread)); +} + +test "Thread: no data argument" { + count = 0; + var thread = try init(incr); + defer thread.deinit(); + try thread.join(); + try testing.expectEqual(@as(u8, 1), count); +} + +test "Thread: with data argument" { + count = 0; + var data: u8 = 2; + var thread = try initData(&data, incrBy); + defer thread.deinit(); + try thread.join(); + try testing.expectEqual(@as(u8, 2), count); +} + +var count: u8 = 0; + +fn incr() void { + count += 1; +} + +fn incrBy(v: *u8) void { + count += v.*; +} diff --git a/src/libuv/main.zig b/src/libuv/main.zig index a3ee6774a..5695f343b 100644 --- a/src/libuv/main.zig +++ b/src/libuv/main.zig @@ -1,9 +1,11 @@ const Loop = @import("Loop.zig"); const Sem = @import("Sem.zig"); +const Thread = @import("Thread.zig"); const Error = @import("error.zig").Error; test { _ = Loop; _ = Sem; + _ = Thread; _ = Error; }