| ... | @@ -9,12 +9,12 @@ const ThreadPool = @This(); | ... | @@ -9,12 +9,12 @@ const ThreadPool = @This(); |
| 9 | lock: std.Mutex = .{}, | 9 | lock: std.Mutex = .{}, |
| 10 | is_running: bool = true, | 10 | is_running: bool = true, |
| 11 | allocator: *std.mem.Allocator, | 11 | allocator: *std.mem.Allocator, |
| 12 | running: usize = 0, | 12 | spawned: usize = 0, |
| 13 | threads: []*std.Thread, | 13 | threads: []*std.Thread, |
| 14 | run_queue: RunQueue = .{}, | 14 | run_queue: RunQueue = .{}, |
| 15 | idle_queue: IdleQueue = .{}, | 15 | idle_queue: IdleQueue = .{}, |
| 16 | | 16 | |
| 17 | const IdleQueue = std.SinglyLinkedList(std.AutoResetEvent); | 17 | const IdleQueue = std.SinglyLinkedList(std.ResetEvent); |
| 18 | const RunQueue = std.SinglyLinkedList(Runnable); | 18 | const RunQueue = std.SinglyLinkedList(Runnable); |
| 19 | const Runnable = struct { | 19 | const Runnable = struct { |
| 20 | runFn: fn (*Runnable) void, | 20 | runFn: fn (*Runnable) void, |
| ... | @@ -30,49 +30,37 @@ pub fn init(self: *ThreadPool, allocator: *std.mem.Allocator) !void { | ... | @@ -30,49 +30,37 @@ pub fn init(self: *ThreadPool, allocator: *std.mem.Allocator) !void { |
| 30 | | 30 | |
| 31 | errdefer self.deinit(); | 31 | errdefer self.deinit(); |
| 32 | | 32 | |
| 33 | var num_threads = std.Thread.cpuCount() catch 1; | 33 | var num_threads = std.math.max(1, std.Thread.cpuCount() catch 1); |
| 34 | if (num_threads > 0) | 34 | self.threads = try allocator.alloc(*std.Thread, num_threads); |
| 35 | self.threads = try allocator.alloc(*std.Thread, num_threads); | | |
| 36 | | 35 | |
| 37 | while (num_threads > 0) : (num_threads -= 1) { | 36 | while (num_threads > 0) : (num_threads -= 1) { |
| 38 | const thread = try std.Thread.spawn(self, runWorker); | 37 | const thread = try std.Thread.spawn(self, runWorker); |
| 39 | self.threads[self.running] = thread; | 38 | self.threads[self.spawned] = thread; |
| 40 | self.running += 1; | 39 | self.spawned += 1; |
| 41 | } | 40 | } |
| 42 | } | 41 | } |
| 43 | | 42 | |
| 44 | pub fn deinit(self: *ThreadPool) void { | 43 | pub fn deinit(self: *ThreadPool) void { |
| 45 | self.shutdown(); | 44 | { |
| 46 | | 45 | const held = self.lock.acquire(); |
| 47 | std.debug.assert(!self.is_running); | 46 | defer held.release(); |
| 48 | for (self.threads[0..self.running]) |thread| | | |
| 49 | thread.wait(); | | |
| 50 | | | |
| 51 | defer self.threads = &[_]*std.Thread{}; | | |
| 52 | if (self.running > 0) | | |
| 53 | self.allocator.free(self.threads); | | |
| 54 | } | | |
| 55 | | | |
| 56 | pub fn shutdown(self: *ThreadPool) void { | | |
| 57 | const held = self.lock.acquire(); | | |
| 58 | | | |
| 59 | if (!self.is_running) | | |
| 60 | return held.release(); | | |
| 61 | | 47 | |
| 62 | var idle_queue = self.idle_queue; | 48 | self.is_running = false; |
| 63 | self.idle_queue = .{}; | 49 | while (self.idle_queue.popFirst()) |idle_node| |
| 64 | self.is_running = false; | 50 | idle_node.data.set(); |
| 65 | held.release(); | 51 | } |
| 66 | | 52 | |
| 67 | while (idle_queue.popFirst()) |idle_node| | 53 | defer self.allocator.free(self.threads); |
| 68 | idle_node.data.set(); | 54 | for (self.threads[0..self.spawned]) |thread| |
| | 55 | thread.wait(); |
| 69 | } | 56 | } |
| 70 | | 57 | |
| 71 | pub fn spawn(self: *ThreadPool, comptime func: anytype, args: anytype) !void { | 58 | pub fn spawn(self: *ThreadPool, comptime func: anytype, args: anytype) !void { |
| 72 | if (std.builtin.single_threaded) { | 59 | if (std.builtin.single_threaded) { |
| 73 | @call(.{}, func, args); | 60 | const result = @call(.{}, func, args); |
| 74 | return; | 61 | return; |
| 75 | } | 62 | } |
| | 63 | |
| 76 | const Args = @TypeOf(args); | 64 | const Args = @TypeOf(args); |
| 77 | const Closure = struct { | 65 | const Closure = struct { |
| 78 | arguments: Args, | 66 | arguments: Args, |
| ... | @@ -83,24 +71,26 @@ pub fn spawn(self: *ThreadPool, comptime func: anytype, args: anytype) !void { | ... | @@ -83,24 +71,26 @@ pub fn spawn(self: *ThreadPool, comptime func: anytype, args: anytype) !void { |
| 83 | const run_node = @fieldParentPtr(RunQueue.Node, "data", runnable); | 71 | const run_node = @fieldParentPtr(RunQueue.Node, "data", runnable); |
| 84 | const closure = @fieldParentPtr(@This(), "run_node", run_node); | 72 | const closure = @fieldParentPtr(@This(), "run_node", run_node); |
| 85 | const result = @call(.{}, func, closure.arguments); | 73 | const result = @call(.{}, func, closure.arguments); |
| | 74 | |
| | 75 | const held = closure.pool.lock.acquire(); |
| | 76 | defer held.release(); |
| 86 | closure.pool.allocator.destroy(closure); | 77 | closure.pool.allocator.destroy(closure); |
| 87 | } | 78 | } |
| 88 | }; | 79 | }; |
| 89 | | 80 | |
| | 81 | const held = self.lock.acquire(); |
| | 82 | defer held.release(); |
| | 83 | |
| 90 | const closure = try self.allocator.create(Closure); | 84 | const closure = try self.allocator.create(Closure); |
| 91 | closure.* = .{ | 85 | closure.* = .{ |
| 92 | .arguments = args, | 86 | .arguments = args, |
| 93 | .pool = self, | 87 | .pool = self, |
| 94 | }; | 88 | }; |
| 95 | | 89 | |
| 96 | const held = self.lock.acquire(); | | |
| 97 | self.run_queue.prepend(&closure.run_node); | 90 | self.run_queue.prepend(&closure.run_node); |
| 98 | | 91 | |
| 99 | const idle_node = self.idle_queue.popFirst(); | 92 | if (self.idle_queue.popFirst()) |idle_node| |
| 100 | held.release(); | 93 | idle_node.data.set(); |
| 101 | | | |
| 102 | if (idle_node) |node| | | |
| 103 | node.data.set(); | | |
| 104 | } | 94 | } |
| 105 | | 95 | |
| 106 | fn runWorker(self: *ThreadPool) void { | 96 | fn runWorker(self: *ThreadPool) void { |
| ... | @@ -113,14 +103,18 @@ fn runWorker(self: *ThreadPool) void { | ... | @@ -113,14 +103,18 @@ fn runWorker(self: *ThreadPool) void { |
| 113 | continue; | 103 | continue; |
| 114 | } | 104 | } |
| 115 | | 105 | |
| 116 | if (!self.is_running) { | 106 | if (self.is_running) { |
| | 107 | var idle_node = IdleQueue.Node{ .data = std.ResetEvent.init() }; |
| | 108 | defer idle_node.data.deinit(); |
| | 109 | |
| | 110 | self.idle_queue.prepend(&idle_node); |
| 117 | held.release(); | 111 | held.release(); |
| 118 | return; | 112 | |
| | 113 | idle_node.data.wait(); |
| | 114 | continue; |
| 119 | } | 115 | } |
| 120 | | 116 | |
| 121 | var idle_node = IdleQueue.Node{ .data = .{} }; | | |
| 122 | self.idle_queue.prepend(&idle_node); | | |
| 123 | held.release(); | 117 | held.release(); |
| 124 | idle_node.data.wait(); | 118 | return; |
| 125 | } | 119 | } |
| 126 | } | 120 | } |