authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2019-10-31 11:41:39-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2019-10-31 11:41:39-04:00
logd3d3e4e374e47b275dd3e0483634852b2d0a56d8
tree31336c781d33854a0734006e9b0076d625886f31
parent788848e123c056b6204f7555a0118377aa4bd8e1
signaturelock-open Commit is signed but in an unrecognized format.

startup code sets up event loop if I/O mode is declared evented


7 files changed, 95 insertions(+), 105 deletions(-)

lib/std/event/channel.zig+31-74
...@@ -7,12 +7,8 @@ const Loop = std.event.Loop;...@@ -7,12 +7,8 @@ const Loop = std.event.Loop;
7/// Many producer, many consumer, thread-safe, runtime configurable buffer size.7/// Many producer, many consumer, thread-safe, runtime configurable buffer size.
8/// When buffer is empty, consumers suspend and are resumed by producers.8/// When buffer is empty, consumers suspend and are resumed by producers.
9/// When buffer is full, producers suspend and are resumed by consumers.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.
12pub fn Channel(comptime T: type) type {10pub fn Channel(comptime T: type) type {
13 return struct {11 return struct {
14 loop: *Loop,
15
16 getters: std.atomic.Queue(GetNode),12 getters: std.atomic.Queue(GetNode),
17 or_null_queue: std.atomic.Queue(*std.atomic.Queue(GetNode).Node),13 or_null_queue: std.atomic.Queue(*std.atomic.Queue(GetNode).Node),
18 putters: std.atomic.Queue(PutNode),14 putters: std.atomic.Queue(PutNode),
...@@ -50,16 +46,17 @@ pub fn Channel(comptime T: type) type {...@@ -50,16 +46,17 @@ pub fn Channel(comptime T: type) type {
50 tick_node: *Loop.NextTickNode,46 tick_node: *Loop.NextTickNode,
51 };47 };
5248
53 /// Call `destroy` when done.49 const global_event_loop = Loop.instance orelse
54 pub fn create(loop: *Loop, capacity: usize) !*SelfChannel {50 @compileError("std.event.Channel currently only works with event-based I/O");
55 const buffer_nodes = try loop.allocator.alloc(T, capacity);
56 errdefer loop.allocator.free(buffer_nodes);
5751
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 self.* = SelfChannel{57 self.* = SelfChannel{
60 .loop = loop,
61 .buffer_len = 0,58 .buffer_len = 0,
62 .buffer_nodes = buffer_nodes,59 .buffer_nodes = buffer,
63 .buffer_index = 0,60 .buffer_index = 0,
64 .dispatch_lock = 0,61 .dispatch_lock = 0,
65 .need_dispatch = 0,62 .need_dispatch = 0,
...@@ -69,21 +66,19 @@ pub fn Channel(comptime T: type) type {...@@ -69,21 +66,19 @@ pub fn Channel(comptime T: type) type {
69 .get_count = 0,66 .get_count = 0,
70 .put_count = 0,67 .put_count = 0,
71 };68 };
72 errdefer loop.allocator.destroy(self);
73
74 return self;
75 }69 }
7670
77 /// must be called when all calls to put and get have suspended and no more calls occur71 /// Must be called when all calls to put and get have suspended and no more calls occur.
78 pub fn destroy(self: *SelfChannel) void {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 while (self.getters.get()) |get_node| {75 while (self.getters.get()) |get_node| {
80 resume get_node.data.tick_node.data;76 resume get_node.data.tick_node.data;
81 }77 }
82 while (self.putters.get()) |put_node| {78 while (self.putters.get()) |put_node| {
83 resume put_node.data.tick_node.data;79 resume put_node.data.tick_node.data;
84 }80 }
85 self.loop.allocator.free(self.buffer_nodes);81 self.* = undefined;
86 self.loop.allocator.destroy(self);
87 }82 }
8883
89 /// puts a data item in the channel. The function returns when the value has been added to the84 /// 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,17 +91,6 @@ pub fn Channel(comptime T: type) type {
96 .data = data,91 .data = data,
97 });92 });
9893
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 suspend {94 suspend {
111 self.putters.put(&queue_node);95 self.putters.put(&queue_node);
112 _ = @atomicRmw(usize, &self.put_count, .Add, 1, .SeqCst);96 _ = @atomicRmw(usize, &self.put_count, .Add, 1, .SeqCst);
...@@ -128,18 +112,6 @@ pub fn Channel(comptime T: type) type {...@@ -128,18 +112,6 @@ pub fn Channel(comptime T: type) type {
128 },112 },
129 });113 });
130114
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 suspend {115 suspend {
144 self.getters.put(&queue_node);116 self.getters.put(&queue_node);
145 _ = @atomicRmw(usize, &self.get_count, .Add, 1, .SeqCst);117 _ = @atomicRmw(usize, &self.get_count, .Add, 1, .SeqCst);
...@@ -158,11 +130,9 @@ pub fn Channel(comptime T: type) type {...@@ -158,11 +130,9 @@ pub fn Channel(comptime T: type) type {
158 // }130 // }
159 //}131 //}
160132
161 /// Await this function to get an item from the channel. If the buffer is empty and there are no133 /// Get an item from the channel. If the buffer is empty and there are no
162 /// puts waiting, this returns null.134 /// puts waiting, this returns `null`.
163 /// Await is necessary for locking purposes. The function will be resumed after checking the channel135 pub fn getOrNull(self: *SelfChannel) ?T {
164 /// for data and will not wait for data to be available.
165 pub async fn getOrNull(self: *SelfChannel) ?T {
166 // TODO integrate this function with named return values136 // TODO integrate this function with named return values
167 // so we can get rid of this extra result copy137 // so we can get rid of this extra result copy
168 var result: ?T = null;138 var result: ?T = null;
...@@ -179,19 +149,6 @@ pub fn Channel(comptime T: type) type {...@@ -179,19 +149,6 @@ pub fn Channel(comptime T: type) type {
179 });149 });
180 or_null_node.data = &queue_node;150 or_null_node.data = &queue_node;
181151
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 suspend {152 suspend {
196 self.getters.put(&queue_node);153 self.getters.put(&queue_node);
197 _ = @atomicRmw(usize, &self.get_count, .Add, 1, .SeqCst);154 _ = @atomicRmw(usize, &self.get_count, .Add, 1, .SeqCst);
...@@ -234,7 +191,7 @@ pub fn Channel(comptime T: type) type {...@@ -234,7 +191,7 @@ pub fn Channel(comptime T: type) type {
234 info.ptr.* = self.buffer_nodes[self.buffer_index -% self.buffer_len];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 self.buffer_len -= 1;195 self.buffer_len -= 1;
239196
240 get_count = @atomicRmw(usize, &self.get_count, .Sub, 1, .SeqCst);197 get_count = @atomicRmw(usize, &self.get_count, .Sub, 1, .SeqCst);
...@@ -254,8 +211,8 @@ pub fn Channel(comptime T: type) type {...@@ -254,8 +211,8 @@ pub fn Channel(comptime T: type) type {
254 info.ptr.* = put_node.data;211 info.ptr.* = put_node.data;
255 },212 },
256 }213 }
257 self.loop.onNextTick(get_node.tick_node);214 global_event_loop.onNextTick(get_node.tick_node);
258 self.loop.onNextTick(put_node.tick_node);215 global_event_loop.onNextTick(put_node.tick_node);
259216
260 get_count = @atomicRmw(usize, &self.get_count, .Sub, 1, .SeqCst);217 get_count = @atomicRmw(usize, &self.get_count, .Sub, 1, .SeqCst);
261 put_count = @atomicRmw(usize, &self.put_count, .Sub, 1, .SeqCst);218 put_count = @atomicRmw(usize, &self.put_count, .Sub, 1, .SeqCst);
...@@ -266,7 +223,7 @@ pub fn Channel(comptime T: type) type {...@@ -266,7 +223,7 @@ pub fn Channel(comptime T: type) type {
266 const put_node = &self.putters.get().?.data;223 const put_node = &self.putters.get().?.data;
267224
268 self.buffer_nodes[self.buffer_index] = put_node.data;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 self.buffer_index +%= 1;227 self.buffer_index +%= 1;
271 self.buffer_len += 1;228 self.buffer_len += 1;
272229
...@@ -282,7 +239,7 @@ pub fn Channel(comptime T: type) type {...@@ -282,7 +239,7 @@ pub fn Channel(comptime T: type) type {
282 var remove_count: usize = 0;239 var remove_count: usize = 0;
283 while (self.or_null_queue.get()) |or_null_node| {240 while (self.or_null_queue.get()) |or_null_node| {
284 remove_count += @boolToInt(self.getters.remove(or_null_node.data));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 if (remove_count != 0) {244 if (remove_count != 0) {
288 _ = @atomicRmw(usize, &self.get_count, .Sub, remove_count, .SeqCst);245 _ = @atomicRmw(usize, &self.get_count, .Sub, remove_count, .SeqCst);
...@@ -315,21 +272,21 @@ test "std.event.Channel" {...@@ -315,21 +272,21 @@ test "std.event.Channel" {
315 // https://github.com/ziglang/zig/issues/3251272 // https://github.com/ziglang/zig/issues/3251
316 if (builtin.os == .freebsd) return error.SkipZigTest;273 if (builtin.os == .freebsd) return error.SkipZigTest;
317274
318 var loop: Loop = undefined;275 // TODO provide a way to run tests in evented I/O mode
319 // TODO make a multi threaded test276 if (!std.io.is_async) return error.SkipZigTest;
320 try loop.initSingleThreaded(std.heap.direct_allocator);
321 defer loop.deinit();
322277
323 const channel = try Channel(i32).create(&loop, 0);278 var channel: Channel(i32) = undefined;
324 defer channel.destroy();279 channel.init([0]i32{});
280 defer channel.deinit();
325281
326 const handle = async testChannelGetter(&loop, channel);282 var handle = async testChannelGetter(&channel);
327 const putter = async testChannelPutter(channel);283 var putter = async testChannelPutter(&channel);
328284
329 loop.run();285 await handle;
286 await putter;
330}287}
331288
332async fn testChannelGetter(loop: *Loop, channel: *Channel(i32)) void {289async fn testChannelGetter(channel: *Channel(i32)) void {
333 const value1 = channel.get();290 const value1 = channel.get();
334 testing.expect(value1 == 1234);291 testing.expect(value1 == 1234);
335292
lib/std/event/future.zig+3-3
...@@ -87,11 +87,11 @@ test "std.event.Future" {...@@ -87,11 +87,11 @@ test "std.event.Future" {
87 if (builtin.single_threaded) return error.SkipZigTest;87 if (builtin.single_threaded) return error.SkipZigTest;
88 // https://github.com/ziglang/zig/issues/325188 // https://github.com/ziglang/zig/issues/3251
89 if (builtin.os == .freebsd) return error.SkipZigTest;89 if (builtin.os == .freebsd) return error.SkipZigTest;
9090 // TODO provide a way to run tests in evented I/O mode
91 const allocator = std.heap.direct_allocator;91 if (!std.io.is_async) return error.SkipZigTest;
9292
93 var loop: Loop = undefined;93 var loop: Loop = undefined;
94 try loop.initMultiThreaded(allocator);94 try loop.initMultiThreaded();
95 defer loop.deinit();95 defer loop.deinit();
9696
97 const handle = async testFuture(&loop);97 const handle = async testFuture(&loop);
lib/std/event/group.zig+3-2
...@@ -87,10 +87,11 @@ test "std.event.Group" {...@@ -87,10 +87,11 @@ test "std.event.Group" {
87 // https://github.com/ziglang/zig/issues/190887 // https://github.com/ziglang/zig/issues/1908
88 if (builtin.single_threaded) return error.SkipZigTest;88 if (builtin.single_threaded) return error.SkipZigTest;
8989
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;
9192
92 var loop: Loop = undefined;93 var loop: Loop = undefined;
93 try loop.initMultiThreaded(allocator);94 try loop.initMultiThreaded();
94 defer loop.deinit();95 defer loop.deinit();
9596
96 const handle = async testGroup(&loop);97 const handle = async testGroup(&loop);
lib/std/event/lock.zig+4-2
...@@ -9,6 +9,7 @@ const Loop = std.event.Loop;...@@ -9,6 +9,7 @@ const Loop = std.event.Loop;
9/// Functions which are waiting for the lock are suspended, and9/// Functions which are waiting for the lock are suspended, and
10/// are resumed when the lock is released, in order.10/// are resumed when the lock is released, in order.
11/// Allows only one actor to hold the lock.11/// Allows only one actor to hold the lock.
12/// TODO: make this API also work in blocking I/O mode.
12pub const Lock = struct {13pub const Lock = struct {
13 loop: *Loop,14 loop: *Loop,
14 shared_bit: u8, // TODO make this a bool15 shared_bit: u8, // TODO make this a bool
...@@ -125,10 +126,11 @@ test "std.event.Lock" {...@@ -125,10 +126,11 @@ test "std.event.Lock" {
125 // TODO https://github.com/ziglang/zig/issues/3251126 // TODO https://github.com/ziglang/zig/issues/3251
126 if (builtin.os == .freebsd) return error.SkipZigTest;127 if (builtin.os == .freebsd) return error.SkipZigTest;
127128
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;
129131
130 var loop: Loop = undefined;132 var loop: Loop = undefined;
131 try loop.initMultiThreaded(allocator);133 try loop.initMultiThreaded();
132 defer loop.deinit();134 defer loop.deinit();
133135
134 var lock = Lock.init(&loop);136 var lock = Lock.init(&loop);
lib/std/event/loop.zig+16-17
...@@ -96,11 +96,11 @@ pub const Loop = struct {...@@ -96,11 +96,11 @@ pub const Loop = struct {
96 /// TODO copy elision / named return values so that the threads referencing *Loop96 /// TODO copy elision / named return values so that the threads referencing *Loop
97 /// have the correct pointer value.97 /// have the correct pointer value.
98 /// https://github.com/ziglang/zig/issues/2761 and https://github.com/ziglang/zig/issues/276598 /// 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 if (builtin.single_threaded) {100 if (builtin.single_threaded) {
101 return self.initSingleThreaded(allocator);101 return self.initSingleThreaded();
102 } else {102 } else {
103 return self.initMultiThreaded(allocator);103 return self.initMultiThreaded();
104 }104 }
105 }105 }
106106
...@@ -108,25 +108,28 @@ pub const Loop = struct {...@@ -108,25 +108,28 @@ pub const Loop = struct {
108 /// TODO copy elision / named return values so that the threads referencing *Loop108 /// TODO copy elision / named return values so that the threads referencing *Loop
109 /// have the correct pointer value.109 /// have the correct pointer value.
110 /// https://github.com/ziglang/zig/issues/2761 and https://github.com/ziglang/zig/issues/2765110 /// 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 {111 pub fn initSingleThreaded(self: *Loop) !void {
112 return self.initInternal(allocator, 1);112 return self.initThreadPool(1);
113 }113 }
114114
115 /// The allocator must be thread-safe because we use it for multiplexing
116 /// async functions onto kernel threads.
117 /// After initialization, call run().115 /// After initialization, call run().
116 /// This is the same as `initThreadPool` using `Thread.cpuCount` to determine the thread
117 /// pool size.
118 /// TODO copy elision / named return values so that the threads referencing *Loop118 /// TODO copy elision / named return values so that the threads referencing *Loop
119 /// have the correct pointer value.119 /// have the correct pointer value.
120 /// https://github.com/ziglang/zig/issues/2761 and https://github.com/ziglang/zig/issues/2765120 /// 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 {121 pub fn initMultiThreaded(self: *Loop) !void {
122 if (builtin.single_threaded) @compileError("initMultiThreaded unavailable when building in single-threaded mode");122 if (builtin.single_threaded)
123 @compileError("initMultiThreaded unavailable when building in single-threaded mode");
123 const core_count = try Thread.cpuCount();124 const core_count = try Thread.cpuCount();
124 return self.initInternal(allocator, core_count);125 return self.initThreadPool(core_count);
125 }126 }
126127
127 /// Thread count is the total thread count. The thread pool size will be128 /// Thread count is the total thread count. The thread pool size will be
128 /// max(thread_count - 1, 0)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 self.* = Loop{133 self.* = Loop{
131 .pending_event_count = 1,134 .pending_event_count = 1,
132 .allocator = allocator,135 .allocator = allocator,
...@@ -932,10 +935,8 @@ test "std.event.Loop - basic" {...@@ -932,10 +935,8 @@ test "std.event.Loop - basic" {
932 // https://github.com/ziglang/zig/issues/1908935 // https://github.com/ziglang/zig/issues/1908
933 if (builtin.single_threaded) return error.SkipZigTest;936 if (builtin.single_threaded) return error.SkipZigTest;
934937
935 const allocator = std.heap.direct_allocator;
936
937 var loop: Loop = undefined;938 var loop: Loop = undefined;
938 try loop.initMultiThreaded(allocator);939 try loop.initMultiThreaded();
939 defer loop.deinit();940 defer loop.deinit();
940941
941 loop.run();942 loop.run();
...@@ -945,10 +946,8 @@ test "std.event.Loop - call" {...@@ -945,10 +946,8 @@ test "std.event.Loop - call" {
945 // https://github.com/ziglang/zig/issues/1908946 // https://github.com/ziglang/zig/issues/1908
946 if (builtin.single_threaded) return error.SkipZigTest;947 if (builtin.single_threaded) return error.SkipZigTest;
947948
948 const allocator = std.heap.direct_allocator;
949
950 var loop: Loop = undefined;949 var loop: Loop = undefined;
951 try loop.initMultiThreaded(allocator);950 try loop.initMultiThreaded();
952 defer loop.deinit();951 defer loop.deinit();
953952
954 var did_it = false;953 var did_it = false;
lib/std/event/rwlock.zig+4-2
...@@ -11,6 +11,7 @@ const Loop = std.event.Loop;...@@ -11,6 +11,7 @@ const Loop = std.event.Loop;
11/// Many readers can hold the lock at the same time; however locking for writing is exclusive.11/// Many readers can hold the lock at the same time; however locking for writing is exclusive.
12/// When a read lock is held, it will not be released until the reader queue is empty.12/// When a read lock is held, it will not be released until the reader queue is empty.
13/// When a write lock is held, it will not be released until the writer queue is empty.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
14pub const RwLock = struct {15pub const RwLock = struct {
15 loop: *Loop,16 loop: *Loop,
16 shared_state: u8, // TODO make this an enum17 shared_state: u8, // TODO make this an enum
...@@ -212,10 +213,11 @@ test "std.event.RwLock" {...@@ -212,10 +213,11 @@ test "std.event.RwLock" {
212 // https://github.com/ziglang/zig/issues/1908213 // https://github.com/ziglang/zig/issues/1908
213 if (builtin.single_threaded) return error.SkipZigTest;214 if (builtin.single_threaded) return error.SkipZigTest;
214215
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;
216218
217 var loop: Loop = undefined;219 var loop: Loop = undefined;
218 try loop.initMultiThreaded(allocator);220 try loop.initMultiThreaded();
219 defer loop.deinit();221 defer loop.deinit();
220222
221 var lock = RwLock.init(&loop);223 var lock = RwLock.init(&loop);
lib/std/special/start.zig+34-5
...@@ -35,7 +35,9 @@ comptime {...@@ -35,7 +35,9 @@ comptime {
35}35}
3636
37extern fn wasm_freestanding_start() void {37extern 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}
4042
41extern fn EfiMain(handle: uefi.Handle, system_table: *uefi.tables.SystemTable) usize {43extern 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,7 +65,9 @@ extern fn EfiMain(handle: uefi.Handle, system_table: *uefi.tables.SystemTable) u
6365
64nakedcc fn _start() noreturn {66nakedcc fn _start() noreturn {
65 if (builtin.os == builtin.Os.wasi) {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 }
6872
69 switch (builtin.arch) {73 switch (builtin.arch) {
...@@ -110,7 +114,7 @@ extern fn WinMainCRTStartup() noreturn {...@@ -110,7 +114,7 @@ extern fn WinMainCRTStartup() noreturn {
110114
111 std.debug.maybeEnableSegfaultHandler();115 std.debug.maybeEnableSegfaultHandler();
112116
113 std.os.windows.kernel32.ExitProcess(callMain());117 std.os.windows.kernel32.ExitProcess(initEventLoopAndCallMain());
114}118}
115119
116// TODO https://github.com/ziglang/zig/issues/265120// TODO https://github.com/ziglang/zig/issues/265
...@@ -170,7 +174,7 @@ fn callMainWithArgs(argc: usize, argv: [*][*]u8, envp: [][*]u8) u8 {...@@ -170,7 +174,7 @@ fn callMainWithArgs(argc: usize, argv: [*][*]u8, envp: [][*]u8) u8 {
170174
171 std.debug.maybeEnableSegfaultHandler();175 std.debug.maybeEnableSegfaultHandler();
172176
173 return callMain();177 return initEventLoopAndCallMain();
174}178}
175179
176extern fn main(c_argc: i32, c_argv: [*][*]u8, c_envp: [*]?[*]u8) i32 {180extern 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,7 +189,32 @@ const bad_main_ret = "expected return type of main to be 'void', '!void', 'noret
185189
186// This is marked inline because for some reason LLVM in release mode fails to inline it,190// This is marked inline because for some reason LLVM in release mode fails to inline it,
187// and we want fewer call frames in stack traces.191// and we want fewer call frames in stack traces.
188inline fn callMain() u8 {192inline 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.
217fn callMain() u8 {
189 switch (@typeInfo(@typeOf(root.main).ReturnType)) {218 switch (@typeInfo(@typeOf(root.main).ReturnType)) {
190 .NoReturn => {219 .NoReturn => {
191 root.main();220 root.main();