| author | |
| committer | |
| log | 5b90fa05a4e5b155f25319713acfc67ad9516c69 |
| tree | 6aaffe4ec16f7f6a18539bf2397176e001bf71cf |
| parent | 0b744d7d670d00fa865ebd17847cbdc1a909ba70 |
11 files changed, 211 insertions(+), 209 deletions(-)
CMakeLists.txt+2-2| ... | ... | @@ -506,7 +506,9 @@ set(ZIG_STAGE2_SOURCES |
| 506 | 506 | "${CMAKE_SOURCE_DIR}/lib/std/Thread.zig" |
| 507 | 507 | "${CMAKE_SOURCE_DIR}/lib/std/Thread/Futex.zig" |
| 508 | 508 | "${CMAKE_SOURCE_DIR}/lib/std/Thread/Mutex.zig" |
| 509 | "${CMAKE_SOURCE_DIR}/lib/std/Thread/Pool.zig" | |
| 509 | 510 | "${CMAKE_SOURCE_DIR}/lib/std/Thread/ResetEvent.zig" |
| 511 | "${CMAKE_SOURCE_DIR}/lib/std/Thread/WaitGroup.zig" | |
| 510 | 512 | "${CMAKE_SOURCE_DIR}/lib/std/time.zig" |
| 511 | 513 | "${CMAKE_SOURCE_DIR}/lib/std/treap.zig" |
| 512 | 514 | "${CMAKE_SOURCE_DIR}/lib/std/unicode.zig" |
| ... | ... | @@ -530,9 +532,7 @@ set(ZIG_STAGE2_SOURCES |
| 530 | 532 | "${CMAKE_SOURCE_DIR}/src/Package.zig" |
| 531 | 533 | "${CMAKE_SOURCE_DIR}/src/RangeSet.zig" |
| 532 | 534 | "${CMAKE_SOURCE_DIR}/src/Sema.zig" |
| 533 | "${CMAKE_SOURCE_DIR}/src/ThreadPool.zig" | |
| 534 | 535 | "${CMAKE_SOURCE_DIR}/src/TypedValue.zig" |
| 535 | "${CMAKE_SOURCE_DIR}/src/WaitGroup.zig" | |
| 536 | 536 | "${CMAKE_SOURCE_DIR}/src/Zir.zig" |
| 537 | 537 | "${CMAKE_SOURCE_DIR}/src/arch/aarch64/CodeGen.zig" |
| 538 | 538 | "${CMAKE_SOURCE_DIR}/src/arch/aarch64/Emit.zig" |
lib/std/Thread.zig+2| ... | ... | @@ -16,6 +16,8 @@ pub const Mutex = @import("Thread/Mutex.zig"); |
| 16 | 16 | pub const Semaphore = @import("Thread/Semaphore.zig"); |
| 17 | 17 | pub const Condition = @import("Thread/Condition.zig"); |
| 18 | 18 | pub const RwLock = @import("Thread/RwLock.zig"); |
| 19 | pub const Pool = @import("Thread/Pool.zig"); | |
| 20 | pub const WaitGroup = @import("Thread/WaitGroup.zig"); | |
| 19 | 21 | |
| 20 | 22 | pub const use_pthreads = target.os.tag != .windows and target.os.tag != .wasi and builtin.link_libc; |
| 21 | 23 | const is_gnu = target.abi.isGnu(); |
lib/std/Thread/Pool.zig created+152| ... | ... | @@ -0,0 +1,152 @@ |
| 1 | const std = @import("std"); | |
| 2 | const builtin = @import("builtin"); | |
| 3 | const Pool = @This(); | |
| 4 | const WaitGroup = @import("WaitGroup.zig"); | |
| 5 | ||
| 6 | mutex: std.Thread.Mutex = .{}, | |
| 7 | cond: std.Thread.Condition = .{}, | |
| 8 | run_queue: RunQueue = .{}, | |
| 9 | is_running: bool = true, | |
| 10 | allocator: std.mem.Allocator, | |
| 11 | threads: []std.Thread, | |
| 12 | ||
| 13 | const RunQueue = std.SinglyLinkedList(Runnable); | |
| 14 | const Runnable = struct { | |
| 15 | runFn: RunProto, | |
| 16 | }; | |
| 17 | ||
| 18 | const RunProto = *const fn (*Runnable) void; | |
| 19 | ||
| 20 | pub fn init(pool: *Pool, allocator: std.mem.Allocator) !void { | |
| 21 | pool.* = .{ | |
| 22 | .allocator = allocator, | |
| 23 | .threads = &[_]std.Thread{}, | |
| 24 | }; | |
| 25 | ||
| 26 | if (builtin.single_threaded) { | |
| 27 | return; | |
| 28 | } | |
| 29 | ||
| 30 | const thread_count = std.math.max(1, std.Thread.getCpuCount() catch 1); | |
| 31 | pool.threads = try allocator.alloc(std.Thread, thread_count); | |
| 32 | errdefer allocator.free(pool.threads); | |
| 33 | ||
| 34 | // kill and join any threads we spawned previously on error. | |
| 35 | var spawned: usize = 0; | |
| 36 | errdefer pool.join(spawned); | |
| 37 | ||
| 38 | for (pool.threads) |*thread| { | |
| 39 | thread.* = try std.Thread.spawn(.{}, worker, .{pool}); | |
| 40 | spawned += 1; | |
| 41 | } | |
| 42 | } | |
| 43 | ||
| 44 | pub fn deinit(pool: *Pool) void { | |
| 45 | pool.join(pool.threads.len); // kill and join all threads. | |
| 46 | pool.* = undefined; | |
| 47 | } | |
| 48 | ||
| 49 | fn join(pool: *Pool, spawned: usize) void { | |
| 50 | if (builtin.single_threaded) { | |
| 51 | return; | |
| 52 | } | |
| 53 | ||
| 54 | { | |
| 55 | pool.mutex.lock(); | |
| 56 | defer pool.mutex.unlock(); | |
| 57 | ||
| 58 | // ensure future worker threads exit the dequeue loop | |
| 59 | pool.is_running = false; | |
| 60 | } | |
| 61 | ||
| 62 | // wake up any sleeping threads (this can be done outside the mutex) | |
| 63 | // then wait for all the threads we know are spawned to complete. | |
| 64 | pool.cond.broadcast(); | |
| 65 | for (pool.threads[0..spawned]) |thread| { | |
| 66 | thread.join(); | |
| 67 | } | |
| 68 | ||
| 69 | pool.allocator.free(pool.threads); | |
| 70 | } | |
| 71 | ||
| 72 | pub fn spawn(pool: *Pool, comptime func: anytype, args: anytype) !void { | |
| 73 | if (builtin.single_threaded) { | |
| 74 | @call(.auto, func, args); | |
| 75 | return; | |
| 76 | } | |
| 77 | ||
| 78 | const Args = @TypeOf(args); | |
| 79 | const Closure = struct { | |
| 80 | arguments: Args, | |
| 81 | pool: *Pool, | |
| 82 | run_node: RunQueue.Node = .{ .data = .{ .runFn = runFn } }, | |
| 83 | ||
| 84 | fn runFn(runnable: *Runnable) void { | |
| 85 | const run_node = @fieldParentPtr(RunQueue.Node, "data", runnable); | |
| 86 | const closure = @fieldParentPtr(@This(), "run_node", run_node); | |
| 87 | @call(.auto, func, closure.arguments); | |
| 88 | ||
| 89 | // The thread pool's allocator is protected by the mutex. | |
| 90 | const mutex = &closure.pool.mutex; | |
| 91 | mutex.lock(); | |
| 92 | defer mutex.unlock(); | |
| 93 | ||
| 94 | closure.pool.allocator.destroy(closure); | |
| 95 | } | |
| 96 | }; | |
| 97 | ||
| 98 | { | |
| 99 | pool.mutex.lock(); | |
| 100 | defer pool.mutex.unlock(); | |
| 101 | ||
| 102 | const closure = try pool.allocator.create(Closure); | |
| 103 | closure.* = .{ | |
| 104 | .arguments = args, | |
| 105 | .pool = pool, | |
| 106 | }; | |
| 107 | ||
| 108 | pool.run_queue.prepend(&closure.run_node); | |
| 109 | } | |
| 110 | ||
| 111 | // Notify waiting threads outside the lock to try and keep the critical section small. | |
| 112 | pool.cond.signal(); | |
| 113 | } | |
| 114 | ||
| 115 | fn worker(pool: *Pool) void { | |
| 116 | pool.mutex.lock(); | |
| 117 | defer pool.mutex.unlock(); | |
| 118 | ||
| 119 | while (true) { | |
| 120 | while (pool.run_queue.popFirst()) |run_node| { | |
| 121 | // Temporarily unlock the mutex in order to execute the run_node | |
| 122 | pool.mutex.unlock(); | |
| 123 | defer pool.mutex.lock(); | |
| 124 | ||
| 125 | const runFn = run_node.data.runFn; | |
| 126 | runFn(&run_node.data); | |
| 127 | } | |
| 128 | ||
| 129 | // Stop executing instead of waiting if the thread pool is no longer running. | |
| 130 | if (pool.is_running) { | |
| 131 | pool.cond.wait(&pool.mutex); | |
| 132 | } else { | |
| 133 | break; | |
| 134 | } | |
| 135 | } | |
| 136 | } | |
| 137 | ||
| 138 | pub fn waitAndWork(pool: *Pool, wait_group: *WaitGroup) void { | |
| 139 | while (!wait_group.isDone()) { | |
| 140 | if (blk: { | |
| 141 | pool.mutex.lock(); | |
| 142 | defer pool.mutex.unlock(); | |
| 143 | break :blk pool.run_queue.popFirst(); | |
| 144 | }) |run_node| { | |
| 145 | run_node.data.runFn(&run_node.data); | |
| 146 | continue; | |
| 147 | } | |
| 148 | ||
| 149 | wait_group.wait(); | |
| 150 | return; | |
| 151 | } | |
| 152 | } |
lib/std/Thread/WaitGroup.zig created+46| ... | ... | @@ -0,0 +1,46 @@ |
| 1 | const std = @import("std"); | |
| 2 | const Atomic = std.atomic.Atomic; | |
| 3 | const assert = std.debug.assert; | |
| 4 | const WaitGroup = @This(); | |
| 5 | ||
| 6 | const is_waiting: usize = 1 << 0; | |
| 7 | const one_pending: usize = 1 << 1; | |
| 8 | ||
| 9 | state: Atomic(usize) = Atomic(usize).init(0), | |
| 10 | event: std.Thread.ResetEvent = .{}, | |
| 11 | ||
| 12 | pub fn start(self: *WaitGroup) void { | |
| 13 | const state = self.state.fetchAdd(one_pending, .Monotonic); | |
| 14 | assert((state / one_pending) < (std.math.maxInt(usize) / one_pending)); | |
| 15 | } | |
| 16 | ||
| 17 | pub fn finish(self: *WaitGroup) void { | |
| 18 | const state = self.state.fetchSub(one_pending, .Release); | |
| 19 | assert((state / one_pending) > 0); | |
| 20 | ||
| 21 | if (state == (one_pending | is_waiting)) { | |
| 22 | self.state.fence(.Acquire); | |
| 23 | self.event.set(); | |
| 24 | } | |
| 25 | } | |
| 26 | ||
| 27 | pub fn wait(self: *WaitGroup) void { | |
| 28 | var state = self.state.fetchAdd(is_waiting, .Acquire); | |
| 29 | assert(state & is_waiting == 0); | |
| 30 | ||
| 31 | if ((state / one_pending) > 0) { | |
| 32 | self.event.wait(); | |
| 33 | } | |
| 34 | } | |
| 35 | ||
| 36 | pub fn reset(self: *WaitGroup) void { | |
| 37 | self.state.store(0, .Monotonic); | |
| 38 | self.event.reset(); | |
| 39 | } | |
| 40 | ||
| 41 | pub fn isDone(wg: *WaitGroup) bool { | |
| 42 | const state = wg.state.load(.Acquire); | |
| 43 | assert(state & is_waiting == 0); | |
| 44 | ||
| 45 | return (state / one_pending) == 0; | |
| 46 | } |
src/Compilation.zig+2-2| ... | ... | @@ -7,6 +7,8 @@ const Allocator = std.mem.Allocator; |
| 7 | 7 | const assert = std.debug.assert; |
| 8 | 8 | const log = std.log.scoped(.compilation); |
| 9 | 9 | const Target = std.Target; |
| 10 | const ThreadPool = std.Thread.Pool; | |
| 11 | const WaitGroup = std.Thread.WaitGroup; | |
| 10 | 12 | |
| 11 | 13 | const Value = @import("value.zig").Value; |
| 12 | 14 | const Type = @import("type.zig").Type; |
| ... | ... | @@ -30,8 +32,6 @@ const Cache = std.Build.Cache; |
| 30 | 32 | const translate_c = @import("translate_c.zig"); |
| 31 | 33 | const clang = @import("clang.zig"); |
| 32 | 34 | const c_codegen = @import("codegen/c.zig"); |
| 33 | const ThreadPool = @import("ThreadPool.zig"); | |
| 34 | const WaitGroup = @import("WaitGroup.zig"); | |
| 35 | 35 | const libtsan = @import("libtsan.zig"); |
| 36 | 36 | const Zir = @import("Zir.zig"); |
| 37 | 37 | const Autodoc = @import("Autodoc.zig"); |
src/Package.zig+2-2| ... | ... | @@ -8,11 +8,11 @@ const Allocator = mem.Allocator; |
| 8 | 8 | const assert = std.debug.assert; |
| 9 | 9 | const log = std.log.scoped(.package); |
| 10 | 10 | const main = @import("main.zig"); |
| 11 | const ThreadPool = std.Thread.Pool; | |
| 12 | const WaitGroup = std.Thread.WaitGroup; | |
| 11 | 13 | |
| 12 | 14 | const Compilation = @import("Compilation.zig"); |
| 13 | 15 | const Module = @import("Module.zig"); |
| 14 | const ThreadPool = @import("ThreadPool.zig"); | |
| 15 | const WaitGroup = @import("WaitGroup.zig"); | |
| 16 | 16 | const Cache = std.Build.Cache; |
| 17 | 17 | const build_options = @import("build_options"); |
| 18 | 18 | const Manifest = @import("Manifest.zig"); |
src/ThreadPool.zig deleted-152| ... | ... | @@ -1,152 +0,0 @@ |
| 1 | const std = @import("std"); | |
| 2 | const builtin = @import("builtin"); | |
| 3 | const ThreadPool = @This(); | |
| 4 | const WaitGroup = @import("WaitGroup.zig"); | |
| 5 | ||
| 6 | mutex: std.Thread.Mutex = .{}, | |
| 7 | cond: std.Thread.Condition = .{}, | |
| 8 | run_queue: RunQueue = .{}, | |
| 9 | is_running: bool = true, | |
| 10 | allocator: std.mem.Allocator, | |
| 11 | threads: []std.Thread, | |
| 12 | ||
| 13 | const RunQueue = std.SinglyLinkedList(Runnable); | |
| 14 | const Runnable = struct { | |
| 15 | runFn: RunProto, | |
| 16 | }; | |
| 17 | ||
| 18 | const RunProto = *const fn (*Runnable) void; | |
| 19 | ||
| 20 | pub fn init(pool: *ThreadPool, allocator: std.mem.Allocator) !void { | |
| 21 | pool.* = .{ | |
| 22 | .allocator = allocator, | |
| 23 | .threads = &[_]std.Thread{}, | |
| 24 | }; | |
| 25 | ||
| 26 | if (builtin.single_threaded) { | |
| 27 | return; | |
| 28 | } | |
| 29 | ||
| 30 | const thread_count = std.math.max(1, std.Thread.getCpuCount() catch 1); | |
| 31 | pool.threads = try allocator.alloc(std.Thread, thread_count); | |
| 32 | errdefer allocator.free(pool.threads); | |
| 33 | ||
| 34 | // kill and join any threads we spawned previously on error. | |
| 35 | var spawned: usize = 0; | |
| 36 | errdefer pool.join(spawned); | |
| 37 | ||
| 38 | for (pool.threads) |*thread| { | |
| 39 | thread.* = try std.Thread.spawn(.{}, worker, .{pool}); | |
| 40 | spawned += 1; | |
| 41 | } | |
| 42 | } | |
| 43 | ||
| 44 | pub fn deinit(pool: *ThreadPool) void { | |
| 45 | pool.join(pool.threads.len); // kill and join all threads. | |
| 46 | pool.* = undefined; | |
| 47 | } | |
| 48 | ||
| 49 | fn join(pool: *ThreadPool, spawned: usize) void { | |
| 50 | if (builtin.single_threaded) { | |
| 51 | return; | |
| 52 | } | |
| 53 | ||
| 54 | { | |
| 55 | pool.mutex.lock(); | |
| 56 | defer pool.mutex.unlock(); | |
| 57 | ||
| 58 | // ensure future worker threads exit the dequeue loop | |
| 59 | pool.is_running = false; | |
| 60 | } | |
| 61 | ||
| 62 | // wake up any sleeping threads (this can be done outside the mutex) | |
| 63 | // then wait for all the threads we know are spawned to complete. | |
| 64 | pool.cond.broadcast(); | |
| 65 | for (pool.threads[0..spawned]) |thread| { | |
| 66 | thread.join(); | |
| 67 | } | |
| 68 | ||
| 69 | pool.allocator.free(pool.threads); | |
| 70 | } | |
| 71 | ||
| 72 | pub fn spawn(pool: *ThreadPool, comptime func: anytype, args: anytype) !void { | |
| 73 | if (builtin.single_threaded) { | |
| 74 | @call(.auto, func, args); | |
| 75 | return; | |
| 76 | } | |
| 77 | ||
| 78 | const Args = @TypeOf(args); | |
| 79 | const Closure = struct { | |
| 80 | arguments: Args, | |
| 81 | pool: *ThreadPool, | |
| 82 | run_node: RunQueue.Node = .{ .data = .{ .runFn = runFn } }, | |
| 83 | ||
| 84 | fn runFn(runnable: *Runnable) void { | |
| 85 | const run_node = @fieldParentPtr(RunQueue.Node, "data", runnable); | |
| 86 | const closure = @fieldParentPtr(@This(), "run_node", run_node); | |
| 87 | @call(.auto, func, closure.arguments); | |
| 88 | ||
| 89 | // The thread pool's allocator is protected by the mutex. | |
| 90 | const mutex = &closure.pool.mutex; | |
| 91 | mutex.lock(); | |
| 92 | defer mutex.unlock(); | |
| 93 | ||
| 94 | closure.pool.allocator.destroy(closure); | |
| 95 | } | |
| 96 | }; | |
| 97 | ||
| 98 | { | |
| 99 | pool.mutex.lock(); | |
| 100 | defer pool.mutex.unlock(); | |
| 101 | ||
| 102 | const closure = try pool.allocator.create(Closure); | |
| 103 | closure.* = .{ | |
| 104 | .arguments = args, | |
| 105 | .pool = pool, | |
| 106 | }; | |
| 107 | ||
| 108 | pool.run_queue.prepend(&closure.run_node); | |
| 109 | } | |
| 110 | ||
| 111 | // Notify waiting threads outside the lock to try and keep the critical section small. | |
| 112 | pool.cond.signal(); | |
| 113 | } | |
| 114 | ||
| 115 | fn worker(pool: *ThreadPool) void { | |
| 116 | pool.mutex.lock(); | |
| 117 | defer pool.mutex.unlock(); | |
| 118 | ||
| 119 | while (true) { | |
| 120 | while (pool.run_queue.popFirst()) |run_node| { | |
| 121 | // Temporarily unlock the mutex in order to execute the run_node | |
| 122 | pool.mutex.unlock(); | |
| 123 | defer pool.mutex.lock(); | |
| 124 | ||
| 125 | const runFn = run_node.data.runFn; | |
| 126 | runFn(&run_node.data); | |
| 127 | } | |
| 128 | ||
| 129 | // Stop executing instead of waiting if the thread pool is no longer running. | |
| 130 | if (pool.is_running) { | |
| 131 | pool.cond.wait(&pool.mutex); | |
| 132 | } else { | |
| 133 | break; | |
| 134 | } | |
| 135 | } | |
| 136 | } | |
| 137 | ||
| 138 | pub fn waitAndWork(pool: *ThreadPool, wait_group: *WaitGroup) void { | |
| 139 | while (!wait_group.isDone()) { | |
| 140 | if (blk: { | |
| 141 | pool.mutex.lock(); | |
| 142 | defer pool.mutex.unlock(); | |
| 143 | break :blk pool.run_queue.popFirst(); | |
| 144 | }) |run_node| { | |
| 145 | run_node.data.runFn(&run_node.data); | |
| 146 | continue; | |
| 147 | } | |
| 148 | ||
| 149 | wait_group.wait(); | |
| 150 | return; | |
| 151 | } | |
| 152 | } |
src/WaitGroup.zig deleted-46| ... | ... | @@ -1,46 +0,0 @@ |
| 1 | const std = @import("std"); | |
| 2 | const Atomic = std.atomic.Atomic; | |
| 3 | const assert = std.debug.assert; | |
| 4 | const WaitGroup = @This(); | |
| 5 | ||
| 6 | const is_waiting: usize = 1 << 0; | |
| 7 | const one_pending: usize = 1 << 1; | |
| 8 | ||
| 9 | state: Atomic(usize) = Atomic(usize).init(0), | |
| 10 | event: std.Thread.ResetEvent = .{}, | |
| 11 | ||
| 12 | pub fn start(self: *WaitGroup) void { | |
| 13 | const state = self.state.fetchAdd(one_pending, .Monotonic); | |
| 14 | assert((state / one_pending) < (std.math.maxInt(usize) / one_pending)); | |
| 15 | } | |
| 16 | ||
| 17 | pub fn finish(self: *WaitGroup) void { | |
| 18 | const state = self.state.fetchSub(one_pending, .Release); | |
| 19 | assert((state / one_pending) > 0); | |
| 20 | ||
| 21 | if (state == (one_pending | is_waiting)) { | |
| 22 | self.state.fence(.Acquire); | |
| 23 | self.event.set(); | |
| 24 | } | |
| 25 | } | |
| 26 | ||
| 27 | pub fn wait(self: *WaitGroup) void { | |
| 28 | var state = self.state.fetchAdd(is_waiting, .Acquire); | |
| 29 | assert(state & is_waiting == 0); | |
| 30 | ||
| 31 | if ((state / one_pending) > 0) { | |
| 32 | self.event.wait(); | |
| 33 | } | |
| 34 | } | |
| 35 | ||
| 36 | pub fn reset(self: *WaitGroup) void { | |
| 37 | self.state.store(0, .Monotonic); | |
| 38 | self.event.reset(); | |
| 39 | } | |
| 40 | ||
| 41 | pub fn isDone(wg: *WaitGroup) bool { | |
| 42 | const state = wg.state.load(.Acquire); | |
| 43 | assert(state & is_waiting == 0); | |
| 44 | ||
| 45 | return (state / one_pending) == 0; | |
| 46 | } |
src/link/MachO/CodeSignature.zig+2-2| ... | ... | @@ -7,12 +7,12 @@ const log = std.log.scoped(.link); |
| 7 | 7 | const macho = std.macho; |
| 8 | 8 | const mem = std.mem; |
| 9 | 9 | const testing = std.testing; |
| 10 | const ThreadPool = std.Thread.Pool; | |
| 11 | const WaitGroup = std.Thread.WaitGroup; | |
| 10 | 12 | |
| 11 | 13 | const Allocator = mem.Allocator; |
| 12 | 14 | const Compilation = @import("../../Compilation.zig"); |
| 13 | 15 | const Sha256 = std.crypto.hash.sha2.Sha256; |
| 14 | const ThreadPool = @import("../../ThreadPool.zig"); | |
| 15 | const WaitGroup = @import("../../WaitGroup.zig"); | |
| 16 | 16 | |
| 17 | 17 | const hash_size = Sha256.digest_length; |
| 18 | 18 |
src/main.zig+1-1| ... | ... | @@ -9,6 +9,7 @@ const Allocator = mem.Allocator; |
| 9 | 9 | const ArrayList = std.ArrayList; |
| 10 | 10 | const Ast = std.zig.Ast; |
| 11 | 11 | const warn = std.log.warn; |
| 12 | const ThreadPool = std.Thread.Pool; | |
| 12 | 13 | |
| 13 | 14 | const tracy = @import("tracy.zig"); |
| 14 | 15 | const Compilation = @import("Compilation.zig"); |
| ... | ... | @@ -22,7 +23,6 @@ const translate_c = @import("translate_c.zig"); |
| 22 | 23 | const clang = @import("clang.zig"); |
| 23 | 24 | const Cache = std.Build.Cache; |
| 24 | 25 | const target_util = @import("target.zig"); |
| 25 | const ThreadPool = @import("ThreadPool.zig"); | |
| 26 | 26 | const crash_report = @import("crash_report.zig"); |
| 27 | 27 | |
| 28 | 28 | pub const std_options = struct { |
src/test.zig+2-2| ... | ... | @@ -4,14 +4,14 @@ const Allocator = std.mem.Allocator; |
| 4 | 4 | const CrossTarget = std.zig.CrossTarget; |
| 5 | 5 | const print = std.debug.print; |
| 6 | 6 | const assert = std.debug.assert; |
| 7 | const ThreadPool = std.Thread.Pool; | |
| 8 | const WaitGroup = std.Thread.WaitGroup; | |
| 7 | 9 | |
| 8 | 10 | const link = @import("link.zig"); |
| 9 | 11 | const Compilation = @import("Compilation.zig"); |
| 10 | 12 | const Package = @import("Package.zig"); |
| 11 | 13 | const introspect = @import("introspect.zig"); |
| 12 | 14 | const build_options = @import("build_options"); |
| 13 | const ThreadPool = @import("ThreadPool.zig"); | |
| 14 | const WaitGroup = @import("WaitGroup.zig"); | |
| 15 | 15 | const zig_h = link.File.C.zig_h; |
| 16 | 16 | |
| 17 | 17 | const enable_qemu: bool = build_options.enable_qemu; |