authorgravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2019-11-06 21:41:35+02:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2019-11-06 15:17:40-05:00
log4530adbd3314c28e2cfe831e7a20b154614e2c9c
tree3c2e31a1017db85db193e317c31007056e13c6d0
parent913f7d045020f7f3301201cf69e1d788439d8886

use global event loop in std.event types


6 files changed, 55 insertions(+), 77 deletions(-)

lib/std/event/future.zig+5-12
......@@ -3,7 +3,6 @@ const assert = std.debug.assert;
33const testing = std.testing;
44const builtin = @import("builtin");
55const Lock = std.event.Lock;
6const Loop = std.event.Loop;
76
87/// This is a value that starts out unavailable, until resolve() is called
98/// While it is unavailable, functions suspend when they try to get() it,
......@@ -23,9 +22,9 @@ pub fn Future(comptime T: type) type {
2322 const Self = @This();
2423 const Queue = std.atomic.Queue(anyframe);
2524
26 pub fn init(loop: *Loop) Self {
25 pub fn init() Self {
2726 return Self{
28 .lock = Lock.initLocked(loop),
27 .lock = Lock.initLocked(),
2928 .available = 0,
3029 .data = undefined,
3130 };
......@@ -90,17 +89,11 @@ test "std.event.Future" {
9089 // TODO provide a way to run tests in evented I/O mode
9190 if (!std.io.is_async) return error.SkipZigTest;
9291
93 var loop: Loop = undefined;
94 try loop.initMultiThreaded();
95 defer loop.deinit();
96
97 const handle = async testFuture(&loop);
98
99 loop.run();
92 const handle = async testFuture();
10093}
10194
102fn testFuture(loop: *Loop) void {
103 var future = Future(i32).init(loop);
95fn testFuture() void {
96 var future = Future(i32).init();
10497
10598 var a = async waitOnFuture(&future);
10699 var b = async waitOnFuture(&future);
lib/std/event/group.zig+11-15
......@@ -1,8 +1,8 @@
11const std = @import("../std.zig");
22const builtin = @import("builtin");
33const Lock = std.event.Lock;
4const Loop = std.event.Loop;
54const testing = std.testing;
5const Allocator = std.mem.Allocator;
66
77/// ReturnType must be `void` or `E!void`
88pub fn Group(comptime ReturnType: type) type {
......@@ -10,6 +10,7 @@ pub fn Group(comptime ReturnType: type) type {
1010 frame_stack: Stack,
1111 alloc_stack: Stack,
1212 lock: Lock,
13 allocator: *Allocator,
1314
1415 const Self = @This();
1516
......@@ -19,17 +20,18 @@ pub fn Group(comptime ReturnType: type) type {
1920 };
2021 const Stack = std.atomic.Stack(anyframe->ReturnType);
2122
22 pub fn init(loop: *Loop) Self {
23 pub fn init(allocator: *Allocator) Self {
2324 return Self{
2425 .frame_stack = Stack.init(),
2526 .alloc_stack = Stack.init(),
26 .lock = Lock.init(loop),
27 .lock = Lock.init(),
28 .allocator = allocator,
2729 };
2830 }
2931
3032 /// Add a frame to the group. Thread-safe.
3133 pub fn add(self: *Self, handle: anyframe->ReturnType) (error{OutOfMemory}!void) {
32 const node = try self.lock.loop.allocator.create(Stack.Node);
34 const node = try self.allocator.create(Stack.Node);
3335 node.* = Stack.Node{
3436 .next = undefined,
3537 .data = handle,
......@@ -66,7 +68,7 @@ pub fn Group(comptime ReturnType: type) type {
6668 }
6769 while (self.alloc_stack.pop()) |node| {
6870 const handle = node.data;
69 self.lock.loop.allocator.destroy(node);
71 self.allocator.destroy(node);
7072 if (Error == void) {
7173 await handle;
7274 } else {
......@@ -87,18 +89,12 @@ test "std.event.Group" {
8789 // TODO provide a way to run tests in evented I/O mode
8890 if (!std.io.is_async) return error.SkipZigTest;
8991
90 var loop: Loop = undefined;
91 try loop.initMultiThreaded();
92 defer loop.deinit();
93
94 const handle = async testGroup(&loop);
95
96 loop.run();
92 const handle = async testGroup(std.heap.direct_allocator);
9793}
9894
99async fn testGroup(loop: *Loop) void {
95async fn testGroup(allocator: *Allocator) void {
10096 var count: usize = 0;
101 var group = Group(void).init(loop);
97 var group = Group(void).init(allocator);
10298 var sleep_a_little_frame = async sleepALittle(&count);
10399 group.add(&sleep_a_little_frame) catch @panic("memory");
104100 var increase_by_ten_frame = async increaseByTen(&count);
......@@ -106,7 +102,7 @@ async fn testGroup(loop: *Loop) void {
106102 group.wait();
107103 testing.expect(count == 11);
108104
109 var another = Group(anyerror!void).init(loop);
105 var another = Group(anyerror!void).init(allocator);
110106 var something_else_frame = async somethingElse();
111107 another.add(&something_else_frame) catch @panic("memory");
112108 var something_that_fails_frame = async doSomethingThatFails();
lib/std/event/lock.zig+13-18
......@@ -11,20 +11,22 @@ const Loop = std.event.Loop;
1111/// Allows only one actor to hold the lock.
1212/// TODO: make this API also work in blocking I/O mode.
1313pub const Lock = struct {
14 loop: *Loop,
1514 shared_bit: u8, // TODO make this a bool
1615 queue: Queue,
1716 queue_empty_bit: u8, // TODO make this a bool
1817
1918 const Queue = std.atomic.Queue(anyframe);
2019
20 const global_event_loop = Loop.instance orelse
21 @compileError("std.event.Lock currently only works with event-based I/O");
22
2123 pub const Held = struct {
2224 lock: *Lock,
2325
2426 pub fn release(self: Held) void {
2527 // Resume the next item from the queue.
2628 if (self.lock.queue.get()) |node| {
27 self.lock.loop.onNextTick(node);
29 global_event_loop.onNextTick(node);
2830 return;
2931 }
3032
......@@ -49,7 +51,7 @@ pub const Lock = struct {
4951
5052 // Resume the next item from the queue.
5153 if (self.lock.queue.get()) |node| {
52 self.lock.loop.onNextTick(node);
54 global_event_loop.onNextTick(node);
5355 return;
5456 }
5557
......@@ -65,18 +67,16 @@ pub const Lock = struct {
6567 }
6668 };
6769
68 pub fn init(loop: *Loop) Lock {
70 pub fn init() Lock {
6971 return Lock{
70 .loop = loop,
7172 .shared_bit = 0,
7273 .queue = Queue.init(),
7374 .queue_empty_bit = 1,
7475 };
7576 }
7677
77 pub fn initLocked(loop: *Loop) Lock {
78 pub fn initLocked() Lock {
7879 return Lock{
79 .loop = loop,
8080 .shared_bit = 1,
8181 .queue = Queue.init(),
8282 .queue_empty_bit = 1,
......@@ -126,27 +126,22 @@ test "std.event.Lock" {
126126 // TODO provide a way to run tests in evented I/O mode
127127 if (!std.io.is_async) return error.SkipZigTest;
128128
129 var loop: Loop = undefined;
130 try loop.initMultiThreaded();
131 defer loop.deinit();
132
133 var lock = Lock.init(&loop);
129 var lock = Lock.init();
134130 defer lock.deinit();
135131
136 _ = async testLock(&loop, &lock);
137 loop.run();
132 _ = async testLock(&lock);
138133
139134 testing.expectEqualSlices(i32, [1]i32{3 * @intCast(i32, shared_test_data.len)} ** shared_test_data.len, shared_test_data);
140135}
141136
142async fn testLock(loop: *Loop, lock: *Lock) void {
137async fn testLock(lock: *Lock) void {
143138 var handle1 = async lockRunner(lock);
144139 var tick_node1 = Loop.NextTickNode{
145140 .prev = undefined,
146141 .next = undefined,
147142 .data = &handle1,
148143 };
149 loop.onNextTick(&tick_node1);
144 Loop.instance.?.onNextTick(&tick_node1);
150145
151146 var handle2 = async lockRunner(lock);
152147 var tick_node2 = Loop.NextTickNode{
......@@ -154,7 +149,7 @@ async fn testLock(loop: *Loop, lock: *Lock) void {
154149 .next = undefined,
155150 .data = &handle2,
156151 };
157 loop.onNextTick(&tick_node2);
152 Loop.instance.?.onNextTick(&tick_node2);
158153
159154 var handle3 = async lockRunner(lock);
160155 var tick_node3 = Loop.NextTickNode{
......@@ -162,7 +157,7 @@ async fn testLock(loop: *Loop, lock: *Lock) void {
162157 .next = undefined,
163158 .data = &handle3,
164159 };
165 loop.onNextTick(&tick_node3);
160 Loop.instance.?.onNextTick(&tick_node3);
166161
167162 await handle1;
168163 await handle2;
lib/std/event/locked.zig+3-4
......@@ -1,6 +1,5 @@
11const std = @import("../std.zig");
22const Lock = std.event.Lock;
3const Loop = std.event.Loop;
43
54/// Thread-safe async/await lock that protects one piece of data.
65/// Functions which are waiting for the lock are suspended, and
......@@ -21,9 +20,9 @@ pub fn Locked(comptime T: type) type {
2120 }
2221 };
2322
24 pub fn init(loop: *Loop, data: T) Self {
23 pub fn init(data: T) Self {
2524 return Self{
26 .lock = Lock.init(loop),
25 .lock = Lock.init(),
2726 .private_data = data,
2827 };
2928 }
......@@ -35,7 +34,7 @@ pub fn Locked(comptime T: type) type {
3534 pub async fn acquire(self: *Self) HeldLock {
3635 return HeldLock{
3736 // TODO guaranteed allocation elision
38 .held = await (async self.lock.acquire() catch unreachable),
37 .held = self.lock.acquire(),
3938 .value = &self.private_data,
4039 };
4140 }
lib/std/event/rwlock.zig+19-23
......@@ -13,7 +13,6 @@ const Loop = std.event.Loop;
1313/// When a write lock is held, it will not be released until the writer queue is empty.
1414/// TODO: make this API also work in blocking I/O mode
1515pub const RwLock = struct {
16 loop: *Loop,
1716 shared_state: u8, // TODO make this an enum
1817 writer_queue: Queue,
1918 reader_queue: Queue,
......@@ -29,6 +28,9 @@ pub const RwLock = struct {
2928
3029 const Queue = std.atomic.Queue(anyframe);
3130
31 const global_event_loop = Loop.instance orelse
32 @compileError("std.event.RwLock currently only works with event-based I/O");
33
3234 pub const HeldRead = struct {
3335 lock: *RwLock,
3436
......@@ -55,7 +57,7 @@ pub const RwLock = struct {
5557 // See if we can leave it locked for writing, and pass the lock to the next writer
5658 // in the queue to grab the lock.
5759 if (self.lock.writer_queue.get()) |node| {
58 self.lock.loop.onNextTick(node);
60 global_event_loop.onNextTick(node);
5961 return;
6062 }
6163
......@@ -64,7 +66,7 @@ pub const RwLock = struct {
6466 // Switch to a read lock.
6567 _ = @atomicRmw(u8, &self.lock.shared_state, .Xchg, State.ReadLock, .SeqCst);
6668 while (self.lock.reader_queue.get()) |node| {
67 self.lock.loop.onNextTick(node);
69 global_event_loop.onNextTick(node);
6870 }
6971 return;
7072 }
......@@ -76,9 +78,8 @@ pub const RwLock = struct {
7678 }
7779 };
7880
79 pub fn init(loop: *Loop) RwLock {
81 pub fn init() RwLock {
8082 return RwLock{
81 .loop = loop,
8283 .shared_state = State.Unlocked,
8384 .writer_queue = Queue.init(),
8485 .writer_queue_empty_bit = 1,
......@@ -120,7 +121,7 @@ pub const RwLock = struct {
120121 // Give out all the read locks.
121122 if (self.reader_queue.get()) |first_node| {
122123 while (self.reader_queue.get()) |node| {
123 self.loop.onNextTick(node);
124 global_event_loop.onNextTick(node);
124125 }
125126 resume first_node.data;
126127 }
......@@ -171,7 +172,7 @@ pub const RwLock = struct {
171172 }
172173 // If there's an item in the writer queue, give them the lock, and we're done.
173174 if (self.writer_queue.get()) |node| {
174 self.loop.onNextTick(node);
175 global_event_loop.onNextTick(node);
175176 return;
176177 }
177178 // Release the lock again.
......@@ -187,9 +188,9 @@ pub const RwLock = struct {
187188 }
188189 // If there are any items in the reader queue, give out all the reader locks, and we're done.
189190 if (self.reader_queue.get()) |first_node| {
190 self.loop.onNextTick(first_node);
191 global_event_loop.onNextTick(first_node);
191192 while (self.reader_queue.get()) |node| {
192 self.loop.onNextTick(node);
193 global_event_loop.onNextTick(node);
193194 }
194195 return;
195196 }
......@@ -216,46 +217,41 @@ test "std.event.RwLock" {
216217 // TODO provide a way to run tests in evented I/O mode
217218 if (!std.io.is_async) return error.SkipZigTest;
218219
219 var loop: Loop = undefined;
220 try loop.initMultiThreaded();
221 defer loop.deinit();
222
223 var lock = RwLock.init(&loop);
220 var lock = RwLock.init();
224221 defer lock.deinit();
225222
226 const handle = testLock(&loop, &lock);
227 loop.run();
223 const handle = testLock(std.heap.direct_allocator, &lock);
228224
229225 const expected_result = [1]i32{shared_it_count * @intCast(i32, shared_test_data.len)} ** shared_test_data.len;
230226 testing.expectEqualSlices(i32, expected_result, shared_test_data);
231227}
232228
233async fn testLock(loop: *Loop, lock: *RwLock) void {
229async fn testLock(allocator: *Allocator, lock: *RwLock) void {
234230 var read_nodes: [100]Loop.NextTickNode = undefined;
235231 for (read_nodes) |*read_node| {
236 const frame = loop.allocator.create(@Frame(readRunner)) catch @panic("memory");
232 const frame = allocator.create(@Frame(readRunner)) catch @panic("memory");
237233 read_node.data = frame;
238234 frame.* = async readRunner(lock);
239 loop.onNextTick(read_node);
235 Loop.instance.?.onNextTick(read_node);
240236 }
241237
242238 var write_nodes: [shared_it_count]Loop.NextTickNode = undefined;
243239 for (write_nodes) |*write_node| {
244 const frame = loop.allocator.create(@Frame(writeRunner)) catch @panic("memory");
240 const frame = allocator.create(@Frame(writeRunner)) catch @panic("memory");
245241 write_node.data = frame;
246242 frame.* = async writeRunner(lock);
247 loop.onNextTick(write_node);
243 Loop.instance.?.onNextTick(write_node);
248244 }
249245
250246 for (write_nodes) |*write_node| {
251247 const casted = @ptrCast(*const @Frame(writeRunner), write_node.data);
252248 await casted;
253 loop.allocator.destroy(casted);
249 allocator.destroy(casted);
254250 }
255251 for (read_nodes) |*read_node| {
256252 const casted = @ptrCast(*const @Frame(readRunner), read_node.data);
257253 await casted;
258 loop.allocator.destroy(casted);
254 allocator.destroy(casted);
259255 }
260256}
261257
lib/std/event/rwlocked.zig+4-5
......@@ -1,6 +1,5 @@
11const std = @import("../std.zig");
22const RwLock = std.event.RwLock;
3const Loop = std.event.Loop;
43
54/// Thread-safe async/await RW lock that protects one piece of data.
65/// Functions which are waiting for the lock are suspended, and
......@@ -30,9 +29,9 @@ pub fn RwLocked(comptime T: type) type {
3029 }
3130 };
3231
33 pub fn init(loop: *Loop, data: T) Self {
32 pub fn init(data: T) Self {
3433 return Self{
35 .lock = RwLock.init(loop),
34 .lock = RwLock.init(),
3635 .locked_data = data,
3736 };
3837 }
......@@ -43,14 +42,14 @@ pub fn RwLocked(comptime T: type) type {
4342
4443 pub async fn acquireRead(self: *Self) HeldReadLock {
4544 return HeldReadLock{
46 .held = await (async self.lock.acquireRead() catch unreachable),
45 .held = self.lock.acquireRead(),
4746 .value = &self.locked_data,
4847 };
4948 }
5049
5150 pub async fn acquireWrite(self: *Self) HeldWriteLock {
5251 return HeldWriteLock{
53 .held = await (async self.lock.acquireWrite() catch unreachable),
52 .held = self.lock.acquireWrite(),
5453 .value = &self.locked_data,
5554 };
5655 }