authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-07-09 21:21:59-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-07-09 21:21:59-04:00
logc89aac85c440ea4cbccf1abdbd6acf84a33077e3
treef1f759bbf0d6c071fcc5951d1515e9379c4853d8
parent1a1534ecb55d0273bd9cd62d415ac840eb73b2e5

better workaround for guaranteeing memory in coroutine frame

See #1194

2 files changed, 43 insertions(+), 22 deletions(-)

std/atomic/queue_mpsc.zig+25
...@@ -60,6 +60,31 @@ pub fn QueueMpsc(comptime T: type) type {...@@ -60,6 +60,31 @@ pub fn QueueMpsc(comptime T: type) type {
60 }60 }
61 return self.outbox.isEmpty();61 return self.outbox.isEmpty();
62 }62 }
63
64 /// For debugging only. No API guarantees about what this does.
65 pub fn dump(self: *Self) void {
66 {
67 var it = self.outbox.root;
68 while (it) |node| {
69 std.debug.warn("0x{x} -> ", @ptrToInt(node));
70 it = node.next;
71 }
72 }
73 const inbox_index = self.inbox_index;
74 const inboxes = []*std.atomic.Stack(T){
75 &self.inboxes[self.inbox_index],
76 &self.inboxes[1 - self.inbox_index],
77 };
78 for (inboxes) |inbox| {
79 var it = inbox.root;
80 while (it) |node| {
81 std.debug.warn("0x{x} -> ", @ptrToInt(node));
82 it = node.next;
83 }
84 }
85
86 std.debug.warn("null\n");
87 }
63 };88 };
64}89}
6590
std/event.zig+18-22
...@@ -439,15 +439,14 @@ pub const Loop = struct {...@@ -439,15 +439,14 @@ pub const Loop = struct {
439439
440 pub async fn waitFd(self: *Loop, fd: i32) !void {440 pub async fn waitFd(self: *Loop, fd: i32) !void {
441 defer self.removeFd(fd);441 defer self.removeFd(fd);
442 var resume_node = ResumeNode{
443 .id = ResumeNode.Id.Basic,
444 .handle = undefined,
445 };
446 suspend |p| {442 suspend |p| {
447 resume_node.handle = p;443 // TODO explicitly put this memory in the coroutine frame #1194
444 var resume_node = ResumeNode{
445 .id = ResumeNode.Id.Basic,
446 .handle = p,
447 };
448 try self.addFd(fd, &resume_node);448 try self.addFd(fd, &resume_node);
449 }449 }
450 var a = &resume_node; // TODO better way to explicitly put memory in coro frame
451 }450 }
452451
453 /// Bring your own linked list node. This means it can't fail.452 /// Bring your own linked list node. This means it can't fail.
...@@ -618,8 +617,7 @@ pub const Loop = struct {...@@ -618,8 +617,7 @@ pub const Loop = struct {
618 while (true) {617 while (true) {
619 var nbytes: windows.DWORD = undefined;618 var nbytes: windows.DWORD = undefined;
620 var overlapped: ?*windows.OVERLAPPED = undefined;619 var overlapped: ?*windows.OVERLAPPED = undefined;
621 switch (std.os.windowsGetQueuedCompletionStatus(self.os_data.io_port, &nbytes, &completion_key, 620 switch (std.os.windowsGetQueuedCompletionStatus(self.os_data.io_port, &nbytes, &completion_key, &overlapped, windows.INFINITE)) {
622 &overlapped, windows.INFINITE)) {
623 std.os.WindowsWaitResult.Aborted => return,621 std.os.WindowsWaitResult.Aborted => return,
624 std.os.WindowsWaitResult.Normal => {},622 std.os.WindowsWaitResult.Normal => {},
625 }623 }
...@@ -1062,10 +1060,13 @@ pub const Lock = struct {...@@ -1062,10 +1060,13 @@ pub const Lock = struct {
1062 }1060 }
10631061
1064 pub async fn acquire(self: *Lock) Held {1062 pub async fn acquire(self: *Lock) Held {
1065 var my_tick_node: Loop.NextTickNode = undefined;
1066
1067 s: suspend |handle| {1063 s: suspend |handle| {
1068 my_tick_node.data = handle;1064 // TODO explicitly put this memory in the coroutine frame #1194
1065 var my_tick_node = Loop.NextTickNode{
1066 .data = handle,
1067 .next = undefined,
1068 };
1069
1069 self.queue.put(&my_tick_node);1070 self.queue.put(&my_tick_node);
10701071
1071 // At this point, we are in the queue, so we might have already been resumed and this coroutine1072 // At this point, we are in the queue, so we might have already been resumed and this coroutine
...@@ -1107,10 +1108,6 @@ pub const Lock = struct {...@@ -1107,10 +1108,6 @@ pub const Lock = struct {
1107 }1108 }
1108 }1109 }
11091110
1110 // TODO this workaround to force my_tick_node to be in the coroutine frame should
1111 // not be necessary
1112 var trash1 = &my_tick_node;
1113
1114 return Held{ .lock = self };1111 return Held{ .lock = self };
1115 }1112 }
1116};1113};
...@@ -1176,6 +1173,10 @@ test "std.event.Lock" {...@@ -1176,6 +1173,10 @@ test "std.event.Lock" {
1176}1173}
11771174
1178async fn testLock(loop: *Loop, lock: *Lock) void {1175async fn testLock(loop: *Loop, lock: *Lock) void {
1176 // TODO explicitly put next tick node memory in the coroutine frame #1194
1177 suspend |p| {
1178 resume p;
1179 }
1179 const handle1 = async lockRunner(lock) catch @panic("out of memory");1180 const handle1 = async lockRunner(lock) catch @panic("out of memory");
1180 var tick_node1 = Loop.NextTickNode{1181 var tick_node1 = Loop.NextTickNode{
1181 .next = undefined,1182 .next = undefined,
...@@ -1200,12 +1201,6 @@ async fn testLock(loop: *Loop, lock: *Lock) void {...@@ -1200,12 +1201,6 @@ async fn testLock(loop: *Loop, lock: *Lock) void {
1200 await handle1;1201 await handle1;
1201 await handle2;1202 await handle2;
1202 await handle3;1203 await handle3;
1203
1204 // TODO this is to force tick node memory to be in the coro frame
1205 // there should be a way to make it explicit where the memory is
1206 var a = &tick_node1;
1207 var b = &tick_node2;
1208 var c = &tick_node3;
1209}1204}
12101205
1211var shared_test_data = [1]i32{0} ** 10;1206var shared_test_data = [1]i32{0} ** 10;
...@@ -1216,7 +1211,8 @@ async fn lockRunner(lock: *Lock) void {...@@ -1216,7 +1211,8 @@ async fn lockRunner(lock: *Lock) void {
12161211
1217 var i: usize = 0;1212 var i: usize = 0;
1218 while (i < shared_test_data.len) : (i += 1) {1213 while (i < shared_test_data.len) : (i += 1) {
1219 const handle = await (async lock.acquire() catch @panic("out of memory"));1214 const lock_promise = async lock.acquire() catch @panic("out of memory");
1215 const handle = await lock_promise;
1220 defer handle.release();1216 defer handle.release();
12211217
1222 shared_test_index = 0;1218 shared_test_index = 0;