authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-12-21 14:21:51-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-12-23 13:36:21-08:00
log829c00a77fd2d6b7576c6d2b724f69ba9cfe10f2
tree879be7bfbb6b021489f1d45d63af9732dba4593a
parent4eb4d26fa14524652bed69325eb491f39701d995

kprotty ThreadPool and WaitGroup patch


5 files changed, 57 insertions(+), 104 deletions(-)

CMakeLists.txt-1
......@@ -512,7 +512,6 @@ set(ZIG_STAGE2_SOURCES
512512 "${CMAKE_SOURCE_DIR}/src/Cache.zig"
513513 "${CMAKE_SOURCE_DIR}/src/Compilation.zig"
514514 "${CMAKE_SOURCE_DIR}/src/DepTokenizer.zig"
515 "${CMAKE_SOURCE_DIR}/src/Event.zig"
516515 "${CMAKE_SOURCE_DIR}/src/Module.zig"
517516 "${CMAKE_SOURCE_DIR}/src/Package.zig"
518517 "${CMAKE_SOURCE_DIR}/src/RangeSet.zig"
ci/drone/linux_script+1-2
......@@ -17,8 +17,7 @@ git config core.abbrev 9
1717
1818mkdir build
1919cd build
20# TODO figure out why Drone CI is deadlocking and stop passing -DZIG_SINGLE_THREADED=ON
21cmake .. -DCMAKE_BUILD_TYPE=Release "-DCMAKE_INSTALL_PREFIX=$DISTDIR" -DZIG_STATIC=ON -DCMAKE_PREFIX_PATH=/deps/local -GNinja -DZIG_SINGLE_THREADED=ON
20cmake .. -DCMAKE_BUILD_TYPE=Release "-DCMAKE_INSTALL_PREFIX=$DISTDIR" -DZIG_STATIC=ON -DCMAKE_PREFIX_PATH=/deps/local -GNinja
2221
2322samu install
2423./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.
6const std = @import("std");
7const Event = @This();
8
9lock: std.Mutex = .{},
10event: std.ResetEvent = undefined,
11state: enum { empty, waiting, notified } = .empty,
12
13pub 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
29pub 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();
99lock: std.Mutex = .{},
1010is_running: bool = true,
1111allocator: *std.mem.Allocator,
12running: usize = 0,
12spawned: usize = 0,
1313threads: []*std.Thread,
1414run_queue: RunQueue = .{},
1515idle_queue: IdleQueue = .{},
1616
17const IdleQueue = std.SinglyLinkedList(std.AutoResetEvent);
17const IdleQueue = std.SinglyLinkedList(std.ResetEvent);
1818const RunQueue = std.SinglyLinkedList(Runnable);
1919const Runnable = struct {
2020 runFn: fn (*Runnable) void,
......@@ -30,49 +30,37 @@ pub fn init(self: *ThreadPool, allocator: *std.mem.Allocator) !void {
3030
3131 errdefer self.deinit();
3232
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);
3635
3736 while (num_threads > 0) : (num_threads -= 1) {
3837 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;
4140 }
4241}
4342
4443pub 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
56pub 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();
6147
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 }
6652
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();
6956}
7057
7158pub fn spawn(self: *ThreadPool, comptime func: anytype, args: anytype) !void {
7259 if (std.builtin.single_threaded) {
73 @call(.{}, func, args);
60 const result = @call(.{}, func, args);
7461 return;
7562 }
63
7664 const Args = @TypeOf(args);
7765 const Closure = struct {
7866 arguments: Args,
......@@ -83,24 +71,26 @@ pub fn spawn(self: *ThreadPool, comptime func: anytype, args: anytype) !void {
8371 const run_node = @fieldParentPtr(RunQueue.Node, "data", runnable);
8472 const closure = @fieldParentPtr(@This(), "run_node", run_node);
8573 const result = @call(.{}, func, closure.arguments);
74
75 const held = closure.pool.lock.acquire();
76 defer held.release();
8677 closure.pool.allocator.destroy(closure);
8778 }
8879 };
8980
81 const held = self.lock.acquire();
82 defer held.release();
83
9084 const closure = try self.allocator.create(Closure);
9185 closure.* = .{
9286 .arguments = args,
9387 .pool = self,
9488 };
9589
96 const held = self.lock.acquire();
9790 self.run_queue.prepend(&closure.run_node);
9891
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();
10494}
10595
10696fn runWorker(self: *ThreadPool) void {
......@@ -113,14 +103,18 @@ fn runWorker(self: *ThreadPool) void {
113103 continue;
114104 }
115105
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);
117111 held.release();
118 return;
112
113 idle_node.data.wait();
114 continue;
119115 }
120116
121 var idle_node = IdleQueue.Node{ .data = .{} };
122 self.idle_queue.prepend(&idle_node);
123117 held.release();
124 idle_node.data.wait();
118 return;
125119 }
126120}
src/WaitGroup.zig+21-17
......@@ -5,11 +5,10 @@
55// and substantial portions of the software.
66const std = @import("std");
77const WaitGroup = @This();
8const Event = @import("Event.zig");
98
109lock: std.Mutex = .{},
1110counter: usize = 0,
12event: ?*Event = null,
11event: ?*std.ResetEvent = null,
1312
1413pub fn start(self: *WaitGroup) void {
1514 const held = self.lock.acquire();
......@@ -19,28 +18,33 @@ pub fn start(self: *WaitGroup) void {
1918}
2019
2120pub fn stop(self: *WaitGroup) void {
22 var event: ?*Event = null;
23 defer if (event) |waiter|
24 waiter.set();
25
2621 const held = self.lock.acquire();
2722 defer held.release();
2823
2924 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 }
3232}
3333
3434pub fn wait(self: *WaitGroup) void {
35 var event = Event{};
36 var has_event = false;
37 defer if (has_event)
38 event.wait();
39
4035 const held = self.lock.acquire();
41 defer held.release();
4236
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();
4650}