authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-06-11 17:34:45-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-06-11 17:34:45-04:00
log5252566137c621c19225e692c82264096ff9f5fe
tree192a8c3f2cbe8f1cf2ce8f45e110b9965a526fb6
parent03c16c6c548a8f8246c3dcff540482b7612aab80

langref: add coroutines documentation

See #367

1 files changed, 272 insertions(+), 1 deletions(-)

doc/langref.html.in+272-1
......@@ -3908,6 +3908,277 @@ pub fn main() void {
39083908 <p>TODO: @fence()</p>
39093909 <p>TODO: @atomic rmw</p>
39103910 <p>TODO: builtin atomic memory ordering enum</p>
3911 {#header_close#}
3912 {#header_open|Coroutines#}
3913 <p>
3914 A coroutine is a generalization of a function.
3915 </p>
3916 <p>
3917 When you call a function, it creates a stack frame,
3918 and then the function runs until it reaches a return
3919 statement, and then the stack frame is destroyed.
3920 At the callsite, the next line of code does not run
3921 until the function returns.
3922 </p>
3923 <p>
3924 A coroutine is like a function, but it can be suspended
3925 and resumed any number of times, and then it must be
3926 explicitly destroyed. When a coroutine suspends, it
3927 returns to the resumer.
3928 </p>
3929 {#header_open|Minimal Coroutine Example#}
3930 <p>
3931 Declare a coroutine with the <code>async</code> keyword.
3932 The expression in angle brackets must evaluate to a struct
3933 which has these fields:
3934 </p>
3935 <ul>
3936 <li><code>allocFn: fn (self: *Allocator, byte_count: usize, alignment: u29) Error![]u8</code> - where <code>Error</code> can be any error set.</li>
3937 <li><code>freeFn: fn (self: *Allocator, old_mem: []u8) void</code></li>
3938 </ul>
3939 <p>
3940 You may notice that this corresponds to the <code>std.mem.Allocator</code> interface.
3941 This makes it convenient to integrate with existing allocators. Note, however,
3942 that the language feature does not depend on the standard library, and any struct which
3943 has these fields is allowed.
3944 </p>
3945 <p>
3946 Omitting the angle bracket expression when defining an async function makes
3947 the function generic. Zig will infer the allocator type when the async function is called.
3948 </p>
3949 <p>
3950 Call a coroutine with the <code>async</code> keyword. Here, the expression in angle brackets
3951 is a pointer to the allocator struct that the coroutine expects.
3952 </p>
3953 <p>
3954 The result of an async function call is a <code>promise->T</code> type, where <code>T</code>
3955 is the return type of the async function. Once a promise has been created, it must be
3956 consumed, either with <code>cancel</code> or <code>await</code>:
3957 </p>
3958 <p>
3959 Async functions start executing when created, so in the following example, the entire
3960 async function completes before it is canceled:
3961 </p>
3962 {#code_begin|test#}
3963const std = @import("std");
3964const assert = std.debug.assert;
3965
3966var x: i32 = 1;
3967
3968test "create a coroutine and cancel it" {
3969 const p = try async<std.debug.global_allocator> simpleAsyncFn();
3970 comptime assert(@typeOf(p) == promise->void);
3971 cancel p;
3972 assert(x == 2);
3973}
3974async<*std.mem.Allocator> fn simpleAsyncFn() void {
3975 x += 1;
3976}
3977 {#code_end#}
3978 {#header_close#}
3979 {#header_open|Suspend and Resume#}
3980 <p>
3981 At any point, an async function may suspend itself. This causes control flow to
3982 return to the caller or resumer. The following code demonstrates where control flow
3983 goes:
3984 </p>
3985 {#code_begin|test#}
3986const std = @import("std");
3987const assert = std.debug.assert;
3988
3989test "coroutine suspend, resume, cancel" {
3990 seq('a');
3991 const p = try async<std.debug.global_allocator> testAsyncSeq();
3992 seq('c');
3993 resume p;
3994 seq('f');
3995 cancel p;
3996 seq('g');
3997
3998 assert(std.mem.eql(u8, points, "abcdefg"));
3999}
4000async fn testAsyncSeq() void {
4001 defer seq('e');
4002
4003 seq('b');
4004 suspend;
4005 seq('d');
4006}
4007var points = []u8{0} ** "abcdefg".len;
4008var index: usize = 0;
4009
4010fn seq(c: u8) void {
4011 points[index] = c;
4012 index += 1;
4013}
4014 {#code_end#}
4015 <p>
4016 When an async function suspends itself, it must be sure that it will be
4017 resumed or canceled somehow, for example by registering its promise handle
4018 in an event loop. Use a suspend capture block to gain access to the
4019 promise:
4020 </p>
4021 {#code_begin|test#}
4022const std = @import("std");
4023const assert = std.debug.assert;
4024
4025test "coroutine suspend with block" {
4026 const p = try async<std.debug.global_allocator> testSuspendBlock();
4027 std.debug.assert(!result);
4028 resume a_promise;
4029 std.debug.assert(result);
4030 cancel p;
4031}
4032
4033var a_promise: promise = undefined;
4034var result = false;
4035async fn testSuspendBlock() void {
4036 suspend |p| {
4037 comptime assert(@typeOf(p) == promise->void);
4038 a_promise = p;
4039 }
4040 result = true;
4041}
4042 {#code_end#}
4043 <p>
4044 Every suspend point in an async function represents a point at which the coroutine
4045 could be destroyed. If that happens, <code>defer</code> expressions that are in
4046 scope are run, as well as <code>errdefer</code> expressions.
4047 </p>
4048 <p>
4049 {#link|Await#} counts as a suspend point.
4050 </p>
4051 {#header_open|Breaking from Suspend Blocks#}
4052 <p>
4053 Suspend blocks support labeled break, just like {#link|while#} and {#link|for#}.
4054 </p>
4055 <p>
4056 Upon entering a <code>suspend</code> block, the coroutine is already considered
4057 suspended, and can be resumed. For example, if you started another kernel thread,
4058 and had that thread call <code>resume</code> on the promise handle provided by the
4059 <code>suspend</code> block, the new thread would begin executing after the suspend
4060 block, while the old thread continued executing the suspend block.
4061 </p>
4062 <p>
4063 However, if you use labeled <code>break</code> on the suspend block, the coroutine
4064 never returns to its resumer and continues executing.
4065 </p>
4066 {#code_begin|test#}
4067const std = @import("std");
4068const assert = std.debug.assert;
4069
4070test "break from suspend" {
4071 var buf: [500]u8 = undefined;
4072 var a = &std.heap.FixedBufferAllocator.init(buf[0..]).allocator;
4073 var my_result: i32 = 1;
4074 const p = try async<a> testBreakFromSuspend(&my_result);
4075 cancel p;
4076 std.debug.assert(my_result == 2);
4077}
4078async fn testBreakFromSuspend(my_result: *i32) void {
4079 s: suspend |p| {
4080 break :s;
4081 }
4082 my_result.* += 1;
4083 suspend;
4084 my_result.* += 1;
4085}
4086 {#code_end#}
4087 {#header_close#}
4088 {#header_close#}
4089 {#header_open|Await#}
4090 <p>
4091 The <code>await</code> keyword is used to coordinate with an async function's
4092 <code>return</code> statement.
4093 </p>
4094 <p>
4095 <code>await</code> is valid only in an <code>async</code> function, and it takes
4096 as an operand a promise handle.
4097 If the async function associated with the promise handle has already returned,
4098 then <code>await</code> destroys the target async function, and gives the return value.
4099 Otherwise, <code>await</code> suspends the current async function, registering its
4100 promise handle with the target coroutine. It becomes the target coroutine's responsibility
4101 to have ensured that it will be resumed or destroyed. When the target coroutine reaches
4102 its return statement, it gives the return value to the awaiter, destroys itself, and then
4103 resumes the awaiter.
4104 </p>
4105 <p>
4106 A promise handle must be consumed exactly once after it is created, either by <code>cancel</code> or <code>await</code>.
4107 </p>
4108 <p>
4109 <code>await</code> counts as a suspend point, and therefore at every <code>await</code>,
4110 a coroutine can be potentially destroyed, which would run <code>defer</code> and <code>errdefer</code> expressions.
4111 </p>
4112 {#code_begin|test#}
4113const std = @import("std");
4114const assert = std.debug.assert;
4115
4116var a_promise: promise = undefined;
4117var final_result: i32 = 0;
4118
4119test "coroutine await" {
4120 seq('a');
4121 const p = async<std.debug.global_allocator> amain() catch unreachable;
4122 seq('f');
4123 resume a_promise;
4124 seq('i');
4125 assert(final_result == 1234);
4126 assert(std.mem.eql(u8, seq_points, "abcdefghi"));
4127}
4128async fn amain() void {
4129 seq('b');
4130 const p = async another() catch unreachable;
4131 seq('e');
4132 final_result = await p;
4133 seq('h');
4134}
4135async fn another() i32 {
4136 seq('c');
4137 suspend |p| {
4138 seq('d');
4139 a_promise = p;
4140 }
4141 seq('g');
4142 return 1234;
4143}
4144
4145var seq_points = []u8{0} ** "abcdefghi".len;
4146var seq_index: usize = 0;
4147
4148fn seq(c: u8) void {
4149 seq_points[seq_index] = c;
4150 seq_index += 1;
4151}
4152 {#code_end#}
4153 <p>
4154 In general, <code>suspend</code> is lower level than <code>await</code>. Most application
4155 code will use only <code>async</code> and <code>await</code>, but event loop
4156 implementations will make use of <code>suspend</code> internally.
4157 </p>
4158 {#header_close#}
4159 {#header_open|Open Issues#}
4160 <p>
4161 There are a few issues with coroutines that are considered unresolved. Best be aware of them,
4162 as the situation is likely to change before 1.0.0:
4163 </p>
4164 <ul>
4165 <li>Async functions have optimizations disabled - even in release modes - due to an
4166 <a href="https://github.com/ziglang/zig/issues/802">LLVM bug</a>.
4167 </li>
4168 <li>
4169 There are some situations where we can know statically that there will not be
4170 memory allocation failure, but Zig still forces us to handle it.
4171 TODO file an issue for this and link it here.
4172 </li>
4173 <li>
4174 Zig does not take advantage of LLVM's allocation elision optimization for
4175 coroutines. It crashed LLVM when I tried to do it the first time. This is
4176 related to the other 2 bullet points here. See
4177 <a href="https://github.com/ziglang/zig/issues/802">#802</a>.
4178 </li>
4179 </ul>
4180 {#header_close#}
4181
39114182 {#header_close#}
39124183 {#header_open|Builtin Functions#}
39134184 <p>
......@@ -6124,7 +6395,7 @@ hljs.registerLanguage("zig", function(t) {
61246395 },
61256396 a = t.IR + "\\s*\\(",
61266397 c = {
6127 keyword: "const align var extern stdcallcc nakedcc volatile export pub noalias inline struct packed enum union break return try catch test continue unreachable comptime and or asm defer errdefer if else switch while for fn use bool f32 f64 void type noreturn error i8 u8 i16 u16 i32 u32 i64 u64 isize usize i8w u8w i16w i32w u32w i64w u64w isizew usizew c_short c_ushort c_int c_uint c_long c_ulong c_longlong c_ulonglong",
6398 keyword: "const align var extern stdcallcc nakedcc volatile export pub noalias inline struct packed enum union break return try catch test continue unreachable comptime and or asm defer errdefer if else switch while for fn use bool f32 f64 void type noreturn error i8 u8 i16 u16 i32 u32 i64 u64 isize usize i8w u8w i16w i32w u32w i64w u64w isizew usizew c_short c_ushort c_int c_uint c_long c_ulong c_longlong c_ulonglong resume cancel await async",
61286399 built_in: "atomicLoad breakpoint returnAddress frameAddress fieldParentPtr setFloatMode IntType OpaqueType compileError compileLog setCold setRuntimeSafety setEvalBranchQuota offsetOf memcpy inlineCall setGlobalLinkage setGlobalSection divTrunc divFloor enumTagName intToPtr ptrToInt panic ptrCast bitCast rem mod memset sizeOf alignOf alignCast maxValue minValue memberCount memberName memberType typeOf addWithOverflow subWithOverflow mulWithOverflow shlWithOverflow shlExact shrExact cInclude cDefine cUndef ctz clz import cImport errorName embedFile cmpxchgStrong cmpxchgWeak fence divExact truncate atomicRmw sqrt field typeInfo typeName newStackCall",
61296400 literal: "true false null undefined"
61306401 },