| author | |
| committer | |
| log | d3d3e4e374e47b275dd3e0483634852b2d0a56d8 |
| tree | 31336c781d33854a0734006e9b0076d625886f31 |
| parent | 788848e123c056b6204f7555a0118377aa4bd8e1 |
| signature |
7 files changed, 95 insertions(+), 105 deletions(-)
lib/std/event/channel.zig+31-74| ... | ... | @@ -7,12 +7,8 @@ const Loop = std.event.Loop; |
| 7 | 7 | /// Many producer, many consumer, thread-safe, runtime configurable buffer size. |
| 8 | 8 | /// When buffer is empty, consumers suspend and are resumed by producers. |
| 9 | 9 | /// When buffer is full, producers suspend and are resumed by consumers. |
| 10 | /// TODO now that async function rewrite has landed, this API should be adjusted | |
| 11 | /// to not use the event loop's allocator, and to not require allocation. | |
| 12 | 10 | pub fn Channel(comptime T: type) type { |
| 13 | 11 | return struct { |
| 14 | loop: *Loop, | |
| 15 | ||
| 16 | 12 | getters: std.atomic.Queue(GetNode), |
| 17 | 13 | or_null_queue: std.atomic.Queue(*std.atomic.Queue(GetNode).Node), |
| 18 | 14 | putters: std.atomic.Queue(PutNode), |
| ... | ... | @@ -50,16 +46,17 @@ pub fn Channel(comptime T: type) type { |
| 50 | 46 | tick_node: *Loop.NextTickNode, |
| 51 | 47 | }; |
| 52 | 48 | |
| 53 | /// Call `destroy` when done. | |
| 54 | pub fn create(loop: *Loop, capacity: usize) !*SelfChannel { | |
| 55 | const buffer_nodes = try loop.allocator.alloc(T, capacity); | |
| 56 | errdefer loop.allocator.free(buffer_nodes); | |
| 49 | const global_event_loop = Loop.instance orelse | |
| 50 | @compileError("std.event.Channel currently only works with event-based I/O"); | |
| 57 | 51 | |
| 58 | const self = try loop.allocator.create(SelfChannel); | |
| 52 | /// Call `deinit` to free resources when done. | |
| 53 | /// `buffer` must live until `deinit` is called. | |
| 54 | /// For a zero length buffer, use `[0]T{}`. | |
| 55 | /// TODO https://github.com/ziglang/zig/issues/2765 | |
| 56 | pub fn init(self: *SelfChannel, buffer: []T) void { | |
| 59 | 57 | self.* = SelfChannel{ |
| 60 | .loop = loop, | |
| 61 | 58 | .buffer_len = 0, |
| 62 | .buffer_nodes = buffer_nodes, | |
| 59 | .buffer_nodes = buffer, | |
| 63 | 60 | .buffer_index = 0, |
| 64 | 61 | .dispatch_lock = 0, |
| 65 | 62 | .need_dispatch = 0, |
| ... | ... | @@ -69,21 +66,19 @@ pub fn Channel(comptime T: type) type { |
| 69 | 66 | .get_count = 0, |
| 70 | 67 | .put_count = 0, |
| 71 | 68 | }; |
| 72 | errdefer loop.allocator.destroy(self); | |
| 73 | ||
| 74 | return self; | |
| 75 | 69 | } |
| 76 | 70 | |
| 77 | /// must be called when all calls to put and get have suspended and no more calls occur | |
| 78 | pub fn destroy(self: *SelfChannel) void { | |
| 71 | /// Must be called when all calls to put and get have suspended and no more calls occur. | |
| 72 | /// This can be omitted if caller can guarantee that the suspended putters and getters | |
| 73 | /// do not need to be run to completion. Note that this may leave awaiters hanging. | |
| 74 | pub fn deinit(self: *SelfChannel) void { | |
| 79 | 75 | while (self.getters.get()) |get_node| { |
| 80 | 76 | resume get_node.data.tick_node.data; |
| 81 | 77 | } |
| 82 | 78 | while (self.putters.get()) |put_node| { |
| 83 | 79 | resume put_node.data.tick_node.data; |
| 84 | 80 | } |
| 85 | self.loop.allocator.free(self.buffer_nodes); | |
| 86 | self.loop.allocator.destroy(self); | |
| 81 | self.* = undefined; | |
| 87 | 82 | } |
| 88 | 83 | |
| 89 | 84 | /// puts a data item in the channel. The function returns when the value has been added to the |
| ... | ... | @@ -96,17 +91,6 @@ pub fn Channel(comptime T: type) type { |
| 96 | 91 | .data = data, |
| 97 | 92 | }); |
| 98 | 93 | |
| 99 | // TODO test canceling a put() | |
| 100 | errdefer { | |
| 101 | _ = @atomicRmw(usize, &self.put_count, .Sub, 1, .SeqCst); | |
| 102 | const need_dispatch = !self.putters.remove(&queue_node); | |
| 103 | self.loop.cancelOnNextTick(&my_tick_node); | |
| 104 | if (need_dispatch) { | |
| 105 | // oops we made the put_count incorrect for a period of time. fix by dispatching. | |
| 106 | _ = @atomicRmw(usize, &self.put_count, .Add, 1, .SeqCst); | |
| 107 | self.dispatch(); | |
| 108 | } | |
| 109 | } | |
| 110 | 94 | suspend { |
| 111 | 95 | self.putters.put(&queue_node); |
| 112 | 96 | _ = @atomicRmw(usize, &self.put_count, .Add, 1, .SeqCst); |
| ... | ... | @@ -128,18 +112,6 @@ pub fn Channel(comptime T: type) type { |
| 128 | 112 | }, |
| 129 | 113 | }); |
| 130 | 114 | |
| 131 | // TODO test canceling a get() | |
| 132 | errdefer { | |
| 133 | _ = @atomicRmw(usize, &self.get_count, .Sub, 1, .SeqCst); | |
| 134 | const need_dispatch = !self.getters.remove(&queue_node); | |
| 135 | self.loop.cancelOnNextTick(&my_tick_node); | |
| 136 | if (need_dispatch) { | |
| 137 | // oops we made the get_count incorrect for a period of time. fix by dispatching. | |
| 138 | _ = @atomicRmw(usize, &self.get_count, .Add, 1, .SeqCst); | |
| 139 | self.dispatch(); | |
| 140 | } | |
| 141 | } | |
| 142 | ||
| 143 | 115 | suspend { |
| 144 | 116 | self.getters.put(&queue_node); |
| 145 | 117 | _ = @atomicRmw(usize, &self.get_count, .Add, 1, .SeqCst); |
| ... | ... | @@ -158,11 +130,9 @@ pub fn Channel(comptime T: type) type { |
| 158 | 130 | // } |
| 159 | 131 | //} |
| 160 | 132 | |
| 161 | /// Await this function to get an item from the channel. If the buffer is empty and there are no | |
| 162 | /// puts waiting, this returns null. | |
| 163 | /// Await is necessary for locking purposes. The function will be resumed after checking the channel | |
| 164 | /// for data and will not wait for data to be available. | |
| 165 | pub async fn getOrNull(self: *SelfChannel) ?T { | |
| 133 | /// Get an item from the channel. If the buffer is empty and there are no | |
| 134 | /// puts waiting, this returns `null`. | |
| 135 | pub fn getOrNull(self: *SelfChannel) ?T { | |
| 166 | 136 | // TODO integrate this function with named return values |
| 167 | 137 | // so we can get rid of this extra result copy |
| 168 | 138 | var result: ?T = null; |
| ... | ... | @@ -179,19 +149,6 @@ pub fn Channel(comptime T: type) type { |
| 179 | 149 | }); |
| 180 | 150 | or_null_node.data = &queue_node; |
| 181 | 151 | |
| 182 | // TODO test canceling getOrNull | |
| 183 | errdefer { | |
| 184 | _ = self.or_null_queue.remove(&or_null_node); | |
| 185 | _ = @atomicRmw(usize, &self.get_count, .Sub, 1, .SeqCst); | |
| 186 | const need_dispatch = !self.getters.remove(&queue_node); | |
| 187 | self.loop.cancelOnNextTick(&my_tick_node); | |
| 188 | if (need_dispatch) { | |
| 189 | // oops we made the get_count incorrect for a period of time. fix by dispatching. | |
| 190 | _ = @atomicRmw(usize, &self.get_count, .Add, 1, .SeqCst); | |
| 191 | self.dispatch(); | |
| 192 | } | |
| 193 | } | |
| 194 | ||
| 195 | 152 | suspend { |
| 196 | 153 | self.getters.put(&queue_node); |
| 197 | 154 | _ = @atomicRmw(usize, &self.get_count, .Add, 1, .SeqCst); |
| ... | ... | @@ -234,7 +191,7 @@ pub fn Channel(comptime T: type) type { |
| 234 | 191 | info.ptr.* = self.buffer_nodes[self.buffer_index -% self.buffer_len]; |
| 235 | 192 | }, |
| 236 | 193 | } |
| 237 | self.loop.onNextTick(get_node.tick_node); | |
| 194 | global_event_loop.onNextTick(get_node.tick_node); | |
| 238 | 195 | self.buffer_len -= 1; |
| 239 | 196 | |
| 240 | 197 | get_count = @atomicRmw(usize, &self.get_count, .Sub, 1, .SeqCst); |
| ... | ... | @@ -254,8 +211,8 @@ pub fn Channel(comptime T: type) type { |
| 254 | 211 | info.ptr.* = put_node.data; |
| 255 | 212 | }, |
| 256 | 213 | } |
| 257 | self.loop.onNextTick(get_node.tick_node); | |
| 258 | self.loop.onNextTick(put_node.tick_node); | |
| 214 | global_event_loop.onNextTick(get_node.tick_node); | |
| 215 | global_event_loop.onNextTick(put_node.tick_node); | |
| 259 | 216 | |
| 260 | 217 | get_count = @atomicRmw(usize, &self.get_count, .Sub, 1, .SeqCst); |
| 261 | 218 | put_count = @atomicRmw(usize, &self.put_count, .Sub, 1, .SeqCst); |
| ... | ... | @@ -266,7 +223,7 @@ pub fn Channel(comptime T: type) type { |
| 266 | 223 | const put_node = &self.putters.get().?.data; |
| 267 | 224 | |
| 268 | 225 | self.buffer_nodes[self.buffer_index] = put_node.data; |
| 269 | self.loop.onNextTick(put_node.tick_node); | |
| 226 | global_event_loop.onNextTick(put_node.tick_node); | |
| 270 | 227 | self.buffer_index +%= 1; |
| 271 | 228 | self.buffer_len += 1; |
| 272 | 229 | |
| ... | ... | @@ -282,7 +239,7 @@ pub fn Channel(comptime T: type) type { |
| 282 | 239 | var remove_count: usize = 0; |
| 283 | 240 | while (self.or_null_queue.get()) |or_null_node| { |
| 284 | 241 | remove_count += @boolToInt(self.getters.remove(or_null_node.data)); |
| 285 | self.loop.onNextTick(or_null_node.data.data.tick_node); | |
| 242 | global_event_loop.onNextTick(or_null_node.data.data.tick_node); | |
| 286 | 243 | } |
| 287 | 244 | if (remove_count != 0) { |
| 288 | 245 | _ = @atomicRmw(usize, &self.get_count, .Sub, remove_count, .SeqCst); |
| ... | ... | @@ -315,21 +272,21 @@ test "std.event.Channel" { |
| 315 | 272 | // https://github.com/ziglang/zig/issues/3251 |
| 316 | 273 | if (builtin.os == .freebsd) return error.SkipZigTest; |
| 317 | 274 | |
| 318 | var loop: Loop = undefined; | |
| 319 | // TODO make a multi threaded test | |
| 320 | try loop.initSingleThreaded(std.heap.direct_allocator); | |
| 321 | defer loop.deinit(); | |
| 275 | // TODO provide a way to run tests in evented I/O mode | |
| 276 | if (!std.io.is_async) return error.SkipZigTest; | |
| 322 | 277 | |
| 323 | const channel = try Channel(i32).create(&loop, 0); | |
| 324 | defer channel.destroy(); | |
| 278 | var channel: Channel(i32) = undefined; | |
| 279 | channel.init([0]i32{}); | |
| 280 | defer channel.deinit(); | |
| 325 | 281 | |
| 326 | const handle = async testChannelGetter(&loop, channel); | |
| 327 | const putter = async testChannelPutter(channel); | |
| 282 | var handle = async testChannelGetter(&channel); | |
| 283 | var putter = async testChannelPutter(&channel); | |
| 328 | 284 | |
| 329 | loop.run(); | |
| 285 | await handle; | |
| 286 | await putter; | |
| 330 | 287 | } |
| 331 | 288 | |
| 332 | async fn testChannelGetter(loop: *Loop, channel: *Channel(i32)) void { | |
| 289 | async fn testChannelGetter(channel: *Channel(i32)) void { | |
| 333 | 290 | const value1 = channel.get(); |
| 334 | 291 | testing.expect(value1 == 1234); |
| 335 | 292 |
lib/std/event/future.zig+3-3| ... | ... | @@ -87,11 +87,11 @@ test "std.event.Future" { |
| 87 | 87 | if (builtin.single_threaded) return error.SkipZigTest; |
| 88 | 88 | // https://github.com/ziglang/zig/issues/3251 |
| 89 | 89 | if (builtin.os == .freebsd) return error.SkipZigTest; |
| 90 | ||
| 91 | const allocator = std.heap.direct_allocator; | |
| 90 | // TODO provide a way to run tests in evented I/O mode | |
| 91 | if (!std.io.is_async) return error.SkipZigTest; | |
| 92 | 92 | |
| 93 | 93 | var loop: Loop = undefined; |
| 94 | try loop.initMultiThreaded(allocator); | |
| 94 | try loop.initMultiThreaded(); | |
| 95 | 95 | defer loop.deinit(); |
| 96 | 96 | |
| 97 | 97 | const handle = async testFuture(&loop); |
lib/std/event/group.zig+3-2| ... | ... | @@ -87,10 +87,11 @@ test "std.event.Group" { |
| 87 | 87 | // https://github.com/ziglang/zig/issues/1908 |
| 88 | 88 | if (builtin.single_threaded) return error.SkipZigTest; |
| 89 | 89 | |
| 90 | const allocator = std.heap.direct_allocator; | |
| 90 | // TODO provide a way to run tests in evented I/O mode | |
| 91 | if (!std.io.is_async) return error.SkipZigTest; | |
| 91 | 92 | |
| 92 | 93 | var loop: Loop = undefined; |
| 93 | try loop.initMultiThreaded(allocator); | |
| 94 | try loop.initMultiThreaded(); | |
| 94 | 95 | defer loop.deinit(); |
| 95 | 96 | |
| 96 | 97 | const handle = async testGroup(&loop); |
lib/std/event/lock.zig+4-2| ... | ... | @@ -9,6 +9,7 @@ const Loop = std.event.Loop; |
| 9 | 9 | /// Functions which are waiting for the lock are suspended, and |
| 10 | 10 | /// are resumed when the lock is released, in order. |
| 11 | 11 | /// Allows only one actor to hold the lock. |
| 12 | /// TODO: make this API also work in blocking I/O mode. | |
| 12 | 13 | pub const Lock = struct { |
| 13 | 14 | loop: *Loop, |
| 14 | 15 | shared_bit: u8, // TODO make this a bool |
| ... | ... | @@ -125,10 +126,11 @@ test "std.event.Lock" { |
| 125 | 126 | // TODO https://github.com/ziglang/zig/issues/3251 |
| 126 | 127 | if (builtin.os == .freebsd) return error.SkipZigTest; |
| 127 | 128 | |
| 128 | const allocator = std.heap.direct_allocator; | |
| 129 | // TODO provide a way to run tests in evented I/O mode | |
| 130 | if (!std.io.is_async) return error.SkipZigTest; | |
| 129 | 131 | |
| 130 | 132 | var loop: Loop = undefined; |
| 131 | try loop.initMultiThreaded(allocator); | |
| 133 | try loop.initMultiThreaded(); | |
| 132 | 134 | defer loop.deinit(); |
| 133 | 135 | |
| 134 | 136 | var lock = Lock.init(&loop); |
lib/std/event/loop.zig+16-17| ... | ... | @@ -96,11 +96,11 @@ pub const Loop = struct { |
| 96 | 96 | /// TODO copy elision / named return values so that the threads referencing *Loop |
| 97 | 97 | /// have the correct pointer value. |
| 98 | 98 | /// https://github.com/ziglang/zig/issues/2761 and https://github.com/ziglang/zig/issues/2765 |
| 99 | pub fn init(self: *Loop, allocator: *mem.Allocator) !void { | |
| 99 | pub fn init(self: *Loop) !void { | |
| 100 | 100 | if (builtin.single_threaded) { |
| 101 | return self.initSingleThreaded(allocator); | |
| 101 | return self.initSingleThreaded(); | |
| 102 | 102 | } else { |
| 103 | return self.initMultiThreaded(allocator); | |
| 103 | return self.initMultiThreaded(); | |
| 104 | 104 | } |
| 105 | 105 | } |
| 106 | 106 | |
| ... | ... | @@ -108,25 +108,28 @@ pub const Loop = struct { |
| 108 | 108 | /// TODO copy elision / named return values so that the threads referencing *Loop |
| 109 | 109 | /// have the correct pointer value. |
| 110 | 110 | /// https://github.com/ziglang/zig/issues/2761 and https://github.com/ziglang/zig/issues/2765 |
| 111 | pub fn initSingleThreaded(self: *Loop, allocator: *mem.Allocator) !void { | |
| 112 | return self.initInternal(allocator, 1); | |
| 111 | pub fn initSingleThreaded(self: *Loop) !void { | |
| 112 | return self.initThreadPool(1); | |
| 113 | 113 | } |
| 114 | 114 | |
| 115 | /// The allocator must be thread-safe because we use it for multiplexing | |
| 116 | /// async functions onto kernel threads. | |
| 117 | 115 | /// After initialization, call run(). |
| 116 | /// This is the same as `initThreadPool` using `Thread.cpuCount` to determine the thread | |
| 117 | /// pool size. | |
| 118 | 118 | /// TODO copy elision / named return values so that the threads referencing *Loop |
| 119 | 119 | /// have the correct pointer value. |
| 120 | 120 | /// https://github.com/ziglang/zig/issues/2761 and https://github.com/ziglang/zig/issues/2765 |
| 121 | pub fn initMultiThreaded(self: *Loop, allocator: *mem.Allocator) !void { | |
| 122 | if (builtin.single_threaded) @compileError("initMultiThreaded unavailable when building in single-threaded mode"); | |
| 121 | pub fn initMultiThreaded(self: *Loop) !void { | |
| 122 | if (builtin.single_threaded) | |
| 123 | @compileError("initMultiThreaded unavailable when building in single-threaded mode"); | |
| 123 | 124 | const core_count = try Thread.cpuCount(); |
| 124 | return self.initInternal(allocator, core_count); | |
| 125 | return self.initThreadPool(core_count); | |
| 125 | 126 | } |
| 126 | 127 | |
| 127 | 128 | /// Thread count is the total thread count. The thread pool size will be |
| 128 | 129 | /// max(thread_count - 1, 0) |
| 129 | fn initInternal(self: *Loop, allocator: *mem.Allocator, thread_count: usize) !void { | |
| 130 | pub fn initThreadPool(self: *Loop, thread_count: usize) !void { | |
| 131 | // TODO: https://github.com/ziglang/zig/issues/3539 | |
| 132 | const allocator = std.heap.direct_allocator; | |
| 130 | 133 | self.* = Loop{ |
| 131 | 134 | .pending_event_count = 1, |
| 132 | 135 | .allocator = allocator, |
| ... | ... | @@ -932,10 +935,8 @@ test "std.event.Loop - basic" { |
| 932 | 935 | // https://github.com/ziglang/zig/issues/1908 |
| 933 | 936 | if (builtin.single_threaded) return error.SkipZigTest; |
| 934 | 937 | |
| 935 | const allocator = std.heap.direct_allocator; | |
| 936 | ||
| 937 | 938 | var loop: Loop = undefined; |
| 938 | try loop.initMultiThreaded(allocator); | |
| 939 | try loop.initMultiThreaded(); | |
| 939 | 940 | defer loop.deinit(); |
| 940 | 941 | |
| 941 | 942 | loop.run(); |
| ... | ... | @@ -945,10 +946,8 @@ test "std.event.Loop - call" { |
| 945 | 946 | // https://github.com/ziglang/zig/issues/1908 |
| 946 | 947 | if (builtin.single_threaded) return error.SkipZigTest; |
| 947 | 948 | |
| 948 | const allocator = std.heap.direct_allocator; | |
| 949 | ||
| 950 | 949 | var loop: Loop = undefined; |
| 951 | try loop.initMultiThreaded(allocator); | |
| 950 | try loop.initMultiThreaded(); | |
| 952 | 951 | defer loop.deinit(); |
| 953 | 952 | |
| 954 | 953 | var did_it = false; |
lib/std/event/rwlock.zig+4-2| ... | ... | @@ -11,6 +11,7 @@ const Loop = std.event.Loop; |
| 11 | 11 | /// Many readers can hold the lock at the same time; however locking for writing is exclusive. |
| 12 | 12 | /// When a read lock is held, it will not be released until the reader queue is empty. |
| 13 | 13 | /// When a write lock is held, it will not be released until the writer queue is empty. |
| 14 | /// TODO: make this API also work in blocking I/O mode | |
| 14 | 15 | pub const RwLock = struct { |
| 15 | 16 | loop: *Loop, |
| 16 | 17 | shared_state: u8, // TODO make this an enum |
| ... | ... | @@ -212,10 +213,11 @@ test "std.event.RwLock" { |
| 212 | 213 | // https://github.com/ziglang/zig/issues/1908 |
| 213 | 214 | if (builtin.single_threaded) return error.SkipZigTest; |
| 214 | 215 | |
| 215 | const allocator = std.heap.direct_allocator; | |
| 216 | // TODO provide a way to run tests in evented I/O mode | |
| 217 | if (!std.io.is_async) return error.SkipZigTest; | |
| 216 | 218 | |
| 217 | 219 | var loop: Loop = undefined; |
| 218 | try loop.initMultiThreaded(allocator); | |
| 220 | try loop.initMultiThreaded(); | |
| 219 | 221 | defer loop.deinit(); |
| 220 | 222 | |
| 221 | 223 | var lock = RwLock.init(&loop); |
lib/std/special/start.zig+34-5| ... | ... | @@ -35,7 +35,9 @@ comptime { |
| 35 | 35 | } |
| 36 | 36 | |
| 37 | 37 | extern fn wasm_freestanding_start() void { |
| 38 | _ = callMain(); | |
| 38 | // This is marked inline because for some reason LLVM in release mode fails to inline it, | |
| 39 | // and we want fewer call frames in stack traces. | |
| 40 | _ = @inlineCall(callMain); | |
| 39 | 41 | } |
| 40 | 42 | |
| 41 | 43 | extern fn EfiMain(handle: uefi.Handle, system_table: *uefi.tables.SystemTable) usize { |
| ... | ... | @@ -63,7 +65,9 @@ extern fn EfiMain(handle: uefi.Handle, system_table: *uefi.tables.SystemTable) u |
| 63 | 65 | |
| 64 | 66 | nakedcc fn _start() noreturn { |
| 65 | 67 | if (builtin.os == builtin.Os.wasi) { |
| 66 | std.os.wasi.proc_exit(callMain()); | |
| 68 | // This is marked inline because for some reason LLVM in release mode fails to inline it, | |
| 69 | // and we want fewer call frames in stack traces. | |
| 70 | std.os.wasi.proc_exit(@inlineCall(callMain)); | |
| 67 | 71 | } |
| 68 | 72 | |
| 69 | 73 | switch (builtin.arch) { |
| ... | ... | @@ -110,7 +114,7 @@ extern fn WinMainCRTStartup() noreturn { |
| 110 | 114 | |
| 111 | 115 | std.debug.maybeEnableSegfaultHandler(); |
| 112 | 116 | |
| 113 | std.os.windows.kernel32.ExitProcess(callMain()); | |
| 117 | std.os.windows.kernel32.ExitProcess(initEventLoopAndCallMain()); | |
| 114 | 118 | } |
| 115 | 119 | |
| 116 | 120 | // TODO https://github.com/ziglang/zig/issues/265 |
| ... | ... | @@ -170,7 +174,7 @@ fn callMainWithArgs(argc: usize, argv: [*][*]u8, envp: [][*]u8) u8 { |
| 170 | 174 | |
| 171 | 175 | std.debug.maybeEnableSegfaultHandler(); |
| 172 | 176 | |
| 173 | return callMain(); | |
| 177 | return initEventLoopAndCallMain(); | |
| 174 | 178 | } |
| 175 | 179 | |
| 176 | 180 | extern fn main(c_argc: i32, c_argv: [*][*]u8, c_envp: [*]?[*]u8) i32 { |
| ... | ... | @@ -185,7 +189,32 @@ const bad_main_ret = "expected return type of main to be 'void', '!void', 'noret |
| 185 | 189 | |
| 186 | 190 | // This is marked inline because for some reason LLVM in release mode fails to inline it, |
| 187 | 191 | // and we want fewer call frames in stack traces. |
| 188 | inline fn callMain() u8 { | |
| 192 | inline fn initEventLoopAndCallMain() u8 { | |
| 193 | if (std.event.Loop.instance) |loop| { | |
| 194 | loop.init() catch |err| { | |
| 195 | std.debug.warn("error: {}\n", @errorName(err)); | |
| 196 | if (@errorReturnTrace()) |trace| { | |
| 197 | std.debug.dumpStackTrace(trace.*); | |
| 198 | } | |
| 199 | return 1; | |
| 200 | }; | |
| 201 | defer loop.deinit(); | |
| 202 | ||
| 203 | var result: u8 = undefined; | |
| 204 | var frame: @Frame(callMain) = undefined; | |
| 205 | _ = @asyncCall(&frame, &result, callMain); | |
| 206 | loop.run(); | |
| 207 | return result; | |
| 208 | } else { | |
| 209 | // This is marked inline because for some reason LLVM in release mode fails to inline it, | |
| 210 | // and we want fewer call frames in stack traces. | |
| 211 | return @inlineCall(callMain); | |
| 212 | } | |
| 213 | } | |
| 214 | ||
| 215 | // This is not marked inline because it is called with @asyncCall when | |
| 216 | // there is an event loop. | |
| 217 | fn callMain() u8 { | |
| 189 | 218 | switch (@typeInfo(@typeOf(root.main).ReturnType)) { |
| 190 | 219 | .NoReturn => { |
| 191 | 220 | root.main(); |