authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2019-08-11 19:53:10-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2019-08-11 19:53:10-04:00
log4d8d513e16d308131846d98267bc844bf702e9ce
tree44bf7273c880681aa6193d45a8830510426eff24
parentaf8c6ccb4bcae7baf30f3b1032a98b82f39d9c26
signaturelock-open Commit is signed but in an unrecognized format.

all tests passing


17 files changed, 240 insertions(+), 381 deletions(-)

BRANCH_TODO+5-1
......@@ -1,10 +1,13 @@
1 * for loops need to spill the index. other payload captures probably also need to spill
2 * compile error (instead of crashing) for trying to get @Frame of generic function
3 * compile error (instead of crashing) for trying to async call and passing @Frame of wrong function
4 * `const result = (await a) + (await b);` this causes "Instruction does not dominate all uses" - need spill
15 * compile error for error: expected anyframe->T, found 'anyframe'
26 * compile error for error: expected anyframe->T, found 'i32'
37 * await of a non async function
48 * async call on a non async function
59 * a test where an async function destroys its own frame in a defer
610 * implicit cast of normal function to async function should be allowed when it is inferred to be async
7 * revive std.event.Loop
811 * @typeInfo for @Frame(func)
912 * peer type resolution of *@Frame(func) and anyframe
1013 * peer type resolution of *@Frame(func) and anyframe->T when the return type matches
......@@ -36,3 +39,4 @@
3639 - it can be assumed that these are always available: the awaiter ptr, return ptr if applicable,
3740 error return trace ptr if applicable.
3841 - it can be assumed that it is never cancelled
42 * fix the debug info for variables of async functions
doc/docgen.zig+1-1
......@@ -770,7 +770,7 @@ fn tokenizeAndPrintRaw(docgen_tokenizer: *Tokenizer, out: var, source_token: Tok
770770 .Keyword_or,
771771 .Keyword_orelse,
772772 .Keyword_packed,
773 .Keyword_promise,
773 .Keyword_anyframe,
774774 .Keyword_pub,
775775 .Keyword_resume,
776776 .Keyword_return,
doc/langref.html.in+26-55
......@@ -6024,13 +6024,14 @@ const assert = std.debug.assert;
60246024
60256025var x: i32 = 1;
60266026
6027test "create a coroutine and cancel it" {
6028 const p = try async<std.debug.global_allocator> simpleAsyncFn();
6029 comptime assert(@typeOf(p) == promise->void);
6030 cancel p;
6027test "call an async function" {
6028 var frame = async simpleAsyncFn();
6029 comptime assert(@typeOf(frame) == @Frame(simpleAsyncFn));
60316030 assert(x == 2);
60326031}
6033async<*std.mem.Allocator> fn simpleAsyncFn() void {
6032fn simpleAsyncFn() void {
6033 x += 1;
6034 suspend;
60346035 x += 1;
60356036}
60366037 {#code_end#}
......@@ -6041,60 +6042,33 @@ async<*std.mem.Allocator> fn simpleAsyncFn() void {
60416042 return to the caller or resumer. The following code demonstrates where control flow
60426043 goes:
60436044 </p>
6044 {#code_begin|test#}
6045const std = @import("std");
6046const assert = std.debug.assert;
6047
6048test "coroutine suspend, resume, cancel" {
6049 seq('a');
6050 const p = try async<std.debug.global_allocator> testAsyncSeq();
6051 seq('c');
6052 resume p;
6053 seq('f');
6054 cancel p;
6055 seq('g');
6056
6057 assert(std.mem.eql(u8, points, "abcdefg"));
6058}
6059async fn testAsyncSeq() void {
6060 defer seq('e');
6061
6062 seq('b');
6063 suspend;
6064 seq('d');
6065}
6066var points = [_]u8{0} ** "abcdefg".len;
6067var index: usize = 0;
6068
6069fn seq(c: u8) void {
6070 points[index] = c;
6071 index += 1;
6072}
6073 {#code_end#}
6045 <p>
6046 TODO another test example here
6047 </p>
60746048 <p>
60756049 When an async function suspends itself, it must be sure that it will be
60766050 resumed or canceled somehow, for example by registering its promise handle
60776051 in an event loop. Use a suspend capture block to gain access to the
6078 promise:
6052 promise (TODO this is outdated):
60796053 </p>
60806054 {#code_begin|test#}
60816055const std = @import("std");
60826056const assert = std.debug.assert;
60836057
6058var the_frame: anyframe = undefined;
6059var result = false;
6060
60846061test "coroutine suspend with block" {
6085 const p = try async<std.debug.global_allocator> testSuspendBlock();
6062 _ = async testSuspendBlock();
60866063 std.debug.assert(!result);
6087 resume a_promise;
6064 resume the_frame;
60886065 std.debug.assert(result);
6089 cancel p;
60906066}
60916067
6092var a_promise: promise = undefined;
6093var result = false;
6094async fn testSuspendBlock() void {
6068fn testSuspendBlock() void {
60956069 suspend {
6096 comptime assert(@typeOf(@handle()) == promise->void);
6097 a_promise = @handle();
6070 comptime assert(@typeOf(@frame()) == *@Frame(testSuspendBlock));
6071 the_frame = @frame();
60986072 }
60996073 result = true;
61006074}
......@@ -6124,16 +6098,13 @@ const std = @import("std");
61246098const assert = std.debug.assert;
61256099
61266100test "resume from suspend" {
6127 var buf: [500]u8 = undefined;
6128 var a = &std.heap.FixedBufferAllocator.init(buf[0..]).allocator;
61296101 var my_result: i32 = 1;
6130 const p = try async<a> testResumeFromSuspend(&my_result);
6131 cancel p;
6102 _ = async testResumeFromSuspend(&my_result);
61326103 std.debug.assert(my_result == 2);
61336104}
61346105async fn testResumeFromSuspend(my_result: *i32) void {
61356106 suspend {
6136 resume @handle();
6107 resume @frame();
61376108 }
61386109 my_result.* += 1;
61396110 suspend;
......@@ -6172,30 +6143,30 @@ async fn testResumeFromSuspend(my_result: *i32) void {
61726143const std = @import("std");
61736144const assert = std.debug.assert;
61746145
6175var a_promise: promise = undefined;
6146var the_frame: anyframe = undefined;
61766147var final_result: i32 = 0;
61776148
61786149test "coroutine await" {
61796150 seq('a');
6180 const p = async<std.debug.global_allocator> amain() catch unreachable;
6151 _ = async amain();
61816152 seq('f');
6182 resume a_promise;
6153 resume the_frame;
61836154 seq('i');
61846155 assert(final_result == 1234);
61856156 assert(std.mem.eql(u8, seq_points, "abcdefghi"));
61866157}
61876158async fn amain() void {
61886159 seq('b');
6189 const p = async another() catch unreachable;
6160 var f = async another();
61906161 seq('e');
6191 final_result = await p;
6162 final_result = await f;
61926163 seq('h');
61936164}
61946165async fn another() i32 {
61956166 seq('c');
61966167 suspend {
61976168 seq('d');
6198 a_promise = @handle();
6169 the_frame = @frame();
61996170 }
62006171 seq('g');
62016172 return 1234;
src/analyze.cpp+1-1
......@@ -5325,7 +5325,7 @@ static Error resolve_coro_frame(CodeGen *g, ZigType *frame_type) {
53255325 if (*instruction->name_hint == 0) {
53265326 name = buf_ptr(buf_sprintf("@local%" ZIG_PRI_usize, alloca_i));
53275327 } else {
5328 name = instruction->name_hint;
5328 name = buf_ptr(buf_sprintf("%s.%" ZIG_PRI_usize, instruction->name_hint, alloca_i));
53295329 }
53305330 field_names.append(name);
53315331 field_types.append(child_type);
src/codegen.cpp+27-22
......@@ -535,24 +535,24 @@ static LLVMValueRef make_fn_llvm_value(CodeGen *g, ZigFn *fn) {
535535 // use the ABI alignment, which is fine.
536536 }
537537
538 unsigned init_gen_i = 0;
539 if (!type_has_bits(return_type)) {
540 // nothing to do
541 } else if (type_is_nonnull_ptr(return_type)) {
542 addLLVMAttr(llvm_fn, 0, "nonnull");
543 } else if (!is_async && want_first_arg_sret(g, &fn_type->data.fn.fn_type_id)) {
544 // Sret pointers must not be address 0
545 addLLVMArgAttr(llvm_fn, 0, "nonnull");
546 addLLVMArgAttr(llvm_fn, 0, "sret");
547 if (cc_want_sret_attr(cc)) {
548 addLLVMArgAttr(llvm_fn, 0, "noalias");
549 }
550 init_gen_i = 1;
551 }
552
553538 if (is_async) {
554539 addLLVMArgAttr(llvm_fn, 0, "nonnull");
555540 } else {
541 unsigned init_gen_i = 0;
542 if (!type_has_bits(return_type)) {
543 // nothing to do
544 } else if (type_is_nonnull_ptr(return_type)) {
545 addLLVMAttr(llvm_fn, 0, "nonnull");
546 } else if (want_first_arg_sret(g, &fn_type->data.fn.fn_type_id)) {
547 // Sret pointers must not be address 0
548 addLLVMArgAttr(llvm_fn, 0, "nonnull");
549 addLLVMArgAttr(llvm_fn, 0, "sret");
550 if (cc_want_sret_attr(cc)) {
551 addLLVMArgAttr(llvm_fn, 0, "noalias");
552 }
553 init_gen_i = 1;
554 }
555
556556 // set parameter attributes
557557 FnWalk fn_walk = {};
558558 fn_walk.id = FnWalkIdAttrs;
......@@ -911,7 +911,7 @@ static Buf *panic_msg_buf(PanicMsgId msg_id) {
911911 case PanicMsgIdBadResume:
912912 return buf_create_from_str("resumed an async function which already returned");
913913 case PanicMsgIdBadAwait:
914 return buf_create_from_str("async function awaited/canceled twice");
914 return buf_create_from_str("async function awaited twice");
915915 case PanicMsgIdBadReturn:
916916 return buf_create_from_str("async function returned twice");
917917 case PanicMsgIdResumedAnAwaitingFn:
......@@ -2350,6 +2350,10 @@ static LLVMValueRef ir_render_return_begin(CodeGen *g, IrExecutable *executable,
23502350 return get_handle_value(g, g->cur_ret_ptr, operand_type, get_pointer_to_type(g, operand_type, true));
23512351}
23522352
2353static void set_tail_call_if_appropriate(CodeGen *g, LLVMValueRef call_inst) {
2354 LLVMSetTailCall(call_inst, true);
2355}
2356
23532357static LLVMValueRef ir_render_return(CodeGen *g, IrExecutable *executable, IrInstructionReturn *instruction) {
23542358 if (fn_is_async(g->cur_fn)) {
23552359 LLVMTypeRef usize_type_ref = g->builtin_types.entry_usize->llvm_type;
......@@ -2394,7 +2398,7 @@ static LLVMValueRef ir_render_return(CodeGen *g, IrExecutable *executable, IrIns
23942398 LLVMValueRef their_frame_ptr = LLVMBuildIntToPtr(g->builder, masked_prev_val,
23952399 get_llvm_type(g, any_frame_type), "");
23962400 LLVMValueRef call_inst = gen_resume(g, nullptr, their_frame_ptr, ResumeIdReturn, nullptr);
2397 LLVMSetTailCall(call_inst, true);
2401 set_tail_call_if_appropriate(g, call_inst);
23982402 LLVMBuildRetVoid(g->builder);
23992403
24002404 g->cur_is_after_return = false;
......@@ -4009,7 +4013,7 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr
40094013 LLVMBasicBlockRef call_bb = gen_suspend_begin(g, "CallResume");
40104014
40114015 LLVMValueRef call_inst = gen_resume(g, fn_val, frame_result_loc, ResumeIdCall, nullptr);
4012 LLVMSetTailCall(call_inst, true);
4016 set_tail_call_if_appropriate(g, call_inst);
40134017 LLVMBuildRetVoid(g->builder);
40144018
40154019 LLVMPositionBuilderAtEnd(g->builder, call_bb);
......@@ -5520,7 +5524,7 @@ static LLVMValueRef ir_render_cancel(CodeGen *g, IrExecutable *executable, IrIns
55205524
55215525 LLVMPositionBuilderAtEnd(g->builder, early_return_block);
55225526 LLVMValueRef call_inst = gen_resume(g, nullptr, target_frame_ptr, ResumeIdAwaitEarlyReturn, awaiter_ored_val);
5523 LLVMSetTailCall(call_inst, true);
5527 set_tail_call_if_appropriate(g, call_inst);
55245528 LLVMBuildRetVoid(g->builder);
55255529
55265530 LLVMPositionBuilderAtEnd(g->builder, resume_bb);
......@@ -5556,8 +5560,9 @@ static LLVMValueRef ir_render_await(CodeGen *g, IrExecutable *executable, IrInst
55565560 }
55575561
55585562 // supply the error return trace pointer
5559 LLVMValueRef my_err_ret_trace_val = get_cur_err_ret_trace_val(g, instruction->base.scope);
5560 if (my_err_ret_trace_val != nullptr) {
5563 if (codegen_fn_has_err_ret_tracing_arg(g, result_type)) {
5564 LLVMValueRef my_err_ret_trace_val = get_cur_err_ret_trace_val(g, instruction->base.scope);
5565 assert(my_err_ret_trace_val != nullptr);
55615566 LLVMValueRef err_ret_trace_ptr_ptr = LLVMBuildStructGEP(g->builder, target_frame_ptr,
55625567 frame_index_trace_arg(g, result_type), "");
55635568 LLVMBuildStore(g->builder, my_err_ret_trace_val, err_ret_trace_ptr_ptr);
......@@ -5588,7 +5593,7 @@ static LLVMValueRef ir_render_await(CodeGen *g, IrExecutable *executable, IrInst
55885593 // Tail resume it now, so that it can complete.
55895594 LLVMPositionBuilderAtEnd(g->builder, early_return_block);
55905595 LLVMValueRef call_inst = gen_resume(g, nullptr, target_frame_ptr, ResumeIdAwaitEarlyReturn, awaiter_init_val);
5591 LLVMSetTailCall(call_inst, true);
5596 set_tail_call_if_appropriate(g, call_inst);
55925597 LLVMBuildRetVoid(g->builder);
55935598
55945599 // Rely on the target to resume us from suspension.
src/ir.cpp+3
......@@ -15064,6 +15064,9 @@ static IrInstruction *ir_analyze_async_call(IrAnalyze *ira, IrInstructionCallSrc
1506415064 if (result_loc != nullptr && (type_is_invalid(result_loc->value.type) || instr_is_unreachable(result_loc))) {
1506515065 return result_loc;
1506615066 }
15067 result_loc = ir_implicit_cast(ira, result_loc, get_pointer_to_type(ira->codegen, frame_type, false));
15068 if (type_is_invalid(result_loc->value.type))
15069 return ira->codegen->invalid_instruction;
1506715070 return &ir_build_call_gen(ira, &call_instruction->base, fn_entry, fn_ref, arg_count,
1506815071 casted_args, FnInlineAuto, true, nullptr, result_loc, frame_type)->base;
1506915072}
std/event/channel.zig+6-5
......@@ -77,18 +77,19 @@ pub fn Channel(comptime T: type) type {
7777 /// must be called when all calls to put and get have suspended and no more calls occur
7878 pub fn destroy(self: *SelfChannel) void {
7979 while (self.getters.get()) |get_node| {
80 cancel get_node.data.tick_node.data;
80 resume get_node.data.tick_node.data;
8181 }
8282 while (self.putters.get()) |put_node| {
83 cancel put_node.data.tick_node.data;
83 resume put_node.data.tick_node.data;
8484 }
8585 self.loop.allocator.free(self.buffer_nodes);
8686 self.loop.allocator.destroy(self);
8787 }
8888
89 /// puts a data item in the channel. The promise completes when the value has been added to the
89 /// puts a data item in the channel. The function returns when the value has been added to the
9090 /// buffer, or in the case of a zero size buffer, when the item has been retrieved by a getter.
91 pub async fn put(self: *SelfChannel, data: T) void {
91 /// Or when the channel is destroyed.
92 pub fn put(self: *SelfChannel, data: T) void {
9293 var my_tick_node = Loop.NextTickNode.init(@frame());
9394 var queue_node = std.atomic.Queue(PutNode).Node.init(PutNode{
9495 .tick_node = &my_tick_node,
......@@ -114,7 +115,7 @@ pub fn Channel(comptime T: type) type {
114115 }
115116 }
116117
117 /// await this function to get an item from the channel. If the buffer is empty, the promise will
118 /// await this function to get an item from the channel. If the buffer is empty, the frame will
118119 /// complete when the next item is put in the channel.
119120 pub async fn get(self: *SelfChannel) T {
120121 // TODO integrate this function with named return values
std/event/fs.zig+24-78
......@@ -76,12 +76,8 @@ pub const Request = struct {
7676
7777pub const PWriteVError = error{OutOfMemory} || File.WriteError;
7878
79/// data - just the inner references - must live until pwritev promise completes.
79/// data - just the inner references - must live until pwritev frame completes.
8080pub async fn pwritev(loop: *Loop, fd: fd_t, data: []const []const u8, offset: usize) PWriteVError!void {
81 // workaround for https://github.com/ziglang/zig/issues/1194
82 suspend {
83 resume @handle();
84 }
8581 switch (builtin.os) {
8682 .macosx,
8783 .linux,
......@@ -109,7 +105,7 @@ pub async fn pwritev(loop: *Loop, fd: fd_t, data: []const []const u8, offset: us
109105 }
110106}
111107
112/// data must outlive the returned promise
108/// data must outlive the returned frame
113109pub async fn pwritevWindows(loop: *Loop, fd: fd_t, data: []const []const u8, offset: usize) os.WindowsWriteError!void {
114110 if (data.len == 0) return;
115111 if (data.len == 1) return await (async pwriteWindows(loop, fd, data[0], offset) catch unreachable);
......@@ -123,15 +119,10 @@ pub async fn pwritevWindows(loop: *Loop, fd: fd_t, data: []const []const u8, off
123119}
124120
125121pub async fn pwriteWindows(loop: *Loop, fd: fd_t, data: []const u8, offset: u64) os.WindowsWriteError!void {
126 // workaround for https://github.com/ziglang/zig/issues/1194
127 suspend {
128 resume @handle();
129 }
130
131122 var resume_node = Loop.ResumeNode.Basic{
132123 .base = Loop.ResumeNode{
133124 .id = Loop.ResumeNode.Id.Basic,
134 .handle = @handle(),
125 .handle = @frame(),
135126 .overlapped = windows.OVERLAPPED{
136127 .Internal = 0,
137128 .InternalHigh = 0,
......@@ -166,18 +157,13 @@ pub async fn pwriteWindows(loop: *Loop, fd: fd_t, data: []const u8, offset: u64)
166157 }
167158}
168159
169/// iovecs must live until pwritev promise completes.
160/// iovecs must live until pwritev frame completes.
170161pub async fn pwritevPosix(
171162 loop: *Loop,
172163 fd: fd_t,
173164 iovecs: []const os.iovec_const,
174165 offset: usize,
175166) os.WriteError!void {
176 // workaround for https://github.com/ziglang/zig/issues/1194
177 suspend {
178 resume @handle();
179 }
180
181167 var req_node = RequestNode{
182168 .prev = null,
183169 .next = null,
......@@ -194,7 +180,7 @@ pub async fn pwritevPosix(
194180 .TickNode = Loop.NextTickNode{
195181 .prev = null,
196182 .next = null,
197 .data = @handle(),
183 .data = @frame(),
198184 },
199185 },
200186 },
......@@ -211,13 +197,8 @@ pub async fn pwritevPosix(
211197
212198pub const PReadVError = error{OutOfMemory} || File.ReadError;
213199
214/// data - just the inner references - must live until preadv promise completes.
200/// data - just the inner references - must live until preadv frame completes.
215201pub async fn preadv(loop: *Loop, fd: fd_t, data: []const []u8, offset: usize) PReadVError!usize {
216 // workaround for https://github.com/ziglang/zig/issues/1194
217 suspend {
218 resume @handle();
219 }
220
221202 assert(data.len != 0);
222203 switch (builtin.os) {
223204 .macosx,
......@@ -246,7 +227,7 @@ pub async fn preadv(loop: *Loop, fd: fd_t, data: []const []u8, offset: usize) PR
246227 }
247228}
248229
249/// data must outlive the returned promise
230/// data must outlive the returned frame
250231pub async fn preadvWindows(loop: *Loop, fd: fd_t, data: []const []u8, offset: u64) !usize {
251232 assert(data.len != 0);
252233 if (data.len == 1) return await (async preadWindows(loop, fd, data[0], offset) catch unreachable);
......@@ -272,15 +253,10 @@ pub async fn preadvWindows(loop: *Loop, fd: fd_t, data: []const []u8, offset: u6
272253}
273254
274255pub async fn preadWindows(loop: *Loop, fd: fd_t, data: []u8, offset: u64) !usize {
275 // workaround for https://github.com/ziglang/zig/issues/1194
276 suspend {
277 resume @handle();
278 }
279
280256 var resume_node = Loop.ResumeNode.Basic{
281257 .base = Loop.ResumeNode{
282258 .id = Loop.ResumeNode.Id.Basic,
283 .handle = @handle(),
259 .handle = @frame(),
284260 .overlapped = windows.OVERLAPPED{
285261 .Internal = 0,
286262 .InternalHigh = 0,
......@@ -314,18 +290,13 @@ pub async fn preadWindows(loop: *Loop, fd: fd_t, data: []u8, offset: u64) !usize
314290 return usize(bytes_transferred);
315291}
316292
317/// iovecs must live until preadv promise completes
293/// iovecs must live until preadv frame completes
318294pub async fn preadvPosix(
319295 loop: *Loop,
320296 fd: fd_t,
321297 iovecs: []const os.iovec,
322298 offset: usize,
323299) os.ReadError!usize {
324 // workaround for https://github.com/ziglang/zig/issues/1194
325 suspend {
326 resume @handle();
327 }
328
329300 var req_node = RequestNode{
330301 .prev = null,
331302 .next = null,
......@@ -342,7 +313,7 @@ pub async fn preadvPosix(
342313 .TickNode = Loop.NextTickNode{
343314 .prev = null,
344315 .next = null,
345 .data = @handle(),
316 .data = @frame(),
346317 },
347318 },
348319 },
......@@ -363,11 +334,6 @@ pub async fn openPosix(
363334 flags: u32,
364335 mode: File.Mode,
365336) File.OpenError!fd_t {
366 // workaround for https://github.com/ziglang/zig/issues/1194
367 suspend {
368 resume @handle();
369 }
370
371337 const path_c = try std.os.toPosixPath(path);
372338
373339 var req_node = RequestNode{
......@@ -386,7 +352,7 @@ pub async fn openPosix(
386352 .TickNode = Loop.NextTickNode{
387353 .prev = null,
388354 .next = null,
389 .data = @handle(),
355 .data = @frame(),
390356 },
391357 },
392358 },
......@@ -643,11 +609,6 @@ async fn writeFileWindows(loop: *Loop, path: []const u8, contents: []const u8) !
643609}
644610
645611async fn writeFileModeThread(loop: *Loop, path: []const u8, contents: []const u8, mode: File.Mode) !void {
646 // workaround for https://github.com/ziglang/zig/issues/1194
647 suspend {
648 resume @handle();
649 }
650
651612 const path_with_null = try std.cstr.addNullByte(loop.allocator, path);
652613 defer loop.allocator.free(path_with_null);
653614
......@@ -667,7 +628,7 @@ async fn writeFileModeThread(loop: *Loop, path: []const u8, contents: []const u8
667628 .TickNode = Loop.NextTickNode{
668629 .prev = null,
669630 .next = null,
670 .data = @handle(),
631 .data = @frame(),
671632 },
672633 },
673634 },
......@@ -682,7 +643,7 @@ async fn writeFileModeThread(loop: *Loop, path: []const u8, contents: []const u8
682643 return req_node.data.msg.WriteFile.result;
683644}
684645
685/// The promise resumes when the last data has been confirmed written, but before the file handle
646/// The frame resumes when the last data has been confirmed written, but before the file handle
686647/// is closed.
687648/// Caller owns returned memory.
688649pub async fn readFile(loop: *Loop, file_path: []const u8, max_size: usize) ![]u8 {
......@@ -734,7 +695,7 @@ pub const WatchEventId = enum {
734695//
735696// const FileTable = std.AutoHashMap([]const u8, *Put);
736697// const Put = struct {
737// putter: promise,
698// putter: anyframe,
738699// value_ptr: *V,
739700// };
740701// },
......@@ -748,21 +709,21 @@ pub const WatchEventId = enum {
748709// const WindowsOsData = struct {
749710// table_lock: event.Lock,
750711// dir_table: DirTable,
751// all_putters: std.atomic.Queue(promise),
712// all_putters: std.atomic.Queue(anyframe),
752713// ref_count: std.atomic.Int(usize),
753714//
754715// const DirTable = std.AutoHashMap([]const u8, *Dir);
755716// const FileTable = std.AutoHashMap([]const u16, V);
756717//
757718// const Dir = struct {
758// putter: promise,
719// putter: anyframe,
759720// file_table: FileTable,
760721// table_lock: event.Lock,
761722// };
762723// };
763724//
764725// const LinuxOsData = struct {
765// putter: promise,
726// putter: anyframe,
766727// inotify_fd: i32,
767728// wd_table: WdTable,
768729// table_lock: event.Lock,
......@@ -776,7 +737,7 @@ pub const WatchEventId = enum {
776737// };
777738// };
778739//
779// const FileToHandle = std.AutoHashMap([]const u8, promise);
740// const FileToHandle = std.AutoHashMap([]const u8, anyframe);
780741//
781742// const Self = @This();
782743//
......@@ -811,7 +772,7 @@ pub const WatchEventId = enum {
811772// .table_lock = event.Lock.init(loop),
812773// .dir_table = OsData.DirTable.init(loop.allocator),
813774// .ref_count = std.atomic.Int(usize).init(1),
814// .all_putters = std.atomic.Queue(promise).init(),
775// .all_putters = std.atomic.Queue(anyframe).init(),
815776// },
816777// };
817778// return self;
......@@ -926,14 +887,9 @@ pub const WatchEventId = enum {
926887// }
927888//
928889// async fn kqPutEvents(self: *Self, close_op: *CloseOperation, value: V, out_put: **OsData.Put) void {
929// // TODO https://github.com/ziglang/zig/issues/1194
930// suspend {
931// resume @handle();
932// }
933//
934890// var value_copy = value;
935891// var put = OsData.Put{
936// .putter = @handle(),
892// .putter = @frame(),
937893// .value_ptr = &value_copy,
938894// };
939895// out_put.* = &put;
......@@ -1091,18 +1047,13 @@ pub const WatchEventId = enum {
10911047// }
10921048//
10931049// async fn windowsDirReader(self: *Self, dir_handle: windows.HANDLE, dir: *OsData.Dir) void {
1094// // TODO https://github.com/ziglang/zig/issues/1194
1095// suspend {
1096// resume @handle();
1097// }
1098//
10991050// self.ref();
11001051// defer self.deref();
11011052//
11021053// defer os.close(dir_handle);
11031054//
1104// var putter_node = std.atomic.Queue(promise).Node{
1105// .data = @handle(),
1055// var putter_node = std.atomic.Queue(anyframe).Node{
1056// .data = @frame(),
11061057// .prev = null,
11071058// .next = null,
11081059// };
......@@ -1112,7 +1063,7 @@ pub const WatchEventId = enum {
11121063// var resume_node = Loop.ResumeNode.Basic{
11131064// .base = Loop.ResumeNode{
11141065// .id = Loop.ResumeNode.Id.Basic,
1115// .handle = @handle(),
1066// .handle = @frame(),
11161067// .overlapped = windows.OVERLAPPED{
11171068// .Internal = 0,
11181069// .InternalHigh = 0,
......@@ -1207,17 +1158,12 @@ pub const WatchEventId = enum {
12071158// }
12081159//
12091160// async fn linuxEventPutter(inotify_fd: i32, channel: *event.Channel(Event.Error!Event), out_watch: **Self) void {
1210// // TODO https://github.com/ziglang/zig/issues/1194
1211// suspend {
1212// resume @handle();
1213// }
1214//
12151161// const loop = channel.loop;
12161162//
12171163// var watch = Self{
12181164// .channel = channel,
12191165// .os_data = OsData{
1220// .putter = @handle(),
1166// .putter = @frame(),
12211167// .inotify_fd = inotify_fd,
12221168// .wd_table = OsData.WdTable.init(loop.allocator),
12231169// .table_lock = event.Lock.init(loop),
std/event/future.zig+19-26
......@@ -2,8 +2,6 @@ const std = @import("../std.zig");
22const assert = std.debug.assert;
33const testing = std.testing;
44const builtin = @import("builtin");
5const AtomicRmwOp = builtin.AtomicRmwOp;
6const AtomicOrder = builtin.AtomicOrder;
75const Lock = std.event.Lock;
86const Loop = std.event.Loop;
97
......@@ -23,7 +21,7 @@ pub fn Future(comptime T: type) type {
2321 available: u8,
2422
2523 const Self = @This();
26 const Queue = std.atomic.Queue(promise);
24 const Queue = std.atomic.Queue(anyframe);
2725
2826 pub fn init(loop: *Loop) Self {
2927 return Self{
......@@ -37,10 +35,10 @@ pub fn Future(comptime T: type) type {
3735 /// available.
3836 /// Thread-safe.
3937 pub async fn get(self: *Self) *T {
40 if (@atomicLoad(u8, &self.available, AtomicOrder.SeqCst) == 2) {
38 if (@atomicLoad(u8, &self.available, .SeqCst) == 2) {
4139 return &self.data;
4240 }
43 const held = await (async self.lock.acquire() catch unreachable);
41 const held = self.lock.acquire();
4442 held.release();
4543
4644 return &self.data;
......@@ -49,7 +47,7 @@ pub fn Future(comptime T: type) type {
4947 /// Gets the data without waiting for it. If it's available, a pointer is
5048 /// returned. Otherwise, null is returned.
5149 pub fn getOrNull(self: *Self) ?*T {
52 if (@atomicLoad(u8, &self.available, AtomicOrder.SeqCst) == 2) {
50 if (@atomicLoad(u8, &self.available, .SeqCst) == 2) {
5351 return &self.data;
5452 } else {
5553 return null;
......@@ -62,10 +60,10 @@ pub fn Future(comptime T: type) type {
6260 /// It's not required to call start() before resolve() but it can be useful since
6361 /// this method is thread-safe.
6462 pub async fn start(self: *Self) ?*T {
65 const state = @cmpxchgStrong(u8, &self.available, 0, 1, AtomicOrder.SeqCst, AtomicOrder.SeqCst) orelse return null;
63 const state = @cmpxchgStrong(u8, &self.available, 0, 1, .SeqCst, .SeqCst) orelse return null;
6664 switch (state) {
6765 1 => {
68 const held = await (async self.lock.acquire() catch unreachable);
66 const held = self.lock.acquire();
6967 held.release();
7068 return &self.data;
7169 },
......@@ -77,7 +75,7 @@ pub fn Future(comptime T: type) type {
7775 /// Make the data become available. May be called only once.
7876 /// Before calling this, modify the `data` property.
7977 pub fn resolve(self: *Self) void {
80 const prev = @atomicRmw(u8, &self.available, AtomicRmwOp.Xchg, 2, AtomicOrder.SeqCst);
78 const prev = @atomicRmw(u8, &self.available, .Xchg, 2, .SeqCst);
8179 assert(prev == 0 or prev == 1); // resolve() called twice
8280 Lock.Held.release(Lock.Held{ .lock = &self.lock });
8381 }
......@@ -86,7 +84,7 @@ pub fn Future(comptime T: type) type {
8684
8785test "std.event.Future" {
8886 // https://github.com/ziglang/zig/issues/1908
89 if (builtin.single_threaded or builtin.os != builtin.Os.linux) return error.SkipZigTest;
87 if (builtin.single_threaded) return error.SkipZigTest;
9088
9189 const allocator = std.heap.direct_allocator;
9290
......@@ -94,38 +92,33 @@ test "std.event.Future" {
9492 try loop.initMultiThreaded(allocator);
9593 defer loop.deinit();
9694
97 const handle = try async<allocator> testFuture(&loop);
98 defer cancel handle;
95 const handle = async testFuture(&loop);
9996
10097 loop.run();
10198}
10299
103100async fn testFuture(loop: *Loop) void {
104 suspend {
105 resume @handle();
106 }
107101 var future = Future(i32).init(loop);
108102
109 const a = async waitOnFuture(&future) catch @panic("memory");
110 const b = async waitOnFuture(&future) catch @panic("memory");
111 const c = async resolveFuture(&future) catch @panic("memory");
103 const a = async waitOnFuture(&future);
104 const b = async waitOnFuture(&future);
105 const c = async resolveFuture(&future);
106
107 // TODO make this work:
108 //const result = (await a) + (await b);
109 const a_result = await a;
110 const b_result = await b;
111 const result = a_result + b_result;
112112
113 const result = (await a) + (await b);
114113 cancel c;
115114 testing.expect(result == 12);
116115}
117116
118117async fn waitOnFuture(future: *Future(i32)) i32 {
119 suspend {
120 resume @handle();
121 }
122 return (await (async future.get() catch @panic("memory"))).*;
118 return future.get().*;
123119}
124120
125121async fn resolveFuture(future: *Future(i32)) void {
126 suspend {
127 resume @handle();
128 }
129122 future.data = 6;
130123 future.resolve();
131124}
std/event/group.zig+20-48
......@@ -2,8 +2,6 @@ const std = @import("../std.zig");
22const builtin = @import("builtin");
33const Lock = std.event.Lock;
44const Loop = std.event.Loop;
5const AtomicRmwOp = builtin.AtomicRmwOp;
6const AtomicOrder = builtin.AtomicOrder;
75const testing = std.testing;
86
97/// ReturnType must be `void` or `E!void`
......@@ -16,10 +14,10 @@ pub fn Group(comptime ReturnType: type) type {
1614 const Self = @This();
1715
1816 const Error = switch (@typeInfo(ReturnType)) {
19 builtin.TypeId.ErrorUnion => |payload| payload.error_set,
17 .ErrorUnion => |payload| payload.error_set,
2018 else => void,
2119 };
22 const Stack = std.atomic.Stack(promise->ReturnType);
20 const Stack = std.atomic.Stack(anyframe->ReturnType);
2321
2422 pub fn init(loop: *Loop) Self {
2523 return Self{
......@@ -29,7 +27,7 @@ pub fn Group(comptime ReturnType: type) type {
2927 };
3028 }
3129
32 /// Cancel all the outstanding promises. Can be called even if wait was already called.
30 /// Cancel all the outstanding frames. Can be called even if wait was already called.
3331 pub fn deinit(self: *Self) void {
3432 while (self.coro_stack.pop()) |node| {
3533 cancel node.data;
......@@ -40,8 +38,8 @@ pub fn Group(comptime ReturnType: type) type {
4038 }
4139 }
4240
43 /// Add a promise to the group. Thread-safe.
44 pub fn add(self: *Self, handle: promise->ReturnType) (error{OutOfMemory}!void) {
41 /// Add a frame to the group. Thread-safe.
42 pub fn add(self: *Self, handle: anyframe->ReturnType) (error{OutOfMemory}!void) {
4543 const node = try self.lock.loop.allocator.create(Stack.Node);
4644 node.* = Stack.Node{
4745 .next = undefined,
......@@ -51,7 +49,7 @@ pub fn Group(comptime ReturnType: type) type {
5149 }
5250
5351 /// Add a node to the group. Thread-safe. Cannot fail.
54 /// `node.data` should be the promise handle to add to the group.
52 /// `node.data` should be the frame handle to add to the group.
5553 /// The node's memory should be in the coroutine frame of
5654 /// the handle that is in the node, or somewhere guaranteed to live
5755 /// at least as long.
......@@ -59,40 +57,11 @@ pub fn Group(comptime ReturnType: type) type {
5957 self.coro_stack.push(node);
6058 }
6159
62 /// This is equivalent to an async call, but the async function is added to the group, instead
63 /// of returning a promise. func must be async and have return type ReturnType.
64 /// Thread-safe.
65 pub fn call(self: *Self, comptime func: var, args: ...) (error{OutOfMemory}!void) {
66 const S = struct {
67 async fn asyncFunc(node: **Stack.Node, args2: ...) ReturnType {
68 // TODO this is a hack to make the memory following be inside the coro frame
69 suspend {
70 var my_node: Stack.Node = undefined;
71 node.* = &my_node;
72 resume @handle();
73 }
74
75 // TODO this allocation elision should be guaranteed because we await it in
76 // this coro frame
77 return await (async func(args2) catch unreachable);
78 }
79 };
80 var node: *Stack.Node = undefined;
81 const handle = try async<self.lock.loop.allocator> S.asyncFunc(&node, args);
82 node.* = Stack.Node{
83 .next = undefined,
84 .data = handle,
85 };
86 self.coro_stack.push(node);
87 }
88
8960 /// Wait for all the calls and promises of the group to complete.
9061 /// Thread-safe.
9162 /// Safe to call any number of times.
9263 pub async fn wait(self: *Self) ReturnType {
93 // TODO catch unreachable because the allocation can be grouped with
94 // the coro frame allocation
95 const held = await (async self.lock.acquire() catch unreachable);
64 const held = self.lock.acquire();
9665 defer held.release();
9766
9867 while (self.coro_stack.pop()) |node| {
......@@ -131,8 +100,7 @@ test "std.event.Group" {
131100 try loop.initMultiThreaded(allocator);
132101 defer loop.deinit();
133102
134 const handle = try async<allocator> testGroup(&loop);
135 defer cancel handle;
103 const handle = async testGroup(&loop);
136104
137105 loop.run();
138106}
......@@ -140,26 +108,30 @@ test "std.event.Group" {
140108async fn testGroup(loop: *Loop) void {
141109 var count: usize = 0;
142110 var group = Group(void).init(loop);
143 group.add(async sleepALittle(&count) catch @panic("memory")) catch @panic("memory");
144 group.call(increaseByTen, &count) catch @panic("memory");
145 await (async group.wait() catch @panic("memory"));
111 var sleep_a_little_frame = async sleepALittle(&count);
112 group.add(&sleep_a_little_frame) catch @panic("memory");
113 var increase_by_ten_frame = async increaseByTen(&count);
114 group.add(&increase_by_ten_frame) catch @panic("memory");
115 group.wait();
146116 testing.expect(count == 11);
147117
148118 var another = Group(anyerror!void).init(loop);
149 another.add(async somethingElse() catch @panic("memory")) catch @panic("memory");
150 another.call(doSomethingThatFails) catch @panic("memory");
151 testing.expectError(error.ItBroke, await (async another.wait() catch @panic("memory")));
119 var something_else_frame = async somethingElse();
120 another.add(&something_else_frame) catch @panic("memory");
121 var something_that_fails_frame = async doSomethingThatFails();
122 another.add(&something_that_fails_frame) catch @panic("memory");
123 testing.expectError(error.ItBroke, another.wait());
152124}
153125
154126async fn sleepALittle(count: *usize) void {
155127 std.time.sleep(1 * std.time.millisecond);
156 _ = @atomicRmw(usize, count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst);
128 _ = @atomicRmw(usize, count, .Add, 1, .SeqCst);
157129}
158130
159131async fn increaseByTen(count: *usize) void {
160132 var i: usize = 0;
161133 while (i < 10) : (i += 1) {
162 _ = @atomicRmw(usize, count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst);
134 _ = @atomicRmw(usize, count, .Add, 1, .SeqCst);
163135 }
164136}
165137
std/event/io.zig+9-10
......@@ -1,6 +1,5 @@
11const std = @import("../std.zig");
22const builtin = @import("builtin");
3const Allocator = std.mem.Allocator;
43const assert = std.debug.assert;
54const mem = std.mem;
65
......@@ -12,13 +11,13 @@ pub fn InStream(comptime ReadError: type) type {
1211 /// Return the number of bytes read. It may be less than buffer.len.
1312 /// If the number of bytes read is 0, it means end of stream.
1413 /// End of stream is not an error condition.
15 readFn: async<*Allocator> fn (self: *Self, buffer: []u8) Error!usize,
14 readFn: async fn (self: *Self, buffer: []u8) Error!usize,
1615
1716 /// Return the number of bytes read. It may be less than buffer.len.
1817 /// If the number of bytes read is 0, it means end of stream.
1918 /// End of stream is not an error condition.
2019 pub async fn read(self: *Self, buffer: []u8) !usize {
21 return await (async self.readFn(self, buffer) catch unreachable);
20 return self.readFn(self, buffer);
2221 }
2322
2423 /// Return the number of bytes read. If it is less than buffer.len
......@@ -26,7 +25,7 @@ pub fn InStream(comptime ReadError: type) type {
2625 pub async fn readFull(self: *Self, buffer: []u8) !usize {
2726 var index: usize = 0;
2827 while (index != buf.len) {
29 const amt_read = try await (async self.read(buf[index..]) catch unreachable);
28 const amt_read = try self.read(buf[index..]);
3029 if (amt_read == 0) return index;
3130 index += amt_read;
3231 }
......@@ -35,25 +34,25 @@ pub fn InStream(comptime ReadError: type) type {
3534
3635 /// Same as `readFull` but end of stream returns `error.EndOfStream`.
3736 pub async fn readNoEof(self: *Self, buf: []u8) !void {
38 const amt_read = try await (async self.readFull(buf[index..]) catch unreachable);
37 const amt_read = try self.readFull(buf[index..]);
3938 if (amt_read < buf.len) return error.EndOfStream;
4039 }
4140
4241 pub async fn readIntLittle(self: *Self, comptime T: type) !T {
4342 var bytes: [@sizeOf(T)]u8 = undefined;
44 try await (async self.readNoEof(bytes[0..]) catch unreachable);
43 try self.readNoEof(bytes[0..]);
4544 return mem.readIntLittle(T, &bytes);
4645 }
4746
4847 pub async fn readIntBe(self: *Self, comptime T: type) !T {
4948 var bytes: [@sizeOf(T)]u8 = undefined;
50 try await (async self.readNoEof(bytes[0..]) catch unreachable);
49 try self.readNoEof(bytes[0..]);
5150 return mem.readIntBig(T, &bytes);
5251 }
5352
5453 pub async fn readInt(self: *Self, comptime T: type, endian: builtin.Endian) !T {
5554 var bytes: [@sizeOf(T)]u8 = undefined;
56 try await (async self.readNoEof(bytes[0..]) catch unreachable);
55 try self.readNoEof(bytes[0..]);
5756 return mem.readInt(T, &bytes, endian);
5857 }
5958
......@@ -61,7 +60,7 @@ pub fn InStream(comptime ReadError: type) type {
6160 // Only extern and packed structs have defined in-memory layout.
6261 comptime assert(@typeInfo(T).Struct.layout != builtin.TypeInfo.ContainerLayout.Auto);
6362 var res: [1]T = undefined;
64 try await (async self.readNoEof(@sliceToBytes(res[0..])) catch unreachable);
63 try self.readNoEof(@sliceToBytes(res[0..]));
6564 return res[0];
6665 }
6766 };
......@@ -72,6 +71,6 @@ pub fn OutStream(comptime WriteError: type) type {
7271 const Self = @This();
7372 pub const Error = WriteError;
7473
75 writeFn: async<*Allocator> fn (self: *Self, buffer: []u8) Error!void,
74 writeFn: async fn (self: *Self, buffer: []u8) Error!void,
7675 };
7776}
std/event/lock.zig+21-33
......@@ -3,8 +3,6 @@ const builtin = @import("builtin");
33const assert = std.debug.assert;
44const testing = std.testing;
55const mem = std.mem;
6const AtomicRmwOp = builtin.AtomicRmwOp;
7const AtomicOrder = builtin.AtomicOrder;
86const Loop = std.event.Loop;
97
108/// Thread-safe async/await lock.
......@@ -17,7 +15,7 @@ pub const Lock = struct {
1715 queue: Queue,
1816 queue_empty_bit: u8, // TODO make this a bool
1917
20 const Queue = std.atomic.Queue(promise);
18 const Queue = std.atomic.Queue(anyframe);
2119
2220 pub const Held = struct {
2321 lock: *Lock,
......@@ -30,19 +28,19 @@ pub const Lock = struct {
3028 }
3129
3230 // We need to release the lock.
33 _ = @atomicRmw(u8, &self.lock.queue_empty_bit, AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst);
34 _ = @atomicRmw(u8, &self.lock.shared_bit, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst);
31 _ = @atomicRmw(u8, &self.lock.queue_empty_bit, .Xchg, 1, .SeqCst);
32 _ = @atomicRmw(u8, &self.lock.shared_bit, .Xchg, 0, .SeqCst);
3533
3634 // There might be a queue item. If we know the queue is empty, we can be done,
3735 // because the other actor will try to obtain the lock.
3836 // But if there's a queue item, we are the actor which must loop and attempt
3937 // to grab the lock again.
40 if (@atomicLoad(u8, &self.lock.queue_empty_bit, AtomicOrder.SeqCst) == 1) {
38 if (@atomicLoad(u8, &self.lock.queue_empty_bit, .SeqCst) == 1) {
4139 return;
4240 }
4341
4442 while (true) {
45 const old_bit = @atomicRmw(u8, &self.lock.shared_bit, AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst);
43 const old_bit = @atomicRmw(u8, &self.lock.shared_bit, .Xchg, 1, .SeqCst);
4644 if (old_bit != 0) {
4745 // We did not obtain the lock. Great, the queue is someone else's problem.
4846 return;
......@@ -55,11 +53,11 @@ pub const Lock = struct {
5553 }
5654
5755 // Release the lock again.
58 _ = @atomicRmw(u8, &self.lock.queue_empty_bit, AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst);
59 _ = @atomicRmw(u8, &self.lock.shared_bit, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst);
56 _ = @atomicRmw(u8, &self.lock.queue_empty_bit, .Xchg, 1, .SeqCst);
57 _ = @atomicRmw(u8, &self.lock.shared_bit, .Xchg, 0, .SeqCst);
6058
6159 // Find out if we can be done.
62 if (@atomicLoad(u8, &self.lock.queue_empty_bit, AtomicOrder.SeqCst) == 1) {
60 if (@atomicLoad(u8, &self.lock.queue_empty_bit, .SeqCst) == 1) {
6361 return;
6462 }
6563 }
......@@ -88,15 +86,11 @@ pub const Lock = struct {
8886 /// All calls to acquire() and release() must complete before calling deinit().
8987 pub fn deinit(self: *Lock) void {
9088 assert(self.shared_bit == 0);
91 while (self.queue.get()) |node| cancel node.data;
89 while (self.queue.get()) |node| resume node.data;
9290 }
9391
9492 pub async fn acquire(self: *Lock) Held {
95 // TODO explicitly put this memory in the coroutine frame #1194
96 suspend {
97 resume @handle();
98 }
99 var my_tick_node = Loop.NextTickNode.init(@handle());
93 var my_tick_node = Loop.NextTickNode.init(@frame());
10094
10195 errdefer _ = self.queue.remove(&my_tick_node); // TODO test canceling an acquire
10296 suspend {
......@@ -107,9 +101,9 @@ pub const Lock = struct {
107101
108102 // We set this bit so that later we can rely on the fact, that if queue_empty_bit is 1, some actor
109103 // will attempt to grab the lock.
110 _ = @atomicRmw(u8, &self.queue_empty_bit, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst);
104 _ = @atomicRmw(u8, &self.queue_empty_bit, .Xchg, 0, .SeqCst);
111105
112 const old_bit = @atomicRmw(u8, &self.shared_bit, AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst);
106 const old_bit = @atomicRmw(u8, &self.shared_bit, .Xchg, 1, .SeqCst);
113107 if (old_bit == 0) {
114108 if (self.queue.get()) |node| {
115109 // Whether this node is us or someone else, we tail resume it.
......@@ -123,8 +117,7 @@ pub const Lock = struct {
123117};
124118
125119test "std.event.Lock" {
126 // TODO https://github.com/ziglang/zig/issues/2377
127 if (true) return error.SkipZigTest;
120 // TODO https://github.com/ziglang/zig/issues/1908
128121 if (builtin.single_threaded) return error.SkipZigTest;
129122
130123 const allocator = std.heap.direct_allocator;
......@@ -136,39 +129,34 @@ test "std.event.Lock" {
136129 var lock = Lock.init(&loop);
137130 defer lock.deinit();
138131
139 const handle = try async<allocator> testLock(&loop, &lock);
140 defer cancel handle;
132 _ = async testLock(&loop, &lock);
141133 loop.run();
142134
143135 testing.expectEqualSlices(i32, [1]i32{3 * @intCast(i32, shared_test_data.len)} ** shared_test_data.len, shared_test_data);
144136}
145137
146138async fn testLock(loop: *Loop, lock: *Lock) void {
147 // TODO explicitly put next tick node memory in the coroutine frame #1194
148 suspend {
149 resume @handle();
150 }
151 const handle1 = async lockRunner(lock) catch @panic("out of memory");
139 const handle1 = async lockRunner(lock);
152140 var tick_node1 = Loop.NextTickNode{
153141 .prev = undefined,
154142 .next = undefined,
155 .data = handle1,
143 .data = &handle1,
156144 };
157145 loop.onNextTick(&tick_node1);
158146
159 const handle2 = async lockRunner(lock) catch @panic("out of memory");
147 const handle2 = async lockRunner(lock);
160148 var tick_node2 = Loop.NextTickNode{
161149 .prev = undefined,
162150 .next = undefined,
163 .data = handle2,
151 .data = &handle2,
164152 };
165153 loop.onNextTick(&tick_node2);
166154
167 const handle3 = async lockRunner(lock) catch @panic("out of memory");
155 const handle3 = async lockRunner(lock);
168156 var tick_node3 = Loop.NextTickNode{
169157 .prev = undefined,
170158 .next = undefined,
171 .data = handle3,
159 .data = &handle3,
172160 };
173161 loop.onNextTick(&tick_node3);
174162
......@@ -185,7 +173,7 @@ async fn lockRunner(lock: *Lock) void {
185173
186174 var i: usize = 0;
187175 while (i < shared_test_data.len) : (i += 1) {
188 const lock_promise = async lock.acquire() catch @panic("out of memory");
176 const lock_promise = async lock.acquire();
189177 const handle = await lock_promise;
190178 defer handle.release();
191179
std/event/loop.zig+2-2
......@@ -457,7 +457,7 @@ pub const Loop = struct {
457457 var resume_node = ResumeNode.Basic{
458458 .base = ResumeNode{
459459 .id = ResumeNode.Id.Basic,
460 .handle = @handle(),
460 .handle = @frame(),
461461 .overlapped = ResumeNode.overlapped_init,
462462 },
463463 };
......@@ -469,7 +469,7 @@ pub const Loop = struct {
469469 var resume_node = ResumeNode.Basic{
470470 .base = ResumeNode{
471471 .id = ResumeNode.Id.Basic,
472 .handle = @handle(),
472 .handle = @frame(),
473473 .overlapped = ResumeNode.overlapped_init,
474474 },
475475 .kev = undefined,
std/event/net.zig+23-30
......@@ -9,17 +9,17 @@ const File = std.fs.File;
99const fd_t = os.fd_t;
1010
1111pub const Server = struct {
12 handleRequestFn: async<*mem.Allocator> fn (*Server, *const std.net.Address, File) void,
12 handleRequestFn: async fn (*Server, *const std.net.Address, File) void,
1313
1414 loop: *Loop,
1515 sockfd: ?i32,
16 accept_coro: ?promise,
16 accept_coro: ?anyframe,
1717 listen_address: std.net.Address,
1818
1919 waiting_for_emfile_node: PromiseNode,
2020 listen_resume_node: event.Loop.ResumeNode,
2121
22 const PromiseNode = std.TailQueue(promise).Node;
22 const PromiseNode = std.TailQueue(anyframe).Node;
2323
2424 pub fn init(loop: *Loop) Server {
2525 // TODO can't initialize handler coroutine here because we need well defined copy elision
......@@ -41,7 +41,7 @@ pub const Server = struct {
4141 pub fn listen(
4242 self: *Server,
4343 address: *const std.net.Address,
44 handleRequestFn: async<*mem.Allocator> fn (*Server, *const std.net.Address, File) void,
44 handleRequestFn: async fn (*Server, *const std.net.Address, File) void,
4545 ) !void {
4646 self.handleRequestFn = handleRequestFn;
4747
......@@ -53,7 +53,7 @@ pub const Server = struct {
5353 try os.listen(sockfd, os.SOMAXCONN);
5454 self.listen_address = std.net.Address.initPosix(try os.getsockname(sockfd));
5555
56 self.accept_coro = try async<self.loop.allocator> Server.handler(self);
56 self.accept_coro = async Server.handler(self);
5757 errdefer cancel self.accept_coro.?;
5858
5959 self.listen_resume_node.handle = self.accept_coro.?;
......@@ -86,12 +86,7 @@ pub const Server = struct {
8686 continue;
8787 }
8888 var socket = File.openHandle(accepted_fd);
89 _ = async<self.loop.allocator> self.handleRequestFn(self, &accepted_addr, socket) catch |err| switch (err) {
90 error.OutOfMemory => {
91 socket.close();
92 continue;
93 },
94 };
89 self.handleRequestFn(self, &accepted_addr, socket);
9590 } else |err| switch (err) {
9691 error.ProcessFdQuotaExceeded => @panic("TODO handle this error"),
9792 error.ConnectionAborted => continue,
......@@ -124,7 +119,7 @@ pub async fn connectUnixSocket(loop: *Loop, path: []const u8) !i32 {
124119 mem.copy(u8, sock_addr.path[0..], path);
125120 const size = @intCast(u32, @sizeOf(os.sa_family_t) + path.len);
126121 try os.connect_async(sockfd, &sock_addr, size);
127 try await try async loop.linuxWaitFd(sockfd, os.EPOLLIN | os.EPOLLOUT | os.EPOLLET);
122 try loop.linuxWaitFd(sockfd, os.EPOLLIN | os.EPOLLOUT | os.EPOLLET);
128123 try os.getsockoptError(sockfd);
129124
130125 return sockfd;
......@@ -149,7 +144,7 @@ pub async fn read(loop: *std.event.Loop, fd: fd_t, buffer: []u8) ReadError!usize
149144 .iov_len = buffer.len,
150145 };
151146 const iovs: *const [1]os.iovec = &iov;
152 return await (async readvPosix(loop, fd, iovs, 1) catch unreachable);
147 return readvPosix(loop, fd, iovs, 1);
153148}
154149
155150pub const WriteError = error{};
......@@ -160,7 +155,7 @@ pub async fn write(loop: *std.event.Loop, fd: fd_t, buffer: []const u8) WriteErr
160155 .iov_len = buffer.len,
161156 };
162157 const iovs: *const [1]os.iovec_const = &iov;
163 return await (async writevPosix(loop, fd, iovs, 1) catch unreachable);
158 return writevPosix(loop, fd, iovs, 1);
164159}
165160
166161pub async fn writevPosix(loop: *Loop, fd: i32, iov: [*]const os.iovec_const, count: usize) !void {
......@@ -174,7 +169,7 @@ pub async fn writevPosix(loop: *Loop, fd: i32, iov: [*]const os.iovec_const, cou
174169 os.EINVAL => unreachable,
175170 os.EFAULT => unreachable,
176171 os.EAGAIN => {
177 try await (async loop.linuxWaitFd(fd, os.EPOLLET | os.EPOLLOUT) catch unreachable);
172 try loop.linuxWaitFd(fd, os.EPOLLET | os.EPOLLOUT);
178173 continue;
179174 },
180175 os.EBADF => unreachable, // always a race condition
......@@ -205,7 +200,7 @@ pub async fn readvPosix(loop: *std.event.Loop, fd: i32, iov: [*]os.iovec, count:
205200 os.EINVAL => unreachable,
206201 os.EFAULT => unreachable,
207202 os.EAGAIN => {
208 try await (async loop.linuxWaitFd(fd, os.EPOLLET | os.EPOLLIN) catch unreachable);
203 try loop.linuxWaitFd(fd, os.EPOLLET | os.EPOLLIN);
209204 continue;
210205 },
211206 os.EBADF => unreachable, // always a race condition
......@@ -232,7 +227,7 @@ pub async fn writev(loop: *Loop, fd: fd_t, data: []const []const u8) !void {
232227 };
233228 }
234229
235 return await (async writevPosix(loop, fd, iovecs.ptr, data.len) catch unreachable);
230 return writevPosix(loop, fd, iovecs.ptr, data.len);
236231}
237232
238233pub async fn readv(loop: *Loop, fd: fd_t, data: []const []u8) !usize {
......@@ -246,7 +241,7 @@ pub async fn readv(loop: *Loop, fd: fd_t, data: []const []u8) !usize {
246241 };
247242 }
248243
249 return await (async readvPosix(loop, fd, iovecs.ptr, data.len) catch unreachable);
244 return readvPosix(loop, fd, iovecs.ptr, data.len);
250245}
251246
252247pub async fn connect(loop: *Loop, _address: *const std.net.Address) !File {
......@@ -256,7 +251,7 @@ pub async fn connect(loop: *Loop, _address: *const std.net.Address) !File {
256251 errdefer os.close(sockfd);
257252
258253 try os.connect_async(sockfd, &address.os_addr, @sizeOf(os.sockaddr_in));
259 try await try async loop.linuxWaitFd(sockfd, os.EPOLLIN | os.EPOLLOUT | os.EPOLLET);
254 try loop.linuxWaitFd(sockfd, os.EPOLLIN | os.EPOLLOUT | os.EPOLLET);
260255 try os.getsockoptError(sockfd);
261256
262257 return File.openHandle(sockfd);
......@@ -275,17 +270,16 @@ test "listen on a port, send bytes, receive bytes" {
275270 tcp_server: Server,
276271
277272 const Self = @This();
278 async<*mem.Allocator> fn handler(tcp_server: *Server, _addr: *const std.net.Address, _socket: File) void {
273 async fn handler(tcp_server: *Server, _addr: *const std.net.Address, _socket: File) void {
279274 const self = @fieldParentPtr(Self, "tcp_server", tcp_server);
280275 var socket = _socket; // TODO https://github.com/ziglang/zig/issues/1592
281276 defer socket.close();
282277 // TODO guarantee elision of this allocation
283 const next_handler = async errorableHandler(self, _addr, socket) catch unreachable;
284 (await next_handler) catch |err| {
278 const next_handler = errorableHandler(self, _addr, socket) catch |err| {
285279 std.debug.panic("unable to handle connection: {}\n", err);
286280 };
287281 suspend {
288 cancel @handle();
282 cancel @frame();
289283 }
290284 }
291285 async fn errorableHandler(self: *Self, _addr: *const std.net.Address, _socket: File) !void {
......@@ -306,15 +300,14 @@ test "listen on a port, send bytes, receive bytes" {
306300 defer server.tcp_server.deinit();
307301 try server.tcp_server.listen(&addr, MyServer.handler);
308302
309 const p = try async<std.debug.global_allocator> doAsyncTest(&loop, &server.tcp_server.listen_address, &server.tcp_server);
310 defer cancel p;
303 _ = async doAsyncTest(&loop, &server.tcp_server.listen_address, &server.tcp_server);
311304 loop.run();
312305}
313306
314307async fn doAsyncTest(loop: *Loop, address: *const std.net.Address, server: *Server) void {
315308 errdefer @panic("test failure");
316309
317 var socket_file = try await try async connect(loop, address);
310 var socket_file = try connect(loop, address);
318311 defer socket_file.close();
319312
320313 var buf: [512]u8 = undefined;
......@@ -340,9 +333,9 @@ pub const OutStream = struct {
340333 };
341334 }
342335
343 async<*mem.Allocator> fn writeFn(out_stream: *Stream, bytes: []const u8) Error!void {
336 async fn writeFn(out_stream: *Stream, bytes: []const u8) Error!void {
344337 const self = @fieldParentPtr(OutStream, "stream", out_stream);
345 return await (async write(self.loop, self.fd, bytes) catch unreachable);
338 return write(self.loop, self.fd, bytes);
346339 }
347340};
348341
......@@ -362,8 +355,8 @@ pub const InStream = struct {
362355 };
363356 }
364357
365 async<*mem.Allocator> fn readFn(in_stream: *Stream, bytes: []u8) Error!usize {
358 async fn readFn(in_stream: *Stream, bytes: []u8) Error!usize {
366359 const self = @fieldParentPtr(InStream, "stream", in_stream);
367 return await (async read(self.loop, self.fd, bytes) catch unreachable);
360 return read(self.loop, self.fd, bytes);
368361 }
369362};
std/event/rwlock.zig+43-42
......@@ -3,8 +3,6 @@ const builtin = @import("builtin");
33const assert = std.debug.assert;
44const testing = std.testing;
55const mem = std.mem;
6const AtomicRmwOp = builtin.AtomicRmwOp;
7const AtomicOrder = builtin.AtomicOrder;
86const Loop = std.event.Loop;
97
108/// Thread-safe async/await lock.
......@@ -28,19 +26,19 @@ pub const RwLock = struct {
2826 const ReadLock = 2;
2927 };
3028
31 const Queue = std.atomic.Queue(promise);
29 const Queue = std.atomic.Queue(anyframe);
3230
3331 pub const HeldRead = struct {
3432 lock: *RwLock,
3533
3634 pub fn release(self: HeldRead) void {
3735 // If other readers still hold the lock, we're done.
38 if (@atomicRmw(usize, &self.lock.reader_lock_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst) != 1) {
36 if (@atomicRmw(usize, &self.lock.reader_lock_count, .Sub, 1, .SeqCst) != 1) {
3937 return;
4038 }
4139
42 _ = @atomicRmw(u8, &self.lock.reader_queue_empty_bit, AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst);
43 if (@cmpxchgStrong(u8, &self.lock.shared_state, State.ReadLock, State.Unlocked, AtomicOrder.SeqCst, AtomicOrder.SeqCst) != null) {
40 _ = @atomicRmw(u8, &self.lock.reader_queue_empty_bit, .Xchg, 1, .SeqCst);
41 if (@cmpxchgStrong(u8, &self.lock.shared_state, State.ReadLock, State.Unlocked, .SeqCst, .SeqCst) != null) {
4442 // Didn't unlock. Someone else's problem.
4543 return;
4644 }
......@@ -61,17 +59,17 @@ pub const RwLock = struct {
6159 }
6260
6361 // We need to release the write lock. Check if any readers are waiting to grab the lock.
64 if (@atomicLoad(u8, &self.lock.reader_queue_empty_bit, AtomicOrder.SeqCst) == 0) {
62 if (@atomicLoad(u8, &self.lock.reader_queue_empty_bit, .SeqCst) == 0) {
6563 // Switch to a read lock.
66 _ = @atomicRmw(u8, &self.lock.shared_state, AtomicRmwOp.Xchg, State.ReadLock, AtomicOrder.SeqCst);
64 _ = @atomicRmw(u8, &self.lock.shared_state, .Xchg, State.ReadLock, .SeqCst);
6765 while (self.lock.reader_queue.get()) |node| {
6866 self.lock.loop.onNextTick(node);
6967 }
7068 return;
7169 }
7270
73 _ = @atomicRmw(u8, &self.lock.writer_queue_empty_bit, AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst);
74 _ = @atomicRmw(u8, &self.lock.shared_state, AtomicRmwOp.Xchg, State.Unlocked, AtomicOrder.SeqCst);
71 _ = @atomicRmw(u8, &self.lock.writer_queue_empty_bit, .Xchg, 1, .SeqCst);
72 _ = @atomicRmw(u8, &self.lock.shared_state, .Xchg, State.Unlocked, .SeqCst);
7573
7674 self.lock.commonPostUnlock();
7775 }
......@@ -93,17 +91,16 @@ pub const RwLock = struct {
9391 /// All calls to acquire() and release() must complete before calling deinit().
9492 pub fn deinit(self: *RwLock) void {
9593 assert(self.shared_state == State.Unlocked);
96 while (self.writer_queue.get()) |node| cancel node.data;
97 while (self.reader_queue.get()) |node| cancel node.data;
94 while (self.writer_queue.get()) |node| resume node.data;
95 while (self.reader_queue.get()) |node| resume node.data;
9896 }
9997
10098 pub async fn acquireRead(self: *RwLock) HeldRead {
101 _ = @atomicRmw(usize, &self.reader_lock_count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst);
99 _ = @atomicRmw(usize, &self.reader_lock_count, .Add, 1, .SeqCst);
102100
103101 suspend {
104 // TODO explicitly put this memory in the coroutine frame #1194
105102 var my_tick_node = Loop.NextTickNode{
106 .data = @handle(),
103 .data = @frame(),
107104 .prev = undefined,
108105 .next = undefined,
109106 };
......@@ -115,10 +112,10 @@ pub const RwLock = struct {
115112
116113 // We set this bit so that later we can rely on the fact, that if reader_queue_empty_bit is 1,
117114 // some actor will attempt to grab the lock.
118 _ = @atomicRmw(u8, &self.reader_queue_empty_bit, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst);
115 _ = @atomicRmw(u8, &self.reader_queue_empty_bit, .Xchg, 0, .SeqCst);
119116
120117 // Here we don't care if we are the one to do the locking or if it was already locked for reading.
121 const have_read_lock = if (@cmpxchgStrong(u8, &self.shared_state, State.Unlocked, State.ReadLock, AtomicOrder.SeqCst, AtomicOrder.SeqCst)) |old_state| old_state == State.ReadLock else true;
118 const have_read_lock = if (@cmpxchgStrong(u8, &self.shared_state, State.Unlocked, State.ReadLock, .SeqCst, .SeqCst)) |old_state| old_state == State.ReadLock else true;
122119 if (have_read_lock) {
123120 // Give out all the read locks.
124121 if (self.reader_queue.get()) |first_node| {
......@@ -134,9 +131,8 @@ pub const RwLock = struct {
134131
135132 pub async fn acquireWrite(self: *RwLock) HeldWrite {
136133 suspend {
137 // TODO explicitly put this memory in the coroutine frame #1194
138134 var my_tick_node = Loop.NextTickNode{
139 .data = @handle(),
135 .data = @frame(),
140136 .prev = undefined,
141137 .next = undefined,
142138 };
......@@ -148,10 +144,10 @@ pub const RwLock = struct {
148144
149145 // We set this bit so that later we can rely on the fact, that if writer_queue_empty_bit is 1,
150146 // some actor will attempt to grab the lock.
151 _ = @atomicRmw(u8, &self.writer_queue_empty_bit, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst);
147 _ = @atomicRmw(u8, &self.writer_queue_empty_bit, .Xchg, 0, .SeqCst);
152148
153149 // Here we must be the one to acquire the write lock. It cannot already be locked.
154 if (@cmpxchgStrong(u8, &self.shared_state, State.Unlocked, State.WriteLock, AtomicOrder.SeqCst, AtomicOrder.SeqCst) == null) {
150 if (@cmpxchgStrong(u8, &self.shared_state, State.Unlocked, State.WriteLock, .SeqCst, .SeqCst) == null) {
155151 // We now have a write lock.
156152 if (self.writer_queue.get()) |node| {
157153 // Whether this node is us or someone else, we tail resume it.
......@@ -169,8 +165,8 @@ pub const RwLock = struct {
169165 // obtain the lock.
170166 // But if there's a writer_queue item or a reader_queue item,
171167 // we are the actor which must loop and attempt to grab the lock again.
172 if (@atomicLoad(u8, &self.writer_queue_empty_bit, AtomicOrder.SeqCst) == 0) {
173 if (@cmpxchgStrong(u8, &self.shared_state, State.Unlocked, State.WriteLock, AtomicOrder.SeqCst, AtomicOrder.SeqCst) != null) {
168 if (@atomicLoad(u8, &self.writer_queue_empty_bit, .SeqCst) == 0) {
169 if (@cmpxchgStrong(u8, &self.shared_state, State.Unlocked, State.WriteLock, .SeqCst, .SeqCst) != null) {
174170 // We did not obtain the lock. Great, the queues are someone else's problem.
175171 return;
176172 }
......@@ -180,13 +176,13 @@ pub const RwLock = struct {
180176 return;
181177 }
182178 // Release the lock again.
183 _ = @atomicRmw(u8, &self.writer_queue_empty_bit, AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst);
184 _ = @atomicRmw(u8, &self.shared_state, AtomicRmwOp.Xchg, State.Unlocked, AtomicOrder.SeqCst);
179 _ = @atomicRmw(u8, &self.writer_queue_empty_bit, .Xchg, 1, .SeqCst);
180 _ = @atomicRmw(u8, &self.shared_state, .Xchg, State.Unlocked, .SeqCst);
185181 continue;
186182 }
187183
188 if (@atomicLoad(u8, &self.reader_queue_empty_bit, AtomicOrder.SeqCst) == 0) {
189 if (@cmpxchgStrong(u8, &self.shared_state, State.Unlocked, State.ReadLock, AtomicOrder.SeqCst, AtomicOrder.SeqCst) != null) {
184 if (@atomicLoad(u8, &self.reader_queue_empty_bit, .SeqCst) == 0) {
185 if (@cmpxchgStrong(u8, &self.shared_state, State.Unlocked, State.ReadLock, .SeqCst, .SeqCst) != null) {
190186 // We did not obtain the lock. Great, the queues are someone else's problem.
191187 return;
192188 }
......@@ -199,8 +195,8 @@ pub const RwLock = struct {
199195 return;
200196 }
201197 // Release the lock again.
202 _ = @atomicRmw(u8, &self.reader_queue_empty_bit, AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst);
203 if (@cmpxchgStrong(u8, &self.shared_state, State.ReadLock, State.Unlocked, AtomicOrder.SeqCst, AtomicOrder.SeqCst) != null) {
198 _ = @atomicRmw(u8, &self.reader_queue_empty_bit, .Xchg, 1, .SeqCst);
199 if (@cmpxchgStrong(u8, &self.shared_state, State.ReadLock, State.Unlocked, .SeqCst, .SeqCst) != null) {
204200 // Didn't unlock. Someone else's problem.
205201 return;
206202 }
......@@ -215,6 +211,9 @@ test "std.event.RwLock" {
215211 // https://github.com/ziglang/zig/issues/2377
216212 if (true) return error.SkipZigTest;
217213
214 // https://github.com/ziglang/zig/issues/1908
215 if (builtin.single_threaded) return error.SkipZigTest;
216
218217 const allocator = std.heap.direct_allocator;
219218
220219 var loop: Loop = undefined;
......@@ -224,8 +223,7 @@ test "std.event.RwLock" {
224223 var lock = RwLock.init(&loop);
225224 defer lock.deinit();
226225
227 const handle = try async<allocator> testLock(&loop, &lock);
228 defer cancel handle;
226 const handle = testLock(&loop, &lock);
229227 loop.run();
230228
231229 const expected_result = [1]i32{shared_it_count * @intCast(i32, shared_test_data.len)} ** shared_test_data.len;
......@@ -233,28 +231,31 @@ test "std.event.RwLock" {
233231}
234232
235233async fn testLock(loop: *Loop, lock: *RwLock) void {
236 // TODO explicitly put next tick node memory in the coroutine frame #1194
237 suspend {
238 resume @handle();
239 }
240
241234 var read_nodes: [100]Loop.NextTickNode = undefined;
242235 for (read_nodes) |*read_node| {
243 read_node.data = async readRunner(lock) catch @panic("out of memory");
236 const frame = loop.allocator.create(@Frame(readRunner)) catch @panic("memory");
237 read_node.data = frame;
238 frame.* = async readRunner(lock);
244239 loop.onNextTick(read_node);
245240 }
246241
247242 var write_nodes: [shared_it_count]Loop.NextTickNode = undefined;
248243 for (write_nodes) |*write_node| {
249 write_node.data = async writeRunner(lock) catch @panic("out of memory");
244 const frame = loop.allocator.create(@Frame(writeRunner)) catch @panic("memory");
245 write_node.data = frame;
246 frame.* = async writeRunner(lock);
250247 loop.onNextTick(write_node);
251248 }
252249
253250 for (write_nodes) |*write_node| {
254 await @ptrCast(promise->void, write_node.data);
251 const casted = @ptrCast(*const @Frame(writeRunner), write_node.data);
252 await casted;
253 loop.allocator.destroy(casted);
255254 }
256255 for (read_nodes) |*read_node| {
257 await @ptrCast(promise->void, read_node.data);
256 const casted = @ptrCast(*const @Frame(readRunner), read_node.data);
257 await casted;
258 loop.allocator.destroy(casted);
258259 }
259260}
260261
......@@ -269,7 +270,7 @@ async fn writeRunner(lock: *RwLock) void {
269270 var i: usize = 0;
270271 while (i < shared_test_data.len) : (i += 1) {
271272 std.time.sleep(100 * std.time.microsecond);
272 const lock_promise = async lock.acquireWrite() catch @panic("out of memory");
273 const lock_promise = async lock.acquireWrite();
273274 const handle = await lock_promise;
274275 defer handle.release();
275276
......@@ -287,7 +288,7 @@ async fn readRunner(lock: *RwLock) void {
287288
288289 var i: usize = 0;
289290 while (i < shared_test_data.len) : (i += 1) {
290 const lock_promise = async lock.acquireRead() catch @panic("out of memory");
291 const lock_promise = async lock.acquireRead();
291292 const handle = await lock_promise;
292293 defer handle.release();
293294
std/zig/parser_test.zig+1-1
......@@ -1183,7 +1183,7 @@ test "zig fmt: resume from suspend block" {
11831183 try testCanonical(
11841184 \\fn foo() void {
11851185 \\ suspend {
1186 \\ resume @handle();
1186 \\ resume @frame();
11871187 \\ }
11881188 \\}
11891189 \\
test/compile_errors.zig+9-26
......@@ -1403,24 +1403,14 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
14031403 );
14041404
14051405 cases.add(
1406 "@handle() called outside of function definition",
1407 \\var handle_undef: promise = undefined;
1408 \\var handle_dummy: promise = @handle();
1406 "@frame() called outside of function definition",
1407 \\var handle_undef: anyframe = undefined;
1408 \\var handle_dummy: anyframe = @frame();
14091409 \\export fn entry() bool {
14101410 \\ return handle_undef == handle_dummy;
14111411 \\}
14121412 ,
1413 "tmp.zig:2:29: error: @handle() called outside of function definition",
1414 );
1415
1416 cases.add(
1417 "@handle() in non-async function",
1418 \\export fn entry() bool {
1419 \\ var handle_undef: promise = undefined;
1420 \\ return handle_undef == @handle();
1421 \\}
1422 ,
1423 "tmp.zig:3:28: error: @handle() in non-async function",
1413 "tmp.zig:2:30: error: @frame() called outside of function definition",
14241414 );
14251415
14261416 cases.add(
......@@ -1796,15 +1786,9 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
17961786
17971787 cases.add(
17981788 "suspend inside suspend block",
1799 \\const std = @import("std",);
1800 \\
18011789 \\export fn entry() void {
1802 \\ var buf: [500]u8 = undefined;
1803 \\ var a = &std.heap.FixedBufferAllocator.init(buf[0..]).allocator;
1804 \\ const p = (async<a> foo()) catch unreachable;
1805 \\ cancel p;
1790 \\ _ = async foo();
18061791 \\}
1807 \\
18081792 \\async fn foo() void {
18091793 \\ suspend {
18101794 \\ suspend {
......@@ -1812,8 +1796,8 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
18121796 \\ }
18131797 \\}
18141798 ,
1815 "tmp.zig:12:9: error: cannot suspend inside suspend block",
1816 "tmp.zig:11:5: note: other suspend block here",
1799 "tmp.zig:6:9: error: cannot suspend inside suspend block",
1800 "tmp.zig:5:5: note: other suspend block here",
18171801 );
18181802
18191803 cases.add(
......@@ -1854,15 +1838,14 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
18541838
18551839 cases.add(
18561840 "returning error from void async function",
1857 \\const std = @import("std",);
18581841 \\export fn entry() void {
1859 \\ const p = async<std.debug.global_allocator> amain() catch unreachable;
1842 \\ _ = async amain();
18601843 \\}
18611844 \\async fn amain() void {
18621845 \\ return error.ShouldBeCompileError;
18631846 \\}
18641847 ,
1865 "tmp.zig:6:17: error: expected type 'void', found 'error{ShouldBeCompileError}'",
1848 "tmp.zig:5:17: error: expected type 'void', found 'error{ShouldBeCompileError}'",
18661849 );
18671850
18681851 cases.add(