master
1const builtin = @import("builtin");
2
3const std = @import("std");
4const Io = std.Io;
5const testing = std.testing;
6const assert = std.debug.assert;
7
8test "concurrent vs main prevents deadlock via oversubscription" {
9 var threaded: Io.Threaded = .init(std.testing.allocator);
10 defer threaded.deinit();
11 const io = threaded.io();
12
13 threaded.async_limit = .nothing;
14
15 var queue: Io.Queue(u8) = .init(&.{});
16
17 var putter = io.concurrent(put, .{ io, &queue }) catch |err| switch (err) {
18 error.ConcurrencyUnavailable => {
19 try testing.expect(builtin.single_threaded);
20 return;
21 },
22 };
23 defer putter.cancel(io);
24
25 try testing.expectEqual(42, queue.getOneUncancelable(io));
26}
27
28fn put(io: Io, queue: *Io.Queue(u8)) void {
29 queue.putOneUncancelable(io, 42);
30}
31
32fn get(io: Io, queue: *Io.Queue(u8)) void {
33 assert(queue.getOneUncancelable(io) == 42);
34}
35
36test "concurrent vs concurrent prevents deadlock via oversubscription" {
37 var threaded: Io.Threaded = .init(std.testing.allocator);
38 defer threaded.deinit();
39 const io = threaded.io();
40
41 threaded.async_limit = .nothing;
42
43 var queue: Io.Queue(u8) = .init(&.{});
44
45 var putter = io.concurrent(put, .{ io, &queue }) catch |err| switch (err) {
46 error.ConcurrencyUnavailable => {
47 try testing.expect(builtin.single_threaded);
48 return;
49 },
50 };
51 defer putter.cancel(io);
52
53 var getter = try io.concurrent(get, .{ io, &queue });
54 defer getter.cancel(io);
55
56 getter.await(io);
57 putter.await(io);
58}
59
60const ByteArray256 = struct { x: [32]u8 align(32) };
61const ByteArray512 = struct { x: [64]u8 align(64) };
62
63fn concatByteArrays(a: ByteArray256, b: ByteArray256) ByteArray512 {
64 return .{ .x = a.x ++ b.x };
65}
66
67test "async/concurrent context and result alignment" {
68 var buffer: [2048]u8 align(@alignOf(ByteArray512)) = undefined;
69 var fba: std.heap.FixedBufferAllocator = .init(&buffer);
70
71 var threaded: std.Io.Threaded = .init(fba.allocator());
72 defer threaded.deinit();
73 const io = threaded.io();
74
75 const a: ByteArray256 = .{ .x = @splat(2) };
76 const b: ByteArray256 = .{ .x = @splat(3) };
77 const expected: ByteArray512 = .{ .x = @as([32]u8, @splat(2)) ++ @as([32]u8, @splat(3)) };
78
79 {
80 var future = io.async(concatByteArrays, .{ a, b });
81 const result = future.await(io);
82 try std.testing.expectEqualSlices(u8, &expected.x, &result.x);
83 }
84 {
85 var future = io.concurrent(concatByteArrays, .{ a, b }) catch |err| switch (err) {
86 error.ConcurrencyUnavailable => {
87 try testing.expect(builtin.single_threaded);
88 return;
89 },
90 };
91 const result = future.await(io);
92 try std.testing.expectEqualSlices(u8, &expected.x, &result.x);
93 }
94}
95
96fn concatByteArraysResultPtr(a: ByteArray256, b: ByteArray256, result: *ByteArray512) void {
97 result.* = .{ .x = a.x ++ b.x };
98}
99
100test "Group.async context alignment" {
101 var buffer: [2048]u8 align(@alignOf(ByteArray512)) = undefined;
102 var fba: std.heap.FixedBufferAllocator = .init(&buffer);
103
104 var threaded: std.Io.Threaded = .init(fba.allocator());
105 defer threaded.deinit();
106 const io = threaded.io();
107
108 const a: ByteArray256 = .{ .x = @splat(2) };
109 const b: ByteArray256 = .{ .x = @splat(3) };
110 const expected: ByteArray512 = .{ .x = @as([32]u8, @splat(2)) ++ @as([32]u8, @splat(3)) };
111
112 var group: std.Io.Group = .init;
113 var result: ByteArray512 = undefined;
114 group.async(io, concatByteArraysResultPtr, .{ a, b, &result });
115 group.wait(io);
116 try std.testing.expectEqualSlices(u8, &expected.x, &result.x);
117}
118
119fn returnArray() [32]u8 {
120 return @splat(5);
121}
122
123test "async with array return type" {
124 var threaded: std.Io.Threaded = .init(std.testing.allocator);
125 defer threaded.deinit();
126 const io = threaded.io();
127
128 var future = io.async(returnArray, .{});
129 const result = future.await(io);
130 try std.testing.expectEqualSlices(u8, &@as([32]u8, @splat(5)), &result);
131}