| author | |
| committer | |
| log | 829c00a77fd2d6b7576c6d2b724f69ba9cfe10f2 |
| tree | 879be7bfbb6b021489f1d45d63af9732dba4593a |
| parent | 4eb4d26fa14524652bed69325eb491f39701d995 |
5 files changed, 57 insertions(+), 104 deletions(-)
CMakeLists.txt-1| ... | ... | @@ -512,7 +512,6 @@ set(ZIG_STAGE2_SOURCES |
| 512 | 512 | "${CMAKE_SOURCE_DIR}/src/Cache.zig" |
| 513 | 513 | "${CMAKE_SOURCE_DIR}/src/Compilation.zig" |
| 514 | 514 | "${CMAKE_SOURCE_DIR}/src/DepTokenizer.zig" |
| 515 | "${CMAKE_SOURCE_DIR}/src/Event.zig" | |
| 516 | 515 | "${CMAKE_SOURCE_DIR}/src/Module.zig" |
| 517 | 516 | "${CMAKE_SOURCE_DIR}/src/Package.zig" |
| 518 | 517 | "${CMAKE_SOURCE_DIR}/src/RangeSet.zig" |
ci/drone/linux_script+1-2| ... | ... | @@ -17,8 +17,7 @@ git config core.abbrev 9 |
| 17 | 17 | |
| 18 | 18 | mkdir build |
| 19 | 19 | cd build |
| 20 | # TODO figure out why Drone CI is deadlocking and stop passing -DZIG_SINGLE_THREADED=ON | |
| 21 | cmake .. -DCMAKE_BUILD_TYPE=Release "-DCMAKE_INSTALL_PREFIX=$DISTDIR" -DZIG_STATIC=ON -DCMAKE_PREFIX_PATH=/deps/local -GNinja -DZIG_SINGLE_THREADED=ON | |
| 20 | cmake .. -DCMAKE_BUILD_TYPE=Release "-DCMAKE_INSTALL_PREFIX=$DISTDIR" -DZIG_STATIC=ON -DCMAKE_PREFIX_PATH=/deps/local -GNinja | |
| 22 | 21 | |
| 23 | 22 | samu install |
| 24 | 23 | ./zig build test -Dskip-release -Dskip-non-native |
src/Event.zig deleted-43| ... | ... | @@ -1,43 +0,0 @@ |
| 1 | // SPDX-License-Identifier: MIT | |
| 2 | // Copyright (c) 2015-2020 Zig Contributors | |
| 3 | // This file is part of [zig](https://ziglang.org/), which is MIT licensed. | |
| 4 | // The MIT license requires this copyright notice to be included in all copies | |
| 5 | // and substantial portions of the software. | |
| 6 | const std = @import("std"); | |
| 7 | const Event = @This(); | |
| 8 | ||
| 9 | lock: std.Mutex = .{}, | |
| 10 | event: std.ResetEvent = undefined, | |
| 11 | state: enum { empty, waiting, notified } = .empty, | |
| 12 | ||
| 13 | pub fn wait(self: *Event) void { | |
| 14 | const held = self.lock.acquire(); | |
| 15 | ||
| 16 | switch (self.state) { | |
| 17 | .empty => { | |
| 18 | self.state = .waiting; | |
| 19 | self.event = @TypeOf(self.event).init(); | |
| 20 | held.release(); | |
| 21 | self.event.wait(); | |
| 22 | self.event.deinit(); | |
| 23 | }, | |
| 24 | .waiting => unreachable, | |
| 25 | .notified => held.release(), | |
| 26 | } | |
| 27 | } | |
| 28 | ||
| 29 | pub fn set(self: *Event) void { | |
| 30 | const held = self.lock.acquire(); | |
| 31 | ||
| 32 | switch (self.state) { | |
| 33 | .empty => { | |
| 34 | self.state = .notified; | |
| 35 | held.release(); | |
| 36 | }, | |
| 37 | .waiting => { | |
| 38 | held.release(); | |
| 39 | self.event.set(); | |
| 40 | }, | |
| 41 | .notified => unreachable, | |
| 42 | } | |
| 43 | } |
src/ThreadPool.zig+35-41| ... | ... | @@ -9,12 +9,12 @@ const ThreadPool = @This(); |
| 9 | 9 | lock: std.Mutex = .{}, |
| 10 | 10 | is_running: bool = true, |
| 11 | 11 | allocator: *std.mem.Allocator, |
| 12 | running: usize = 0, | |
| 12 | spawned: usize = 0, | |
| 13 | 13 | threads: []*std.Thread, |
| 14 | 14 | run_queue: RunQueue = .{}, |
| 15 | 15 | idle_queue: IdleQueue = .{}, |
| 16 | 16 | |
| 17 | const IdleQueue = std.SinglyLinkedList(std.AutoResetEvent); | |
| 17 | const IdleQueue = std.SinglyLinkedList(std.ResetEvent); | |
| 18 | 18 | const RunQueue = std.SinglyLinkedList(Runnable); |
| 19 | 19 | const Runnable = struct { |
| 20 | 20 | runFn: fn (*Runnable) void, |
| ... | ... | @@ -30,49 +30,37 @@ pub fn init(self: *ThreadPool, allocator: *std.mem.Allocator) !void { |
| 30 | 30 | |
| 31 | 31 | errdefer self.deinit(); |
| 32 | 32 | |
| 33 | var num_threads = std.Thread.cpuCount() catch 1; | |
| 34 | if (num_threads > 0) | |
| 35 | self.threads = try allocator.alloc(*std.Thread, num_threads); | |
| 33 | var num_threads = std.math.max(1, std.Thread.cpuCount() catch 1); | |
| 34 | self.threads = try allocator.alloc(*std.Thread, num_threads); | |
| 36 | 35 | |
| 37 | 36 | while (num_threads > 0) : (num_threads -= 1) { |
| 38 | 37 | const thread = try std.Thread.spawn(self, runWorker); |
| 39 | self.threads[self.running] = thread; | |
| 40 | self.running += 1; | |
| 38 | self.threads[self.spawned] = thread; | |
| 39 | self.spawned += 1; | |
| 41 | 40 | } |
| 42 | 41 | } |
| 43 | 42 | |
| 44 | 43 | pub fn deinit(self: *ThreadPool) void { |
| 45 | self.shutdown(); | |
| 46 | ||
| 47 | std.debug.assert(!self.is_running); | |
| 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(); | |
| 44 | { | |
| 45 | const held = self.lock.acquire(); | |
| 46 | defer held.release(); | |
| 61 | 47 | |
| 62 | var idle_queue = self.idle_queue; | |
| 63 | self.idle_queue = .{}; | |
| 64 | self.is_running = false; | |
| 65 | held.release(); | |
| 48 | self.is_running = false; | |
| 49 | while (self.idle_queue.popFirst()) |idle_node| | |
| 50 | idle_node.data.set(); | |
| 51 | } | |
| 66 | 52 | |
| 67 | while (idle_queue.popFirst()) |idle_node| | |
| 68 | idle_node.data.set(); | |
| 53 | defer self.allocator.free(self.threads); | |
| 54 | for (self.threads[0..self.spawned]) |thread| | |
| 55 | thread.wait(); | |
| 69 | 56 | } |
| 70 | 57 | |
| 71 | 58 | pub fn spawn(self: *ThreadPool, comptime func: anytype, args: anytype) !void { |
| 72 | 59 | if (std.builtin.single_threaded) { |
| 73 | @call(.{}, func, args); | |
| 60 | const result = @call(.{}, func, args); | |
| 74 | 61 | return; |
| 75 | 62 | } |
| 63 | ||
| 76 | 64 | const Args = @TypeOf(args); |
| 77 | 65 | const Closure = struct { |
| 78 | 66 | arguments: Args, |
| ... | ... | @@ -83,24 +71,26 @@ pub fn spawn(self: *ThreadPool, comptime func: anytype, args: anytype) !void { |
| 83 | 71 | const run_node = @fieldParentPtr(RunQueue.Node, "data", runnable); |
| 84 | 72 | const closure = @fieldParentPtr(@This(), "run_node", run_node); |
| 85 | 73 | const result = @call(.{}, func, closure.arguments); |
| 74 | ||
| 75 | const held = closure.pool.lock.acquire(); | |
| 76 | defer held.release(); | |
| 86 | 77 | closure.pool.allocator.destroy(closure); |
| 87 | 78 | } |
| 88 | 79 | }; |
| 89 | 80 | |
| 81 | const held = self.lock.acquire(); | |
| 82 | defer held.release(); | |
| 83 | ||
| 90 | 84 | const closure = try self.allocator.create(Closure); |
| 91 | 85 | closure.* = .{ |
| 92 | 86 | .arguments = args, |
| 93 | 87 | .pool = self, |
| 94 | 88 | }; |
| 95 | 89 | |
| 96 | const held = self.lock.acquire(); | |
| 97 | 90 | self.run_queue.prepend(&closure.run_node); |
| 98 | 91 | |
| 99 | const idle_node = self.idle_queue.popFirst(); | |
| 100 | held.release(); | |
| 101 | ||
| 102 | if (idle_node) |node| | |
| 103 | node.data.set(); | |
| 92 | if (self.idle_queue.popFirst()) |idle_node| | |
| 93 | idle_node.data.set(); | |
| 104 | 94 | } |
| 105 | 95 | |
| 106 | 96 | fn runWorker(self: *ThreadPool) void { |
| ... | ... | @@ -113,14 +103,18 @@ fn runWorker(self: *ThreadPool) void { |
| 113 | 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 | 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 | 117 | held.release(); |
| 124 | idle_node.data.wait(); | |
| 118 | return; | |
| 125 | 119 | } |
| 126 | 120 | } |
src/WaitGroup.zig+21-17| ... | ... | @@ -5,11 +5,10 @@ |
| 5 | 5 | // and substantial portions of the software. |
| 6 | 6 | const std = @import("std"); |
| 7 | 7 | const WaitGroup = @This(); |
| 8 | const Event = @import("Event.zig"); | |
| 9 | 8 | |
| 10 | 9 | lock: std.Mutex = .{}, |
| 11 | 10 | counter: usize = 0, |
| 12 | event: ?*Event = null, | |
| 11 | event: ?*std.ResetEvent = null, | |
| 13 | 12 | |
| 14 | 13 | pub fn start(self: *WaitGroup) void { |
| 15 | 14 | const held = self.lock.acquire(); |
| ... | ... | @@ -19,28 +18,33 @@ pub fn start(self: *WaitGroup) void { |
| 19 | 18 | } |
| 20 | 19 | |
| 21 | 20 | pub fn stop(self: *WaitGroup) void { |
| 22 | var event: ?*Event = null; | |
| 23 | defer if (event) |waiter| | |
| 24 | waiter.set(); | |
| 25 | ||
| 26 | 21 | const held = self.lock.acquire(); |
| 27 | 22 | defer held.release(); |
| 28 | 23 | |
| 29 | 24 | self.counter -= 1; |
| 30 | if (self.counter == 0) | |
| 31 | std.mem.swap(?*Event, &self.event, &event); | |
| 25 | ||
| 26 | if (self.counter == 0) { | |
| 27 | if (self.event) |event| { | |
| 28 | self.event = null; | |
| 29 | event.set(); | |
| 30 | } | |
| 31 | } | |
| 32 | 32 | } |
| 33 | 33 | |
| 34 | 34 | pub fn wait(self: *WaitGroup) void { |
| 35 | var event = Event{}; | |
| 36 | var has_event = false; | |
| 37 | defer if (has_event) | |
| 38 | event.wait(); | |
| 39 | ||
| 40 | 35 | const held = self.lock.acquire(); |
| 41 | defer held.release(); | |
| 42 | 36 | |
| 43 | has_event = self.counter != 0; | |
| 44 | if (has_event) | |
| 45 | self.event = &event; | |
| 37 | if (self.counter == 0) { | |
| 38 | held.release(); | |
| 39 | return; | |
| 40 | } | |
| 41 | ||
| 42 | var event = std.ResetEvent.init(); | |
| 43 | defer event.deinit(); | |
| 44 | ||
| 45 | std.debug.assert(self.event == null); | |
| 46 | self.event = &event; | |
| 47 | ||
| 48 | held.release(); | |
| 49 | event.wait(); | |
| 46 | 50 | } |