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 {...@@ -717,13 +717,13 @@ pub const Compilation = struct {
717 }717 }
718718
719 async fn buildAsync(self: *Compilation) void {719 async fn buildAsync(self: *Compilation) void {
720 while (true) {720 var build_result = await (async self.initialCompile() catch unreachable);
721 // TODO directly awaiting async should guarantee memory allocation elision
722 const build_result = await (async self.compileAndLink() catch unreachable);
723721
722 while (true) {
723 const link_result = if (build_result) self.maybeLink() else |err| err;
724 // this makes a handy error return trace and stack trace in debug mode724 // this makes a handy error return trace and stack trace in debug mode
725 if (std.debug.runtime_safety) {725 if (std.debug.runtime_safety) {
726 build_result catch unreachable;726 link_result catch unreachable;
727 }727 }
728728
729 const compile_errors = blk: {729 const compile_errors = blk: {
...@@ -732,7 +732,7 @@ pub const Compilation = struct {...@@ -732,7 +732,7 @@ pub const Compilation = struct {
732 break :blk held.value.toOwnedSlice();732 break :blk held.value.toOwnedSlice();
733 };733 };
734734
735 if (build_result) |_| {735 if (link_result) |_| {
736 if (compile_errors.len == 0) {736 if (compile_errors.len == 0) {
737 await (async self.events.put(Event.Ok) catch unreachable);737 await (async self.events.put(Event.Ok) catch unreachable);
738 } else {738 } else {
...@@ -745,108 +745,158 @@ pub const Compilation = struct {...@@ -745,108 +745,158 @@ pub const Compilation = struct {
745 await (async self.events.put(Event{ .Error = err }) catch unreachable);745 await (async self.events.put(Event{ .Error = err }) catch unreachable);
746 }746 }
747747
748 // for now we stop after 1748 var group = event.Group(BuildError!void).init(self.loop);
749 return;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);
750 }753 }
751 }754 }
752755
753 async fn compileAndLink(self: *Compilation) !void {756 async fn rebuildFile(self: *Compilation, root_scope: *Scope.Root) !void {
754 if (self.root_src_path) |root_src_path| {757 const tree_scope = blk: {
755 // TODO async/await os.path.real758 const source_code = (await (async fs.readFile(
756 const root_src_real_path = os.path.real(self.gpa(), root_src_path) catch |err| {759 self.loop,
757 try printError("unable to get real path '{}': {}", root_src_path, err);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);
758 return err;764 return err;
759 };765 };
760 const root_scope = blk: {766 errdefer self.gpa().free(source_code);
761 errdefer self.gpa().free(root_src_real_path);
762767
763 const source_code = (await (async fs.readFile(768 const tree = try self.gpa().createOne(ast.Tree);
764 self.loop,769 tree.* = try std.zig.parse(self.gpa(), source_code);
765 root_src_real_path,770 errdefer {
766 max_src_size,771 tree.deinit();
767 ) catch unreachable)) catch |err| {772 self.gpa().destroy(tree);
768 try printError("unable to open '{}': {}", root_src_real_path, err);773 }
769 return err;
770 };
771 errdefer self.gpa().free(source_code);
772774
773 const tree = try self.gpa().createOne(ast.Tree);775 break :blk try Scope.AstTree.create(self, tree, root_scope);
774 tree.* = try std.zig.parse(self.gpa(), source_code);776 };
775 errdefer {777 defer tree_scope.base.deref(self);
776 tree.deinit();
777 self.gpa().destroy(tree);
778 }
779778
780 break :blk try Scope.Root.create(self, tree, root_src_real_path);779 var error_it = tree_scope.tree.errors.iterator(0);
781 };780 while (error_it.next()) |parse_error| {
782 defer root_scope.base.deref(self);781 const msg = try Msg.createFromParseErrorAndScope(self, tree_scope, parse_error);
783 const tree = root_scope.tree;782 errdefer msg.destroy();
784783
785 var error_it = tree.errors.iterator(0);784 try await (async self.addCompileErrorAsync(msg) catch unreachable);
786 while (error_it.next()) |parse_error| {785 }
787 const msg = try Msg.createFromParseErrorAndScope(self, root_scope, parse_error);786 if (tree_scope.tree.errors.len != 0) {
788 errdefer msg.destroy();787 return;
788 }
789789
790 try await (async self.addCompileErrorAsync(msg) catch unreachable);790 const locked_table = await (async root_scope.decls.table.acquireWrite() catch unreachable);
791 }791 defer locked_table.release();
792 if (tree.errors.len != 0) {
793 return;
794 }
795792
796 const decls = try Scope.Decls.create(self, &root_scope.base);793 var decl_group = event.Group(BuildError!void).init(self.loop);
797 defer decls.base.deref(self);794 defer decl_group.deinit();
798795
799 var decl_group = event.Group(BuildError!void).init(self.loop);796 try self.rebuildChangedDecls(
800 var decl_group_consumed = false;797 &decl_group,
801 errdefer if (!decl_group_consumed) decl_group.cancelAll();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 try await (async decl_group.wait() catch unreachable);
804 while (it.next()) |decl_ptr| {805 }
805 const decl = decl_ptr.*;
806 switch (decl.id) {
807 ast.Node.Id.Comptime => {
808 const comptime_node = @fieldParentPtr(ast.Node.Comptime, "base", decl);
809806
810 try self.prelink_group.call(addCompTimeBlock, self, &decls.base, comptime_node);807 async fn rebuildChangedDecls(
811 },808 self: *Compilation,
812 ast.Node.Id.VarDecl => @panic("TODO"),809 group: *event.Group(BuildError!void),
813 ast.Node.Id.FnProto => {810 locked_table: *Decl.Table,
814 const fn_proto = @fieldParentPtr(ast.Node.FnProto, "base", decl);811 decl_scope: *Scope.Decls,
815812 ast_decls: &ast.Node.Root.DeclList,
816 const name = if (fn_proto.name_token) |name_token| tree.tokenSlice(name_token) else {813 tree_scope: *Scope.AstTree,
817 try self.addCompileError(root_scope, Span{814 ) !void {
818 .first = fn_proto.fn_token,815 var existing_decls = try locked_table.clone();
819 .last = fn_proto.fn_token + 1,816 defer existing_decls.deinit();
820 }, "missing function name");817
821 continue;818 var ast_it = ast_decls.iterator(0);
822 };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
824 const fn_decl = try self.gpa().create(Decl.Fn{852 const fn_decl = try self.gpa().create(Decl.Fn{
825 .base = Decl{853 .base = Decl{
826 .id = Decl.Id.Fn,854 .id = Decl.Id.Fn,
827 .name = name,855 .name = name,
828 .visib = parseVisibToken(tree, fn_proto.visib_token),856 .visib = parseVisibToken(tree_scope.tree, fn_proto.visib_token),
829 .resolution = event.Future(BuildError!void).init(self.loop),857 .resolution = event.Future(BuildError!void).init(self.loop),
830 .parent_scope = &decls.base,858 .parent_scope = &decl_scope.base,
831 },859 },
832 .value = Decl.Fn.Val{ .Unresolved = {} },860 .value = Decl.Fn.Val{ .Unresolved = {} },
833 .fn_proto = fn_proto,861 .fn_proto = fn_proto,
834 });862 });
835 errdefer self.gpa().destroy(fn_decl);863 errdefer self.gpa().destroy(fn_decl);
836864
837 try decl_group.call(addTopLevelDecl, self, decls, &fn_decl.base);865 try group.call(addTopLevelDecl, self, &fn_decl.base, locked_table);
838 },866 }
839 ast.Node.Id.TestDecl => @panic("TODO"),867 },
840 else => unreachable,868 ast.Node.Id.TestDecl => @panic("TODO"),
841 }869 else => unreachable,
842 }870 }
843 decl_group_consumed = true;871 }
844 try await (async decl_group.wait() catch unreachable);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.891 break :blk try Scope.Root.create(self, root_src_real_path);
847 decls.name_future.resolve();892 };
893 defer root_scope.base.deref(self);
894
895 try self.rebuildFile(root_scope);
848 }896 }
897 }
849898
899 async fn maybeLink(self: *Compilation) !void {
850 (await (async self.prelink_group.wait() catch unreachable)) catch |err| switch (err) {900 (await (async self.prelink_group.wait() catch unreachable)) catch |err| switch (err) {
851 error.SemanticAnalysisFailed => {},901 error.SemanticAnalysisFailed => {},
852 else => return err,902 else => return err,
...@@ -920,28 +970,20 @@ pub const Compilation = struct {...@@ -920,28 +970,20 @@ pub const Compilation = struct {
920 analyzed_code.destroy(comp.gpa());970 analyzed_code.destroy(comp.gpa());
921 }971 }
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 {
924 const tree = decl.findRootScope().tree;978 const tree = decl.findRootScope().tree;
925 const is_export = decl.isExported(tree);979 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
931 if (is_export) {981 if (is_export) {
932 try self.prelink_group.call(verifyUniqueSymbol, self, decl);982 try self.prelink_group.call(verifyUniqueSymbol, self, decl);
933 try self.prelink_group.call(resolveDecl, self, decl);983 try self.prelink_group.call(resolveDecl, self, decl);
934 }984 }
935985
936 add_to_table_resolved = true;986 if (try locked_table.put(decl.name, decl)) |other_decl| {
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| {
945 try self.addCompileError(decls.base.findRoot(), decl.getSpan(), "redefinition of '{}'", decl.name);987 try self.addCompileError(decls.base.findRoot(), decl.getSpan(), "redefinition of '{}'", decl.name);
946 // TODO note: other definition here988 // TODO note: other definition here
947 }989 }
src-self-hosted/decl.zig+1
...@@ -16,6 +16,7 @@ pub const Decl = struct {...@@ -16,6 +16,7 @@ pub const Decl = struct {
16 visib: Visib,16 visib: Visib,
17 resolution: event.Future(Compilation.BuildError!void),17 resolution: event.Future(Compilation.BuildError!void),
18 parent_scope: *Scope,18 parent_scope: *Scope,
19 tree_scope: *Scope.AstTree,
1920
20 pub const Table = std.HashMap([]const u8, *Decl, mem.hash_slice_u8, mem.eql_slice_u8);21 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 {...@@ -49,7 +49,7 @@ pub const Msg = struct {
49 };49 };
5050
51 const ScopeAndComp = struct {51 const ScopeAndComp = struct {
52 root_scope: *Scope.Root,52 tree_scope: *Scope.AstTree,
53 compilation: *Compilation,53 compilation: *Compilation,
54 };54 };
5555
...@@ -60,7 +60,7 @@ pub const Msg = struct {...@@ -60,7 +60,7 @@ pub const Msg = struct {
60 path_and_tree.allocator.destroy(self);60 path_and_tree.allocator.destroy(self);
61 },61 },
62 Data.ScopeAndComp => |scope_and_comp| {62 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);
64 scope_and_comp.compilation.gpa().free(self.text);64 scope_and_comp.compilation.gpa().free(self.text);
65 scope_and_comp.compilation.gpa().destroy(self);65 scope_and_comp.compilation.gpa().destroy(self);
66 },66 },
...@@ -84,7 +84,7 @@ pub const Msg = struct {...@@ -84,7 +84,7 @@ pub const Msg = struct {
84 return path_and_tree.realpath;84 return path_and_tree.realpath;
85 },85 },
86 Data.ScopeAndComp => |scope_and_comp| {86 Data.ScopeAndComp => |scope_and_comp| {
87 return scope_and_comp.root_scope.realpath;87 return scope_and_comp.tree_scope.root().realpath;
88 },88 },
89 }89 }
90 }90 }
...@@ -95,31 +95,31 @@ pub const Msg = struct {...@@ -95,31 +95,31 @@ pub const Msg = struct {
95 return path_and_tree.tree;95 return path_and_tree.tree;
96 },96 },
97 Data.ScopeAndComp => |scope_and_comp| {97 Data.ScopeAndComp => |scope_and_comp| {
98 return scope_and_comp.root_scope.tree;98 return scope_and_comp.tree_scope.tree;
99 },99 },
100 }100 }
101 }101 }
102102
103 /// Takes ownership of text103 /// Takes ownership of text
104 /// References root_scope, and derefs when the msg is freed104 /// References tree_scope, and derefs when the msg is freed
105 pub fn createFromScope(comp: *Compilation, root_scope: *Scope.Root, span: Span, text: []u8) !*Msg {105 pub fn createFromScope(comp: *Compilation, tree_scope: *Scope.AstTree, span: Span, text: []u8) !*Msg {
106 const msg = try comp.gpa().create(Msg{106 const msg = try comp.gpa().create(Msg{
107 .text = text,107 .text = text,
108 .span = span,108 .span = span,
109 .data = Data{109 .data = Data{
110 .ScopeAndComp = ScopeAndComp{110 .ScopeAndComp = ScopeAndComp{
111 .root_scope = root_scope,111 .tree_scope = tree_scope,
112 .compilation = comp,112 .compilation = comp,
113 },113 },
114 },114 },
115 });115 });
116 root_scope.base.ref();116 tree_scope.base.ref();
117 return msg;117 return msg;
118 }118 }
119119
120 pub fn createFromParseErrorAndScope(120 pub fn createFromParseErrorAndScope(
121 comp: *Compilation,121 comp: *Compilation,
122 root_scope: *Scope.Root,122 tree_scope: *Scope.AstTree,
123 parse_error: *const ast.Error,123 parse_error: *const ast.Error,
124 ) !*Msg {124 ) !*Msg {
125 const loc_token = parse_error.loc();125 const loc_token = parse_error.loc();
...@@ -127,7 +127,7 @@ pub const Msg = struct {...@@ -127,7 +127,7 @@ pub const Msg = struct {
127 defer text_buf.deinit();127 defer text_buf.deinit();
128128
129 var out_stream = &std.io.BufferOutStream.init(&text_buf).stream;129 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
132 const msg = try comp.gpa().create(Msg{132 const msg = try comp.gpa().create(Msg{
133 .text = undefined,133 .text = undefined,
...@@ -137,12 +137,12 @@ pub const Msg = struct {...@@ -137,12 +137,12 @@ pub const Msg = struct {
137 },137 },
138 .data = Data{138 .data = Data{
139 .ScopeAndComp = ScopeAndComp{139 .ScopeAndComp = ScopeAndComp{
140 .root_scope = root_scope,140 .tree_scope = tree_scope,
141 .compilation = comp,141 .compilation = comp,
142 },142 },
143 },143 },
144 });144 });
145 root_scope.base.ref();145 tree_scope.base.ref();
146 msg.text = text_buf.toOwnedSlice();146 msg.text = text_buf.toOwnedSlice();
147 return msg;147 return msg;
148 }148 }
src-self-hosted/ir.zig+3-2
...@@ -1929,8 +1929,9 @@ pub const Builder = struct {...@@ -1929,8 +1929,9 @@ pub const Builder = struct {
1929 Scope.Id.Root => return Ident.NotFound,1929 Scope.Id.Root => return Ident.NotFound,
1930 Scope.Id.Decls => {1930 Scope.Id.Decls => {
1931 const decls = @fieldParentPtr(Scope.Decls, "base", s);1931 const decls = @fieldParentPtr(Scope.Decls, "base", s);
1932 const table = await (async decls.getTableReadOnly() catch unreachable);1932 const locked_table = await (async decls.table.acquireRead() catch unreachable);
1933 if (table.get(name)) |entry| {1933 defer locked_table.release();
1934 if (locked_table.value.get(name)) |entry| {
1934 return Ident{ .Decl = entry.value };1935 return Ident{ .Decl = entry.value };
1935 }1936 }
1936 },1937 },
src-self-hosted/libc_installation.zig+2-4
...@@ -143,7 +143,7 @@ pub const LibCInstallation = struct {...@@ -143,7 +143,7 @@ pub const LibCInstallation = struct {
143 pub async fn findNative(self: *LibCInstallation, loop: *event.Loop) !void {143 pub async fn findNative(self: *LibCInstallation, loop: *event.Loop) !void {
144 self.initEmpty();144 self.initEmpty();
145 var group = event.Group(FindError!void).init(loop);145 var group = event.Group(FindError!void).init(loop);
146 errdefer group.cancelAll();146 errdefer group.deinit();
147 var windows_sdk: ?*c.ZigWindowsSDK = null;147 var windows_sdk: ?*c.ZigWindowsSDK = null;
148 errdefer if (windows_sdk) |sdk| c.zig_free_windows_sdk(@ptrCast(?[*]c.ZigWindowsSDK, sdk));148 errdefer if (windows_sdk) |sdk| c.zig_free_windows_sdk(@ptrCast(?[*]c.ZigWindowsSDK, sdk));
149149
...@@ -313,7 +313,7 @@ pub const LibCInstallation = struct {...@@ -313,7 +313,7 @@ pub const LibCInstallation = struct {
313 },313 },
314 };314 };
315 var group = event.Group(FindError!void).init(loop);315 var group = event.Group(FindError!void).init(loop);
316 errdefer group.cancelAll();316 errdefer group.deinit();
317 for (dyn_tests) |*dyn_test| {317 for (dyn_tests) |*dyn_test| {
318 try group.call(testNativeDynamicLinker, self, loop, dyn_test);318 try group.call(testNativeDynamicLinker, self, loop, dyn_test);
319 }319 }
...@@ -341,7 +341,6 @@ pub const LibCInstallation = struct {...@@ -341,7 +341,6 @@ pub const LibCInstallation = struct {
341 }341 }
342 }342 }
343343
344
345 async fn findNativeKernel32LibDir(self: *LibCInstallation, loop: *event.Loop, sdk: *c.ZigWindowsSDK) FindError!void {344 async fn findNativeKernel32LibDir(self: *LibCInstallation, loop: *event.Loop, sdk: *c.ZigWindowsSDK) FindError!void {
346 var search_buf: [2]Search = undefined;345 var search_buf: [2]Search = undefined;
347 const searches = fillSearch(&search_buf, sdk);346 const searches = fillSearch(&search_buf, sdk);
...@@ -450,7 +449,6 @@ fn fillSearch(search_buf: *[2]Search, sdk: *c.ZigWindowsSDK) []Search {...@@ -450,7 +449,6 @@ fn fillSearch(search_buf: *[2]Search, sdk: *c.ZigWindowsSDK) []Search {
450 return search_buf[0..search_end];449 return search_buf[0..search_end];
451}450}
452451
453
454fn fileExists(allocator: *std.mem.Allocator, path: []const u8) !bool {452fn fileExists(allocator: *std.mem.Allocator, path: []const u8) !bool {
455 if (std.os.File.access(allocator, path)) |_| {453 if (std.os.File.access(allocator, path)) |_| {
456 return true;454 return true;
src-self-hosted/main.zig+32-33
...@@ -71,26 +71,26 @@ pub fn main() !void {...@@ -71,26 +71,26 @@ pub fn main() !void {
71 }71 }
7272
73 const commands = []Command{73 const commands = []Command{
74 Command{74 //Command{
75 .name = "build-exe",75 // .name = "build-exe",
76 .exec = cmdBuildExe,76 // .exec = cmdBuildExe,
77 },77 //},
78 Command{78 //Command{
79 .name = "build-lib",79 // .name = "build-lib",
80 .exec = cmdBuildLib,80 // .exec = cmdBuildLib,
81 },81 //},
82 Command{82 //Command{
83 .name = "build-obj",83 // .name = "build-obj",
84 .exec = cmdBuildObj,84 // .exec = cmdBuildObj,
85 },85 //},
86 Command{86 Command{
87 .name = "fmt",87 .name = "fmt",
88 .exec = cmdFmt,88 .exec = cmdFmt,
89 },89 },
90 Command{90 //Command{
91 .name = "libc",91 // .name = "libc",
92 .exec = cmdLibC,92 // .exec = cmdLibC,
93 },93 //},
94 Command{94 Command{
95 .name = "targets",95 .name = "targets",
96 .exec = cmdTargets,96 .exec = cmdTargets,
...@@ -472,23 +472,22 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Co...@@ -472,23 +472,22 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Co
472}472}
473473
474async fn processBuildEvents(comp: *Compilation, color: errmsg.Color) void {474async fn processBuildEvents(comp: *Compilation, color: errmsg.Color) void {
475 // TODO directly awaiting async should guarantee memory allocation elision475 while (true) {
476 const build_event = await (async comp.events.get() catch unreachable);476 // TODO directly awaiting async should guarantee memory allocation elision
477477 const build_event = await (async comp.events.get() catch unreachable);
478 switch (build_event) {478
479 Compilation.Event.Ok => {479 switch (build_event) {
480 return;480 Compilation.Event.Ok => {},
481 },481 Compilation.Event.Error => |err| {
482 Compilation.Event.Error => |err| {482 stderr.print("build failed: {}\n", @errorName(err)) catch os.exit(1);
483 std.debug.warn("build failed: {}\n", @errorName(err));483 },
484 os.exit(1);484 Compilation.Event.Fail => |msgs| {
485 },485 for (msgs) |msg| {
486 Compilation.Event.Fail => |msgs| {486 defer msg.destroy();
487 for (msgs) |msg| {487 msg.printToFile(&stderr_file, color) catch os.exit(1);
488 defer msg.destroy();488 }
489 msg.printToFile(&stderr_file, color) catch os.exit(1);489 },
490 }490 }
491 },
492 }491 }
493}492}
494493
src-self-hosted/scope.zig+40-20
...@@ -36,6 +36,7 @@ pub const Scope = struct {...@@ -36,6 +36,7 @@ pub const Scope = struct {
36 Id.Defer => @fieldParentPtr(Defer, "base", base).destroy(comp),36 Id.Defer => @fieldParentPtr(Defer, "base", base).destroy(comp),
37 Id.DeferExpr => @fieldParentPtr(DeferExpr, "base", base).destroy(comp),37 Id.DeferExpr => @fieldParentPtr(DeferExpr, "base", base).destroy(comp),
38 Id.Var => @fieldParentPtr(Var, "base", base).destroy(comp),38 Id.Var => @fieldParentPtr(Var, "base", base).destroy(comp),
39 Id.AstTree => @fieldParentPtr(AstTree, "base", base).destroy(comp),
39 }40 }
40 }41 }
41 }42 }
...@@ -97,6 +98,7 @@ pub const Scope = struct {...@@ -97,6 +98,7 @@ pub const Scope = struct {
9798
98 pub const Id = enum {99 pub const Id = enum {
99 Root,100 Root,
101 AstTree,
100 Decls,102 Decls,
101 Block,103 Block,
102 FnDef,104 FnDef,
...@@ -108,13 +110,12 @@ pub const Scope = struct {...@@ -108,13 +110,12 @@ pub const Scope = struct {
108110
109 pub const Root = struct {111 pub const Root = struct {
110 base: Scope,112 base: Scope,
111 tree: *ast.Tree,
112 realpath: []const u8,113 realpath: []const u8,
114 decls: *Decls,
113115
114 /// Creates a Root scope with 1 reference116 /// Creates a Root scope with 1 reference
115 /// Takes ownership of realpath117 /// Takes ownership of realpath
116 /// Takes ownership of tree, will deinit and destroy when done.118 pub fn create(comp: *Compilation, realpath: []u8) !*Root {
117 pub fn create(comp: *Compilation, tree: *ast.Tree, realpath: []u8) !*Root {
118 const self = try comp.gpa().createOne(Root);119 const self = try comp.gpa().createOne(Root);
119 self.* = Root{120 self.* = Root{
120 .base = Scope{121 .base = Scope{
...@@ -122,40 +123,64 @@ pub const Scope = struct {...@@ -122,40 +123,64 @@ pub const Scope = struct {
122 .parent = null,123 .parent = null,
123 .ref_count = std.atomic.Int(usize).init(1),124 .ref_count = std.atomic.Int(usize).init(1),
124 },125 },
125 .tree = tree,
126 .realpath = realpath,126 .realpath = realpath,
127 .decls = undefined,
127 };128 };
128129 errdefer comp.gpa().destroy(self);
130 self.decls = try Decls.create(comp, &self.base);
129 return self;131 return self;
130 }132 }
131133
132 pub fn destroy(self: *Root, comp: *Compilation) void {134 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 {
133 comp.gpa().free(self.tree.source);159 comp.gpa().free(self.tree.source);
134 self.tree.deinit();160 self.tree.deinit();
135 comp.gpa().destroy(self.tree);161 comp.gpa().destroy(self.tree);
136 comp.gpa().free(self.realpath);
137 comp.gpa().destroy(self);162 comp.gpa().destroy(self);
138 }163 }
164
165 pub fn root(self: *AstTree) *Root {
166 return self.base.findRoot();
167 }
139 };168 };
140169
141 pub const Decls = struct {170 pub const Decls = struct {
142 base: Scope,171 base: Scope,
143172
144 /// The lock must be respected for writing. However once name_future resolves,173 /// This table remains Write Locked when the names are incomplete or possibly outdated.
145 /// readers can freely access it.174 /// So if a reader manages to grab a lock, it can be sure that the set of names is complete
146 table: event.Locked(Decl.Table),175 /// and correct.
147176 table: event.RwLocked(Decl.Table),
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),
152177
153 /// Creates a Decls scope with 1 reference178 /// Creates a Decls scope with 1 reference
154 pub fn create(comp: *Compilation, parent: *Scope) !*Decls {179 pub fn create(comp: *Compilation, parent: *Scope) !*Decls {
155 const self = try comp.gpa().createOne(Decls);180 const self = try comp.gpa().createOne(Decls);
156 self.* = Decls{181 self.* = Decls{
157 .base = undefined,182 .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())),
159 .name_future = event.Future(void).init(comp.loop),184 .name_future = event.Future(void).init(comp.loop),
160 };185 };
161 self.base.init(Id.Decls, parent);186 self.base.init(Id.Decls, parent);
...@@ -166,11 +191,6 @@ pub const Scope = struct {...@@ -166,11 +191,6 @@ pub const Scope = struct {
166 self.table.deinit();191 self.table.deinit();
167 comp.gpa().destroy(self);192 comp.gpa().destroy(self);
168 }193 }
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 }
174 };194 };
175195
176 pub const Block = struct {196 pub const Block = struct {
src-self-hosted/test.zig+1-1
...@@ -50,7 +50,7 @@ pub const TestContext = struct {...@@ -50,7 +50,7 @@ pub const TestContext = struct {
50 errdefer self.event_loop_local.deinit();50 errdefer self.event_loop_local.deinit();
5151
52 self.group = std.event.Group(error!void).init(&self.loop);52 self.group = std.event.Group(error!void).init(&self.loop);
53 errdefer self.group.cancelAll();53 errdefer self.group.deinit();
5454
55 self.zig_lib_dir = try introspect.resolveZigLibDir(allocator);55 self.zig_lib_dir = try introspect.resolveZigLibDir(allocator);
56 errdefer allocator.free(self.zig_lib_dir);56 errdefer allocator.free(self.zig_lib_dir);
std/atomic/queue.zig+60-24
...@@ -1,40 +1,38 @@...@@ -1,40 +1,38 @@
1const std = @import("../index.zig");
1const builtin = @import("builtin");2const builtin = @import("builtin");
2const AtomicOrder = builtin.AtomicOrder;3const AtomicOrder = builtin.AtomicOrder;
3const AtomicRmwOp = builtin.AtomicRmwOp;4const AtomicRmwOp = builtin.AtomicRmwOp;
5const assert = std.debug.assert;
46
5/// Many producer, many consumer, non-allocating, thread-safe.7/// Many producer, many consumer, non-allocating, thread-safe.
6/// Uses a spinlock to protect get() and put().8/// Uses a mutex to protect access.
7pub fn Queue(comptime T: type) type {9pub fn Queue(comptime T: type) type {
8 return struct {10 return struct {
9 head: ?*Node,11 head: ?*Node,
10 tail: ?*Node,12 tail: ?*Node,
11 lock: u8,13 mutex: std.Mutex,
1214
13 pub const Self = this;15 pub const Self = this;
1416 pub const Node = std.LinkedList(T).Node;
15 pub const Node = struct {
16 next: ?*Node,
17 data: T,
18 };
1917
20 pub fn init() Self {18 pub fn init() Self {
21 return Self{19 return Self{
22 .head = null,20 .head = null,
23 .tail = null,21 .tail = null,
24 .lock = 0,22 .mutex = std.Mutex.init(),
25 };23 };
26 }24 }
2725
28 pub fn put(self: *Self, node: *Node) void {26 pub fn put(self: *Self, node: *Node) void {
29 node.next = null;27 node.next = null;
3028
31 while (@atomicRmw(u8, &self.lock, builtin.AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst) != 0) {}29 const held = self.mutex.acquire();
32 defer assert(@atomicRmw(u8, &self.lock, builtin.AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst) == 1);30 defer held.release();
3331
34 const opt_tail = self.tail;32 node.prev = self.tail;
35 self.tail = node;33 self.tail = node;
36 if (opt_tail) |tail| {34 if (node.prev) |prev_tail| {
37 tail.next = node;35 prev_tail.next = node;
38 } else {36 } else {
39 assert(self.head == null);37 assert(self.head == null);
40 self.head = node;38 self.head = node;
...@@ -42,18 +40,27 @@ pub fn Queue(comptime T: type) type {...@@ -42,18 +40,27 @@ pub fn Queue(comptime T: type) type {
42 }40 }
4341
44 pub fn get(self: *Self) ?*Node {42 pub fn get(self: *Self) ?*Node {
45 while (@atomicRmw(u8, &self.lock, builtin.AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst) != 0) {}43 const held = self.mutex.acquire();
46 defer assert(@atomicRmw(u8, &self.lock, builtin.AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst) == 1);44 defer held.release();
4745
48 const head = self.head orelse return null;46 const head = self.head orelse return null;
49 self.head = head.next;47 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;
51 return head;56 return head;
52 }57 }
5358
54 pub fn unget(self: *Self, node: *Node) void {59 pub fn unget(self: *Self, node: *Node) void {
55 while (@atomicRmw(u8, &self.lock, builtin.AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst) != 0) {}60 node.prev = null;
56 defer assert(@atomicRmw(u8, &self.lock, builtin.AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst) == 1);61
62 const held = self.mutex.acquire();
63 defer held.release();
5764
58 const opt_head = self.head;65 const opt_head = self.head;
59 self.head = node;66 self.head = node;
...@@ -65,13 +72,39 @@ pub fn Queue(comptime T: type) type {...@@ -65,13 +72,39 @@ pub fn Queue(comptime T: type) type {
65 }72 }
66 }73 }
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
68 pub fn isEmpty(self: *Self) bool {99 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;
70 }103 }
71104
72 pub fn dump(self: *Self) void {105 pub fn dump(self: *Self) void {
73 while (@atomicRmw(u8, &self.lock, builtin.AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst) != 0) {}106 const held = self.mutex.acquire();
74 defer assert(@atomicRmw(u8, &self.lock, builtin.AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst) == 1);107 defer held.release();
75108
76 std.debug.warn("head: ");109 std.debug.warn("head: ");
77 dumpRecursive(self.head, 0);110 dumpRecursive(self.head, 0);
...@@ -93,9 +126,6 @@ pub fn Queue(comptime T: type) type {...@@ -93,9 +126,6 @@ pub fn Queue(comptime T: type) type {
93 };126 };
94}127}
95128
96const std = @import("../index.zig");
97const assert = std.debug.assert;
98
99const Context = struct {129const Context = struct {
100 allocator: *std.mem.Allocator,130 allocator: *std.mem.Allocator,
101 queue: *Queue(i32),131 queue: *Queue(i32),
...@@ -169,6 +199,7 @@ fn startPuts(ctx: *Context) u8 {...@@ -169,6 +199,7 @@ fn startPuts(ctx: *Context) u8 {
169 std.os.time.sleep(0, 1); // let the os scheduler be our fuzz199 std.os.time.sleep(0, 1); // let the os scheduler be our fuzz
170 const x = @bitCast(i32, r.random.scalar(u32));200 const x = @bitCast(i32, r.random.scalar(u32));
171 const node = ctx.allocator.create(Queue(i32).Node{201 const node = ctx.allocator.create(Queue(i32).Node{
202 .prev = undefined,
172 .next = undefined,203 .next = undefined,
173 .data = x,204 .data = x,
174 }) catch unreachable;205 }) catch unreachable;
...@@ -198,12 +229,14 @@ test "std.atomic.Queue single-threaded" {...@@ -198,12 +229,14 @@ test "std.atomic.Queue single-threaded" {
198 var node_0 = Queue(i32).Node{229 var node_0 = Queue(i32).Node{
199 .data = 0,230 .data = 0,
200 .next = undefined,231 .next = undefined,
232 .prev = undefined,
201 };233 };
202 queue.put(&node_0);234 queue.put(&node_0);
203235
204 var node_1 = Queue(i32).Node{236 var node_1 = Queue(i32).Node{
205 .data = 1,237 .data = 1,
206 .next = undefined,238 .next = undefined,
239 .prev = undefined,
207 };240 };
208 queue.put(&node_1);241 queue.put(&node_1);
209242
...@@ -212,12 +245,14 @@ test "std.atomic.Queue single-threaded" {...@@ -212,12 +245,14 @@ test "std.atomic.Queue single-threaded" {
212 var node_2 = Queue(i32).Node{245 var node_2 = Queue(i32).Node{
213 .data = 2,246 .data = 2,
214 .next = undefined,247 .next = undefined,
248 .prev = undefined,
215 };249 };
216 queue.put(&node_2);250 queue.put(&node_2);
217251
218 var node_3 = Queue(i32).Node{252 var node_3 = Queue(i32).Node{
219 .data = 3,253 .data = 3,
220 .next = undefined,254 .next = undefined,
255 .prev = undefined,
221 };256 };
222 queue.put(&node_3);257 queue.put(&node_3);
223258
...@@ -228,6 +263,7 @@ test "std.atomic.Queue single-threaded" {...@@ -228,6 +263,7 @@ test "std.atomic.Queue single-threaded" {
228 var node_4 = Queue(i32).Node{263 var node_4 = Queue(i32).Node{
229 .data = 4,264 .data = 4,
230 .next = undefined,265 .next = undefined,
266 .prev = undefined,
231 };267 };
232 queue.put(&node_4);268 queue.put(&node_4);
233269
std/event.zig+1-1
...@@ -3,7 +3,7 @@ pub const Future = @import("event/future.zig").Future;...@@ -3,7 +3,7 @@ pub const Future = @import("event/future.zig").Future;
3pub const Group = @import("event/group.zig").Group;3pub const Group = @import("event/group.zig").Group;
4pub const Lock = @import("event/lock.zig").Lock;4pub const Lock = @import("event/lock.zig").Lock;
5pub const Locked = @import("event/locked.zig").Locked;5pub const Locked = @import("event/locked.zig").Locked;
6pub const RwLock = @import("event/rwlock.zig").Lock;6pub const RwLock = @import("event/rwlock.zig").RwLock;
7pub const RwLocked = @import("event/rwlocked.zig").RwLocked;7pub const RwLocked = @import("event/rwlocked.zig").RwLocked;
8pub const Loop = @import("event/loop.zig").Loop;8pub const Loop = @import("event/loop.zig").Loop;
9pub const fs = @import("event/fs.zig");9pub const fs = @import("event/fs.zig");
std/event/channel.zig+168-29
...@@ -5,7 +5,7 @@ const AtomicRmwOp = builtin.AtomicRmwOp;...@@ -5,7 +5,7 @@ const AtomicRmwOp = builtin.AtomicRmwOp;
5const AtomicOrder = builtin.AtomicOrder;5const AtomicOrder = builtin.AtomicOrder;
6const Loop = std.event.Loop;6const Loop = std.event.Loop;
77
8/// many producer, many consumer, thread-safe, lock-free, runtime configurable buffer size8/// many producer, many consumer, thread-safe, runtime configurable buffer size
9/// when buffer is empty, consumers suspend and are resumed by producers9/// when buffer is empty, consumers suspend and are resumed by producers
10/// when buffer is full, producers suspend and are resumed by consumers10/// when buffer is full, producers suspend and are resumed by consumers
11pub fn Channel(comptime T: type) type {11pub fn Channel(comptime T: type) type {
...@@ -13,6 +13,7 @@ pub fn Channel(comptime T: type) type {...@@ -13,6 +13,7 @@ pub fn Channel(comptime T: type) type {
13 loop: *Loop,13 loop: *Loop,
1414
15 getters: std.atomic.Queue(GetNode),15 getters: std.atomic.Queue(GetNode),
16 or_null_queue: std.atomic.Queue(*std.atomic.Queue(GetNode).Node),
16 putters: std.atomic.Queue(PutNode),17 putters: std.atomic.Queue(PutNode),
17 get_count: usize,18 get_count: usize,
18 put_count: usize,19 put_count: usize,
...@@ -26,8 +27,22 @@ pub fn Channel(comptime T: type) type {...@@ -26,8 +27,22 @@ pub fn Channel(comptime T: type) type {
2627
27 const SelfChannel = this;28 const SelfChannel = this;
28 const GetNode = struct {29 const GetNode = struct {
29 ptr: *T,
30 tick_node: *Loop.NextTickNode,30 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 };
31 };46 };
32 const PutNode = struct {47 const PutNode = struct {
33 data: T,48 data: T,
...@@ -48,6 +63,7 @@ pub fn Channel(comptime T: type) type {...@@ -48,6 +63,7 @@ pub fn Channel(comptime T: type) type {
48 .need_dispatch = 0,63 .need_dispatch = 0,
49 .getters = std.atomic.Queue(GetNode).init(),64 .getters = std.atomic.Queue(GetNode).init(),
50 .putters = std.atomic.Queue(PutNode).init(),65 .putters = std.atomic.Queue(PutNode).init(),
66 .or_null_queue = std.atomic.Queue(*std.atomic.Queue(GetNode).Node).init(),
51 .get_count = 0,67 .get_count = 0,
52 .put_count = 0,68 .put_count = 0,
53 });69 });
...@@ -71,18 +87,31 @@ pub fn Channel(comptime T: type) type {...@@ -71,18 +87,31 @@ pub fn Channel(comptime T: type) type {
71 /// puts a data item in the channel. The promise completes when the value has been added to the87 /// puts a data item in the channel. The promise completes when the value has been added to the
72 /// buffer, or in the case of a zero size buffer, when the item has been retrieved by a getter.88 /// buffer, or in the case of a zero size buffer, when the item has been retrieved by a getter.
73 pub async fn put(self: *SelfChannel, data: T) void {89 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 }
74 suspend |handle| {114 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 };
86 self.putters.put(&queue_node);115 self.putters.put(&queue_node);
87 _ = @atomicRmw(usize, &self.put_count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst);116 _ = @atomicRmw(usize, &self.put_count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst);
88117
...@@ -93,21 +122,37 @@ pub fn Channel(comptime T: type) type {...@@ -93,21 +122,37 @@ pub fn Channel(comptime T: type) type {
93 /// await this function to get an item from the channel. If the buffer is empty, the promise will122 /// await this function to get an item from the channel. If the buffer is empty, the promise will
94 /// complete when the next item is put in the channel.123 /// complete when the next item is put in the channel.
95 pub async fn get(self: *SelfChannel) T {124 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
96 // TODO integrate this function with named return values132 // TODO integrate this function with named return values
97 // so we can get rid of this extra result copy133 // so we can get rid of this extra result copy
98 var result: T = undefined;134 var result: T = undefined;
99 suspend |handle| {135 var my_tick_node = Loop.NextTickNode.init(my_handle);
100 var my_tick_node = Loop.NextTickNode{136 var queue_node = std.atomic.Queue(GetNode).Node.init(GetNode{
101 .next = undefined,137 .tick_node = &my_tick_node,
102 .data = handle,138 .data = GetNode.Data{
103 };139 .Normal = GetNode.Normal{ .ptr = &result },
104 var queue_node = std.atomic.Queue(GetNode).Node{140 },
105 .data = GetNode{141 });
106 .ptr = &result,142
107 .tick_node = &my_tick_node,143 // TODO test canceling a get()
108 },144 errdefer {
109 .next = undefined,145 _ = @atomicRmw(usize, &self.get_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst);
110 };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 |_| {
111 self.getters.put(&queue_node);156 self.getters.put(&queue_node);
112 _ = @atomicRmw(usize, &self.get_count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst);157 _ = @atomicRmw(usize, &self.get_count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst);
113158
...@@ -116,8 +161,64 @@ pub fn Channel(comptime T: type) type {...@@ -116,8 +161,64 @@ pub fn Channel(comptime T: type) type {
116 return result;161 return result;
117 }162 }
118163
119 fn getOrNull(self: *SelfChannel) ?T {164 //pub async fn select(comptime EnumUnion: type, channels: ...) EnumUnion {
120 TODO();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;
121 }222 }
122223
123 fn dispatch(self: *SelfChannel) void {224 fn dispatch(self: *SelfChannel) void {
...@@ -143,7 +244,15 @@ pub fn Channel(comptime T: type) type {...@@ -143,7 +244,15 @@ pub fn Channel(comptime T: type) type {
143 if (get_count == 0) break :one_dispatch;244 if (get_count == 0) break :one_dispatch;
144245
145 const get_node = &self.getters.get().?.data;246 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 }
147 self.loop.onNextTick(get_node.tick_node);256 self.loop.onNextTick(get_node.tick_node);
148 self.buffer_len -= 1;257 self.buffer_len -= 1;
149258
...@@ -155,7 +264,15 @@ pub fn Channel(comptime T: type) type {...@@ -155,7 +264,15 @@ pub fn Channel(comptime T: type) type {
155 const get_node = &self.getters.get().?.data;264 const get_node = &self.getters.get().?.data;
156 const put_node = &self.putters.get().?.data;265 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 }
159 self.loop.onNextTick(get_node.tick_node);276 self.loop.onNextTick(get_node.tick_node);
160 self.loop.onNextTick(put_node.tick_node);277 self.loop.onNextTick(put_node.tick_node);
161278
...@@ -180,6 +297,16 @@ pub fn Channel(comptime T: type) type {...@@ -180,6 +297,16 @@ pub fn Channel(comptime T: type) type {
180 _ = @atomicRmw(usize, &self.get_count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst);297 _ = @atomicRmw(usize, &self.get_count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst);
181 _ = @atomicRmw(usize, &self.put_count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst);298 _ = @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
183 // clear need-dispatch flag310 // clear need-dispatch flag
184 const need_dispatch = @atomicRmw(u8, &self.need_dispatch, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst);311 const need_dispatch = @atomicRmw(u8, &self.need_dispatch, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst);
185 if (need_dispatch != 0) continue;312 if (need_dispatch != 0) continue;
...@@ -230,6 +357,15 @@ async fn testChannelGetter(loop: *Loop, channel: *Channel(i32)) void {...@@ -230,6 +357,15 @@ async fn testChannelGetter(loop: *Loop, channel: *Channel(i32)) void {
230 const value2_promise = try async channel.get();357 const value2_promise = try async channel.get();
231 const value2 = await value2_promise;358 const value2 = await value2_promise;
232 assert(value2 == 4567);359 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;
233}369}
234370
235async fn testChannelPutter(channel: *Channel(i32)) void {371async fn testChannelPutter(channel: *Channel(i32)) void {
...@@ -237,3 +373,6 @@ async fn testChannelPutter(channel: *Channel(i32)) void {...@@ -237,3 +373,6 @@ async fn testChannelPutter(channel: *Channel(i32)) void {
237 await (async channel.put(4567) catch @panic("out of memory"));373 await (async channel.put(4567) catch @panic("out of memory"));
238}374}
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:...@@ -99,6 +99,7 @@ pub async fn pwritev(loop: *event.Loop, fd: os.FileHandle, offset: usize, data:
99 }99 }
100100
101 var req_node = RequestNode{101 var req_node = RequestNode{
102 .prev = undefined,
102 .next = undefined,103 .next = undefined,
103 .data = Request{104 .data = Request{
104 .msg = Request.Msg{105 .msg = Request.Msg{
...@@ -111,6 +112,7 @@ pub async fn pwritev(loop: *event.Loop, fd: os.FileHandle, offset: usize, data:...@@ -111,6 +112,7 @@ pub async fn pwritev(loop: *event.Loop, fd: os.FileHandle, offset: usize, data:
111 },112 },
112 .finish = Request.Finish{113 .finish = Request.Finish{
113 .TickNode = event.Loop.NextTickNode{114 .TickNode = event.Loop.NextTickNode{
115 .prev = undefined,
114 .next = undefined,116 .next = undefined,
115 .data = my_handle,117 .data = my_handle,
116 },118 },
...@@ -148,6 +150,7 @@ pub async fn preadv(loop: *event.Loop, fd: os.FileHandle, offset: usize, data: [...@@ -148,6 +150,7 @@ pub async fn preadv(loop: *event.Loop, fd: os.FileHandle, offset: usize, data: [
148 }150 }
149151
150 var req_node = RequestNode{152 var req_node = RequestNode{
153 .prev = undefined,
151 .next = undefined,154 .next = undefined,
152 .data = Request{155 .data = Request{
153 .msg = Request.Msg{156 .msg = Request.Msg{
...@@ -160,6 +163,7 @@ pub async fn preadv(loop: *event.Loop, fd: os.FileHandle, offset: usize, data: [...@@ -160,6 +163,7 @@ pub async fn preadv(loop: *event.Loop, fd: os.FileHandle, offset: usize, data: [
160 },163 },
161 .finish = Request.Finish{164 .finish = Request.Finish{
162 .TickNode = event.Loop.NextTickNode{165 .TickNode = event.Loop.NextTickNode{
166 .prev = undefined,
163 .next = undefined,167 .next = undefined,
164 .data = my_handle,168 .data = my_handle,
165 },169 },
...@@ -186,6 +190,7 @@ pub async fn openRead(loop: *event.Loop, path: []const u8) os.File.OpenError!os....@@ -186,6 +190,7 @@ pub async fn openRead(loop: *event.Loop, path: []const u8) os.File.OpenError!os.
186 defer loop.allocator.free(path_with_null);190 defer loop.allocator.free(path_with_null);
187191
188 var req_node = RequestNode{192 var req_node = RequestNode{
193 .prev = undefined,
189 .next = undefined,194 .next = undefined,
190 .data = Request{195 .data = Request{
191 .msg = Request.Msg{196 .msg = Request.Msg{
...@@ -196,6 +201,7 @@ pub async fn openRead(loop: *event.Loop, path: []const u8) os.File.OpenError!os....@@ -196,6 +201,7 @@ pub async fn openRead(loop: *event.Loop, path: []const u8) os.File.OpenError!os.
196 },201 },
197 .finish = Request.Finish{202 .finish = Request.Finish{
198 .TickNode = event.Loop.NextTickNode{203 .TickNode = event.Loop.NextTickNode{
204 .prev = undefined,
199 .next = undefined,205 .next = undefined,
200 .data = my_handle,206 .data = my_handle,
201 },207 },
...@@ -227,6 +233,7 @@ pub async fn openReadWrite(...@@ -227,6 +233,7 @@ pub async fn openReadWrite(
227 defer loop.allocator.free(path_with_null);233 defer loop.allocator.free(path_with_null);
228234
229 var req_node = RequestNode{235 var req_node = RequestNode{
236 .prev = undefined,
230 .next = undefined,237 .next = undefined,
231 .data = Request{238 .data = Request{
232 .msg = Request.Msg{239 .msg = Request.Msg{
...@@ -238,6 +245,7 @@ pub async fn openReadWrite(...@@ -238,6 +245,7 @@ pub async fn openReadWrite(
238 },245 },
239 .finish = Request.Finish{246 .finish = Request.Finish{
240 .TickNode = event.Loop.NextTickNode{247 .TickNode = event.Loop.NextTickNode{
248 .prev = undefined,
241 .next = undefined,249 .next = undefined,
242 .data = my_handle,250 .data = my_handle,
243 },251 },
...@@ -267,6 +275,7 @@ pub const CloseOperation = struct {...@@ -267,6 +275,7 @@ pub const CloseOperation = struct {
267 .loop = loop,275 .loop = loop,
268 .have_fd = false,276 .have_fd = false,
269 .close_req_node = RequestNode{277 .close_req_node = RequestNode{
278 .prev = undefined,
270 .next = undefined,279 .next = undefined,
271 .data = Request{280 .data = Request{
272 .msg = Request.Msg{281 .msg = Request.Msg{
...@@ -312,6 +321,7 @@ pub async fn writeFileMode(loop: *event.Loop, path: []const u8, contents: []cons...@@ -312,6 +321,7 @@ pub async fn writeFileMode(loop: *event.Loop, path: []const u8, contents: []cons
312 defer loop.allocator.free(path_with_null);321 defer loop.allocator.free(path_with_null);
313322
314 var req_node = RequestNode{323 var req_node = RequestNode{
324 .prev = undefined,
315 .next = undefined,325 .next = undefined,
316 .data = Request{326 .data = Request{
317 .msg = Request.Msg{327 .msg = Request.Msg{
...@@ -324,6 +334,7 @@ pub async fn writeFileMode(loop: *event.Loop, path: []const u8, contents: []cons...@@ -324,6 +334,7 @@ pub async fn writeFileMode(loop: *event.Loop, path: []const u8, contents: []cons
324 },334 },
325 .finish = Request.Finish{335 .finish = Request.Finish{
326 .TickNode = event.Loop.NextTickNode{336 .TickNode = event.Loop.NextTickNode{
337 .prev = undefined,
327 .next = undefined,338 .next = undefined,
328 .data = my_handle,339 .data = my_handle,
329 },340 },
std/event/lock.zig+12-6
...@@ -91,13 +91,16 @@ pub const Lock = struct {...@@ -91,13 +91,16 @@ pub const Lock = struct {
91 }91 }
9292
93 pub async fn acquire(self: *Lock) Held {93 pub async fn acquire(self: *Lock) Held {
94 suspend |handle| {94 // TODO explicitly put this memory in the coroutine frame #1194
95 // TODO explicitly put this memory in the coroutine frame #119495 var my_handle: promise = undefined;
96 var my_tick_node = Loop.NextTickNode{96 suspend |p| {
97 .data = handle,97 my_handle = p;
98 .next = undefined,98 resume p;
99 };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 |_| {
101 self.queue.put(&my_tick_node);104 self.queue.put(&my_tick_node);
102105
103 // At this point, we are in the queue, so we might have already been resumed and this coroutine106 // 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 {...@@ -170,6 +173,7 @@ async fn testLock(loop: *Loop, lock: *Lock) void {
170 }173 }
171 const handle1 = async lockRunner(lock) catch @panic("out of memory");174 const handle1 = async lockRunner(lock) catch @panic("out of memory");
172 var tick_node1 = Loop.NextTickNode{175 var tick_node1 = Loop.NextTickNode{
176 .prev = undefined,
173 .next = undefined,177 .next = undefined,
174 .data = handle1,178 .data = handle1,
175 };179 };
...@@ -177,6 +181,7 @@ async fn testLock(loop: *Loop, lock: *Lock) void {...@@ -177,6 +181,7 @@ async fn testLock(loop: *Loop, lock: *Lock) void {
177181
178 const handle2 = async lockRunner(lock) catch @panic("out of memory");182 const handle2 = async lockRunner(lock) catch @panic("out of memory");
179 var tick_node2 = Loop.NextTickNode{183 var tick_node2 = Loop.NextTickNode{
184 .prev = undefined,
180 .next = undefined,185 .next = undefined,
181 .data = handle2,186 .data = handle2,
182 };187 };
...@@ -184,6 +189,7 @@ async fn testLock(loop: *Loop, lock: *Lock) void {...@@ -184,6 +189,7 @@ async fn testLock(loop: *Loop, lock: *Lock) void {
184189
185 const handle3 = async lockRunner(lock) catch @panic("out of memory");190 const handle3 = async lockRunner(lock) catch @panic("out of memory");
186 var tick_node3 = Loop.NextTickNode{191 var tick_node3 = Loop.NextTickNode{
192 .prev = undefined,
187 .next = undefined,193 .next = undefined,
188 .data = handle3,194 .data = handle3,
189 };195 };
std/event/loop.zig+11
...@@ -120,6 +120,7 @@ pub const Loop = struct {...@@ -120,6 +120,7 @@ pub const Loop = struct {
120 // we need another thread for the file system because Linux does not have an async120 // we need another thread for the file system because Linux does not have an async
121 // file system I/O API.121 // file system I/O API.
122 self.os_data.fs_end_request = fs.RequestNode{122 self.os_data.fs_end_request = fs.RequestNode{
123 .prev = undefined,
123 .next = undefined,124 .next = undefined,
124 .data = fs.Request{125 .data = fs.Request{
125 .msg = fs.Request.Msg.End,126 .msg = fs.Request.Msg.End,
...@@ -206,6 +207,7 @@ pub const Loop = struct {...@@ -206,6 +207,7 @@ pub const Loop = struct {
206 .udata = @ptrToInt(&eventfd_node.data.base),207 .udata = @ptrToInt(&eventfd_node.data.base),
207 },208 },
208 },209 },
210 .prev = undefined,
209 .next = undefined,211 .next = undefined,
210 };212 };
211 self.available_eventfd_resume_nodes.push(eventfd_node);213 self.available_eventfd_resume_nodes.push(eventfd_node);
...@@ -270,6 +272,7 @@ pub const Loop = struct {...@@ -270,6 +272,7 @@ pub const Loop = struct {
270 // this one is for sending events272 // this one is for sending events
271 .completion_key = @ptrToInt(&eventfd_node.data.base),273 .completion_key = @ptrToInt(&eventfd_node.data.base),
272 },274 },
275 .prev = undefined,
273 .next = undefined,276 .next = undefined,
274 };277 };
275 self.available_eventfd_resume_nodes.push(eventfd_node);278 self.available_eventfd_resume_nodes.push(eventfd_node);
...@@ -422,6 +425,12 @@ pub const Loop = struct {...@@ -422,6 +425,12 @@ pub const Loop = struct {
422 self.dispatch();425 self.dispatch();
423 }426 }
424427
428 pub fn cancelOnNextTick(self: *Loop, node: *NextTickNode) void {
429 if (self.next_tick_queue.remove(node)) {
430 self.finishOneEvent();
431 }
432 }
433
425 pub fn run(self: *Loop) void {434 pub fn run(self: *Loop) void {
426 self.finishOneEvent(); // the reference we start with435 self.finishOneEvent(); // the reference we start with
427436
...@@ -443,6 +452,7 @@ pub const Loop = struct {...@@ -443,6 +452,7 @@ pub const Loop = struct {
443 suspend |p| {452 suspend |p| {
444 handle.* = p;453 handle.* = p;
445 var my_tick_node = Loop.NextTickNode{454 var my_tick_node = Loop.NextTickNode{
455 .prev = undefined,
446 .next = undefined,456 .next = undefined,
447 .data = p,457 .data = p,
448 };458 };
...@@ -464,6 +474,7 @@ pub const Loop = struct {...@@ -464,6 +474,7 @@ pub const Loop = struct {
464 pub async fn yield(self: *Loop) void {474 pub async fn yield(self: *Loop) void {
465 suspend |p| {475 suspend |p| {
466 var my_tick_node = Loop.NextTickNode{476 var my_tick_node = Loop.NextTickNode{
477 .prev = undefined,
467 .next = undefined,478 .next = undefined,
468 .data = p,479 .data = p,
469 };480 };
std/event/rwlock.zig+2
...@@ -101,6 +101,7 @@ pub const RwLock = struct {...@@ -101,6 +101,7 @@ pub const RwLock = struct {
101 // TODO explicitly put this memory in the coroutine frame #1194101 // TODO explicitly put this memory in the coroutine frame #1194
102 var my_tick_node = Loop.NextTickNode{102 var my_tick_node = Loop.NextTickNode{
103 .data = handle,103 .data = handle,
104 .prev = undefined,
104 .next = undefined,105 .next = undefined,
105 };106 };
106107
...@@ -133,6 +134,7 @@ pub const RwLock = struct {...@@ -133,6 +134,7 @@ pub const RwLock = struct {
133 // TODO explicitly put this memory in the coroutine frame #1194134 // TODO explicitly put this memory in the coroutine frame #1194
134 var my_tick_node = Loop.NextTickNode{135 var my_tick_node = Loop.NextTickNode{
135 .data = handle,136 .data = handle,
137 .prev = undefined,
136 .next = undefined,138 .next = undefined,
137 };139 };
138140
std/index.zig-1
...@@ -6,7 +6,6 @@ pub const Buffer = @import("buffer.zig").Buffer;...@@ -6,7 +6,6 @@ pub const Buffer = @import("buffer.zig").Buffer;
6pub const BufferOutStream = @import("buffer.zig").BufferOutStream;6pub const BufferOutStream = @import("buffer.zig").BufferOutStream;
7pub const HashMap = @import("hash_map.zig").HashMap;7pub const HashMap = @import("hash_map.zig").HashMap;
8pub const LinkedList = @import("linked_list.zig").LinkedList;8pub const LinkedList = @import("linked_list.zig").LinkedList;
9pub const IntrusiveLinkedList = @import("linked_list.zig").IntrusiveLinkedList;
10pub const SegmentedList = @import("segmented_list.zig").SegmentedList;9pub const SegmentedList = @import("segmented_list.zig").SegmentedList;
11pub const DynLib = @import("dynamic_library.zig").DynLib;10pub const DynLib = @import("dynamic_library.zig").DynLib;
12pub const Mutex = @import("mutex.zig").Mutex;11pub const Mutex = @import("mutex.zig").Mutex;
std/linked_list.zig+4-97
...@@ -4,18 +4,8 @@ const assert = debug.assert;...@@ -4,18 +4,8 @@ const assert = debug.assert;
4const mem = std.mem;4const mem = std.mem;
5const Allocator = mem.Allocator;5const 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
17/// Generic doubly linked list.7/// 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 {
19 return struct {9 return struct {
20 const Self = this;10 const Self = this;
2111
...@@ -25,23 +15,13 @@ fn BaseLinkedList(comptime T: type, comptime ParentType: type, comptime field_na...@@ -25,23 +15,13 @@ fn BaseLinkedList(comptime T: type, comptime ParentType: type, comptime field_na
25 next: ?*Node,15 next: ?*Node,
26 data: T,16 data: T,
2717
28 pub fn init(value: *const T) Node {18 pub fn init(data: T) Node {
29 return Node{19 return Node{
30 .prev = null,20 .prev = null,
31 .next = null,21 .next = null,
32 .data = value.*,22 .data = data,
33 };23 };
34 }24 }
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 }
45 };25 };
4626
47 first: ?*Node,27 first: ?*Node,
...@@ -60,10 +40,6 @@ fn BaseLinkedList(comptime T: type, comptime ParentType: type, comptime field_na...@@ -60,10 +40,6 @@ fn BaseLinkedList(comptime T: type, comptime ParentType: type, comptime field_na
60 };40 };
61 }41 }
6242
63 fn isIntrusive() bool {
64 return ParentType != void or field_name.len != 0;
65 }
66
67 /// Insert a new node after an existing one.43 /// Insert a new node after an existing one.
68 ///44 ///
69 /// Arguments:45 /// Arguments:
...@@ -192,7 +168,6 @@ fn BaseLinkedList(comptime T: type, comptime ParentType: type, comptime field_na...@@ -192,7 +168,6 @@ fn BaseLinkedList(comptime T: type, comptime ParentType: type, comptime field_na
192 /// Returns:168 /// Returns:
193 /// A pointer to the new node.169 /// A pointer to the new node.
194 pub fn allocateNode(list: *Self, allocator: *Allocator) !*Node {170 pub fn allocateNode(list: *Self, allocator: *Allocator) !*Node {
195 comptime assert(!isIntrusive());
196 return allocator.create(Node(undefined));171 return allocator.create(Node(undefined));
197 }172 }
198173
...@@ -202,7 +177,6 @@ fn BaseLinkedList(comptime T: type, comptime ParentType: type, comptime field_na...@@ -202,7 +177,6 @@ fn BaseLinkedList(comptime T: type, comptime ParentType: type, comptime field_na
202 /// node: Pointer to the node to deallocate.177 /// node: Pointer to the node to deallocate.
203 /// allocator: Dynamic memory allocator.178 /// allocator: Dynamic memory allocator.
204 pub fn destroyNode(list: *Self, node: *Node, allocator: *Allocator) void {179 pub fn destroyNode(list: *Self, node: *Node, allocator: *Allocator) void {
205 comptime assert(!isIntrusive());
206 allocator.destroy(node);180 allocator.destroy(node);
207 }181 }
208182
...@@ -214,8 +188,7 @@ fn BaseLinkedList(comptime T: type, comptime ParentType: type, comptime field_na...@@ -214,8 +188,7 @@ fn BaseLinkedList(comptime T: type, comptime ParentType: type, comptime field_na
214 ///188 ///
215 /// Returns:189 /// Returns:
216 /// A pointer to the new node.190 /// A pointer to the new node.
217 pub fn createNode(list: *Self, data: *const T, allocator: *Allocator) !*Node {191 pub fn createNode(list: *Self, data: T, allocator: *Allocator) !*Node {
218 comptime assert(!isIntrusive());
219 var node = try list.allocateNode(allocator);192 var node = try list.allocateNode(allocator);
220 node.* = Node.init(data);193 node.* = Node.init(data);
221 return node;194 return node;
...@@ -274,69 +247,3 @@ test "basic linked list test" {...@@ -274,69 +247,3 @@ test "basic linked list test" {
274 assert(list.last.?.data == 4);247 assert(list.last.?.data == 4);
275 assert(list.len == 2);248 assert(list.len == 2);
276}249}
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}