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
signature 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,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
1 * compile error for error: expected anyframe->T, found 'anyframe'5 * compile error for error: expected anyframe->T, found 'anyframe'
2 * compile error for error: expected anyframe->T, found 'i32'6 * compile error for error: expected anyframe->T, found 'i32'
3 * await of a non async function7 * await of a non async function
4 * async call on a non async function8 * async call on a non async function
5 * a test where an async function destroys its own frame in a defer9 * a test where an async function destroys its own frame in a defer
6 * implicit cast of normal function to async function should be allowed when it is inferred to be async10 * implicit cast of normal function to async function should be allowed when it is inferred to be async
7 * revive std.event.Loop
8 * @typeInfo for @Frame(func)11 * @typeInfo for @Frame(func)
9 * peer type resolution of *@Frame(func) and anyframe12 * peer type resolution of *@Frame(func) and anyframe
10 * peer type resolution of *@Frame(func) and anyframe->T when the return type matches13 * peer type resolution of *@Frame(func) and anyframe->T when the return type matches
...@@ -36,3 +39,4 @@...@@ -36,3 +39,4 @@
36 - it can be assumed that these are always available: the awaiter ptr, return ptr if applicable,39 - it can be assumed that these are always available: the awaiter ptr, return ptr if applicable,
37 error return trace ptr if applicable.40 error return trace ptr if applicable.
38 - it can be assumed that it is never cancelled41 - 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...@@ -770,7 +770,7 @@ fn tokenizeAndPrintRaw(docgen_tokenizer: *Tokenizer, out: var, source_token: Tok
770 .Keyword_or,770 .Keyword_or,
771 .Keyword_orelse,771 .Keyword_orelse,
772 .Keyword_packed,772 .Keyword_packed,
773 .Keyword_promise,773 .Keyword_anyframe,
774 .Keyword_pub,774 .Keyword_pub,
775 .Keyword_resume,775 .Keyword_resume,
776 .Keyword_return,776 .Keyword_return,
doc/langref.html.in+26-55
...@@ -6024,13 +6024,14 @@ const assert = std.debug.assert;...@@ -6024,13 +6024,14 @@ const assert = std.debug.assert;
60246024
6025var x: i32 = 1;6025var x: i32 = 1;
60266026
6027test "create a coroutine and cancel it" {6027test "call an async function" {
6028 const p = try async<std.debug.global_allocator> simpleAsyncFn();6028 var frame = async simpleAsyncFn();
6029 comptime assert(@typeOf(p) == promise->void);6029 comptime assert(@typeOf(frame) == @Frame(simpleAsyncFn));
6030 cancel p;
6031 assert(x == 2);6030 assert(x == 2);
6032}6031}
6033async<*std.mem.Allocator> fn simpleAsyncFn() void {6032fn simpleAsyncFn() void {
6033 x += 1;
6034 suspend;
6034 x += 1;6035 x += 1;
6035}6036}
6036 {#code_end#}6037 {#code_end#}
...@@ -6041,60 +6042,33 @@ async<*std.mem.Allocator> fn simpleAsyncFn() void {...@@ -6041,60 +6042,33 @@ async<*std.mem.Allocator> fn simpleAsyncFn() void {
6041 return to the caller or resumer. The following code demonstrates where control flow6042 return to the caller or resumer. The following code demonstrates where control flow
6042 goes:6043 goes:
6043 </p>6044 </p>
6044 {#code_begin|test#}6045 <p>
6045const std = @import("std");6046 TODO another test example here
6046const assert = std.debug.assert;6047 </p>
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#}
6074 <p>6048 <p>
6075 When an async function suspends itself, it must be sure that it will be6049 When an async function suspends itself, it must be sure that it will be
6076 resumed or canceled somehow, for example by registering its promise handle6050 resumed or canceled somehow, for example by registering its promise handle
6077 in an event loop. Use a suspend capture block to gain access to the6051 in an event loop. Use a suspend capture block to gain access to the
6078 promise:6052 promise (TODO this is outdated):
6079 </p>6053 </p>
6080 {#code_begin|test#}6054 {#code_begin|test#}
6081const std = @import("std");6055const std = @import("std");
6082const assert = std.debug.assert;6056const assert = std.debug.assert;
60836057
6058var the_frame: anyframe = undefined;
6059var result = false;
6060
6084test "coroutine suspend with block" {6061test "coroutine suspend with block" {
6085 const p = try async<std.debug.global_allocator> testSuspendBlock();6062 _ = async testSuspendBlock();
6086 std.debug.assert(!result);6063 std.debug.assert(!result);
6087 resume a_promise;6064 resume the_frame;
6088 std.debug.assert(result);6065 std.debug.assert(result);
6089 cancel p;
6090}6066}
60916067
6092var a_promise: promise = undefined;6068fn testSuspendBlock() void {
6093var result = false;
6094async fn testSuspendBlock() void {
6095 suspend {6069 suspend {
6096 comptime assert(@typeOf(@handle()) == promise->void);6070 comptime assert(@typeOf(@frame()) == *@Frame(testSuspendBlock));
6097 a_promise = @handle();6071 the_frame = @frame();
6098 }6072 }
6099 result = true;6073 result = true;
6100}6074}
...@@ -6124,16 +6098,13 @@ const std = @import("std");...@@ -6124,16 +6098,13 @@ const std = @import("std");
6124const assert = std.debug.assert;6098const assert = std.debug.assert;
61256099
6126test "resume from suspend" {6100test "resume from suspend" {
6127 var buf: [500]u8 = undefined;
6128 var a = &std.heap.FixedBufferAllocator.init(buf[0..]).allocator;
6129 var my_result: i32 = 1;6101 var my_result: i32 = 1;
6130 const p = try async<a> testResumeFromSuspend(&my_result);6102 _ = async testResumeFromSuspend(&my_result);
6131 cancel p;
6132 std.debug.assert(my_result == 2);6103 std.debug.assert(my_result == 2);
6133}6104}
6134async fn testResumeFromSuspend(my_result: *i32) void {6105async fn testResumeFromSuspend(my_result: *i32) void {
6135 suspend {6106 suspend {
6136 resume @handle();6107 resume @frame();
6137 }6108 }
6138 my_result.* += 1;6109 my_result.* += 1;
6139 suspend;6110 suspend;
...@@ -6172,30 +6143,30 @@ async fn testResumeFromSuspend(my_result: *i32) void {...@@ -6172,30 +6143,30 @@ async fn testResumeFromSuspend(my_result: *i32) void {
6172const std = @import("std");6143const std = @import("std");
6173const assert = std.debug.assert;6144const assert = std.debug.assert;
61746145
6175var a_promise: promise = undefined;6146var the_frame: anyframe = undefined;
6176var final_result: i32 = 0;6147var final_result: i32 = 0;
61776148
6178test "coroutine await" {6149test "coroutine await" {
6179 seq('a');6150 seq('a');
6180 const p = async<std.debug.global_allocator> amain() catch unreachable;6151 _ = async amain();
6181 seq('f');6152 seq('f');
6182 resume a_promise;6153 resume the_frame;
6183 seq('i');6154 seq('i');
6184 assert(final_result == 1234);6155 assert(final_result == 1234);
6185 assert(std.mem.eql(u8, seq_points, "abcdefghi"));6156 assert(std.mem.eql(u8, seq_points, "abcdefghi"));
6186}6157}
6187async fn amain() void {6158async fn amain() void {
6188 seq('b');6159 seq('b');
6189 const p = async another() catch unreachable;6160 var f = async another();
6190 seq('e');6161 seq('e');
6191 final_result = await p;6162 final_result = await f;
6192 seq('h');6163 seq('h');
6193}6164}
6194async fn another() i32 {6165async fn another() i32 {
6195 seq('c');6166 seq('c');
6196 suspend {6167 suspend {
6197 seq('d');6168 seq('d');
6198 a_promise = @handle();6169 the_frame = @frame();
6199 }6170 }
6200 seq('g');6171 seq('g');
6201 return 1234;6172 return 1234;
src/analyze.cpp+1-1
...@@ -5325,7 +5325,7 @@ static Error resolve_coro_frame(CodeGen *g, ZigType *frame_type) {...@@ -5325,7 +5325,7 @@ static Error resolve_coro_frame(CodeGen *g, ZigType *frame_type) {
5325 if (*instruction->name_hint == 0) {5325 if (*instruction->name_hint == 0) {
5326 name = buf_ptr(buf_sprintf("@local%" ZIG_PRI_usize, alloca_i));5326 name = buf_ptr(buf_sprintf("@local%" ZIG_PRI_usize, alloca_i));
5327 } else {5327 } else {
5328 name = instruction->name_hint;5328 name = buf_ptr(buf_sprintf("%s.%" ZIG_PRI_usize, instruction->name_hint, alloca_i));
5329 }5329 }
5330 field_names.append(name);5330 field_names.append(name);
5331 field_types.append(child_type);5331 field_types.append(child_type);
src/codegen.cpp+27-22
...@@ -535,24 +535,24 @@ static LLVMValueRef make_fn_llvm_value(CodeGen *g, ZigFn *fn) {...@@ -535,24 +535,24 @@ static LLVMValueRef make_fn_llvm_value(CodeGen *g, ZigFn *fn) {
535 // use the ABI alignment, which is fine.535 // use the ABI alignment, which is fine.
536 }536 }
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
553 if (is_async) {538 if (is_async) {
554 addLLVMArgAttr(llvm_fn, 0, "nonnull");539 addLLVMArgAttr(llvm_fn, 0, "nonnull");
555 } else {540 } 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
556 // set parameter attributes556 // set parameter attributes
557 FnWalk fn_walk = {};557 FnWalk fn_walk = {};
558 fn_walk.id = FnWalkIdAttrs;558 fn_walk.id = FnWalkIdAttrs;
...@@ -911,7 +911,7 @@ static Buf *panic_msg_buf(PanicMsgId msg_id) {...@@ -911,7 +911,7 @@ static Buf *panic_msg_buf(PanicMsgId msg_id) {
911 case PanicMsgIdBadResume:911 case PanicMsgIdBadResume:
912 return buf_create_from_str("resumed an async function which already returned");912 return buf_create_from_str("resumed an async function which already returned");
913 case PanicMsgIdBadAwait:913 case PanicMsgIdBadAwait:
914 return buf_create_from_str("async function awaited/canceled twice");914 return buf_create_from_str("async function awaited twice");
915 case PanicMsgIdBadReturn:915 case PanicMsgIdBadReturn:
916 return buf_create_from_str("async function returned twice");916 return buf_create_from_str("async function returned twice");
917 case PanicMsgIdResumedAnAwaitingFn:917 case PanicMsgIdResumedAnAwaitingFn:
...@@ -2350,6 +2350,10 @@ static LLVMValueRef ir_render_return_begin(CodeGen *g, IrExecutable *executable,...@@ -2350,6 +2350,10 @@ static LLVMValueRef ir_render_return_begin(CodeGen *g, IrExecutable *executable,
2350 return get_handle_value(g, g->cur_ret_ptr, operand_type, get_pointer_to_type(g, operand_type, true));2350 return get_handle_value(g, g->cur_ret_ptr, operand_type, get_pointer_to_type(g, operand_type, true));
2351}2351}
23522352
2353static void set_tail_call_if_appropriate(CodeGen *g, LLVMValueRef call_inst) {
2354 LLVMSetTailCall(call_inst, true);
2355}
2356
2353static LLVMValueRef ir_render_return(CodeGen *g, IrExecutable *executable, IrInstructionReturn *instruction) {2357static LLVMValueRef ir_render_return(CodeGen *g, IrExecutable *executable, IrInstructionReturn *instruction) {
2354 if (fn_is_async(g->cur_fn)) {2358 if (fn_is_async(g->cur_fn)) {
2355 LLVMTypeRef usize_type_ref = g->builtin_types.entry_usize->llvm_type;2359 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...@@ -2394,7 +2398,7 @@ static LLVMValueRef ir_render_return(CodeGen *g, IrExecutable *executable, IrIns
2394 LLVMValueRef their_frame_ptr = LLVMBuildIntToPtr(g->builder, masked_prev_val,2398 LLVMValueRef their_frame_ptr = LLVMBuildIntToPtr(g->builder, masked_prev_val,
2395 get_llvm_type(g, any_frame_type), "");2399 get_llvm_type(g, any_frame_type), "");
2396 LLVMValueRef call_inst = gen_resume(g, nullptr, their_frame_ptr, ResumeIdReturn, nullptr);2400 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);
2398 LLVMBuildRetVoid(g->builder);2402 LLVMBuildRetVoid(g->builder);
23992403
2400 g->cur_is_after_return = false;2404 g->cur_is_after_return = false;
...@@ -4009,7 +4013,7 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr...@@ -4009,7 +4013,7 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr
4009 LLVMBasicBlockRef call_bb = gen_suspend_begin(g, "CallResume");4013 LLVMBasicBlockRef call_bb = gen_suspend_begin(g, "CallResume");
40104014
4011 LLVMValueRef call_inst = gen_resume(g, fn_val, frame_result_loc, ResumeIdCall, nullptr);4015 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);
4013 LLVMBuildRetVoid(g->builder);4017 LLVMBuildRetVoid(g->builder);
40144018
4015 LLVMPositionBuilderAtEnd(g->builder, call_bb);4019 LLVMPositionBuilderAtEnd(g->builder, call_bb);
...@@ -5520,7 +5524,7 @@ static LLVMValueRef ir_render_cancel(CodeGen *g, IrExecutable *executable, IrIns...@@ -5520,7 +5524,7 @@ static LLVMValueRef ir_render_cancel(CodeGen *g, IrExecutable *executable, IrIns
55205524
5521 LLVMPositionBuilderAtEnd(g->builder, early_return_block);5525 LLVMPositionBuilderAtEnd(g->builder, early_return_block);
5522 LLVMValueRef call_inst = gen_resume(g, nullptr, target_frame_ptr, ResumeIdAwaitEarlyReturn, awaiter_ored_val);5526 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);
5524 LLVMBuildRetVoid(g->builder);5528 LLVMBuildRetVoid(g->builder);
55255529
5526 LLVMPositionBuilderAtEnd(g->builder, resume_bb);5530 LLVMPositionBuilderAtEnd(g->builder, resume_bb);
...@@ -5556,8 +5560,9 @@ static LLVMValueRef ir_render_await(CodeGen *g, IrExecutable *executable, IrInst...@@ -5556,8 +5560,9 @@ static LLVMValueRef ir_render_await(CodeGen *g, IrExecutable *executable, IrInst
5556 }5560 }
55575561
5558 // supply the error return trace pointer5562 // supply the error return trace pointer
5559 LLVMValueRef my_err_ret_trace_val = get_cur_err_ret_trace_val(g, instruction->base.scope);5563 if (codegen_fn_has_err_ret_tracing_arg(g, result_type)) {
5560 if (my_err_ret_trace_val != nullptr) {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);
5561 LLVMValueRef err_ret_trace_ptr_ptr = LLVMBuildStructGEP(g->builder, target_frame_ptr,5566 LLVMValueRef err_ret_trace_ptr_ptr = LLVMBuildStructGEP(g->builder, target_frame_ptr,
5562 frame_index_trace_arg(g, result_type), "");5567 frame_index_trace_arg(g, result_type), "");
5563 LLVMBuildStore(g->builder, my_err_ret_trace_val, err_ret_trace_ptr_ptr);5568 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...@@ -5588,7 +5593,7 @@ static LLVMValueRef ir_render_await(CodeGen *g, IrExecutable *executable, IrInst
5588 // Tail resume it now, so that it can complete.5593 // Tail resume it now, so that it can complete.
5589 LLVMPositionBuilderAtEnd(g->builder, early_return_block);5594 LLVMPositionBuilderAtEnd(g->builder, early_return_block);
5590 LLVMValueRef call_inst = gen_resume(g, nullptr, target_frame_ptr, ResumeIdAwaitEarlyReturn, awaiter_init_val);5595 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);
5592 LLVMBuildRetVoid(g->builder);5597 LLVMBuildRetVoid(g->builder);
55935598
5594 // Rely on the target to resume us from suspension.5599 // 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...@@ -15064,6 +15064,9 @@ static IrInstruction *ir_analyze_async_call(IrAnalyze *ira, IrInstructionCallSrc
15064 if (result_loc != nullptr && (type_is_invalid(result_loc->value.type) || instr_is_unreachable(result_loc))) {15064 if (result_loc != nullptr && (type_is_invalid(result_loc->value.type) || instr_is_unreachable(result_loc))) {
15065 return result_loc;15065 return result_loc;
15066 }15066 }
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;
15067 return &ir_build_call_gen(ira, &call_instruction->base, fn_entry, fn_ref, arg_count,15070 return &ir_build_call_gen(ira, &call_instruction->base, fn_entry, fn_ref, arg_count,
15068 casted_args, FnInlineAuto, true, nullptr, result_loc, frame_type)->base;15071 casted_args, FnInlineAuto, true, nullptr, result_loc, frame_type)->base;
15069}15072}
std/event/channel.zig+6-5
...@@ -77,18 +77,19 @@ pub fn Channel(comptime T: type) type {...@@ -77,18 +77,19 @@ pub fn Channel(comptime T: type) type {
77 /// must be called when all calls to put and get have suspended and no more calls occur77 /// must be called when all calls to put and get have suspended and no more calls occur
78 pub fn destroy(self: *SelfChannel) void {78 pub fn destroy(self: *SelfChannel) void {
79 while (self.getters.get()) |get_node| {79 while (self.getters.get()) |get_node| {
80 cancel get_node.data.tick_node.data;80 resume get_node.data.tick_node.data;
81 }81 }
82 while (self.putters.get()) |put_node| {82 while (self.putters.get()) |put_node| {
83 cancel put_node.data.tick_node.data;83 resume put_node.data.tick_node.data;
84 }84 }
85 self.loop.allocator.free(self.buffer_nodes);85 self.loop.allocator.free(self.buffer_nodes);
86 self.loop.allocator.destroy(self);86 self.loop.allocator.destroy(self);
87 }87 }
8888
89 /// puts a data item in the channel. The promise completes when the value has been added to the89 /// puts a data item in the channel. The function returns when the value has been added to the
90 /// buffer, or in the case of a zero size buffer, when the item has been retrieved by a getter.90 /// 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 {
92 var my_tick_node = Loop.NextTickNode.init(@frame());93 var my_tick_node = Loop.NextTickNode.init(@frame());
93 var queue_node = std.atomic.Queue(PutNode).Node.init(PutNode{94 var queue_node = std.atomic.Queue(PutNode).Node.init(PutNode{
94 .tick_node = &my_tick_node,95 .tick_node = &my_tick_node,
...@@ -114,7 +115,7 @@ pub fn Channel(comptime T: type) type {...@@ -114,7 +115,7 @@ pub fn Channel(comptime T: type) type {
114 }115 }
115 }116 }
116117
117 /// await this function to get an item from the channel. If the buffer is empty, the promise will118 /// await this function to get an item from the channel. If the buffer is empty, the frame will
118 /// complete when the next item is put in the channel.119 /// complete when the next item is put in the channel.
119 pub async fn get(self: *SelfChannel) T {120 pub async fn get(self: *SelfChannel) T {
120 // TODO integrate this function with named return values121 // TODO integrate this function with named return values
std/event/fs.zig+24-78
...@@ -76,12 +76,8 @@ pub const Request = struct {...@@ -76,12 +76,8 @@ pub const Request = struct {
7676
77pub const PWriteVError = error{OutOfMemory} || File.WriteError;77pub 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.
80pub async fn pwritev(loop: *Loop, fd: fd_t, data: []const []const u8, offset: usize) PWriteVError!void {80pub 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 }
85 switch (builtin.os) {81 switch (builtin.os) {
86 .macosx,82 .macosx,
87 .linux,83 .linux,
...@@ -109,7 +105,7 @@ pub async fn pwritev(loop: *Loop, fd: fd_t, data: []const []const u8, offset: us...@@ -109,7 +105,7 @@ pub async fn pwritev(loop: *Loop, fd: fd_t, data: []const []const u8, offset: us
109 }105 }
110}106}
111107
112/// data must outlive the returned promise108/// data must outlive the returned frame
113pub async fn pwritevWindows(loop: *Loop, fd: fd_t, data: []const []const u8, offset: usize) os.WindowsWriteError!void {109pub async fn pwritevWindows(loop: *Loop, fd: fd_t, data: []const []const u8, offset: usize) os.WindowsWriteError!void {
114 if (data.len == 0) return;110 if (data.len == 0) return;
115 if (data.len == 1) return await (async pwriteWindows(loop, fd, data[0], offset) catch unreachable);111 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...@@ -123,15 +119,10 @@ pub async fn pwritevWindows(loop: *Loop, fd: fd_t, data: []const []const u8, off
123}119}
124120
125pub async fn pwriteWindows(loop: *Loop, fd: fd_t, data: []const u8, offset: u64) os.WindowsWriteError!void {121pub 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
131 var resume_node = Loop.ResumeNode.Basic{122 var resume_node = Loop.ResumeNode.Basic{
132 .base = Loop.ResumeNode{123 .base = Loop.ResumeNode{
133 .id = Loop.ResumeNode.Id.Basic,124 .id = Loop.ResumeNode.Id.Basic,
134 .handle = @handle(),125 .handle = @frame(),
135 .overlapped = windows.OVERLAPPED{126 .overlapped = windows.OVERLAPPED{
136 .Internal = 0,127 .Internal = 0,
137 .InternalHigh = 0,128 .InternalHigh = 0,
...@@ -166,18 +157,13 @@ pub async fn pwriteWindows(loop: *Loop, fd: fd_t, data: []const u8, offset: u64)...@@ -166,18 +157,13 @@ pub async fn pwriteWindows(loop: *Loop, fd: fd_t, data: []const u8, offset: u64)
166 }157 }
167}158}
168159
169/// iovecs must live until pwritev promise completes.160/// iovecs must live until pwritev frame completes.
170pub async fn pwritevPosix(161pub async fn pwritevPosix(
171 loop: *Loop,162 loop: *Loop,
172 fd: fd_t,163 fd: fd_t,
173 iovecs: []const os.iovec_const,164 iovecs: []const os.iovec_const,
174 offset: usize,165 offset: usize,
175) os.WriteError!void {166) os.WriteError!void {
176 // workaround for https://github.com/ziglang/zig/issues/1194
177 suspend {
178 resume @handle();
179 }
180
181 var req_node = RequestNode{167 var req_node = RequestNode{
182 .prev = null,168 .prev = null,
183 .next = null,169 .next = null,
...@@ -194,7 +180,7 @@ pub async fn pwritevPosix(...@@ -194,7 +180,7 @@ pub async fn pwritevPosix(
194 .TickNode = Loop.NextTickNode{180 .TickNode = Loop.NextTickNode{
195 .prev = null,181 .prev = null,
196 .next = null,182 .next = null,
197 .data = @handle(),183 .data = @frame(),
198 },184 },
199 },185 },
200 },186 },
...@@ -211,13 +197,8 @@ pub async fn pwritevPosix(...@@ -211,13 +197,8 @@ pub async fn pwritevPosix(
211197
212pub const PReadVError = error{OutOfMemory} || File.ReadError;198pub 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.
215pub async fn preadv(loop: *Loop, fd: fd_t, data: []const []u8, offset: usize) PReadVError!usize {201pub 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
221 assert(data.len != 0);202 assert(data.len != 0);
222 switch (builtin.os) {203 switch (builtin.os) {
223 .macosx,204 .macosx,
...@@ -246,7 +227,7 @@ pub async fn preadv(loop: *Loop, fd: fd_t, data: []const []u8, offset: usize) PR...@@ -246,7 +227,7 @@ pub async fn preadv(loop: *Loop, fd: fd_t, data: []const []u8, offset: usize) PR
246 }227 }
247}228}
248229
249/// data must outlive the returned promise230/// data must outlive the returned frame
250pub async fn preadvWindows(loop: *Loop, fd: fd_t, data: []const []u8, offset: u64) !usize {231pub async fn preadvWindows(loop: *Loop, fd: fd_t, data: []const []u8, offset: u64) !usize {
251 assert(data.len != 0);232 assert(data.len != 0);
252 if (data.len == 1) return await (async preadWindows(loop, fd, data[0], offset) catch unreachable);233 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...@@ -272,15 +253,10 @@ pub async fn preadvWindows(loop: *Loop, fd: fd_t, data: []const []u8, offset: u6
272}253}
273254
274pub async fn preadWindows(loop: *Loop, fd: fd_t, data: []u8, offset: u64) !usize {255pub 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
280 var resume_node = Loop.ResumeNode.Basic{256 var resume_node = Loop.ResumeNode.Basic{
281 .base = Loop.ResumeNode{257 .base = Loop.ResumeNode{
282 .id = Loop.ResumeNode.Id.Basic,258 .id = Loop.ResumeNode.Id.Basic,
283 .handle = @handle(),259 .handle = @frame(),
284 .overlapped = windows.OVERLAPPED{260 .overlapped = windows.OVERLAPPED{
285 .Internal = 0,261 .Internal = 0,
286 .InternalHigh = 0,262 .InternalHigh = 0,
...@@ -314,18 +290,13 @@ pub async fn preadWindows(loop: *Loop, fd: fd_t, data: []u8, offset: u64) !usize...@@ -314,18 +290,13 @@ pub async fn preadWindows(loop: *Loop, fd: fd_t, data: []u8, offset: u64) !usize
314 return usize(bytes_transferred);290 return usize(bytes_transferred);
315}291}
316292
317/// iovecs must live until preadv promise completes293/// iovecs must live until preadv frame completes
318pub async fn preadvPosix(294pub async fn preadvPosix(
319 loop: *Loop,295 loop: *Loop,
320 fd: fd_t,296 fd: fd_t,
321 iovecs: []const os.iovec,297 iovecs: []const os.iovec,
322 offset: usize,298 offset: usize,
323) os.ReadError!usize {299) os.ReadError!usize {
324 // workaround for https://github.com/ziglang/zig/issues/1194
325 suspend {
326 resume @handle();
327 }
328
329 var req_node = RequestNode{300 var req_node = RequestNode{
330 .prev = null,301 .prev = null,
331 .next = null,302 .next = null,
...@@ -342,7 +313,7 @@ pub async fn preadvPosix(...@@ -342,7 +313,7 @@ pub async fn preadvPosix(
342 .TickNode = Loop.NextTickNode{313 .TickNode = Loop.NextTickNode{
343 .prev = null,314 .prev = null,
344 .next = null,315 .next = null,
345 .data = @handle(),316 .data = @frame(),
346 },317 },
347 },318 },
348 },319 },
...@@ -363,11 +334,6 @@ pub async fn openPosix(...@@ -363,11 +334,6 @@ pub async fn openPosix(
363 flags: u32,334 flags: u32,
364 mode: File.Mode,335 mode: File.Mode,
365) File.OpenError!fd_t {336) File.OpenError!fd_t {
366 // workaround for https://github.com/ziglang/zig/issues/1194
367 suspend {
368 resume @handle();
369 }
370
371 const path_c = try std.os.toPosixPath(path);337 const path_c = try std.os.toPosixPath(path);
372338
373 var req_node = RequestNode{339 var req_node = RequestNode{
...@@ -386,7 +352,7 @@ pub async fn openPosix(...@@ -386,7 +352,7 @@ pub async fn openPosix(
386 .TickNode = Loop.NextTickNode{352 .TickNode = Loop.NextTickNode{
387 .prev = null,353 .prev = null,
388 .next = null,354 .next = null,
389 .data = @handle(),355 .data = @frame(),
390 },356 },
391 },357 },
392 },358 },
...@@ -643,11 +609,6 @@ async fn writeFileWindows(loop: *Loop, path: []const u8, contents: []const u8) !...@@ -643,11 +609,6 @@ async fn writeFileWindows(loop: *Loop, path: []const u8, contents: []const u8) !
643}609}
644610
645async fn writeFileModeThread(loop: *Loop, path: []const u8, contents: []const u8, mode: File.Mode) !void {611async 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
651 const path_with_null = try std.cstr.addNullByte(loop.allocator, path);612 const path_with_null = try std.cstr.addNullByte(loop.allocator, path);
652 defer loop.allocator.free(path_with_null);613 defer loop.allocator.free(path_with_null);
653614
...@@ -667,7 +628,7 @@ async fn writeFileModeThread(loop: *Loop, path: []const u8, contents: []const u8...@@ -667,7 +628,7 @@ async fn writeFileModeThread(loop: *Loop, path: []const u8, contents: []const u8
667 .TickNode = Loop.NextTickNode{628 .TickNode = Loop.NextTickNode{
668 .prev = null,629 .prev = null,
669 .next = null,630 .next = null,
670 .data = @handle(),631 .data = @frame(),
671 },632 },
672 },633 },
673 },634 },
...@@ -682,7 +643,7 @@ async fn writeFileModeThread(loop: *Loop, path: []const u8, contents: []const u8...@@ -682,7 +643,7 @@ async fn writeFileModeThread(loop: *Loop, path: []const u8, contents: []const u8
682 return req_node.data.msg.WriteFile.result;643 return req_node.data.msg.WriteFile.result;
683}644}
684645
685/// The promise resumes when the last data has been confirmed written, but before the file handle646/// The frame resumes when the last data has been confirmed written, but before the file handle
686/// is closed.647/// is closed.
687/// Caller owns returned memory.648/// Caller owns returned memory.
688pub async fn readFile(loop: *Loop, file_path: []const u8, max_size: usize) ![]u8 {649pub async fn readFile(loop: *Loop, file_path: []const u8, max_size: usize) ![]u8 {
...@@ -734,7 +695,7 @@ pub const WatchEventId = enum {...@@ -734,7 +695,7 @@ pub const WatchEventId = enum {
734//695//
735// const FileTable = std.AutoHashMap([]const u8, *Put);696// const FileTable = std.AutoHashMap([]const u8, *Put);
736// const Put = struct {697// const Put = struct {
737// putter: promise,698// putter: anyframe,
738// value_ptr: *V,699// value_ptr: *V,
739// };700// };
740// },701// },
...@@ -748,21 +709,21 @@ pub const WatchEventId = enum {...@@ -748,21 +709,21 @@ pub const WatchEventId = enum {
748// const WindowsOsData = struct {709// const WindowsOsData = struct {
749// table_lock: event.Lock,710// table_lock: event.Lock,
750// dir_table: DirTable,711// dir_table: DirTable,
751// all_putters: std.atomic.Queue(promise),712// all_putters: std.atomic.Queue(anyframe),
752// ref_count: std.atomic.Int(usize),713// ref_count: std.atomic.Int(usize),
753//714//
754// const DirTable = std.AutoHashMap([]const u8, *Dir);715// const DirTable = std.AutoHashMap([]const u8, *Dir);
755// const FileTable = std.AutoHashMap([]const u16, V);716// const FileTable = std.AutoHashMap([]const u16, V);
756//717//
757// const Dir = struct {718// const Dir = struct {
758// putter: promise,719// putter: anyframe,
759// file_table: FileTable,720// file_table: FileTable,
760// table_lock: event.Lock,721// table_lock: event.Lock,
761// };722// };
762// };723// };
763//724//
764// const LinuxOsData = struct {725// const LinuxOsData = struct {
765// putter: promise,726// putter: anyframe,
766// inotify_fd: i32,727// inotify_fd: i32,
767// wd_table: WdTable,728// wd_table: WdTable,
768// table_lock: event.Lock,729// table_lock: event.Lock,
...@@ -776,7 +737,7 @@ pub const WatchEventId = enum {...@@ -776,7 +737,7 @@ pub const WatchEventId = enum {
776// };737// };
777// };738// };
778//739//
779// const FileToHandle = std.AutoHashMap([]const u8, promise);740// const FileToHandle = std.AutoHashMap([]const u8, anyframe);
780//741//
781// const Self = @This();742// const Self = @This();
782//743//
...@@ -811,7 +772,7 @@ pub const WatchEventId = enum {...@@ -811,7 +772,7 @@ pub const WatchEventId = enum {
811// .table_lock = event.Lock.init(loop),772// .table_lock = event.Lock.init(loop),
812// .dir_table = OsData.DirTable.init(loop.allocator),773// .dir_table = OsData.DirTable.init(loop.allocator),
813// .ref_count = std.atomic.Int(usize).init(1),774// .ref_count = std.atomic.Int(usize).init(1),
814// .all_putters = std.atomic.Queue(promise).init(),775// .all_putters = std.atomic.Queue(anyframe).init(),
815// },776// },
816// };777// };
817// return self;778// return self;
...@@ -926,14 +887,9 @@ pub const WatchEventId = enum {...@@ -926,14 +887,9 @@ pub const WatchEventId = enum {
926// }887// }
927//888//
928// async fn kqPutEvents(self: *Self, close_op: *CloseOperation, value: V, out_put: **OsData.Put) void {889// 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//
934// var value_copy = value;890// var value_copy = value;
935// var put = OsData.Put{891// var put = OsData.Put{
936// .putter = @handle(),892// .putter = @frame(),
937// .value_ptr = &value_copy,893// .value_ptr = &value_copy,
938// };894// };
939// out_put.* = &put;895// out_put.* = &put;
...@@ -1091,18 +1047,13 @@ pub const WatchEventId = enum {...@@ -1091,18 +1047,13 @@ pub const WatchEventId = enum {
1091// }1047// }
1092//1048//
1093// async fn windowsDirReader(self: *Self, dir_handle: windows.HANDLE, dir: *OsData.Dir) void {1049// 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//
1099// self.ref();1050// self.ref();
1100// defer self.deref();1051// defer self.deref();
1101//1052//
1102// defer os.close(dir_handle);1053// defer os.close(dir_handle);
1103//1054//
1104// var putter_node = std.atomic.Queue(promise).Node{1055// var putter_node = std.atomic.Queue(anyframe).Node{
1105// .data = @handle(),1056// .data = @frame(),
1106// .prev = null,1057// .prev = null,
1107// .next = null,1058// .next = null,
1108// };1059// };
...@@ -1112,7 +1063,7 @@ pub const WatchEventId = enum {...@@ -1112,7 +1063,7 @@ pub const WatchEventId = enum {
1112// var resume_node = Loop.ResumeNode.Basic{1063// var resume_node = Loop.ResumeNode.Basic{
1113// .base = Loop.ResumeNode{1064// .base = Loop.ResumeNode{
1114// .id = Loop.ResumeNode.Id.Basic,1065// .id = Loop.ResumeNode.Id.Basic,
1115// .handle = @handle(),1066// .handle = @frame(),
1116// .overlapped = windows.OVERLAPPED{1067// .overlapped = windows.OVERLAPPED{
1117// .Internal = 0,1068// .Internal = 0,
1118// .InternalHigh = 0,1069// .InternalHigh = 0,
...@@ -1207,17 +1158,12 @@ pub const WatchEventId = enum {...@@ -1207,17 +1158,12 @@ pub const WatchEventId = enum {
1207// }1158// }
1208//1159//
1209// async fn linuxEventPutter(inotify_fd: i32, channel: *event.Channel(Event.Error!Event), out_watch: **Self) void {1160// 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//
1215// const loop = channel.loop;1161// const loop = channel.loop;
1216//1162//
1217// var watch = Self{1163// var watch = Self{
1218// .channel = channel,1164// .channel = channel,
1219// .os_data = OsData{1165// .os_data = OsData{
1220// .putter = @handle(),1166// .putter = @frame(),
1221// .inotify_fd = inotify_fd,1167// .inotify_fd = inotify_fd,
1222// .wd_table = OsData.WdTable.init(loop.allocator),1168// .wd_table = OsData.WdTable.init(loop.allocator),
1223// .table_lock = event.Lock.init(loop),1169// .table_lock = event.Lock.init(loop),
std/event/future.zig+19-26
...@@ -2,8 +2,6 @@ const std = @import("../std.zig");...@@ -2,8 +2,6 @@ const std = @import("../std.zig");
2const assert = std.debug.assert;2const assert = std.debug.assert;
3const testing = std.testing;3const testing = std.testing;
4const builtin = @import("builtin");4const builtin = @import("builtin");
5const AtomicRmwOp = builtin.AtomicRmwOp;
6const AtomicOrder = builtin.AtomicOrder;
7const Lock = std.event.Lock;5const Lock = std.event.Lock;
8const Loop = std.event.Loop;6const Loop = std.event.Loop;
97
...@@ -23,7 +21,7 @@ pub fn Future(comptime T: type) type {...@@ -23,7 +21,7 @@ pub fn Future(comptime T: type) type {
23 available: u8,21 available: u8,
2422
25 const Self = @This();23 const Self = @This();
26 const Queue = std.atomic.Queue(promise);24 const Queue = std.atomic.Queue(anyframe);
2725
28 pub fn init(loop: *Loop) Self {26 pub fn init(loop: *Loop) Self {
29 return Self{27 return Self{
...@@ -37,10 +35,10 @@ pub fn Future(comptime T: type) type {...@@ -37,10 +35,10 @@ pub fn Future(comptime T: type) type {
37 /// available.35 /// available.
38 /// Thread-safe.36 /// Thread-safe.
39 pub async fn get(self: *Self) *T {37 pub async fn get(self: *Self) *T {
40 if (@atomicLoad(u8, &self.available, AtomicOrder.SeqCst) == 2) {38 if (@atomicLoad(u8, &self.available, .SeqCst) == 2) {
41 return &self.data;39 return &self.data;
42 }40 }
43 const held = await (async self.lock.acquire() catch unreachable);41 const held = self.lock.acquire();
44 held.release();42 held.release();
4543
46 return &self.data;44 return &self.data;
...@@ -49,7 +47,7 @@ pub fn Future(comptime T: type) type {...@@ -49,7 +47,7 @@ pub fn Future(comptime T: type) type {
49 /// Gets the data without waiting for it. If it's available, a pointer is47 /// Gets the data without waiting for it. If it's available, a pointer is
50 /// returned. Otherwise, null is returned.48 /// returned. Otherwise, null is returned.
51 pub fn getOrNull(self: *Self) ?*T {49 pub fn getOrNull(self: *Self) ?*T {
52 if (@atomicLoad(u8, &self.available, AtomicOrder.SeqCst) == 2) {50 if (@atomicLoad(u8, &self.available, .SeqCst) == 2) {
53 return &self.data;51 return &self.data;
54 } else {52 } else {
55 return null;53 return null;
...@@ -62,10 +60,10 @@ pub fn Future(comptime T: type) type {...@@ -62,10 +60,10 @@ pub fn Future(comptime T: type) type {
62 /// It's not required to call start() before resolve() but it can be useful since60 /// It's not required to call start() before resolve() but it can be useful since
63 /// this method is thread-safe.61 /// this method is thread-safe.
64 pub async fn start(self: *Self) ?*T {62 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;
66 switch (state) {64 switch (state) {
67 1 => {65 1 => {
68 const held = await (async self.lock.acquire() catch unreachable);66 const held = self.lock.acquire();
69 held.release();67 held.release();
70 return &self.data;68 return &self.data;
71 },69 },
...@@ -77,7 +75,7 @@ pub fn Future(comptime T: type) type {...@@ -77,7 +75,7 @@ pub fn Future(comptime T: type) type {
77 /// Make the data become available. May be called only once.75 /// Make the data become available. May be called only once.
78 /// Before calling this, modify the `data` property.76 /// Before calling this, modify the `data` property.
79 pub fn resolve(self: *Self) void {77 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);
81 assert(prev == 0 or prev == 1); // resolve() called twice79 assert(prev == 0 or prev == 1); // resolve() called twice
82 Lock.Held.release(Lock.Held{ .lock = &self.lock });80 Lock.Held.release(Lock.Held{ .lock = &self.lock });
83 }81 }
...@@ -86,7 +84,7 @@ pub fn Future(comptime T: type) type {...@@ -86,7 +84,7 @@ pub fn Future(comptime T: type) type {
8684
87test "std.event.Future" {85test "std.event.Future" {
88 // https://github.com/ziglang/zig/issues/190886 // 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
91 const allocator = std.heap.direct_allocator;89 const allocator = std.heap.direct_allocator;
9290
...@@ -94,38 +92,33 @@ test "std.event.Future" {...@@ -94,38 +92,33 @@ test "std.event.Future" {
94 try loop.initMultiThreaded(allocator);92 try loop.initMultiThreaded(allocator);
95 defer loop.deinit();93 defer loop.deinit();
9694
97 const handle = try async<allocator> testFuture(&loop);95 const handle = async testFuture(&loop);
98 defer cancel handle;
9996
100 loop.run();97 loop.run();
101}98}
10299
103async fn testFuture(loop: *Loop) void {100async fn testFuture(loop: *Loop) void {
104 suspend {
105 resume @handle();
106 }
107 var future = Future(i32).init(loop);101 var future = Future(i32).init(loop);
108102
109 const a = async waitOnFuture(&future) catch @panic("memory");103 const a = async waitOnFuture(&future);
110 const b = async waitOnFuture(&future) catch @panic("memory");104 const b = async waitOnFuture(&future);
111 const c = async resolveFuture(&future) catch @panic("memory");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);
114 cancel c;113 cancel c;
115 testing.expect(result == 12);114 testing.expect(result == 12);
116}115}
117116
118async fn waitOnFuture(future: *Future(i32)) i32 {117async fn waitOnFuture(future: *Future(i32)) i32 {
119 suspend {118 return future.get().*;
120 resume @handle();
121 }
122 return (await (async future.get() catch @panic("memory"))).*;
123}119}
124120
125async fn resolveFuture(future: *Future(i32)) void {121async fn resolveFuture(future: *Future(i32)) void {
126 suspend {
127 resume @handle();
128 }
129 future.data = 6;122 future.data = 6;
130 future.resolve();123 future.resolve();
131}124}
std/event/group.zig+20-48
...@@ -2,8 +2,6 @@ const std = @import("../std.zig");...@@ -2,8 +2,6 @@ const std = @import("../std.zig");
2const builtin = @import("builtin");2const builtin = @import("builtin");
3const Lock = std.event.Lock;3const Lock = std.event.Lock;
4const Loop = std.event.Loop;4const Loop = std.event.Loop;
5const AtomicRmwOp = builtin.AtomicRmwOp;
6const AtomicOrder = builtin.AtomicOrder;
7const testing = std.testing;5const testing = std.testing;
86
9/// ReturnType must be `void` or `E!void`7/// ReturnType must be `void` or `E!void`
...@@ -16,10 +14,10 @@ pub fn Group(comptime ReturnType: type) type {...@@ -16,10 +14,10 @@ pub fn Group(comptime ReturnType: type) type {
16 const Self = @This();14 const Self = @This();
1715
18 const Error = switch (@typeInfo(ReturnType)) {16 const Error = switch (@typeInfo(ReturnType)) {
19 builtin.TypeId.ErrorUnion => |payload| payload.error_set,17 .ErrorUnion => |payload| payload.error_set,
20 else => void,18 else => void,
21 };19 };
22 const Stack = std.atomic.Stack(promise->ReturnType);20 const Stack = std.atomic.Stack(anyframe->ReturnType);
2321
24 pub fn init(loop: *Loop) Self {22 pub fn init(loop: *Loop) Self {
25 return Self{23 return Self{
...@@ -29,7 +27,7 @@ pub fn Group(comptime ReturnType: type) type {...@@ -29,7 +27,7 @@ pub fn Group(comptime ReturnType: type) type {
29 };27 };
30 }28 }
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.
33 pub fn deinit(self: *Self) void {31 pub fn deinit(self: *Self) void {
34 while (self.coro_stack.pop()) |node| {32 while (self.coro_stack.pop()) |node| {
35 cancel node.data;33 cancel node.data;
...@@ -40,8 +38,8 @@ pub fn Group(comptime ReturnType: type) type {...@@ -40,8 +38,8 @@ pub fn Group(comptime ReturnType: type) type {
40 }38 }
41 }39 }
4240
43 /// Add a promise to the group. Thread-safe.41 /// Add a frame to the group. Thread-safe.
44 pub fn add(self: *Self, handle: promise->ReturnType) (error{OutOfMemory}!void) {42 pub fn add(self: *Self, handle: anyframe->ReturnType) (error{OutOfMemory}!void) {
45 const node = try self.lock.loop.allocator.create(Stack.Node);43 const node = try self.lock.loop.allocator.create(Stack.Node);
46 node.* = Stack.Node{44 node.* = Stack.Node{
47 .next = undefined,45 .next = undefined,
...@@ -51,7 +49,7 @@ pub fn Group(comptime ReturnType: type) type {...@@ -51,7 +49,7 @@ pub fn Group(comptime ReturnType: type) type {
51 }49 }
5250
53 /// Add a node to the group. Thread-safe. Cannot fail.51 /// 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.
55 /// The node's memory should be in the coroutine frame of53 /// The node's memory should be in the coroutine frame of
56 /// the handle that is in the node, or somewhere guaranteed to live54 /// the handle that is in the node, or somewhere guaranteed to live
57 /// at least as long.55 /// at least as long.
...@@ -59,40 +57,11 @@ pub fn Group(comptime ReturnType: type) type {...@@ -59,40 +57,11 @@ pub fn Group(comptime ReturnType: type) type {
59 self.coro_stack.push(node);57 self.coro_stack.push(node);
60 }58 }
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
89 /// Wait for all the calls and promises of the group to complete.60 /// Wait for all the calls and promises of the group to complete.
90 /// Thread-safe.61 /// Thread-safe.
91 /// Safe to call any number of times.62 /// Safe to call any number of times.
92 pub async fn wait(self: *Self) ReturnType {63 pub async fn wait(self: *Self) ReturnType {
93 // TODO catch unreachable because the allocation can be grouped with64 const held = self.lock.acquire();
94 // the coro frame allocation
95 const held = await (async self.lock.acquire() catch unreachable);
96 defer held.release();65 defer held.release();
9766
98 while (self.coro_stack.pop()) |node| {67 while (self.coro_stack.pop()) |node| {
...@@ -131,8 +100,7 @@ test "std.event.Group" {...@@ -131,8 +100,7 @@ test "std.event.Group" {
131 try loop.initMultiThreaded(allocator);100 try loop.initMultiThreaded(allocator);
132 defer loop.deinit();101 defer loop.deinit();
133102
134 const handle = try async<allocator> testGroup(&loop);103 const handle = async testGroup(&loop);
135 defer cancel handle;
136104
137 loop.run();105 loop.run();
138}106}
...@@ -140,26 +108,30 @@ test "std.event.Group" {...@@ -140,26 +108,30 @@ test "std.event.Group" {
140async fn testGroup(loop: *Loop) void {108async fn testGroup(loop: *Loop) void {
141 var count: usize = 0;109 var count: usize = 0;
142 var group = Group(void).init(loop);110 var group = Group(void).init(loop);
143 group.add(async sleepALittle(&count) catch @panic("memory")) catch @panic("memory");111 var sleep_a_little_frame = async sleepALittle(&count);
144 group.call(increaseByTen, &count) catch @panic("memory");112 group.add(&sleep_a_little_frame) catch @panic("memory");
145 await (async group.wait() 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();
146 testing.expect(count == 11);116 testing.expect(count == 11);
147117
148 var another = Group(anyerror!void).init(loop);118 var another = Group(anyerror!void).init(loop);
149 another.add(async somethingElse() catch @panic("memory")) catch @panic("memory");119 var something_else_frame = async somethingElse();
150 another.call(doSomethingThatFails) catch @panic("memory");120 another.add(&something_else_frame) catch @panic("memory");
151 testing.expectError(error.ItBroke, await (async another.wait() 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());
152}124}
153125
154async fn sleepALittle(count: *usize) void {126async fn sleepALittle(count: *usize) void {
155 std.time.sleep(1 * std.time.millisecond);127 std.time.sleep(1 * std.time.millisecond);
156 _ = @atomicRmw(usize, count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst);128 _ = @atomicRmw(usize, count, .Add, 1, .SeqCst);
157}129}
158130
159async fn increaseByTen(count: *usize) void {131async fn increaseByTen(count: *usize) void {
160 var i: usize = 0;132 var i: usize = 0;
161 while (i < 10) : (i += 1) {133 while (i < 10) : (i += 1) {
162 _ = @atomicRmw(usize, count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst);134 _ = @atomicRmw(usize, count, .Add, 1, .SeqCst);
163 }135 }
164}136}
165137
std/event/io.zig+9-10
...@@ -1,6 +1,5 @@...@@ -1,6 +1,5 @@
1const std = @import("../std.zig");1const std = @import("../std.zig");
2const builtin = @import("builtin");2const builtin = @import("builtin");
3const Allocator = std.mem.Allocator;
4const assert = std.debug.assert;3const assert = std.debug.assert;
5const mem = std.mem;4const mem = std.mem;
65
...@@ -12,13 +11,13 @@ pub fn InStream(comptime ReadError: type) type {...@@ -12,13 +11,13 @@ pub fn InStream(comptime ReadError: type) type {
12 /// Return the number of bytes read. It may be less than buffer.len.11 /// Return the number of bytes read. It may be less than buffer.len.
13 /// If the number of bytes read is 0, it means end of stream.12 /// If the number of bytes read is 0, it means end of stream.
14 /// End of stream is not an error condition.13 /// 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
17 /// Return the number of bytes read. It may be less than buffer.len.16 /// Return the number of bytes read. It may be less than buffer.len.
18 /// If the number of bytes read is 0, it means end of stream.17 /// If the number of bytes read is 0, it means end of stream.
19 /// End of stream is not an error condition.18 /// End of stream is not an error condition.
20 pub async fn read(self: *Self, buffer: []u8) !usize {19 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);
22 }21 }
2322
24 /// Return the number of bytes read. If it is less than buffer.len23 /// Return the number of bytes read. If it is less than buffer.len
...@@ -26,7 +25,7 @@ pub fn InStream(comptime ReadError: type) type {...@@ -26,7 +25,7 @@ pub fn InStream(comptime ReadError: type) type {
26 pub async fn readFull(self: *Self, buffer: []u8) !usize {25 pub async fn readFull(self: *Self, buffer: []u8) !usize {
27 var index: usize = 0;26 var index: usize = 0;
28 while (index != buf.len) {27 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..]);
30 if (amt_read == 0) return index;29 if (amt_read == 0) return index;
31 index += amt_read;30 index += amt_read;
32 }31 }
...@@ -35,25 +34,25 @@ pub fn InStream(comptime ReadError: type) type {...@@ -35,25 +34,25 @@ pub fn InStream(comptime ReadError: type) type {
3534
36 /// Same as `readFull` but end of stream returns `error.EndOfStream`.35 /// Same as `readFull` but end of stream returns `error.EndOfStream`.
37 pub async fn readNoEof(self: *Self, buf: []u8) !void {36 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..]);
39 if (amt_read < buf.len) return error.EndOfStream;38 if (amt_read < buf.len) return error.EndOfStream;
40 }39 }
4140
42 pub async fn readIntLittle(self: *Self, comptime T: type) !T {41 pub async fn readIntLittle(self: *Self, comptime T: type) !T {
43 var bytes: [@sizeOf(T)]u8 = undefined;42 var bytes: [@sizeOf(T)]u8 = undefined;
44 try await (async self.readNoEof(bytes[0..]) catch unreachable);43 try self.readNoEof(bytes[0..]);
45 return mem.readIntLittle(T, &bytes);44 return mem.readIntLittle(T, &bytes);
46 }45 }
4746
48 pub async fn readIntBe(self: *Self, comptime T: type) !T {47 pub async fn readIntBe(self: *Self, comptime T: type) !T {
49 var bytes: [@sizeOf(T)]u8 = undefined;48 var bytes: [@sizeOf(T)]u8 = undefined;
50 try await (async self.readNoEof(bytes[0..]) catch unreachable);49 try self.readNoEof(bytes[0..]);
51 return mem.readIntBig(T, &bytes);50 return mem.readIntBig(T, &bytes);
52 }51 }
5352
54 pub async fn readInt(self: *Self, comptime T: type, endian: builtin.Endian) !T {53 pub async fn readInt(self: *Self, comptime T: type, endian: builtin.Endian) !T {
55 var bytes: [@sizeOf(T)]u8 = undefined;54 var bytes: [@sizeOf(T)]u8 = undefined;
56 try await (async self.readNoEof(bytes[0..]) catch unreachable);55 try self.readNoEof(bytes[0..]);
57 return mem.readInt(T, &bytes, endian);56 return mem.readInt(T, &bytes, endian);
58 }57 }
5958
...@@ -61,7 +60,7 @@ pub fn InStream(comptime ReadError: type) type {...@@ -61,7 +60,7 @@ pub fn InStream(comptime ReadError: type) type {
61 // Only extern and packed structs have defined in-memory layout.60 // Only extern and packed structs have defined in-memory layout.
62 comptime assert(@typeInfo(T).Struct.layout != builtin.TypeInfo.ContainerLayout.Auto);61 comptime assert(@typeInfo(T).Struct.layout != builtin.TypeInfo.ContainerLayout.Auto);
63 var res: [1]T = undefined;62 var res: [1]T = undefined;
64 try await (async self.readNoEof(@sliceToBytes(res[0..])) catch unreachable);63 try self.readNoEof(@sliceToBytes(res[0..]));
65 return res[0];64 return res[0];
66 }65 }
67 };66 };
...@@ -72,6 +71,6 @@ pub fn OutStream(comptime WriteError: type) type {...@@ -72,6 +71,6 @@ pub fn OutStream(comptime WriteError: type) type {
72 const Self = @This();71 const Self = @This();
73 pub const Error = WriteError;72 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,
76 };75 };
77}76}
std/event/lock.zig+21-33
...@@ -3,8 +3,6 @@ const builtin = @import("builtin");...@@ -3,8 +3,6 @@ const builtin = @import("builtin");
3const assert = std.debug.assert;3const assert = std.debug.assert;
4const testing = std.testing;4const testing = std.testing;
5const mem = std.mem;5const mem = std.mem;
6const AtomicRmwOp = builtin.AtomicRmwOp;
7const AtomicOrder = builtin.AtomicOrder;
8const Loop = std.event.Loop;6const Loop = std.event.Loop;
97
10/// Thread-safe async/await lock.8/// Thread-safe async/await lock.
...@@ -17,7 +15,7 @@ pub const Lock = struct {...@@ -17,7 +15,7 @@ pub const Lock = struct {
17 queue: Queue,15 queue: Queue,
18 queue_empty_bit: u8, // TODO make this a bool16 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
22 pub const Held = struct {20 pub const Held = struct {
23 lock: *Lock,21 lock: *Lock,
...@@ -30,19 +28,19 @@ pub const Lock = struct {...@@ -30,19 +28,19 @@ pub const Lock = struct {
30 }28 }
3129
32 // We need to release the lock.30 // We need to release the lock.
33 _ = @atomicRmw(u8, &self.lock.queue_empty_bit, AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst);31 _ = @atomicRmw(u8, &self.lock.queue_empty_bit, .Xchg, 1, .SeqCst);
34 _ = @atomicRmw(u8, &self.lock.shared_bit, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst);32 _ = @atomicRmw(u8, &self.lock.shared_bit, .Xchg, 0, .SeqCst);
3533
36 // There might be a queue item. If we know the queue is empty, we can be done,34 // There might be a queue item. If we know the queue is empty, we can be done,
37 // because the other actor will try to obtain the lock.35 // because the other actor will try to obtain the lock.
38 // But if there's a queue item, we are the actor which must loop and attempt36 // But if there's a queue item, we are the actor which must loop and attempt
39 // to grab the lock again.37 // 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) {
41 return;39 return;
42 }40 }
4341
44 while (true) {42 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);
46 if (old_bit != 0) {44 if (old_bit != 0) {
47 // We did not obtain the lock. Great, the queue is someone else's problem.45 // We did not obtain the lock. Great, the queue is someone else's problem.
48 return;46 return;
...@@ -55,11 +53,11 @@ pub const Lock = struct {...@@ -55,11 +53,11 @@ pub const Lock = struct {
55 }53 }
5654
57 // Release the lock again.55 // Release the lock again.
58 _ = @atomicRmw(u8, &self.lock.queue_empty_bit, AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst);56 _ = @atomicRmw(u8, &self.lock.queue_empty_bit, .Xchg, 1, .SeqCst);
59 _ = @atomicRmw(u8, &self.lock.shared_bit, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst);57 _ = @atomicRmw(u8, &self.lock.shared_bit, .Xchg, 0, .SeqCst);
6058
61 // Find out if we can be done.59 // 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) {
63 return;61 return;
64 }62 }
65 }63 }
...@@ -88,15 +86,11 @@ pub const Lock = struct {...@@ -88,15 +86,11 @@ pub const Lock = struct {
88 /// All calls to acquire() and release() must complete before calling deinit().86 /// All calls to acquire() and release() must complete before calling deinit().
89 pub fn deinit(self: *Lock) void {87 pub fn deinit(self: *Lock) void {
90 assert(self.shared_bit == 0);88 assert(self.shared_bit == 0);
91 while (self.queue.get()) |node| cancel node.data;89 while (self.queue.get()) |node| resume node.data;
92 }90 }
9391
94 pub async fn acquire(self: *Lock) Held {92 pub async fn acquire(self: *Lock) Held {
95 // TODO explicitly put this memory in the coroutine frame #119493 var my_tick_node = Loop.NextTickNode.init(@frame());
96 suspend {
97 resume @handle();
98 }
99 var my_tick_node = Loop.NextTickNode.init(@handle());
10094
101 errdefer _ = self.queue.remove(&my_tick_node); // TODO test canceling an acquire95 errdefer _ = self.queue.remove(&my_tick_node); // TODO test canceling an acquire
102 suspend {96 suspend {
...@@ -107,9 +101,9 @@ pub const Lock = struct {...@@ -107,9 +101,9 @@ pub const Lock = struct {
107101
108 // We set this bit so that later we can rely on the fact, that if queue_empty_bit is 1, some actor102 // We set this bit so that later we can rely on the fact, that if queue_empty_bit is 1, some actor
109 // will attempt to grab the lock.103 // 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);
113 if (old_bit == 0) {107 if (old_bit == 0) {
114 if (self.queue.get()) |node| {108 if (self.queue.get()) |node| {
115 // Whether this node is us or someone else, we tail resume it.109 // Whether this node is us or someone else, we tail resume it.
...@@ -123,8 +117,7 @@ pub const Lock = struct {...@@ -123,8 +117,7 @@ pub const Lock = struct {
123};117};
124118
125test "std.event.Lock" {119test "std.event.Lock" {
126 // TODO https://github.com/ziglang/zig/issues/2377120 // TODO https://github.com/ziglang/zig/issues/1908
127 if (true) return error.SkipZigTest;
128 if (builtin.single_threaded) return error.SkipZigTest;121 if (builtin.single_threaded) return error.SkipZigTest;
129122
130 const allocator = std.heap.direct_allocator;123 const allocator = std.heap.direct_allocator;
...@@ -136,39 +129,34 @@ test "std.event.Lock" {...@@ -136,39 +129,34 @@ test "std.event.Lock" {
136 var lock = Lock.init(&loop);129 var lock = Lock.init(&loop);
137 defer lock.deinit();130 defer lock.deinit();
138131
139 const handle = try async<allocator> testLock(&loop, &lock);132 _ = async testLock(&loop, &lock);
140 defer cancel handle;
141 loop.run();133 loop.run();
142134
143 testing.expectEqualSlices(i32, [1]i32{3 * @intCast(i32, shared_test_data.len)} ** shared_test_data.len, shared_test_data);135 testing.expectEqualSlices(i32, [1]i32{3 * @intCast(i32, shared_test_data.len)} ** shared_test_data.len, shared_test_data);
144}136}
145137
146async fn testLock(loop: *Loop, lock: *Lock) void {138async fn testLock(loop: *Loop, lock: *Lock) void {
147 // TODO explicitly put next tick node memory in the coroutine frame #1194139 const handle1 = async lockRunner(lock);
148 suspend {
149 resume @handle();
150 }
151 const handle1 = async lockRunner(lock) catch @panic("out of memory");
152 var tick_node1 = Loop.NextTickNode{140 var tick_node1 = Loop.NextTickNode{
153 .prev = undefined,141 .prev = undefined,
154 .next = undefined,142 .next = undefined,
155 .data = handle1,143 .data = &handle1,
156 };144 };
157 loop.onNextTick(&tick_node1);145 loop.onNextTick(&tick_node1);
158146
159 const handle2 = async lockRunner(lock) catch @panic("out of memory");147 const handle2 = async lockRunner(lock);
160 var tick_node2 = Loop.NextTickNode{148 var tick_node2 = Loop.NextTickNode{
161 .prev = undefined,149 .prev = undefined,
162 .next = undefined,150 .next = undefined,
163 .data = handle2,151 .data = &handle2,
164 };152 };
165 loop.onNextTick(&tick_node2);153 loop.onNextTick(&tick_node2);
166154
167 const handle3 = async lockRunner(lock) catch @panic("out of memory");155 const handle3 = async lockRunner(lock);
168 var tick_node3 = Loop.NextTickNode{156 var tick_node3 = Loop.NextTickNode{
169 .prev = undefined,157 .prev = undefined,
170 .next = undefined,158 .next = undefined,
171 .data = handle3,159 .data = &handle3,
172 };160 };
173 loop.onNextTick(&tick_node3);161 loop.onNextTick(&tick_node3);
174162
...@@ -185,7 +173,7 @@ async fn lockRunner(lock: *Lock) void {...@@ -185,7 +173,7 @@ async fn lockRunner(lock: *Lock) void {
185173
186 var i: usize = 0;174 var i: usize = 0;
187 while (i < shared_test_data.len) : (i += 1) {175 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();
189 const handle = await lock_promise;177 const handle = await lock_promise;
190 defer handle.release();178 defer handle.release();
191179
std/event/loop.zig+2-2
...@@ -457,7 +457,7 @@ pub const Loop = struct {...@@ -457,7 +457,7 @@ pub const Loop = struct {
457 var resume_node = ResumeNode.Basic{457 var resume_node = ResumeNode.Basic{
458 .base = ResumeNode{458 .base = ResumeNode{
459 .id = ResumeNode.Id.Basic,459 .id = ResumeNode.Id.Basic,
460 .handle = @handle(),460 .handle = @frame(),
461 .overlapped = ResumeNode.overlapped_init,461 .overlapped = ResumeNode.overlapped_init,
462 },462 },
463 };463 };
...@@ -469,7 +469,7 @@ pub const Loop = struct {...@@ -469,7 +469,7 @@ pub const Loop = struct {
469 var resume_node = ResumeNode.Basic{469 var resume_node = ResumeNode.Basic{
470 .base = ResumeNode{470 .base = ResumeNode{
471 .id = ResumeNode.Id.Basic,471 .id = ResumeNode.Id.Basic,
472 .handle = @handle(),472 .handle = @frame(),
473 .overlapped = ResumeNode.overlapped_init,473 .overlapped = ResumeNode.overlapped_init,
474 },474 },
475 .kev = undefined,475 .kev = undefined,
std/event/net.zig+23-30
...@@ -9,17 +9,17 @@ const File = std.fs.File;...@@ -9,17 +9,17 @@ const File = std.fs.File;
9const fd_t = os.fd_t;9const fd_t = os.fd_t;
1010
11pub const Server = struct {11pub 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
14 loop: *Loop,14 loop: *Loop,
15 sockfd: ?i32,15 sockfd: ?i32,
16 accept_coro: ?promise,16 accept_coro: ?anyframe,
17 listen_address: std.net.Address,17 listen_address: std.net.Address,
1818
19 waiting_for_emfile_node: PromiseNode,19 waiting_for_emfile_node: PromiseNode,
20 listen_resume_node: event.Loop.ResumeNode,20 listen_resume_node: event.Loop.ResumeNode,
2121
22 const PromiseNode = std.TailQueue(promise).Node;22 const PromiseNode = std.TailQueue(anyframe).Node;
2323
24 pub fn init(loop: *Loop) Server {24 pub fn init(loop: *Loop) Server {
25 // TODO can't initialize handler coroutine here because we need well defined copy elision25 // TODO can't initialize handler coroutine here because we need well defined copy elision
...@@ -41,7 +41,7 @@ pub const Server = struct {...@@ -41,7 +41,7 @@ pub const Server = struct {
41 pub fn listen(41 pub fn listen(
42 self: *Server,42 self: *Server,
43 address: *const std.net.Address,43 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,
45 ) !void {45 ) !void {
46 self.handleRequestFn = handleRequestFn;46 self.handleRequestFn = handleRequestFn;
4747
...@@ -53,7 +53,7 @@ pub const Server = struct {...@@ -53,7 +53,7 @@ pub const Server = struct {
53 try os.listen(sockfd, os.SOMAXCONN);53 try os.listen(sockfd, os.SOMAXCONN);
54 self.listen_address = std.net.Address.initPosix(try os.getsockname(sockfd));54 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);
57 errdefer cancel self.accept_coro.?;57 errdefer cancel self.accept_coro.?;
5858
59 self.listen_resume_node.handle = self.accept_coro.?;59 self.listen_resume_node.handle = self.accept_coro.?;
...@@ -86,12 +86,7 @@ pub const Server = struct {...@@ -86,12 +86,7 @@ pub const Server = struct {
86 continue;86 continue;
87 }87 }
88 var socket = File.openHandle(accepted_fd);88 var socket = File.openHandle(accepted_fd);
89 _ = async<self.loop.allocator> self.handleRequestFn(self, &accepted_addr, socket) catch |err| switch (err) {89 self.handleRequestFn(self, &accepted_addr, socket);
90 error.OutOfMemory => {
91 socket.close();
92 continue;
93 },
94 };
95 } else |err| switch (err) {90 } else |err| switch (err) {
96 error.ProcessFdQuotaExceeded => @panic("TODO handle this error"),91 error.ProcessFdQuotaExceeded => @panic("TODO handle this error"),
97 error.ConnectionAborted => continue,92 error.ConnectionAborted => continue,
...@@ -124,7 +119,7 @@ pub async fn connectUnixSocket(loop: *Loop, path: []const u8) !i32 {...@@ -124,7 +119,7 @@ pub async fn connectUnixSocket(loop: *Loop, path: []const u8) !i32 {
124 mem.copy(u8, sock_addr.path[0..], path);119 mem.copy(u8, sock_addr.path[0..], path);
125 const size = @intCast(u32, @sizeOf(os.sa_family_t) + path.len);120 const size = @intCast(u32, @sizeOf(os.sa_family_t) + path.len);
126 try os.connect_async(sockfd, &sock_addr, size);121 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);
128 try os.getsockoptError(sockfd);123 try os.getsockoptError(sockfd);
129124
130 return sockfd;125 return sockfd;
...@@ -149,7 +144,7 @@ pub async fn read(loop: *std.event.Loop, fd: fd_t, buffer: []u8) ReadError!usize...@@ -149,7 +144,7 @@ pub async fn read(loop: *std.event.Loop, fd: fd_t, buffer: []u8) ReadError!usize
149 .iov_len = buffer.len,144 .iov_len = buffer.len,
150 };145 };
151 const iovs: *const [1]os.iovec = &iov;146 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);
153}148}
154149
155pub const WriteError = error{};150pub const WriteError = error{};
...@@ -160,7 +155,7 @@ pub async fn write(loop: *std.event.Loop, fd: fd_t, buffer: []const u8) WriteErr...@@ -160,7 +155,7 @@ pub async fn write(loop: *std.event.Loop, fd: fd_t, buffer: []const u8) WriteErr
160 .iov_len = buffer.len,155 .iov_len = buffer.len,
161 };156 };
162 const iovs: *const [1]os.iovec_const = &iov;157 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);
164}159}
165160
166pub async fn writevPosix(loop: *Loop, fd: i32, iov: [*]const os.iovec_const, count: usize) !void {161pub 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...@@ -174,7 +169,7 @@ pub async fn writevPosix(loop: *Loop, fd: i32, iov: [*]const os.iovec_const, cou
174 os.EINVAL => unreachable,169 os.EINVAL => unreachable,
175 os.EFAULT => unreachable,170 os.EFAULT => unreachable,
176 os.EAGAIN => {171 os.EAGAIN => {
177 try await (async loop.linuxWaitFd(fd, os.EPOLLET | os.EPOLLOUT) catch unreachable);172 try loop.linuxWaitFd(fd, os.EPOLLET | os.EPOLLOUT);
178 continue;173 continue;
179 },174 },
180 os.EBADF => unreachable, // always a race condition175 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:...@@ -205,7 +200,7 @@ pub async fn readvPosix(loop: *std.event.Loop, fd: i32, iov: [*]os.iovec, count:
205 os.EINVAL => unreachable,200 os.EINVAL => unreachable,
206 os.EFAULT => unreachable,201 os.EFAULT => unreachable,
207 os.EAGAIN => {202 os.EAGAIN => {
208 try await (async loop.linuxWaitFd(fd, os.EPOLLET | os.EPOLLIN) catch unreachable);203 try loop.linuxWaitFd(fd, os.EPOLLET | os.EPOLLIN);
209 continue;204 continue;
210 },205 },
211 os.EBADF => unreachable, // always a race condition206 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 {...@@ -232,7 +227,7 @@ pub async fn writev(loop: *Loop, fd: fd_t, data: []const []const u8) !void {
232 };227 };
233 }228 }
234229
235 return await (async writevPosix(loop, fd, iovecs.ptr, data.len) catch unreachable);230 return writevPosix(loop, fd, iovecs.ptr, data.len);
236}231}
237232
238pub async fn readv(loop: *Loop, fd: fd_t, data: []const []u8) !usize {233pub 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 {...@@ -246,7 +241,7 @@ pub async fn readv(loop: *Loop, fd: fd_t, data: []const []u8) !usize {
246 };241 };
247 }242 }
248243
249 return await (async readvPosix(loop, fd, iovecs.ptr, data.len) catch unreachable);244 return readvPosix(loop, fd, iovecs.ptr, data.len);
250}245}
251246
252pub async fn connect(loop: *Loop, _address: *const std.net.Address) !File {247pub 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 {...@@ -256,7 +251,7 @@ pub async fn connect(loop: *Loop, _address: *const std.net.Address) !File {
256 errdefer os.close(sockfd);251 errdefer os.close(sockfd);
257252
258 try os.connect_async(sockfd, &address.os_addr, @sizeOf(os.sockaddr_in));253 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);
260 try os.getsockoptError(sockfd);255 try os.getsockoptError(sockfd);
261256
262 return File.openHandle(sockfd);257 return File.openHandle(sockfd);
...@@ -275,17 +270,16 @@ test "listen on a port, send bytes, receive bytes" {...@@ -275,17 +270,16 @@ test "listen on a port, send bytes, receive bytes" {
275 tcp_server: Server,270 tcp_server: Server,
276271
277 const Self = @This();272 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 {
279 const self = @fieldParentPtr(Self, "tcp_server", tcp_server);274 const self = @fieldParentPtr(Self, "tcp_server", tcp_server);
280 var socket = _socket; // TODO https://github.com/ziglang/zig/issues/1592275 var socket = _socket; // TODO https://github.com/ziglang/zig/issues/1592
281 defer socket.close();276 defer socket.close();
282 // TODO guarantee elision of this allocation277 // TODO guarantee elision of this allocation
283 const next_handler = async errorableHandler(self, _addr, socket) catch unreachable;278 const next_handler = errorableHandler(self, _addr, socket) catch |err| {
284 (await next_handler) catch |err| {
285 std.debug.panic("unable to handle connection: {}\n", err);279 std.debug.panic("unable to handle connection: {}\n", err);
286 };280 };
287 suspend {281 suspend {
288 cancel @handle();282 cancel @frame();
289 }283 }
290 }284 }
291 async fn errorableHandler(self: *Self, _addr: *const std.net.Address, _socket: File) !void {285 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" {...@@ -306,15 +300,14 @@ test "listen on a port, send bytes, receive bytes" {
306 defer server.tcp_server.deinit();300 defer server.tcp_server.deinit();
307 try server.tcp_server.listen(&addr, MyServer.handler);301 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);303 _ = async doAsyncTest(&loop, &server.tcp_server.listen_address, &server.tcp_server);
310 defer cancel p;
311 loop.run();304 loop.run();
312}305}
313306
314async fn doAsyncTest(loop: *Loop, address: *const std.net.Address, server: *Server) void {307async fn doAsyncTest(loop: *Loop, address: *const std.net.Address, server: *Server) void {
315 errdefer @panic("test failure");308 errdefer @panic("test failure");
316309
317 var socket_file = try await try async connect(loop, address);310 var socket_file = try connect(loop, address);
318 defer socket_file.close();311 defer socket_file.close();
319312
320 var buf: [512]u8 = undefined;313 var buf: [512]u8 = undefined;
...@@ -340,9 +333,9 @@ pub const OutStream = struct {...@@ -340,9 +333,9 @@ pub const OutStream = struct {
340 };333 };
341 }334 }
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 {
344 const self = @fieldParentPtr(OutStream, "stream", out_stream);337 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);
346 }339 }
347};340};
348341
...@@ -362,8 +355,8 @@ pub const InStream = struct {...@@ -362,8 +355,8 @@ pub const InStream = struct {
362 };355 };
363 }356 }
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 {
366 const self = @fieldParentPtr(InStream, "stream", in_stream);359 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);
368 }361 }
369};362};
std/event/rwlock.zig+43-42
...@@ -3,8 +3,6 @@ const builtin = @import("builtin");...@@ -3,8 +3,6 @@ const builtin = @import("builtin");
3const assert = std.debug.assert;3const assert = std.debug.assert;
4const testing = std.testing;4const testing = std.testing;
5const mem = std.mem;5const mem = std.mem;
6const AtomicRmwOp = builtin.AtomicRmwOp;
7const AtomicOrder = builtin.AtomicOrder;
8const Loop = std.event.Loop;6const Loop = std.event.Loop;
97
10/// Thread-safe async/await lock.8/// Thread-safe async/await lock.
...@@ -28,19 +26,19 @@ pub const RwLock = struct {...@@ -28,19 +26,19 @@ pub const RwLock = struct {
28 const ReadLock = 2;26 const ReadLock = 2;
29 };27 };
3028
31 const Queue = std.atomic.Queue(promise);29 const Queue = std.atomic.Queue(anyframe);
3230
33 pub const HeldRead = struct {31 pub const HeldRead = struct {
34 lock: *RwLock,32 lock: *RwLock,
3533
36 pub fn release(self: HeldRead) void {34 pub fn release(self: HeldRead) void {
37 // If other readers still hold the lock, we're done.35 // 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) {
39 return;37 return;
40 }38 }
4139
42 _ = @atomicRmw(u8, &self.lock.reader_queue_empty_bit, AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst);40 _ = @atomicRmw(u8, &self.lock.reader_queue_empty_bit, .Xchg, 1, .SeqCst);
43 if (@cmpxchgStrong(u8, &self.lock.shared_state, State.ReadLock, State.Unlocked, AtomicOrder.SeqCst, AtomicOrder.SeqCst) != null) {41 if (@cmpxchgStrong(u8, &self.lock.shared_state, State.ReadLock, State.Unlocked, .SeqCst, .SeqCst) != null) {
44 // Didn't unlock. Someone else's problem.42 // Didn't unlock. Someone else's problem.
45 return;43 return;
46 }44 }
...@@ -61,17 +59,17 @@ pub const RwLock = struct {...@@ -61,17 +59,17 @@ pub const RwLock = struct {
61 }59 }
6260
63 // We need to release the write lock. Check if any readers are waiting to grab the lock.61 // 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) {
65 // Switch to a read lock.63 // 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);
67 while (self.lock.reader_queue.get()) |node| {65 while (self.lock.reader_queue.get()) |node| {
68 self.lock.loop.onNextTick(node);66 self.lock.loop.onNextTick(node);
69 }67 }
70 return;68 return;
71 }69 }
7270
73 _ = @atomicRmw(u8, &self.lock.writer_queue_empty_bit, AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst);71 _ = @atomicRmw(u8, &self.lock.writer_queue_empty_bit, .Xchg, 1, .SeqCst);
74 _ = @atomicRmw(u8, &self.lock.shared_state, AtomicRmwOp.Xchg, State.Unlocked, AtomicOrder.SeqCst);72 _ = @atomicRmw(u8, &self.lock.shared_state, .Xchg, State.Unlocked, .SeqCst);
7573
76 self.lock.commonPostUnlock();74 self.lock.commonPostUnlock();
77 }75 }
...@@ -93,17 +91,16 @@ pub const RwLock = struct {...@@ -93,17 +91,16 @@ pub const RwLock = struct {
93 /// All calls to acquire() and release() must complete before calling deinit().91 /// All calls to acquire() and release() must complete before calling deinit().
94 pub fn deinit(self: *RwLock) void {92 pub fn deinit(self: *RwLock) void {
95 assert(self.shared_state == State.Unlocked);93 assert(self.shared_state == State.Unlocked);
96 while (self.writer_queue.get()) |node| cancel node.data;94 while (self.writer_queue.get()) |node| resume node.data;
97 while (self.reader_queue.get()) |node| cancel node.data;95 while (self.reader_queue.get()) |node| resume node.data;
98 }96 }
9997
100 pub async fn acquireRead(self: *RwLock) HeldRead {98 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
103 suspend {101 suspend {
104 // TODO explicitly put this memory in the coroutine frame #1194
105 var my_tick_node = Loop.NextTickNode{102 var my_tick_node = Loop.NextTickNode{
106 .data = @handle(),103 .data = @frame(),
107 .prev = undefined,104 .prev = undefined,
108 .next = undefined,105 .next = undefined,
109 };106 };
...@@ -115,10 +112,10 @@ pub const RwLock = struct {...@@ -115,10 +112,10 @@ pub const RwLock = struct {
115112
116 // We set this bit so that later we can rely on the fact, that if reader_queue_empty_bit is 1,113 // We set this bit so that later we can rely on the fact, that if reader_queue_empty_bit is 1,
117 // some actor will attempt to grab the lock.114 // 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
120 // Here we don't care if we are the one to do the locking or if it was already locked for reading.117 // 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;
122 if (have_read_lock) {119 if (have_read_lock) {
123 // Give out all the read locks.120 // Give out all the read locks.
124 if (self.reader_queue.get()) |first_node| {121 if (self.reader_queue.get()) |first_node| {
...@@ -134,9 +131,8 @@ pub const RwLock = struct {...@@ -134,9 +131,8 @@ pub const RwLock = struct {
134131
135 pub async fn acquireWrite(self: *RwLock) HeldWrite {132 pub async fn acquireWrite(self: *RwLock) HeldWrite {
136 suspend {133 suspend {
137 // TODO explicitly put this memory in the coroutine frame #1194
138 var my_tick_node = Loop.NextTickNode{134 var my_tick_node = Loop.NextTickNode{
139 .data = @handle(),135 .data = @frame(),
140 .prev = undefined,136 .prev = undefined,
141 .next = undefined,137 .next = undefined,
142 };138 };
...@@ -148,10 +144,10 @@ pub const RwLock = struct {...@@ -148,10 +144,10 @@ pub const RwLock = struct {
148144
149 // We set this bit so that later we can rely on the fact, that if writer_queue_empty_bit is 1,145 // We set this bit so that later we can rely on the fact, that if writer_queue_empty_bit is 1,
150 // some actor will attempt to grab the lock.146 // 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
153 // Here we must be the one to acquire the write lock. It cannot already be locked.149 // 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) {
155 // We now have a write lock.151 // We now have a write lock.
156 if (self.writer_queue.get()) |node| {152 if (self.writer_queue.get()) |node| {
157 // Whether this node is us or someone else, we tail resume it.153 // Whether this node is us or someone else, we tail resume it.
...@@ -169,8 +165,8 @@ pub const RwLock = struct {...@@ -169,8 +165,8 @@ pub const RwLock = struct {
169 // obtain the lock.165 // obtain the lock.
170 // But if there's a writer_queue item or a reader_queue item,166 // But if there's a writer_queue item or a reader_queue item,
171 // we are the actor which must loop and attempt to grab the lock again.167 // 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) {168 if (@atomicLoad(u8, &self.writer_queue_empty_bit, .SeqCst) == 0) {
173 if (@cmpxchgStrong(u8, &self.shared_state, State.Unlocked, State.WriteLock, AtomicOrder.SeqCst, AtomicOrder.SeqCst) != null) {169 if (@cmpxchgStrong(u8, &self.shared_state, State.Unlocked, State.WriteLock, .SeqCst, .SeqCst) != null) {
174 // We did not obtain the lock. Great, the queues are someone else's problem.170 // We did not obtain the lock. Great, the queues are someone else's problem.
175 return;171 return;
176 }172 }
...@@ -180,13 +176,13 @@ pub const RwLock = struct {...@@ -180,13 +176,13 @@ pub const RwLock = struct {
180 return;176 return;
181 }177 }
182 // Release the lock again.178 // Release the lock again.
183 _ = @atomicRmw(u8, &self.writer_queue_empty_bit, AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst);179 _ = @atomicRmw(u8, &self.writer_queue_empty_bit, .Xchg, 1, .SeqCst);
184 _ = @atomicRmw(u8, &self.shared_state, AtomicRmwOp.Xchg, State.Unlocked, AtomicOrder.SeqCst);180 _ = @atomicRmw(u8, &self.shared_state, .Xchg, State.Unlocked, .SeqCst);
185 continue;181 continue;
186 }182 }
187183
188 if (@atomicLoad(u8, &self.reader_queue_empty_bit, AtomicOrder.SeqCst) == 0) {184 if (@atomicLoad(u8, &self.reader_queue_empty_bit, .SeqCst) == 0) {
189 if (@cmpxchgStrong(u8, &self.shared_state, State.Unlocked, State.ReadLock, AtomicOrder.SeqCst, AtomicOrder.SeqCst) != null) {185 if (@cmpxchgStrong(u8, &self.shared_state, State.Unlocked, State.ReadLock, .SeqCst, .SeqCst) != null) {
190 // We did not obtain the lock. Great, the queues are someone else's problem.186 // We did not obtain the lock. Great, the queues are someone else's problem.
191 return;187 return;
192 }188 }
...@@ -199,8 +195,8 @@ pub const RwLock = struct {...@@ -199,8 +195,8 @@ pub const RwLock = struct {
199 return;195 return;
200 }196 }
201 // Release the lock again.197 // Release the lock again.
202 _ = @atomicRmw(u8, &self.reader_queue_empty_bit, AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst);198 _ = @atomicRmw(u8, &self.reader_queue_empty_bit, .Xchg, 1, .SeqCst);
203 if (@cmpxchgStrong(u8, &self.shared_state, State.ReadLock, State.Unlocked, AtomicOrder.SeqCst, AtomicOrder.SeqCst) != null) {199 if (@cmpxchgStrong(u8, &self.shared_state, State.ReadLock, State.Unlocked, .SeqCst, .SeqCst) != null) {
204 // Didn't unlock. Someone else's problem.200 // Didn't unlock. Someone else's problem.
205 return;201 return;
206 }202 }
...@@ -215,6 +211,9 @@ test "std.event.RwLock" {...@@ -215,6 +211,9 @@ test "std.event.RwLock" {
215 // https://github.com/ziglang/zig/issues/2377211 // https://github.com/ziglang/zig/issues/2377
216 if (true) return error.SkipZigTest;212 if (true) return error.SkipZigTest;
217213
214 // https://github.com/ziglang/zig/issues/1908
215 if (builtin.single_threaded) return error.SkipZigTest;
216
218 const allocator = std.heap.direct_allocator;217 const allocator = std.heap.direct_allocator;
219218
220 var loop: Loop = undefined;219 var loop: Loop = undefined;
...@@ -224,8 +223,7 @@ test "std.event.RwLock" {...@@ -224,8 +223,7 @@ test "std.event.RwLock" {
224 var lock = RwLock.init(&loop);223 var lock = RwLock.init(&loop);
225 defer lock.deinit();224 defer lock.deinit();
226225
227 const handle = try async<allocator> testLock(&loop, &lock);226 const handle = testLock(&loop, &lock);
228 defer cancel handle;
229 loop.run();227 loop.run();
230228
231 const expected_result = [1]i32{shared_it_count * @intCast(i32, shared_test_data.len)} ** shared_test_data.len;229 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" {...@@ -233,28 +231,31 @@ test "std.event.RwLock" {
233}231}
234232
235async fn testLock(loop: *Loop, lock: *RwLock) void {233async 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
241 var read_nodes: [100]Loop.NextTickNode = undefined;234 var read_nodes: [100]Loop.NextTickNode = undefined;
242 for (read_nodes) |*read_node| {235 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);
244 loop.onNextTick(read_node);239 loop.onNextTick(read_node);
245 }240 }
246241
247 var write_nodes: [shared_it_count]Loop.NextTickNode = undefined;242 var write_nodes: [shared_it_count]Loop.NextTickNode = undefined;
248 for (write_nodes) |*write_node| {243 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);
250 loop.onNextTick(write_node);247 loop.onNextTick(write_node);
251 }248 }
252249
253 for (write_nodes) |*write_node| {250 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);
255 }254 }
256 for (read_nodes) |*read_node| {255 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);
258 }259 }
259}260}
260261
...@@ -269,7 +270,7 @@ async fn writeRunner(lock: *RwLock) void {...@@ -269,7 +270,7 @@ async fn writeRunner(lock: *RwLock) void {
269 var i: usize = 0;270 var i: usize = 0;
270 while (i < shared_test_data.len) : (i += 1) {271 while (i < shared_test_data.len) : (i += 1) {
271 std.time.sleep(100 * std.time.microsecond);272 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();
273 const handle = await lock_promise;274 const handle = await lock_promise;
274 defer handle.release();275 defer handle.release();
275276
...@@ -287,7 +288,7 @@ async fn readRunner(lock: *RwLock) void {...@@ -287,7 +288,7 @@ async fn readRunner(lock: *RwLock) void {
287288
288 var i: usize = 0;289 var i: usize = 0;
289 while (i < shared_test_data.len) : (i += 1) {290 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();
291 const handle = await lock_promise;292 const handle = await lock_promise;
292 defer handle.release();293 defer handle.release();
293294
std/zig/parser_test.zig+1-1
...@@ -1183,7 +1183,7 @@ test "zig fmt: resume from suspend block" {...@@ -1183,7 +1183,7 @@ test "zig fmt: resume from suspend block" {
1183 try testCanonical(1183 try testCanonical(
1184 \\fn foo() void {1184 \\fn foo() void {
1185 \\ suspend {1185 \\ suspend {
1186 \\ resume @handle();1186 \\ resume @frame();
1187 \\ }1187 \\ }
1188 \\}1188 \\}
1189 \\1189 \\
test/compile_errors.zig+9-26
...@@ -1403,24 +1403,14 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {...@@ -1403,24 +1403,14 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
1403 );1403 );
14041404
1405 cases.add(1405 cases.add(
1406 "@handle() called outside of function definition",1406 "@frame() called outside of function definition",
1407 \\var handle_undef: promise = undefined;1407 \\var handle_undef: anyframe = undefined;
1408 \\var handle_dummy: promise = @handle();1408 \\var handle_dummy: anyframe = @frame();
1409 \\export fn entry() bool {1409 \\export fn entry() bool {
1410 \\ return handle_undef == handle_dummy;1410 \\ return handle_undef == handle_dummy;
1411 \\}1411 \\}
1412 ,1412 ,
1413 "tmp.zig:2:29: error: @handle() called outside of function definition",1413 "tmp.zig:2:30: error: @frame() 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",
1424 );1414 );
14251415
1426 cases.add(1416 cases.add(
...@@ -1796,15 +1786,9 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {...@@ -1796,15 +1786,9 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
17961786
1797 cases.add(1787 cases.add(
1798 "suspend inside suspend block",1788 "suspend inside suspend block",
1799 \\const std = @import("std",);
1800 \\
1801 \\export fn entry() void {1789 \\export fn entry() void {
1802 \\ var buf: [500]u8 = undefined;1790 \\ _ = async foo();
1803 \\ var a = &std.heap.FixedBufferAllocator.init(buf[0..]).allocator;
1804 \\ const p = (async<a> foo()) catch unreachable;
1805 \\ cancel p;
1806 \\}1791 \\}
1807 \\
1808 \\async fn foo() void {1792 \\async fn foo() void {
1809 \\ suspend {1793 \\ suspend {
1810 \\ suspend {1794 \\ suspend {
...@@ -1812,8 +1796,8 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {...@@ -1812,8 +1796,8 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
1812 \\ }1796 \\ }
1813 \\}1797 \\}
1814 ,1798 ,
1815 "tmp.zig:12:9: error: cannot suspend inside suspend block",1799 "tmp.zig:6:9: error: cannot suspend inside suspend block",
1816 "tmp.zig:11:5: note: other suspend block here",1800 "tmp.zig:5:5: note: other suspend block here",
1817 );1801 );
18181802
1819 cases.add(1803 cases.add(
...@@ -1854,15 +1838,14 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {...@@ -1854,15 +1838,14 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
18541838
1855 cases.add(1839 cases.add(
1856 "returning error from void async function",1840 "returning error from void async function",
1857 \\const std = @import("std",);
1858 \\export fn entry() void {1841 \\export fn entry() void {
1859 \\ const p = async<std.debug.global_allocator> amain() catch unreachable;1842 \\ _ = async amain();
1860 \\}1843 \\}
1861 \\async fn amain() void {1844 \\async fn amain() void {
1862 \\ return error.ShouldBeCompileError;1845 \\ return error.ShouldBeCompileError;
1863 \\}1846 \\}
1864 ,1847 ,
1865 "tmp.zig:6:17: error: expected type 'void', found 'error{ShouldBeCompileError}'",1848 "tmp.zig:5:17: error: expected type 'void', found 'error{ShouldBeCompileError}'",
1866 );1849 );
18671850
1868 cases.add(1851 cases.add(