| 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,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. | ||
| 12 | pub fn Channel(comptime T: type) type { | 10 | pub 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 | }; |
| 52 | 48 | ||
| 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); | ||
| 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 | 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 | } |
| 76 | 70 | ||
| 77 | /// must be called when all calls to put and get have suspended and no more calls occur | 71 | /// 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 | } |
| 88 | 83 | ||
| 89 | /// puts a data item in the channel. The function returns when the value has been added to the | 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,17 +91,6 @@ pub fn Channel(comptime T: type) type { |
| 96 | .data = data, | 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 | 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 | }); |
| 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 | 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 | //} |
| 160 | 132 | ||
| 161 | /// Await this function to get an item from the channel. If the buffer is empty and there are no | 133 | /// 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 channel | 135 | 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 values | 136 | // TODO integrate this function with named return values |
| 167 | // so we can get rid of this extra result copy | 137 | // 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; |
| 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 | 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; |
| 239 | 196 | ||
| 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); |
| 259 | 216 | ||
| 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; |
| 267 | 224 | ||
| 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; |
| 272 | 229 | ||
| ... | @@ -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/3251 | 272 | // https://github.com/ziglang/zig/issues/3251 |
| 316 | if (builtin.os == .freebsd) return error.SkipZigTest; | 273 | if (builtin.os == .freebsd) return error.SkipZigTest; |
| 317 | 274 | ||
| 318 | var loop: Loop = undefined; | 275 | // TODO provide a way to run tests in evented I/O mode |
| 319 | // TODO make a multi threaded test | 276 | if (!std.io.is_async) return error.SkipZigTest; |
| 320 | try loop.initSingleThreaded(std.heap.direct_allocator); | ||
| 321 | defer loop.deinit(); | ||
| 322 | 277 | ||
| 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(); | ||
| 325 | 281 | ||
| 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); |
| 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 | const value1 = channel.get(); | 290 | const value1 = channel.get(); |
| 334 | testing.expect(value1 == 1234); | 291 | testing.expect(value1 == 1234); |
| 335 | 292 |
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/3251 | 88 | // https://github.com/ziglang/zig/issues/3251 |
| 89 | if (builtin.os == .freebsd) return error.SkipZigTest; | 89 | if (builtin.os == .freebsd) return error.SkipZigTest; |
| 90 | 90 | // 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; |
| 92 | 92 | ||
| 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(); |
| 96 | 96 | ||
| 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/1908 | 87 | // https://github.com/ziglang/zig/issues/1908 |
| 88 | if (builtin.single_threaded) return error.SkipZigTest; | 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 | 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(); |
| 95 | 96 | ||
| 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, and | 9 | /// 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. | ||
| 12 | pub const Lock = struct { | 13 | pub const Lock = struct { |
| 13 | loop: *Loop, | 14 | loop: *Loop, |
| 14 | shared_bit: u8, // TODO make this a bool | 15 | 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/3251 | 126 | // TODO https://github.com/ziglang/zig/issues/3251 |
| 126 | if (builtin.os == .freebsd) return error.SkipZigTest; | 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 | 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(); |
| 133 | 135 | ||
| 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 *Loop | 96 | /// 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/2765 | 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 | 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 | } |
| 106 | 106 | ||
| ... | @@ -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 *Loop | 108 | /// 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/2765 | 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 { | 111 | pub fn initSingleThreaded(self: *Loop) !void { |
| 112 | return self.initInternal(allocator, 1); | 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 | /// 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 *Loop | 118 | /// 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/2765 | 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 { | 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 | } |
| 126 | 127 | ||
| 127 | /// Thread count is the total thread count. The thread pool size will be | 128 | /// 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/1908 | 935 | // https://github.com/ziglang/zig/issues/1908 |
| 933 | if (builtin.single_threaded) return error.SkipZigTest; | 936 | if (builtin.single_threaded) return error.SkipZigTest; |
| 934 | 937 | ||
| 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(); |
| 940 | 941 | ||
| 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/1908 | 946 | // https://github.com/ziglang/zig/issues/1908 |
| 946 | if (builtin.single_threaded) return error.SkipZigTest; | 947 | if (builtin.single_threaded) return error.SkipZigTest; |
| 947 | 948 | ||
| 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(); |
| 953 | 952 | ||
| 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 | ||
| 14 | pub const RwLock = struct { | 15 | pub const RwLock = struct { |
| 15 | loop: *Loop, | 16 | loop: *Loop, |
| 16 | shared_state: u8, // TODO make this an enum | 17 | 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/1908 | 213 | // https://github.com/ziglang/zig/issues/1908 |
| 213 | if (builtin.single_threaded) return error.SkipZigTest; | 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 | 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(); |
| 220 | 222 | ||
| 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 | } |
| 36 | 36 | ||
| 37 | extern fn wasm_freestanding_start() void { | 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 | extern fn EfiMain(handle: uefi.Handle, system_table: *uefi.tables.SystemTable) usize { | 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,7 +65,9 @@ extern fn EfiMain(handle: uefi.Handle, system_table: *uefi.tables.SystemTable) u |
| 63 | 65 | ||
| 64 | nakedcc fn _start() noreturn { | 66 | nakedcc 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 | } |
| 68 | 72 | ||
| 69 | switch (builtin.arch) { | 73 | switch (builtin.arch) { |
| ... | @@ -110,7 +114,7 @@ extern fn WinMainCRTStartup() noreturn { | ... | @@ -110,7 +114,7 @@ extern fn WinMainCRTStartup() noreturn { |
| 110 | 114 | ||
| 111 | std.debug.maybeEnableSegfaultHandler(); | 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 | // TODO https://github.com/ziglang/zig/issues/265 | 120 | // 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 { |
| 170 | 174 | ||
| 171 | std.debug.maybeEnableSegfaultHandler(); | 175 | std.debug.maybeEnableSegfaultHandler(); |
| 172 | 176 | ||
| 173 | return callMain(); | 177 | return initEventLoopAndCallMain(); |
| 174 | } | 178 | } |
| 175 | 179 | ||
| 176 | extern fn main(c_argc: i32, c_argv: [*][*]u8, c_envp: [*]?[*]u8) i32 { | 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,7 +189,32 @@ const bad_main_ret = "expected return type of main to be 'void', '!void', 'noret |
| 185 | 189 | ||
| 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. |
| 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 | switch (@typeInfo(@typeOf(root.main).ReturnType)) { | 218 | switch (@typeInfo(@typeOf(root.main).ReturnType)) { |
| 190 | .NoReturn => { | 219 | .NoReturn => { |
| 191 | root.main(); | 220 | root.main(); |