authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-08-02 17:04:17-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-08-02 17:04:17-04:00
log821805aa92898cfdb770b87ac916e45e428621b8
tree9fc4468728398b92c5d3c4bdf62fd57434abf9ee
parente3ae2cfb5243e7255bf4dbcc8a9b7e77a31e9d45

WIP: Channel.getOrNull


17 files changed, 493 insertions(+), 321 deletions(-)

src-self-hosted/compilation.zig+133-91
......@@ -717,13 +717,13 @@ pub const Compilation = struct {
717717 }
718718
719719 async fn buildAsync(self: *Compilation) void {
720 while (true) {
721 // TODO directly awaiting async should guarantee memory allocation elision
722 const build_result = await (async self.compileAndLink() catch unreachable);
720 var build_result = await (async self.initialCompile() catch unreachable);
723721
722 while (true) {
723 const link_result = if (build_result) self.maybeLink() else |err| err;
724724 // this makes a handy error return trace and stack trace in debug mode
725725 if (std.debug.runtime_safety) {
726 build_result catch unreachable;
726 link_result catch unreachable;
727727 }
728728
729729 const compile_errors = blk: {
......@@ -732,7 +732,7 @@ pub const Compilation = struct {
732732 break :blk held.value.toOwnedSlice();
733733 };
734734
735 if (build_result) |_| {
735 if (link_result) |_| {
736736 if (compile_errors.len == 0) {
737737 await (async self.events.put(Event.Ok) catch unreachable);
738738 } else {
......@@ -745,108 +745,158 @@ pub const Compilation = struct {
745745 await (async self.events.put(Event{ .Error = err }) catch unreachable);
746746 }
747747
748 // for now we stop after 1
749 return;
748 var group = event.Group(BuildError!void).init(self.loop);
749 while (self.fs_watch.channel.getOrNull()) |root_scope| {
750 try group.call(rebuildFile, self, root_scope);
751 }
752 build_result = await (async group.wait() catch unreachable);
750753 }
751754 }
752755
753 async fn compileAndLink(self: *Compilation) !void {
754 if (self.root_src_path) |root_src_path| {
755 // TODO async/await os.path.real
756 const root_src_real_path = os.path.real(self.gpa(), root_src_path) catch |err| {
757 try printError("unable to get real path '{}': {}", root_src_path, err);
756 async fn rebuildFile(self: *Compilation, root_scope: *Scope.Root) !void {
757 const tree_scope = blk: {
758 const source_code = (await (async fs.readFile(
759 self.loop,
760 root_src_real_path,
761 max_src_size,
762 ) catch unreachable)) catch |err| {
763 try printError("unable to open '{}': {}", root_src_real_path, err);
758764 return err;
759765 };
760 const root_scope = blk: {
761 errdefer self.gpa().free(root_src_real_path);
766 errdefer self.gpa().free(source_code);
762767
763 const source_code = (await (async fs.readFile(
764 self.loop,
765 root_src_real_path,
766 max_src_size,
767 ) catch unreachable)) catch |err| {
768 try printError("unable to open '{}': {}", root_src_real_path, err);
769 return err;
770 };
771 errdefer self.gpa().free(source_code);
768 const tree = try self.gpa().createOne(ast.Tree);
769 tree.* = try std.zig.parse(self.gpa(), source_code);
770 errdefer {
771 tree.deinit();
772 self.gpa().destroy(tree);
773 }
772774
773 const tree = try self.gpa().createOne(ast.Tree);
774 tree.* = try std.zig.parse(self.gpa(), source_code);
775 errdefer {
776 tree.deinit();
777 self.gpa().destroy(tree);
778 }
775 break :blk try Scope.AstTree.create(self, tree, root_scope);
776 };
777 defer tree_scope.base.deref(self);
779778
780 break :blk try Scope.Root.create(self, tree, root_src_real_path);
781 };
782 defer root_scope.base.deref(self);
783 const tree = root_scope.tree;
779 var error_it = tree_scope.tree.errors.iterator(0);
780 while (error_it.next()) |parse_error| {
781 const msg = try Msg.createFromParseErrorAndScope(self, tree_scope, parse_error);
782 errdefer msg.destroy();
784783
785 var error_it = tree.errors.iterator(0);
786 while (error_it.next()) |parse_error| {
787 const msg = try Msg.createFromParseErrorAndScope(self, root_scope, parse_error);
788 errdefer msg.destroy();
784 try await (async self.addCompileErrorAsync(msg) catch unreachable);
785 }
786 if (tree_scope.tree.errors.len != 0) {
787 return;
788 }
789789
790 try await (async self.addCompileErrorAsync(msg) catch unreachable);
791 }
792 if (tree.errors.len != 0) {
793 return;
794 }
790 const locked_table = await (async root_scope.decls.table.acquireWrite() catch unreachable);
791 defer locked_table.release();
795792
796 const decls = try Scope.Decls.create(self, &root_scope.base);
797 defer decls.base.deref(self);
793 var decl_group = event.Group(BuildError!void).init(self.loop);
794 defer decl_group.deinit();
798795
799 var decl_group = event.Group(BuildError!void).init(self.loop);
800 var decl_group_consumed = false;
801 errdefer if (!decl_group_consumed) decl_group.cancelAll();
796 try self.rebuildChangedDecls(
797 &decl_group,
798 locked_table,
799 root_scope.decls,
800 &tree_scope.tree.root_node.decls,
801 tree_scope,
802 );
802803
803 var it = tree.root_node.decls.iterator(0);
804 while (it.next()) |decl_ptr| {
805 const decl = decl_ptr.*;
806 switch (decl.id) {
807 ast.Node.Id.Comptime => {
808 const comptime_node = @fieldParentPtr(ast.Node.Comptime, "base", decl);
804 try await (async decl_group.wait() catch unreachable);
805 }
809806
810 try self.prelink_group.call(addCompTimeBlock, self, &decls.base, comptime_node);
811 },
812 ast.Node.Id.VarDecl => @panic("TODO"),
813 ast.Node.Id.FnProto => {
814 const fn_proto = @fieldParentPtr(ast.Node.FnProto, "base", decl);
815
816 const name = if (fn_proto.name_token) |name_token| tree.tokenSlice(name_token) else {
817 try self.addCompileError(root_scope, Span{
818 .first = fn_proto.fn_token,
819 .last = fn_proto.fn_token + 1,
820 }, "missing function name");
821 continue;
822 };
807 async fn rebuildChangedDecls(
808 self: *Compilation,
809 group: *event.Group(BuildError!void),
810 locked_table: *Decl.Table,
811 decl_scope: *Scope.Decls,
812 ast_decls: &ast.Node.Root.DeclList,
813 tree_scope: *Scope.AstTree,
814 ) !void {
815 var existing_decls = try locked_table.clone();
816 defer existing_decls.deinit();
817
818 var ast_it = ast_decls.iterator(0);
819 while (ast_it.next()) |decl_ptr| {
820 const decl = decl_ptr.*;
821 switch (decl.id) {
822 ast.Node.Id.Comptime => {
823 const comptime_node = @fieldParentPtr(ast.Node.Comptime, "base", decl);
824
825 // TODO connect existing comptime decls to updated source files
823826
827 try self.prelink_group.call(addCompTimeBlock, self, &decl_scope.base, comptime_node);
828 },
829 ast.Node.Id.VarDecl => @panic("TODO"),
830 ast.Node.Id.FnProto => {
831 const fn_proto = @fieldParentPtr(ast.Node.FnProto, "base", decl);
832
833 const name = if (fn_proto.name_token) |name_token| tree_scope.tree.tokenSlice(name_token) else {
834 try self.addCompileError(root_scope, Span{
835 .first = fn_proto.fn_token,
836 .last = fn_proto.fn_token + 1,
837 }, "missing function name");
838 continue;
839 };
840
841 if (existing_decls.remove(name)) |entry| {
842 // compare new code to existing
843 const existing_decl = entry.value;
844 // Just compare the old bytes to the new bytes of the top level decl.
845 // Even if the AST is technically the same, we want error messages to display
846 // from the most recent source.
847 @panic("TODO handle decl comparison");
848 // Add the new thing before dereferencing the old thing. This way we don't end
849 // up pointlessly re-creating things we end up using in the new thing.
850 } else {
851 // add new decl
824852 const fn_decl = try self.gpa().create(Decl.Fn{
825853 .base = Decl{
826854 .id = Decl.Id.Fn,
827855 .name = name,
828 .visib = parseVisibToken(tree, fn_proto.visib_token),
856 .visib = parseVisibToken(tree_scope.tree, fn_proto.visib_token),
829857 .resolution = event.Future(BuildError!void).init(self.loop),
830 .parent_scope = &decls.base,
858 .parent_scope = &decl_scope.base,
831859 },
832860 .value = Decl.Fn.Val{ .Unresolved = {} },
833861 .fn_proto = fn_proto,
834862 });
835863 errdefer self.gpa().destroy(fn_decl);
836864
837 try decl_group.call(addTopLevelDecl, self, decls, &fn_decl.base);
838 },
839 ast.Node.Id.TestDecl => @panic("TODO"),
840 else => unreachable,
841 }
865 try group.call(addTopLevelDecl, self, &fn_decl.base, locked_table);
866 }
867 },
868 ast.Node.Id.TestDecl => @panic("TODO"),
869 else => unreachable,
842870 }
843 decl_group_consumed = true;
844 try await (async decl_group.wait() catch unreachable);
871 }
872
873 var existing_decl_it = existing_decls.iterator();
874 while (existing_decl_it.next()) |entry| {
875 // this decl was deleted
876 const existing_decl = entry.value;
877 @panic("TODO handle decl deletion");
878 }
879 }
880
881 async fn initialCompile(self: *Compilation) !void {
882 if (self.root_src_path) |root_src_path| {
883 const root_scope = blk: {
884 // TODO async/await os.path.real
885 const root_src_real_path = os.path.real(self.gpa(), root_src_path) catch |err| {
886 try printError("unable to get real path '{}': {}", root_src_path, err);
887 return err;
888 };
889 errdefer self.gpa().free(root_src_real_path);
845890
846 // Now other code can rely on the decls scope having a complete list of names.
847 decls.name_future.resolve();
891 break :blk try Scope.Root.create(self, root_src_real_path);
892 };
893 defer root_scope.base.deref(self);
894
895 try self.rebuildFile(root_scope);
848896 }
897 }
849898
899 async fn maybeLink(self: *Compilation) !void {
850900 (await (async self.prelink_group.wait() catch unreachable)) catch |err| switch (err) {
851901 error.SemanticAnalysisFailed => {},
852902 else => return err,
......@@ -920,28 +970,20 @@ pub const Compilation = struct {
920970 analyzed_code.destroy(comp.gpa());
921971 }
922972
923 async fn addTopLevelDecl(self: *Compilation, decls: *Scope.Decls, decl: *Decl) !void {
973 async fn addTopLevelDecl(
974 self: *Compilation,
975 decl: *Decl,
976 locked_table: *Decl.Table,
977 ) !void {
924978 const tree = decl.findRootScope().tree;
925979 const is_export = decl.isExported(tree);
926980
927 var add_to_table_resolved = false;
928 const add_to_table = async self.addDeclToTable(decls, decl) catch unreachable;
929 errdefer if (!add_to_table_resolved) cancel add_to_table; // TODO https://github.com/ziglang/zig/issues/1261
930
931981 if (is_export) {
932982 try self.prelink_group.call(verifyUniqueSymbol, self, decl);
933983 try self.prelink_group.call(resolveDecl, self, decl);
934984 }
935985
936 add_to_table_resolved = true;
937 try await add_to_table;
938 }
939
940 async fn addDeclToTable(self: *Compilation, decls: *Scope.Decls, decl: *Decl) !void {
941 const held = await (async decls.table.acquire() catch unreachable);
942 defer held.release();
943
944 if (try held.value.put(decl.name, decl)) |other_decl| {
986 if (try locked_table.put(decl.name, decl)) |other_decl| {
945987 try self.addCompileError(decls.base.findRoot(), decl.getSpan(), "redefinition of '{}'", decl.name);
946988 // TODO note: other definition here
947989 }
src-self-hosted/decl.zig+1
......@@ -16,6 +16,7 @@ pub const Decl = struct {
1616 visib: Visib,
1717 resolution: event.Future(Compilation.BuildError!void),
1818 parent_scope: *Scope,
19 tree_scope: *Scope.AstTree,
1920
2021 pub const Table = std.HashMap([]const u8, *Decl, mem.hash_slice_u8, mem.eql_slice_u8);
2122
src-self-hosted/errmsg.zig+12-12
......@@ -49,7 +49,7 @@ pub const Msg = struct {
4949 };
5050
5151 const ScopeAndComp = struct {
52 root_scope: *Scope.Root,
52 tree_scope: *Scope.AstTree,
5353 compilation: *Compilation,
5454 };
5555
......@@ -60,7 +60,7 @@ pub const Msg = struct {
6060 path_and_tree.allocator.destroy(self);
6161 },
6262 Data.ScopeAndComp => |scope_and_comp| {
63 scope_and_comp.root_scope.base.deref(scope_and_comp.compilation);
63 scope_and_comp.tree_scope.base.deref(scope_and_comp.compilation);
6464 scope_and_comp.compilation.gpa().free(self.text);
6565 scope_and_comp.compilation.gpa().destroy(self);
6666 },
......@@ -84,7 +84,7 @@ pub const Msg = struct {
8484 return path_and_tree.realpath;
8585 },
8686 Data.ScopeAndComp => |scope_and_comp| {
87 return scope_and_comp.root_scope.realpath;
87 return scope_and_comp.tree_scope.root().realpath;
8888 },
8989 }
9090 }
......@@ -95,31 +95,31 @@ pub const Msg = struct {
9595 return path_and_tree.tree;
9696 },
9797 Data.ScopeAndComp => |scope_and_comp| {
98 return scope_and_comp.root_scope.tree;
98 return scope_and_comp.tree_scope.tree;
9999 },
100100 }
101101 }
102102
103103 /// Takes ownership of text
104 /// References root_scope, and derefs when the msg is freed
105 pub fn createFromScope(comp: *Compilation, root_scope: *Scope.Root, span: Span, text: []u8) !*Msg {
104 /// References tree_scope, and derefs when the msg is freed
105 pub fn createFromScope(comp: *Compilation, tree_scope: *Scope.AstTree, span: Span, text: []u8) !*Msg {
106106 const msg = try comp.gpa().create(Msg{
107107 .text = text,
108108 .span = span,
109109 .data = Data{
110110 .ScopeAndComp = ScopeAndComp{
111 .root_scope = root_scope,
111 .tree_scope = tree_scope,
112112 .compilation = comp,
113113 },
114114 },
115115 });
116 root_scope.base.ref();
116 tree_scope.base.ref();
117117 return msg;
118118 }
119119
120120 pub fn createFromParseErrorAndScope(
121121 comp: *Compilation,
122 root_scope: *Scope.Root,
122 tree_scope: *Scope.AstTree,
123123 parse_error: *const ast.Error,
124124 ) !*Msg {
125125 const loc_token = parse_error.loc();
......@@ -127,7 +127,7 @@ pub const Msg = struct {
127127 defer text_buf.deinit();
128128
129129 var out_stream = &std.io.BufferOutStream.init(&text_buf).stream;
130 try parse_error.render(&root_scope.tree.tokens, out_stream);
130 try parse_error.render(&tree_scope.tree.tokens, out_stream);
131131
132132 const msg = try comp.gpa().create(Msg{
133133 .text = undefined,
......@@ -137,12 +137,12 @@ pub const Msg = struct {
137137 },
138138 .data = Data{
139139 .ScopeAndComp = ScopeAndComp{
140 .root_scope = root_scope,
140 .tree_scope = tree_scope,
141141 .compilation = comp,
142142 },
143143 },
144144 });
145 root_scope.base.ref();
145 tree_scope.base.ref();
146146 msg.text = text_buf.toOwnedSlice();
147147 return msg;
148148 }
src-self-hosted/ir.zig+3-2
......@@ -1929,8 +1929,9 @@ pub const Builder = struct {
19291929 Scope.Id.Root => return Ident.NotFound,
19301930 Scope.Id.Decls => {
19311931 const decls = @fieldParentPtr(Scope.Decls, "base", s);
1932 const table = await (async decls.getTableReadOnly() catch unreachable);
1933 if (table.get(name)) |entry| {
1932 const locked_table = await (async decls.table.acquireRead() catch unreachable);
1933 defer locked_table.release();
1934 if (locked_table.value.get(name)) |entry| {
19341935 return Ident{ .Decl = entry.value };
19351936 }
19361937 },
src-self-hosted/libc_installation.zig+2-4
......@@ -143,7 +143,7 @@ pub const LibCInstallation = struct {
143143 pub async fn findNative(self: *LibCInstallation, loop: *event.Loop) !void {
144144 self.initEmpty();
145145 var group = event.Group(FindError!void).init(loop);
146 errdefer group.cancelAll();
146 errdefer group.deinit();
147147 var windows_sdk: ?*c.ZigWindowsSDK = null;
148148 errdefer if (windows_sdk) |sdk| c.zig_free_windows_sdk(@ptrCast(?[*]c.ZigWindowsSDK, sdk));
149149
......@@ -313,7 +313,7 @@ pub const LibCInstallation = struct {
313313 },
314314 };
315315 var group = event.Group(FindError!void).init(loop);
316 errdefer group.cancelAll();
316 errdefer group.deinit();
317317 for (dyn_tests) |*dyn_test| {
318318 try group.call(testNativeDynamicLinker, self, loop, dyn_test);
319319 }
......@@ -341,7 +341,6 @@ pub const LibCInstallation = struct {
341341 }
342342 }
343343
344
345344 async fn findNativeKernel32LibDir(self: *LibCInstallation, loop: *event.Loop, sdk: *c.ZigWindowsSDK) FindError!void {
346345 var search_buf: [2]Search = undefined;
347346 const searches = fillSearch(&search_buf, sdk);
......@@ -450,7 +449,6 @@ fn fillSearch(search_buf: *[2]Search, sdk: *c.ZigWindowsSDK) []Search {
450449 return search_buf[0..search_end];
451450}
452451
453
454452fn fileExists(allocator: *std.mem.Allocator, path: []const u8) !bool {
455453 if (std.os.File.access(allocator, path)) |_| {
456454 return true;
src-self-hosted/main.zig+32-33
......@@ -71,26 +71,26 @@ pub fn main() !void {
7171 }
7272
7373 const commands = []Command{
74 Command{
75 .name = "build-exe",
76 .exec = cmdBuildExe,
77 },
78 Command{
79 .name = "build-lib",
80 .exec = cmdBuildLib,
81 },
82 Command{
83 .name = "build-obj",
84 .exec = cmdBuildObj,
85 },
74 //Command{
75 // .name = "build-exe",
76 // .exec = cmdBuildExe,
77 //},
78 //Command{
79 // .name = "build-lib",
80 // .exec = cmdBuildLib,
81 //},
82 //Command{
83 // .name = "build-obj",
84 // .exec = cmdBuildObj,
85 //},
8686 Command{
8787 .name = "fmt",
8888 .exec = cmdFmt,
8989 },
90 Command{
91 .name = "libc",
92 .exec = cmdLibC,
93 },
90 //Command{
91 // .name = "libc",
92 // .exec = cmdLibC,
93 //},
9494 Command{
9595 .name = "targets",
9696 .exec = cmdTargets,
......@@ -472,23 +472,22 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Co
472472}
473473
474474async fn processBuildEvents(comp: *Compilation, color: errmsg.Color) void {
475 // TODO directly awaiting async should guarantee memory allocation elision
476 const build_event = await (async comp.events.get() catch unreachable);
477
478 switch (build_event) {
479 Compilation.Event.Ok => {
480 return;
481 },
482 Compilation.Event.Error => |err| {
483 std.debug.warn("build failed: {}\n", @errorName(err));
484 os.exit(1);
485 },
486 Compilation.Event.Fail => |msgs| {
487 for (msgs) |msg| {
488 defer msg.destroy();
489 msg.printToFile(&stderr_file, color) catch os.exit(1);
490 }
491 },
475 while (true) {
476 // TODO directly awaiting async should guarantee memory allocation elision
477 const build_event = await (async comp.events.get() catch unreachable);
478
479 switch (build_event) {
480 Compilation.Event.Ok => {},
481 Compilation.Event.Error => |err| {
482 stderr.print("build failed: {}\n", @errorName(err)) catch os.exit(1);
483 },
484 Compilation.Event.Fail => |msgs| {
485 for (msgs) |msg| {
486 defer msg.destroy();
487 msg.printToFile(&stderr_file, color) catch os.exit(1);
488 }
489 },
490 }
492491 }
493492}
494493
src-self-hosted/scope.zig+40-20
......@@ -36,6 +36,7 @@ pub const Scope = struct {
3636 Id.Defer => @fieldParentPtr(Defer, "base", base).destroy(comp),
3737 Id.DeferExpr => @fieldParentPtr(DeferExpr, "base", base).destroy(comp),
3838 Id.Var => @fieldParentPtr(Var, "base", base).destroy(comp),
39 Id.AstTree => @fieldParentPtr(AstTree, "base", base).destroy(comp),
3940 }
4041 }
4142 }
......@@ -97,6 +98,7 @@ pub const Scope = struct {
9798
9899 pub const Id = enum {
99100 Root,
101 AstTree,
100102 Decls,
101103 Block,
102104 FnDef,
......@@ -108,13 +110,12 @@ pub const Scope = struct {
108110
109111 pub const Root = struct {
110112 base: Scope,
111 tree: *ast.Tree,
112113 realpath: []const u8,
114 decls: *Decls,
113115
114116 /// Creates a Root scope with 1 reference
115117 /// Takes ownership of realpath
116 /// Takes ownership of tree, will deinit and destroy when done.
117 pub fn create(comp: *Compilation, tree: *ast.Tree, realpath: []u8) !*Root {
118 pub fn create(comp: *Compilation, realpath: []u8) !*Root {
118119 const self = try comp.gpa().createOne(Root);
119120 self.* = Root{
120121 .base = Scope{
......@@ -122,40 +123,64 @@ pub const Scope = struct {
122123 .parent = null,
123124 .ref_count = std.atomic.Int(usize).init(1),
124125 },
125 .tree = tree,
126126 .realpath = realpath,
127 .decls = undefined,
127128 };
128
129 errdefer comp.gpa().destroy(self);
130 self.decls = try Decls.create(comp, &self.base);
129131 return self;
130132 }
131133
132134 pub fn destroy(self: *Root, comp: *Compilation) void {
135 self.decls.base.deref(comp);
136 comp.gpa().free(self.realpath);
137 comp.gpa().destroy(self);
138 }
139 };
140
141 pub const AstTree = struct {
142 base: Scope,
143 tree: *ast.Tree,
144
145 /// Creates a scope with 1 reference
146 /// Takes ownership of tree, will deinit and destroy when done.
147 pub fn create(comp: *Compilation, tree: *ast.Tree, root: *Root) !*AstTree {
148 const self = try comp.gpa().createOne(Root);
149 self.* = AstTree{
150 .base = undefined,
151 .tree = tree,
152 };
153 self.base.init(Id.AstTree, &root.base);
154
155 return self;
156 }
157
158 pub fn destroy(self: *AstTree, comp: *Compilation) void {
133159 comp.gpa().free(self.tree.source);
134160 self.tree.deinit();
135161 comp.gpa().destroy(self.tree);
136 comp.gpa().free(self.realpath);
137162 comp.gpa().destroy(self);
138163 }
164
165 pub fn root(self: *AstTree) *Root {
166 return self.base.findRoot();
167 }
139168 };
140169
141170 pub const Decls = struct {
142171 base: Scope,
143172
144 /// The lock must be respected for writing. However once name_future resolves,
145 /// readers can freely access it.
146 table: event.Locked(Decl.Table),
147
148 /// Once this future is resolved, the table is complete and available for unlocked
149 /// read-only access. It does not mean all the decls are resolved; it means only that
150 /// the table has all the names. Each decl in the table has its own resolution state.
151 name_future: event.Future(void),
173 /// This table remains Write Locked when the names are incomplete or possibly outdated.
174 /// So if a reader manages to grab a lock, it can be sure that the set of names is complete
175 /// and correct.
176 table: event.RwLocked(Decl.Table),
152177
153178 /// Creates a Decls scope with 1 reference
154179 pub fn create(comp: *Compilation, parent: *Scope) !*Decls {
155180 const self = try comp.gpa().createOne(Decls);
156181 self.* = Decls{
157182 .base = undefined,
158 .table = event.Locked(Decl.Table).init(comp.loop, Decl.Table.init(comp.gpa())),
183 .table = event.RwLocked(Decl.Table).init(comp.loop, Decl.Table.init(comp.gpa())),
159184 .name_future = event.Future(void).init(comp.loop),
160185 };
161186 self.base.init(Id.Decls, parent);
......@@ -166,11 +191,6 @@ pub const Scope = struct {
166191 self.table.deinit();
167192 comp.gpa().destroy(self);
168193 }
169
170 pub async fn getTableReadOnly(self: *Decls) *Decl.Table {
171 _ = await (async self.name_future.get() catch unreachable);
172 return &self.table.private_data;
173 }
174194 };
175195
176196 pub const Block = struct {
src-self-hosted/test.zig+1-1
......@@ -50,7 +50,7 @@ pub const TestContext = struct {
5050 errdefer self.event_loop_local.deinit();
5151
5252 self.group = std.event.Group(error!void).init(&self.loop);
53 errdefer self.group.cancelAll();
53 errdefer self.group.deinit();
5454
5555 self.zig_lib_dir = try introspect.resolveZigLibDir(allocator);
5656 errdefer allocator.free(self.zig_lib_dir);
std/atomic/queue.zig+60-24
......@@ -1,40 +1,38 @@
1const std = @import("../index.zig");
12const builtin = @import("builtin");
23const AtomicOrder = builtin.AtomicOrder;
34const AtomicRmwOp = builtin.AtomicRmwOp;
5const assert = std.debug.assert;
46
57/// Many producer, many consumer, non-allocating, thread-safe.
6/// Uses a spinlock to protect get() and put().
8/// Uses a mutex to protect access.
79pub fn Queue(comptime T: type) type {
810 return struct {
911 head: ?*Node,
1012 tail: ?*Node,
11 lock: u8,
13 mutex: std.Mutex,
1214
1315 pub const Self = this;
14
15 pub const Node = struct {
16 next: ?*Node,
17 data: T,
18 };
16 pub const Node = std.LinkedList(T).Node;
1917
2018 pub fn init() Self {
2119 return Self{
2220 .head = null,
2321 .tail = null,
24 .lock = 0,
22 .mutex = std.Mutex.init(),
2523 };
2624 }
2725
2826 pub fn put(self: *Self, node: *Node) void {
2927 node.next = null;
3028
31 while (@atomicRmw(u8, &self.lock, builtin.AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst) != 0) {}
32 defer assert(@atomicRmw(u8, &self.lock, builtin.AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst) == 1);
29 const held = self.mutex.acquire();
30 defer held.release();
3331
34 const opt_tail = self.tail;
32 node.prev = self.tail;
3533 self.tail = node;
36 if (opt_tail) |tail| {
37 tail.next = node;
34 if (node.prev) |prev_tail| {
35 prev_tail.next = node;
3836 } else {
3937 assert(self.head == null);
4038 self.head = node;
......@@ -42,18 +40,27 @@ pub fn Queue(comptime T: type) type {
4240 }
4341
4442 pub fn get(self: *Self) ?*Node {
45 while (@atomicRmw(u8, &self.lock, builtin.AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst) != 0) {}
46 defer assert(@atomicRmw(u8, &self.lock, builtin.AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst) == 1);
43 const held = self.mutex.acquire();
44 defer held.release();
4745
4846 const head = self.head orelse return null;
4947 self.head = head.next;
50 if (head.next == null) self.tail = null;
48 if (head.next) |new_head| {
49 new_head.prev = null;
50 } else {
51 self.tail = null;
52 }
53 // This way, a get() and a remove() are thread-safe with each other.
54 head.prev = null;
55 head.next = null;
5156 return head;
5257 }
5358
5459 pub fn unget(self: *Self, node: *Node) void {
55 while (@atomicRmw(u8, &self.lock, builtin.AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst) != 0) {}
56 defer assert(@atomicRmw(u8, &self.lock, builtin.AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst) == 1);
60 node.prev = null;
61
62 const held = self.mutex.acquire();
63 defer held.release();
5764
5865 const opt_head = self.head;
5966 self.head = node;
......@@ -65,13 +72,39 @@ pub fn Queue(comptime T: type) type {
6572 }
6673 }
6774
75 /// Thread-safe with get() and remove(). Returns whether node was actually removed.
76 pub fn remove(self: *Self, node: *Node) bool {
77 const held = self.mutex.acquire();
78 defer held.release();
79
80 if (node.prev == null and node.next == null and self.head != node) {
81 return false;
82 }
83
84 if (node.prev) |prev| {
85 prev.next = node.next;
86 } else {
87 self.head = node.next;
88 }
89 if (node.next) |next| {
90 next.prev = node.prev;
91 } else {
92 self.tail = node.prev;
93 }
94 node.prev = null;
95 node.next = null;
96 return true;
97 }
98
6899 pub fn isEmpty(self: *Self) bool {
69 return @atomicLoad(?*Node, &self.head, builtin.AtomicOrder.SeqCst) != null;
100 const held = self.mutex.acquire();
101 defer held.release();
102 return self.head != null;
70103 }
71104
72105 pub fn dump(self: *Self) void {
73 while (@atomicRmw(u8, &self.lock, builtin.AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst) != 0) {}
74 defer assert(@atomicRmw(u8, &self.lock, builtin.AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst) == 1);
106 const held = self.mutex.acquire();
107 defer held.release();
75108
76109 std.debug.warn("head: ");
77110 dumpRecursive(self.head, 0);
......@@ -93,9 +126,6 @@ pub fn Queue(comptime T: type) type {
93126 };
94127}
95128
96const std = @import("../index.zig");
97const assert = std.debug.assert;
98
99129const Context = struct {
100130 allocator: *std.mem.Allocator,
101131 queue: *Queue(i32),
......@@ -169,6 +199,7 @@ fn startPuts(ctx: *Context) u8 {
169199 std.os.time.sleep(0, 1); // let the os scheduler be our fuzz
170200 const x = @bitCast(i32, r.random.scalar(u32));
171201 const node = ctx.allocator.create(Queue(i32).Node{
202 .prev = undefined,
172203 .next = undefined,
173204 .data = x,
174205 }) catch unreachable;
......@@ -198,12 +229,14 @@ test "std.atomic.Queue single-threaded" {
198229 var node_0 = Queue(i32).Node{
199230 .data = 0,
200231 .next = undefined,
232 .prev = undefined,
201233 };
202234 queue.put(&node_0);
203235
204236 var node_1 = Queue(i32).Node{
205237 .data = 1,
206238 .next = undefined,
239 .prev = undefined,
207240 };
208241 queue.put(&node_1);
209242
......@@ -212,12 +245,14 @@ test "std.atomic.Queue single-threaded" {
212245 var node_2 = Queue(i32).Node{
213246 .data = 2,
214247 .next = undefined,
248 .prev = undefined,
215249 };
216250 queue.put(&node_2);
217251
218252 var node_3 = Queue(i32).Node{
219253 .data = 3,
220254 .next = undefined,
255 .prev = undefined,
221256 };
222257 queue.put(&node_3);
223258
......@@ -228,6 +263,7 @@ test "std.atomic.Queue single-threaded" {
228263 var node_4 = Queue(i32).Node{
229264 .data = 4,
230265 .next = undefined,
266 .prev = undefined,
231267 };
232268 queue.put(&node_4);
233269
std/event.zig+1-1
......@@ -3,7 +3,7 @@ pub const Future = @import("event/future.zig").Future;
33pub const Group = @import("event/group.zig").Group;
44pub const Lock = @import("event/lock.zig").Lock;
55pub const Locked = @import("event/locked.zig").Locked;
6pub const RwLock = @import("event/rwlock.zig").Lock;
6pub const RwLock = @import("event/rwlock.zig").RwLock;
77pub const RwLocked = @import("event/rwlocked.zig").RwLocked;
88pub const Loop = @import("event/loop.zig").Loop;
99pub const fs = @import("event/fs.zig");
std/event/channel.zig+168-29
......@@ -5,7 +5,7 @@ const AtomicRmwOp = builtin.AtomicRmwOp;
55const AtomicOrder = builtin.AtomicOrder;
66const Loop = std.event.Loop;
77
8/// many producer, many consumer, thread-safe, lock-free, runtime configurable buffer size
8/// many producer, many consumer, thread-safe, runtime configurable buffer size
99/// when buffer is empty, consumers suspend and are resumed by producers
1010/// when buffer is full, producers suspend and are resumed by consumers
1111pub fn Channel(comptime T: type) type {
......@@ -13,6 +13,7 @@ pub fn Channel(comptime T: type) type {
1313 loop: *Loop,
1414
1515 getters: std.atomic.Queue(GetNode),
16 or_null_queue: std.atomic.Queue(*std.atomic.Queue(GetNode).Node),
1617 putters: std.atomic.Queue(PutNode),
1718 get_count: usize,
1819 put_count: usize,
......@@ -26,8 +27,22 @@ pub fn Channel(comptime T: type) type {
2627
2728 const SelfChannel = this;
2829 const GetNode = struct {
29 ptr: *T,
3030 tick_node: *Loop.NextTickNode,
31 data: Data,
32
33 const Data = union(enum) {
34 Normal: Normal,
35 OrNull: OrNull,
36 };
37
38 const Normal = struct {
39 ptr: *T,
40 };
41
42 const OrNull = struct {
43 ptr: *?T,
44 or_null: *std.atomic.Queue(*std.atomic.Queue(GetNode).Node).Node,
45 };
3146 };
3247 const PutNode = struct {
3348 data: T,
......@@ -48,6 +63,7 @@ pub fn Channel(comptime T: type) type {
4863 .need_dispatch = 0,
4964 .getters = std.atomic.Queue(GetNode).init(),
5065 .putters = std.atomic.Queue(PutNode).init(),
66 .or_null_queue = std.atomic.Queue(*std.atomic.Queue(GetNode).Node).init(),
5167 .get_count = 0,
5268 .put_count = 0,
5369 });
......@@ -71,18 +87,31 @@ pub fn Channel(comptime T: type) type {
7187 /// puts a data item in the channel. The promise completes when the value has been added to the
7288 /// buffer, or in the case of a zero size buffer, when the item has been retrieved by a getter.
7389 pub async fn put(self: *SelfChannel, data: T) void {
90 // TODO fix this workaround
91 var my_handle: promise = undefined;
92 suspend |p| {
93 my_handle = p;
94 resume p;
95 }
96
97 var my_tick_node = Loop.NextTickNode.init(my_handle);
98 var queue_node = std.atomic.Queue(PutNode).Node.init(PutNode{
99 .tick_node = &my_tick_node,
100 .data = data,
101 });
102
103 // TODO test canceling a put()
104 errdefer {
105 _ = @atomicRmw(usize, &self.put_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst);
106 const need_dispatch = !self.putters.remove(&queue_node);
107 self.loop.cancelOnNextTick(&my_tick_node);
108 if (need_dispatch) {
109 // oops we made the put_count incorrect for a period of time. fix by dispatching.
110 _ = @atomicRmw(usize, &self.put_count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst);
111 self.dispatch();
112 }
113 }
74114 suspend |handle| {
75 var my_tick_node = Loop.NextTickNode{
76 .next = undefined,
77 .data = handle,
78 };
79 var queue_node = std.atomic.Queue(PutNode).Node{
80 .data = PutNode{
81 .tick_node = &my_tick_node,
82 .data = data,
83 },
84 .next = undefined,
85 };
86115 self.putters.put(&queue_node);
87116 _ = @atomicRmw(usize, &self.put_count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst);
88117
......@@ -93,21 +122,37 @@ pub fn Channel(comptime T: type) type {
93122 /// await this function to get an item from the channel. If the buffer is empty, the promise will
94123 /// complete when the next item is put in the channel.
95124 pub async fn get(self: *SelfChannel) T {
125 // TODO fix this workaround
126 var my_handle: promise = undefined;
127 suspend |p| {
128 my_handle = p;
129 resume p;
130 }
131
96132 // TODO integrate this function with named return values
97133 // so we can get rid of this extra result copy
98134 var result: T = undefined;
99 suspend |handle| {
100 var my_tick_node = Loop.NextTickNode{
101 .next = undefined,
102 .data = handle,
103 };
104 var queue_node = std.atomic.Queue(GetNode).Node{
105 .data = GetNode{
106 .ptr = &result,
107 .tick_node = &my_tick_node,
108 },
109 .next = undefined,
110 };
135 var my_tick_node = Loop.NextTickNode.init(my_handle);
136 var queue_node = std.atomic.Queue(GetNode).Node.init(GetNode{
137 .tick_node = &my_tick_node,
138 .data = GetNode.Data{
139 .Normal = GetNode.Normal{ .ptr = &result },
140 },
141 });
142
143 // TODO test canceling a get()
144 errdefer {
145 _ = @atomicRmw(usize, &self.get_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst);
146 const need_dispatch = !self.getters.remove(&queue_node);
147 self.loop.cancelOnNextTick(&my_tick_node);
148 if (need_dispatch) {
149 // oops we made the get_count incorrect for a period of time. fix by dispatching.
150 _ = @atomicRmw(usize, &self.get_count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst);
151 self.dispatch();
152 }
153 }
154
155 suspend |_| {
111156 self.getters.put(&queue_node);
112157 _ = @atomicRmw(usize, &self.get_count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst);
113158
......@@ -116,8 +161,64 @@ pub fn Channel(comptime T: type) type {
116161 return result;
117162 }
118163
119 fn getOrNull(self: *SelfChannel) ?T {
120 TODO();
164 //pub async fn select(comptime EnumUnion: type, channels: ...) EnumUnion {
165 // assert(@memberCount(EnumUnion) == channels.len); // enum union and channels mismatch
166 // assert(channels.len != 0); // enum unions cannot have 0 fields
167 // if (channels.len == 1) {
168 // const result = await (async channels[0].get() catch unreachable);
169 // return @unionInit(EnumUnion, @memberName(EnumUnion, 0), result);
170 // }
171 //}
172
173 /// Await this function to get an item from the channel. If the buffer is empty and there are no
174 /// puts waiting, this returns null.
175 /// Await is necessary for locking purposes. The function will be resumed after checking the channel
176 /// for data and will not wait for data to be available.
177 pub async fn getOrNull(self: *SelfChannel) ?T {
178 // TODO fix this workaround
179 var my_handle: promise = undefined;
180 suspend |p| {
181 my_handle = p;
182 resume p;
183 }
184
185 // TODO integrate this function with named return values
186 // so we can get rid of this extra result copy
187 var result: ?T = null;
188 var my_tick_node = Loop.NextTickNode.init(my_handle);
189 var or_null_node = std.atomic.Queue(*std.atomic.Queue(GetNode).Node).Node.init(undefined);
190 var queue_node = std.atomic.Queue(GetNode).Node.init(GetNode{
191 .tick_node = &my_tick_node,
192 .data = GetNode.Data{
193 .OrNull = GetNode.OrNull{
194 .ptr = &result,
195 .or_null = &or_null_node,
196 },
197 },
198 });
199 or_null_node.data = &queue_node;
200
201 // TODO test canceling getOrNull
202 errdefer {
203 _ = self.or_null_queue.remove(&or_null_node);
204 _ = @atomicRmw(usize, &self.get_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst);
205 const need_dispatch = !self.getters.remove(&queue_node);
206 self.loop.cancelOnNextTick(&my_tick_node);
207 if (need_dispatch) {
208 // oops we made the get_count incorrect for a period of time. fix by dispatching.
209 _ = @atomicRmw(usize, &self.get_count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst);
210 self.dispatch();
211 }
212 }
213
214 suspend |_| {
215 self.getters.put(&queue_node);
216 _ = @atomicRmw(usize, &self.get_count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst);
217 self.or_null_queue.put(&or_null_node);
218
219 self.dispatch();
220 }
221 return result;
121222 }
122223
123224 fn dispatch(self: *SelfChannel) void {
......@@ -143,7 +244,15 @@ pub fn Channel(comptime T: type) type {
143244 if (get_count == 0) break :one_dispatch;
144245
145246 const get_node = &self.getters.get().?.data;
146 get_node.ptr.* = self.buffer_nodes[self.buffer_index -% self.buffer_len];
247 switch (get_node.data) {
248 GetNode.Data.Normal => |info| {
249 info.ptr.* = self.buffer_nodes[self.buffer_index -% self.buffer_len];
250 },
251 GetNode.Data.OrNull => |info| {
252 _ = self.or_null_queue.remove(info.or_null);
253 info.ptr.* = self.buffer_nodes[self.buffer_index -% self.buffer_len];
254 },
255 }
147256 self.loop.onNextTick(get_node.tick_node);
148257 self.buffer_len -= 1;
149258
......@@ -155,7 +264,15 @@ pub fn Channel(comptime T: type) type {
155264 const get_node = &self.getters.get().?.data;
156265 const put_node = &self.putters.get().?.data;
157266
158 get_node.ptr.* = put_node.data;
267 switch (get_node.data) {
268 GetNode.Data.Normal => |info| {
269 info.ptr.* = put_node.data;
270 },
271 GetNode.Data.OrNull => |info| {
272 _ = self.or_null_queue.remove(info.or_null);
273 info.ptr.* = put_node.data;
274 },
275 }
159276 self.loop.onNextTick(get_node.tick_node);
160277 self.loop.onNextTick(put_node.tick_node);
161278
......@@ -180,6 +297,16 @@ pub fn Channel(comptime T: type) type {
180297 _ = @atomicRmw(usize, &self.get_count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst);
181298 _ = @atomicRmw(usize, &self.put_count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst);
182299
300 // All the "get or null" functions should resume now.
301 var remove_count: usize = 0;
302 while (self.or_null_queue.get()) |or_null_node| {
303 remove_count += @boolToInt(self.getters.remove(or_null_node.data));
304 self.loop.onNextTick(or_null_node.data.data.tick_node);
305 }
306 if (remove_count != 0) {
307 _ = @atomicRmw(usize, &self.get_count, AtomicRmwOp.Sub, remove_count, AtomicOrder.SeqCst);
308 }
309
183310 // clear need-dispatch flag
184311 const need_dispatch = @atomicRmw(u8, &self.need_dispatch, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst);
185312 if (need_dispatch != 0) continue;
......@@ -230,6 +357,15 @@ async fn testChannelGetter(loop: *Loop, channel: *Channel(i32)) void {
230357 const value2_promise = try async channel.get();
231358 const value2 = await value2_promise;
232359 assert(value2 == 4567);
360
361 const value3_promise = try async channel.getOrNull();
362 const value3 = await value3_promise;
363 assert(value3 == null);
364
365 const last_put = try async testPut(channel, 4444);
366 const value4 = await try async channel.getOrNull();
367 assert(value4.? == 4444);
368 await last_put;
233369}
234370
235371async fn testChannelPutter(channel: *Channel(i32)) void {
......@@ -237,3 +373,6 @@ async fn testChannelPutter(channel: *Channel(i32)) void {
237373 await (async channel.put(4567) catch @panic("out of memory"));
238374}
239375
376async fn testPut(channel: *Channel(i32), value: i32) void {
377 await (async channel.put(value) catch @panic("out of memory"));
378}
std/event/fs.zig+11
......@@ -99,6 +99,7 @@ pub async fn pwritev(loop: *event.Loop, fd: os.FileHandle, offset: usize, data:
9999 }
100100
101101 var req_node = RequestNode{
102 .prev = undefined,
102103 .next = undefined,
103104 .data = Request{
104105 .msg = Request.Msg{
......@@ -111,6 +112,7 @@ pub async fn pwritev(loop: *event.Loop, fd: os.FileHandle, offset: usize, data:
111112 },
112113 .finish = Request.Finish{
113114 .TickNode = event.Loop.NextTickNode{
115 .prev = undefined,
114116 .next = undefined,
115117 .data = my_handle,
116118 },
......@@ -148,6 +150,7 @@ pub async fn preadv(loop: *event.Loop, fd: os.FileHandle, offset: usize, data: [
148150 }
149151
150152 var req_node = RequestNode{
153 .prev = undefined,
151154 .next = undefined,
152155 .data = Request{
153156 .msg = Request.Msg{
......@@ -160,6 +163,7 @@ pub async fn preadv(loop: *event.Loop, fd: os.FileHandle, offset: usize, data: [
160163 },
161164 .finish = Request.Finish{
162165 .TickNode = event.Loop.NextTickNode{
166 .prev = undefined,
163167 .next = undefined,
164168 .data = my_handle,
165169 },
......@@ -186,6 +190,7 @@ pub async fn openRead(loop: *event.Loop, path: []const u8) os.File.OpenError!os.
186190 defer loop.allocator.free(path_with_null);
187191
188192 var req_node = RequestNode{
193 .prev = undefined,
189194 .next = undefined,
190195 .data = Request{
191196 .msg = Request.Msg{
......@@ -196,6 +201,7 @@ pub async fn openRead(loop: *event.Loop, path: []const u8) os.File.OpenError!os.
196201 },
197202 .finish = Request.Finish{
198203 .TickNode = event.Loop.NextTickNode{
204 .prev = undefined,
199205 .next = undefined,
200206 .data = my_handle,
201207 },
......@@ -227,6 +233,7 @@ pub async fn openReadWrite(
227233 defer loop.allocator.free(path_with_null);
228234
229235 var req_node = RequestNode{
236 .prev = undefined,
230237 .next = undefined,
231238 .data = Request{
232239 .msg = Request.Msg{
......@@ -238,6 +245,7 @@ pub async fn openReadWrite(
238245 },
239246 .finish = Request.Finish{
240247 .TickNode = event.Loop.NextTickNode{
248 .prev = undefined,
241249 .next = undefined,
242250 .data = my_handle,
243251 },
......@@ -267,6 +275,7 @@ pub const CloseOperation = struct {
267275 .loop = loop,
268276 .have_fd = false,
269277 .close_req_node = RequestNode{
278 .prev = undefined,
270279 .next = undefined,
271280 .data = Request{
272281 .msg = Request.Msg{
......@@ -312,6 +321,7 @@ pub async fn writeFileMode(loop: *event.Loop, path: []const u8, contents: []cons
312321 defer loop.allocator.free(path_with_null);
313322
314323 var req_node = RequestNode{
324 .prev = undefined,
315325 .next = undefined,
316326 .data = Request{
317327 .msg = Request.Msg{
......@@ -324,6 +334,7 @@ pub async fn writeFileMode(loop: *event.Loop, path: []const u8, contents: []cons
324334 },
325335 .finish = Request.Finish{
326336 .TickNode = event.Loop.NextTickNode{
337 .prev = undefined,
327338 .next = undefined,
328339 .data = my_handle,
329340 },
std/event/lock.zig+12-6
......@@ -91,13 +91,16 @@ pub const Lock = struct {
9191 }
9292
9393 pub async fn acquire(self: *Lock) Held {
94 suspend |handle| {
95 // TODO explicitly put this memory in the coroutine frame #1194
96 var my_tick_node = Loop.NextTickNode{
97 .data = handle,
98 .next = undefined,
99 };
94 // TODO explicitly put this memory in the coroutine frame #1194
95 var my_handle: promise = undefined;
96 suspend |p| {
97 my_handle = p;
98 resume p;
99 }
100 var my_tick_node = Loop.NextTickNode.init(my_handle);
100101
102 errdefer _ = self.queue.remove(&my_tick_node); // TODO test canceling an acquire
103 suspend |_| {
101104 self.queue.put(&my_tick_node);
102105
103106 // At this point, we are in the queue, so we might have already been resumed and this coroutine
......@@ -170,6 +173,7 @@ async fn testLock(loop: *Loop, lock: *Lock) void {
170173 }
171174 const handle1 = async lockRunner(lock) catch @panic("out of memory");
172175 var tick_node1 = Loop.NextTickNode{
176 .prev = undefined,
173177 .next = undefined,
174178 .data = handle1,
175179 };
......@@ -177,6 +181,7 @@ async fn testLock(loop: *Loop, lock: *Lock) void {
177181
178182 const handle2 = async lockRunner(lock) catch @panic("out of memory");
179183 var tick_node2 = Loop.NextTickNode{
184 .prev = undefined,
180185 .next = undefined,
181186 .data = handle2,
182187 };
......@@ -184,6 +189,7 @@ async fn testLock(loop: *Loop, lock: *Lock) void {
184189
185190 const handle3 = async lockRunner(lock) catch @panic("out of memory");
186191 var tick_node3 = Loop.NextTickNode{
192 .prev = undefined,
187193 .next = undefined,
188194 .data = handle3,
189195 };
std/event/loop.zig+11
......@@ -120,6 +120,7 @@ pub const Loop = struct {
120120 // we need another thread for the file system because Linux does not have an async
121121 // file system I/O API.
122122 self.os_data.fs_end_request = fs.RequestNode{
123 .prev = undefined,
123124 .next = undefined,
124125 .data = fs.Request{
125126 .msg = fs.Request.Msg.End,
......@@ -206,6 +207,7 @@ pub const Loop = struct {
206207 .udata = @ptrToInt(&eventfd_node.data.base),
207208 },
208209 },
210 .prev = undefined,
209211 .next = undefined,
210212 };
211213 self.available_eventfd_resume_nodes.push(eventfd_node);
......@@ -270,6 +272,7 @@ pub const Loop = struct {
270272 // this one is for sending events
271273 .completion_key = @ptrToInt(&eventfd_node.data.base),
272274 },
275 .prev = undefined,
273276 .next = undefined,
274277 };
275278 self.available_eventfd_resume_nodes.push(eventfd_node);
......@@ -422,6 +425,12 @@ pub const Loop = struct {
422425 self.dispatch();
423426 }
424427
428 pub fn cancelOnNextTick(self: *Loop, node: *NextTickNode) void {
429 if (self.next_tick_queue.remove(node)) {
430 self.finishOneEvent();
431 }
432 }
433
425434 pub fn run(self: *Loop) void {
426435 self.finishOneEvent(); // the reference we start with
427436
......@@ -443,6 +452,7 @@ pub const Loop = struct {
443452 suspend |p| {
444453 handle.* = p;
445454 var my_tick_node = Loop.NextTickNode{
455 .prev = undefined,
446456 .next = undefined,
447457 .data = p,
448458 };
......@@ -464,6 +474,7 @@ pub const Loop = struct {
464474 pub async fn yield(self: *Loop) void {
465475 suspend |p| {
466476 var my_tick_node = Loop.NextTickNode{
477 .prev = undefined,
467478 .next = undefined,
468479 .data = p,
469480 };
std/event/rwlock.zig+2
......@@ -101,6 +101,7 @@ pub const RwLock = struct {
101101 // TODO explicitly put this memory in the coroutine frame #1194
102102 var my_tick_node = Loop.NextTickNode{
103103 .data = handle,
104 .prev = undefined,
104105 .next = undefined,
105106 };
106107
......@@ -133,6 +134,7 @@ pub const RwLock = struct {
133134 // TODO explicitly put this memory in the coroutine frame #1194
134135 var my_tick_node = Loop.NextTickNode{
135136 .data = handle,
137 .prev = undefined,
136138 .next = undefined,
137139 };
138140
std/index.zig-1
......@@ -6,7 +6,6 @@ pub const Buffer = @import("buffer.zig").Buffer;
66pub const BufferOutStream = @import("buffer.zig").BufferOutStream;
77pub const HashMap = @import("hash_map.zig").HashMap;
88pub const LinkedList = @import("linked_list.zig").LinkedList;
9pub const IntrusiveLinkedList = @import("linked_list.zig").IntrusiveLinkedList;
109pub const SegmentedList = @import("segmented_list.zig").SegmentedList;
1110pub const DynLib = @import("dynamic_library.zig").DynLib;
1211pub const Mutex = @import("mutex.zig").Mutex;
std/linked_list.zig+4-97
......@@ -4,18 +4,8 @@ const assert = debug.assert;
44const mem = std.mem;
55const Allocator = mem.Allocator;
66
7/// Generic non-intrusive doubly linked list.
8pub fn LinkedList(comptime T: type) type {
9 return BaseLinkedList(T, void, "");
10}
11
12/// Generic intrusive doubly linked list.
13pub fn IntrusiveLinkedList(comptime ParentType: type, comptime field_name: []const u8) type {
14 return BaseLinkedList(void, ParentType, field_name);
15}
16
177/// Generic doubly linked list.
18fn BaseLinkedList(comptime T: type, comptime ParentType: type, comptime field_name: []const u8) type {
8pub fn LinkedList(comptime T: type) type {
199 return struct {
2010 const Self = this;
2111
......@@ -25,23 +15,13 @@ fn BaseLinkedList(comptime T: type, comptime ParentType: type, comptime field_na
2515 next: ?*Node,
2616 data: T,
2717
28 pub fn init(value: *const T) Node {
18 pub fn init(data: T) Node {
2919 return Node{
3020 .prev = null,
3121 .next = null,
32 .data = value.*,
22 .data = data,
3323 };
3424 }
35
36 pub fn initIntrusive() Node {
37 // TODO: when #678 is solved this can become `init`.
38 return Node.init({});
39 }
40
41 pub fn toData(node: *Node) *ParentType {
42 comptime assert(isIntrusive());
43 return @fieldParentPtr(ParentType, field_name, node);
44 }
4525 };
4626
4727 first: ?*Node,
......@@ -60,10 +40,6 @@ fn BaseLinkedList(comptime T: type, comptime ParentType: type, comptime field_na
6040 };
6141 }
6242
63 fn isIntrusive() bool {
64 return ParentType != void or field_name.len != 0;
65 }
66
6743 /// Insert a new node after an existing one.
6844 ///
6945 /// Arguments:
......@@ -192,7 +168,6 @@ fn BaseLinkedList(comptime T: type, comptime ParentType: type, comptime field_na
192168 /// Returns:
193169 /// A pointer to the new node.
194170 pub fn allocateNode(list: *Self, allocator: *Allocator) !*Node {
195 comptime assert(!isIntrusive());
196171 return allocator.create(Node(undefined));
197172 }
198173
......@@ -202,7 +177,6 @@ fn BaseLinkedList(comptime T: type, comptime ParentType: type, comptime field_na
202177 /// node: Pointer to the node to deallocate.
203178 /// allocator: Dynamic memory allocator.
204179 pub fn destroyNode(list: *Self, node: *Node, allocator: *Allocator) void {
205 comptime assert(!isIntrusive());
206180 allocator.destroy(node);
207181 }
208182
......@@ -214,8 +188,7 @@ fn BaseLinkedList(comptime T: type, comptime ParentType: type, comptime field_na
214188 ///
215189 /// Returns:
216190 /// A pointer to the new node.
217 pub fn createNode(list: *Self, data: *const T, allocator: *Allocator) !*Node {
218 comptime assert(!isIntrusive());
191 pub fn createNode(list: *Self, data: T, allocator: *Allocator) !*Node {
219192 var node = try list.allocateNode(allocator);
220193 node.* = Node.init(data);
221194 return node;
......@@ -274,69 +247,3 @@ test "basic linked list test" {
274247 assert(list.last.?.data == 4);
275248 assert(list.len == 2);
276249}
277
278const ElementList = IntrusiveLinkedList(Element, "link");
279const Element = struct {
280 value: u32,
281 link: IntrusiveLinkedList(Element, "link").Node,
282};
283
284test "basic intrusive linked list test" {
285 const allocator = debug.global_allocator;
286 var list = ElementList.init();
287
288 var one = Element{
289 .value = 1,
290 .link = ElementList.Node.initIntrusive(),
291 };
292 var two = Element{
293 .value = 2,
294 .link = ElementList.Node.initIntrusive(),
295 };
296 var three = Element{
297 .value = 3,
298 .link = ElementList.Node.initIntrusive(),
299 };
300 var four = Element{
301 .value = 4,
302 .link = ElementList.Node.initIntrusive(),
303 };
304 var five = Element{
305 .value = 5,
306 .link = ElementList.Node.initIntrusive(),
307 };
308
309 list.append(&two.link); // {2}
310 list.append(&five.link); // {2, 5}
311 list.prepend(&one.link); // {1, 2, 5}
312 list.insertBefore(&five.link, &four.link); // {1, 2, 4, 5}
313 list.insertAfter(&two.link, &three.link); // {1, 2, 3, 4, 5}
314
315 // Traverse forwards.
316 {
317 var it = list.first;
318 var index: u32 = 1;
319 while (it) |node| : (it = node.next) {
320 assert(node.toData().value == index);
321 index += 1;
322 }
323 }
324
325 // Traverse backwards.
326 {
327 var it = list.last;
328 var index: u32 = 1;
329 while (it) |node| : (it = node.prev) {
330 assert(node.toData().value == (6 - index));
331 index += 1;
332 }
333 }
334
335 var first = list.popFirst(); // {2, 3, 4, 5}
336 var last = list.pop(); // {2, 3, 4}
337 list.remove(&three.link); // {2, 4}
338
339 assert(list.first.?.toData().value == 2);
340 assert(list.last.?.toData().value == 4);
341 assert(list.len == 2);
342}