mirror of
https://github.com/ziglang/zig.git
synced 2025-12-06 14:23:09 +00:00
langref: eliminate dependencies on stage1
This commit removes async/await/suspend/resume from the language reference, as that feature does not yet work in the self-hosted compiler. We will be regressing this feature temporarily. Users of these language features should stick with 0.10.x with the `-fstage1` flag until they are restored. See tracking issue #6025.
This commit is contained in:
parent
2823fcabd1
commit
6e52f36d46
@ -1179,33 +1179,8 @@ test "this will be skipped" {
|
|||||||
return error.SkipZigTest;
|
return error.SkipZigTest;
|
||||||
}
|
}
|
||||||
{#code_end#}
|
{#code_end#}
|
||||||
<p>
|
|
||||||
The default test runner skips tests containing a {#link|suspend point|Async Functions#} while the
|
|
||||||
test is running using the default, blocking IO mode.
|
|
||||||
(The evented IO mode is enabled using the <kbd>--test-evented-io</kbd> command line parameter.)
|
|
||||||
</p>
|
|
||||||
{#code_begin|test|async_skip#}
|
|
||||||
{#backend_stage1#}
|
|
||||||
const std = @import("std");
|
|
||||||
|
|
||||||
test "async skip test" {
|
|
||||||
var frame = async func();
|
|
||||||
const result = await frame;
|
|
||||||
try std.testing.expect(result == 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn func() i32 {
|
|
||||||
suspend {
|
|
||||||
resume @frame();
|
|
||||||
}
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
{#code_end#}
|
|
||||||
<p>
|
|
||||||
In the code sample above, the test would not be skipped in blocking IO mode if the {#syntax#}nosuspend{#endsyntax#}
|
|
||||||
keyword was used (see {#link|Async and Await#}).
|
|
||||||
</p>
|
|
||||||
{#header_close#}
|
{#header_close#}
|
||||||
|
|
||||||
{#header_open|Report Memory Leaks#}
|
{#header_open|Report Memory Leaks#}
|
||||||
<p>
|
<p>
|
||||||
When code allocates {#link|Memory#} using the {#link|Zig Standard Library#}'s testing allocator,
|
When code allocates {#link|Memory#} using the {#link|Zig Standard Library#}'s testing allocator,
|
||||||
@ -6288,7 +6263,6 @@ test "float widening" {
|
|||||||
<li>Cast {#syntax#}5{#endsyntax#} to {#syntax#}comptime_float{#endsyntax#} resulting in {#syntax#}@as(comptime_float, 10.8){#endsyntax#}, which is casted to {#syntax#}@as(f32, 10.8){#endsyntax#}</li>
|
<li>Cast {#syntax#}5{#endsyntax#} to {#syntax#}comptime_float{#endsyntax#} resulting in {#syntax#}@as(comptime_float, 10.8){#endsyntax#}, which is casted to {#syntax#}@as(f32, 10.8){#endsyntax#}</li>
|
||||||
</ul>
|
</ul>
|
||||||
{#code_begin|test_err#}
|
{#code_begin|test_err#}
|
||||||
{#backend_stage1#}
|
|
||||||
// Compile time coercion of float to int
|
// Compile time coercion of float to int
|
||||||
test "implicit cast to comptime_int" {
|
test "implicit cast to comptime_int" {
|
||||||
var f: f32 = 54.0 / 5;
|
var f: f32 = 54.0 / 5;
|
||||||
@ -7400,7 +7374,6 @@ pub fn main() void {
|
|||||||
</p>
|
</p>
|
||||||
{#code_begin|exe#}
|
{#code_begin|exe#}
|
||||||
{#target_linux_x86_64#}
|
{#target_linux_x86_64#}
|
||||||
{#backend_stage1#}
|
|
||||||
pub fn main() noreturn {
|
pub fn main() noreturn {
|
||||||
const msg = "hello world\n";
|
const msg = "hello world\n";
|
||||||
_ = syscall3(SYS_write, STDOUT_FILENO, @ptrToInt(msg), msg.len);
|
_ = syscall3(SYS_write, STDOUT_FILENO, @ptrToInt(msg), msg.len);
|
||||||
@ -7588,388 +7561,15 @@ test "global assembly" {
|
|||||||
<p>TODO: @atomic rmw</p>
|
<p>TODO: @atomic rmw</p>
|
||||||
<p>TODO: builtin atomic memory ordering enum</p>
|
<p>TODO: builtin atomic memory ordering enum</p>
|
||||||
{#header_close#}
|
{#header_close#}
|
||||||
|
|
||||||
{#header_open|Async Functions#}
|
{#header_open|Async Functions#}
|
||||||
<p>
|
<p>Async functions are being temporarily regressed and will be
|
||||||
When a function is called, a frame is pushed to the stack,
|
<a href="https://github.com/ziglang/zig/issues/6025">restored before Zig
|
||||||
the function runs until it reaches a return statement, and then the frame is popped from the stack.
|
0.11.0 is tagged</a>. I apologize for the instability. Please use Zig 0.10.0 with
|
||||||
The code following the callsite does not run until the function returns.
|
the <code>-fstage1</code> flag for now if you need this feature.</p>
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
An async function is a function whose execution is split into an {#syntax#}async{#endsyntax#} initiation,
|
|
||||||
followed by an {#syntax#}await{#endsyntax#} completion. Its frame is
|
|
||||||
provided explicitly by the caller, and it can be suspended and resumed any number of times.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
The code following the {#syntax#}async{#endsyntax#} callsite runs immediately after the async
|
|
||||||
function first suspends. When the return value of the async function is needed,
|
|
||||||
the calling code can {#syntax#}await{#endsyntax#} on the async function frame.
|
|
||||||
This will suspend the calling code until the async function completes, at which point
|
|
||||||
execution resumes just after the {#syntax#}await{#endsyntax#} callsite.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
Zig infers that a function is {#syntax#}async{#endsyntax#} when it observes that the function contains
|
|
||||||
a <strong>suspension point</strong>. Async functions can be called the same as normal functions. A
|
|
||||||
function call of an async function is a suspend point.
|
|
||||||
</p>
|
|
||||||
{#header_open|Suspend and Resume#}
|
|
||||||
<p>
|
|
||||||
At any point, a function may suspend itself. This causes control flow to
|
|
||||||
return to the callsite (in the case of the first suspension),
|
|
||||||
or resumer (in the case of subsequent suspensions).
|
|
||||||
</p>
|
|
||||||
{#code_begin|test|suspend_no_resume#}
|
|
||||||
{#backend_stage1#}
|
|
||||||
const std = @import("std");
|
|
||||||
const expect = std.testing.expect;
|
|
||||||
|
|
||||||
var x: i32 = 1;
|
|
||||||
|
|
||||||
test "suspend with no resume" {
|
|
||||||
var frame = async func();
|
|
||||||
try expect(x == 2);
|
|
||||||
_ = frame;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn func() void {
|
|
||||||
x += 1;
|
|
||||||
suspend {}
|
|
||||||
// This line is never reached because the suspend has no matching resume.
|
|
||||||
x += 1;
|
|
||||||
}
|
|
||||||
{#code_end#}
|
|
||||||
<p>
|
|
||||||
In the same way that each allocation should have a corresponding free,
|
|
||||||
Each {#syntax#}suspend{#endsyntax#} should have a corresponding {#syntax#}resume{#endsyntax#}.
|
|
||||||
A <strong>suspend block</strong> allows a function to put a pointer to its own
|
|
||||||
frame somewhere, for example into an event loop, even if that action will perform a
|
|
||||||
{#syntax#}resume{#endsyntax#} operation on a different thread.
|
|
||||||
{#link|@frame#} provides access to the async function frame pointer.
|
|
||||||
</p>
|
|
||||||
{#code_begin|test|async_suspend_block#}
|
|
||||||
{#backend_stage1#}
|
|
||||||
const std = @import("std");
|
|
||||||
const expect = std.testing.expect;
|
|
||||||
|
|
||||||
var the_frame: anyframe = undefined;
|
|
||||||
var result = false;
|
|
||||||
|
|
||||||
test "async function suspend with block" {
|
|
||||||
_ = async testSuspendBlock();
|
|
||||||
try expect(!result);
|
|
||||||
resume the_frame;
|
|
||||||
try expect(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn testSuspendBlock() void {
|
|
||||||
suspend {
|
|
||||||
comptime try expect(@TypeOf(@frame()) == *@Frame(testSuspendBlock));
|
|
||||||
the_frame = @frame();
|
|
||||||
}
|
|
||||||
result = true;
|
|
||||||
}
|
|
||||||
{#code_end#}
|
|
||||||
<p>
|
|
||||||
{#syntax#}suspend{#endsyntax#} causes a function to be {#syntax#}async{#endsyntax#}.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
{#header_open|Resuming from Suspend Blocks#}
|
|
||||||
<p>
|
|
||||||
Upon entering a {#syntax#}suspend{#endsyntax#} block, the async function is already considered
|
|
||||||
suspended, and can be resumed. For example, if you started another kernel thread,
|
|
||||||
and had that thread call {#syntax#}resume{#endsyntax#} on the frame pointer provided by the
|
|
||||||
{#link|@frame#}, the new thread would begin executing after the suspend
|
|
||||||
block, while the old thread continued executing the suspend block.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
However, the async function can be directly resumed from the suspend block, in which case it
|
|
||||||
never returns to its resumer and continues executing.
|
|
||||||
</p>
|
|
||||||
{#code_begin|test|resume_from_suspend#}
|
|
||||||
{#backend_stage1#}
|
|
||||||
const std = @import("std");
|
|
||||||
const expect = std.testing.expect;
|
|
||||||
|
|
||||||
test "resume from suspend" {
|
|
||||||
var my_result: i32 = 1;
|
|
||||||
_ = async testResumeFromSuspend(&my_result);
|
|
||||||
try std.testing.expect(my_result == 2);
|
|
||||||
}
|
|
||||||
fn testResumeFromSuspend(my_result: *i32) void {
|
|
||||||
suspend {
|
|
||||||
resume @frame();
|
|
||||||
}
|
|
||||||
my_result.* += 1;
|
|
||||||
suspend {}
|
|
||||||
my_result.* += 1;
|
|
||||||
}
|
|
||||||
{#code_end#}
|
|
||||||
<p>
|
|
||||||
This is guaranteed to tail call, and therefore will not cause a new stack frame.
|
|
||||||
</p>
|
|
||||||
{#header_close#}
|
|
||||||
{#header_close#}
|
{#header_close#}
|
||||||
|
|
||||||
{#header_open|Async and Await#}
|
{#header_open|Builtin Functions|2col#}
|
||||||
<p>
|
|
||||||
In the same way that every {#syntax#}suspend{#endsyntax#} has a matching
|
|
||||||
{#syntax#}resume{#endsyntax#}, every {#syntax#}async{#endsyntax#} has a matching {#syntax#}await{#endsyntax#}
|
|
||||||
in standard code.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
However, it is possible to have an {#syntax#}async{#endsyntax#} call
|
|
||||||
without a matching {#syntax#}await{#endsyntax#}. Upon completion of the async function,
|
|
||||||
execution would continue at the most recent {#syntax#}async{#endsyntax#} callsite or {#syntax#}resume{#endsyntax#} callsite,
|
|
||||||
and the return value of the async function would be lost.
|
|
||||||
</p>
|
|
||||||
{#code_begin|test|async_await#}
|
|
||||||
{#backend_stage1#}
|
|
||||||
const std = @import("std");
|
|
||||||
const expect = std.testing.expect;
|
|
||||||
|
|
||||||
test "async and await" {
|
|
||||||
// The test block is not async and so cannot have a suspend
|
|
||||||
// point in it. By using the nosuspend keyword, we promise that
|
|
||||||
// the code in amain will finish executing without suspending
|
|
||||||
// back to the test block.
|
|
||||||
nosuspend amain();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn amain() void {
|
|
||||||
var frame = async func();
|
|
||||||
comptime try expect(@TypeOf(frame) == @Frame(func));
|
|
||||||
|
|
||||||
const ptr: anyframe->void = &frame;
|
|
||||||
const any_ptr: anyframe = ptr;
|
|
||||||
|
|
||||||
resume any_ptr;
|
|
||||||
await ptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn func() void {
|
|
||||||
suspend {}
|
|
||||||
}
|
|
||||||
{#code_end#}
|
|
||||||
<p>
|
|
||||||
The {#syntax#}await{#endsyntax#} keyword is used to coordinate with an async function's
|
|
||||||
{#syntax#}return{#endsyntax#} statement.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
{#syntax#}await{#endsyntax#} is a suspend point, and takes as an operand anything that
|
|
||||||
coerces to {#syntax#}anyframe->T{#endsyntax#}. Calling {#syntax#}await{#endsyntax#} on
|
|
||||||
the frame of an async function will cause execution to continue at the
|
|
||||||
{#syntax#}await{#endsyntax#} callsite once the target function completes.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
There is a common misconception that {#syntax#}await{#endsyntax#} resumes the target function.
|
|
||||||
It is the other way around: it suspends until the target function completes.
|
|
||||||
In the event that the target function has already completed, {#syntax#}await{#endsyntax#}
|
|
||||||
does not suspend; instead it copies the
|
|
||||||
return value directly from the target function's frame.
|
|
||||||
</p>
|
|
||||||
{#code_begin|test|async_await_sequence#}
|
|
||||||
{#backend_stage1#}
|
|
||||||
const std = @import("std");
|
|
||||||
const expect = std.testing.expect;
|
|
||||||
|
|
||||||
var the_frame: anyframe = undefined;
|
|
||||||
var final_result: i32 = 0;
|
|
||||||
|
|
||||||
test "async function await" {
|
|
||||||
seq('a');
|
|
||||||
_ = async amain();
|
|
||||||
seq('f');
|
|
||||||
resume the_frame;
|
|
||||||
seq('i');
|
|
||||||
try expect(final_result == 1234);
|
|
||||||
try expect(std.mem.eql(u8, &seq_points, "abcdefghi"));
|
|
||||||
}
|
|
||||||
fn amain() void {
|
|
||||||
seq('b');
|
|
||||||
var f = async another();
|
|
||||||
seq('e');
|
|
||||||
final_result = await f;
|
|
||||||
seq('h');
|
|
||||||
}
|
|
||||||
fn another() i32 {
|
|
||||||
seq('c');
|
|
||||||
suspend {
|
|
||||||
seq('d');
|
|
||||||
the_frame = @frame();
|
|
||||||
}
|
|
||||||
seq('g');
|
|
||||||
return 1234;
|
|
||||||
}
|
|
||||||
|
|
||||||
var seq_points = [_]u8{0} ** "abcdefghi".len;
|
|
||||||
var seq_index: usize = 0;
|
|
||||||
|
|
||||||
fn seq(c: u8) void {
|
|
||||||
seq_points[seq_index] = c;
|
|
||||||
seq_index += 1;
|
|
||||||
}
|
|
||||||
{#code_end#}
|
|
||||||
<p>
|
|
||||||
In general, {#syntax#}suspend{#endsyntax#} is lower level than {#syntax#}await{#endsyntax#}. Most application
|
|
||||||
code will use only {#syntax#}async{#endsyntax#} and {#syntax#}await{#endsyntax#}, but event loop
|
|
||||||
implementations will make use of {#syntax#}suspend{#endsyntax#} internally.
|
|
||||||
</p>
|
|
||||||
{#header_close#}
|
|
||||||
|
|
||||||
{#header_open|Async Function Example#}
|
|
||||||
<p>
|
|
||||||
Putting all of this together, here is an example of typical
|
|
||||||
{#syntax#}async{#endsyntax#}/{#syntax#}await{#endsyntax#} usage:
|
|
||||||
</p>
|
|
||||||
{#code_begin|exe|async#}
|
|
||||||
{#backend_stage1#}
|
|
||||||
const std = @import("std");
|
|
||||||
const Allocator = std.mem.Allocator;
|
|
||||||
|
|
||||||
pub fn main() void {
|
|
||||||
_ = async amainWrap();
|
|
||||||
|
|
||||||
// Typically we would use an event loop to manage resuming async functions,
|
|
||||||
// but in this example we hard code what the event loop would do,
|
|
||||||
// to make things deterministic.
|
|
||||||
resume global_file_frame;
|
|
||||||
resume global_download_frame;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn amainWrap() void {
|
|
||||||
amain() catch |e| {
|
|
||||||
std.debug.print("{}\n", .{e});
|
|
||||||
if (@errorReturnTrace()) |trace| {
|
|
||||||
std.debug.dumpStackTrace(trace.*);
|
|
||||||
}
|
|
||||||
std.process.exit(1);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
fn amain() !void {
|
|
||||||
const allocator = std.heap.page_allocator;
|
|
||||||
var download_frame = async fetchUrl(allocator, "https://example.com/");
|
|
||||||
var awaited_download_frame = false;
|
|
||||||
errdefer if (!awaited_download_frame) {
|
|
||||||
if (await download_frame) |r| allocator.free(r) else |_| {}
|
|
||||||
};
|
|
||||||
|
|
||||||
var file_frame = async readFile(allocator, "something.txt");
|
|
||||||
var awaited_file_frame = false;
|
|
||||||
errdefer if (!awaited_file_frame) {
|
|
||||||
if (await file_frame) |r| allocator.free(r) else |_| {}
|
|
||||||
};
|
|
||||||
|
|
||||||
awaited_file_frame = true;
|
|
||||||
const file_text = try await file_frame;
|
|
||||||
defer allocator.free(file_text);
|
|
||||||
|
|
||||||
awaited_download_frame = true;
|
|
||||||
const download_text = try await download_frame;
|
|
||||||
defer allocator.free(download_text);
|
|
||||||
|
|
||||||
std.debug.print("download_text: {s}\n", .{download_text});
|
|
||||||
std.debug.print("file_text: {s}\n", .{file_text});
|
|
||||||
}
|
|
||||||
|
|
||||||
var global_download_frame: anyframe = undefined;
|
|
||||||
fn fetchUrl(allocator: Allocator, url: []const u8) ![]u8 {
|
|
||||||
_ = url; // this is just an example, we don't actually do it!
|
|
||||||
const result = try allocator.dupe(u8, "this is the downloaded url contents");
|
|
||||||
errdefer allocator.free(result);
|
|
||||||
suspend {
|
|
||||||
global_download_frame = @frame();
|
|
||||||
}
|
|
||||||
std.debug.print("fetchUrl returning\n", .{});
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
var global_file_frame: anyframe = undefined;
|
|
||||||
fn readFile(allocator: Allocator, filename: []const u8) ![]u8 {
|
|
||||||
_ = filename; // this is just an example, we don't actually do it!
|
|
||||||
const result = try allocator.dupe(u8, "this is the file contents");
|
|
||||||
errdefer allocator.free(result);
|
|
||||||
suspend {
|
|
||||||
global_file_frame = @frame();
|
|
||||||
}
|
|
||||||
std.debug.print("readFile returning\n", .{});
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
{#code_end#}
|
|
||||||
<p>
|
|
||||||
Now we remove the {#syntax#}suspend{#endsyntax#} and {#syntax#}resume{#endsyntax#} code, and
|
|
||||||
observe the same behavior, with one tiny difference:
|
|
||||||
</p>
|
|
||||||
{#code_begin|exe|blocking#}
|
|
||||||
{#backend_stage1#}
|
|
||||||
const std = @import("std");
|
|
||||||
const Allocator = std.mem.Allocator;
|
|
||||||
|
|
||||||
pub fn main() void {
|
|
||||||
_ = async amainWrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn amainWrap() void {
|
|
||||||
amain() catch |e| {
|
|
||||||
std.debug.print("{}\n", .{e});
|
|
||||||
if (@errorReturnTrace()) |trace| {
|
|
||||||
std.debug.dumpStackTrace(trace.*);
|
|
||||||
}
|
|
||||||
std.process.exit(1);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
fn amain() !void {
|
|
||||||
const allocator = std.heap.page_allocator;
|
|
||||||
var download_frame = async fetchUrl(allocator, "https://example.com/");
|
|
||||||
var awaited_download_frame = false;
|
|
||||||
errdefer if (!awaited_download_frame) {
|
|
||||||
if (await download_frame) |r| allocator.free(r) else |_| {}
|
|
||||||
};
|
|
||||||
|
|
||||||
var file_frame = async readFile(allocator, "something.txt");
|
|
||||||
var awaited_file_frame = false;
|
|
||||||
errdefer if (!awaited_file_frame) {
|
|
||||||
if (await file_frame) |r| allocator.free(r) else |_| {}
|
|
||||||
};
|
|
||||||
|
|
||||||
awaited_file_frame = true;
|
|
||||||
const file_text = try await file_frame;
|
|
||||||
defer allocator.free(file_text);
|
|
||||||
|
|
||||||
awaited_download_frame = true;
|
|
||||||
const download_text = try await download_frame;
|
|
||||||
defer allocator.free(download_text);
|
|
||||||
|
|
||||||
std.debug.print("download_text: {s}\n", .{download_text});
|
|
||||||
std.debug.print("file_text: {s}\n", .{file_text});
|
|
||||||
}
|
|
||||||
|
|
||||||
fn fetchUrl(allocator: Allocator, url: []const u8) ![]u8 {
|
|
||||||
_ = url; // this is just an example, we don't actually do it!
|
|
||||||
const result = try allocator.dupe(u8, "this is the downloaded url contents");
|
|
||||||
errdefer allocator.free(result);
|
|
||||||
std.debug.print("fetchUrl returning\n", .{});
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn readFile(allocator: Allocator, filename: []const u8) ![]u8 {
|
|
||||||
_ = filename; // this is just an example, we don't actually do it!
|
|
||||||
const result = try allocator.dupe(u8, "this is the file contents");
|
|
||||||
errdefer allocator.free(result);
|
|
||||||
std.debug.print("readFile returning\n", .{});
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
{#code_end#}
|
|
||||||
<p>
|
|
||||||
Previously, the {#syntax#}fetchUrl{#endsyntax#} and {#syntax#}readFile{#endsyntax#} functions suspended,
|
|
||||||
and were resumed in an order determined by the {#syntax#}main{#endsyntax#} function. Now,
|
|
||||||
since there are no suspend points, the order of the printed "... returning" messages
|
|
||||||
is determined by the order of {#syntax#}async{#endsyntax#} callsites.
|
|
||||||
</p>
|
|
||||||
{#header_close#}
|
|
||||||
|
|
||||||
{#header_close#}
|
|
||||||
{#header_open|Builtin Functions|2col#}
|
|
||||||
<p>
|
<p>
|
||||||
Builtin functions are provided by the compiler and are prefixed with <code>@</code>.
|
Builtin functions are provided by the compiler and are prefixed with <code>@</code>.
|
||||||
The {#syntax#}comptime{#endsyntax#} keyword on a parameter means that the parameter must be known
|
The {#syntax#}comptime{#endsyntax#} keyword on a parameter means that the parameter must be known
|
||||||
@ -8028,49 +7628,6 @@ comptime {
|
|||||||
</p>
|
</p>
|
||||||
{#header_close#}
|
{#header_close#}
|
||||||
|
|
||||||
{#header_open|@asyncCall#}
|
|
||||||
<pre>{#syntax#}@asyncCall(frame_buffer: []align(@alignOf(@Frame(anyAsyncFunction))) u8, result_ptr, function_ptr, args: anytype) anyframe->T{#endsyntax#}</pre>
|
|
||||||
<p>
|
|
||||||
{#syntax#}@asyncCall{#endsyntax#} performs an {#syntax#}async{#endsyntax#} call on a function pointer,
|
|
||||||
which may or may not be an {#link|async function|Async Functions#}.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
The provided {#syntax#}frame_buffer{#endsyntax#} must be large enough to fit the entire function frame.
|
|
||||||
This size can be determined with {#link|@frameSize#}. To provide a too-small buffer
|
|
||||||
invokes safety-checked {#link|Undefined Behavior#}.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
{#syntax#}result_ptr{#endsyntax#} is optional ({#link|null#} may be provided). If provided,
|
|
||||||
the function call will write its result directly to the result pointer, which will be available to
|
|
||||||
read after {#link|await|Async and Await#} completes. Any result location provided to
|
|
||||||
{#syntax#}await{#endsyntax#} will copy the result from {#syntax#}result_ptr{#endsyntax#}.
|
|
||||||
</p>
|
|
||||||
{#code_begin|test|async_struct_field_fn_pointer#}
|
|
||||||
{#backend_stage1#}
|
|
||||||
const std = @import("std");
|
|
||||||
const expect = std.testing.expect;
|
|
||||||
|
|
||||||
test "async fn pointer in a struct field" {
|
|
||||||
var data: i32 = 1;
|
|
||||||
const Foo = struct {
|
|
||||||
bar: fn (*i32) callconv(.Async) void,
|
|
||||||
};
|
|
||||||
var foo = Foo{ .bar = func };
|
|
||||||
var bytes: [64]u8 align(@alignOf(@Frame(func))) = undefined;
|
|
||||||
const f = @asyncCall(&bytes, {}, foo.bar, .{&data});
|
|
||||||
try expect(data == 2);
|
|
||||||
resume f;
|
|
||||||
try expect(data == 4);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn func(y: *i32) void {
|
|
||||||
defer y.* += 2;
|
|
||||||
y.* += 1;
|
|
||||||
suspend {}
|
|
||||||
}
|
|
||||||
{#code_end#}
|
|
||||||
{#header_close#}
|
|
||||||
|
|
||||||
{#header_open|@atomicLoad#}
|
{#header_open|@atomicLoad#}
|
||||||
<pre>{#syntax#}@atomicLoad(comptime T: type, ptr: *const T, comptime ordering: builtin.AtomicOrder) T{#endsyntax#}</pre>
|
<pre>{#syntax#}@atomicLoad(comptime T: type, ptr: *const T, comptime ordering: builtin.AtomicOrder) T{#endsyntax#}</pre>
|
||||||
<p>
|
<p>
|
||||||
@ -8786,45 +8343,6 @@ test "decl access by string" {
|
|||||||
{#see_also|@intToFloat#}
|
{#see_also|@intToFloat#}
|
||||||
{#header_close#}
|
{#header_close#}
|
||||||
|
|
||||||
{#header_open|@frame#}
|
|
||||||
<pre>{#syntax#}@frame() *@Frame(func){#endsyntax#}</pre>
|
|
||||||
<p>
|
|
||||||
This function returns a pointer to the frame for a given function. This type
|
|
||||||
can be {#link|coerced|Type Coercion#} to {#syntax#}anyframe->T{#endsyntax#} and
|
|
||||||
to {#syntax#}anyframe{#endsyntax#}, where {#syntax#}T{#endsyntax#} is the return type
|
|
||||||
of the function in scope.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
This function does not mark a suspension point, but it does cause the function in scope
|
|
||||||
to become an {#link|async function|Async Functions#}.
|
|
||||||
</p>
|
|
||||||
{#header_close#}
|
|
||||||
|
|
||||||
{#header_open|@Frame#}
|
|
||||||
<pre>{#syntax#}@Frame(func: anytype) type{#endsyntax#}</pre>
|
|
||||||
<p>
|
|
||||||
This function returns the frame type of a function. This works for {#link|Async Functions#}
|
|
||||||
as well as any function without a specific calling convention.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
This type is suitable to be used as the return type of {#link|async|Async and Await#} which
|
|
||||||
allows one to, for example, heap-allocate an async function frame:
|
|
||||||
</p>
|
|
||||||
{#code_begin|test|heap_allocated_frame#}
|
|
||||||
{#backend_stage1#}
|
|
||||||
const std = @import("std");
|
|
||||||
|
|
||||||
test "heap allocated frame" {
|
|
||||||
const frame = try std.heap.page_allocator.create(@Frame(func));
|
|
||||||
frame.* = async func();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn func() void {
|
|
||||||
suspend {}
|
|
||||||
}
|
|
||||||
{#code_end#}
|
|
||||||
{#header_close#}
|
|
||||||
|
|
||||||
{#header_open|@frameAddress#}
|
{#header_open|@frameAddress#}
|
||||||
<pre>{#syntax#}@frameAddress() usize{#endsyntax#}</pre>
|
<pre>{#syntax#}@frameAddress() usize{#endsyntax#}</pre>
|
||||||
<p>
|
<p>
|
||||||
@ -8840,17 +8358,6 @@ fn func() void {
|
|||||||
</p>
|
</p>
|
||||||
{#header_close#}
|
{#header_close#}
|
||||||
|
|
||||||
{#header_open|@frameSize#}
|
|
||||||
<pre>{#syntax#}@frameSize(func: anytype) usize{#endsyntax#}</pre>
|
|
||||||
<p>
|
|
||||||
This is the same as {#syntax#}@sizeOf(@Frame(func)){#endsyntax#}, where {#syntax#}func{#endsyntax#}
|
|
||||||
may be runtime-known.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
This function is typically used in conjunction with {#link|@asyncCall#}.
|
|
||||||
</p>
|
|
||||||
{#header_close#}
|
|
||||||
|
|
||||||
{#header_open|@hasDecl#}
|
{#header_open|@hasDecl#}
|
||||||
<pre>{#syntax#}@hasDecl(comptime Container: type, comptime name: []const u8) bool{#endsyntax#}</pre>
|
<pre>{#syntax#}@hasDecl(comptime Container: type, comptime name: []const u8) bool{#endsyntax#}</pre>
|
||||||
<p>
|
<p>
|
||||||
@ -9851,7 +9358,6 @@ test "integer truncation" {
|
|||||||
<li>{#link|Error Union Type#}</li>
|
<li>{#link|Error Union Type#}</li>
|
||||||
<li>{#link|Vectors#}</li>
|
<li>{#link|Vectors#}</li>
|
||||||
<li>{#link|opaque#}</li>
|
<li>{#link|opaque#}</li>
|
||||||
<li>{#link|@Frame#}</li>
|
|
||||||
<li>{#syntax#}anyframe{#endsyntax#}</li>
|
<li>{#syntax#}anyframe{#endsyntax#}</li>
|
||||||
<li>{#link|struct#}</li>
|
<li>{#link|struct#}</li>
|
||||||
<li>{#link|enum#}</li>
|
<li>{#link|enum#}</li>
|
||||||
@ -10242,7 +9748,6 @@ test "wraparound addition and subtraction" {
|
|||||||
{#header_open|Exact Left Shift Overflow#}
|
{#header_open|Exact Left Shift Overflow#}
|
||||||
<p>At compile-time:</p>
|
<p>At compile-time:</p>
|
||||||
{#code_begin|test_err|operation caused overflow#}
|
{#code_begin|test_err|operation caused overflow#}
|
||||||
{#backend_stage1#}
|
|
||||||
comptime {
|
comptime {
|
||||||
const x = @shlExact(@as(u8, 0b01010101), 2);
|
const x = @shlExact(@as(u8, 0b01010101), 2);
|
||||||
_ = x;
|
_ = x;
|
||||||
@ -10262,7 +9767,6 @@ pub fn main() void {
|
|||||||
{#header_open|Exact Right Shift Overflow#}
|
{#header_open|Exact Right Shift Overflow#}
|
||||||
<p>At compile-time:</p>
|
<p>At compile-time:</p>
|
||||||
{#code_begin|test_err|exact shift shifted out 1 bits#}
|
{#code_begin|test_err|exact shift shifted out 1 bits#}
|
||||||
{#backend_stage1#}
|
|
||||||
comptime {
|
comptime {
|
||||||
const x = @shrExact(@as(u8, 0b10101010), 2);
|
const x = @shrExact(@as(u8, 0b10101010), 2);
|
||||||
_ = x;
|
_ = x;
|
||||||
@ -10325,8 +9829,7 @@ pub fn main() void {
|
|||||||
{#header_close#}
|
{#header_close#}
|
||||||
{#header_open|Exact Division Remainder#}
|
{#header_open|Exact Division Remainder#}
|
||||||
<p>At compile-time:</p>
|
<p>At compile-time:</p>
|
||||||
{#code_begin|test_err|exact division had a remainder#}
|
{#code_begin|test_err|exact division produced remainder#}
|
||||||
{#backend_stage1#}
|
|
||||||
comptime {
|
comptime {
|
||||||
const a: u32 = 10;
|
const a: u32 = 10;
|
||||||
const b: u32 = 3;
|
const b: u32 = 3;
|
||||||
@ -10636,7 +10139,6 @@ fn bar(f: *Foo) void {
|
|||||||
</p>
|
</p>
|
||||||
<p>At compile-time:</p>
|
<p>At compile-time:</p>
|
||||||
{#code_begin|test_err|null pointer casted to type#}
|
{#code_begin|test_err|null pointer casted to type#}
|
||||||
{#backend_stage1#}
|
|
||||||
comptime {
|
comptime {
|
||||||
const opt_ptr: ?*i32 = null;
|
const opt_ptr: ?*i32 = null;
|
||||||
const ptr = @ptrCast(*i32, opt_ptr);
|
const ptr = @ptrCast(*i32, opt_ptr);
|
||||||
@ -12271,9 +11773,6 @@ fn readU32Be() u32 {}
|
|||||||
</th>
|
</th>
|
||||||
<td>
|
<td>
|
||||||
{#syntax#}resume{#endsyntax#} will continue execution of a function frame after the point the function was suspended.
|
{#syntax#}resume{#endsyntax#} will continue execution of a function frame after the point the function was suspended.
|
||||||
<ul>
|
|
||||||
<li>See also {#link|Suspend and Resume#}</li>
|
|
||||||
</ul>
|
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
@ -12317,9 +11816,6 @@ fn readU32Be() u32 {}
|
|||||||
{#syntax#}suspend{#endsyntax#} will cause control flow to return to the call site or resumer of the function.
|
{#syntax#}suspend{#endsyntax#} will cause control flow to return to the call site or resumer of the function.
|
||||||
{#syntax#}suspend{#endsyntax#} can also be used before a block within a function,
|
{#syntax#}suspend{#endsyntax#} can also be used before a block within a function,
|
||||||
to allow the function access to its frame before control flow returns to the call site.
|
to allow the function access to its frame before control flow returns to the call site.
|
||||||
<ul>
|
|
||||||
<li>See also {#link|Suspend and Resume#}</li>
|
|
||||||
</ul>
|
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user