authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-08-10 15:51:17-04:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2018-08-10 15:51:17-04:00
logc4b9466da7592b95246909908619b68db5389ceb
tree6c55cc3ecb3e289fe2c13e346b70560fceaafbef
parentd927f347de1f5a19545fc235f8779c2326409543
parent598e80957e6eccc13ade72ce2693dcd60934763d
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #1294 from ziglang/async-fs

introduce std.event.fs for async file system functions

46 files changed, 4041 insertions(+), 1047 deletions(-)

CMakeLists.txt+4
......@@ -460,11 +460,14 @@ set(ZIG_STD_FILES
460460 "empty.zig"
461461 "event.zig"
462462 "event/channel.zig"
463 "event/fs.zig"
463464 "event/future.zig"
464465 "event/group.zig"
465466 "event/lock.zig"
466467 "event/locked.zig"
467468 "event/loop.zig"
469 "event/rwlock.zig"
470 "event/rwlocked.zig"
468471 "event/tcp.zig"
469472 "fmt/errol/enum3.zig"
470473 "fmt/errol/index.zig"
......@@ -553,6 +556,7 @@ set(ZIG_STD_FILES
553556 "math/tanh.zig"
554557 "math/trunc.zig"
555558 "mem.zig"
559 "mutex.zig"
556560 "net.zig"
557561 "os/child_process.zig"
558562 "os/darwin.zig"
doc/docgen.zig+2-2
......@@ -370,9 +370,9 @@ fn genToc(allocator: *mem.Allocator, tokenizer: *Tokenizer) !Toc {
370370 .n = header_stack_size,
371371 },
372372 });
373 if (try urls.put(urlized, tag_token)) |other_tag_token| {
373 if (try urls.put(urlized, tag_token)) |entry| {
374374 parseError(tokenizer, tag_token, "duplicate header url: #{}", urlized) catch {};
375 parseError(tokenizer, other_tag_token, "other tag here") catch {};
375 parseError(tokenizer, entry.value, "other tag here") catch {};
376376 return error.ParseError;
377377 }
378378 if (last_action == Action.Open) {
src-self-hosted/codegen.zig+2-2
......@@ -19,8 +19,8 @@ pub async fn renderToLlvm(comp: *Compilation, fn_val: *Value.Fn, code: *ir.Code)
1919 var output_path = try await (async comp.createRandomOutputPath(comp.target.objFileExt()) catch unreachable);
2020 errdefer output_path.deinit();
2121
22 const llvm_handle = try comp.event_loop_local.getAnyLlvmContext();
23 defer llvm_handle.release(comp.event_loop_local);
22 const llvm_handle = try comp.zig_compiler.getAnyLlvmContext();
23 defer llvm_handle.release(comp.zig_compiler);
2424
2525 const context = llvm_handle.node.data;
2626
src-self-hosted/compilation.zig+346-180
......@@ -30,9 +30,12 @@ const Package = @import("package.zig").Package;
3030const link = @import("link.zig").link;
3131const LibCInstallation = @import("libc_installation.zig").LibCInstallation;
3232const CInt = @import("c_int.zig").CInt;
33const fs = event.fs;
34
35const max_src_size = 2 * 1024 * 1024 * 1024; // 2 GiB
3336
3437/// Data that is local to the event loop.
35pub const EventLoopLocal = struct {
38pub const ZigCompiler = struct {
3639 loop: *event.Loop,
3740 llvm_handle_pool: std.atomic.Stack(llvm.ContextRef),
3841 lld_lock: event.Lock,
......@@ -44,7 +47,7 @@ pub const EventLoopLocal = struct {
4447
4548 var lazy_init_targets = std.lazyInit(void);
4649
47 fn init(loop: *event.Loop) !EventLoopLocal {
50 fn init(loop: *event.Loop) !ZigCompiler {
4851 lazy_init_targets.get() orelse {
4952 Target.initializeAll();
5053 lazy_init_targets.resolve();
......@@ -54,7 +57,7 @@ pub const EventLoopLocal = struct {
5457 try std.os.getRandomBytes(seed_bytes[0..]);
5558 const seed = std.mem.readInt(seed_bytes, u64, builtin.Endian.Big);
5659
57 return EventLoopLocal{
60 return ZigCompiler{
5861 .loop = loop,
5962 .lld_lock = event.Lock.init(loop),
6063 .llvm_handle_pool = std.atomic.Stack(llvm.ContextRef).init(),
......@@ -64,7 +67,7 @@ pub const EventLoopLocal = struct {
6467 }
6568
6669 /// Must be called only after EventLoop.run completes.
67 fn deinit(self: *EventLoopLocal) void {
70 fn deinit(self: *ZigCompiler) void {
6871 self.lld_lock.deinit();
6972 while (self.llvm_handle_pool.pop()) |node| {
7073 c.LLVMContextDispose(node.data);
......@@ -74,7 +77,7 @@ pub const EventLoopLocal = struct {
7477
7578 /// Gets an exclusive handle on any LlvmContext.
7679 /// Caller must release the handle when done.
77 pub fn getAnyLlvmContext(self: *EventLoopLocal) !LlvmHandle {
80 pub fn getAnyLlvmContext(self: *ZigCompiler) !LlvmHandle {
7881 if (self.llvm_handle_pool.pop()) |node| return LlvmHandle{ .node = node };
7982
8083 const context_ref = c.LLVMContextCreate() orelse return error.OutOfMemory;
......@@ -89,24 +92,36 @@ pub const EventLoopLocal = struct {
8992 return LlvmHandle{ .node = node };
9093 }
9194
92 pub async fn getNativeLibC(self: *EventLoopLocal) !*LibCInstallation {
95 pub async fn getNativeLibC(self: *ZigCompiler) !*LibCInstallation {
9396 if (await (async self.native_libc.start() catch unreachable)) |ptr| return ptr;
9497 try await (async self.native_libc.data.findNative(self.loop) catch unreachable);
9598 self.native_libc.resolve();
9699 return &self.native_libc.data;
97100 }
101
102 /// Must be called only once, ever. Sets global state.
103 pub fn setLlvmArgv(allocator: *Allocator, llvm_argv: []const []const u8) !void {
104 if (llvm_argv.len != 0) {
105 var c_compatible_args = try std.cstr.NullTerminated2DArray.fromSlices(allocator, [][]const []const u8{
106 [][]const u8{"zig (LLVM option parsing)"},
107 llvm_argv,
108 });
109 defer c_compatible_args.deinit();
110 c.ZigLLVMParseCommandLineOptions(llvm_argv.len + 1, c_compatible_args.ptr);
111 }
112 }
98113};
99114
100115pub const LlvmHandle = struct {
101116 node: *std.atomic.Stack(llvm.ContextRef).Node,
102117
103 pub fn release(self: LlvmHandle, event_loop_local: *EventLoopLocal) void {
104 event_loop_local.llvm_handle_pool.push(self.node);
118 pub fn release(self: LlvmHandle, zig_compiler: *ZigCompiler) void {
119 zig_compiler.llvm_handle_pool.push(self.node);
105120 }
106121};
107122
108123pub const Compilation = struct {
109 event_loop_local: *EventLoopLocal,
124 zig_compiler: *ZigCompiler,
110125 loop: *event.Loop,
111126 name: Buffer,
112127 llvm_triple: Buffer,
......@@ -134,7 +149,6 @@ pub const Compilation = struct {
134149 linker_rdynamic: bool,
135150
136151 clang_argv: []const []const u8,
137 llvm_argv: []const []const u8,
138152 lib_dirs: []const []const u8,
139153 rpath_list: []const []const u8,
140154 assembly_files: []const []const u8,
......@@ -214,6 +228,8 @@ pub const Compilation = struct {
214228 deinit_group: event.Group(void),
215229
216230 destroy_handle: promise,
231 main_loop_handle: promise,
232 main_loop_future: event.Future(void),
217233
218234 have_err_ret_tracing: bool,
219235
......@@ -227,6 +243,8 @@ pub const Compilation = struct {
227243
228244 c_int_types: [CInt.list.len]*Type.Int,
229245
246 fs_watch: *fs.Watch(*Scope.Root),
247
230248 const IntTypeTable = std.HashMap(*const Type.Int.Key, *Type.Int, Type.Int.Key.hash, Type.Int.Key.eql);
231249 const ArrayTypeTable = std.HashMap(*const Type.Array.Key, *Type.Array, Type.Array.Key.hash, Type.Array.Key.eql);
232250 const PtrTypeTable = std.HashMap(*const Type.Pointer.Key, *Type.Pointer, Type.Pointer.Key.hash, Type.Pointer.Key.eql);
......@@ -282,6 +300,8 @@ pub const Compilation = struct {
282300 LibCMissingDynamicLinker,
283301 InvalidDarwinVersionString,
284302 UnsupportedLinkArchitecture,
303 UserResourceLimitReached,
304 InvalidUtf8,
285305 };
286306
287307 pub const Event = union(enum) {
......@@ -318,7 +338,7 @@ pub const Compilation = struct {
318338 };
319339
320340 pub fn create(
321 event_loop_local: *EventLoopLocal,
341 zig_compiler: *ZigCompiler,
322342 name: []const u8,
323343 root_src_path: ?[]const u8,
324344 target: Target,
......@@ -327,11 +347,45 @@ pub const Compilation = struct {
327347 is_static: bool,
328348 zig_lib_dir: []const u8,
329349 ) !*Compilation {
330 const loop = event_loop_local.loop;
331 const comp = try event_loop_local.loop.allocator.create(Compilation{
350 var optional_comp: ?*Compilation = null;
351 const handle = try async<zig_compiler.loop.allocator> createAsync(
352 &optional_comp,
353 zig_compiler,
354 name,
355 root_src_path,
356 target,
357 kind,
358 build_mode,
359 is_static,
360 zig_lib_dir,
361 );
362 return optional_comp orelse if (getAwaitResult(
363 zig_compiler.loop.allocator,
364 handle,
365 )) |_| unreachable else |err| err;
366 }
367
368 async fn createAsync(
369 out_comp: *?*Compilation,
370 zig_compiler: *ZigCompiler,
371 name: []const u8,
372 root_src_path: ?[]const u8,
373 target: Target,
374 kind: Kind,
375 build_mode: builtin.Mode,
376 is_static: bool,
377 zig_lib_dir: []const u8,
378 ) !void {
379 // workaround for https://github.com/ziglang/zig/issues/1194
380 suspend {
381 resume @handle();
382 }
383
384 const loop = zig_compiler.loop;
385 var comp = Compilation{
332386 .loop = loop,
333387 .arena_allocator = std.heap.ArenaAllocator.init(loop.allocator),
334 .event_loop_local = event_loop_local,
388 .zig_compiler = zig_compiler,
335389 .events = undefined,
336390 .root_src_path = root_src_path,
337391 .target = target,
......@@ -341,6 +395,9 @@ pub const Compilation = struct {
341395 .zig_lib_dir = zig_lib_dir,
342396 .zig_std_dir = undefined,
343397 .tmp_dir = event.Future(BuildError![]u8).init(loop),
398 .destroy_handle = @handle(),
399 .main_loop_handle = undefined,
400 .main_loop_future = event.Future(void).init(loop),
344401
345402 .name = undefined,
346403 .llvm_triple = undefined,
......@@ -365,7 +422,6 @@ pub const Compilation = struct {
365422 .is_static = is_static,
366423 .linker_rdynamic = false,
367424 .clang_argv = [][]const u8{},
368 .llvm_argv = [][]const u8{},
369425 .lib_dirs = [][]const u8{},
370426 .rpath_list = [][]const u8{},
371427 .assembly_files = [][]const u8{},
......@@ -412,25 +468,26 @@ pub const Compilation = struct {
412468 .std_package = undefined,
413469
414470 .override_libc = null,
415 .destroy_handle = undefined,
416471 .have_err_ret_tracing = false,
417472 .primitive_type_table = undefined,
418 });
419 errdefer {
473
474 .fs_watch = undefined,
475 };
476 comp.link_libs_list = ArrayList(*LinkLib).init(comp.arena());
477 comp.primitive_type_table = TypeTable.init(comp.arena());
478
479 defer {
420480 comp.int_type_table.private_data.deinit();
421481 comp.array_type_table.private_data.deinit();
422482 comp.ptr_type_table.private_data.deinit();
423483 comp.fn_type_table.private_data.deinit();
424484 comp.arena_allocator.deinit();
425 comp.loop.allocator.destroy(comp);
426485 }
427486
428487 comp.name = try Buffer.init(comp.arena(), name);
429488 comp.llvm_triple = try target.getTriple(comp.arena());
430489 comp.llvm_target = try Target.llvmTargetFromTriple(comp.llvm_triple);
431 comp.link_libs_list = ArrayList(*LinkLib).init(comp.arena());
432490 comp.zig_std_dir = try std.os.path.join(comp.arena(), zig_lib_dir, "std");
433 comp.primitive_type_table = TypeTable.init(comp.arena());
434491
435492 const opt_level = switch (build_mode) {
436493 builtin.Mode.Debug => llvm.CodeGenLevelNone,
......@@ -444,8 +501,8 @@ pub const Compilation = struct {
444501 // As a workaround we do not use target native features on Windows.
445502 var target_specific_cpu_args: ?[*]u8 = null;
446503 var target_specific_cpu_features: ?[*]u8 = null;
447 errdefer llvm.DisposeMessage(target_specific_cpu_args);
448 errdefer llvm.DisposeMessage(target_specific_cpu_features);
504 defer llvm.DisposeMessage(target_specific_cpu_args);
505 defer llvm.DisposeMessage(target_specific_cpu_features);
449506 if (target == Target.Native and !target.isWindows()) {
450507 target_specific_cpu_args = llvm.GetHostCPUName() orelse return error.OutOfMemory;
451508 target_specific_cpu_features = llvm.GetNativeFeatures() orelse return error.OutOfMemory;
......@@ -460,16 +517,16 @@ pub const Compilation = struct {
460517 reloc_mode,
461518 llvm.CodeModelDefault,
462519 ) orelse return error.OutOfMemory;
463 errdefer llvm.DisposeTargetMachine(comp.target_machine);
520 defer llvm.DisposeTargetMachine(comp.target_machine);
464521
465522 comp.target_data_ref = llvm.CreateTargetDataLayout(comp.target_machine) orelse return error.OutOfMemory;
466 errdefer llvm.DisposeTargetData(comp.target_data_ref);
523 defer llvm.DisposeTargetData(comp.target_data_ref);
467524
468525 comp.target_layout_str = llvm.CopyStringRepOfTargetData(comp.target_data_ref) orelse return error.OutOfMemory;
469 errdefer llvm.DisposeMessage(comp.target_layout_str);
526 defer llvm.DisposeMessage(comp.target_layout_str);
470527
471528 comp.events = try event.Channel(Event).create(comp.loop, 0);
472 errdefer comp.events.destroy();
529 defer comp.events.destroy();
473530
474531 if (root_src_path) |root_src| {
475532 const dirname = std.os.path.dirname(root_src) orelse ".";
......@@ -482,11 +539,27 @@ pub const Compilation = struct {
482539 comp.root_package = try Package.create(comp.arena(), ".", "");
483540 }
484541
542 comp.fs_watch = try fs.Watch(*Scope.Root).create(loop, 16);
543 defer comp.fs_watch.destroy();
544
485545 try comp.initTypes();
546 defer comp.primitive_type_table.deinit();
547
548 comp.main_loop_handle = async comp.mainLoop() catch unreachable;
549 // Set this to indicate that initialization completed successfully.
550 // from here on out we must not return an error.
551 // This must occur before the first suspend/await.
552 out_comp.* = &comp;
553 // This suspend is resumed by destroy()
554 suspend;
555 // From here on is cleanup.
486556
487 comp.destroy_handle = try async<loop.allocator> comp.internalDeinit();
557 await (async comp.deinit_group.wait() catch unreachable);
488558
489 return comp;
559 if (comp.tmp_dir.getOrNull()) |tmp_dir_result| if (tmp_dir_result.*) |tmp_dir| {
560 // TODO evented I/O?
561 os.deleteTree(comp.arena(), tmp_dir) catch {};
562 } else |_| {};
490563 }
491564
492565 /// it does ref the result because it could be an arbitrary integer size
......@@ -672,55 +745,28 @@ pub const Compilation = struct {
672745 assert((try comp.primitive_type_table.put(comp.u8_type.base.name, &comp.u8_type.base)) == null);
673746 }
674747
675 /// This function can safely use async/await, because it manages Compilation's lifetime,
676 /// and EventLoopLocal.deinit will not be called until the event.Loop.run() completes.
677 async fn internalDeinit(self: *Compilation) void {
678 suspend;
679
680 await (async self.deinit_group.wait() catch unreachable);
681 if (self.tmp_dir.getOrNull()) |tmp_dir_result| if (tmp_dir_result.*) |tmp_dir| {
682 // TODO evented I/O?
683 os.deleteTree(self.arena(), tmp_dir) catch {};
684 } else |_| {};
685
686 self.events.destroy();
687
688 llvm.DisposeMessage(self.target_layout_str);
689 llvm.DisposeTargetData(self.target_data_ref);
690 llvm.DisposeTargetMachine(self.target_machine);
691
692 self.primitive_type_table.deinit();
693
694 self.arena_allocator.deinit();
695 self.gpa().destroy(self);
696 }
697
698748 pub fn destroy(self: *Compilation) void {
749 cancel self.main_loop_handle;
699750 resume self.destroy_handle;
700751 }
701752
702 pub fn build(self: *Compilation) !void {
703 if (self.llvm_argv.len != 0) {
704 var c_compatible_args = try std.cstr.NullTerminated2DArray.fromSlices(self.arena(), [][]const []const u8{
705 [][]const u8{"zig (LLVM option parsing)"},
706 self.llvm_argv,
707 });
708 defer c_compatible_args.deinit();
709 // TODO this sets global state
710 c.ZigLLVMParseCommandLineOptions(self.llvm_argv.len + 1, c_compatible_args.ptr);
711 }
712
713 _ = try async<self.gpa()> self.buildAsync();
753 fn start(self: *Compilation) void {
754 self.main_loop_future.resolve();
714755 }
715756
716 async fn buildAsync(self: *Compilation) void {
717 while (true) {
718 // TODO directly awaiting async should guarantee memory allocation elision
719 const build_result = await (async self.compileAndLink() catch unreachable);
757 async fn mainLoop(self: *Compilation) void {
758 // wait until start() is called
759 _ = await (async self.main_loop_future.get() catch unreachable);
720760
761 var build_result = await (async self.initialCompile() catch unreachable);
762
763 while (true) {
764 const link_result = if (build_result) blk: {
765 break :blk await (async self.maybeLink() catch unreachable);
766 } else |err| err;
721767 // this makes a handy error return trace and stack trace in debug mode
722768 if (std.debug.runtime_safety) {
723 build_result catch unreachable;
769 link_result catch unreachable;
724770 }
725771
726772 const compile_errors = blk: {
......@@ -729,7 +775,7 @@ pub const Compilation = struct {
729775 break :blk held.value.toOwnedSlice();
730776 };
731777
732 if (build_result) |_| {
778 if (link_result) |_| {
733779 if (compile_errors.len == 0) {
734780 await (async self.events.put(Event.Ok) catch unreachable);
735781 } else {
......@@ -742,105 +788,195 @@ pub const Compilation = struct {
742788 await (async self.events.put(Event{ .Error = err }) catch unreachable);
743789 }
744790
745 // for now we stop after 1
746 return;
791 // First, get an item from the watch channel, waiting on the channel.
792 var group = event.Group(BuildError!void).init(self.loop);
793 {
794 const ev = (await (async self.fs_watch.channel.get() catch unreachable)) catch |err| {
795 build_result = err;
796 continue;
797 };
798 const root_scope = ev.data;
799 group.call(rebuildFile, self, root_scope) catch |err| {
800 build_result = err;
801 continue;
802 };
803 }
804 // Next, get all the items from the channel that are buffered up.
805 while (await (async self.fs_watch.channel.getOrNull() catch unreachable)) |ev_or_err| {
806 if (ev_or_err) |ev| {
807 const root_scope = ev.data;
808 group.call(rebuildFile, self, root_scope) catch |err| {
809 build_result = err;
810 continue;
811 };
812 } else |err| {
813 build_result = err;
814 continue;
815 }
816 }
817 build_result = await (async group.wait() catch unreachable);
747818 }
748819 }
749820
750 async fn compileAndLink(self: *Compilation) !void {
751 if (self.root_src_path) |root_src_path| {
752 // TODO async/await os.path.real
753 const root_src_real_path = os.path.real(self.gpa(), root_src_path) catch |err| {
754 try printError("unable to get real path '{}': {}", root_src_path, err);
755 return err;
821 async fn rebuildFile(self: *Compilation, root_scope: *Scope.Root) !void {
822 const tree_scope = blk: {
823 const source_code = (await (async fs.readFile(
824 self.loop,
825 root_scope.realpath,
826 max_src_size,
827 ) catch unreachable)) catch |err| {
828 try self.addCompileErrorCli(root_scope.realpath, "unable to open: {}", @errorName(err));
829 return;
756830 };
757 const root_scope = blk: {
758 errdefer self.gpa().free(root_src_real_path);
831 errdefer self.gpa().free(source_code);
759832
760 // TODO async/await readFileAlloc()
761 const source_code = io.readFileAlloc(self.gpa(), root_src_real_path) catch |err| {
762 try printError("unable to open '{}': {}", root_src_real_path, err);
763 return err;
764 };
765 errdefer self.gpa().free(source_code);
833 const tree = try self.gpa().createOne(ast.Tree);
834 tree.* = try std.zig.parse(self.gpa(), source_code);
835 errdefer {
836 tree.deinit();
837 self.gpa().destroy(tree);
838 }
766839
767 const tree = try self.gpa().createOne(ast.Tree);
768 tree.* = try std.zig.parse(self.gpa(), source_code);
769 errdefer {
770 tree.deinit();
771 self.gpa().destroy(tree);
772 }
840 break :blk try Scope.AstTree.create(self, tree, root_scope);
841 };
842 defer tree_scope.base.deref(self);
773843
774 break :blk try Scope.Root.create(self, tree, root_src_real_path);
775 };
776 defer root_scope.base.deref(self);
777 const tree = root_scope.tree;
844 var error_it = tree_scope.tree.errors.iterator(0);
845 while (error_it.next()) |parse_error| {
846 const msg = try Msg.createFromParseErrorAndScope(self, tree_scope, parse_error);
847 errdefer msg.destroy();
778848
779 var error_it = tree.errors.iterator(0);
780 while (error_it.next()) |parse_error| {
781 const msg = try Msg.createFromParseErrorAndScope(self, root_scope, parse_error);
782 errdefer msg.destroy();
849 try await (async self.addCompileErrorAsync(msg) catch unreachable);
850 }
851 if (tree_scope.tree.errors.len != 0) {
852 return;
853 }
783854
784 try await (async self.addCompileErrorAsync(msg) catch unreachable);
785 }
786 if (tree.errors.len != 0) {
787 return;
788 }
855 const locked_table = await (async root_scope.decls.table.acquireWrite() catch unreachable);
856 defer locked_table.release();
789857
790 const decls = try Scope.Decls.create(self, &root_scope.base);
791 defer decls.base.deref(self);
858 var decl_group = event.Group(BuildError!void).init(self.loop);
859 defer decl_group.deinit();
792860
793 var decl_group = event.Group(BuildError!void).init(self.loop);
794 var decl_group_consumed = false;
795 errdefer if (!decl_group_consumed) decl_group.cancelAll();
861 try await try async self.rebuildChangedDecls(
862 &decl_group,
863 locked_table.value,
864 root_scope.decls,
865 &tree_scope.tree.root_node.decls,
866 tree_scope,
867 );
796868
797 var it = tree.root_node.decls.iterator(0);
798 while (it.next()) |decl_ptr| {
799 const decl = decl_ptr.*;
800 switch (decl.id) {
801 ast.Node.Id.Comptime => {
802 const comptime_node = @fieldParentPtr(ast.Node.Comptime, "base", decl);
869 try await (async decl_group.wait() catch unreachable);
870 }
803871
804 try self.prelink_group.call(addCompTimeBlock, self, &decls.base, comptime_node);
805 },
806 ast.Node.Id.VarDecl => @panic("TODO"),
807 ast.Node.Id.FnProto => {
808 const fn_proto = @fieldParentPtr(ast.Node.FnProto, "base", decl);
809
810 const name = if (fn_proto.name_token) |name_token| tree.tokenSlice(name_token) else {
811 try self.addCompileError(root_scope, Span{
812 .first = fn_proto.fn_token,
813 .last = fn_proto.fn_token + 1,
814 }, "missing function name");
815 continue;
816 };
872 async fn rebuildChangedDecls(
873 self: *Compilation,
874 group: *event.Group(BuildError!void),
875 locked_table: *Decl.Table,
876 decl_scope: *Scope.Decls,
877 ast_decls: *ast.Node.Root.DeclList,
878 tree_scope: *Scope.AstTree,
879 ) !void {
880 var existing_decls = try locked_table.clone();
881 defer existing_decls.deinit();
882
883 var ast_it = ast_decls.iterator(0);
884 while (ast_it.next()) |decl_ptr| {
885 const decl = decl_ptr.*;
886 switch (decl.id) {
887 ast.Node.Id.Comptime => {
888 const comptime_node = @fieldParentPtr(ast.Node.Comptime, "base", decl);
889
890 // TODO connect existing comptime decls to updated source files
891
892 try self.prelink_group.call(addCompTimeBlock, self, tree_scope, &decl_scope.base, comptime_node);
893 },
894 ast.Node.Id.VarDecl => @panic("TODO"),
895 ast.Node.Id.FnProto => {
896 const fn_proto = @fieldParentPtr(ast.Node.FnProto, "base", decl);
897
898 const name = if (fn_proto.name_token) |name_token| tree_scope.tree.tokenSlice(name_token) else {
899 try self.addCompileError(tree_scope, Span{
900 .first = fn_proto.fn_token,
901 .last = fn_proto.fn_token + 1,
902 }, "missing function name");
903 continue;
904 };
817905
906 if (existing_decls.remove(name)) |entry| {
907 // compare new code to existing
908 if (entry.value.cast(Decl.Fn)) |existing_fn_decl| {
909 // Just compare the old bytes to the new bytes of the top level decl.
910 // Even if the AST is technically the same, we want error messages to display
911 // from the most recent source.
912 const old_decl_src = existing_fn_decl.base.tree_scope.tree.getNodeSource(
913 &existing_fn_decl.fn_proto.base,
914 );
915 const new_decl_src = tree_scope.tree.getNodeSource(&fn_proto.base);
916 if (mem.eql(u8, old_decl_src, new_decl_src)) {
917 // it's the same, we can skip this decl
918 continue;
919 } else {
920 @panic("TODO decl changed implementation");
921 // Add the new thing before dereferencing the old thing. This way we don't end
922 // up pointlessly re-creating things we end up using in the new thing.
923 }
924 } else {
925 @panic("TODO decl changed kind");
926 }
927 } else {
928 // add new decl
818929 const fn_decl = try self.gpa().create(Decl.Fn{
819930 .base = Decl{
820931 .id = Decl.Id.Fn,
821932 .name = name,
822 .visib = parseVisibToken(tree, fn_proto.visib_token),
933 .visib = parseVisibToken(tree_scope.tree, fn_proto.visib_token),
823934 .resolution = event.Future(BuildError!void).init(self.loop),
824 .parent_scope = &decls.base,
935 .parent_scope = &decl_scope.base,
936 .tree_scope = tree_scope,
825937 },
826938 .value = Decl.Fn.Val{ .Unresolved = {} },
827939 .fn_proto = fn_proto,
828940 });
941 tree_scope.base.ref();
829942 errdefer self.gpa().destroy(fn_decl);
830943
831 try decl_group.call(addTopLevelDecl, self, decls, &fn_decl.base);
832 },
833 ast.Node.Id.TestDecl => @panic("TODO"),
834 else => unreachable,
835 }
944 try group.call(addTopLevelDecl, self, &fn_decl.base, locked_table);
945 }
946 },
947 ast.Node.Id.TestDecl => @panic("TODO"),
948 else => unreachable,
836949 }
837 decl_group_consumed = true;
838 try await (async decl_group.wait() catch unreachable);
950 }
951
952 var existing_decl_it = existing_decls.iterator();
953 while (existing_decl_it.next()) |entry| {
954 // this decl was deleted
955 const existing_decl = entry.value;
956 @panic("TODO handle decl deletion");
957 }
958 }
959
960 async fn initialCompile(self: *Compilation) !void {
961 if (self.root_src_path) |root_src_path| {
962 const root_scope = blk: {
963 // TODO async/await os.path.real
964 const root_src_real_path = os.path.real(self.gpa(), root_src_path) catch |err| {
965 try self.addCompileErrorCli(root_src_path, "unable to open: {}", @errorName(err));
966 return;
967 };
968 errdefer self.gpa().free(root_src_real_path);
969
970 break :blk try Scope.Root.create(self, root_src_real_path);
971 };
972 defer root_scope.base.deref(self);
839973
840 // Now other code can rely on the decls scope having a complete list of names.
841 decls.name_future.resolve();
974 assert((try await try async self.fs_watch.addFile(root_scope.realpath, root_scope)) == null);
975 try await try async self.rebuildFile(root_scope);
842976 }
977 }
843978
979 async fn maybeLink(self: *Compilation) !void {
844980 (await (async self.prelink_group.wait() catch unreachable)) catch |err| switch (err) {
845981 error.SemanticAnalysisFailed => {},
846982 else => return err,
......@@ -861,6 +997,7 @@ pub const Compilation = struct {
861997 /// caller takes ownership of resulting Code
862998 async fn genAndAnalyzeCode(
863999 comp: *Compilation,
1000 tree_scope: *Scope.AstTree,
8641001 scope: *Scope,
8651002 node: *ast.Node,
8661003 expected_type: ?*Type,
......@@ -868,6 +1005,7 @@ pub const Compilation = struct {
8681005 const unanalyzed_code = try await (async ir.gen(
8691006 comp,
8701007 node,
1008 tree_scope,
8711009 scope,
8721010 ) catch unreachable);
8731011 defer unanalyzed_code.destroy(comp.gpa());
......@@ -894,6 +1032,7 @@ pub const Compilation = struct {
8941032
8951033 async fn addCompTimeBlock(
8961034 comp: *Compilation,
1035 tree_scope: *Scope.AstTree,
8971036 scope: *Scope,
8981037 comptime_node: *ast.Node.Comptime,
8991038 ) !void {
......@@ -902,6 +1041,7 @@ pub const Compilation = struct {
9021041
9031042 const analyzed_code = (await (async genAndAnalyzeCode(
9041043 comp,
1044 tree_scope,
9051045 scope,
9061046 comptime_node.expr,
9071047 &void_type.base,
......@@ -914,38 +1054,42 @@ pub const Compilation = struct {
9141054 analyzed_code.destroy(comp.gpa());
9151055 }
9161056
917 async fn addTopLevelDecl(self: *Compilation, decls: *Scope.Decls, decl: *Decl) !void {
918 const tree = decl.findRootScope().tree;
919 const is_export = decl.isExported(tree);
920
921 var add_to_table_resolved = false;
922 const add_to_table = async self.addDeclToTable(decls, decl) catch unreachable;
923 errdefer if (!add_to_table_resolved) cancel add_to_table; // TODO https://github.com/ziglang/zig/issues/1261
1057 async fn addTopLevelDecl(
1058 self: *Compilation,
1059 decl: *Decl,
1060 locked_table: *Decl.Table,
1061 ) !void {
1062 const is_export = decl.isExported(decl.tree_scope.tree);
9241063
9251064 if (is_export) {
9261065 try self.prelink_group.call(verifyUniqueSymbol, self, decl);
9271066 try self.prelink_group.call(resolveDecl, self, decl);
9281067 }
9291068
930 add_to_table_resolved = true;
931 try await add_to_table;
1069 const gop = try locked_table.getOrPut(decl.name);
1070 if (gop.found_existing) {
1071 try self.addCompileError(decl.tree_scope, decl.getSpan(), "redefinition of '{}'", decl.name);
1072 // TODO note: other definition here
1073 } else {
1074 gop.kv.value = decl;
1075 }
9321076 }
9331077
934 async fn addDeclToTable(self: *Compilation, decls: *Scope.Decls, decl: *Decl) !void {
935 const held = await (async decls.table.acquire() catch unreachable);
936 defer held.release();
1078 fn addCompileError(self: *Compilation, tree_scope: *Scope.AstTree, span: Span, comptime fmt: []const u8, args: ...) !void {
1079 const text = try std.fmt.allocPrint(self.gpa(), fmt, args);
1080 errdefer self.gpa().free(text);
9371081
938 if (try held.value.put(decl.name, decl)) |other_decl| {
939 try self.addCompileError(decls.base.findRoot(), decl.getSpan(), "redefinition of '{}'", decl.name);
940 // TODO note: other definition here
941 }
1082 const msg = try Msg.createFromScope(self, tree_scope, span, text);
1083 errdefer msg.destroy();
1084
1085 try self.prelink_group.call(addCompileErrorAsync, self, msg);
9421086 }
9431087
944 fn addCompileError(self: *Compilation, root: *Scope.Root, span: Span, comptime fmt: []const u8, args: ...) !void {
1088 fn addCompileErrorCli(self: *Compilation, realpath: []const u8, comptime fmt: []const u8, args: ...) !void {
9451089 const text = try std.fmt.allocPrint(self.gpa(), fmt, args);
9461090 errdefer self.gpa().free(text);
9471091
948 const msg = try Msg.createFromScope(self, root, span, text);
1092 const msg = try Msg.createFromCli(self, realpath, text);
9491093 errdefer msg.destroy();
9501094
9511095 try self.prelink_group.call(addCompileErrorAsync, self, msg);
......@@ -969,7 +1113,7 @@ pub const Compilation = struct {
9691113
9701114 if (try exported_symbol_names.value.put(decl.name, decl)) |other_decl| {
9711115 try self.addCompileError(
972 decl.findRootScope(),
1116 decl.tree_scope,
9731117 decl.getSpan(),
9741118 "exported symbol collision: '{}'",
9751119 decl.name,
......@@ -1019,7 +1163,7 @@ pub const Compilation = struct {
10191163 async fn startFindingNativeLibC(self: *Compilation) void {
10201164 await (async self.loop.yield() catch unreachable);
10211165 // we don't care if it fails, we're just trying to kick off the future resolution
1022 _ = (await (async self.event_loop_local.getNativeLibC() catch unreachable)) catch return;
1166 _ = (await (async self.zig_compiler.getNativeLibC() catch unreachable)) catch return;
10231167 }
10241168
10251169 /// General Purpose Allocator. Must free when done.
......@@ -1077,7 +1221,7 @@ pub const Compilation = struct {
10771221 var rand_bytes: [9]u8 = undefined;
10781222
10791223 {
1080 const held = await (async self.event_loop_local.prng.acquire() catch unreachable);
1224 const held = await (async self.zig_compiler.prng.acquire() catch unreachable);
10811225 defer held.release();
10821226
10831227 held.value.random.bytes(rand_bytes[0..]);
......@@ -1093,18 +1237,24 @@ pub const Compilation = struct {
10931237 }
10941238
10951239 /// Returns a value which has been ref()'d once
1096 async fn analyzeConstValue(comp: *Compilation, scope: *Scope, node: *ast.Node, expected_type: *Type) !*Value {
1097 const analyzed_code = try await (async comp.genAndAnalyzeCode(scope, node, expected_type) catch unreachable);
1240 async fn analyzeConstValue(
1241 comp: *Compilation,
1242 tree_scope: *Scope.AstTree,
1243 scope: *Scope,
1244 node: *ast.Node,
1245 expected_type: *Type,
1246 ) !*Value {
1247 const analyzed_code = try await (async comp.genAndAnalyzeCode(tree_scope, scope, node, expected_type) catch unreachable);
10981248 defer analyzed_code.destroy(comp.gpa());
10991249
11001250 return analyzed_code.getCompTimeResult(comp);
11011251 }
11021252
1103 async fn analyzeTypeExpr(comp: *Compilation, scope: *Scope, node: *ast.Node) !*Type {
1253 async fn analyzeTypeExpr(comp: *Compilation, tree_scope: *Scope.AstTree, scope: *Scope, node: *ast.Node) !*Type {
11041254 const meta_type = &Type.MetaType.get(comp).base;
11051255 defer meta_type.base.deref(comp);
11061256
1107 const result_val = try await (async comp.analyzeConstValue(scope, node, meta_type) catch unreachable);
1257 const result_val = try await (async comp.analyzeConstValue(tree_scope, scope, node, meta_type) catch unreachable);
11081258 errdefer result_val.base.deref(comp);
11091259
11101260 return result_val.cast(Type).?;
......@@ -1120,13 +1270,6 @@ pub const Compilation = struct {
11201270 }
11211271};
11221272
1123fn printError(comptime format: []const u8, args: ...) !void {
1124 var stderr_file = try std.io.getStdErr();
1125 var stderr_file_out_stream = std.io.FileOutStream.init(&stderr_file);
1126 const out_stream = &stderr_file_out_stream.stream;
1127 try out_stream.print(format, args);
1128}
1129
11301273fn parseVisibToken(tree: *ast.Tree, optional_token_index: ?ast.TokenIndex) Visib {
11311274 if (optional_token_index) |token_index| {
11321275 const token = tree.tokens.at(token_index);
......@@ -1150,12 +1293,14 @@ async fn generateDecl(comp: *Compilation, decl: *Decl) !void {
11501293}
11511294
11521295async fn generateDeclFn(comp: *Compilation, fn_decl: *Decl.Fn) !void {
1296 const tree_scope = fn_decl.base.tree_scope;
1297
11531298 const body_node = fn_decl.fn_proto.body_node orelse return await (async generateDeclFnProto(comp, fn_decl) catch unreachable);
11541299
11551300 const fndef_scope = try Scope.FnDef.create(comp, fn_decl.base.parent_scope);
11561301 defer fndef_scope.base.deref(comp);
11571302
1158 const fn_type = try await (async analyzeFnType(comp, fn_decl.base.parent_scope, fn_decl.fn_proto) catch unreachable);
1303 const fn_type = try await (async analyzeFnType(comp, tree_scope, fn_decl.base.parent_scope, fn_decl.fn_proto) catch unreachable);
11591304 defer fn_type.base.base.deref(comp);
11601305
11611306 var symbol_name = try std.Buffer.init(comp.gpa(), fn_decl.base.name);
......@@ -1168,18 +1313,17 @@ async fn generateDeclFn(comp: *Compilation, fn_decl: *Decl.Fn) !void {
11681313 symbol_name_consumed = true;
11691314
11701315 // Define local parameter variables
1171 const root_scope = fn_decl.base.findRootScope();
11721316 for (fn_type.key.data.Normal.params) |param, i| {
11731317 //AstNode *param_decl_node = get_param_decl_node(fn_table_entry, i);
11741318 const param_decl = @fieldParentPtr(ast.Node.ParamDecl, "base", fn_decl.fn_proto.params.at(i).*);
11751319 const name_token = param_decl.name_token orelse {
1176 try comp.addCompileError(root_scope, Span{
1320 try comp.addCompileError(tree_scope, Span{
11771321 .first = param_decl.firstToken(),
11781322 .last = param_decl.type_node.firstToken(),
11791323 }, "missing parameter name");
11801324 return error.SemanticAnalysisFailed;
11811325 };
1182 const param_name = root_scope.tree.tokenSlice(name_token);
1326 const param_name = tree_scope.tree.tokenSlice(name_token);
11831327
11841328 // if (is_noalias && get_codegen_ptr_type(param_type) == nullptr) {
11851329 // add_node_error(g, param_decl_node, buf_sprintf("noalias on non-pointer parameter"));
......@@ -1201,6 +1345,7 @@ async fn generateDeclFn(comp: *Compilation, fn_decl: *Decl.Fn) !void {
12011345 }
12021346
12031347 const analyzed_code = try await (async comp.genAndAnalyzeCode(
1348 tree_scope,
12041349 fn_val.child_scope,
12051350 body_node,
12061351 fn_type.key.data.Normal.return_type,
......@@ -1231,12 +1376,17 @@ fn getZigDir(allocator: *mem.Allocator) ![]u8 {
12311376 return os.getAppDataDir(allocator, "zig");
12321377}
12331378
1234async fn analyzeFnType(comp: *Compilation, scope: *Scope, fn_proto: *ast.Node.FnProto) !*Type.Fn {
1379async fn analyzeFnType(
1380 comp: *Compilation,
1381 tree_scope: *Scope.AstTree,
1382 scope: *Scope,
1383 fn_proto: *ast.Node.FnProto,
1384) !*Type.Fn {
12351385 const return_type_node = switch (fn_proto.return_type) {
12361386 ast.Node.FnProto.ReturnType.Explicit => |n| n,
12371387 ast.Node.FnProto.ReturnType.InferErrorSet => |n| n,
12381388 };
1239 const return_type = try await (async comp.analyzeTypeExpr(scope, return_type_node) catch unreachable);
1389 const return_type = try await (async comp.analyzeTypeExpr(tree_scope, scope, return_type_node) catch unreachable);
12401390 return_type.base.deref(comp);
12411391
12421392 var params = ArrayList(Type.Fn.Param).init(comp.gpa());
......@@ -1252,7 +1402,7 @@ async fn analyzeFnType(comp: *Compilation, scope: *Scope, fn_proto: *ast.Node.Fn
12521402 var it = fn_proto.params.iterator(0);
12531403 while (it.next()) |param_node_ptr| {
12541404 const param_node = param_node_ptr.*.cast(ast.Node.ParamDecl).?;
1255 const param_type = try await (async comp.analyzeTypeExpr(scope, param_node.type_node) catch unreachable);
1405 const param_type = try await (async comp.analyzeTypeExpr(tree_scope, scope, param_node.type_node) catch unreachable);
12561406 errdefer param_type.base.deref(comp);
12571407 try params.append(Type.Fn.Param{
12581408 .typ = param_type,
......@@ -1289,7 +1439,12 @@ async fn analyzeFnType(comp: *Compilation, scope: *Scope, fn_proto: *ast.Node.Fn
12891439}
12901440
12911441async fn generateDeclFnProto(comp: *Compilation, fn_decl: *Decl.Fn) !void {
1292 const fn_type = try await (async analyzeFnType(comp, fn_decl.base.parent_scope, fn_decl.fn_proto) catch unreachable);
1442 const fn_type = try await (async analyzeFnType(
1443 comp,
1444 fn_decl.base.tree_scope,
1445 fn_decl.base.parent_scope,
1446 fn_decl.fn_proto,
1447 ) catch unreachable);
12931448 defer fn_type.base.base.deref(comp);
12941449
12951450 var symbol_name = try std.Buffer.init(comp.gpa(), fn_decl.base.name);
......@@ -1301,3 +1456,14 @@ async fn generateDeclFnProto(comp: *Compilation, fn_decl: *Decl.Fn) !void {
13011456 fn_decl.value = Decl.Fn.Val{ .FnProto = fn_proto_val };
13021457 symbol_name_consumed = true;
13031458}
1459
1460// TODO these are hacks which should probably be solved by the language
1461fn getAwaitResult(allocator: *Allocator, handle: var) @typeInfo(@typeOf(handle)).Promise.child.? {
1462 var result: ?@typeInfo(@typeOf(handle)).Promise.child.? = null;
1463 cancel (async<allocator> getAwaitResultAsync(handle, &result) catch unreachable);
1464 return result.?;
1465}
1466
1467async fn getAwaitResultAsync(handle: var, out: *?@typeInfo(@typeOf(handle)).Promise.child.?) void {
1468 out.* = await handle;
1469}
src-self-hosted/decl.zig+8-1
......@@ -17,8 +17,16 @@ pub const Decl = struct {
1717 resolution: event.Future(Compilation.BuildError!void),
1818 parent_scope: *Scope,
1919
20 // TODO when we destroy the decl, deref the tree scope
21 tree_scope: *Scope.AstTree,
22
2023 pub const Table = std.HashMap([]const u8, *Decl, mem.hash_slice_u8, mem.eql_slice_u8);
2124
25 pub fn cast(base: *Decl, comptime T: type) ?*T {
26 if (base.id != @field(Id, @typeName(T))) return null;
27 return @fieldParentPtr(T, "base", base);
28 }
29
2230 pub fn isExported(base: *const Decl, tree: *ast.Tree) bool {
2331 switch (base.id) {
2432 Id.Fn => {
......@@ -95,4 +103,3 @@ pub const Decl = struct {
95103 base: Decl,
96104 };
97105};
98
src-self-hosted/errmsg.zig+86-39
......@@ -33,35 +33,48 @@ pub const Span = struct {
3333};
3434
3535pub const Msg = struct {
36 span: Span,
3736 text: []u8,
37 realpath: []u8,
3838 data: Data,
3939
4040 const Data = union(enum) {
41 Cli: Cli,
4142 PathAndTree: PathAndTree,
4243 ScopeAndComp: ScopeAndComp,
4344 };
4445
4546 const PathAndTree = struct {
46 realpath: []const u8,
47 span: Span,
4748 tree: *ast.Tree,
4849 allocator: *mem.Allocator,
4950 };
5051
5152 const ScopeAndComp = struct {
52 root_scope: *Scope.Root,
53 span: Span,
54 tree_scope: *Scope.AstTree,
5355 compilation: *Compilation,
5456 };
5557
58 const Cli = struct {
59 allocator: *mem.Allocator,
60 };
61
5662 pub fn destroy(self: *Msg) void {
5763 switch (self.data) {
64 Data.Cli => |cli| {
65 cli.allocator.free(self.text);
66 cli.allocator.free(self.realpath);
67 cli.allocator.destroy(self);
68 },
5869 Data.PathAndTree => |path_and_tree| {
5970 path_and_tree.allocator.free(self.text);
71 path_and_tree.allocator.free(self.realpath);
6072 path_and_tree.allocator.destroy(self);
6173 },
6274 Data.ScopeAndComp => |scope_and_comp| {
63 scope_and_comp.root_scope.base.deref(scope_and_comp.compilation);
75 scope_and_comp.tree_scope.base.deref(scope_and_comp.compilation);
6476 scope_and_comp.compilation.gpa().free(self.text);
77 scope_and_comp.compilation.gpa().free(self.realpath);
6578 scope_and_comp.compilation.gpa().destroy(self);
6679 },
6780 }
......@@ -69,6 +82,7 @@ pub const Msg = struct {
6982
7083 fn getAllocator(self: *const Msg) *mem.Allocator {
7184 switch (self.data) {
85 Data.Cli => |cli| return cli.allocator,
7286 Data.PathAndTree => |path_and_tree| {
7387 return path_and_tree.allocator;
7488 },
......@@ -78,71 +92,93 @@ pub const Msg = struct {
7892 }
7993 }
8094
81 pub fn getRealPath(self: *const Msg) []const u8 {
82 switch (self.data) {
83 Data.PathAndTree => |path_and_tree| {
84 return path_and_tree.realpath;
85 },
86 Data.ScopeAndComp => |scope_and_comp| {
87 return scope_and_comp.root_scope.realpath;
88 },
89 }
90 }
91
9295 pub fn getTree(self: *const Msg) *ast.Tree {
9396 switch (self.data) {
97 Data.Cli => unreachable,
9498 Data.PathAndTree => |path_and_tree| {
9599 return path_and_tree.tree;
96100 },
97101 Data.ScopeAndComp => |scope_and_comp| {
98 return scope_and_comp.root_scope.tree;
102 return scope_and_comp.tree_scope.tree;
99103 },
100104 }
101105 }
102106
107 pub fn getSpan(self: *const Msg) Span {
108 return switch (self.data) {
109 Data.Cli => unreachable,
110 Data.PathAndTree => |path_and_tree| path_and_tree.span,
111 Data.ScopeAndComp => |scope_and_comp| scope_and_comp.span,
112 };
113 }
114
103115 /// 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 {
116 /// References tree_scope, and derefs when the msg is freed
117 pub fn createFromScope(comp: *Compilation, tree_scope: *Scope.AstTree, span: Span, text: []u8) !*Msg {
118 const realpath = try mem.dupe(comp.gpa(), u8, tree_scope.root().realpath);
119 errdefer comp.gpa().free(realpath);
120
106121 const msg = try comp.gpa().create(Msg{
107122 .text = text,
108 .span = span,
123 .realpath = realpath,
109124 .data = Data{
110125 .ScopeAndComp = ScopeAndComp{
111 .root_scope = root_scope,
126 .tree_scope = tree_scope,
112127 .compilation = comp,
128 .span = span,
113129 },
114130 },
115131 });
116 root_scope.base.ref();
132 tree_scope.base.ref();
133 return msg;
134 }
135
136 /// Caller owns returned Msg and must free with `allocator`
137 /// allocator will additionally be used for printing messages later.
138 pub fn createFromCli(comp: *Compilation, realpath: []const u8, text: []u8) !*Msg {
139 const realpath_copy = try mem.dupe(comp.gpa(), u8, realpath);
140 errdefer comp.gpa().free(realpath_copy);
141
142 const msg = try comp.gpa().create(Msg{
143 .text = text,
144 .realpath = realpath_copy,
145 .data = Data{
146 .Cli = Cli{ .allocator = comp.gpa() },
147 },
148 });
117149 return msg;
118150 }
119151
120152 pub fn createFromParseErrorAndScope(
121153 comp: *Compilation,
122 root_scope: *Scope.Root,
154 tree_scope: *Scope.AstTree,
123155 parse_error: *const ast.Error,
124156 ) !*Msg {
125157 const loc_token = parse_error.loc();
126158 var text_buf = try std.Buffer.initSize(comp.gpa(), 0);
127159 defer text_buf.deinit();
128160
161 const realpath_copy = try mem.dupe(comp.gpa(), u8, tree_scope.root().realpath);
162 errdefer comp.gpa().free(realpath_copy);
163
129164 var out_stream = &std.io.BufferOutStream.init(&text_buf).stream;
130 try parse_error.render(&root_scope.tree.tokens, out_stream);
165 try parse_error.render(&tree_scope.tree.tokens, out_stream);
131166
132167 const msg = try comp.gpa().create(Msg{
133168 .text = undefined,
134 .span = Span{
135 .first = loc_token,
136 .last = loc_token,
137 },
169 .realpath = realpath_copy,
138170 .data = Data{
139171 .ScopeAndComp = ScopeAndComp{
140 .root_scope = root_scope,
172 .tree_scope = tree_scope,
141173 .compilation = comp,
174 .span = Span{
175 .first = loc_token,
176 .last = loc_token,
177 },
142178 },
143179 },
144180 });
145 root_scope.base.ref();
181 tree_scope.base.ref();
146182 msg.text = text_buf.toOwnedSlice();
147183 return msg;
148184 }
......@@ -161,22 +197,25 @@ pub const Msg = struct {
161197 var text_buf = try std.Buffer.initSize(allocator, 0);
162198 defer text_buf.deinit();
163199
200 const realpath_copy = try mem.dupe(allocator, u8, realpath);
201 errdefer allocator.free(realpath_copy);
202
164203 var out_stream = &std.io.BufferOutStream.init(&text_buf).stream;
165204 try parse_error.render(&tree.tokens, out_stream);
166205
167206 const msg = try allocator.create(Msg{
168207 .text = undefined,
208 .realpath = realpath_copy,
169209 .data = Data{
170210 .PathAndTree = PathAndTree{
171211 .allocator = allocator,
172 .realpath = realpath,
173212 .tree = tree,
213 .span = Span{
214 .first = loc_token,
215 .last = loc_token,
216 },
174217 },
175218 },
176 .span = Span{
177 .first = loc_token,
178 .last = loc_token,
179 },
180219 });
181220 msg.text = text_buf.toOwnedSlice();
182221 errdefer allocator.destroy(msg);
......@@ -185,20 +224,28 @@ pub const Msg = struct {
185224 }
186225
187226 pub fn printToStream(msg: *const Msg, stream: var, color_on: bool) !void {
227 switch (msg.data) {
228 Data.Cli => {
229 try stream.print("{}:-:-: error: {}\n", msg.realpath, msg.text);
230 return;
231 },
232 else => {},
233 }
234
188235 const allocator = msg.getAllocator();
189 const realpath = msg.getRealPath();
190236 const tree = msg.getTree();
191237
192238 const cwd = try os.getCwd(allocator);
193239 defer allocator.free(cwd);
194240
195 const relpath = try os.path.relative(allocator, cwd, realpath);
241 const relpath = try os.path.relative(allocator, cwd, msg.realpath);
196242 defer allocator.free(relpath);
197243
198 const path = if (relpath.len < realpath.len) relpath else realpath;
244 const path = if (relpath.len < msg.realpath.len) relpath else msg.realpath;
245 const span = msg.getSpan();
199246
200 const first_token = tree.tokens.at(msg.span.first);
201 const last_token = tree.tokens.at(msg.span.last);
247 const first_token = tree.tokens.at(span.first);
248 const last_token = tree.tokens.at(span.last);
202249 const start_loc = tree.tokenLocationPtr(0, first_token);
203250 const end_loc = tree.tokenLocationPtr(first_token.end, last_token);
204251 if (!color_on) {
src-self-hosted/ir.zig+26-22
......@@ -961,6 +961,7 @@ pub const Code = struct {
961961 basic_block_list: std.ArrayList(*BasicBlock),
962962 arena: std.heap.ArenaAllocator,
963963 return_type: ?*Type,
964 tree_scope: *Scope.AstTree,
964965
965966 /// allocator is comp.gpa()
966967 pub fn destroy(self: *Code, allocator: *Allocator) void {
......@@ -990,14 +991,14 @@ pub const Code = struct {
990991 return ret_value.val.KnownValue.getRef();
991992 }
992993 try comp.addCompileError(
993 ret_value.scope.findRoot(),
994 self.tree_scope,
994995 ret_value.span,
995996 "unable to evaluate constant expression",
996997 );
997998 return error.SemanticAnalysisFailed;
998999 } else if (inst.hasSideEffects()) {
9991000 try comp.addCompileError(
1000 inst.scope.findRoot(),
1001 self.tree_scope,
10011002 inst.span,
10021003 "unable to evaluate constant expression",
10031004 );
......@@ -1013,25 +1014,24 @@ pub const Builder = struct {
10131014 code: *Code,
10141015 current_basic_block: *BasicBlock,
10151016 next_debug_id: usize,
1016 root_scope: *Scope.Root,
10171017 is_comptime: bool,
10181018 is_async: bool,
10191019 begin_scope: ?*Scope,
10201020
10211021 pub const Error = Analyze.Error;
10221022
1023 pub fn init(comp: *Compilation, root_scope: *Scope.Root, begin_scope: ?*Scope) !Builder {
1023 pub fn init(comp: *Compilation, tree_scope: *Scope.AstTree, begin_scope: ?*Scope) !Builder {
10241024 const code = try comp.gpa().create(Code{
10251025 .basic_block_list = undefined,
10261026 .arena = std.heap.ArenaAllocator.init(comp.gpa()),
10271027 .return_type = null,
1028 .tree_scope = tree_scope,
10281029 });
10291030 code.basic_block_list = std.ArrayList(*BasicBlock).init(&code.arena.allocator);
10301031 errdefer code.destroy(comp.gpa());
10311032
10321033 return Builder{
10331034 .comp = comp,
1034 .root_scope = root_scope,
10351035 .current_basic_block = undefined,
10361036 .code = code,
10371037 .next_debug_id = 0,
......@@ -1292,6 +1292,7 @@ pub const Builder = struct {
12921292 Scope.Id.FnDef => return false,
12931293 Scope.Id.Decls => unreachable,
12941294 Scope.Id.Root => unreachable,
1295 Scope.Id.AstTree => unreachable,
12951296 Scope.Id.Block,
12961297 Scope.Id.Defer,
12971298 Scope.Id.DeferExpr,
......@@ -1302,7 +1303,7 @@ pub const Builder = struct {
13021303 }
13031304
13041305 pub fn genIntLit(irb: *Builder, int_lit: *ast.Node.IntegerLiteral, scope: *Scope) !*Inst {
1305 const int_token = irb.root_scope.tree.tokenSlice(int_lit.token);
1306 const int_token = irb.code.tree_scope.tree.tokenSlice(int_lit.token);
13061307
13071308 var base: u8 = undefined;
13081309 var rest: []const u8 = undefined;
......@@ -1341,7 +1342,7 @@ pub const Builder = struct {
13411342 }
13421343
13431344 pub async fn genStrLit(irb: *Builder, str_lit: *ast.Node.StringLiteral, scope: *Scope) !*Inst {
1344 const str_token = irb.root_scope.tree.tokenSlice(str_lit.token);
1345 const str_token = irb.code.tree_scope.tree.tokenSlice(str_lit.token);
13451346 const src_span = Span.token(str_lit.token);
13461347
13471348 var bad_index: usize = undefined;
......@@ -1349,7 +1350,7 @@ pub const Builder = struct {
13491350 error.OutOfMemory => return error.OutOfMemory,
13501351 error.InvalidCharacter => {
13511352 try irb.comp.addCompileError(
1352 irb.root_scope,
1353 irb.code.tree_scope,
13531354 src_span,
13541355 "invalid character in string literal: '{c}'",
13551356 str_token[bad_index],
......@@ -1427,7 +1428,7 @@ pub const Builder = struct {
14271428
14281429 if (statement_node.cast(ast.Node.Defer)) |defer_node| {
14291430 // defer starts a new scope
1430 const defer_token = irb.root_scope.tree.tokens.at(defer_node.defer_token);
1431 const defer_token = irb.code.tree_scope.tree.tokens.at(defer_node.defer_token);
14311432 const kind = switch (defer_token.id) {
14321433 Token.Id.Keyword_defer => Scope.Defer.Kind.ScopeExit,
14331434 Token.Id.Keyword_errdefer => Scope.Defer.Kind.ErrorExit,
......@@ -1513,7 +1514,7 @@ pub const Builder = struct {
15131514 const src_span = Span.token(control_flow_expr.ltoken);
15141515 if (scope.findFnDef() == null) {
15151516 try irb.comp.addCompileError(
1516 irb.root_scope,
1517 irb.code.tree_scope,
15171518 src_span,
15181519 "return expression outside function definition",
15191520 );
......@@ -1523,7 +1524,7 @@ pub const Builder = struct {
15231524 if (scope.findDeferExpr()) |scope_defer_expr| {
15241525 if (!scope_defer_expr.reported_err) {
15251526 try irb.comp.addCompileError(
1526 irb.root_scope,
1527 irb.code.tree_scope,
15271528 src_span,
15281529 "cannot return from defer expression",
15291530 );
......@@ -1599,7 +1600,7 @@ pub const Builder = struct {
15991600
16001601 pub async fn genIdentifier(irb: *Builder, identifier: *ast.Node.Identifier, scope: *Scope, lval: LVal) !*Inst {
16011602 const src_span = Span.token(identifier.token);
1602 const name = irb.root_scope.tree.tokenSlice(identifier.token);
1603 const name = irb.code.tree_scope.tree.tokenSlice(identifier.token);
16031604
16041605 //if (buf_eql_str(variable_name, "_") && lval == LValPtr) {
16051606 // IrInstructionConst *const_instruction = ir_build_instruction<IrInstructionConst>(irb, scope, node);
......@@ -1622,7 +1623,7 @@ pub const Builder = struct {
16221623 }
16231624 } else |err| switch (err) {
16241625 error.Overflow => {
1625 try irb.comp.addCompileError(irb.root_scope, src_span, "integer too large");
1626 try irb.comp.addCompileError(irb.code.tree_scope, src_span, "integer too large");
16261627 return error.SemanticAnalysisFailed;
16271628 },
16281629 error.OutOfMemory => return error.OutOfMemory,
......@@ -1656,7 +1657,7 @@ pub const Builder = struct {
16561657 // TODO put a variable of same name with invalid type in global scope
16571658 // so that future references to this same name will find a variable with an invalid type
16581659
1659 try irb.comp.addCompileError(irb.root_scope, src_span, "unknown identifier '{}'", name);
1660 try irb.comp.addCompileError(irb.code.tree_scope, src_span, "unknown identifier '{}'", name);
16601661 return error.SemanticAnalysisFailed;
16611662 }
16621663
......@@ -1689,6 +1690,7 @@ pub const Builder = struct {
16891690 => scope = scope.parent orelse break,
16901691
16911692 Scope.Id.DeferExpr => unreachable,
1693 Scope.Id.AstTree => unreachable,
16921694 }
16931695 }
16941696 return result;
......@@ -1740,6 +1742,7 @@ pub const Builder = struct {
17401742 => scope = scope.parent orelse return is_noreturn,
17411743
17421744 Scope.Id.DeferExpr => unreachable,
1745 Scope.Id.AstTree => unreachable,
17431746 }
17441747 }
17451748 }
......@@ -1929,8 +1932,9 @@ pub const Builder = struct {
19291932 Scope.Id.Root => return Ident.NotFound,
19301933 Scope.Id.Decls => {
19311934 const decls = @fieldParentPtr(Scope.Decls, "base", s);
1932 const table = await (async decls.getTableReadOnly() catch unreachable);
1933 if (table.get(name)) |entry| {
1935 const locked_table = await (async decls.table.acquireRead() catch unreachable);
1936 defer locked_table.release();
1937 if (locked_table.value.get(name)) |entry| {
19341938 return Ident{ .Decl = entry.value };
19351939 }
19361940 },
......@@ -1967,8 +1971,8 @@ const Analyze = struct {
19671971 OutOfMemory,
19681972 };
19691973
1970 pub fn init(comp: *Compilation, root_scope: *Scope.Root, explicit_return_type: ?*Type) !Analyze {
1971 var irb = try Builder.init(comp, root_scope, null);
1974 pub fn init(comp: *Compilation, tree_scope: *Scope.AstTree, explicit_return_type: ?*Type) !Analyze {
1975 var irb = try Builder.init(comp, tree_scope, null);
19721976 errdefer irb.abort();
19731977
19741978 return Analyze{
......@@ -2046,7 +2050,7 @@ const Analyze = struct {
20462050 }
20472051
20482052 fn addCompileError(self: *Analyze, span: Span, comptime fmt: []const u8, args: ...) !void {
2049 return self.irb.comp.addCompileError(self.irb.root_scope, span, fmt, args);
2053 return self.irb.comp.addCompileError(self.irb.code.tree_scope, span, fmt, args);
20502054 }
20512055
20522056 fn resolvePeerTypes(self: *Analyze, expected_type: ?*Type, peers: []const *Inst) Analyze.Error!*Type {
......@@ -2534,9 +2538,10 @@ const Analyze = struct {
25342538pub async fn gen(
25352539 comp: *Compilation,
25362540 body_node: *ast.Node,
2541 tree_scope: *Scope.AstTree,
25372542 scope: *Scope,
25382543) !*Code {
2539 var irb = try Builder.init(comp, scope.findRoot(), scope);
2544 var irb = try Builder.init(comp, tree_scope, scope);
25402545 errdefer irb.abort();
25412546
25422547 const entry_block = try irb.createBasicBlock(scope, c"Entry");
......@@ -2554,9 +2559,8 @@ pub async fn gen(
25542559
25552560pub async fn analyze(comp: *Compilation, old_code: *Code, expected_type: ?*Type) !*Code {
25562561 const old_entry_bb = old_code.basic_block_list.at(0);
2557 const root_scope = old_entry_bb.scope.findRoot();
25582562
2559 var ira = try Analyze.init(comp, root_scope, expected_type);
2563 var ira = try Analyze.init(comp, old_code.tree_scope, expected_type);
25602564 errdefer ira.abort();
25612565
25622566 const new_entry_bb = try ira.getNewBasicBlock(old_entry_bb, null);
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/link.zig+2-2
......@@ -61,7 +61,7 @@ pub async fn link(comp: *Compilation) !void {
6161 ctx.libc = ctx.comp.override_libc orelse blk: {
6262 switch (comp.target) {
6363 Target.Native => {
64 break :blk (await (async comp.event_loop_local.getNativeLibC() catch unreachable)) catch return error.LibCRequiredButNotProvidedOrFound;
64 break :blk (await (async comp.zig_compiler.getNativeLibC() catch unreachable)) catch return error.LibCRequiredButNotProvidedOrFound;
6565 },
6666 else => return error.LibCRequiredButNotProvidedOrFound,
6767 }
......@@ -83,7 +83,7 @@ pub async fn link(comp: *Compilation) !void {
8383
8484 {
8585 // LLD is not thread-safe, so we grab a global lock.
86 const held = await (async comp.event_loop_local.lld_lock.acquire() catch unreachable);
86 const held = await (async comp.zig_compiler.lld_lock.acquire() catch unreachable);
8787 defer held.release();
8888
8989 // Not evented I/O. LLD does its own multithreading internally.
src-self-hosted/main.zig+169-107
......@@ -14,7 +14,7 @@ const c = @import("c.zig");
1414const introspect = @import("introspect.zig");
1515const Args = arg.Args;
1616const Flag = arg.Flag;
17const EventLoopLocal = @import("compilation.zig").EventLoopLocal;
17const ZigCompiler = @import("compilation.zig").ZigCompiler;
1818const Compilation = @import("compilation.zig").Compilation;
1919const Target = @import("target.zig").Target;
2020const errmsg = @import("errmsg.zig");
......@@ -24,6 +24,8 @@ var stderr_file: os.File = undefined;
2424var stderr: *io.OutStream(io.FileOutStream.Error) = undefined;
2525var stdout: *io.OutStream(io.FileOutStream.Error) = undefined;
2626
27const max_src_size = 2 * 1024 * 1024 * 1024; // 2 GiB
28
2729const usage =
2830 \\usage: zig [command] [options]
2931 \\
......@@ -371,6 +373,16 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Co
371373 os.exit(1);
372374 }
373375
376 var clang_argv_buf = ArrayList([]const u8).init(allocator);
377 defer clang_argv_buf.deinit();
378
379 const mllvm_flags = flags.many("mllvm");
380 for (mllvm_flags) |mllvm| {
381 try clang_argv_buf.append("-mllvm");
382 try clang_argv_buf.append(mllvm);
383 }
384 try ZigCompiler.setLlvmArgv(allocator, mllvm_flags);
385
374386 const zig_lib_dir = introspect.resolveZigLibDir(allocator) catch os.exit(1);
375387 defer allocator.free(zig_lib_dir);
376388
......@@ -380,11 +392,11 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Co
380392 try loop.initMultiThreaded(allocator);
381393 defer loop.deinit();
382394
383 var event_loop_local = try EventLoopLocal.init(&loop);
384 defer event_loop_local.deinit();
395 var zig_compiler = try ZigCompiler.init(&loop);
396 defer zig_compiler.deinit();
385397
386398 var comp = try Compilation.create(
387 &event_loop_local,
399 &zig_compiler,
388400 root_name,
389401 root_source_file,
390402 Target.Native,
......@@ -413,16 +425,6 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Co
413425 comp.linker_script = flags.single("linker-script");
414426 comp.each_lib_rpath = flags.present("each-lib-rpath");
415427
416 var clang_argv_buf = ArrayList([]const u8).init(allocator);
417 defer clang_argv_buf.deinit();
418
419 const mllvm_flags = flags.many("mllvm");
420 for (mllvm_flags) |mllvm| {
421 try clang_argv_buf.append("-mllvm");
422 try clang_argv_buf.append(mllvm);
423 }
424
425 comp.llvm_argv = mllvm_flags;
426428 comp.clang_argv = clang_argv_buf.toSliceConst();
427429
428430 comp.strip = flags.present("strip");
......@@ -465,30 +467,34 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Co
465467 comp.link_out_file = flags.single("output");
466468 comp.link_objects = link_objects;
467469
468 try comp.build();
470 comp.start();
469471 const process_build_events_handle = try async<loop.allocator> processBuildEvents(comp, color);
470472 defer cancel process_build_events_handle;
471473 loop.run();
472474}
473475
474476async 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 },
477 var count: usize = 0;
478 while (true) {
479 // TODO directly awaiting async should guarantee memory allocation elision
480 const build_event = await (async comp.events.get() catch unreachable);
481 count += 1;
482
483 switch (build_event) {
484 Compilation.Event.Ok => {
485 stderr.print("Build {} succeeded\n", count) catch os.exit(1);
486 },
487 Compilation.Event.Error => |err| {
488 stderr.print("Build {} failed: {}\n", count, @errorName(err)) catch os.exit(1);
489 },
490 Compilation.Event.Fail => |msgs| {
491 stderr.print("Build {} compile errors:\n", count) catch os.exit(1);
492 for (msgs) |msg| {
493 defer msg.destroy();
494 msg.printToFile(&stderr_file, color) catch os.exit(1);
495 }
496 },
497 }
492498 }
493499}
494500
......@@ -528,33 +534,12 @@ const args_fmt_spec = []Flag{
528534};
529535
530536const Fmt = struct {
531 seen: std.HashMap([]const u8, void, mem.hash_slice_u8, mem.eql_slice_u8),
532 queue: std.LinkedList([]const u8),
537 seen: event.Locked(SeenMap),
533538 any_error: bool,
539 color: errmsg.Color,
540 loop: *event.Loop,
534541
535 // file_path must outlive Fmt
536 fn addToQueue(self: *Fmt, file_path: []const u8) !void {
537 const new_node = try self.seen.allocator.create(std.LinkedList([]const u8).Node{
538 .prev = undefined,
539 .next = undefined,
540 .data = file_path,
541 });
542
543 if (try self.seen.put(file_path, {})) |_| return;
544
545 self.queue.append(new_node);
546 }
547
548 fn addDirToQueue(self: *Fmt, file_path: []const u8) !void {
549 var dir = try std.os.Dir.open(self.seen.allocator, file_path);
550 defer dir.close();
551 while (try dir.next()) |entry| {
552 if (entry.kind == std.os.Dir.Entry.Kind.Directory or mem.endsWith(u8, entry.name, ".zig")) {
553 const full_path = try os.path.join(self.seen.allocator, file_path, entry.name);
554 try self.addToQueue(full_path);
555 }
556 }
557 }
542 const SeenMap = std.HashMap([]const u8, void, mem.hash_slice_u8, mem.eql_slice_u8);
558543};
559544
560545fn parseLibcPaths(allocator: *Allocator, libc: *LibCInstallation, libc_paths_file: []const u8) void {
......@@ -587,17 +572,17 @@ fn cmdLibC(allocator: *Allocator, args: []const []const u8) !void {
587572 try loop.initMultiThreaded(allocator);
588573 defer loop.deinit();
589574
590 var event_loop_local = try EventLoopLocal.init(&loop);
591 defer event_loop_local.deinit();
575 var zig_compiler = try ZigCompiler.init(&loop);
576 defer zig_compiler.deinit();
592577
593 const handle = try async<loop.allocator> findLibCAsync(&event_loop_local);
578 const handle = try async<loop.allocator> findLibCAsync(&zig_compiler);
594579 defer cancel handle;
595580
596581 loop.run();
597582}
598583
599async fn findLibCAsync(event_loop_local: *EventLoopLocal) void {
600 const libc = (await (async event_loop_local.getNativeLibC() catch unreachable)) catch |err| {
584async fn findLibCAsync(zig_compiler: *ZigCompiler) void {
585 const libc = (await (async zig_compiler.getNativeLibC() catch unreachable)) catch |err| {
601586 stderr.print("unable to find libc: {}\n", @errorName(err)) catch os.exit(1);
602587 os.exit(1);
603588 };
......@@ -636,7 +621,7 @@ fn cmdFmt(allocator: *Allocator, args: []const []const u8) !void {
636621 var stdin_file = try io.getStdIn();
637622 var stdin = io.FileInStream.init(&stdin_file);
638623
639 const source_code = try stdin.stream.readAllAlloc(allocator, @maxValue(usize));
624 const source_code = try stdin.stream.readAllAlloc(allocator, max_src_size);
640625 defer allocator.free(source_code);
641626
642627 var tree = std.zig.parse(allocator, source_code) catch |err| {
......@@ -665,66 +650,143 @@ fn cmdFmt(allocator: *Allocator, args: []const []const u8) !void {
665650 os.exit(1);
666651 }
667652
653 var loop: event.Loop = undefined;
654 try loop.initMultiThreaded(allocator);
655 defer loop.deinit();
656
657 var result: FmtError!void = undefined;
658 const main_handle = try async<allocator> asyncFmtMainChecked(
659 &result,
660 &loop,
661 flags,
662 color,
663 );
664 defer cancel main_handle;
665 loop.run();
666 return result;
667}
668
669async fn asyncFmtMainChecked(
670 result: *(FmtError!void),
671 loop: *event.Loop,
672 flags: *const Args,
673 color: errmsg.Color,
674) void {
675 result.* = await (async asyncFmtMain(loop, flags, color) catch unreachable);
676}
677
678const FmtError = error{
679 SystemResources,
680 OperationAborted,
681 IoPending,
682 BrokenPipe,
683 Unexpected,
684 WouldBlock,
685 FileClosed,
686 DestinationAddressRequired,
687 DiskQuota,
688 FileTooBig,
689 InputOutput,
690 NoSpaceLeft,
691 AccessDenied,
692 OutOfMemory,
693 RenameAcrossMountPoints,
694 ReadOnlyFileSystem,
695 LinkQuotaExceeded,
696 FileBusy,
697} || os.File.OpenError;
698
699async fn asyncFmtMain(
700 loop: *event.Loop,
701 flags: *const Args,
702 color: errmsg.Color,
703) FmtError!void {
704 suspend {
705 resume @handle();
706 }
668707 var fmt = Fmt{
669 .seen = std.HashMap([]const u8, void, mem.hash_slice_u8, mem.eql_slice_u8).init(allocator),
670 .queue = std.LinkedList([]const u8).init(),
708 .seen = event.Locked(Fmt.SeenMap).init(loop, Fmt.SeenMap.init(loop.allocator)),
671709 .any_error = false,
710 .color = color,
711 .loop = loop,
672712 };
673713
714 var group = event.Group(FmtError!void).init(loop);
674715 for (flags.positionals.toSliceConst()) |file_path| {
675 try fmt.addToQueue(file_path);
716 try group.call(fmtPath, &fmt, file_path);
676717 }
718 try await (async group.wait() catch unreachable);
719 if (fmt.any_error) {
720 os.exit(1);
721 }
722}
677723
678 while (fmt.queue.popFirst()) |node| {
679 const file_path = node.data;
724async fn fmtPath(fmt: *Fmt, file_path_ref: []const u8) FmtError!void {
725 const file_path = try std.mem.dupe(fmt.loop.allocator, u8, file_path_ref);
726 defer fmt.loop.allocator.free(file_path);
680727
681 var file = try os.File.openRead(allocator, file_path);
682 defer file.close();
728 {
729 const held = await (async fmt.seen.acquire() catch unreachable);
730 defer held.release();
683731
684 const source_code = io.readFileAlloc(allocator, file_path) catch |err| switch (err) {
685 error.IsDir => {
686 try fmt.addDirToQueue(file_path);
687 continue;
688 },
689 else => {
690 try stderr.print("unable to open '{}': {}\n", file_path, err);
691 fmt.any_error = true;
692 continue;
693 },
694 };
695 defer allocator.free(source_code);
732 if (try held.value.put(file_path, {})) |_| return;
733 }
696734
697 var tree = std.zig.parse(allocator, source_code) catch |err| {
698 try stderr.print("error parsing file '{}': {}\n", file_path, err);
735 const source_code = (await try async event.fs.readFile(
736 fmt.loop,
737 file_path,
738 max_src_size,
739 )) catch |err| switch (err) {
740 error.IsDir => {
741 // TODO make event based (and dir.next())
742 var dir = try std.os.Dir.open(fmt.loop.allocator, file_path);
743 defer dir.close();
744
745 var group = event.Group(FmtError!void).init(fmt.loop);
746 while (try dir.next()) |entry| {
747 if (entry.kind == std.os.Dir.Entry.Kind.Directory or mem.endsWith(u8, entry.name, ".zig")) {
748 const full_path = try os.path.join(fmt.loop.allocator, file_path, entry.name);
749 try group.call(fmtPath, fmt, full_path);
750 }
751 }
752 return await (async group.wait() catch unreachable);
753 },
754 else => {
755 // TODO lock stderr printing
756 try stderr.print("unable to open '{}': {}\n", file_path, err);
699757 fmt.any_error = true;
700 continue;
701 };
702 defer tree.deinit();
703
704 var error_it = tree.errors.iterator(0);
705 while (error_it.next()) |parse_error| {
706 const msg = try errmsg.Msg.createFromParseError(allocator, parse_error, &tree, file_path);
707 defer msg.destroy();
758 return;
759 },
760 };
761 defer fmt.loop.allocator.free(source_code);
708762
709 try msg.printToFile(&stderr_file, color);
710 }
711 if (tree.errors.len != 0) {
712 fmt.any_error = true;
713 continue;
714 }
763 var tree = std.zig.parse(fmt.loop.allocator, source_code) catch |err| {
764 try stderr.print("error parsing file '{}': {}\n", file_path, err);
765 fmt.any_error = true;
766 return;
767 };
768 defer tree.deinit();
715769
716 const baf = try io.BufferedAtomicFile.create(allocator, file_path);
717 defer baf.destroy();
770 var error_it = tree.errors.iterator(0);
771 while (error_it.next()) |parse_error| {
772 const msg = try errmsg.Msg.createFromParseError(fmt.loop.allocator, parse_error, &tree, file_path);
773 defer fmt.loop.allocator.destroy(msg);
718774
719 const anything_changed = try std.zig.render(allocator, baf.stream(), &tree);
720 if (anything_changed) {
721 try stderr.print("{}\n", file_path);
722 try baf.finish();
723 }
775 try msg.printToFile(&stderr_file, fmt.color);
776 }
777 if (tree.errors.len != 0) {
778 fmt.any_error = true;
779 return;
724780 }
725781
726 if (fmt.any_error) {
727 os.exit(1);
782 // TODO make this evented
783 const baf = try io.BufferedAtomicFile.create(fmt.loop.allocator, file_path);
784 defer baf.destroy();
785
786 const anything_changed = try std.zig.render(fmt.loop.allocator, baf.stream(), &tree);
787 if (anything_changed) {
788 try stderr.print("{}\n", file_path);
789 try baf.finish();
728790 }
729791}
730792
src-self-hosted/scope.zig+45-21
......@@ -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 }
......@@ -62,6 +63,8 @@ pub const Scope = struct {
6263 Id.CompTime,
6364 Id.Var,
6465 => scope = scope.parent.?,
66
67 Id.AstTree => unreachable,
6568 }
6669 }
6770 }
......@@ -82,6 +85,8 @@ pub const Scope = struct {
8285 Id.Root,
8386 Id.Var,
8487 => scope = scope.parent orelse return null,
88
89 Id.AstTree => unreachable,
8590 }
8691 }
8792 }
......@@ -97,6 +102,7 @@ pub const Scope = struct {
97102
98103 pub const Id = enum {
99104 Root,
105 AstTree,
100106 Decls,
101107 Block,
102108 FnDef,
......@@ -108,13 +114,12 @@ pub const Scope = struct {
108114
109115 pub const Root = struct {
110116 base: Scope,
111 tree: *ast.Tree,
112117 realpath: []const u8,
118 decls: *Decls,
113119
114120 /// Creates a Root scope with 1 reference
115121 /// 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 {
122 pub fn create(comp: *Compilation, realpath: []u8) !*Root {
118123 const self = try comp.gpa().createOne(Root);
119124 self.* = Root{
120125 .base = Scope{
......@@ -122,41 +127,65 @@ pub const Scope = struct {
122127 .parent = null,
123128 .ref_count = std.atomic.Int(usize).init(1),
124129 },
125 .tree = tree,
126130 .realpath = realpath,
131 .decls = undefined,
127132 };
128
133 errdefer comp.gpa().destroy(self);
134 self.decls = try Decls.create(comp, &self.base);
129135 return self;
130136 }
131137
132138 pub fn destroy(self: *Root, comp: *Compilation) void {
139 // TODO comp.fs_watch.removeFile(self.realpath);
140 self.decls.base.deref(comp);
141 comp.gpa().free(self.realpath);
142 comp.gpa().destroy(self);
143 }
144 };
145
146 pub const AstTree = struct {
147 base: Scope,
148 tree: *ast.Tree,
149
150 /// Creates a scope with 1 reference
151 /// Takes ownership of tree, will deinit and destroy when done.
152 pub fn create(comp: *Compilation, tree: *ast.Tree, root_scope: *Root) !*AstTree {
153 const self = try comp.gpa().createOne(AstTree);
154 self.* = AstTree{
155 .base = undefined,
156 .tree = tree,
157 };
158 self.base.init(Id.AstTree, &root_scope.base);
159
160 return self;
161 }
162
163 pub fn destroy(self: *AstTree, comp: *Compilation) void {
133164 comp.gpa().free(self.tree.source);
134165 self.tree.deinit();
135166 comp.gpa().destroy(self.tree);
136 comp.gpa().free(self.realpath);
137167 comp.gpa().destroy(self);
138168 }
169
170 pub fn root(self: *AstTree) *Root {
171 return self.base.findRoot();
172 }
139173 };
140174
141175 pub const Decls = struct {
142176 base: Scope,
143177
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),
178 /// This table remains Write Locked when the names are incomplete or possibly outdated.
179 /// So if a reader manages to grab a lock, it can be sure that the set of names is complete
180 /// and correct.
181 table: event.RwLocked(Decl.Table),
152182
153183 /// Creates a Decls scope with 1 reference
154184 pub fn create(comp: *Compilation, parent: *Scope) !*Decls {
155185 const self = try comp.gpa().createOne(Decls);
156186 self.* = Decls{
157187 .base = undefined,
158 .table = event.Locked(Decl.Table).init(comp.loop, Decl.Table.init(comp.gpa())),
159 .name_future = event.Future(void).init(comp.loop),
188 .table = event.RwLocked(Decl.Table).init(comp.loop, Decl.Table.init(comp.gpa())),
160189 };
161190 self.base.init(Id.Decls, parent);
162191 return self;
......@@ -166,11 +195,6 @@ pub const Scope = struct {
166195 self.table.deinit();
167196 comp.gpa().destroy(self);
168197 }
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 }
174198 };
175199
176200 pub const Block = struct {
src-self-hosted/test.zig+16-15
......@@ -6,7 +6,7 @@ const Compilation = @import("compilation.zig").Compilation;
66const introspect = @import("introspect.zig");
77const assertOrPanic = std.debug.assertOrPanic;
88const errmsg = @import("errmsg.zig");
9const EventLoopLocal = @import("compilation.zig").EventLoopLocal;
9const ZigCompiler = @import("compilation.zig").ZigCompiler;
1010
1111var ctx: TestContext = undefined;
1212
......@@ -25,7 +25,7 @@ const allocator = std.heap.c_allocator;
2525
2626pub const TestContext = struct {
2727 loop: std.event.Loop,
28 event_loop_local: EventLoopLocal,
28 zig_compiler: ZigCompiler,
2929 zig_lib_dir: []u8,
3030 file_index: std.atomic.Int(usize),
3131 group: std.event.Group(error!void),
......@@ -37,20 +37,20 @@ pub const TestContext = struct {
3737 self.* = TestContext{
3838 .any_err = {},
3939 .loop = undefined,
40 .event_loop_local = undefined,
40 .zig_compiler = undefined,
4141 .zig_lib_dir = undefined,
4242 .group = undefined,
4343 .file_index = std.atomic.Int(usize).init(0),
4444 };
4545
46 try self.loop.initMultiThreaded(allocator);
46 try self.loop.initSingleThreaded(allocator);
4747 errdefer self.loop.deinit();
4848
49 self.event_loop_local = try EventLoopLocal.init(&self.loop);
50 errdefer self.event_loop_local.deinit();
49 self.zig_compiler = try ZigCompiler.init(&self.loop);
50 errdefer self.zig_compiler.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);
......@@ -62,7 +62,7 @@ pub const TestContext = struct {
6262 fn deinit(self: *TestContext) void {
6363 std.os.deleteTree(allocator, tmp_dir_name) catch {};
6464 allocator.free(self.zig_lib_dir);
65 self.event_loop_local.deinit();
65 self.zig_compiler.deinit();
6666 self.loop.deinit();
6767 }
6868
......@@ -97,7 +97,7 @@ pub const TestContext = struct {
9797 try std.io.writeFile(allocator, file1_path, source);
9898
9999 var comp = try Compilation.create(
100 &self.event_loop_local,
100 &self.zig_compiler,
101101 "test",
102102 file1_path,
103103 Target.Native,
......@@ -108,7 +108,7 @@ pub const TestContext = struct {
108108 );
109109 errdefer comp.destroy();
110110
111 try comp.build();
111 comp.start();
112112
113113 try self.group.call(getModuleEvent, comp, source, path, line, column, msg);
114114 }
......@@ -131,7 +131,7 @@ pub const TestContext = struct {
131131 try std.io.writeFile(allocator, file1_path, source);
132132
133133 var comp = try Compilation.create(
134 &self.event_loop_local,
134 &self.zig_compiler,
135135 "test",
136136 file1_path,
137137 Target.Native,
......@@ -144,7 +144,7 @@ pub const TestContext = struct {
144144
145145 _ = try comp.addLinkLib("c", true);
146146 comp.link_out_file = output_file;
147 try comp.build();
147 comp.start();
148148
149149 try self.group.call(getModuleEventSuccess, comp, output_file, expected_output);
150150 }
......@@ -212,9 +212,10 @@ pub const TestContext = struct {
212212 Compilation.Event.Fail => |msgs| {
213213 assertOrPanic(msgs.len != 0);
214214 for (msgs) |msg| {
215 if (mem.endsWith(u8, msg.getRealPath(), path) and mem.eql(u8, msg.text, text)) {
216 const first_token = msg.getTree().tokens.at(msg.span.first);
217 const last_token = msg.getTree().tokens.at(msg.span.first);
215 if (mem.endsWith(u8, msg.realpath, path) and mem.eql(u8, msg.text, text)) {
216 const span = msg.getSpan();
217 const first_token = msg.getTree().tokens.at(span.first);
218 const last_token = msg.getTree().tokens.at(span.first);
218219 const start_loc = msg.getTree().tokenLocationPtr(0, first_token);
219220 if (start_loc.line + 1 == line and start_loc.column + 1 == column) {
220221 return;
src-self-hosted/type.zig+2-2
......@@ -184,8 +184,8 @@ pub const Type = struct {
184184 if (await (async base.abi_alignment.start() catch unreachable)) |ptr| return ptr.*;
185185
186186 {
187 const held = try comp.event_loop_local.getAnyLlvmContext();
188 defer held.release(comp.event_loop_local);
187 const held = try comp.zig_compiler.getAnyLlvmContext();
188 defer held.release(comp.zig_compiler);
189189
190190 const llvm_context = held.node.data;
191191
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/build.zig+61-52
......@@ -424,60 +424,69 @@ pub const Builder = struct {
424424 return mode;
425425 }
426426
427 pub fn addUserInputOption(self: *Builder, name: []const u8, value: []const u8) bool {
428 if (self.user_input_options.put(name, UserInputOption{
429 .name = name,
430 .value = UserValue{ .Scalar = value },
431 .used = false,
432 }) catch unreachable) |*prev_value| {
433 // option already exists
434 switch (prev_value.value) {
435 UserValue.Scalar => |s| {
436 // turn it into a list
437 var list = ArrayList([]const u8).init(self.allocator);
438 list.append(s) catch unreachable;
439 list.append(value) catch unreachable;
440 _ = self.user_input_options.put(name, UserInputOption{
441 .name = name,
442 .value = UserValue{ .List = list },
443 .used = false,
444 }) catch unreachable;
445 },
446 UserValue.List => |*list| {
447 // append to the list
448 list.append(value) catch unreachable;
449 _ = self.user_input_options.put(name, UserInputOption{
450 .name = name,
451 .value = UserValue{ .List = list.* },
452 .used = false,
453 }) catch unreachable;
454 },
455 UserValue.Flag => {
456 warn("Option '-D{}={}' conflicts with flag '-D{}'.\n", name, value, name);
457 return true;
458 },
459 }
427 pub fn addUserInputOption(self: *Builder, name: []const u8, value: []const u8) !bool {
428 const gop = try self.user_input_options.getOrPut(name);
429 if (!gop.found_existing) {
430 gop.kv.value = UserInputOption{
431 .name = name,
432 .value = UserValue{ .Scalar = value },
433 .used = false,
434 };
435 return false;
436 }
437
438 // option already exists
439 switch (gop.kv.value.value) {
440 UserValue.Scalar => |s| {
441 // turn it into a list
442 var list = ArrayList([]const u8).init(self.allocator);
443 list.append(s) catch unreachable;
444 list.append(value) catch unreachable;
445 _ = self.user_input_options.put(name, UserInputOption{
446 .name = name,
447 .value = UserValue{ .List = list },
448 .used = false,
449 }) catch unreachable;
450 },
451 UserValue.List => |*list| {
452 // append to the list
453 list.append(value) catch unreachable;
454 _ = self.user_input_options.put(name, UserInputOption{
455 .name = name,
456 .value = UserValue{ .List = list.* },
457 .used = false,
458 }) catch unreachable;
459 },
460 UserValue.Flag => {
461 warn("Option '-D{}={}' conflicts with flag '-D{}'.\n", name, value, name);
462 return true;
463 },
460464 }
461465 return false;
462466 }
463467
464 pub fn addUserInputFlag(self: *Builder, name: []const u8) bool {
465 if (self.user_input_options.put(name, UserInputOption{
466 .name = name,
467 .value = UserValue{ .Flag = {} },
468 .used = false,
469 }) catch unreachable) |*prev_value| {
470 switch (prev_value.value) {
471 UserValue.Scalar => |s| {
472 warn("Flag '-D{}' conflicts with option '-D{}={}'.\n", name, name, s);
473 return true;
474 },
475 UserValue.List => {
476 warn("Flag '-D{}' conflicts with multiple options of the same name.\n", name);
477 return true;
478 },
479 UserValue.Flag => {},
480 }
468 pub fn addUserInputFlag(self: *Builder, name: []const u8) !bool {
469 const gop = try self.user_input_options.getOrPut(name);
470 if (!gop.found_existing) {
471 gop.kv.value = UserInputOption{
472 .name = name,
473 .value = UserValue{ .Flag = {} },
474 .used = false,
475 };
476 return false;
477 }
478
479 // option already exists
480 switch (gop.kv.value.value) {
481 UserValue.Scalar => |s| {
482 warn("Flag '-D{}' conflicts with option '-D{}={}'.\n", name, name, s);
483 return true;
484 },
485 UserValue.List => {
486 warn("Flag '-D{}' conflicts with multiple options of the same name.\n", name);
487 return true;
488 },
489 UserValue.Flag => {},
481490 }
482491 return false;
483492 }
......@@ -603,10 +612,10 @@ pub const Builder = struct {
603612 }
604613
605614 fn copyFile(self: *Builder, source_path: []const u8, dest_path: []const u8) !void {
606 return self.copyFileMode(source_path, dest_path, os.default_file_mode);
615 return self.copyFileMode(source_path, dest_path, os.File.default_mode);
607616 }
608617
609 fn copyFileMode(self: *Builder, source_path: []const u8, dest_path: []const u8, mode: os.FileMode) !void {
618 fn copyFileMode(self: *Builder, source_path: []const u8, dest_path: []const u8, mode: os.File.Mode) !void {
610619 if (self.verbose) {
611620 warn("cp {} {}\n", source_path, dest_path);
612621 }
std/c/darwin.zig+26-8
......@@ -30,10 +30,36 @@ pub extern "c" fn sysctl(name: [*]c_int, namelen: c_uint, oldp: ?*c_void, oldlen
3030pub extern "c" fn sysctlbyname(name: [*]const u8, oldp: ?*c_void, oldlenp: ?*usize, newp: ?*c_void, newlen: usize) c_int;
3131pub extern "c" fn sysctlnametomib(name: [*]const u8, mibp: ?*c_int, sizep: ?*usize) c_int;
3232
33pub extern "c" fn bind(socket: c_int, address: ?*const sockaddr, address_len: socklen_t) c_int;
34pub extern "c" fn socket(domain: c_int, type: c_int, protocol: c_int) c_int;
35
3336pub use @import("../os/darwin/errno.zig");
3437
3538pub const _errno = __error;
3639
40pub const in_port_t = u16;
41pub const sa_family_t = u8;
42pub const socklen_t = u32;
43pub const sockaddr = extern union {
44 in: sockaddr_in,
45 in6: sockaddr_in6,
46};
47pub const sockaddr_in = extern struct {
48 len: u8,
49 family: sa_family_t,
50 port: in_port_t,
51 addr: u32,
52 zero: [8]u8,
53};
54pub const sockaddr_in6 = extern struct {
55 len: u8,
56 family: sa_family_t,
57 port: in_port_t,
58 flowinfo: u32,
59 addr: [16]u8,
60 scope_id: u32,
61};
62
3763pub const timeval = extern struct {
3864 tv_sec: isize,
3965 tv_usec: isize,
......@@ -98,14 +124,6 @@ pub const dirent = extern struct {
98124 d_name: u8, // field address is address of first byte of name
99125};
100126
101pub const sockaddr = extern struct {
102 sa_len: u8,
103 sa_family: sa_family_t,
104 sa_data: [14]u8,
105};
106
107pub const sa_family_t = u8;
108
109127pub const pthread_attr_t = extern struct {
110128 __sig: c_long,
111129 __opaque: [56]u8,
std/c/index.zig+2
......@@ -21,8 +21,10 @@ pub extern "c" fn lseek(fd: c_int, offset: isize, whence: c_int) isize;
2121pub extern "c" fn open(path: [*]const u8, oflag: c_int, ...) c_int;
2222pub extern "c" fn raise(sig: c_int) c_int;
2323pub extern "c" fn read(fd: c_int, buf: *c_void, nbyte: usize) isize;
24pub extern "c" fn pread(fd: c_int, buf: *c_void, nbyte: usize, offset: u64) isize;
2425pub extern "c" fn stat(noalias path: [*]const u8, noalias buf: *Stat) c_int;
2526pub extern "c" fn write(fd: c_int, buf: *const c_void, nbyte: usize) isize;
27pub extern "c" fn pwrite(fd: c_int, buf: *const c_void, nbyte: usize, offset: u64) isize;
2628pub extern "c" fn mmap(addr: ?*c_void, len: usize, prot: c_int, flags: c_int, fd: c_int, offset: isize) ?*c_void;
2729pub extern "c" fn munmap(addr: *c_void, len: usize) c_int;
2830pub extern "c" fn unlink(path: [*]const u8) c_int;
std/debug/index.zig+4-5
......@@ -23,7 +23,10 @@ pub const runtime_safety = switch (builtin.mode) {
2323var stderr_file: os.File = undefined;
2424var stderr_file_out_stream: io.FileOutStream = undefined;
2525var stderr_stream: ?*io.OutStream(io.FileOutStream.Error) = null;
26var stderr_mutex = std.Mutex.init();
2627pub fn warn(comptime fmt: []const u8, args: ...) void {
28 const held = stderr_mutex.acquire();
29 defer held.release();
2730 const stderr = getStderrStream() catch return;
2831 stderr.print(fmt, args) catch return;
2932}
......@@ -672,14 +675,10 @@ fn parseFormValueRef(allocator: *mem.Allocator, in_stream: var, comptime T: type
672675
673676const ParseFormValueError = error{
674677 EndOfStream,
675 Io,
676 BadFd,
677 Unexpected,
678678 InvalidDebugInfo,
679679 EndOfFile,
680 IsDir,
681680 OutOfMemory,
682};
681} || std.os.File.ReadError;
683682
684683fn parseFormValue(allocator: *mem.Allocator, in_stream: var, form_id: u64, is_64: bool) ParseFormValueError!FormValue {
685684 return switch (form_id) {
std/event.zig+14-8
......@@ -1,17 +1,23 @@
1pub const Channel = @import("event/channel.zig").Channel;
2pub const Future = @import("event/future.zig").Future;
3pub const Group = @import("event/group.zig").Group;
4pub const Lock = @import("event/lock.zig").Lock;
15pub const Locked = @import("event/locked.zig").Locked;
6pub const RwLock = @import("event/rwlock.zig").RwLock;
7pub const RwLocked = @import("event/rwlocked.zig").RwLocked;
28pub const Loop = @import("event/loop.zig").Loop;
3pub const Lock = @import("event/lock.zig").Lock;
9pub const fs = @import("event/fs.zig");
410pub const tcp = @import("event/tcp.zig");
5pub const Channel = @import("event/channel.zig").Channel;
6pub const Group = @import("event/group.zig").Group;
7pub const Future = @import("event/future.zig").Future;
811
912test "import event tests" {
13 _ = @import("event/channel.zig");
14 _ = @import("event/fs.zig");
15 _ = @import("event/future.zig");
16 _ = @import("event/group.zig");
17 _ = @import("event/lock.zig");
1018 _ = @import("event/locked.zig");
19 _ = @import("event/rwlock.zig");
20 _ = @import("event/rwlocked.zig");
1121 _ = @import("event/loop.zig");
12 _ = @import("event/lock.zig");
1322 _ = @import("event/tcp.zig");
14 _ = @import("event/channel.zig");
15 _ = @import("event/group.zig");
16 _ = @import("event/future.zig");
1723}
std/event/channel.zig+161-24
......@@ -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,29 @@ 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 suspend {
92 resume @handle();
93 }
94
95 var my_tick_node = Loop.NextTickNode.init(@handle());
96 var queue_node = std.atomic.Queue(PutNode).Node.init(PutNode{
97 .tick_node = &my_tick_node,
98 .data = data,
99 });
100
101 // TODO test canceling a put()
102 errdefer {
103 _ = @atomicRmw(usize, &self.put_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst);
104 const need_dispatch = !self.putters.remove(&queue_node);
105 self.loop.cancelOnNextTick(&my_tick_node);
106 if (need_dispatch) {
107 // oops we made the put_count incorrect for a period of time. fix by dispatching.
108 _ = @atomicRmw(usize, &self.put_count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst);
109 self.dispatch();
110 }
111 }
74112 suspend {
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 };
86113 self.putters.put(&queue_node);
87114 _ = @atomicRmw(usize, &self.put_count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst);
88115
......@@ -93,23 +120,95 @@ pub fn Channel(comptime T: type) type {
93120 /// await this function to get an item from the channel. If the buffer is empty, the promise will
94121 /// complete when the next item is put in the channel.
95122 pub async fn get(self: *SelfChannel) T {
123 // TODO fix this workaround
124 suspend {
125 resume @handle();
126 }
127
96128 // TODO integrate this function with named return values
97129 // so we can get rid of this extra result copy
98130 var result: T = undefined;
131 var my_tick_node = Loop.NextTickNode.init(@handle());
132 var queue_node = std.atomic.Queue(GetNode).Node.init(GetNode{
133 .tick_node = &my_tick_node,
134 .data = GetNode.Data{
135 .Normal = GetNode.Normal{ .ptr = &result },
136 },
137 });
138
139 // TODO test canceling a get()
140 errdefer {
141 _ = @atomicRmw(usize, &self.get_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst);
142 const need_dispatch = !self.getters.remove(&queue_node);
143 self.loop.cancelOnNextTick(&my_tick_node);
144 if (need_dispatch) {
145 // oops we made the get_count incorrect for a period of time. fix by dispatching.
146 _ = @atomicRmw(usize, &self.get_count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst);
147 self.dispatch();
148 }
149 }
150
151 suspend {
152 self.getters.put(&queue_node);
153 _ = @atomicRmw(usize, &self.get_count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst);
154
155 self.dispatch();
156 }
157 return result;
158 }
159
160 //pub async fn select(comptime EnumUnion: type, channels: ...) EnumUnion {
161 // assert(@memberCount(EnumUnion) == channels.len); // enum union and channels mismatch
162 // assert(channels.len != 0); // enum unions cannot have 0 fields
163 // if (channels.len == 1) {
164 // const result = await (async channels[0].get() catch unreachable);
165 // return @unionInit(EnumUnion, @memberName(EnumUnion, 0), result);
166 // }
167 //}
168
169 /// Await this function to get an item from the channel. If the buffer is empty and there are no
170 /// puts waiting, this returns null.
171 /// Await is necessary for locking purposes. The function will be resumed after checking the channel
172 /// for data and will not wait for data to be available.
173 pub async fn getOrNull(self: *SelfChannel) ?T {
174 // TODO fix this workaround
99175 suspend {
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{
176 resume @handle();
177 }
178
179 // TODO integrate this function with named return values
180 // so we can get rid of this extra result copy
181 var result: ?T = null;
182 var my_tick_node = Loop.NextTickNode.init(@handle());
183 var or_null_node = std.atomic.Queue(*std.atomic.Queue(GetNode).Node).Node.init(undefined);
184 var queue_node = std.atomic.Queue(GetNode).Node.init(GetNode{
185 .tick_node = &my_tick_node,
186 .data = GetNode.Data{
187 .OrNull = GetNode.OrNull{
106188 .ptr = &result,
107 .tick_node = &my_tick_node,
189 .or_null = &or_null_node,
108190 },
109 .next = undefined,
110 };
191 },
192 });
193 or_null_node.data = &queue_node;
194
195 // TODO test canceling getOrNull
196 errdefer {
197 _ = self.or_null_queue.remove(&or_null_node);
198 _ = @atomicRmw(usize, &self.get_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst);
199 const need_dispatch = !self.getters.remove(&queue_node);
200 self.loop.cancelOnNextTick(&my_tick_node);
201 if (need_dispatch) {
202 // oops we made the get_count incorrect for a period of time. fix by dispatching.
203 _ = @atomicRmw(usize, &self.get_count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst);
204 self.dispatch();
205 }
206 }
207
208 suspend {
111209 self.getters.put(&queue_node);
112210 _ = @atomicRmw(usize, &self.get_count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst);
211 self.or_null_queue.put(&or_null_node);
113212
114213 self.dispatch();
115214 }
......@@ -139,7 +238,15 @@ pub fn Channel(comptime T: type) type {
139238 if (get_count == 0) break :one_dispatch;
140239
141240 const get_node = &self.getters.get().?.data;
142 get_node.ptr.* = self.buffer_nodes[self.buffer_index -% self.buffer_len];
241 switch (get_node.data) {
242 GetNode.Data.Normal => |info| {
243 info.ptr.* = self.buffer_nodes[self.buffer_index -% self.buffer_len];
244 },
245 GetNode.Data.OrNull => |info| {
246 _ = self.or_null_queue.remove(info.or_null);
247 info.ptr.* = self.buffer_nodes[self.buffer_index -% self.buffer_len];
248 },
249 }
143250 self.loop.onNextTick(get_node.tick_node);
144251 self.buffer_len -= 1;
145252
......@@ -151,7 +258,15 @@ pub fn Channel(comptime T: type) type {
151258 const get_node = &self.getters.get().?.data;
152259 const put_node = &self.putters.get().?.data;
153260
154 get_node.ptr.* = put_node.data;
261 switch (get_node.data) {
262 GetNode.Data.Normal => |info| {
263 info.ptr.* = put_node.data;
264 },
265 GetNode.Data.OrNull => |info| {
266 _ = self.or_null_queue.remove(info.or_null);
267 info.ptr.* = put_node.data;
268 },
269 }
155270 self.loop.onNextTick(get_node.tick_node);
156271 self.loop.onNextTick(put_node.tick_node);
157272
......@@ -176,6 +291,16 @@ pub fn Channel(comptime T: type) type {
176291 _ = @atomicRmw(usize, &self.get_count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst);
177292 _ = @atomicRmw(usize, &self.put_count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst);
178293
294 // All the "get or null" functions should resume now.
295 var remove_count: usize = 0;
296 while (self.or_null_queue.get()) |or_null_node| {
297 remove_count += @boolToInt(self.getters.remove(or_null_node.data));
298 self.loop.onNextTick(or_null_node.data.data.tick_node);
299 }
300 if (remove_count != 0) {
301 _ = @atomicRmw(usize, &self.get_count, AtomicRmwOp.Sub, remove_count, AtomicOrder.SeqCst);
302 }
303
179304 // clear need-dispatch flag
180305 const need_dispatch = @atomicRmw(u8, &self.need_dispatch, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst);
181306 if (need_dispatch != 0) continue;
......@@ -226,6 +351,15 @@ async fn testChannelGetter(loop: *Loop, channel: *Channel(i32)) void {
226351 const value2_promise = try async channel.get();
227352 const value2 = await value2_promise;
228353 assert(value2 == 4567);
354
355 const value3_promise = try async channel.getOrNull();
356 const value3 = await value3_promise;
357 assert(value3 == null);
358
359 const last_put = try async testPut(channel, 4444);
360 const value4 = await try async channel.getOrNull();
361 assert(value4.? == 4444);
362 await last_put;
229363}
230364
231365async fn testChannelPutter(channel: *Channel(i32)) void {
......@@ -233,3 +367,6 @@ async fn testChannelPutter(channel: *Channel(i32)) void {
233367 await (async channel.put(4567) catch @panic("out of memory"));
234368}
235369
370async fn testPut(channel: *Channel(i32), value: i32) void {
371 await (async channel.put(value) catch @panic("out of memory"));
372}
std/event/fs.zig created+1362
......@@ -0,0 +1,1362 @@
1const builtin = @import("builtin");
2const std = @import("../index.zig");
3const event = std.event;
4const assert = std.debug.assert;
5const os = std.os;
6const mem = std.mem;
7const posix = os.posix;
8const windows = os.windows;
9const Loop = event.Loop;
10
11pub const RequestNode = std.atomic.Queue(Request).Node;
12
13pub const Request = struct {
14 msg: Msg,
15 finish: Finish,
16
17 pub const Finish = union(enum) {
18 TickNode: Loop.NextTickNode,
19 DeallocCloseOperation: *CloseOperation,
20 NoAction,
21 };
22
23 pub const Msg = union(enum) {
24 PWriteV: PWriteV,
25 PReadV: PReadV,
26 Open: Open,
27 Close: Close,
28 WriteFile: WriteFile,
29 End, // special - means the fs thread should exit
30
31 pub const PWriteV = struct {
32 fd: os.FileHandle,
33 iov: []os.posix.iovec_const,
34 offset: usize,
35 result: Error!void,
36
37 pub const Error = os.File.WriteError;
38 };
39
40 pub const PReadV = struct {
41 fd: os.FileHandle,
42 iov: []os.posix.iovec,
43 offset: usize,
44 result: Error!usize,
45
46 pub const Error = os.File.ReadError;
47 };
48
49 pub const Open = struct {
50 /// must be null terminated. TODO https://github.com/ziglang/zig/issues/265
51 path: []const u8,
52 flags: u32,
53 mode: os.File.Mode,
54 result: Error!os.FileHandle,
55
56 pub const Error = os.File.OpenError;
57 };
58
59 pub const WriteFile = struct {
60 /// must be null terminated. TODO https://github.com/ziglang/zig/issues/265
61 path: []const u8,
62 contents: []const u8,
63 mode: os.File.Mode,
64 result: Error!void,
65
66 pub const Error = os.File.OpenError || os.File.WriteError;
67 };
68
69 pub const Close = struct {
70 fd: os.FileHandle,
71 };
72 };
73};
74
75/// data - just the inner references - must live until pwritev promise completes.
76pub async fn pwritev(loop: *Loop, fd: os.FileHandle, data: []const []const u8, offset: usize) !void {
77 switch (builtin.os) {
78 builtin.Os.macosx,
79 builtin.Os.linux,
80 => return await (async pwritevPosix(loop, fd, data, offset) catch unreachable),
81 builtin.Os.windows,
82 => return await (async pwritevWindows(loop, fd, data, offset) catch unreachable),
83 else => @compileError("Unsupported OS"),
84 }
85}
86
87/// data - just the inner references - must live until pwritev promise completes.
88pub async fn pwritevWindows(loop: *Loop, fd: os.FileHandle, data: []const []const u8, offset: usize) !void {
89 if (data.len == 0) return;
90 if (data.len == 1) return await (async pwriteWindows(loop, fd, data[0], offset) catch unreachable);
91
92 const data_copy = try std.mem.dupe(loop.allocator, []const u8, data);
93 defer loop.allocator.free(data_copy);
94
95 // TODO do these in parallel
96 var off = offset;
97 for (data_copy) |buf| {
98 try await (async pwriteWindows(loop, fd, buf, off) catch unreachable);
99 off += buf.len;
100 }
101}
102
103pub async fn pwriteWindows(loop: *Loop, fd: os.FileHandle, data: []const u8, offset: u64) os.WindowsWriteError!void {
104 // workaround for https://github.com/ziglang/zig/issues/1194
105 suspend {
106 resume @handle();
107 }
108
109 var resume_node = Loop.ResumeNode.Basic{
110 .base = Loop.ResumeNode{
111 .id = Loop.ResumeNode.Id.Basic,
112 .handle = @handle(),
113 },
114 };
115 const completion_key = @ptrToInt(&resume_node.base);
116 // TODO support concurrent async ops on the file handle
117 // we can do this by ignoring completion key and using @fieldParentPtr with the *Overlapped
118 _ = try os.windowsCreateIoCompletionPort(fd, loop.os_data.io_port, completion_key, undefined);
119 var overlapped = windows.OVERLAPPED{
120 .Internal = 0,
121 .InternalHigh = 0,
122 .Offset = @truncate(u32, offset),
123 .OffsetHigh = @truncate(u32, offset >> 32),
124 .hEvent = null,
125 };
126 loop.beginOneEvent();
127 errdefer loop.finishOneEvent();
128
129 errdefer {
130 _ = windows.CancelIoEx(fd, &overlapped);
131 }
132 suspend {
133 _ = windows.WriteFile(fd, data.ptr, @intCast(windows.DWORD, data.len), null, &overlapped);
134 }
135 var bytes_transferred: windows.DWORD = undefined;
136 if (windows.GetOverlappedResult(fd, &overlapped, &bytes_transferred, windows.FALSE) == 0) {
137 const err = windows.GetLastError();
138 return switch (err) {
139 windows.ERROR.IO_PENDING => unreachable,
140 windows.ERROR.INVALID_USER_BUFFER => error.SystemResources,
141 windows.ERROR.NOT_ENOUGH_MEMORY => error.SystemResources,
142 windows.ERROR.OPERATION_ABORTED => error.OperationAborted,
143 windows.ERROR.NOT_ENOUGH_QUOTA => error.SystemResources,
144 windows.ERROR.BROKEN_PIPE => error.BrokenPipe,
145 else => os.unexpectedErrorWindows(err),
146 };
147 }
148}
149
150
151/// data - just the inner references - must live until pwritev promise completes.
152pub async fn pwritevPosix(loop: *Loop, fd: os.FileHandle, data: []const []const u8, offset: usize) !void {
153 // workaround for https://github.com/ziglang/zig/issues/1194
154 suspend {
155 resume @handle();
156 }
157
158 const iovecs = try loop.allocator.alloc(os.posix.iovec_const, data.len);
159 defer loop.allocator.free(iovecs);
160
161 for (data) |buf, i| {
162 iovecs[i] = os.posix.iovec_const{
163 .iov_base = buf.ptr,
164 .iov_len = buf.len,
165 };
166 }
167
168 var req_node = RequestNode{
169 .prev = null,
170 .next = null,
171 .data = Request{
172 .msg = Request.Msg{
173 .PWriteV = Request.Msg.PWriteV{
174 .fd = fd,
175 .iov = iovecs,
176 .offset = offset,
177 .result = undefined,
178 },
179 },
180 .finish = Request.Finish{
181 .TickNode = Loop.NextTickNode{
182 .prev = null,
183 .next = null,
184 .data = @handle(),
185 },
186 },
187 },
188 };
189
190 errdefer loop.posixFsCancel(&req_node);
191
192 suspend {
193 loop.posixFsRequest(&req_node);
194 }
195
196 return req_node.data.msg.PWriteV.result;
197}
198
199/// data - just the inner references - must live until preadv promise completes.
200pub async fn preadv(loop: *Loop, fd: os.FileHandle, data: []const []u8, offset: usize) !usize {
201 assert(data.len != 0);
202 switch (builtin.os) {
203 builtin.Os.macosx,
204 builtin.Os.linux,
205 => return await (async preadvPosix(loop, fd, data, offset) catch unreachable),
206 builtin.Os.windows,
207 => return await (async preadvWindows(loop, fd, data, offset) catch unreachable),
208 else => @compileError("Unsupported OS"),
209 }
210}
211
212pub async fn preadvWindows(loop: *Loop, fd: os.FileHandle, data: []const []u8, offset: u64) !usize {
213 assert(data.len != 0);
214 if (data.len == 1) return await (async preadWindows(loop, fd, data[0], offset) catch unreachable);
215
216 const data_copy = try std.mem.dupe(loop.allocator, []u8, data);
217 defer loop.allocator.free(data_copy);
218
219 // TODO do these in parallel?
220 var off: usize = 0;
221 var iov_i: usize = 0;
222 var inner_off: usize = 0;
223 while (true) {
224 const v = data_copy[iov_i];
225 const amt_read = try await (async preadWindows(loop, fd, v[inner_off .. v.len-inner_off], offset + off) catch unreachable);
226 off += amt_read;
227 inner_off += amt_read;
228 if (inner_off == v.len) {
229 iov_i += 1;
230 inner_off = 0;
231 if (iov_i == data_copy.len) {
232 return off;
233 }
234 }
235 if (amt_read == 0) return off; // EOF
236 }
237}
238
239pub async fn preadWindows(loop: *Loop, fd: os.FileHandle, data: []u8, offset: u64) !usize {
240 // workaround for https://github.com/ziglang/zig/issues/1194
241 suspend {
242 resume @handle();
243 }
244
245 var resume_node = Loop.ResumeNode.Basic{
246 .base = Loop.ResumeNode{
247 .id = Loop.ResumeNode.Id.Basic,
248 .handle = @handle(),
249 },
250 };
251 const completion_key = @ptrToInt(&resume_node.base);
252 // TODO support concurrent async ops on the file handle
253 // we can do this by ignoring completion key and using @fieldParentPtr with the *Overlapped
254 _ = try os.windowsCreateIoCompletionPort(fd, loop.os_data.io_port, completion_key, undefined);
255 var overlapped = windows.OVERLAPPED{
256 .Internal = 0,
257 .InternalHigh = 0,
258 .Offset = @truncate(u32, offset),
259 .OffsetHigh = @truncate(u32, offset >> 32),
260 .hEvent = null,
261 };
262 loop.beginOneEvent();
263 errdefer loop.finishOneEvent();
264
265 errdefer {
266 _ = windows.CancelIoEx(fd, &overlapped);
267 }
268 suspend {
269 _ = windows.ReadFile(fd, data.ptr, @intCast(windows.DWORD, data.len), null, &overlapped);
270 }
271 var bytes_transferred: windows.DWORD = undefined;
272 if (windows.GetOverlappedResult(fd, &overlapped, &bytes_transferred, windows.FALSE) == 0) {
273 const err = windows.GetLastError();
274 return switch (err) {
275 windows.ERROR.IO_PENDING => unreachable,
276 windows.ERROR.OPERATION_ABORTED => error.OperationAborted,
277 windows.ERROR.BROKEN_PIPE => error.BrokenPipe,
278 else => os.unexpectedErrorWindows(err),
279 };
280 }
281 return usize(bytes_transferred);
282}
283
284/// data - just the inner references - must live until preadv promise completes.
285pub async fn preadvPosix(loop: *Loop, fd: os.FileHandle, data: []const []u8, offset: usize) !usize {
286 // workaround for https://github.com/ziglang/zig/issues/1194
287 suspend {
288 resume @handle();
289 }
290
291 const iovecs = try loop.allocator.alloc(os.posix.iovec, data.len);
292 defer loop.allocator.free(iovecs);
293
294 for (data) |buf, i| {
295 iovecs[i] = os.posix.iovec{
296 .iov_base = buf.ptr,
297 .iov_len = buf.len,
298 };
299 }
300
301 var req_node = RequestNode{
302 .prev = null,
303 .next = null,
304 .data = Request{
305 .msg = Request.Msg{
306 .PReadV = Request.Msg.PReadV{
307 .fd = fd,
308 .iov = iovecs,
309 .offset = offset,
310 .result = undefined,
311 },
312 },
313 .finish = Request.Finish{
314 .TickNode = Loop.NextTickNode{
315 .prev = null,
316 .next = null,
317 .data = @handle(),
318 },
319 },
320 },
321 };
322
323 errdefer loop.posixFsCancel(&req_node);
324
325 suspend {
326 loop.posixFsRequest(&req_node);
327 }
328
329 return req_node.data.msg.PReadV.result;
330}
331
332pub async fn openPosix(
333 loop: *Loop,
334 path: []const u8,
335 flags: u32,
336 mode: os.File.Mode,
337) os.File.OpenError!os.FileHandle {
338 // workaround for https://github.com/ziglang/zig/issues/1194
339 suspend {
340 resume @handle();
341 }
342
343 const path_with_null = try std.cstr.addNullByte(loop.allocator, path);
344 defer loop.allocator.free(path_with_null);
345
346 var req_node = RequestNode{
347 .prev = null,
348 .next = null,
349 .data = Request{
350 .msg = Request.Msg{
351 .Open = Request.Msg.Open{
352 .path = path_with_null[0..path.len],
353 .flags = flags,
354 .mode = mode,
355 .result = undefined,
356 },
357 },
358 .finish = Request.Finish{
359 .TickNode = Loop.NextTickNode{
360 .prev = null,
361 .next = null,
362 .data = @handle(),
363 },
364 },
365 },
366 };
367
368 errdefer loop.posixFsCancel(&req_node);
369
370 suspend {
371 loop.posixFsRequest(&req_node);
372 }
373
374 return req_node.data.msg.Open.result;
375}
376
377pub async fn openRead(loop: *Loop, path: []const u8) os.File.OpenError!os.FileHandle {
378 switch (builtin.os) {
379 builtin.Os.macosx, builtin.Os.linux => {
380 const flags = posix.O_LARGEFILE | posix.O_RDONLY | posix.O_CLOEXEC;
381 return await (async openPosix(loop, path, flags, os.File.default_mode) catch unreachable);
382 },
383
384 builtin.Os.windows => return os.windowsOpen(
385 loop.allocator,
386 path,
387 windows.GENERIC_READ,
388 windows.FILE_SHARE_READ,
389 windows.OPEN_EXISTING,
390 windows.FILE_ATTRIBUTE_NORMAL | windows.FILE_FLAG_OVERLAPPED,
391 ),
392
393 else => @compileError("Unsupported OS"),
394 }
395}
396
397/// Creates if does not exist. Truncates the file if it exists.
398/// Uses the default mode.
399pub async fn openWrite(loop: *Loop, path: []const u8) os.File.OpenError!os.FileHandle {
400 return await (async openWriteMode(loop, path, os.File.default_mode) catch unreachable);
401}
402
403/// Creates if does not exist. Truncates the file if it exists.
404pub async fn openWriteMode(loop: *Loop, path: []const u8, mode: os.File.Mode) os.File.OpenError!os.FileHandle {
405 switch (builtin.os) {
406 builtin.Os.macosx,
407 builtin.Os.linux,
408 => {
409 const flags = posix.O_LARGEFILE | posix.O_WRONLY | posix.O_CREAT | posix.O_CLOEXEC | posix.O_TRUNC;
410 return await (async openPosix(loop, path, flags, os.File.default_mode) catch unreachable);
411 },
412 builtin.Os.windows,
413 => return os.windowsOpen(
414 loop.allocator,
415 path,
416 windows.GENERIC_WRITE,
417 windows.FILE_SHARE_WRITE | windows.FILE_SHARE_READ | windows.FILE_SHARE_DELETE,
418 windows.CREATE_ALWAYS,
419 windows.FILE_ATTRIBUTE_NORMAL | windows.FILE_FLAG_OVERLAPPED,
420 ),
421 else => @compileError("Unsupported OS"),
422 }
423}
424
425/// Creates if does not exist. Does not truncate.
426pub async fn openReadWrite(
427 loop: *Loop,
428 path: []const u8,
429 mode: os.File.Mode,
430) os.File.OpenError!os.FileHandle {
431 switch (builtin.os) {
432 builtin.Os.macosx, builtin.Os.linux => {
433 const flags = posix.O_LARGEFILE | posix.O_RDWR | posix.O_CREAT | posix.O_CLOEXEC;
434 return await (async openPosix(loop, path, flags, mode) catch unreachable);
435 },
436
437 builtin.Os.windows => return os.windowsOpen(
438 loop.allocator,
439 path,
440 windows.GENERIC_WRITE|windows.GENERIC_READ,
441 windows.FILE_SHARE_WRITE | windows.FILE_SHARE_READ | windows.FILE_SHARE_DELETE,
442 windows.OPEN_ALWAYS,
443 windows.FILE_ATTRIBUTE_NORMAL | windows.FILE_FLAG_OVERLAPPED,
444 ),
445
446 else => @compileError("Unsupported OS"),
447 }
448}
449
450/// This abstraction helps to close file handles in defer expressions
451/// without the possibility of failure and without the use of suspend points.
452/// Start a `CloseOperation` before opening a file, so that you can defer
453/// `CloseOperation.finish`.
454/// If you call `setHandle` then finishing will close the fd; otherwise finishing
455/// will deallocate the `CloseOperation`.
456pub const CloseOperation = struct {
457 loop: *Loop,
458 os_data: OsData,
459
460 const OsData = switch (builtin.os) {
461 builtin.Os.linux, builtin.Os.macosx => OsDataPosix,
462
463 builtin.Os.windows => struct {
464 handle: ?os.FileHandle,
465 },
466
467 else => @compileError("Unsupported OS"),
468 };
469
470 const OsDataPosix = struct {
471 have_fd: bool,
472 close_req_node: RequestNode,
473 };
474
475 pub fn start(loop: *Loop) (error{OutOfMemory}!*CloseOperation) {
476 const self = try loop.allocator.createOne(CloseOperation);
477 self.* = CloseOperation{
478 .loop = loop,
479 .os_data = switch (builtin.os) {
480 builtin.Os.linux, builtin.Os.macosx => initOsDataPosix(self),
481 builtin.Os.windows => OsData{ .handle = null },
482 else => @compileError("Unsupported OS"),
483 },
484 };
485 return self;
486 }
487
488 fn initOsDataPosix(self: *CloseOperation) OsData {
489 return OsData{
490 .have_fd = false,
491 .close_req_node = RequestNode{
492 .prev = null,
493 .next = null,
494 .data = Request{
495 .msg = Request.Msg{
496 .Close = Request.Msg.Close{ .fd = undefined },
497 },
498 .finish = Request.Finish{ .DeallocCloseOperation = self },
499 },
500 },
501 };
502 }
503
504 /// Defer this after creating.
505 pub fn finish(self: *CloseOperation) void {
506 switch (builtin.os) {
507 builtin.Os.linux,
508 builtin.Os.macosx,
509 => {
510 if (self.os_data.have_fd) {
511 self.loop.posixFsRequest(&self.os_data.close_req_node);
512 } else {
513 self.loop.allocator.destroy(self);
514 }
515 },
516 builtin.Os.windows,
517 => {
518 if (self.os_data.handle) |handle| {
519 os.close(handle);
520 }
521 self.loop.allocator.destroy(self);
522 },
523 else => @compileError("Unsupported OS"),
524 }
525 }
526
527 pub fn setHandle(self: *CloseOperation, handle: os.FileHandle) void {
528 switch (builtin.os) {
529 builtin.Os.linux,
530 builtin.Os.macosx,
531 => {
532 self.os_data.close_req_node.data.msg.Close.fd = handle;
533 self.os_data.have_fd = true;
534 },
535 builtin.Os.windows,
536 => {
537 self.os_data.handle = handle;
538 },
539 else => @compileError("Unsupported OS"),
540 }
541 }
542
543 /// Undo a `setHandle`.
544 pub fn clearHandle(self: *CloseOperation) void {
545 switch (builtin.os) {
546 builtin.Os.linux,
547 builtin.Os.macosx,
548 => {
549 self.os_data.have_fd = false;
550 },
551 builtin.Os.windows,
552 => {
553 self.os_data.handle = null;
554 },
555 else => @compileError("Unsupported OS"),
556 }
557 }
558
559 pub fn getHandle(self: *CloseOperation) os.FileHandle {
560 switch (builtin.os) {
561 builtin.Os.linux,
562 builtin.Os.macosx,
563 => {
564 assert(self.os_data.have_fd);
565 return self.os_data.close_req_node.data.msg.Close.fd;
566 },
567 builtin.Os.windows,
568 => {
569 return self.os_data.handle.?;
570 },
571 else => @compileError("Unsupported OS"),
572 }
573 }
574};
575
576/// contents must remain alive until writeFile completes.
577/// TODO make this atomic or provide writeFileAtomic and rename this one to writeFileTruncate
578pub async fn writeFile(loop: *Loop, path: []const u8, contents: []const u8) !void {
579 return await (async writeFileMode(loop, path, contents, os.File.default_mode) catch unreachable);
580}
581
582/// contents must remain alive until writeFile completes.
583pub async fn writeFileMode(loop: *Loop, path: []const u8, contents: []const u8, mode: os.File.Mode) !void {
584 switch (builtin.os) {
585 builtin.Os.linux,
586 builtin.Os.macosx,
587 => return await (async writeFileModeThread(loop, path, contents, mode) catch unreachable),
588 builtin.Os.windows,
589 => return await (async writeFileWindows(loop, path, contents) catch unreachable),
590 else => @compileError("Unsupported OS"),
591 }
592}
593
594async fn writeFileWindows(loop: *Loop, path: []const u8, contents: []const u8) !void {
595 const handle = try os.windowsOpen(
596 loop.allocator,
597 path,
598 windows.GENERIC_WRITE,
599 windows.FILE_SHARE_WRITE | windows.FILE_SHARE_READ | windows.FILE_SHARE_DELETE,
600 windows.CREATE_ALWAYS,
601 windows.FILE_ATTRIBUTE_NORMAL | windows.FILE_FLAG_OVERLAPPED,
602 );
603 defer os.close(handle);
604
605 try await (async pwriteWindows(loop, handle, contents, 0) catch unreachable);
606}
607
608async fn writeFileModeThread(loop: *Loop, path: []const u8, contents: []const u8, mode: os.File.Mode) !void {
609 // workaround for https://github.com/ziglang/zig/issues/1194
610 suspend {
611 resume @handle();
612 }
613
614 const path_with_null = try std.cstr.addNullByte(loop.allocator, path);
615 defer loop.allocator.free(path_with_null);
616
617 var req_node = RequestNode{
618 .prev = null,
619 .next = null,
620 .data = Request{
621 .msg = Request.Msg{
622 .WriteFile = Request.Msg.WriteFile{
623 .path = path_with_null[0..path.len],
624 .contents = contents,
625 .mode = mode,
626 .result = undefined,
627 },
628 },
629 .finish = Request.Finish{
630 .TickNode = Loop.NextTickNode{
631 .prev = null,
632 .next = null,
633 .data = @handle(),
634 },
635 },
636 },
637 };
638
639 errdefer loop.posixFsCancel(&req_node);
640
641 suspend {
642 loop.posixFsRequest(&req_node);
643 }
644
645 return req_node.data.msg.WriteFile.result;
646}
647
648/// The promise resumes when the last data has been confirmed written, but before the file handle
649/// is closed.
650/// Caller owns returned memory.
651pub async fn readFile(loop: *Loop, file_path: []const u8, max_size: usize) ![]u8 {
652 var close_op = try CloseOperation.start(loop);
653 defer close_op.finish();
654
655 const path_with_null = try std.cstr.addNullByte(loop.allocator, file_path);
656 defer loop.allocator.free(path_with_null);
657
658 const fd = try await (async openRead(loop, path_with_null[0..file_path.len]) catch unreachable);
659 close_op.setHandle(fd);
660
661 var list = std.ArrayList(u8).init(loop.allocator);
662 defer list.deinit();
663
664 while (true) {
665 try list.ensureCapacity(list.len + os.page_size);
666 const buf = list.items[list.len..];
667 const buf_array = [][]u8{buf};
668 const amt = try await (async preadv(loop, fd, buf_array, list.len) catch unreachable);
669 list.len += amt;
670 if (list.len > max_size) {
671 return error.FileTooBig;
672 }
673 if (amt < buf.len) {
674 return list.toOwnedSlice();
675 }
676 }
677}
678
679pub const WatchEventId = enum {
680 CloseWrite,
681 Delete,
682};
683
684pub const WatchEventError = error{
685 UserResourceLimitReached,
686 SystemResources,
687 AccessDenied,
688 Unexpected, // TODO remove this possibility
689};
690
691pub fn Watch(comptime V: type) type {
692 return struct {
693 channel: *event.Channel(Event.Error!Event),
694 os_data: OsData,
695
696 const OsData = switch (builtin.os) {
697 builtin.Os.macosx => struct {
698 file_table: FileTable,
699 table_lock: event.Lock,
700
701 const FileTable = std.AutoHashMap([]const u8, *Put);
702 const Put = struct {
703 putter: promise,
704 value_ptr: *V,
705 };
706 },
707
708 builtin.Os.linux => LinuxOsData,
709 builtin.Os.windows => WindowsOsData,
710
711 else => @compileError("Unsupported OS"),
712 };
713
714 const WindowsOsData = struct {
715 table_lock: event.Lock,
716 dir_table: DirTable,
717 all_putters: std.atomic.Queue(promise),
718 ref_count: std.atomic.Int(usize),
719
720 const DirTable = std.AutoHashMap([]const u8, *Dir);
721 const FileTable = std.AutoHashMap([]const u16, V);
722
723 const Dir = struct {
724 putter: promise,
725 file_table: FileTable,
726 table_lock: event.Lock,
727 };
728 };
729
730 const LinuxOsData = struct {
731 putter: promise,
732 inotify_fd: i32,
733 wd_table: WdTable,
734 table_lock: event.Lock,
735
736 const WdTable = std.AutoHashMap(i32, Dir);
737 const FileTable = std.AutoHashMap([]const u8, V);
738
739 const Dir = struct {
740 dirname: []const u8,
741 file_table: FileTable,
742 };
743 };
744
745 const FileToHandle = std.AutoHashMap([]const u8, promise);
746
747 const Self = this;
748
749 pub const Event = struct {
750 id: Id,
751 data: V,
752
753 pub const Id = WatchEventId;
754 pub const Error = WatchEventError;
755 };
756
757 pub fn create(loop: *Loop, event_buf_count: usize) !*Self {
758 const channel = try event.Channel(Self.Event.Error!Self.Event).create(loop, event_buf_count);
759 errdefer channel.destroy();
760
761 switch (builtin.os) {
762 builtin.Os.linux => {
763 const inotify_fd = try os.linuxINotifyInit1(os.linux.IN_NONBLOCK | os.linux.IN_CLOEXEC);
764 errdefer os.close(inotify_fd);
765
766 var result: *Self = undefined;
767 _ = try async<loop.allocator> linuxEventPutter(inotify_fd, channel, &result);
768 return result;
769 },
770
771 builtin.Os.windows => {
772 const self = try loop.allocator.createOne(Self);
773 errdefer loop.allocator.destroy(self);
774 self.* = Self{
775 .channel = channel,
776 .os_data = OsData{
777 .table_lock = event.Lock.init(loop),
778 .dir_table = OsData.DirTable.init(loop.allocator),
779 .ref_count = std.atomic.Int(usize).init(1),
780 .all_putters = std.atomic.Queue(promise).init(),
781 },
782 };
783 return self;
784 },
785
786 builtin.Os.macosx => {
787 const self = try loop.allocator.createOne(Self);
788 errdefer loop.allocator.destroy(self);
789
790 self.* = Self{
791 .channel = channel,
792 .os_data = OsData{
793 .table_lock = event.Lock.init(loop),
794 .file_table = OsData.FileTable.init(loop.allocator),
795 },
796 };
797 return self;
798 },
799 else => @compileError("Unsupported OS"),
800 }
801 }
802
803 /// All addFile calls and removeFile calls must have completed.
804 pub fn destroy(self: *Self) void {
805 switch (builtin.os) {
806 builtin.Os.macosx => {
807 // TODO we need to cancel the coroutines before destroying the lock
808 self.os_data.table_lock.deinit();
809 var it = self.os_data.file_table.iterator();
810 while (it.next()) |entry| {
811 cancel entry.value.putter;
812 self.channel.loop.allocator.free(entry.key);
813 }
814 self.channel.destroy();
815 },
816 builtin.Os.linux => cancel self.os_data.putter,
817 builtin.Os.windows => {
818 while (self.os_data.all_putters.get()) |putter_node| {
819 cancel putter_node.data;
820 }
821 self.deref();
822 },
823 else => @compileError("Unsupported OS"),
824 }
825 }
826
827 fn ref(self: *Self) void {
828 _ = self.os_data.ref_count.incr();
829 }
830
831 fn deref(self: *Self) void {
832 if (self.os_data.ref_count.decr() == 1) {
833 const allocator = self.channel.loop.allocator;
834 self.os_data.table_lock.deinit();
835 var it = self.os_data.dir_table.iterator();
836 while (it.next()) |entry| {
837 allocator.free(entry.key);
838 allocator.destroy(entry.value);
839 }
840 self.os_data.dir_table.deinit();
841 self.channel.destroy();
842 allocator.destroy(self);
843 }
844 }
845
846 pub async fn addFile(self: *Self, file_path: []const u8, value: V) !?V {
847 switch (builtin.os) {
848 builtin.Os.macosx => return await (async addFileMacosx(self, file_path, value) catch unreachable),
849 builtin.Os.linux => return await (async addFileLinux(self, file_path, value) catch unreachable),
850 builtin.Os.windows => return await (async addFileWindows(self, file_path, value) catch unreachable),
851 else => @compileError("Unsupported OS"),
852 }
853 }
854
855 async fn addFileMacosx(self: *Self, file_path: []const u8, value: V) !?V {
856 const resolved_path = try os.path.resolve(self.channel.loop.allocator, file_path);
857 var resolved_path_consumed = false;
858 defer if (!resolved_path_consumed) self.channel.loop.allocator.free(resolved_path);
859
860 var close_op = try CloseOperation.start(self.channel.loop);
861 var close_op_consumed = false;
862 defer if (!close_op_consumed) close_op.finish();
863
864 const flags = posix.O_SYMLINK | posix.O_EVTONLY;
865 const mode = 0;
866 const fd = try await (async openPosix(self.channel.loop, resolved_path, flags, mode) catch unreachable);
867 close_op.setHandle(fd);
868
869 var put_data: *OsData.Put = undefined;
870 const putter = try async self.kqPutEvents(close_op, value, &put_data);
871 close_op_consumed = true;
872 errdefer cancel putter;
873
874 const result = blk: {
875 const held = await (async self.os_data.table_lock.acquire() catch unreachable);
876 defer held.release();
877
878 const gop = try self.os_data.file_table.getOrPut(resolved_path);
879 if (gop.found_existing) {
880 const prev_value = gop.kv.value.value_ptr.*;
881 cancel gop.kv.value.putter;
882 gop.kv.value = put_data;
883 break :blk prev_value;
884 } else {
885 resolved_path_consumed = true;
886 gop.kv.value = put_data;
887 break :blk null;
888 }
889 };
890
891 return result;
892 }
893
894 async fn kqPutEvents(self: *Self, close_op: *CloseOperation, value: V, out_put: **OsData.Put) void {
895 // TODO https://github.com/ziglang/zig/issues/1194
896 suspend {
897 resume @handle();
898 }
899
900 var value_copy = value;
901 var put = OsData.Put{
902 .putter = @handle(),
903 .value_ptr = &value_copy,
904 };
905 out_put.* = &put;
906 self.channel.loop.beginOneEvent();
907
908 defer {
909 close_op.finish();
910 self.channel.loop.finishOneEvent();
911 }
912
913 while (true) {
914 if (await (async self.channel.loop.bsdWaitKev(
915 @intCast(usize, close_op.getHandle()),
916 posix.EVFILT_VNODE,
917 posix.NOTE_WRITE | posix.NOTE_DELETE,
918 ) catch unreachable)) |kev| {
919 // TODO handle EV_ERROR
920 if (kev.fflags & posix.NOTE_DELETE != 0) {
921 await (async self.channel.put(Self.Event{
922 .id = Event.Id.Delete,
923 .data = value_copy,
924 }) catch unreachable);
925 } else if (kev.fflags & posix.NOTE_WRITE != 0) {
926 await (async self.channel.put(Self.Event{
927 .id = Event.Id.CloseWrite,
928 .data = value_copy,
929 }) catch unreachable);
930 }
931 } else |err| switch (err) {
932 error.EventNotFound => unreachable,
933 error.ProcessNotFound => unreachable,
934 error.AccessDenied, error.SystemResources => {
935 // TODO https://github.com/ziglang/zig/issues/769
936 const casted_err = @errSetCast(error{
937 AccessDenied,
938 SystemResources,
939 }, err);
940 await (async self.channel.put(casted_err) catch unreachable);
941 },
942 }
943 }
944 }
945
946 async fn addFileLinux(self: *Self, file_path: []const u8, value: V) !?V {
947 const value_copy = value;
948
949 const dirname = os.path.dirname(file_path) orelse ".";
950 const dirname_with_null = try std.cstr.addNullByte(self.channel.loop.allocator, dirname);
951 var dirname_with_null_consumed = false;
952 defer if (!dirname_with_null_consumed) self.channel.loop.allocator.free(dirname_with_null);
953
954 const basename = os.path.basename(file_path);
955 const basename_with_null = try std.cstr.addNullByte(self.channel.loop.allocator, basename);
956 var basename_with_null_consumed = false;
957 defer if (!basename_with_null_consumed) self.channel.loop.allocator.free(basename_with_null);
958
959 const wd = try os.linuxINotifyAddWatchC(
960 self.os_data.inotify_fd,
961 dirname_with_null.ptr,
962 os.linux.IN_CLOSE_WRITE | os.linux.IN_ONLYDIR | os.linux.IN_EXCL_UNLINK,
963 );
964 // wd is either a newly created watch or an existing one.
965
966 const held = await (async self.os_data.table_lock.acquire() catch unreachable);
967 defer held.release();
968
969 const gop = try self.os_data.wd_table.getOrPut(wd);
970 if (!gop.found_existing) {
971 gop.kv.value = OsData.Dir{
972 .dirname = dirname_with_null,
973 .file_table = OsData.FileTable.init(self.channel.loop.allocator),
974 };
975 dirname_with_null_consumed = true;
976 }
977 const dir = &gop.kv.value;
978
979 const file_table_gop = try dir.file_table.getOrPut(basename_with_null);
980 if (file_table_gop.found_existing) {
981 const prev_value = file_table_gop.kv.value;
982 file_table_gop.kv.value = value_copy;
983 return prev_value;
984 } else {
985 file_table_gop.kv.value = value_copy;
986 basename_with_null_consumed = true;
987 return null;
988 }
989 }
990
991 async fn addFileWindows(self: *Self, file_path: []const u8, value: V) !?V {
992 const value_copy = value;
993 // TODO we might need to convert dirname and basename to canonical file paths ("short"?)
994
995 const dirname = try std.mem.dupe(self.channel.loop.allocator, u8, os.path.dirname(file_path) orelse ".");
996 var dirname_consumed = false;
997 defer if (!dirname_consumed) self.channel.loop.allocator.free(dirname);
998
999 const dirname_utf16le = try std.unicode.utf8ToUtf16LeWithNull(self.channel.loop.allocator, dirname);
1000 defer self.channel.loop.allocator.free(dirname_utf16le);
1001
1002 // TODO https://github.com/ziglang/zig/issues/265
1003 const basename = os.path.basename(file_path);
1004 const basename_utf16le_null = try std.unicode.utf8ToUtf16LeWithNull(self.channel.loop.allocator, basename);
1005 var basename_utf16le_null_consumed = false;
1006 defer if (!basename_utf16le_null_consumed) self.channel.loop.allocator.free(basename_utf16le_null);
1007 const basename_utf16le_no_null = basename_utf16le_null[0..basename_utf16le_null.len-1];
1008
1009 const dir_handle = windows.CreateFileW(
1010 dirname_utf16le.ptr,
1011 windows.FILE_LIST_DIRECTORY,
1012 windows.FILE_SHARE_READ | windows.FILE_SHARE_DELETE | windows.FILE_SHARE_WRITE,
1013 null,
1014 windows.OPEN_EXISTING,
1015 windows.FILE_FLAG_BACKUP_SEMANTICS | windows.FILE_FLAG_OVERLAPPED,
1016 null,
1017 );
1018 if (dir_handle == windows.INVALID_HANDLE_VALUE) {
1019 const err = windows.GetLastError();
1020 switch (err) {
1021 windows.ERROR.FILE_NOT_FOUND,
1022 windows.ERROR.PATH_NOT_FOUND,
1023 => return error.PathNotFound,
1024 else => return os.unexpectedErrorWindows(err),
1025 }
1026 }
1027 var dir_handle_consumed = false;
1028 defer if (!dir_handle_consumed) os.close(dir_handle);
1029
1030 const held = await (async self.os_data.table_lock.acquire() catch unreachable);
1031 defer held.release();
1032
1033 const gop = try self.os_data.dir_table.getOrPut(dirname);
1034 if (gop.found_existing) {
1035 const dir = gop.kv.value;
1036 const held_dir_lock = await (async dir.table_lock.acquire() catch unreachable);
1037 defer held_dir_lock.release();
1038
1039 const file_gop = try dir.file_table.getOrPut(basename_utf16le_no_null);
1040 if (file_gop.found_existing) {
1041 const prev_value = file_gop.kv.value;
1042 file_gop.kv.value = value_copy;
1043 return prev_value;
1044 } else {
1045 file_gop.kv.value = value_copy;
1046 basename_utf16le_null_consumed = true;
1047 return null;
1048 }
1049 } else {
1050 errdefer _ = self.os_data.dir_table.remove(dirname);
1051 const dir = try self.channel.loop.allocator.createOne(OsData.Dir);
1052 errdefer self.channel.loop.allocator.destroy(dir);
1053
1054 dir.* = OsData.Dir{
1055 .file_table = OsData.FileTable.init(self.channel.loop.allocator),
1056 .table_lock = event.Lock.init(self.channel.loop),
1057 .putter = undefined,
1058 };
1059 gop.kv.value = dir;
1060 assert((try dir.file_table.put(basename_utf16le_no_null, value_copy)) == null);
1061 basename_utf16le_null_consumed = true;
1062
1063 dir.putter = try async self.windowsDirReader(dir_handle, dir);
1064 dir_handle_consumed = true;
1065
1066 dirname_consumed = true;
1067
1068 return null;
1069 }
1070 }
1071
1072 async fn windowsDirReader(self: *Self, dir_handle: windows.HANDLE, dir: *OsData.Dir) void {
1073 // TODO https://github.com/ziglang/zig/issues/1194
1074 suspend {
1075 resume @handle();
1076 }
1077
1078 self.ref();
1079 defer self.deref();
1080
1081 defer os.close(dir_handle);
1082
1083 var putter_node = std.atomic.Queue(promise).Node{
1084 .data = @handle(),
1085 .prev = null,
1086 .next = null,
1087 };
1088 self.os_data.all_putters.put(&putter_node);
1089 defer _ = self.os_data.all_putters.remove(&putter_node);
1090
1091 var resume_node = Loop.ResumeNode.Basic{
1092 .base = Loop.ResumeNode{
1093 .id = Loop.ResumeNode.Id.Basic,
1094 .handle = @handle(),
1095 },
1096 };
1097 const completion_key = @ptrToInt(&resume_node.base);
1098 var overlapped = windows.OVERLAPPED{
1099 .Internal = 0,
1100 .InternalHigh = 0,
1101 .Offset = 0,
1102 .OffsetHigh = 0,
1103 .hEvent = null,
1104 };
1105 var event_buf: [4096]u8 align(@alignOf(windows.FILE_NOTIFY_INFORMATION)) = undefined;
1106
1107 // TODO handle this error not in the channel but in the setup
1108 _ = os.windowsCreateIoCompletionPort(
1109 dir_handle, self.channel.loop.os_data.io_port, completion_key, undefined,
1110 ) catch |err| {
1111 await (async self.channel.put(err) catch unreachable);
1112 return;
1113 };
1114
1115 while (true) {
1116 {
1117 // TODO only 1 beginOneEvent for the whole coroutine
1118 self.channel.loop.beginOneEvent();
1119 errdefer self.channel.loop.finishOneEvent();
1120 errdefer {
1121 _ = windows.CancelIoEx(dir_handle, &overlapped);
1122 }
1123 suspend {
1124 _ = windows.ReadDirectoryChangesW(
1125 dir_handle,
1126 &event_buf,
1127 @intCast(windows.DWORD, event_buf.len),
1128 windows.FALSE, // watch subtree
1129 windows.FILE_NOTIFY_CHANGE_FILE_NAME | windows.FILE_NOTIFY_CHANGE_DIR_NAME |
1130 windows.FILE_NOTIFY_CHANGE_ATTRIBUTES | windows.FILE_NOTIFY_CHANGE_SIZE |
1131 windows.FILE_NOTIFY_CHANGE_LAST_WRITE | windows.FILE_NOTIFY_CHANGE_LAST_ACCESS |
1132 windows.FILE_NOTIFY_CHANGE_CREATION | windows.FILE_NOTIFY_CHANGE_SECURITY,
1133 null, // number of bytes transferred (unused for async)
1134 &overlapped,
1135 null, // completion routine - unused because we use IOCP
1136 );
1137 }
1138 }
1139 var bytes_transferred: windows.DWORD = undefined;
1140 if (windows.GetOverlappedResult(dir_handle, &overlapped, &bytes_transferred, windows.FALSE) == 0) {
1141 const errno = windows.GetLastError();
1142 const err = switch (errno) {
1143 else => os.unexpectedErrorWindows(errno),
1144 };
1145 await (async self.channel.put(err) catch unreachable);
1146 } else {
1147 // can't use @bytesToSlice because of the special variable length name field
1148 var ptr = event_buf[0..].ptr;
1149 const end_ptr = ptr + bytes_transferred;
1150 var ev: *windows.FILE_NOTIFY_INFORMATION = undefined;
1151 while (@ptrToInt(ptr) < @ptrToInt(end_ptr)) : (ptr += ev.NextEntryOffset) {
1152 ev = @ptrCast(*windows.FILE_NOTIFY_INFORMATION, ptr);
1153 const emit = switch (ev.Action) {
1154 windows.FILE_ACTION_REMOVED => WatchEventId.Delete,
1155 windows.FILE_ACTION_MODIFIED => WatchEventId.CloseWrite,
1156 else => null,
1157 };
1158 if (emit) |id| {
1159 const basename_utf16le = ([*]u16)(&ev.FileName)[0..ev.FileNameLength/2];
1160 const user_value = blk: {
1161 const held = await (async dir.table_lock.acquire() catch unreachable);
1162 defer held.release();
1163
1164 if (dir.file_table.get(basename_utf16le)) |entry| {
1165 break :blk entry.value;
1166 } else {
1167 break :blk null;
1168 }
1169 };
1170 if (user_value) |v| {
1171 await (async self.channel.put(Event{
1172 .id = id,
1173 .data = v,
1174 }) catch unreachable);
1175 }
1176 }
1177 if (ev.NextEntryOffset == 0) break;
1178 }
1179 }
1180 }
1181 }
1182
1183 pub async fn removeFile(self: *Self, file_path: []const u8) ?V {
1184 @panic("TODO");
1185 }
1186
1187 async fn linuxEventPutter(inotify_fd: i32, channel: *event.Channel(Event.Error!Event), out_watch: **Self) void {
1188 // TODO https://github.com/ziglang/zig/issues/1194
1189 suspend {
1190 resume @handle();
1191 }
1192
1193 const loop = channel.loop;
1194
1195 var watch = Self{
1196 .channel = channel,
1197 .os_data = OsData{
1198 .putter = @handle(),
1199 .inotify_fd = inotify_fd,
1200 .wd_table = OsData.WdTable.init(loop.allocator),
1201 .table_lock = event.Lock.init(loop),
1202 },
1203 };
1204 out_watch.* = &watch;
1205
1206 loop.beginOneEvent();
1207
1208 defer {
1209 watch.os_data.table_lock.deinit();
1210 var wd_it = watch.os_data.wd_table.iterator();
1211 while (wd_it.next()) |wd_entry| {
1212 var file_it = wd_entry.value.file_table.iterator();
1213 while (file_it.next()) |file_entry| {
1214 loop.allocator.free(file_entry.key);
1215 }
1216 loop.allocator.free(wd_entry.value.dirname);
1217 }
1218 loop.finishOneEvent();
1219 os.close(inotify_fd);
1220 channel.destroy();
1221 }
1222
1223 var event_buf: [4096]u8 align(@alignOf(os.linux.inotify_event)) = undefined;
1224
1225 while (true) {
1226 const rc = os.linux.read(inotify_fd, &event_buf, event_buf.len);
1227 const errno = os.linux.getErrno(rc);
1228 switch (errno) {
1229 0 => {
1230 // can't use @bytesToSlice because of the special variable length name field
1231 var ptr = event_buf[0..].ptr;
1232 const end_ptr = ptr + event_buf.len;
1233 var ev: *os.linux.inotify_event = undefined;
1234 while (@ptrToInt(ptr) < @ptrToInt(end_ptr)) : (ptr += @sizeOf(os.linux.inotify_event) + ev.len) {
1235 ev = @ptrCast(*os.linux.inotify_event, ptr);
1236 if (ev.mask & os.linux.IN_CLOSE_WRITE == os.linux.IN_CLOSE_WRITE) {
1237 const basename_ptr = ptr + @sizeOf(os.linux.inotify_event);
1238 const basename_with_null = basename_ptr[0 .. std.cstr.len(basename_ptr) + 1];
1239 const user_value = blk: {
1240 const held = await (async watch.os_data.table_lock.acquire() catch unreachable);
1241 defer held.release();
1242
1243 const dir = &watch.os_data.wd_table.get(ev.wd).?.value;
1244 if (dir.file_table.get(basename_with_null)) |entry| {
1245 break :blk entry.value;
1246 } else {
1247 break :blk null;
1248 }
1249 };
1250 if (user_value) |v| {
1251 await (async channel.put(Event{
1252 .id = WatchEventId.CloseWrite,
1253 .data = v,
1254 }) catch unreachable);
1255 }
1256 }
1257 }
1258 },
1259 os.linux.EINTR => continue,
1260 os.linux.EINVAL => unreachable,
1261 os.linux.EFAULT => unreachable,
1262 os.linux.EAGAIN => {
1263 (await (async loop.linuxWaitFd(
1264 inotify_fd,
1265 os.linux.EPOLLET | os.linux.EPOLLIN,
1266 ) catch unreachable)) catch |err| {
1267 const transformed_err = switch (err) {
1268 error.InvalidFileDescriptor => unreachable,
1269 error.FileDescriptorAlreadyPresentInSet => unreachable,
1270 error.InvalidSyscall => unreachable,
1271 error.OperationCausesCircularLoop => unreachable,
1272 error.FileDescriptorNotRegistered => unreachable,
1273 error.SystemResources => error.SystemResources,
1274 error.UserResourceLimitReached => error.UserResourceLimitReached,
1275 error.FileDescriptorIncompatibleWithEpoll => unreachable,
1276 error.Unexpected => unreachable,
1277 };
1278 await (async channel.put(transformed_err) catch unreachable);
1279 };
1280 },
1281 else => unreachable,
1282 }
1283 }
1284 }
1285 };
1286}
1287
1288const test_tmp_dir = "std_event_fs_test";
1289
1290test "write a file, watch it, write it again" {
1291 var da = std.heap.DirectAllocator.init();
1292 defer da.deinit();
1293
1294 const allocator = &da.allocator;
1295
1296 // TODO move this into event loop too
1297 try os.makePath(allocator, test_tmp_dir);
1298 defer os.deleteTree(allocator, test_tmp_dir) catch {};
1299
1300 var loop: Loop = undefined;
1301 try loop.initMultiThreaded(allocator);
1302 defer loop.deinit();
1303
1304 var result: error!void = error.ResultNeverWritten;
1305 const handle = try async<allocator> testFsWatchCantFail(&loop, &result);
1306 defer cancel handle;
1307
1308 loop.run();
1309 return result;
1310}
1311
1312async fn testFsWatchCantFail(loop: *Loop, result: *(error!void)) void {
1313 result.* = await async testFsWatch(loop) catch unreachable;
1314}
1315
1316async fn testFsWatch(loop: *Loop) !void {
1317 const file_path = try os.path.join(loop.allocator, test_tmp_dir, "file.txt");
1318 defer loop.allocator.free(file_path);
1319
1320 const contents =
1321 \\line 1
1322 \\line 2
1323 ;
1324 const line2_offset = 7;
1325
1326 // first just write then read the file
1327 try await try async writeFile(loop, file_path, contents);
1328
1329 const read_contents = try await try async readFile(loop, file_path, 1024 * 1024);
1330 assert(mem.eql(u8, read_contents, contents));
1331
1332 // now watch the file
1333 var watch = try Watch(void).create(loop, 0);
1334 defer watch.destroy();
1335
1336 assert((try await try async watch.addFile(file_path, {})) == null);
1337
1338 const ev = try async watch.channel.get();
1339 var ev_consumed = false;
1340 defer if (!ev_consumed) cancel ev;
1341
1342 // overwrite line 2
1343 const fd = try await try async openReadWrite(loop, file_path, os.File.default_mode);
1344 {
1345 defer os.close(fd);
1346
1347 try await try async pwritev(loop, fd, []const []const u8{"lorem ipsum"}, line2_offset);
1348 }
1349
1350 ev_consumed = true;
1351 switch ((try await ev).id) {
1352 WatchEventId.CloseWrite => {},
1353 WatchEventId.Delete => @panic("wrong event"),
1354 }
1355 const contents_updated = try await try async readFile(loop, file_path, 1024 * 1024);
1356 assert(mem.eql(u8, contents_updated,
1357 \\line 1
1358 \\lorem ipsum
1359 ));
1360
1361 // TODO test deleting the file and then re-adding it. we should get events for both
1362}
std/event/group.zig+13-15
......@@ -29,6 +29,17 @@ pub fn Group(comptime ReturnType: type) type {
2929 };
3030 }
3131
32 /// Cancel all the outstanding promises. Can be called even if wait was already called.
33 pub fn deinit(self: *Self) void {
34 while (self.coro_stack.pop()) |node| {
35 cancel node.data;
36 }
37 while (self.alloc_stack.pop()) |node| {
38 cancel node.data;
39 self.lock.loop.allocator.destroy(node);
40 }
41 }
42
3243 /// Add a promise to the group. Thread-safe.
3344 pub fn add(self: *Self, handle: promise->ReturnType) (error{OutOfMemory}!void) {
3445 const node = try self.lock.loop.allocator.create(Stack.Node{
......@@ -88,7 +99,7 @@ pub fn Group(comptime ReturnType: type) type {
8899 await node.data;
89100 } else {
90101 (await node.data) catch |err| {
91 self.cancelAll();
102 self.deinit();
92103 return err;
93104 };
94105 }
......@@ -100,25 +111,12 @@ pub fn Group(comptime ReturnType: type) type {
100111 await handle;
101112 } else {
102113 (await handle) catch |err| {
103 self.cancelAll();
114 self.deinit();
104115 return err;
105116 };
106117 }
107118 }
108119 }
109
110 /// Cancel all the outstanding promises. May only be called if wait was never called.
111 /// TODO These should be `cancelasync` not `cancel`.
112 /// See https://github.com/ziglang/zig/issues/1261
113 pub fn cancelAll(self: *Self) void {
114 while (self.coro_stack.pop()) |node| {
115 cancel node.data;
116 }
117 while (self.alloc_stack.pop()) |node| {
118 cancel node.data;
119 self.lock.loop.allocator.destroy(node);
120 }
121 }
122120 };
123121}
124122
std/event/lock.zig+10-5
......@@ -9,6 +9,7 @@ const Loop = std.event.Loop;
99/// Thread-safe async/await lock.
1010/// Does not make any syscalls - coroutines which are waiting for the lock are suspended, and
1111/// are resumed when the lock is released, in order.
12/// Allows only one actor to hold the lock.
1213pub const Lock = struct {
1314 loop: *Loop,
1415 shared_bit: u8, // TODO make this a bool
......@@ -90,13 +91,14 @@ pub const Lock = struct {
9091 }
9192
9293 pub async fn acquire(self: *Lock) Held {
94 // TODO explicitly put this memory in the coroutine frame #1194
9395 suspend {
94 // TODO explicitly put this memory in the coroutine frame #1194
95 var my_tick_node = Loop.NextTickNode{
96 .data = @handle(),
97 .next = undefined,
98 };
96 resume @handle();
97 }
98 var my_tick_node = Loop.NextTickNode.init(@handle());
9999
100 errdefer _ = self.queue.remove(&my_tick_node); // TODO test canceling an acquire
101 suspend {
100102 self.queue.put(&my_tick_node);
101103
102104 // At this point, we are in the queue, so we might have already been resumed and this coroutine
......@@ -146,6 +148,7 @@ async fn testLock(loop: *Loop, lock: *Lock) void {
146148 }
147149 const handle1 = async lockRunner(lock) catch @panic("out of memory");
148150 var tick_node1 = Loop.NextTickNode{
151 .prev = undefined,
149152 .next = undefined,
150153 .data = handle1,
151154 };
......@@ -153,6 +156,7 @@ async fn testLock(loop: *Loop, lock: *Lock) void {
153156
154157 const handle2 = async lockRunner(lock) catch @panic("out of memory");
155158 var tick_node2 = Loop.NextTickNode{
159 .prev = undefined,
156160 .next = undefined,
157161 .data = handle2,
158162 };
......@@ -160,6 +164,7 @@ async fn testLock(loop: *Loop, lock: *Lock) void {
160164
161165 const handle3 = async lockRunner(lock) catch @panic("out of memory");
162166 var tick_node3 = Loop.NextTickNode{
167 .prev = undefined,
163168 .next = undefined,
164169 .data = handle3,
165170 };
std/event/loop.zig+337-99
......@@ -2,10 +2,12 @@ const std = @import("../index.zig");
22const builtin = @import("builtin");
33const assert = std.debug.assert;
44const mem = std.mem;
5const posix = std.os.posix;
6const windows = std.os.windows;
75const AtomicRmwOp = builtin.AtomicRmwOp;
86const AtomicOrder = builtin.AtomicOrder;
7const fs = std.event.fs;
8const os = std.os;
9const posix = os.posix;
10const windows = os.windows;
911
1012pub const Loop = struct {
1113 allocator: *mem.Allocator,
......@@ -13,7 +15,7 @@ pub const Loop = struct {
1315 os_data: OsData,
1416 final_resume_node: ResumeNode,
1517 pending_event_count: usize,
16 extra_threads: []*std.os.Thread,
18 extra_threads: []*os.Thread,
1719
1820 // pre-allocated eventfds. all permanently active.
1921 // this is how we send promises to be resumed on other threads.
......@@ -50,6 +52,22 @@ pub const Loop = struct {
5052 base: ResumeNode,
5153 kevent: posix.Kevent,
5254 };
55
56 pub const Basic = switch (builtin.os) {
57 builtin.Os.macosx => MacOsBasic,
58 builtin.Os.linux => struct {
59 base: ResumeNode,
60 },
61 builtin.Os.windows => struct {
62 base: ResumeNode,
63 },
64 else => @compileError("unsupported OS"),
65 };
66
67 const MacOsBasic = struct {
68 base: ResumeNode,
69 kev: posix.Kevent,
70 };
5371 };
5472
5573 /// After initialization, call run().
......@@ -65,7 +83,7 @@ pub const Loop = struct {
6583 /// TODO copy elision / named return values so that the threads referencing *Loop
6684 /// have the correct pointer value.
6785 pub fn initMultiThreaded(self: *Loop, allocator: *mem.Allocator) !void {
68 const core_count = try std.os.cpuCount(allocator);
86 const core_count = try os.cpuCount(allocator);
6987 return self.initInternal(allocator, core_count);
7088 }
7189
......@@ -92,7 +110,7 @@ pub const Loop = struct {
92110 );
93111 errdefer self.allocator.free(self.eventfd_resume_nodes);
94112
95 self.extra_threads = try self.allocator.alloc(*std.os.Thread, extra_thread_count);
113 self.extra_threads = try self.allocator.alloc(*os.Thread, extra_thread_count);
96114 errdefer self.allocator.free(self.extra_threads);
97115
98116 try self.initOsData(extra_thread_count);
......@@ -104,17 +122,30 @@ pub const Loop = struct {
104122 self.allocator.free(self.extra_threads);
105123 }
106124
107 const InitOsDataError = std.os.LinuxEpollCreateError || mem.Allocator.Error || std.os.LinuxEventFdError ||
108 std.os.SpawnThreadError || std.os.LinuxEpollCtlError || std.os.BsdKEventError ||
109 std.os.WindowsCreateIoCompletionPortError;
125 const InitOsDataError = os.LinuxEpollCreateError || mem.Allocator.Error || os.LinuxEventFdError ||
126 os.SpawnThreadError || os.LinuxEpollCtlError || os.BsdKEventError ||
127 os.WindowsCreateIoCompletionPortError;
110128
111129 const wakeup_bytes = []u8{0x1} ** 8;
112130
113131 fn initOsData(self: *Loop, extra_thread_count: usize) InitOsDataError!void {
114132 switch (builtin.os) {
115133 builtin.Os.linux => {
134 self.os_data.fs_queue = std.atomic.Queue(fs.Request).init();
135 self.os_data.fs_queue_item = 0;
136 // we need another thread for the file system because Linux does not have an async
137 // file system I/O API.
138 self.os_data.fs_end_request = fs.RequestNode{
139 .prev = undefined,
140 .next = undefined,
141 .data = fs.Request{
142 .msg = fs.Request.Msg.End,
143 .finish = fs.Request.Finish.NoAction,
144 },
145 };
146
116147 errdefer {
117 while (self.available_eventfd_resume_nodes.pop()) |node| std.os.close(node.data.eventfd);
148 while (self.available_eventfd_resume_nodes.pop()) |node| os.close(node.data.eventfd);
118149 }
119150 for (self.eventfd_resume_nodes) |*eventfd_node| {
120151 eventfd_node.* = std.atomic.Stack(ResumeNode.EventFd).Node{
......@@ -123,7 +154,7 @@ pub const Loop = struct {
123154 .id = ResumeNode.Id.EventFd,
124155 .handle = undefined,
125156 },
126 .eventfd = try std.os.linuxEventFd(1, posix.EFD_CLOEXEC | posix.EFD_NONBLOCK),
157 .eventfd = try os.linuxEventFd(1, posix.EFD_CLOEXEC | posix.EFD_NONBLOCK),
127158 .epoll_op = posix.EPOLL_CTL_ADD,
128159 },
129160 .next = undefined,
......@@ -131,44 +162,62 @@ pub const Loop = struct {
131162 self.available_eventfd_resume_nodes.push(eventfd_node);
132163 }
133164
134 self.os_data.epollfd = try std.os.linuxEpollCreate(posix.EPOLL_CLOEXEC);
135 errdefer std.os.close(self.os_data.epollfd);
165 self.os_data.epollfd = try os.linuxEpollCreate(posix.EPOLL_CLOEXEC);
166 errdefer os.close(self.os_data.epollfd);
136167
137 self.os_data.final_eventfd = try std.os.linuxEventFd(0, posix.EFD_CLOEXEC | posix.EFD_NONBLOCK);
138 errdefer std.os.close(self.os_data.final_eventfd);
168 self.os_data.final_eventfd = try os.linuxEventFd(0, posix.EFD_CLOEXEC | posix.EFD_NONBLOCK);
169 errdefer os.close(self.os_data.final_eventfd);
139170
140171 self.os_data.final_eventfd_event = posix.epoll_event{
141172 .events = posix.EPOLLIN,
142173 .data = posix.epoll_data{ .ptr = @ptrToInt(&self.final_resume_node) },
143174 };
144 try std.os.linuxEpollCtl(
175 try os.linuxEpollCtl(
145176 self.os_data.epollfd,
146177 posix.EPOLL_CTL_ADD,
147178 self.os_data.final_eventfd,
148179 &self.os_data.final_eventfd_event,
149180 );
150181
182 self.os_data.fs_thread = try os.spawnThread(self, posixFsRun);
183 errdefer {
184 self.posixFsRequest(&self.os_data.fs_end_request);
185 self.os_data.fs_thread.wait();
186 }
187
151188 var extra_thread_index: usize = 0;
152189 errdefer {
153190 // writing 8 bytes to an eventfd cannot fail
154 std.os.posixWrite(self.os_data.final_eventfd, wakeup_bytes) catch unreachable;
191 os.posixWrite(self.os_data.final_eventfd, wakeup_bytes) catch unreachable;
155192 while (extra_thread_index != 0) {
156193 extra_thread_index -= 1;
157194 self.extra_threads[extra_thread_index].wait();
158195 }
159196 }
160197 while (extra_thread_index < extra_thread_count) : (extra_thread_index += 1) {
161 self.extra_threads[extra_thread_index] = try std.os.spawnThread(self, workerRun);
198 self.extra_threads[extra_thread_index] = try os.spawnThread(self, workerRun);
162199 }
163200 },
164201 builtin.Os.macosx => {
165 self.os_data.kqfd = try std.os.bsdKQueue();
166 errdefer std.os.close(self.os_data.kqfd);
167
168 self.os_data.kevents = try self.allocator.alloc(posix.Kevent, extra_thread_count);
169 errdefer self.allocator.free(self.os_data.kevents);
202 self.os_data.kqfd = try os.bsdKQueue();
203 errdefer os.close(self.os_data.kqfd);
204
205 self.os_data.fs_kqfd = try os.bsdKQueue();
206 errdefer os.close(self.os_data.fs_kqfd);
207
208 self.os_data.fs_queue = std.atomic.Queue(fs.Request).init();
209 // we need another thread for the file system because Darwin does not have an async
210 // file system I/O API.
211 self.os_data.fs_end_request = fs.RequestNode{
212 .prev = undefined,
213 .next = undefined,
214 .data = fs.Request{
215 .msg = fs.Request.Msg.End,
216 .finish = fs.Request.Finish.NoAction,
217 },
218 };
170219
171 const eventlist = ([*]posix.Kevent)(undefined)[0..0];
220 const empty_kevs = ([*]posix.Kevent)(undefined)[0..0];
172221
173222 for (self.eventfd_resume_nodes) |*eventfd_node, i| {
174223 eventfd_node.* = std.atomic.Stack(ResumeNode.EventFd).Node{
......@@ -191,18 +240,9 @@ pub const Loop = struct {
191240 };
192241 self.available_eventfd_resume_nodes.push(eventfd_node);
193242 const kevent_array = (*[1]posix.Kevent)(&eventfd_node.data.kevent);
194 _ = try std.os.bsdKEvent(self.os_data.kqfd, kevent_array, eventlist, null);
243 _ = try os.bsdKEvent(self.os_data.kqfd, kevent_array, empty_kevs, null);
195244 eventfd_node.data.kevent.flags = posix.EV_CLEAR | posix.EV_ENABLE;
196245 eventfd_node.data.kevent.fflags = posix.NOTE_TRIGGER;
197 // this one is for waiting for events
198 self.os_data.kevents[i] = posix.Kevent{
199 .ident = i,
200 .filter = posix.EVFILT_USER,
201 .flags = 0,
202 .fflags = 0,
203 .data = 0,
204 .udata = @ptrToInt(&eventfd_node.data.base),
205 };
206246 }
207247
208248 // Pre-add so that we cannot get error.SystemResources
......@@ -215,31 +255,55 @@ pub const Loop = struct {
215255 .data = 0,
216256 .udata = @ptrToInt(&self.final_resume_node),
217257 };
218 const kevent_array = (*[1]posix.Kevent)(&self.os_data.final_kevent);
219 _ = try std.os.bsdKEvent(self.os_data.kqfd, kevent_array, eventlist, null);
258 const final_kev_arr = (*[1]posix.Kevent)(&self.os_data.final_kevent);
259 _ = try os.bsdKEvent(self.os_data.kqfd, final_kev_arr, empty_kevs, null);
220260 self.os_data.final_kevent.flags = posix.EV_ENABLE;
221261 self.os_data.final_kevent.fflags = posix.NOTE_TRIGGER;
222262
263 self.os_data.fs_kevent_wake = posix.Kevent{
264 .ident = 0,
265 .filter = posix.EVFILT_USER,
266 .flags = posix.EV_ADD | posix.EV_ENABLE,
267 .fflags = posix.NOTE_TRIGGER,
268 .data = 0,
269 .udata = undefined,
270 };
271
272 self.os_data.fs_kevent_wait = posix.Kevent{
273 .ident = 0,
274 .filter = posix.EVFILT_USER,
275 .flags = posix.EV_ADD | posix.EV_CLEAR,
276 .fflags = 0,
277 .data = 0,
278 .udata = undefined,
279 };
280
281 self.os_data.fs_thread = try os.spawnThread(self, posixFsRun);
282 errdefer {
283 self.posixFsRequest(&self.os_data.fs_end_request);
284 self.os_data.fs_thread.wait();
285 }
286
223287 var extra_thread_index: usize = 0;
224288 errdefer {
225 _ = std.os.bsdKEvent(self.os_data.kqfd, kevent_array, eventlist, null) catch unreachable;
289 _ = os.bsdKEvent(self.os_data.kqfd, final_kev_arr, empty_kevs, null) catch unreachable;
226290 while (extra_thread_index != 0) {
227291 extra_thread_index -= 1;
228292 self.extra_threads[extra_thread_index].wait();
229293 }
230294 }
231295 while (extra_thread_index < extra_thread_count) : (extra_thread_index += 1) {
232 self.extra_threads[extra_thread_index] = try std.os.spawnThread(self, workerRun);
296 self.extra_threads[extra_thread_index] = try os.spawnThread(self, workerRun);
233297 }
234298 },
235299 builtin.Os.windows => {
236 self.os_data.io_port = try std.os.windowsCreateIoCompletionPort(
300 self.os_data.io_port = try os.windowsCreateIoCompletionPort(
237301 windows.INVALID_HANDLE_VALUE,
238302 null,
239303 undefined,
240 undefined,
304 @maxValue(windows.DWORD),
241305 );
242 errdefer std.os.close(self.os_data.io_port);
306 errdefer os.close(self.os_data.io_port);
243307
244308 for (self.eventfd_resume_nodes) |*eventfd_node, i| {
245309 eventfd_node.* = std.atomic.Stack(ResumeNode.EventFd).Node{
......@@ -262,7 +326,7 @@ pub const Loop = struct {
262326 while (i < extra_thread_index) : (i += 1) {
263327 while (true) {
264328 const overlapped = @intToPtr(?*windows.OVERLAPPED, 0x1);
265 std.os.windowsPostQueuedCompletionStatus(self.os_data.io_port, undefined, @ptrToInt(&self.final_resume_node), overlapped) catch continue;
329 os.windowsPostQueuedCompletionStatus(self.os_data.io_port, undefined, @ptrToInt(&self.final_resume_node), overlapped) catch continue;
266330 break;
267331 }
268332 }
......@@ -272,7 +336,7 @@ pub const Loop = struct {
272336 }
273337 }
274338 while (extra_thread_index < extra_thread_count) : (extra_thread_index += 1) {
275 self.extra_threads[extra_thread_index] = try std.os.spawnThread(self, workerRun);
339 self.extra_threads[extra_thread_index] = try os.spawnThread(self, workerRun);
276340 }
277341 },
278342 else => {},
......@@ -282,63 +346,113 @@ pub const Loop = struct {
282346 fn deinitOsData(self: *Loop) void {
283347 switch (builtin.os) {
284348 builtin.Os.linux => {
285 std.os.close(self.os_data.final_eventfd);
286 while (self.available_eventfd_resume_nodes.pop()) |node| std.os.close(node.data.eventfd);
287 std.os.close(self.os_data.epollfd);
349 os.close(self.os_data.final_eventfd);
350 while (self.available_eventfd_resume_nodes.pop()) |node| os.close(node.data.eventfd);
351 os.close(self.os_data.epollfd);
288352 self.allocator.free(self.eventfd_resume_nodes);
289353 },
290354 builtin.Os.macosx => {
291 self.allocator.free(self.os_data.kevents);
292 std.os.close(self.os_data.kqfd);
355 os.close(self.os_data.kqfd);
356 os.close(self.os_data.fs_kqfd);
293357 },
294358 builtin.Os.windows => {
295 std.os.close(self.os_data.io_port);
359 os.close(self.os_data.io_port);
296360 },
297361 else => {},
298362 }
299363 }
300364
301365 /// resume_node must live longer than the promise that it holds a reference to.
302 pub fn addFd(self: *Loop, fd: i32, resume_node: *ResumeNode) !void {
303 _ = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst);
304 errdefer {
305 self.finishOneEvent();
306 }
307 try self.modFd(
366 /// flags must contain EPOLLET
367 pub fn linuxAddFd(self: *Loop, fd: i32, resume_node: *ResumeNode, flags: u32) !void {
368 assert(flags & posix.EPOLLET == posix.EPOLLET);
369 self.beginOneEvent();
370 errdefer self.finishOneEvent();
371 try self.linuxModFd(
308372 fd,
309373 posix.EPOLL_CTL_ADD,
310 std.os.linux.EPOLLIN | std.os.linux.EPOLLOUT | std.os.linux.EPOLLET,
374 flags,
311375 resume_node,
312376 );
313377 }
314378
315 pub fn modFd(self: *Loop, fd: i32, op: u32, events: u32, resume_node: *ResumeNode) !void {
316 var ev = std.os.linux.epoll_event{
317 .events = events,
318 .data = std.os.linux.epoll_data{ .ptr = @ptrToInt(resume_node) },
379 pub fn linuxModFd(self: *Loop, fd: i32, op: u32, flags: u32, resume_node: *ResumeNode) !void {
380 assert(flags & posix.EPOLLET == posix.EPOLLET);
381 var ev = os.linux.epoll_event{
382 .events = flags,
383 .data = os.linux.epoll_data{ .ptr = @ptrToInt(resume_node) },
319384 };
320 try std.os.linuxEpollCtl(self.os_data.epollfd, op, fd, &ev);
385 try os.linuxEpollCtl(self.os_data.epollfd, op, fd, &ev);
321386 }
322387
323 pub fn removeFd(self: *Loop, fd: i32) void {
324 self.removeFdNoCounter(fd);
388 pub fn linuxRemoveFd(self: *Loop, fd: i32) void {
389 os.linuxEpollCtl(self.os_data.epollfd, os.linux.EPOLL_CTL_DEL, fd, undefined) catch {};
325390 self.finishOneEvent();
326391 }
327392
328 fn removeFdNoCounter(self: *Loop, fd: i32) void {
329 std.os.linuxEpollCtl(self.os_data.epollfd, std.os.linux.EPOLL_CTL_DEL, fd, undefined) catch {};
393 pub async fn linuxWaitFd(self: *Loop, fd: i32, flags: u32) !void {
394 defer self.linuxRemoveFd(fd);
395 suspend {
396 // TODO explicitly put this memory in the coroutine frame #1194
397 var resume_node = ResumeNode.Basic{
398 .base = ResumeNode{
399 .id = ResumeNode.Id.Basic,
400 .handle = @handle(),
401 },
402 };
403 try self.linuxAddFd(fd, &resume_node.base, flags);
404 }
330405 }
331406
332 pub async fn waitFd(self: *Loop, fd: i32) !void {
333 defer self.removeFd(fd);
407 pub async fn bsdWaitKev(self: *Loop, ident: usize, filter: i16, fflags: u32) !posix.Kevent {
408 // TODO #1194
334409 suspend {
335 // TODO explicitly put this memory in the coroutine frame #1194
336 var resume_node = ResumeNode{
410 resume @handle();
411 }
412 var resume_node = ResumeNode.Basic{
413 .base = ResumeNode{
337414 .id = ResumeNode.Id.Basic,
338415 .handle = @handle(),
339 };
340 try self.addFd(fd, &resume_node);
416 },
417 .kev = undefined,
418 };
419 defer self.bsdRemoveKev(ident, filter);
420 suspend {
421 try self.bsdAddKev(&resume_node, ident, filter, fflags);
341422 }
423 return resume_node.kev;
424 }
425
426 /// resume_node must live longer than the promise that it holds a reference to.
427 pub fn bsdAddKev(self: *Loop, resume_node: *ResumeNode.Basic, ident: usize, filter: i16, fflags: u32) !void {
428 self.beginOneEvent();
429 errdefer self.finishOneEvent();
430 var kev = posix.Kevent{
431 .ident = ident,
432 .filter = filter,
433 .flags = posix.EV_ADD | posix.EV_ENABLE | posix.EV_CLEAR,
434 .fflags = fflags,
435 .data = 0,
436 .udata = @ptrToInt(&resume_node.base),
437 };
438 const kevent_array = (*[1]posix.Kevent)(&kev);
439 const empty_kevs = ([*]posix.Kevent)(undefined)[0..0];
440 _ = try os.bsdKEvent(self.os_data.kqfd, kevent_array, empty_kevs, null);
441 }
442
443 pub fn bsdRemoveKev(self: *Loop, ident: usize, filter: i16) void {
444 var kev = posix.Kevent{
445 .ident = ident,
446 .filter = filter,
447 .flags = posix.EV_DELETE,
448 .fflags = 0,
449 .data = 0,
450 .udata = 0,
451 };
452 const kevent_array = (*[1]posix.Kevent)(&kev);
453 const empty_kevs = ([*]posix.Kevent)(undefined)[0..0];
454 _ = os.bsdKEvent(self.os_data.kqfd, kevent_array, empty_kevs, null) catch undefined;
455 self.finishOneEvent();
342456 }
343457
344458 fn dispatch(self: *Loop) void {
......@@ -352,8 +466,8 @@ pub const Loop = struct {
352466 switch (builtin.os) {
353467 builtin.Os.macosx => {
354468 const kevent_array = (*[1]posix.Kevent)(&eventfd_node.kevent);
355 const eventlist = ([*]posix.Kevent)(undefined)[0..0];
356 _ = std.os.bsdKEvent(self.os_data.kqfd, kevent_array, eventlist, null) catch {
469 const empty_kevs = ([*]posix.Kevent)(undefined)[0..0];
470 _ = os.bsdKEvent(self.os_data.kqfd, kevent_array, empty_kevs, null) catch {
357471 self.next_tick_queue.unget(next_tick_node);
358472 self.available_eventfd_resume_nodes.push(resume_stack_node);
359473 return;
......@@ -361,9 +475,9 @@ pub const Loop = struct {
361475 },
362476 builtin.Os.linux => {
363477 // the pending count is already accounted for
364 const epoll_events = posix.EPOLLONESHOT | std.os.linux.EPOLLIN | std.os.linux.EPOLLOUT |
365 std.os.linux.EPOLLET;
366 self.modFd(
478 const epoll_events = posix.EPOLLONESHOT | os.linux.EPOLLIN | os.linux.EPOLLOUT |
479 os.linux.EPOLLET;
480 self.linuxModFd(
367481 eventfd_node.eventfd,
368482 eventfd_node.epoll_op,
369483 epoll_events,
......@@ -379,7 +493,7 @@ pub const Loop = struct {
379493 // the consumer code can decide whether to read the completion key.
380494 // it has to do this for normal I/O, so we match that behavior here.
381495 const overlapped = @intToPtr(?*windows.OVERLAPPED, 0x1);
382 std.os.windowsPostQueuedCompletionStatus(
496 os.windowsPostQueuedCompletionStatus(
383497 self.os_data.io_port,
384498 undefined,
385499 eventfd_node.completion_key,
......@@ -397,15 +511,29 @@ pub const Loop = struct {
397511
398512 /// Bring your own linked list node. This means it can't fail.
399513 pub fn onNextTick(self: *Loop, node: *NextTickNode) void {
400 _ = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst);
514 self.beginOneEvent(); // finished in dispatch()
401515 self.next_tick_queue.put(node);
402516 self.dispatch();
403517 }
404518
519 pub fn cancelOnNextTick(self: *Loop, node: *NextTickNode) void {
520 if (self.next_tick_queue.remove(node)) {
521 self.finishOneEvent();
522 }
523 }
524
405525 pub fn run(self: *Loop) void {
406526 self.finishOneEvent(); // the reference we start with
407527
408528 self.workerRun();
529
530 switch (builtin.os) {
531 builtin.Os.linux,
532 builtin.Os.macosx,
533 => self.os_data.fs_thread.wait(),
534 else => {},
535 }
536
409537 for (self.extra_threads) |extra_thread| {
410538 extra_thread.wait();
411539 }
......@@ -420,6 +548,7 @@ pub const Loop = struct {
420548 suspend {
421549 handle.* = @handle();
422550 var my_tick_node = Loop.NextTickNode{
551 .prev = undefined,
423552 .next = undefined,
424553 .data = @handle(),
425554 };
......@@ -441,6 +570,7 @@ pub const Loop = struct {
441570 pub async fn yield(self: *Loop) void {
442571 suspend {
443572 var my_tick_node = Loop.NextTickNode{
573 .prev = undefined,
444574 .next = undefined,
445575 .data = @handle(),
446576 };
......@@ -448,20 +578,28 @@ pub const Loop = struct {
448578 }
449579 }
450580
451 fn finishOneEvent(self: *Loop) void {
452 if (@atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst) == 1) {
581 /// call finishOneEvent when done
582 pub fn beginOneEvent(self: *Loop) void {
583 _ = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst);
584 }
585
586 pub fn finishOneEvent(self: *Loop) void {
587 const prev = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst);
588 if (prev == 1) {
453589 // cause all the threads to stop
454590 switch (builtin.os) {
455591 builtin.Os.linux => {
592 self.posixFsRequest(&self.os_data.fs_end_request);
456593 // writing 8 bytes to an eventfd cannot fail
457 std.os.posixWrite(self.os_data.final_eventfd, wakeup_bytes) catch unreachable;
594 os.posixWrite(self.os_data.final_eventfd, wakeup_bytes) catch unreachable;
458595 return;
459596 },
460597 builtin.Os.macosx => {
598 self.posixFsRequest(&self.os_data.fs_end_request);
461599 const final_kevent = (*[1]posix.Kevent)(&self.os_data.final_kevent);
462 const eventlist = ([*]posix.Kevent)(undefined)[0..0];
600 const empty_kevs = ([*]posix.Kevent)(undefined)[0..0];
463601 // cannot fail because we already added it and this just enables it
464 _ = std.os.bsdKEvent(self.os_data.kqfd, final_kevent, eventlist, null) catch unreachable;
602 _ = os.bsdKEvent(self.os_data.kqfd, final_kevent, empty_kevs, null) catch unreachable;
465603 return;
466604 },
467605 builtin.Os.windows => {
......@@ -469,7 +607,7 @@ pub const Loop = struct {
469607 while (i < self.extra_threads.len + 1) : (i += 1) {
470608 while (true) {
471609 const overlapped = @intToPtr(?*windows.OVERLAPPED, 0x1);
472 std.os.windowsPostQueuedCompletionStatus(self.os_data.io_port, undefined, @ptrToInt(&self.final_resume_node), overlapped) catch continue;
610 os.windowsPostQueuedCompletionStatus(self.os_data.io_port, undefined, @ptrToInt(&self.final_resume_node), overlapped) catch continue;
473611 break;
474612 }
475613 }
......@@ -492,8 +630,8 @@ pub const Loop = struct {
492630 switch (builtin.os) {
493631 builtin.Os.linux => {
494632 // only process 1 event so we don't steal from other threads
495 var events: [1]std.os.linux.epoll_event = undefined;
496 const count = std.os.linuxEpollWait(self.os_data.epollfd, events[0..], -1);
633 var events: [1]os.linux.epoll_event = undefined;
634 const count = os.linuxEpollWait(self.os_data.epollfd, events[0..], -1);
497635 for (events[0..count]) |ev| {
498636 const resume_node = @intToPtr(*ResumeNode, ev.data.ptr);
499637 const handle = resume_node.handle;
......@@ -516,13 +654,17 @@ pub const Loop = struct {
516654 },
517655 builtin.Os.macosx => {
518656 var eventlist: [1]posix.Kevent = undefined;
519 const count = std.os.bsdKEvent(self.os_data.kqfd, self.os_data.kevents, eventlist[0..], null) catch unreachable;
657 const empty_kevs = ([*]posix.Kevent)(undefined)[0..0];
658 const count = os.bsdKEvent(self.os_data.kqfd, empty_kevs, eventlist[0..], null) catch unreachable;
520659 for (eventlist[0..count]) |ev| {
521660 const resume_node = @intToPtr(*ResumeNode, ev.udata);
522661 const handle = resume_node.handle;
523662 const resume_node_id = resume_node.id;
524663 switch (resume_node_id) {
525 ResumeNode.Id.Basic => {},
664 ResumeNode.Id.Basic => {
665 const basic_node = @fieldParentPtr(ResumeNode.Basic, "base", resume_node);
666 basic_node.kev = ev;
667 },
526668 ResumeNode.Id.Stop => return,
527669 ResumeNode.Id.EventFd => {
528670 const event_fd_node = @fieldParentPtr(ResumeNode.EventFd, "base", resume_node);
......@@ -541,9 +683,10 @@ pub const Loop = struct {
541683 while (true) {
542684 var nbytes: windows.DWORD = undefined;
543685 var overlapped: ?*windows.OVERLAPPED = undefined;
544 switch (std.os.windowsGetQueuedCompletionStatus(self.os_data.io_port, &nbytes, &completion_key, &overlapped, windows.INFINITE)) {
545 std.os.WindowsWaitResult.Aborted => return,
546 std.os.WindowsWaitResult.Normal => {},
686 switch (os.windowsGetQueuedCompletionStatus(self.os_data.io_port, &nbytes, &completion_key, &overlapped, windows.INFINITE)) {
687 os.WindowsWaitResult.Aborted => return,
688 os.WindowsWaitResult.Normal => {},
689 os.WindowsWaitResult.Cancelled => continue,
547690 }
548691 if (overlapped != null) break;
549692 }
......@@ -560,21 +703,101 @@ pub const Loop = struct {
560703 },
561704 }
562705 resume handle;
563 if (resume_node_id == ResumeNode.Id.EventFd) {
564 self.finishOneEvent();
565 }
706 self.finishOneEvent();
566707 },
567708 else => @compileError("unsupported OS"),
568709 }
569710 }
570711 }
571712
713 fn posixFsRequest(self: *Loop, request_node: *fs.RequestNode) void {
714 self.beginOneEvent(); // finished in posixFsRun after processing the msg
715 self.os_data.fs_queue.put(request_node);
716 switch (builtin.os) {
717 builtin.Os.macosx => {
718 const fs_kevs = (*[1]posix.Kevent)(&self.os_data.fs_kevent_wake);
719 const empty_kevs = ([*]posix.Kevent)(undefined)[0..0];
720 _ = os.bsdKEvent(self.os_data.fs_kqfd, fs_kevs, empty_kevs, null) catch unreachable;
721 },
722 builtin.Os.linux => {
723 _ = @atomicRmw(u8, &self.os_data.fs_queue_item, AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst);
724 const rc = os.linux.futex_wake(@ptrToInt(&self.os_data.fs_queue_item), os.linux.FUTEX_WAKE, 1);
725 switch (os.linux.getErrno(rc)) {
726 0 => {},
727 posix.EINVAL => unreachable,
728 else => unreachable,
729 }
730 },
731 else => @compileError("Unsupported OS"),
732 }
733 }
734
735 fn posixFsCancel(self: *Loop, request_node: *fs.RequestNode) void {
736 if (self.os_data.fs_queue.remove(request_node)) {
737 self.finishOneEvent();
738 }
739 }
740
741 fn posixFsRun(self: *Loop) void {
742 while (true) {
743 if (builtin.os == builtin.Os.linux) {
744 _ = @atomicRmw(u8, &self.os_data.fs_queue_item, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst);
745 }
746 while (self.os_data.fs_queue.get()) |node| {
747 switch (node.data.msg) {
748 @TagType(fs.Request.Msg).End => return,
749 @TagType(fs.Request.Msg).PWriteV => |*msg| {
750 msg.result = os.posix_pwritev(msg.fd, msg.iov.ptr, msg.iov.len, msg.offset);
751 },
752 @TagType(fs.Request.Msg).PReadV => |*msg| {
753 msg.result = os.posix_preadv(msg.fd, msg.iov.ptr, msg.iov.len, msg.offset);
754 },
755 @TagType(fs.Request.Msg).Open => |*msg| {
756 msg.result = os.posixOpenC(msg.path.ptr, msg.flags, msg.mode);
757 },
758 @TagType(fs.Request.Msg).Close => |*msg| os.close(msg.fd),
759 @TagType(fs.Request.Msg).WriteFile => |*msg| blk: {
760 const flags = posix.O_LARGEFILE | posix.O_WRONLY | posix.O_CREAT |
761 posix.O_CLOEXEC | posix.O_TRUNC;
762 const fd = os.posixOpenC(msg.path.ptr, flags, msg.mode) catch |err| {
763 msg.result = err;
764 break :blk;
765 };
766 defer os.close(fd);
767 msg.result = os.posixWrite(fd, msg.contents);
768 },
769 }
770 switch (node.data.finish) {
771 @TagType(fs.Request.Finish).TickNode => |*tick_node| self.onNextTick(tick_node),
772 @TagType(fs.Request.Finish).DeallocCloseOperation => |close_op| {
773 self.allocator.destroy(close_op);
774 },
775 @TagType(fs.Request.Finish).NoAction => {},
776 }
777 self.finishOneEvent();
778 }
779 switch (builtin.os) {
780 builtin.Os.linux => {
781 const rc = os.linux.futex_wait(@ptrToInt(&self.os_data.fs_queue_item), os.linux.FUTEX_WAIT, 0, null);
782 switch (os.linux.getErrno(rc)) {
783 0 => continue,
784 posix.EINTR => continue,
785 posix.EAGAIN => continue,
786 else => unreachable,
787 }
788 },
789 builtin.Os.macosx => {
790 const fs_kevs = (*[1]posix.Kevent)(&self.os_data.fs_kevent_wait);
791 var out_kevs: [1]posix.Kevent = undefined;
792 _ = os.bsdKEvent(self.os_data.fs_kqfd, fs_kevs, out_kevs[0..], null) catch unreachable;
793 },
794 else => @compileError("Unsupported OS"),
795 }
796 }
797 }
798
572799 const OsData = switch (builtin.os) {
573 builtin.Os.linux => struct {
574 epollfd: i32,
575 final_eventfd: i32,
576 final_eventfd_event: std.os.linux.epoll_event,
577 },
800 builtin.Os.linux => LinuxOsData,
578801 builtin.Os.macosx => MacOsData,
579802 builtin.Os.windows => struct {
580803 io_port: windows.HANDLE,
......@@ -586,7 +809,22 @@ pub const Loop = struct {
586809 const MacOsData = struct {
587810 kqfd: i32,
588811 final_kevent: posix.Kevent,
589 kevents: []posix.Kevent,
812 fs_kevent_wake: posix.Kevent,
813 fs_kevent_wait: posix.Kevent,
814 fs_thread: *os.Thread,
815 fs_kqfd: i32,
816 fs_queue: std.atomic.Queue(fs.Request),
817 fs_end_request: fs.RequestNode,
818 };
819
820 const LinuxOsData = struct {
821 epollfd: i32,
822 final_eventfd: i32,
823 final_eventfd_event: os.linux.epoll_event,
824 fs_thread: *os.Thread,
825 fs_queue_item: u8,
826 fs_queue: std.atomic.Queue(fs.Request),
827 fs_end_request: fs.RequestNode,
590828 };
591829};
592830
std/event/rwlock.zig created+296
......@@ -0,0 +1,296 @@
1const std = @import("../index.zig");
2const builtin = @import("builtin");
3const assert = std.debug.assert;
4const mem = std.mem;
5const AtomicRmwOp = builtin.AtomicRmwOp;
6const AtomicOrder = builtin.AtomicOrder;
7const Loop = std.event.Loop;
8
9/// Thread-safe async/await lock.
10/// Does not make any syscalls - coroutines which are waiting for the lock are suspended, and
11/// are resumed when the lock is released, in order.
12/// Many readers can hold the lock at the same time; however locking for writing is exclusive.
13/// When a read lock is held, it will not be released until the reader queue is empty.
14/// When a write lock is held, it will not be released until the writer queue is empty.
15pub const RwLock = struct {
16 loop: *Loop,
17 shared_state: u8, // TODO make this an enum
18 writer_queue: Queue,
19 reader_queue: Queue,
20 writer_queue_empty_bit: u8, // TODO make this a bool
21 reader_queue_empty_bit: u8, // TODO make this a bool
22 reader_lock_count: usize,
23
24 const State = struct {
25 const Unlocked = 0;
26 const WriteLock = 1;
27 const ReadLock = 2;
28 };
29
30 const Queue = std.atomic.Queue(promise);
31
32 pub const HeldRead = struct {
33 lock: *RwLock,
34
35 pub fn release(self: HeldRead) void {
36 // If other readers still hold the lock, we're done.
37 if (@atomicRmw(usize, &self.lock.reader_lock_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst) != 1) {
38 return;
39 }
40
41 _ = @atomicRmw(u8, &self.lock.reader_queue_empty_bit, AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst);
42 if (@cmpxchgStrong(u8, &self.lock.shared_state, State.ReadLock, State.Unlocked, AtomicOrder.SeqCst, AtomicOrder.SeqCst) != null) {
43 // Didn't unlock. Someone else's problem.
44 return;
45 }
46
47 self.lock.commonPostUnlock();
48 }
49 };
50
51 pub const HeldWrite = struct {
52 lock: *RwLock,
53
54 pub fn release(self: HeldWrite) void {
55 // See if we can leave it locked for writing, and pass the lock to the next writer
56 // in the queue to grab the lock.
57 if (self.lock.writer_queue.get()) |node| {
58 self.lock.loop.onNextTick(node);
59 return;
60 }
61
62 // We need to release the write lock. Check if any readers are waiting to grab the lock.
63 if (@atomicLoad(u8, &self.lock.reader_queue_empty_bit, AtomicOrder.SeqCst) == 0) {
64 // Switch to a read lock.
65 _ = @atomicRmw(u8, &self.lock.shared_state, AtomicRmwOp.Xchg, State.ReadLock, AtomicOrder.SeqCst);
66 while (self.lock.reader_queue.get()) |node| {
67 self.lock.loop.onNextTick(node);
68 }
69 return;
70 }
71
72 _ = @atomicRmw(u8, &self.lock.writer_queue_empty_bit, AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst);
73 _ = @atomicRmw(u8, &self.lock.shared_state, AtomicRmwOp.Xchg, State.Unlocked, AtomicOrder.SeqCst);
74
75 self.lock.commonPostUnlock();
76 }
77 };
78
79 pub fn init(loop: *Loop) RwLock {
80 return RwLock{
81 .loop = loop,
82 .shared_state = State.Unlocked,
83 .writer_queue = Queue.init(),
84 .writer_queue_empty_bit = 1,
85 .reader_queue = Queue.init(),
86 .reader_queue_empty_bit = 1,
87 .reader_lock_count = 0,
88 };
89 }
90
91 /// Must be called when not locked. Not thread safe.
92 /// All calls to acquire() and release() must complete before calling deinit().
93 pub fn deinit(self: *RwLock) void {
94 assert(self.shared_state == State.Unlocked);
95 while (self.writer_queue.get()) |node| cancel node.data;
96 while (self.reader_queue.get()) |node| cancel node.data;
97 }
98
99 pub async fn acquireRead(self: *RwLock) HeldRead {
100 _ = @atomicRmw(usize, &self.reader_lock_count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst);
101
102 suspend {
103 // TODO explicitly put this memory in the coroutine frame #1194
104 var my_tick_node = Loop.NextTickNode{
105 .data = @handle(),
106 .prev = undefined,
107 .next = undefined,
108 };
109
110 self.reader_queue.put(&my_tick_node);
111
112 // At this point, we are in the reader_queue, so we might have already been resumed and this coroutine
113 // frame might be destroyed. For the rest of the suspend block we cannot access the coroutine frame.
114
115 // We set this bit so that later we can rely on the fact, that if reader_queue_empty_bit is 1,
116 // some actor will attempt to grab the lock.
117 _ = @atomicRmw(u8, &self.reader_queue_empty_bit, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst);
118
119 // Here we don't care if we are the one to do the locking or if it was already locked for reading.
120 const have_read_lock = if (@cmpxchgStrong(u8, &self.shared_state, State.Unlocked, State.ReadLock, AtomicOrder.SeqCst, AtomicOrder.SeqCst)) |old_state| old_state == State.ReadLock else true;
121 if (have_read_lock) {
122 // Give out all the read locks.
123 if (self.reader_queue.get()) |first_node| {
124 while (self.reader_queue.get()) |node| {
125 self.loop.onNextTick(node);
126 }
127 resume first_node.data;
128 }
129 }
130 }
131 return HeldRead{ .lock = self };
132 }
133
134 pub async fn acquireWrite(self: *RwLock) HeldWrite {
135 suspend {
136 // TODO explicitly put this memory in the coroutine frame #1194
137 var my_tick_node = Loop.NextTickNode{
138 .data = @handle(),
139 .prev = undefined,
140 .next = undefined,
141 };
142
143 self.writer_queue.put(&my_tick_node);
144
145 // At this point, we are in the writer_queue, so we might have already been resumed and this coroutine
146 // frame might be destroyed. For the rest of the suspend block we cannot access the coroutine frame.
147
148 // We set this bit so that later we can rely on the fact, that if writer_queue_empty_bit is 1,
149 // some actor will attempt to grab the lock.
150 _ = @atomicRmw(u8, &self.writer_queue_empty_bit, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst);
151
152 // Here we must be the one to acquire the write lock. It cannot already be locked.
153 if (@cmpxchgStrong(u8, &self.shared_state, State.Unlocked, State.WriteLock, AtomicOrder.SeqCst, AtomicOrder.SeqCst) == null) {
154 // We now have a write lock.
155 if (self.writer_queue.get()) |node| {
156 // Whether this node is us or someone else, we tail resume it.
157 resume node.data;
158 }
159 }
160 }
161 return HeldWrite{ .lock = self };
162 }
163
164 fn commonPostUnlock(self: *RwLock) void {
165 while (true) {
166 // There might be a writer_queue item or a reader_queue item
167 // If we check and both are empty, we can be done, because the other actors will try to
168 // obtain the lock.
169 // But if there's a writer_queue item or a reader_queue item,
170 // we are the actor which must loop and attempt to grab the lock again.
171 if (@atomicLoad(u8, &self.writer_queue_empty_bit, AtomicOrder.SeqCst) == 0) {
172 if (@cmpxchgStrong(u8, &self.shared_state, State.Unlocked, State.WriteLock, AtomicOrder.SeqCst, AtomicOrder.SeqCst) != null) {
173 // We did not obtain the lock. Great, the queues are someone else's problem.
174 return;
175 }
176 // If there's an item in the writer queue, give them the lock, and we're done.
177 if (self.writer_queue.get()) |node| {
178 self.loop.onNextTick(node);
179 return;
180 }
181 // Release the lock again.
182 _ = @atomicRmw(u8, &self.writer_queue_empty_bit, AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst);
183 _ = @atomicRmw(u8, &self.shared_state, AtomicRmwOp.Xchg, State.Unlocked, AtomicOrder.SeqCst);
184 continue;
185 }
186
187 if (@atomicLoad(u8, &self.reader_queue_empty_bit, AtomicOrder.SeqCst) == 0) {
188 if (@cmpxchgStrong(u8, &self.shared_state, State.Unlocked, State.ReadLock, AtomicOrder.SeqCst, AtomicOrder.SeqCst) != null) {
189 // We did not obtain the lock. Great, the queues are someone else's problem.
190 return;
191 }
192 // If there are any items in the reader queue, give out all the reader locks, and we're done.
193 if (self.reader_queue.get()) |first_node| {
194 self.loop.onNextTick(first_node);
195 while (self.reader_queue.get()) |node| {
196 self.loop.onNextTick(node);
197 }
198 return;
199 }
200 // Release the lock again.
201 _ = @atomicRmw(u8, &self.reader_queue_empty_bit, AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst);
202 if (@cmpxchgStrong(u8, &self.shared_state, State.ReadLock, State.Unlocked, AtomicOrder.SeqCst, AtomicOrder.SeqCst) != null) {
203 // Didn't unlock. Someone else's problem.
204 return;
205 }
206 continue;
207 }
208 return;
209 }
210 }
211};
212
213test "std.event.RwLock" {
214 var da = std.heap.DirectAllocator.init();
215 defer da.deinit();
216
217 const allocator = &da.allocator;
218
219 var loop: Loop = undefined;
220 try loop.initMultiThreaded(allocator);
221 defer loop.deinit();
222
223 var lock = RwLock.init(&loop);
224 defer lock.deinit();
225
226 const handle = try async<allocator> testLock(&loop, &lock);
227 defer cancel handle;
228 loop.run();
229
230 const expected_result = [1]i32{shared_it_count * @intCast(i32, shared_test_data.len)} ** shared_test_data.len;
231 assert(mem.eql(i32, shared_test_data, expected_result));
232}
233
234async fn testLock(loop: *Loop, lock: *RwLock) void {
235 // TODO explicitly put next tick node memory in the coroutine frame #1194
236 suspend {
237 resume @handle();
238 }
239
240 var read_nodes: [100]Loop.NextTickNode = undefined;
241 for (read_nodes) |*read_node| {
242 read_node.data = async readRunner(lock) catch @panic("out of memory");
243 loop.onNextTick(read_node);
244 }
245
246 var write_nodes: [shared_it_count]Loop.NextTickNode = undefined;
247 for (write_nodes) |*write_node| {
248 write_node.data = async writeRunner(lock) catch @panic("out of memory");
249 loop.onNextTick(write_node);
250 }
251
252 for (write_nodes) |*write_node| {
253 await @ptrCast(promise->void, write_node.data);
254 }
255 for (read_nodes) |*read_node| {
256 await @ptrCast(promise->void, read_node.data);
257 }
258}
259
260const shared_it_count = 10;
261var shared_test_data = [1]i32{0} ** 10;
262var shared_test_index: usize = 0;
263var shared_count: usize = 0;
264
265async fn writeRunner(lock: *RwLock) void {
266 suspend; // resumed by onNextTick
267
268 var i: usize = 0;
269 while (i < shared_test_data.len) : (i += 1) {
270 std.os.time.sleep(0, 100000);
271 const lock_promise = async lock.acquireWrite() catch @panic("out of memory");
272 const handle = await lock_promise;
273 defer handle.release();
274
275 shared_count += 1;
276 while (shared_test_index < shared_test_data.len) : (shared_test_index += 1) {
277 shared_test_data[shared_test_index] = shared_test_data[shared_test_index] + 1;
278 }
279 shared_test_index = 0;
280 }
281}
282
283async fn readRunner(lock: *RwLock) void {
284 suspend; // resumed by onNextTick
285 std.os.time.sleep(0, 1);
286
287 var i: usize = 0;
288 while (i < shared_test_data.len) : (i += 1) {
289 const lock_promise = async lock.acquireRead() catch @panic("out of memory");
290 const handle = await lock_promise;
291 defer handle.release();
292
293 assert(shared_test_index == 0);
294 assert(shared_test_data[i] == @intCast(i32, shared_count));
295 }
296}
std/event/rwlocked.zig created+58
......@@ -0,0 +1,58 @@
1const std = @import("../index.zig");
2const RwLock = std.event.RwLock;
3const Loop = std.event.Loop;
4
5/// Thread-safe async/await RW lock that protects one piece of data.
6/// Does not make any syscalls - coroutines which are waiting for the lock are suspended, and
7/// are resumed when the lock is released, in order.
8pub fn RwLocked(comptime T: type) type {
9 return struct {
10 lock: RwLock,
11 locked_data: T,
12
13 const Self = this;
14
15 pub const HeldReadLock = struct {
16 value: *const T,
17 held: RwLock.HeldRead,
18
19 pub fn release(self: HeldReadLock) void {
20 self.held.release();
21 }
22 };
23
24 pub const HeldWriteLock = struct {
25 value: *T,
26 held: RwLock.HeldWrite,
27
28 pub fn release(self: HeldWriteLock) void {
29 self.held.release();
30 }
31 };
32
33 pub fn init(loop: *Loop, data: T) Self {
34 return Self{
35 .lock = RwLock.init(loop),
36 .locked_data = data,
37 };
38 }
39
40 pub fn deinit(self: *Self) void {
41 self.lock.deinit();
42 }
43
44 pub async fn acquireRead(self: *Self) HeldReadLock {
45 return HeldReadLock{
46 .held = await (async self.lock.acquireRead() catch unreachable),
47 .value = &self.locked_data,
48 };
49 }
50
51 pub async fn acquireWrite(self: *Self) HeldWriteLock {
52 return HeldWriteLock{
53 .held = await (async self.lock.acquireWrite() catch unreachable),
54 .value = &self.locked_data,
55 };
56 }
57 };
58}
std/event/tcp.zig+3-4
......@@ -55,13 +55,13 @@ pub const Server = struct {
5555 errdefer cancel self.accept_coro.?;
5656
5757 self.listen_resume_node.handle = self.accept_coro.?;
58 try self.loop.addFd(sockfd, &self.listen_resume_node);
58 try self.loop.linuxAddFd(sockfd, &self.listen_resume_node, posix.EPOLLIN | posix.EPOLLOUT | posix.EPOLLET);
5959 errdefer self.loop.removeFd(sockfd);
6060 }
6161
6262 /// Stop listening
6363 pub fn close(self: *Server) void {
64 self.loop.removeFd(self.sockfd.?);
64 self.loop.linuxRemoveFd(self.sockfd.?);
6565 std.os.close(self.sockfd.?);
6666 }
6767
......@@ -116,7 +116,7 @@ pub async fn connect(loop: *Loop, _address: *const std.net.Address) !std.os.File
116116 errdefer std.os.close(sockfd);
117117
118118 try std.os.posixConnectAsync(sockfd, &address.os_addr);
119 try await try async loop.waitFd(sockfd);
119 try await try async loop.linuxWaitFd(sockfd, posix.EPOLLIN | posix.EPOLLOUT | posix.EPOLLET);
120120 try std.os.posixGetSockOptConnectError(sockfd);
121121
122122 return std.os.File.openHandle(sockfd);
......@@ -181,4 +181,3 @@ async fn doAsyncTest(loop: *Loop, address: *const std.net.Address, server: *Serv
181181 assert(mem.eql(u8, msg, "hello from server\n"));
182182 server.close();
183183}
184
std/hash_map.zig+257-57
......@@ -9,6 +9,10 @@ const builtin = @import("builtin");
99const want_modification_safety = builtin.mode != builtin.Mode.ReleaseFast;
1010const debug_u32 = if (want_modification_safety) u32 else void;
1111
12pub fn AutoHashMap(comptime K: type, comptime V: type) type {
13 return HashMap(K, V, getAutoHashFn(K), getAutoEqlFn(K));
14}
15
1216pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u32, comptime eql: fn (a: K, b: K) bool) type {
1317 return struct {
1418 entries: []Entry,
......@@ -20,13 +24,22 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3
2024
2125 const Self = this;
2226
23 pub const Entry = struct {
24 used: bool,
25 distance_from_start_index: usize,
27 pub const KV = struct {
2628 key: K,
2729 value: V,
2830 };
2931
32 const Entry = struct {
33 used: bool,
34 distance_from_start_index: usize,
35 kv: KV,
36 };
37
38 pub const GetOrPutResult = struct {
39 kv: *KV,
40 found_existing: bool,
41 };
42
3043 pub const Iterator = struct {
3144 hm: *const Self,
3245 // how many items have we returned
......@@ -36,7 +49,7 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3
3649 // used to detect concurrent modification
3750 initial_modification_count: debug_u32,
3851
39 pub fn next(it: *Iterator) ?*Entry {
52 pub fn next(it: *Iterator) ?*KV {
4053 if (want_modification_safety) {
4154 assert(it.initial_modification_count == it.hm.modification_count); // concurrent modification
4255 }
......@@ -46,7 +59,7 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3
4659 if (entry.used) {
4760 it.index += 1;
4861 it.count += 1;
49 return entry;
62 return &entry.kv;
5063 }
5164 }
5265 unreachable; // no next item
......@@ -71,7 +84,7 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3
7184 };
7285 }
7386
74 pub fn deinit(hm: *const Self) void {
87 pub fn deinit(hm: Self) void {
7588 hm.allocator.free(hm.entries);
7689 }
7790
......@@ -84,34 +97,65 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3
8497 hm.incrementModificationCount();
8598 }
8699
87 pub fn count(hm: *const Self) usize {
88 return hm.size;
100 pub fn count(self: Self) usize {
101 return self.size;
89102 }
90103
91 /// Returns the value that was already there.
92 pub fn put(hm: *Self, key: K, value: *const V) !?V {
93 if (hm.entries.len == 0) {
94 try hm.initCapacity(16);
104 /// If key exists this function cannot fail.
105 /// If there is an existing item with `key`, then the result
106 /// kv pointer points to it, and found_existing is true.
107 /// Otherwise, puts a new item with undefined value, and
108 /// the kv pointer points to it. Caller should then initialize
109 /// the data.
110 pub fn getOrPut(self: *Self, key: K) !GetOrPutResult {
111 // TODO this implementation can be improved - we should only
112 // have to hash once and find the entry once.
113 if (self.get(key)) |kv| {
114 return GetOrPutResult{
115 .kv = kv,
116 .found_existing = true,
117 };
118 }
119 self.incrementModificationCount();
120 try self.ensureCapacity();
121 const put_result = self.internalPut(key);
122 assert(put_result.old_kv == null);
123 return GetOrPutResult{
124 .kv = &put_result.new_entry.kv,
125 .found_existing = false,
126 };
127 }
128
129 fn ensureCapacity(self: *Self) !void {
130 if (self.entries.len == 0) {
131 return self.initCapacity(16);
95132 }
96 hm.incrementModificationCount();
97133
98134 // if we get too full (60%), double the capacity
99 if (hm.size * 5 >= hm.entries.len * 3) {
100 const old_entries = hm.entries;
101 try hm.initCapacity(hm.entries.len * 2);
135 if (self.size * 5 >= self.entries.len * 3) {
136 const old_entries = self.entries;
137 try self.initCapacity(self.entries.len * 2);
102138 // dump all of the old elements into the new table
103139 for (old_entries) |*old_entry| {
104140 if (old_entry.used) {
105 _ = hm.internalPut(old_entry.key, old_entry.value);
141 self.internalPut(old_entry.kv.key).new_entry.kv.value = old_entry.kv.value;
106142 }
107143 }
108 hm.allocator.free(old_entries);
144 self.allocator.free(old_entries);
109145 }
146 }
147
148 /// Returns the kv pair that was already there.
149 pub fn put(self: *Self, key: K, value: V) !?KV {
150 self.incrementModificationCount();
151 try self.ensureCapacity();
110152
111 return hm.internalPut(key, value);
153 const put_result = self.internalPut(key);
154 put_result.new_entry.kv.value = value;
155 return put_result.old_kv;
112156 }
113157
114 pub fn get(hm: *const Self, key: K) ?*Entry {
158 pub fn get(hm: *const Self, key: K) ?*KV {
115159 if (hm.entries.len == 0) {
116160 return null;
117161 }
......@@ -122,7 +166,7 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3
122166 return hm.get(key) != null;
123167 }
124168
125 pub fn remove(hm: *Self, key: K) ?*Entry {
169 pub fn remove(hm: *Self, key: K) ?*KV {
126170 if (hm.entries.len == 0) return null;
127171 hm.incrementModificationCount();
128172 const start_index = hm.keyToIndex(key);
......@@ -134,7 +178,7 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3
134178
135179 if (!entry.used) return null;
136180
137 if (!eql(entry.key, key)) continue;
181 if (!eql(entry.kv.key, key)) continue;
138182
139183 while (roll_over < hm.entries.len) : (roll_over += 1) {
140184 const next_index = (start_index + roll_over + 1) % hm.entries.len;
......@@ -142,7 +186,7 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3
142186 if (!next_entry.used or next_entry.distance_from_start_index == 0) {
143187 entry.used = false;
144188 hm.size -= 1;
145 return entry;
189 return &entry.kv;
146190 }
147191 entry.* = next_entry.*;
148192 entry.distance_from_start_index -= 1;
......@@ -163,6 +207,16 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3
163207 };
164208 }
165209
210 pub fn clone(self: Self) !Self {
211 var other = Self.init(self.allocator);
212 try other.initCapacity(self.entries.len);
213 var it = self.iterator();
214 while (it.next()) |entry| {
215 assert((try other.put(entry.key, entry.value)) == null);
216 }
217 return other;
218 }
219
166220 fn initCapacity(hm: *Self, capacity: usize) !void {
167221 hm.entries = try hm.allocator.alloc(Entry, capacity);
168222 hm.size = 0;
......@@ -178,60 +232,81 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3
178232 }
179233 }
180234
181 /// Returns the value that was already there.
182 fn internalPut(hm: *Self, orig_key: K, orig_value: *const V) ?V {
235 const InternalPutResult = struct {
236 new_entry: *Entry,
237 old_kv: ?KV,
238 };
239
240 /// Returns a pointer to the new entry.
241 /// Asserts that there is enough space for the new item.
242 fn internalPut(self: *Self, orig_key: K) InternalPutResult {
183243 var key = orig_key;
184 var value = orig_value.*;
185 const start_index = hm.keyToIndex(key);
244 var value: V = undefined;
245 const start_index = self.keyToIndex(key);
186246 var roll_over: usize = 0;
187247 var distance_from_start_index: usize = 0;
188 while (roll_over < hm.entries.len) : ({
248 var got_result_entry = false;
249 var result = InternalPutResult{
250 .new_entry = undefined,
251 .old_kv = null,
252 };
253 while (roll_over < self.entries.len) : ({
189254 roll_over += 1;
190255 distance_from_start_index += 1;
191256 }) {
192 const index = (start_index + roll_over) % hm.entries.len;
193 const entry = &hm.entries[index];
257 const index = (start_index + roll_over) % self.entries.len;
258 const entry = &self.entries[index];
194259
195 if (entry.used and !eql(entry.key, key)) {
260 if (entry.used and !eql(entry.kv.key, key)) {
196261 if (entry.distance_from_start_index < distance_from_start_index) {
197262 // robin hood to the rescue
198263 const tmp = entry.*;
199 hm.max_distance_from_start_index = math.max(hm.max_distance_from_start_index, distance_from_start_index);
264 self.max_distance_from_start_index = math.max(self.max_distance_from_start_index, distance_from_start_index);
265 if (!got_result_entry) {
266 got_result_entry = true;
267 result.new_entry = entry;
268 }
200269 entry.* = Entry{
201270 .used = true,
202271 .distance_from_start_index = distance_from_start_index,
203 .key = key,
204 .value = value,
272 .kv = KV{
273 .key = key,
274 .value = value,
275 },
205276 };
206 key = tmp.key;
207 value = tmp.value;
277 key = tmp.kv.key;
278 value = tmp.kv.value;
208279 distance_from_start_index = tmp.distance_from_start_index;
209280 }
210281 continue;
211282 }
212283
213 var result: ?V = null;
214284 if (entry.used) {
215 result = entry.value;
285 result.old_kv = entry.kv;
216286 } else {
217287 // adding an entry. otherwise overwriting old value with
218288 // same key
219 hm.size += 1;
289 self.size += 1;
220290 }
221291
222 hm.max_distance_from_start_index = math.max(distance_from_start_index, hm.max_distance_from_start_index);
292 self.max_distance_from_start_index = math.max(distance_from_start_index, self.max_distance_from_start_index);
293 if (!got_result_entry) {
294 result.new_entry = entry;
295 }
223296 entry.* = Entry{
224297 .used = true,
225298 .distance_from_start_index = distance_from_start_index,
226 .key = key,
227 .value = value,
299 .kv = KV{
300 .key = key,
301 .value = value,
302 },
228303 };
229304 return result;
230305 }
231306 unreachable; // put into a full map
232307 }
233308
234 fn internalGet(hm: *const Self, key: K) ?*Entry {
309 fn internalGet(hm: Self, key: K) ?*KV {
235310 const start_index = hm.keyToIndex(key);
236311 {
237312 var roll_over: usize = 0;
......@@ -240,13 +315,13 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3
240315 const entry = &hm.entries[index];
241316
242317 if (!entry.used) return null;
243 if (eql(entry.key, key)) return entry;
318 if (eql(entry.kv.key, key)) return &entry.kv;
244319 }
245320 }
246321 return null;
247322 }
248323
249 fn keyToIndex(hm: *const Self, key: K) usize {
324 fn keyToIndex(hm: Self, key: K) usize {
250325 return usize(hash(key)) % hm.entries.len;
251326 }
252327 };
......@@ -256,7 +331,7 @@ test "basic hash map usage" {
256331 var direct_allocator = std.heap.DirectAllocator.init();
257332 defer direct_allocator.deinit();
258333
259 var map = HashMap(i32, i32, hash_i32, eql_i32).init(&direct_allocator.allocator);
334 var map = AutoHashMap(i32, i32).init(&direct_allocator.allocator);
260335 defer map.deinit();
261336
262337 assert((try map.put(1, 11)) == null);
......@@ -265,8 +340,19 @@ test "basic hash map usage" {
265340 assert((try map.put(4, 44)) == null);
266341 assert((try map.put(5, 55)) == null);
267342
268 assert((try map.put(5, 66)).? == 55);
269 assert((try map.put(5, 55)).? == 66);
343 assert((try map.put(5, 66)).?.value == 55);
344 assert((try map.put(5, 55)).?.value == 66);
345
346 const gop1 = try map.getOrPut(5);
347 assert(gop1.found_existing == true);
348 assert(gop1.kv.value == 55);
349 gop1.kv.value = 77;
350 assert(map.get(5).?.value == 77);
351
352 const gop2 = try map.getOrPut(99);
353 assert(gop2.found_existing == false);
354 gop2.kv.value = 42;
355 assert(map.get(99).?.value == 42);
270356
271357 assert(map.contains(2));
272358 assert(map.get(2).?.value == 22);
......@@ -279,7 +365,7 @@ test "iterator hash map" {
279365 var direct_allocator = std.heap.DirectAllocator.init();
280366 defer direct_allocator.deinit();
281367
282 var reset_map = HashMap(i32, i32, hash_i32, eql_i32).init(&direct_allocator.allocator);
368 var reset_map = AutoHashMap(i32, i32).init(&direct_allocator.allocator);
283369 defer reset_map.deinit();
284370
285371 assert((try reset_map.put(1, 11)) == null);
......@@ -287,14 +373,14 @@ test "iterator hash map" {
287373 assert((try reset_map.put(3, 33)) == null);
288374
289375 var keys = []i32{
290 1,
291 2,
292376 3,
377 2,
378 1,
293379 };
294380 var values = []i32{
295 11,
296 22,
297381 33,
382 22,
383 11,
298384 };
299385
300386 var it = reset_map.iterator();
......@@ -322,10 +408,124 @@ test "iterator hash map" {
322408 assert(entry.value == values[0]);
323409}
324410
325fn hash_i32(x: i32) u32 {
326 return @bitCast(u32, x);
411pub fn getAutoHashFn(comptime K: type) (fn (K) u32) {
412 return struct {
413 fn hash(key: K) u32 {
414 comptime var rng = comptime std.rand.DefaultPrng.init(0);
415 return autoHash(key, &rng.random, u32);
416 }
417 }.hash;
418}
419
420pub fn getAutoEqlFn(comptime K: type) (fn (K, K) bool) {
421 return struct {
422 fn eql(a: K, b: K) bool {
423 return autoEql(a, b);
424 }
425 }.eql;
426}
427
428// TODO improve these hash functions
429pub fn autoHash(key: var, comptime rng: *std.rand.Random, comptime HashInt: type) HashInt {
430 switch (@typeInfo(@typeOf(key))) {
431 builtin.TypeId.NoReturn,
432 builtin.TypeId.Opaque,
433 builtin.TypeId.Undefined,
434 builtin.TypeId.ArgTuple,
435 => @compileError("cannot hash this type"),
436
437 builtin.TypeId.Void,
438 builtin.TypeId.Null,
439 => return 0,
440
441 builtin.TypeId.Int => |info| {
442 const unsigned_x = @bitCast(@IntType(false, info.bits), key);
443 if (info.bits <= HashInt.bit_count) {
444 return HashInt(unsigned_x) ^ comptime rng.scalar(HashInt);
445 } else {
446 return @truncate(HashInt, unsigned_x ^ comptime rng.scalar(@typeOf(unsigned_x)));
447 }
448 },
449
450 builtin.TypeId.Float => |info| {
451 return autoHash(@bitCast(@IntType(false, info.bits), key), rng);
452 },
453 builtin.TypeId.Bool => return autoHash(@boolToInt(key), rng),
454 builtin.TypeId.Enum => return autoHash(@enumToInt(key), rng),
455 builtin.TypeId.ErrorSet => return autoHash(@errorToInt(key), rng),
456 builtin.TypeId.Promise, builtin.TypeId.Fn => return autoHash(@ptrToInt(key), rng),
457
458 builtin.TypeId.Namespace,
459 builtin.TypeId.Block,
460 builtin.TypeId.BoundFn,
461 builtin.TypeId.ComptimeFloat,
462 builtin.TypeId.ComptimeInt,
463 builtin.TypeId.Type,
464 => return 0,
465
466 builtin.TypeId.Pointer => |info| switch (info.size) {
467 builtin.TypeInfo.Pointer.Size.One => @compileError("TODO auto hash for single item pointers"),
468 builtin.TypeInfo.Pointer.Size.Many => @compileError("TODO auto hash for many item pointers"),
469 builtin.TypeInfo.Pointer.Size.Slice => {
470 const interval = std.math.max(1, key.len / 256);
471 var i: usize = 0;
472 var h = comptime rng.scalar(HashInt);
473 while (i < key.len) : (i += interval) {
474 h ^= autoHash(key[i], rng, HashInt);
475 }
476 return h;
477 },
478 },
479
480 builtin.TypeId.Optional => @compileError("TODO auto hash for optionals"),
481 builtin.TypeId.Array => @compileError("TODO auto hash for arrays"),
482 builtin.TypeId.Struct => @compileError("TODO auto hash for structs"),
483 builtin.TypeId.Union => @compileError("TODO auto hash for unions"),
484 builtin.TypeId.ErrorUnion => @compileError("TODO auto hash for unions"),
485 }
327486}
328487
329fn eql_i32(a: i32, b: i32) bool {
330 return a == b;
488pub fn autoEql(a: var, b: @typeOf(a)) bool {
489 switch (@typeInfo(@typeOf(a))) {
490 builtin.TypeId.NoReturn,
491 builtin.TypeId.Opaque,
492 builtin.TypeId.Undefined,
493 builtin.TypeId.ArgTuple,
494 => @compileError("cannot test equality of this type"),
495 builtin.TypeId.Void,
496 builtin.TypeId.Null,
497 => return true,
498 builtin.TypeId.Bool,
499 builtin.TypeId.Int,
500 builtin.TypeId.Float,
501 builtin.TypeId.ComptimeFloat,
502 builtin.TypeId.ComptimeInt,
503 builtin.TypeId.Namespace,
504 builtin.TypeId.Block,
505 builtin.TypeId.Promise,
506 builtin.TypeId.Enum,
507 builtin.TypeId.BoundFn,
508 builtin.TypeId.Fn,
509 builtin.TypeId.ErrorSet,
510 builtin.TypeId.Type,
511 => return a == b,
512
513 builtin.TypeId.Pointer => |info| switch (info.size) {
514 builtin.TypeInfo.Pointer.Size.One => @compileError("TODO auto eql for single item pointers"),
515 builtin.TypeInfo.Pointer.Size.Many => @compileError("TODO auto eql for many item pointers"),
516 builtin.TypeInfo.Pointer.Size.Slice => {
517 if (a.len != b.len) return false;
518 for (a) |a_item, i| {
519 if (!autoEql(a_item, b[i])) return false;
520 }
521 return true;
522 },
523 },
524
525 builtin.TypeId.Optional => @compileError("TODO auto eql for optionals"),
526 builtin.TypeId.Array => @compileError("TODO auto eql for arrays"),
527 builtin.TypeId.Struct => @compileError("TODO auto eql for structs"),
528 builtin.TypeId.Union => @compileError("TODO auto eql for unions"),
529 builtin.TypeId.ErrorUnion => @compileError("TODO auto eql for unions"),
530 }
331531}
std/index.zig+3-1
......@@ -5,10 +5,11 @@ pub const BufSet = @import("buf_set.zig").BufSet;
55pub const Buffer = @import("buffer.zig").Buffer;
66pub const BufferOutStream = @import("buffer.zig").BufferOutStream;
77pub const HashMap = @import("hash_map.zig").HashMap;
8pub const AutoHashMap = @import("hash_map.zig").AutoHashMap;
89pub const LinkedList = @import("linked_list.zig").LinkedList;
9pub const IntrusiveLinkedList = @import("linked_list.zig").IntrusiveLinkedList;
1010pub const SegmentedList = @import("segmented_list.zig").SegmentedList;
1111pub const DynLib = @import("dynamic_library.zig").DynLib;
12pub const Mutex = @import("mutex.zig").Mutex;
1213
1314pub const atomic = @import("atomic/index.zig");
1415pub const base64 = @import("base64.zig");
......@@ -49,6 +50,7 @@ test "std" {
4950 _ = @import("hash_map.zig");
5051 _ = @import("linked_list.zig");
5152 _ = @import("segmented_list.zig");
53 _ = @import("mutex.zig");
5254
5355 _ = @import("base64.zig");
5456 _ = @import("build.zig");
std/io.zig+7-9
......@@ -415,13 +415,12 @@ pub fn PeekStream(comptime buffer_size: usize, comptime InStreamError: type) typ
415415 self.at_end = (read < left);
416416 return pos + read;
417417 }
418
419418 };
420419}
421420
422421pub const SliceInStream = struct {
423422 const Self = this;
424 pub const Error = error { };
423 pub const Error = error{};
425424 pub const Stream = InStream(Error);
426425
427426 pub stream: Stream,
......@@ -481,13 +480,12 @@ pub const SliceOutStream = struct {
481480
482481 assert(self.pos <= self.slice.len);
483482
484 const n =
485 if (self.pos + bytes.len <= self.slice.len)
486 bytes.len
487 else
488 self.slice.len - self.pos;
483 const n = if (self.pos + bytes.len <= self.slice.len)
484 bytes.len
485 else
486 self.slice.len - self.pos;
489487
490 std.mem.copy(u8, self.slice[self.pos..self.pos + n], bytes[0..n]);
488 std.mem.copy(u8, self.slice[self.pos .. self.pos + n], bytes[0..n]);
491489 self.pos += n;
492490
493491 if (n < bytes.len) {
......@@ -586,7 +584,7 @@ pub const BufferedAtomicFile = struct {
586584 });
587585 errdefer allocator.destroy(self);
588586
589 self.atomic_file = try os.AtomicFile.init(allocator, dest_path, os.default_file_mode);
587 self.atomic_file = try os.AtomicFile.init(allocator, dest_path, os.File.default_mode);
590588 errdefer self.atomic_file.deinit();
591589
592590 self.file_stream = FileOutStream.init(&self.atomic_file.file);
std/json.zig+1-1
......@@ -1318,7 +1318,7 @@ pub const Parser = struct {
13181318 _ = p.stack.pop();
13191319
13201320 var object = &p.stack.items[p.stack.len - 1].Object;
1321 _ = try object.put(key, value);
1321 _ = try object.put(key, value.*);
13221322 p.state = State.ObjectKey;
13231323 },
13241324 // Array Parent -> [ ..., <array>, value ]
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}
std/mem.zig+1-1
......@@ -577,7 +577,7 @@ pub fn join(allocator: *Allocator, sep: u8, strings: ...) ![]u8 {
577577 }
578578 }
579579
580 return buf[0..buf_index];
580 return allocator.shrink(u8, buf, buf_index);
581581}
582582
583583test "mem.join" {
std/mutex.zig created+27
......@@ -0,0 +1,27 @@
1const std = @import("index.zig");
2const builtin = @import("builtin");
3const AtomicOrder = builtin.AtomicOrder;
4const AtomicRmwOp = builtin.AtomicRmwOp;
5const assert = std.debug.assert;
6
7/// TODO use syscalls instead of a spinlock
8pub const Mutex = struct {
9 lock: u8, // TODO use a bool
10
11 pub const Held = struct {
12 mutex: *Mutex,
13
14 pub fn release(self: Held) void {
15 assert(@atomicRmw(u8, &self.mutex.lock, builtin.AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst) == 1);
16 }
17 };
18
19 pub fn init() Mutex {
20 return Mutex{ .lock = 0 };
21 }
22
23 pub fn acquire(self: *Mutex) Held {
24 while (@atomicRmw(u8, &self.lock, builtin.AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst) != 0) {}
25 return Held{ .mutex = self };
26 }
27};
std/os/darwin.zig+124-85
......@@ -482,91 +482,98 @@ pub const NOTE_MACH_CONTINUOUS_TIME = 0x00000080;
482482/// data is mach absolute time units
483483pub const NOTE_MACHTIME = 0x00000100;
484484
485pub const AF_UNSPEC: c_int = 0;
486pub const AF_LOCAL: c_int = 1;
487pub const AF_UNIX: c_int = AF_LOCAL;
488pub const AF_INET: c_int = 2;
489pub const AF_SYS_CONTROL: c_int = 2;
490pub const AF_IMPLINK: c_int = 3;
491pub const AF_PUP: c_int = 4;
492pub const AF_CHAOS: c_int = 5;
493pub const AF_NS: c_int = 6;
494pub const AF_ISO: c_int = 7;
495pub const AF_OSI: c_int = AF_ISO;
496pub const AF_ECMA: c_int = 8;
497pub const AF_DATAKIT: c_int = 9;
498pub const AF_CCITT: c_int = 10;
499pub const AF_SNA: c_int = 11;
500pub const AF_DECnet: c_int = 12;
501pub const AF_DLI: c_int = 13;
502pub const AF_LAT: c_int = 14;
503pub const AF_HYLINK: c_int = 15;
504pub const AF_APPLETALK: c_int = 16;
505pub const AF_ROUTE: c_int = 17;
506pub const AF_LINK: c_int = 18;
507pub const AF_XTP: c_int = 19;
508pub const AF_COIP: c_int = 20;
509pub const AF_CNT: c_int = 21;
510pub const AF_RTIP: c_int = 22;
511pub const AF_IPX: c_int = 23;
512pub const AF_SIP: c_int = 24;
513pub const AF_PIP: c_int = 25;
514pub const AF_ISDN: c_int = 28;
515pub const AF_E164: c_int = AF_ISDN;
516pub const AF_KEY: c_int = 29;
517pub const AF_INET6: c_int = 30;
518pub const AF_NATM: c_int = 31;
519pub const AF_SYSTEM: c_int = 32;
520pub const AF_NETBIOS: c_int = 33;
521pub const AF_PPP: c_int = 34;
522pub const AF_MAX: c_int = 40;
523
524pub const PF_UNSPEC: c_int = AF_UNSPEC;
525pub const PF_LOCAL: c_int = AF_LOCAL;
526pub const PF_UNIX: c_int = PF_LOCAL;
527pub const PF_INET: c_int = AF_INET;
528pub const PF_IMPLINK: c_int = AF_IMPLINK;
529pub const PF_PUP: c_int = AF_PUP;
530pub const PF_CHAOS: c_int = AF_CHAOS;
531pub const PF_NS: c_int = AF_NS;
532pub const PF_ISO: c_int = AF_ISO;
533pub const PF_OSI: c_int = AF_ISO;
534pub const PF_ECMA: c_int = AF_ECMA;
535pub const PF_DATAKIT: c_int = AF_DATAKIT;
536pub const PF_CCITT: c_int = AF_CCITT;
537pub const PF_SNA: c_int = AF_SNA;
538pub const PF_DECnet: c_int = AF_DECnet;
539pub const PF_DLI: c_int = AF_DLI;
540pub const PF_LAT: c_int = AF_LAT;
541pub const PF_HYLINK: c_int = AF_HYLINK;
542pub const PF_APPLETALK: c_int = AF_APPLETALK;
543pub const PF_ROUTE: c_int = AF_ROUTE;
544pub const PF_LINK: c_int = AF_LINK;
545pub const PF_XTP: c_int = AF_XTP;
546pub const PF_COIP: c_int = AF_COIP;
547pub const PF_CNT: c_int = AF_CNT;
548pub const PF_SIP: c_int = AF_SIP;
549pub const PF_IPX: c_int = AF_IPX;
550pub const PF_RTIP: c_int = AF_RTIP;
551pub const PF_PIP: c_int = AF_PIP;
552pub const PF_ISDN: c_int = AF_ISDN;
553pub const PF_KEY: c_int = AF_KEY;
554pub const PF_INET6: c_int = AF_INET6;
555pub const PF_NATM: c_int = AF_NATM;
556pub const PF_SYSTEM: c_int = AF_SYSTEM;
557pub const PF_NETBIOS: c_int = AF_NETBIOS;
558pub const PF_PPP: c_int = AF_PPP;
559pub const PF_MAX: c_int = AF_MAX;
560
561pub const SYSPROTO_EVENT: c_int = 1;
562pub const SYSPROTO_CONTROL: c_int = 2;
563
564pub const SOCK_STREAM: c_int = 1;
565pub const SOCK_DGRAM: c_int = 2;
566pub const SOCK_RAW: c_int = 3;
567pub const SOCK_RDM: c_int = 4;
568pub const SOCK_SEQPACKET: c_int = 5;
569pub const SOCK_MAXADDRLEN: c_int = 255;
485pub const AF_UNSPEC = 0;
486pub const AF_LOCAL = 1;
487pub const AF_UNIX = AF_LOCAL;
488pub const AF_INET = 2;
489pub const AF_SYS_CONTROL = 2;
490pub const AF_IMPLINK = 3;
491pub const AF_PUP = 4;
492pub const AF_CHAOS = 5;
493pub const AF_NS = 6;
494pub const AF_ISO = 7;
495pub const AF_OSI = AF_ISO;
496pub const AF_ECMA = 8;
497pub const AF_DATAKIT = 9;
498pub const AF_CCITT = 10;
499pub const AF_SNA = 11;
500pub const AF_DECnet = 12;
501pub const AF_DLI = 13;
502pub const AF_LAT = 14;
503pub const AF_HYLINK = 15;
504pub const AF_APPLETALK = 16;
505pub const AF_ROUTE = 17;
506pub const AF_LINK = 18;
507pub const AF_XTP = 19;
508pub const AF_COIP = 20;
509pub const AF_CNT = 21;
510pub const AF_RTIP = 22;
511pub const AF_IPX = 23;
512pub const AF_SIP = 24;
513pub const AF_PIP = 25;
514pub const AF_ISDN = 28;
515pub const AF_E164 = AF_ISDN;
516pub const AF_KEY = 29;
517pub const AF_INET6 = 30;
518pub const AF_NATM = 31;
519pub const AF_SYSTEM = 32;
520pub const AF_NETBIOS = 33;
521pub const AF_PPP = 34;
522pub const AF_MAX = 40;
523
524pub const PF_UNSPEC = AF_UNSPEC;
525pub const PF_LOCAL = AF_LOCAL;
526pub const PF_UNIX = PF_LOCAL;
527pub const PF_INET = AF_INET;
528pub const PF_IMPLINK = AF_IMPLINK;
529pub const PF_PUP = AF_PUP;
530pub const PF_CHAOS = AF_CHAOS;
531pub const PF_NS = AF_NS;
532pub const PF_ISO = AF_ISO;
533pub const PF_OSI = AF_ISO;
534pub const PF_ECMA = AF_ECMA;
535pub const PF_DATAKIT = AF_DATAKIT;
536pub const PF_CCITT = AF_CCITT;
537pub const PF_SNA = AF_SNA;
538pub const PF_DECnet = AF_DECnet;
539pub const PF_DLI = AF_DLI;
540pub const PF_LAT = AF_LAT;
541pub const PF_HYLINK = AF_HYLINK;
542pub const PF_APPLETALK = AF_APPLETALK;
543pub const PF_ROUTE = AF_ROUTE;
544pub const PF_LINK = AF_LINK;
545pub const PF_XTP = AF_XTP;
546pub const PF_COIP = AF_COIP;
547pub const PF_CNT = AF_CNT;
548pub const PF_SIP = AF_SIP;
549pub const PF_IPX = AF_IPX;
550pub const PF_RTIP = AF_RTIP;
551pub const PF_PIP = AF_PIP;
552pub const PF_ISDN = AF_ISDN;
553pub const PF_KEY = AF_KEY;
554pub const PF_INET6 = AF_INET6;
555pub const PF_NATM = AF_NATM;
556pub const PF_SYSTEM = AF_SYSTEM;
557pub const PF_NETBIOS = AF_NETBIOS;
558pub const PF_PPP = AF_PPP;
559pub const PF_MAX = AF_MAX;
560
561pub const SYSPROTO_EVENT = 1;
562pub const SYSPROTO_CONTROL = 2;
563
564pub const SOCK_STREAM = 1;
565pub const SOCK_DGRAM = 2;
566pub const SOCK_RAW = 3;
567pub const SOCK_RDM = 4;
568pub const SOCK_SEQPACKET = 5;
569pub const SOCK_MAXADDRLEN = 255;
570
571pub const IPPROTO_ICMP = 1;
572pub const IPPROTO_ICMPV6 = 58;
573pub const IPPROTO_TCP = 6;
574pub const IPPROTO_UDP = 17;
575pub const IPPROTO_IP = 0;
576pub const IPPROTO_IPV6 = 41;
570577
571578fn wstatus(x: i32) i32 {
572579 return x & 0o177;
......@@ -605,6 +612,11 @@ pub fn abort() noreturn {
605612 c.abort();
606613}
607614
615// bind(int socket, const struct sockaddr *address, socklen_t address_len)
616pub fn bind(fd: i32, addr: *const sockaddr, len: socklen_t) usize {
617 return errnoWrap(c.bind(@bitCast(c_int, fd), addr, len));
618}
619
608620pub fn exit(code: i32) noreturn {
609621 c.exit(code);
610622}
......@@ -634,6 +646,10 @@ pub fn read(fd: i32, buf: [*]u8, nbyte: usize) usize {
634646 return errnoWrap(c.read(fd, @ptrCast(*c_void, buf), nbyte));
635647}
636648
649pub fn pread(fd: i32, buf: [*]u8, nbyte: usize, offset: u64) usize {
650 return errnoWrap(c.pread(fd, @ptrCast(*c_void, buf), nbyte, offset));
651}
652
637653pub fn stat(noalias path: [*]const u8, noalias buf: *stat) usize {
638654 return errnoWrap(c.stat(path, buf));
639655}
......@@ -642,6 +658,10 @@ pub fn write(fd: i32, buf: [*]const u8, nbyte: usize) usize {
642658 return errnoWrap(c.write(fd, @ptrCast(*const c_void, buf), nbyte));
643659}
644660
661pub fn pwrite(fd: i32, buf: [*]const u8, nbyte: usize, offset: u64) usize {
662 return errnoWrap(c.pwrite(fd, @ptrCast(*const c_void, buf), nbyte, offset));
663}
664
645665pub fn mmap(address: ?[*]u8, length: usize, prot: usize, flags: u32, fd: i32, offset: isize) usize {
646666 const ptr_result = c.mmap(
647667 @ptrCast(*c_void, address),
......@@ -805,6 +825,20 @@ pub fn sigaction(sig: u5, noalias act: *const Sigaction, noalias oact: ?*Sigacti
805825 return result;
806826}
807827
828pub fn socket(domain: u32, socket_type: u32, protocol: u32) usize {
829 return errnoWrap(c.socket(@bitCast(c_int, domain), @bitCast(c_int, socket_type), @bitCast(c_int, protocol)));
830}
831
832pub const iovec = extern struct {
833 iov_base: [*]u8,
834 iov_len: usize,
835};
836
837pub const iovec_const = extern struct {
838 iov_base: [*]const u8,
839 iov_len: usize,
840};
841
808842pub const sigset_t = c.sigset_t;
809843pub const empty_sigset = sigset_t(0);
810844
......@@ -812,8 +846,13 @@ pub const timespec = c.timespec;
812846pub const Stat = c.Stat;
813847pub const dirent = c.dirent;
814848
849pub const in_port_t = c.in_port_t;
815850pub const sa_family_t = c.sa_family_t;
851pub const socklen_t = c.socklen_t;
852
816853pub const sockaddr = c.sockaddr;
854pub const sockaddr_in = c.sockaddr_in;
855pub const sockaddr_in6 = c.sockaddr_in6;
817856
818857/// Renamed from `kevent` to `Kevent` to avoid conflict with the syscall.
819858pub const Kevent = c.Kevent;
std/os/file.zig+26-11
......@@ -15,6 +15,16 @@ pub const File = struct {
1515 /// The OS-specific file descriptor or file handle.
1616 handle: os.FileHandle,
1717
18 pub const Mode = switch (builtin.os) {
19 Os.windows => void,
20 else => u32,
21 };
22
23 pub const default_mode = switch (builtin.os) {
24 Os.windows => {},
25 else => 0o666,
26 };
27
1828 pub const OpenError = os.WindowsOpenError || os.PosixOpenError;
1929
2030 /// `path` needs to be copied in memory to add a null terminating byte, hence the allocator.
......@@ -39,16 +49,16 @@ pub const File = struct {
3949 }
4050 }
4151
42 /// Calls `openWriteMode` with os.default_file_mode for the mode.
52 /// Calls `openWriteMode` with os.File.default_mode for the mode.
4353 pub fn openWrite(allocator: *mem.Allocator, path: []const u8) OpenError!File {
44 return openWriteMode(allocator, path, os.default_file_mode);
54 return openWriteMode(allocator, path, os.File.default_mode);
4555 }
4656
4757 /// If the path does not exist it will be created.
4858 /// If a file already exists in the destination it will be truncated.
4959 /// `path` needs to be copied in memory to add a null terminating byte, hence the allocator.
5060 /// Call close to clean up.
51 pub fn openWriteMode(allocator: *mem.Allocator, path: []const u8, file_mode: os.FileMode) OpenError!File {
61 pub fn openWriteMode(allocator: *mem.Allocator, path: []const u8, file_mode: Mode) OpenError!File {
5262 if (is_posix) {
5363 const flags = posix.O_LARGEFILE | posix.O_WRONLY | posix.O_CREAT | posix.O_CLOEXEC | posix.O_TRUNC;
5464 const fd = try os.posixOpen(allocator, path, flags, file_mode);
......@@ -72,7 +82,7 @@ pub const File = struct {
7282 /// If a file already exists in the destination this returns OpenError.PathAlreadyExists
7383 /// `path` needs to be copied in memory to add a null terminating byte, hence the allocator.
7484 /// Call close to clean up.
75 pub fn openWriteNoClobber(allocator: *mem.Allocator, path: []const u8, file_mode: os.FileMode) OpenError!File {
85 pub fn openWriteNoClobber(allocator: *mem.Allocator, path: []const u8, file_mode: Mode) OpenError!File {
7686 if (is_posix) {
7787 const flags = posix.O_LARGEFILE | posix.O_WRONLY | posix.O_CREAT | posix.O_CLOEXEC | posix.O_EXCL;
7888 const fd = try os.posixOpen(allocator, path, flags, file_mode);
......@@ -282,7 +292,7 @@ pub const File = struct {
282292 Unexpected,
283293 };
284294
285 pub fn mode(self: *File) ModeError!os.FileMode {
295 pub fn mode(self: *File) ModeError!Mode {
286296 if (is_posix) {
287297 var stat: posix.Stat = undefined;
288298 const err = posix.getErrno(posix.fstat(self.handle, &stat));
......@@ -296,7 +306,7 @@ pub const File = struct {
296306
297307 // TODO: we should be able to cast u16 to ModeError!u32, making this
298308 // explicit cast not necessary
299 return os.FileMode(stat.mode);
309 return Mode(stat.mode);
300310 } else if (is_windows) {
301311 return {};
302312 } else {
......@@ -305,9 +315,11 @@ pub const File = struct {
305315 }
306316
307317 pub const ReadError = error{
308 BadFd,
309 Io,
318 FileClosed,
319 InputOutput,
310320 IsDir,
321 WouldBlock,
322 SystemResources,
311323
312324 Unexpected,
313325 };
......@@ -323,9 +335,12 @@ pub const File = struct {
323335 posix.EINTR => continue,
324336 posix.EINVAL => unreachable,
325337 posix.EFAULT => unreachable,
326 posix.EBADF => return error.BadFd,
327 posix.EIO => return error.Io,
338 posix.EAGAIN => return error.WouldBlock,
339 posix.EBADF => return error.FileClosed,
340 posix.EIO => return error.InputOutput,
328341 posix.EISDIR => return error.IsDir,
342 posix.ENOBUFS => return error.SystemResources,
343 posix.ENOMEM => return error.SystemResources,
329344 else => return os.unexpectedErrorPosix(read_err),
330345 }
331346 }
......@@ -338,7 +353,7 @@ pub const File = struct {
338353 while (index < buffer.len) {
339354 const want_read_count = @intCast(windows.DWORD, math.min(windows.DWORD(@maxValue(windows.DWORD)), buffer.len - index));
340355 var amt_read: windows.DWORD = undefined;
341 if (windows.ReadFile(self.handle, @ptrCast(*c_void, buffer.ptr + index), want_read_count, &amt_read, null) == 0) {
356 if (windows.ReadFile(self.handle, buffer.ptr + index, want_read_count, &amt_read, null) == 0) {
342357 const err = windows.GetLastError();
343358 return switch (err) {
344359 windows.ERROR.OPERATION_ABORTED => continue,
std/os/index.zig+169-12
......@@ -38,16 +38,6 @@ pub const path = @import("path.zig");
3838pub const File = @import("file.zig").File;
3939pub const time = @import("time.zig");
4040
41pub const FileMode = switch (builtin.os) {
42 Os.windows => void,
43 else => u32,
44};
45
46pub const default_file_mode = switch (builtin.os) {
47 Os.windows => {},
48 else => 0o666,
49};
50
5141pub const page_size = 4 * 1024;
5242
5343pub const UserInfo = @import("get_user_id.zig").UserInfo;
......@@ -256,6 +246,67 @@ pub fn posixRead(fd: i32, buf: []u8) !void {
256246 }
257247}
258248
249/// Number of bytes read is returned. Upon reading end-of-file, zero is returned.
250pub fn posix_preadv(fd: i32, iov: [*]const posix.iovec, count: usize, offset: u64) !usize {
251 switch (builtin.os) {
252 builtin.Os.macosx => {
253 // Darwin does not have preadv but it does have pread.
254 var off: usize = 0;
255 var iov_i: usize = 0;
256 var inner_off: usize = 0;
257 while (true) {
258 const v = iov[iov_i];
259 const rc = darwin.pread(fd, v.iov_base + inner_off, v.iov_len - inner_off, offset + off);
260 const err = darwin.getErrno(rc);
261 switch (err) {
262 0 => {
263 off += rc;
264 inner_off += rc;
265 if (inner_off == v.iov_len) {
266 iov_i += 1;
267 inner_off = 0;
268 if (iov_i == count) {
269 return off;
270 }
271 }
272 if (rc == 0) return off; // EOF
273 continue;
274 },
275 posix.EINTR => continue,
276 posix.EINVAL => unreachable,
277 posix.EFAULT => unreachable,
278 posix.ESPIPE => unreachable, // fd is not seekable
279 posix.EAGAIN => return error.WouldBlock,
280 posix.EBADF => return error.FileClosed,
281 posix.EIO => return error.InputOutput,
282 posix.EISDIR => return error.IsDir,
283 posix.ENOBUFS => return error.SystemResources,
284 posix.ENOMEM => return error.SystemResources,
285 else => return unexpectedErrorPosix(err),
286 }
287 }
288 },
289 builtin.Os.linux, builtin.Os.freebsd => while (true) {
290 const rc = posix.preadv(fd, iov, count, offset);
291 const err = posix.getErrno(rc);
292 switch (err) {
293 0 => return rc,
294 posix.EINTR => continue,
295 posix.EINVAL => unreachable,
296 posix.EFAULT => unreachable,
297 posix.EAGAIN => return error.WouldBlock,
298 posix.EBADF => return error.FileClosed,
299 posix.EIO => return error.InputOutput,
300 posix.EISDIR => return error.IsDir,
301 posix.ENOBUFS => return error.SystemResources,
302 posix.ENOMEM => return error.SystemResources,
303 else => return unexpectedErrorPosix(err),
304 }
305 },
306 else => @compileError("Unsupported OS"),
307 }
308}
309
259310pub const PosixWriteError = error{
260311 WouldBlock,
261312 FileClosed,
......@@ -300,6 +351,71 @@ pub fn posixWrite(fd: i32, bytes: []const u8) !void {
300351 }
301352}
302353
354pub fn posix_pwritev(fd: i32, iov: [*]const posix.iovec_const, count: usize, offset: u64) PosixWriteError!void {
355 switch (builtin.os) {
356 builtin.Os.macosx => {
357 // Darwin does not have pwritev but it does have pwrite.
358 var off: usize = 0;
359 var iov_i: usize = 0;
360 var inner_off: usize = 0;
361 while (true) {
362 const v = iov[iov_i];
363 const rc = darwin.pwrite(fd, v.iov_base + inner_off, v.iov_len - inner_off, offset + off);
364 const err = darwin.getErrno(rc);
365 switch (err) {
366 0 => {
367 off += rc;
368 inner_off += rc;
369 if (inner_off == v.iov_len) {
370 iov_i += 1;
371 inner_off = 0;
372 if (iov_i == count) {
373 return;
374 }
375 }
376 continue;
377 },
378 posix.EINTR => continue,
379 posix.ESPIPE => unreachable, // fd is not seekable
380 posix.EINVAL => unreachable,
381 posix.EFAULT => unreachable,
382 posix.EAGAIN => return PosixWriteError.WouldBlock,
383 posix.EBADF => return PosixWriteError.FileClosed,
384 posix.EDESTADDRREQ => return PosixWriteError.DestinationAddressRequired,
385 posix.EDQUOT => return PosixWriteError.DiskQuota,
386 posix.EFBIG => return PosixWriteError.FileTooBig,
387 posix.EIO => return PosixWriteError.InputOutput,
388 posix.ENOSPC => return PosixWriteError.NoSpaceLeft,
389 posix.EPERM => return PosixWriteError.AccessDenied,
390 posix.EPIPE => return PosixWriteError.BrokenPipe,
391 else => return unexpectedErrorPosix(err),
392 }
393 }
394 },
395 builtin.Os.linux => while (true) {
396 const rc = posix.pwritev(fd, iov, count, offset);
397 const err = posix.getErrno(rc);
398 switch (err) {
399 0 => return,
400 posix.EINTR => continue,
401 posix.EINVAL => unreachable,
402 posix.EFAULT => unreachable,
403 posix.EAGAIN => return PosixWriteError.WouldBlock,
404 posix.EBADF => return PosixWriteError.FileClosed,
405 posix.EDESTADDRREQ => return PosixWriteError.DestinationAddressRequired,
406 posix.EDQUOT => return PosixWriteError.DiskQuota,
407 posix.EFBIG => return PosixWriteError.FileTooBig,
408 posix.EIO => return PosixWriteError.InputOutput,
409 posix.ENOSPC => return PosixWriteError.NoSpaceLeft,
410 posix.EPERM => return PosixWriteError.AccessDenied,
411 posix.EPIPE => return PosixWriteError.BrokenPipe,
412 else => return unexpectedErrorPosix(err),
413 }
414 },
415 else => @compileError("Unsupported OS"),
416 }
417}
418
303419pub const PosixOpenError = error{
304420 OutOfMemory,
305421 AccessDenied,
......@@ -853,7 +969,7 @@ pub fn copyFile(allocator: *Allocator, source_path: []const u8, dest_path: []con
853969/// Guaranteed to be atomic. However until https://patchwork.kernel.org/patch/9636735/ is
854970/// merged and readily available,
855971/// there is a possibility of power loss or application termination leaving temporary files present
856pub fn copyFileMode(allocator: *Allocator, source_path: []const u8, dest_path: []const u8, mode: FileMode) !void {
972pub fn copyFileMode(allocator: *Allocator, source_path: []const u8, dest_path: []const u8, mode: File.Mode) !void {
857973 var in_file = try os.File.openRead(allocator, source_path);
858974 defer in_file.close();
859975
......@@ -879,7 +995,7 @@ pub const AtomicFile = struct {
879995
880996 /// dest_path must remain valid for the lifetime of AtomicFile
881997 /// call finish to atomically replace dest_path with contents
882 pub fn init(allocator: *Allocator, dest_path: []const u8, mode: FileMode) !AtomicFile {
998 pub fn init(allocator: *Allocator, dest_path: []const u8, mode: File.Mode) !AtomicFile {
883999 const dirname = os.path.dirname(dest_path);
8841000
8851001 var rand_buf: [12]u8 = undefined;
......@@ -2943,3 +3059,44 @@ pub fn bsdKEvent(
29433059 }
29443060 }
29453061}
3062
3063pub fn linuxINotifyInit1(flags: u32) !i32 {
3064 const rc = linux.inotify_init1(flags);
3065 const err = posix.getErrno(rc);
3066 switch (err) {
3067 0 => return @intCast(i32, rc),
3068 posix.EINVAL => unreachable,
3069 posix.EMFILE => return error.ProcessFdQuotaExceeded,
3070 posix.ENFILE => return error.SystemFdQuotaExceeded,
3071 posix.ENOMEM => return error.SystemResources,
3072 else => return unexpectedErrorPosix(err),
3073 }
3074}
3075
3076pub fn linuxINotifyAddWatchC(inotify_fd: i32, pathname: [*]const u8, mask: u32) !i32 {
3077 const rc = linux.inotify_add_watch(inotify_fd, pathname, mask);
3078 const err = posix.getErrno(rc);
3079 switch (err) {
3080 0 => return @intCast(i32, rc),
3081 posix.EACCES => return error.AccessDenied,
3082 posix.EBADF => unreachable,
3083 posix.EFAULT => unreachable,
3084 posix.EINVAL => unreachable,
3085 posix.ENAMETOOLONG => return error.NameTooLong,
3086 posix.ENOENT => return error.FileNotFound,
3087 posix.ENOMEM => return error.SystemResources,
3088 posix.ENOSPC => return error.UserResourceLimitReached,
3089 else => return unexpectedErrorPosix(err),
3090 }
3091}
3092
3093pub fn linuxINotifyRmWatch(inotify_fd: i32, wd: i32) !void {
3094 const rc = linux.inotify_rm_watch(inotify_fd, wd);
3095 const err = posix.getErrno(rc);
3096 switch (err) {
3097 0 => return rc,
3098 posix.EBADF => unreachable,
3099 posix.EINVAL => unreachable,
3100 else => unreachable,
3101 }
3102}
std/os/linux/index.zig+68
......@@ -567,6 +567,37 @@ pub const MNT_DETACH = 2;
567567pub const MNT_EXPIRE = 4;
568568pub const UMOUNT_NOFOLLOW = 8;
569569
570pub const IN_CLOEXEC = O_CLOEXEC;
571pub const IN_NONBLOCK = O_NONBLOCK;
572
573pub const IN_ACCESS = 0x00000001;
574pub const IN_MODIFY = 0x00000002;
575pub const IN_ATTRIB = 0x00000004;
576pub const IN_CLOSE_WRITE = 0x00000008;
577pub const IN_CLOSE_NOWRITE = 0x00000010;
578pub const IN_CLOSE = IN_CLOSE_WRITE | IN_CLOSE_NOWRITE;
579pub const IN_OPEN = 0x00000020;
580pub const IN_MOVED_FROM = 0x00000040;
581pub const IN_MOVED_TO = 0x00000080;
582pub const IN_MOVE = IN_MOVED_FROM | IN_MOVED_TO;
583pub const IN_CREATE = 0x00000100;
584pub const IN_DELETE = 0x00000200;
585pub const IN_DELETE_SELF = 0x00000400;
586pub const IN_MOVE_SELF = 0x00000800;
587pub const IN_ALL_EVENTS = 0x00000fff;
588
589pub const IN_UNMOUNT = 0x00002000;
590pub const IN_Q_OVERFLOW = 0x00004000;
591pub const IN_IGNORED = 0x00008000;
592
593pub const IN_ONLYDIR = 0x01000000;
594pub const IN_DONT_FOLLOW = 0x02000000;
595pub const IN_EXCL_UNLINK = 0x04000000;
596pub const IN_MASK_ADD = 0x20000000;
597
598pub const IN_ISDIR = 0x40000000;
599pub const IN_ONESHOT = 0x80000000;
600
570601pub const S_IFMT = 0o170000;
571602
572603pub const S_IFDIR = 0o040000;
......@@ -692,6 +723,10 @@ pub fn futex_wait(uaddr: usize, futex_op: u32, val: i32, timeout: ?*timespec) us
692723 return syscall4(SYS_futex, uaddr, futex_op, @bitCast(u32, val), @ptrToInt(timeout));
693724}
694725
726pub fn futex_wake(uaddr: usize, futex_op: u32, val: i32) usize {
727 return syscall3(SYS_futex, uaddr, futex_op, @bitCast(u32, val));
728}
729
695730pub fn getcwd(buf: [*]u8, size: usize) usize {
696731 return syscall2(SYS_getcwd, @ptrToInt(buf), size);
697732}
......@@ -700,6 +735,18 @@ pub fn getdents(fd: i32, dirp: [*]u8, count: usize) usize {
700735 return syscall3(SYS_getdents, @intCast(usize, fd), @ptrToInt(dirp), count);
701736}
702737
738pub fn inotify_init1(flags: u32) usize {
739 return syscall1(SYS_inotify_init1, flags);
740}
741
742pub fn inotify_add_watch(fd: i32, pathname: [*]const u8, mask: u32) usize {
743 return syscall3(SYS_inotify_add_watch, @intCast(usize, fd), @ptrToInt(pathname), mask);
744}
745
746pub fn inotify_rm_watch(fd: i32, wd: i32) usize {
747 return syscall2(SYS_inotify_rm_watch, @intCast(usize, fd), @intCast(usize, wd));
748}
749
703750pub fn isatty(fd: i32) bool {
704751 var wsz: winsize = undefined;
705752 return syscall3(SYS_ioctl, @intCast(usize, fd), TIOCGWINSZ, @ptrToInt(&wsz)) == 0;
......@@ -742,6 +789,14 @@ pub fn read(fd: i32, buf: [*]u8, count: usize) usize {
742789 return syscall3(SYS_read, @intCast(usize, fd), @ptrToInt(buf), count);
743790}
744791
792pub fn preadv(fd: i32, iov: [*]const iovec, count: usize, offset: u64) usize {
793 return syscall4(SYS_preadv, @intCast(usize, fd), @ptrToInt(iov), count, offset);
794}
795
796pub fn pwritev(fd: i32, iov: [*]const iovec_const, count: usize, offset: u64) usize {
797 return syscall4(SYS_pwritev, @intCast(usize, fd), @ptrToInt(iov), count, offset);
798}
799
745800// TODO https://github.com/ziglang/zig/issues/265
746801pub fn rmdir(path: [*]const u8) usize {
747802 return syscall1(SYS_rmdir, @ptrToInt(path));
......@@ -1064,6 +1119,11 @@ pub const iovec = extern struct {
10641119 iov_len: usize,
10651120};
10661121
1122pub const iovec_const = extern struct {
1123 iov_base: [*]const u8,
1124 iov_len: usize,
1125};
1126
10671127pub fn getsockname(fd: i32, noalias addr: *sockaddr, noalias len: *socklen_t) usize {
10681128 return syscall3(SYS_getsockname, @intCast(usize, fd), @ptrToInt(addr), @ptrToInt(len));
10691129}
......@@ -1372,6 +1432,14 @@ pub fn capset(hdrp: *cap_user_header_t, datap: *const cap_user_data_t) usize {
13721432 return syscall2(SYS_capset, @ptrToInt(hdrp), @ptrToInt(datap));
13731433}
13741434
1435pub const inotify_event = extern struct {
1436 wd: i32,
1437 mask: u32,
1438 cookie: u32,
1439 len: u32,
1440 //name: [?]u8,
1441};
1442
13751443test "import" {
13761444 if (builtin.os == builtin.Os.linux) {
13771445 _ = @import("test.zig");
std/os/path.zig+1-1
......@@ -506,7 +506,7 @@ pub fn resolveWindows(allocator: *Allocator, paths: []const []const u8) ![]u8 {
506506 result_index += 1;
507507 }
508508
509 return result[0..result_index];
509 return allocator.shrink(u8, result, result_index);
510510}
511511
512512/// This function is like a series of `cd` statements executed one after another.
std/os/windows/index.zig+15-2
......@@ -67,8 +67,9 @@ pub const INVALID_FILE_ATTRIBUTES = DWORD(@maxValue(DWORD));
6767pub const OVERLAPPED = extern struct {
6868 Internal: ULONG_PTR,
6969 InternalHigh: ULONG_PTR,
70 Pointer: PVOID,
71 hEvent: HANDLE,
70 Offset: DWORD,
71 OffsetHigh: DWORD,
72 hEvent: ?HANDLE,
7273};
7374pub const LPOVERLAPPED = *OVERLAPPED;
7475
......@@ -350,3 +351,15 @@ pub const E_ACCESSDENIED = @bitCast(c_long, c_ulong(0x80070005));
350351pub const E_HANDLE = @bitCast(c_long, c_ulong(0x80070006));
351352pub const E_OUTOFMEMORY = @bitCast(c_long, c_ulong(0x8007000E));
352353pub const E_INVALIDARG = @bitCast(c_long, c_ulong(0x80070057));
354
355pub const FILE_FLAG_BACKUP_SEMANTICS = 0x02000000;
356pub const FILE_FLAG_DELETE_ON_CLOSE = 0x04000000;
357pub const FILE_FLAG_NO_BUFFERING = 0x20000000;
358pub const FILE_FLAG_OPEN_NO_RECALL = 0x00100000;
359pub const FILE_FLAG_OPEN_REPARSE_POINT = 0x00200000;
360pub const FILE_FLAG_OVERLAPPED = 0x40000000;
361pub const FILE_FLAG_POSIX_SEMANTICS = 0x0100000;
362pub const FILE_FLAG_RANDOM_ACCESS = 0x10000000;
363pub const FILE_FLAG_SESSION_AWARE = 0x00800000;
364pub const FILE_FLAG_SEQUENTIAL_SCAN = 0x08000000;
365pub const FILE_FLAG_WRITE_THROUGH = 0x80000000;
std/os/windows/kernel32.zig+62-5
......@@ -1,5 +1,8 @@
11use @import("index.zig");
22
3
4pub extern "kernel32" stdcallcc fn CancelIoEx(hFile: HANDLE, lpOverlapped: LPOVERLAPPED) BOOL;
5
36pub extern "kernel32" stdcallcc fn CloseHandle(hObject: HANDLE) BOOL;
47
58pub extern "kernel32" stdcallcc fn CreateDirectoryA(
......@@ -8,7 +11,17 @@ pub extern "kernel32" stdcallcc fn CreateDirectoryA(
811) BOOL;
912
1013pub extern "kernel32" stdcallcc fn CreateFileA(
11 lpFileName: LPCSTR,
14 lpFileName: [*]const u8, // TODO null terminated pointer type
15 dwDesiredAccess: DWORD,
16 dwShareMode: DWORD,
17 lpSecurityAttributes: ?LPSECURITY_ATTRIBUTES,
18 dwCreationDisposition: DWORD,
19 dwFlagsAndAttributes: DWORD,
20 hTemplateFile: ?HANDLE,
21) HANDLE;
22
23pub extern "kernel32" stdcallcc fn CreateFileW(
24 lpFileName: [*]const u16, // TODO null terminated pointer type
1225 dwDesiredAccess: DWORD,
1326 dwShareMode: DWORD,
1427 lpSecurityAttributes: ?LPSECURITY_ATTRIBUTES,
......@@ -94,6 +107,9 @@ pub extern "kernel32" stdcallcc fn GetFinalPathNameByHandleA(
94107 dwFlags: DWORD,
95108) DWORD;
96109
110
111pub extern "kernel32" stdcallcc fn GetOverlappedResult(hFile: HANDLE, lpOverlapped: *OVERLAPPED, lpNumberOfBytesTransferred: *DWORD, bWait: BOOL) BOOL;
112
97113pub extern "kernel32" stdcallcc fn GetProcessHeap() ?HANDLE;
98114pub extern "kernel32" stdcallcc fn GetQueuedCompletionStatus(CompletionPort: HANDLE, lpNumberOfBytesTransferred: LPDWORD, lpCompletionKey: *ULONG_PTR, lpOverlapped: *?*OVERLAPPED, dwMilliseconds: DWORD) BOOL;
99115
......@@ -104,7 +120,6 @@ pub extern "kernel32" stdcallcc fn HeapCreate(flOptions: DWORD, dwInitialSize: S
104120pub extern "kernel32" stdcallcc fn HeapDestroy(hHeap: HANDLE) BOOL;
105121pub extern "kernel32" stdcallcc fn HeapReAlloc(hHeap: HANDLE, dwFlags: DWORD, lpMem: *c_void, dwBytes: SIZE_T) ?*c_void;
106122pub extern "kernel32" stdcallcc fn HeapSize(hHeap: HANDLE, dwFlags: DWORD, lpMem: *const c_void) SIZE_T;
107pub extern "kernel32" stdcallcc fn HeapValidate(hHeap: HANDLE, dwFlags: DWORD, lpMem: *const c_void) BOOL;
108123pub extern "kernel32" stdcallcc fn HeapCompact(hHeap: HANDLE, dwFlags: DWORD) SIZE_T;
109124pub extern "kernel32" stdcallcc fn HeapSummary(hHeap: HANDLE, dwFlags: DWORD, lpSummary: LPHEAP_SUMMARY) BOOL;
110125
......@@ -114,6 +129,8 @@ pub extern "kernel32" stdcallcc fn HeapAlloc(hHeap: HANDLE, dwFlags: DWORD, dwBy
114129
115130pub extern "kernel32" stdcallcc fn HeapFree(hHeap: HANDLE, dwFlags: DWORD, lpMem: *c_void) BOOL;
116131
132pub extern "kernel32" stdcallcc fn HeapValidate(hHeap: HANDLE, dwFlags: DWORD, lpMem: ?*const c_void) BOOL;
133
117134pub extern "kernel32" stdcallcc fn MoveFileExA(
118135 lpExistingFileName: LPCSTR,
119136 lpNewFileName: LPCSTR,
......@@ -126,11 +143,22 @@ pub extern "kernel32" stdcallcc fn QueryPerformanceCounter(lpPerformanceCount: *
126143
127144pub extern "kernel32" stdcallcc fn QueryPerformanceFrequency(lpFrequency: *LARGE_INTEGER) BOOL;
128145
146pub extern "kernel32" stdcallcc fn ReadDirectoryChangesW(
147 hDirectory: HANDLE,
148 lpBuffer: [*]align(@alignOf(FILE_NOTIFY_INFORMATION)) u8,
149 nBufferLength: DWORD,
150 bWatchSubtree: BOOL,
151 dwNotifyFilter: DWORD,
152 lpBytesReturned: ?*DWORD,
153 lpOverlapped: ?*OVERLAPPED,
154 lpCompletionRoutine: LPOVERLAPPED_COMPLETION_ROUTINE,
155) BOOL;
156
129157pub extern "kernel32" stdcallcc fn ReadFile(
130158 in_hFile: HANDLE,
131 out_lpBuffer: *c_void,
159 out_lpBuffer: [*]u8,
132160 in_nNumberOfBytesToRead: DWORD,
133 out_lpNumberOfBytesRead: *DWORD,
161 out_lpNumberOfBytesRead: ?*DWORD,
134162 in_out_lpOverlapped: ?*OVERLAPPED,
135163) BOOL;
136164
......@@ -153,13 +181,42 @@ pub extern "kernel32" stdcallcc fn WaitForSingleObject(hHandle: HANDLE, dwMillis
153181
154182pub extern "kernel32" stdcallcc fn WriteFile(
155183 in_hFile: HANDLE,
156 in_lpBuffer: *const c_void,
184 in_lpBuffer: [*]const u8,
157185 in_nNumberOfBytesToWrite: DWORD,
158186 out_lpNumberOfBytesWritten: ?*DWORD,
159187 in_out_lpOverlapped: ?*OVERLAPPED,
160188) BOOL;
161189
190pub extern "kernel32" stdcallcc fn WriteFileEx(hFile: HANDLE, lpBuffer: [*]const u8, nNumberOfBytesToWrite: DWORD, lpOverlapped: LPOVERLAPPED, lpCompletionRoutine: LPOVERLAPPED_COMPLETION_ROUTINE) BOOL;
191
162192//TODO: call unicode versions instead of relying on ANSI code page
163193pub extern "kernel32" stdcallcc fn LoadLibraryA(lpLibFileName: LPCSTR) ?HMODULE;
164194
165195pub extern "kernel32" stdcallcc fn FreeLibrary(hModule: HMODULE) BOOL;
196
197
198pub const FILE_NOTIFY_INFORMATION = extern struct {
199 NextEntryOffset: DWORD,
200 Action: DWORD,
201 FileNameLength: DWORD,
202 FileName: [1]WCHAR,
203};
204
205pub const FILE_ACTION_ADDED = 0x00000001;
206pub const FILE_ACTION_REMOVED = 0x00000002;
207pub const FILE_ACTION_MODIFIED = 0x00000003;
208pub const FILE_ACTION_RENAMED_OLD_NAME = 0x00000004;
209pub const FILE_ACTION_RENAMED_NEW_NAME = 0x00000005;
210
211pub const LPOVERLAPPED_COMPLETION_ROUTINE = ?extern fn(DWORD, DWORD, *OVERLAPPED) void;
212
213pub const FILE_LIST_DIRECTORY = 1;
214
215pub const FILE_NOTIFY_CHANGE_CREATION = 64;
216pub const FILE_NOTIFY_CHANGE_SIZE = 8;
217pub const FILE_NOTIFY_CHANGE_SECURITY = 256;
218pub const FILE_NOTIFY_CHANGE_LAST_ACCESS = 32;
219pub const FILE_NOTIFY_CHANGE_LAST_WRITE = 16;
220pub const FILE_NOTIFY_CHANGE_DIR_NAME = 2;
221pub const FILE_NOTIFY_CHANGE_FILE_NAME = 1;
222pub const FILE_NOTIFY_CHANGE_ATTRIBUTES = 4;
std/os/windows/util.zig+13-10
......@@ -36,20 +36,19 @@ pub fn windowsClose(handle: windows.HANDLE) void {
3636pub const WriteError = error{
3737 SystemResources,
3838 OperationAborted,
39 IoPending,
4039 BrokenPipe,
4140 Unexpected,
4241};
4342
4443pub fn windowsWrite(handle: windows.HANDLE, bytes: []const u8) WriteError!void {
45 if (windows.WriteFile(handle, @ptrCast(*const c_void, bytes.ptr), @intCast(u32, bytes.len), null, null) == 0) {
44 if (windows.WriteFile(handle, bytes.ptr, @intCast(u32, bytes.len), null, null) == 0) {
4645 const err = windows.GetLastError();
4746 return switch (err) {
4847 windows.ERROR.INVALID_USER_BUFFER => WriteError.SystemResources,
4948 windows.ERROR.NOT_ENOUGH_MEMORY => WriteError.SystemResources,
5049 windows.ERROR.OPERATION_ABORTED => WriteError.OperationAborted,
5150 windows.ERROR.NOT_ENOUGH_QUOTA => WriteError.SystemResources,
52 windows.ERROR.IO_PENDING => WriteError.IoPending,
51 windows.ERROR.IO_PENDING => unreachable,
5352 windows.ERROR.BROKEN_PIPE => WriteError.BrokenPipe,
5453 else => os.unexpectedErrorWindows(err),
5554 };
......@@ -221,6 +220,7 @@ pub fn windowsCreateIoCompletionPort(file_handle: windows.HANDLE, existing_compl
221220 const handle = windows.CreateIoCompletionPort(file_handle, existing_completion_port, completion_key, concurrent_thread_count) orelse {
222221 const err = windows.GetLastError();
223222 switch (err) {
223 windows.ERROR.INVALID_PARAMETER => unreachable,
224224 else => return os.unexpectedErrorWindows(err),
225225 }
226226 };
......@@ -238,21 +238,24 @@ pub fn windowsPostQueuedCompletionStatus(completion_port: windows.HANDLE, bytes_
238238 }
239239}
240240
241pub const WindowsWaitResult = error{
241pub const WindowsWaitResult = enum{
242242 Normal,
243243 Aborted,
244 Cancelled,
244245};
245246
246247pub fn windowsGetQueuedCompletionStatus(completion_port: windows.HANDLE, bytes_transferred_count: *windows.DWORD, lpCompletionKey: *usize, lpOverlapped: *?*windows.OVERLAPPED, dwMilliseconds: windows.DWORD) WindowsWaitResult {
247248 if (windows.GetQueuedCompletionStatus(completion_port, bytes_transferred_count, lpCompletionKey, lpOverlapped, dwMilliseconds) == windows.FALSE) {
248 if (std.debug.runtime_safety) {
249 const err = windows.GetLastError();
250 if (err != windows.ERROR.ABANDONED_WAIT_0) {
251 std.debug.warn("err: {}\n", err);
249 const err = windows.GetLastError();
250 switch (err) {
251 windows.ERROR.ABANDONED_WAIT_0 => return WindowsWaitResult.Aborted,
252 windows.ERROR.OPERATION_ABORTED => return WindowsWaitResult.Cancelled,
253 else => {
254 if (std.debug.runtime_safety) {
255 std.debug.panic("unexpected error: {}\n", err);
256 }
252257 }
253 assert(err == windows.ERROR.ABANDONED_WAIT_0);
254258 }
255 return WindowsWaitResult.Aborted;
256259 }
257260 return WindowsWaitResult.Normal;
258261}
std/segmented_list.zig+13-5
......@@ -2,7 +2,7 @@ const std = @import("index.zig");
22const assert = std.debug.assert;
33const Allocator = std.mem.Allocator;
44
5// Imagine that `fn at(self: &Self, index: usize) &T` is a customer asking for a box
5// Imagine that `fn at(self: *Self, index: usize) &T` is a customer asking for a box
66// from a warehouse, based on a flat array, boxes ordered from 0 to N - 1.
77// But the warehouse actually stores boxes in shelves of increasing powers of 2 sizes.
88// So when the customer requests a box index, we have to translate it to shelf index
......@@ -93,6 +93,14 @@ pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type
9393
9494 pub const prealloc_count = prealloc_item_count;
9595
96 fn AtType(comptime SelfType: type) type {
97 if (@typeInfo(SelfType).Pointer.is_const) {
98 return *const T;
99 } else {
100 return *T;
101 }
102 }
103
96104 /// Deinitialize with `deinit`
97105 pub fn init(allocator: *Allocator) Self {
98106 return Self{
......@@ -109,7 +117,7 @@ pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type
109117 self.* = undefined;
110118 }
111119
112 pub fn at(self: *Self, i: usize) *T {
120 pub fn at(self: var, i: usize) AtType(@typeOf(self)) {
113121 assert(i < self.len);
114122 return self.uncheckedAt(i);
115123 }
......@@ -133,7 +141,7 @@ pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type
133141 if (self.len == 0) return null;
134142
135143 const index = self.len - 1;
136 const result = self.uncheckedAt(index).*;
144 const result = uncheckedAt(self, index).*;
137145 self.len = index;
138146 return result;
139147 }
......@@ -141,7 +149,7 @@ pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type
141149 pub fn addOne(self: *Self) !*T {
142150 const new_length = self.len + 1;
143151 try self.growCapacity(new_length);
144 const result = self.uncheckedAt(self.len);
152 const result = uncheckedAt(self, self.len);
145153 self.len = new_length;
146154 return result;
147155 }
......@@ -193,7 +201,7 @@ pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type
193201 self.dynamic_segments = self.allocator.shrink([*]T, self.dynamic_segments, new_cap_shelf_count);
194202 }
195203
196 pub fn uncheckedAt(self: *Self, index: usize) *T {
204 pub fn uncheckedAt(self: var, index: usize) AtType(@typeOf(self)) {
197205 if (index < prealloc_item_count) {
198206 return &self.prealloc_segment[index];
199207 }
std/special/build_runner.zig+2-2
......@@ -72,10 +72,10 @@ pub fn main() !void {
7272 if (mem.indexOfScalar(u8, option_contents, '=')) |name_end| {
7373 const option_name = option_contents[0..name_end];
7474 const option_value = option_contents[name_end + 1 ..];
75 if (builder.addUserInputOption(option_name, option_value))
75 if (try builder.addUserInputOption(option_name, option_value))
7676 return usageAndErr(&builder, false, try stderr_stream);
7777 } else {
78 if (builder.addUserInputFlag(option_contents))
78 if (try builder.addUserInputFlag(option_contents))
7979 return usageAndErr(&builder, false, try stderr_stream);
8080 }
8181 } else if (mem.startsWith(u8, arg, "-")) {
std/unicode.zig+19-1
......@@ -188,6 +188,7 @@ pub const Utf8View = struct {
188188 return Utf8View{ .bytes = s };
189189 }
190190
191 /// TODO: https://github.com/ziglang/zig/issues/425
191192 pub fn initComptime(comptime s: []const u8) Utf8View {
192193 if (comptime init(s)) |r| {
193194 return r;
......@@ -199,7 +200,7 @@ pub const Utf8View = struct {
199200 }
200201 }
201202
202 pub fn iterator(s: *const Utf8View) Utf8Iterator {
203 pub fn iterator(s: Utf8View) Utf8Iterator {
203204 return Utf8Iterator{
204205 .bytes = s.bytes,
205206 .i = 0,
......@@ -530,3 +531,20 @@ test "utf16leToUtf8" {
530531 assert(mem.eql(u8, utf8, "\xf4\x8f\xb0\x80"));
531532 }
532533}
534
535/// TODO support codepoints bigger than 16 bits
536/// TODO type for null terminated pointer
537pub fn utf8ToUtf16LeWithNull(allocator: *mem.Allocator, utf8: []const u8) ![]u16 {
538 var result = std.ArrayList(u16).init(allocator);
539 // optimistically guess that it will not require surrogate pairs
540 try result.ensureCapacity(utf8.len + 1);
541
542 const view = try Utf8View.init(utf8);
543 var it = view.iterator();
544 while (it.nextCodepoint()) |codepoint| {
545 try result.append(@intCast(u16, codepoint)); // TODO surrogate pairs
546 }
547
548 try result.append(0);
549 return result.toOwnedSlice();
550}
std/zig/ast.zig+112-106
......@@ -32,6 +32,12 @@ pub const Tree = struct {
3232 return self.source[token.start..token.end];
3333 }
3434
35 pub fn getNodeSource(self: *const Tree, node: *const Node) []const u8 {
36 const first_token = self.tokens.at(node.firstToken());
37 const last_token = self.tokens.at(node.lastToken());
38 return self.source[first_token.start..last_token.end];
39 }
40
3541 pub const Location = struct {
3642 line: usize,
3743 column: usize,
......@@ -338,7 +344,7 @@ pub const Node = struct {
338344 unreachable;
339345 }
340346
341 pub fn firstToken(base: *Node) TokenIndex {
347 pub fn firstToken(base: *const Node) TokenIndex {
342348 comptime var i = 0;
343349 inline while (i < @memberCount(Id)) : (i += 1) {
344350 if (base.id == @field(Id, @memberName(Id, i))) {
......@@ -349,7 +355,7 @@ pub const Node = struct {
349355 unreachable;
350356 }
351357
352 pub fn lastToken(base: *Node) TokenIndex {
358 pub fn lastToken(base: *const Node) TokenIndex {
353359 comptime var i = 0;
354360 inline while (i < @memberCount(Id)) : (i += 1) {
355361 if (base.id == @field(Id, @memberName(Id, i))) {
......@@ -473,11 +479,11 @@ pub const Node = struct {
473479 return null;
474480 }
475481
476 pub fn firstToken(self: *Root) TokenIndex {
482 pub fn firstToken(self: *const Root) TokenIndex {
477483 return if (self.decls.len == 0) self.eof_token else (self.decls.at(0).*).firstToken();
478484 }
479485
480 pub fn lastToken(self: *Root) TokenIndex {
486 pub fn lastToken(self: *const Root) TokenIndex {
481487 return if (self.decls.len == 0) self.eof_token else (self.decls.at(self.decls.len - 1).*).lastToken();
482488 }
483489 };
......@@ -518,7 +524,7 @@ pub const Node = struct {
518524 return null;
519525 }
520526
521 pub fn firstToken(self: *VarDecl) TokenIndex {
527 pub fn firstToken(self: *const VarDecl) TokenIndex {
522528 if (self.visib_token) |visib_token| return visib_token;
523529 if (self.comptime_token) |comptime_token| return comptime_token;
524530 if (self.extern_export_token) |extern_export_token| return extern_export_token;
......@@ -526,7 +532,7 @@ pub const Node = struct {
526532 return self.mut_token;
527533 }
528534
529 pub fn lastToken(self: *VarDecl) TokenIndex {
535 pub fn lastToken(self: *const VarDecl) TokenIndex {
530536 return self.semicolon_token;
531537 }
532538 };
......@@ -548,12 +554,12 @@ pub const Node = struct {
548554 return null;
549555 }
550556
551 pub fn firstToken(self: *Use) TokenIndex {
557 pub fn firstToken(self: *const Use) TokenIndex {
552558 if (self.visib_token) |visib_token| return visib_token;
553559 return self.use_token;
554560 }
555561
556 pub fn lastToken(self: *Use) TokenIndex {
562 pub fn lastToken(self: *const Use) TokenIndex {
557563 return self.semicolon_token;
558564 }
559565 };
......@@ -575,11 +581,11 @@ pub const Node = struct {
575581 return null;
576582 }
577583
578 pub fn firstToken(self: *ErrorSetDecl) TokenIndex {
584 pub fn firstToken(self: *const ErrorSetDecl) TokenIndex {
579585 return self.error_token;
580586 }
581587
582 pub fn lastToken(self: *ErrorSetDecl) TokenIndex {
588 pub fn lastToken(self: *const ErrorSetDecl) TokenIndex {
583589 return self.rbrace_token;
584590 }
585591 };
......@@ -618,14 +624,14 @@ pub const Node = struct {
618624 return null;
619625 }
620626
621 pub fn firstToken(self: *ContainerDecl) TokenIndex {
627 pub fn firstToken(self: *const ContainerDecl) TokenIndex {
622628 if (self.layout_token) |layout_token| {
623629 return layout_token;
624630 }
625631 return self.kind_token;
626632 }
627633
628 pub fn lastToken(self: *ContainerDecl) TokenIndex {
634 pub fn lastToken(self: *const ContainerDecl) TokenIndex {
629635 return self.rbrace_token;
630636 }
631637 };
......@@ -646,12 +652,12 @@ pub const Node = struct {
646652 return null;
647653 }
648654
649 pub fn firstToken(self: *StructField) TokenIndex {
655 pub fn firstToken(self: *const StructField) TokenIndex {
650656 if (self.visib_token) |visib_token| return visib_token;
651657 return self.name_token;
652658 }
653659
654 pub fn lastToken(self: *StructField) TokenIndex {
660 pub fn lastToken(self: *const StructField) TokenIndex {
655661 return self.type_expr.lastToken();
656662 }
657663 };
......@@ -679,11 +685,11 @@ pub const Node = struct {
679685 return null;
680686 }
681687
682 pub fn firstToken(self: *UnionTag) TokenIndex {
688 pub fn firstToken(self: *const UnionTag) TokenIndex {
683689 return self.name_token;
684690 }
685691
686 pub fn lastToken(self: *UnionTag) TokenIndex {
692 pub fn lastToken(self: *const UnionTag) TokenIndex {
687693 if (self.value_expr) |value_expr| {
688694 return value_expr.lastToken();
689695 }
......@@ -712,11 +718,11 @@ pub const Node = struct {
712718 return null;
713719 }
714720
715 pub fn firstToken(self: *EnumTag) TokenIndex {
721 pub fn firstToken(self: *const EnumTag) TokenIndex {
716722 return self.name_token;
717723 }
718724
719 pub fn lastToken(self: *EnumTag) TokenIndex {
725 pub fn lastToken(self: *const EnumTag) TokenIndex {
720726 if (self.value) |value| {
721727 return value.lastToken();
722728 }
......@@ -741,11 +747,11 @@ pub const Node = struct {
741747 return null;
742748 }
743749
744 pub fn firstToken(self: *ErrorTag) TokenIndex {
750 pub fn firstToken(self: *const ErrorTag) TokenIndex {
745751 return self.name_token;
746752 }
747753
748 pub fn lastToken(self: *ErrorTag) TokenIndex {
754 pub fn lastToken(self: *const ErrorTag) TokenIndex {
749755 return self.name_token;
750756 }
751757 };
......@@ -758,11 +764,11 @@ pub const Node = struct {
758764 return null;
759765 }
760766
761 pub fn firstToken(self: *Identifier) TokenIndex {
767 pub fn firstToken(self: *const Identifier) TokenIndex {
762768 return self.token;
763769 }
764770
765 pub fn lastToken(self: *Identifier) TokenIndex {
771 pub fn lastToken(self: *const Identifier) TokenIndex {
766772 return self.token;
767773 }
768774 };
......@@ -784,11 +790,11 @@ pub const Node = struct {
784790 return null;
785791 }
786792
787 pub fn firstToken(self: *AsyncAttribute) TokenIndex {
793 pub fn firstToken(self: *const AsyncAttribute) TokenIndex {
788794 return self.async_token;
789795 }
790796
791 pub fn lastToken(self: *AsyncAttribute) TokenIndex {
797 pub fn lastToken(self: *const AsyncAttribute) TokenIndex {
792798 if (self.rangle_bracket) |rangle_bracket| {
793799 return rangle_bracket;
794800 }
......@@ -856,7 +862,7 @@ pub const Node = struct {
856862 return null;
857863 }
858864
859 pub fn firstToken(self: *FnProto) TokenIndex {
865 pub fn firstToken(self: *const FnProto) TokenIndex {
860866 if (self.visib_token) |visib_token| return visib_token;
861867 if (self.async_attr) |async_attr| return async_attr.firstToken();
862868 if (self.extern_export_inline_token) |extern_export_inline_token| return extern_export_inline_token;
......@@ -865,7 +871,7 @@ pub const Node = struct {
865871 return self.fn_token;
866872 }
867873
868 pub fn lastToken(self: *FnProto) TokenIndex {
874 pub fn lastToken(self: *const FnProto) TokenIndex {
869875 if (self.body_node) |body_node| return body_node.lastToken();
870876 switch (self.return_type) {
871877 // TODO allow this and next prong to share bodies since the types are the same
......@@ -896,11 +902,11 @@ pub const Node = struct {
896902 return null;
897903 }
898904
899 pub fn firstToken(self: *PromiseType) TokenIndex {
905 pub fn firstToken(self: *const PromiseType) TokenIndex {
900906 return self.promise_token;
901907 }
902908
903 pub fn lastToken(self: *PromiseType) TokenIndex {
909 pub fn lastToken(self: *const PromiseType) TokenIndex {
904910 if (self.result) |result| return result.return_type.lastToken();
905911 return self.promise_token;
906912 }
......@@ -923,14 +929,14 @@ pub const Node = struct {
923929 return null;
924930 }
925931
926 pub fn firstToken(self: *ParamDecl) TokenIndex {
932 pub fn firstToken(self: *const ParamDecl) TokenIndex {
927933 if (self.comptime_token) |comptime_token| return comptime_token;
928934 if (self.noalias_token) |noalias_token| return noalias_token;
929935 if (self.name_token) |name_token| return name_token;
930936 return self.type_node.firstToken();
931937 }
932938
933 pub fn lastToken(self: *ParamDecl) TokenIndex {
939 pub fn lastToken(self: *const ParamDecl) TokenIndex {
934940 if (self.var_args_token) |var_args_token| return var_args_token;
935941 return self.type_node.lastToken();
936942 }
......@@ -954,7 +960,7 @@ pub const Node = struct {
954960 return null;
955961 }
956962
957 pub fn firstToken(self: *Block) TokenIndex {
963 pub fn firstToken(self: *const Block) TokenIndex {
958964 if (self.label) |label| {
959965 return label;
960966 }
......@@ -962,7 +968,7 @@ pub const Node = struct {
962968 return self.lbrace;
963969 }
964970
965 pub fn lastToken(self: *Block) TokenIndex {
971 pub fn lastToken(self: *const Block) TokenIndex {
966972 return self.rbrace;
967973 }
968974 };
......@@ -981,11 +987,11 @@ pub const Node = struct {
981987 return null;
982988 }
983989
984 pub fn firstToken(self: *Defer) TokenIndex {
990 pub fn firstToken(self: *const Defer) TokenIndex {
985991 return self.defer_token;
986992 }
987993
988 pub fn lastToken(self: *Defer) TokenIndex {
994 pub fn lastToken(self: *const Defer) TokenIndex {
989995 return self.expr.lastToken();
990996 }
991997 };
......@@ -1005,11 +1011,11 @@ pub const Node = struct {
10051011 return null;
10061012 }
10071013
1008 pub fn firstToken(self: *Comptime) TokenIndex {
1014 pub fn firstToken(self: *const Comptime) TokenIndex {
10091015 return self.comptime_token;
10101016 }
10111017
1012 pub fn lastToken(self: *Comptime) TokenIndex {
1018 pub fn lastToken(self: *const Comptime) TokenIndex {
10131019 return self.expr.lastToken();
10141020 }
10151021 };
......@@ -1029,11 +1035,11 @@ pub const Node = struct {
10291035 return null;
10301036 }
10311037
1032 pub fn firstToken(self: *Payload) TokenIndex {
1038 pub fn firstToken(self: *const Payload) TokenIndex {
10331039 return self.lpipe;
10341040 }
10351041
1036 pub fn lastToken(self: *Payload) TokenIndex {
1042 pub fn lastToken(self: *const Payload) TokenIndex {
10371043 return self.rpipe;
10381044 }
10391045 };
......@@ -1054,11 +1060,11 @@ pub const Node = struct {
10541060 return null;
10551061 }
10561062
1057 pub fn firstToken(self: *PointerPayload) TokenIndex {
1063 pub fn firstToken(self: *const PointerPayload) TokenIndex {
10581064 return self.lpipe;
10591065 }
10601066
1061 pub fn lastToken(self: *PointerPayload) TokenIndex {
1067 pub fn lastToken(self: *const PointerPayload) TokenIndex {
10621068 return self.rpipe;
10631069 }
10641070 };
......@@ -1085,11 +1091,11 @@ pub const Node = struct {
10851091 return null;
10861092 }
10871093
1088 pub fn firstToken(self: *PointerIndexPayload) TokenIndex {
1094 pub fn firstToken(self: *const PointerIndexPayload) TokenIndex {
10891095 return self.lpipe;
10901096 }
10911097
1092 pub fn lastToken(self: *PointerIndexPayload) TokenIndex {
1098 pub fn lastToken(self: *const PointerIndexPayload) TokenIndex {
10931099 return self.rpipe;
10941100 }
10951101 };
......@@ -1114,11 +1120,11 @@ pub const Node = struct {
11141120 return null;
11151121 }
11161122
1117 pub fn firstToken(self: *Else) TokenIndex {
1123 pub fn firstToken(self: *const Else) TokenIndex {
11181124 return self.else_token;
11191125 }
11201126
1121 pub fn lastToken(self: *Else) TokenIndex {
1127 pub fn lastToken(self: *const Else) TokenIndex {
11221128 return self.body.lastToken();
11231129 }
11241130 };
......@@ -1146,11 +1152,11 @@ pub const Node = struct {
11461152 return null;
11471153 }
11481154
1149 pub fn firstToken(self: *Switch) TokenIndex {
1155 pub fn firstToken(self: *const Switch) TokenIndex {
11501156 return self.switch_token;
11511157 }
11521158
1153 pub fn lastToken(self: *Switch) TokenIndex {
1159 pub fn lastToken(self: *const Switch) TokenIndex {
11541160 return self.rbrace;
11551161 }
11561162 };
......@@ -1181,11 +1187,11 @@ pub const Node = struct {
11811187 return null;
11821188 }
11831189
1184 pub fn firstToken(self: *SwitchCase) TokenIndex {
1190 pub fn firstToken(self: *const SwitchCase) TokenIndex {
11851191 return (self.items.at(0).*).firstToken();
11861192 }
11871193
1188 pub fn lastToken(self: *SwitchCase) TokenIndex {
1194 pub fn lastToken(self: *const SwitchCase) TokenIndex {
11891195 return self.expr.lastToken();
11901196 }
11911197 };
......@@ -1198,11 +1204,11 @@ pub const Node = struct {
11981204 return null;
11991205 }
12001206
1201 pub fn firstToken(self: *SwitchElse) TokenIndex {
1207 pub fn firstToken(self: *const SwitchElse) TokenIndex {
12021208 return self.token;
12031209 }
12041210
1205 pub fn lastToken(self: *SwitchElse) TokenIndex {
1211 pub fn lastToken(self: *const SwitchElse) TokenIndex {
12061212 return self.token;
12071213 }
12081214 };
......@@ -1245,7 +1251,7 @@ pub const Node = struct {
12451251 return null;
12461252 }
12471253
1248 pub fn firstToken(self: *While) TokenIndex {
1254 pub fn firstToken(self: *const While) TokenIndex {
12491255 if (self.label) |label| {
12501256 return label;
12511257 }
......@@ -1257,7 +1263,7 @@ pub const Node = struct {
12571263 return self.while_token;
12581264 }
12591265
1260 pub fn lastToken(self: *While) TokenIndex {
1266 pub fn lastToken(self: *const While) TokenIndex {
12611267 if (self.@"else") |@"else"| {
12621268 return @"else".body.lastToken();
12631269 }
......@@ -1298,7 +1304,7 @@ pub const Node = struct {
12981304 return null;
12991305 }
13001306
1301 pub fn firstToken(self: *For) TokenIndex {
1307 pub fn firstToken(self: *const For) TokenIndex {
13021308 if (self.label) |label| {
13031309 return label;
13041310 }
......@@ -1310,7 +1316,7 @@ pub const Node = struct {
13101316 return self.for_token;
13111317 }
13121318
1313 pub fn lastToken(self: *For) TokenIndex {
1319 pub fn lastToken(self: *const For) TokenIndex {
13141320 if (self.@"else") |@"else"| {
13151321 return @"else".body.lastToken();
13161322 }
......@@ -1349,11 +1355,11 @@ pub const Node = struct {
13491355 return null;
13501356 }
13511357
1352 pub fn firstToken(self: *If) TokenIndex {
1358 pub fn firstToken(self: *const If) TokenIndex {
13531359 return self.if_token;
13541360 }
13551361
1356 pub fn lastToken(self: *If) TokenIndex {
1362 pub fn lastToken(self: *const If) TokenIndex {
13571363 if (self.@"else") |@"else"| {
13581364 return @"else".body.lastToken();
13591365 }
......@@ -1480,11 +1486,11 @@ pub const Node = struct {
14801486 return null;
14811487 }
14821488
1483 pub fn firstToken(self: *InfixOp) TokenIndex {
1489 pub fn firstToken(self: *const InfixOp) TokenIndex {
14841490 return self.lhs.firstToken();
14851491 }
14861492
1487 pub fn lastToken(self: *InfixOp) TokenIndex {
1493 pub fn lastToken(self: *const InfixOp) TokenIndex {
14881494 return self.rhs.lastToken();
14891495 }
14901496 };
......@@ -1570,11 +1576,11 @@ pub const Node = struct {
15701576 return null;
15711577 }
15721578
1573 pub fn firstToken(self: *PrefixOp) TokenIndex {
1579 pub fn firstToken(self: *const PrefixOp) TokenIndex {
15741580 return self.op_token;
15751581 }
15761582
1577 pub fn lastToken(self: *PrefixOp) TokenIndex {
1583 pub fn lastToken(self: *const PrefixOp) TokenIndex {
15781584 return self.rhs.lastToken();
15791585 }
15801586 };
......@@ -1594,11 +1600,11 @@ pub const Node = struct {
15941600 return null;
15951601 }
15961602
1597 pub fn firstToken(self: *FieldInitializer) TokenIndex {
1603 pub fn firstToken(self: *const FieldInitializer) TokenIndex {
15981604 return self.period_token;
15991605 }
16001606
1601 pub fn lastToken(self: *FieldInitializer) TokenIndex {
1607 pub fn lastToken(self: *const FieldInitializer) TokenIndex {
16021608 return self.expr.lastToken();
16031609 }
16041610 };
......@@ -1673,7 +1679,7 @@ pub const Node = struct {
16731679 return null;
16741680 }
16751681
1676 pub fn firstToken(self: *SuffixOp) TokenIndex {
1682 pub fn firstToken(self: *const SuffixOp) TokenIndex {
16771683 switch (self.op) {
16781684 @TagType(Op).Call => |*call_info| if (call_info.async_attr) |async_attr| return async_attr.firstToken(),
16791685 else => {},
......@@ -1681,7 +1687,7 @@ pub const Node = struct {
16811687 return self.lhs.firstToken();
16821688 }
16831689
1684 pub fn lastToken(self: *SuffixOp) TokenIndex {
1690 pub fn lastToken(self: *const SuffixOp) TokenIndex {
16851691 return self.rtoken;
16861692 }
16871693 };
......@@ -1701,11 +1707,11 @@ pub const Node = struct {
17011707 return null;
17021708 }
17031709
1704 pub fn firstToken(self: *GroupedExpression) TokenIndex {
1710 pub fn firstToken(self: *const GroupedExpression) TokenIndex {
17051711 return self.lparen;
17061712 }
17071713
1708 pub fn lastToken(self: *GroupedExpression) TokenIndex {
1714 pub fn lastToken(self: *const GroupedExpression) TokenIndex {
17091715 return self.rparen;
17101716 }
17111717 };
......@@ -1749,11 +1755,11 @@ pub const Node = struct {
17491755 return null;
17501756 }
17511757
1752 pub fn firstToken(self: *ControlFlowExpression) TokenIndex {
1758 pub fn firstToken(self: *const ControlFlowExpression) TokenIndex {
17531759 return self.ltoken;
17541760 }
17551761
1756 pub fn lastToken(self: *ControlFlowExpression) TokenIndex {
1762 pub fn lastToken(self: *const ControlFlowExpression) TokenIndex {
17571763 if (self.rhs) |rhs| {
17581764 return rhs.lastToken();
17591765 }
......@@ -1792,11 +1798,11 @@ pub const Node = struct {
17921798 return null;
17931799 }
17941800
1795 pub fn firstToken(self: *Suspend) TokenIndex {
1801 pub fn firstToken(self: *const Suspend) TokenIndex {
17961802 return self.suspend_token;
17971803 }
17981804
1799 pub fn lastToken(self: *Suspend) TokenIndex {
1805 pub fn lastToken(self: *const Suspend) TokenIndex {
18001806 if (self.body) |body| {
18011807 return body.lastToken();
18021808 }
......@@ -1813,11 +1819,11 @@ pub const Node = struct {
18131819 return null;
18141820 }
18151821
1816 pub fn firstToken(self: *IntegerLiteral) TokenIndex {
1822 pub fn firstToken(self: *const IntegerLiteral) TokenIndex {
18171823 return self.token;
18181824 }
18191825
1820 pub fn lastToken(self: *IntegerLiteral) TokenIndex {
1826 pub fn lastToken(self: *const IntegerLiteral) TokenIndex {
18211827 return self.token;
18221828 }
18231829 };
......@@ -1830,11 +1836,11 @@ pub const Node = struct {
18301836 return null;
18311837 }
18321838
1833 pub fn firstToken(self: *FloatLiteral) TokenIndex {
1839 pub fn firstToken(self: *const FloatLiteral) TokenIndex {
18341840 return self.token;
18351841 }
18361842
1837 pub fn lastToken(self: *FloatLiteral) TokenIndex {
1843 pub fn lastToken(self: *const FloatLiteral) TokenIndex {
18381844 return self.token;
18391845 }
18401846 };
......@@ -1856,11 +1862,11 @@ pub const Node = struct {
18561862 return null;
18571863 }
18581864
1859 pub fn firstToken(self: *BuiltinCall) TokenIndex {
1865 pub fn firstToken(self: *const BuiltinCall) TokenIndex {
18601866 return self.builtin_token;
18611867 }
18621868
1863 pub fn lastToken(self: *BuiltinCall) TokenIndex {
1869 pub fn lastToken(self: *const BuiltinCall) TokenIndex {
18641870 return self.rparen_token;
18651871 }
18661872 };
......@@ -1873,11 +1879,11 @@ pub const Node = struct {
18731879 return null;
18741880 }
18751881
1876 pub fn firstToken(self: *StringLiteral) TokenIndex {
1882 pub fn firstToken(self: *const StringLiteral) TokenIndex {
18771883 return self.token;
18781884 }
18791885
1880 pub fn lastToken(self: *StringLiteral) TokenIndex {
1886 pub fn lastToken(self: *const StringLiteral) TokenIndex {
18811887 return self.token;
18821888 }
18831889 };
......@@ -1892,11 +1898,11 @@ pub const Node = struct {
18921898 return null;
18931899 }
18941900
1895 pub fn firstToken(self: *MultilineStringLiteral) TokenIndex {
1901 pub fn firstToken(self: *const MultilineStringLiteral) TokenIndex {
18961902 return self.lines.at(0).*;
18971903 }
18981904
1899 pub fn lastToken(self: *MultilineStringLiteral) TokenIndex {
1905 pub fn lastToken(self: *const MultilineStringLiteral) TokenIndex {
19001906 return self.lines.at(self.lines.len - 1).*;
19011907 }
19021908 };
......@@ -1909,11 +1915,11 @@ pub const Node = struct {
19091915 return null;
19101916 }
19111917
1912 pub fn firstToken(self: *CharLiteral) TokenIndex {
1918 pub fn firstToken(self: *const CharLiteral) TokenIndex {
19131919 return self.token;
19141920 }
19151921
1916 pub fn lastToken(self: *CharLiteral) TokenIndex {
1922 pub fn lastToken(self: *const CharLiteral) TokenIndex {
19171923 return self.token;
19181924 }
19191925 };
......@@ -1926,11 +1932,11 @@ pub const Node = struct {
19261932 return null;
19271933 }
19281934
1929 pub fn firstToken(self: *BoolLiteral) TokenIndex {
1935 pub fn firstToken(self: *const BoolLiteral) TokenIndex {
19301936 return self.token;
19311937 }
19321938
1933 pub fn lastToken(self: *BoolLiteral) TokenIndex {
1939 pub fn lastToken(self: *const BoolLiteral) TokenIndex {
19341940 return self.token;
19351941 }
19361942 };
......@@ -1943,11 +1949,11 @@ pub const Node = struct {
19431949 return null;
19441950 }
19451951
1946 pub fn firstToken(self: *NullLiteral) TokenIndex {
1952 pub fn firstToken(self: *const NullLiteral) TokenIndex {
19471953 return self.token;
19481954 }
19491955
1950 pub fn lastToken(self: *NullLiteral) TokenIndex {
1956 pub fn lastToken(self: *const NullLiteral) TokenIndex {
19511957 return self.token;
19521958 }
19531959 };
......@@ -1960,11 +1966,11 @@ pub const Node = struct {
19601966 return null;
19611967 }
19621968
1963 pub fn firstToken(self: *UndefinedLiteral) TokenIndex {
1969 pub fn firstToken(self: *const UndefinedLiteral) TokenIndex {
19641970 return self.token;
19651971 }
19661972
1967 pub fn lastToken(self: *UndefinedLiteral) TokenIndex {
1973 pub fn lastToken(self: *const UndefinedLiteral) TokenIndex {
19681974 return self.token;
19691975 }
19701976 };
......@@ -1977,11 +1983,11 @@ pub const Node = struct {
19771983 return null;
19781984 }
19791985
1980 pub fn firstToken(self: *ThisLiteral) TokenIndex {
1986 pub fn firstToken(self: *const ThisLiteral) TokenIndex {
19811987 return self.token;
19821988 }
19831989
1984 pub fn lastToken(self: *ThisLiteral) TokenIndex {
1990 pub fn lastToken(self: *const ThisLiteral) TokenIndex {
19851991 return self.token;
19861992 }
19871993 };
......@@ -2022,11 +2028,11 @@ pub const Node = struct {
20222028 return null;
20232029 }
20242030
2025 pub fn firstToken(self: *AsmOutput) TokenIndex {
2031 pub fn firstToken(self: *const AsmOutput) TokenIndex {
20262032 return self.lbracket;
20272033 }
20282034
2029 pub fn lastToken(self: *AsmOutput) TokenIndex {
2035 pub fn lastToken(self: *const AsmOutput) TokenIndex {
20302036 return self.rparen;
20312037 }
20322038 };
......@@ -2054,11 +2060,11 @@ pub const Node = struct {
20542060 return null;
20552061 }
20562062
2057 pub fn firstToken(self: *AsmInput) TokenIndex {
2063 pub fn firstToken(self: *const AsmInput) TokenIndex {
20582064 return self.lbracket;
20592065 }
20602066
2061 pub fn lastToken(self: *AsmInput) TokenIndex {
2067 pub fn lastToken(self: *const AsmInput) TokenIndex {
20622068 return self.rparen;
20632069 }
20642070 };
......@@ -2089,11 +2095,11 @@ pub const Node = struct {
20892095 return null;
20902096 }
20912097
2092 pub fn firstToken(self: *Asm) TokenIndex {
2098 pub fn firstToken(self: *const Asm) TokenIndex {
20932099 return self.asm_token;
20942100 }
20952101
2096 pub fn lastToken(self: *Asm) TokenIndex {
2102 pub fn lastToken(self: *const Asm) TokenIndex {
20972103 return self.rparen;
20982104 }
20992105 };
......@@ -2106,11 +2112,11 @@ pub const Node = struct {
21062112 return null;
21072113 }
21082114
2109 pub fn firstToken(self: *Unreachable) TokenIndex {
2115 pub fn firstToken(self: *const Unreachable) TokenIndex {
21102116 return self.token;
21112117 }
21122118
2113 pub fn lastToken(self: *Unreachable) TokenIndex {
2119 pub fn lastToken(self: *const Unreachable) TokenIndex {
21142120 return self.token;
21152121 }
21162122 };
......@@ -2123,11 +2129,11 @@ pub const Node = struct {
21232129 return null;
21242130 }
21252131
2126 pub fn firstToken(self: *ErrorType) TokenIndex {
2132 pub fn firstToken(self: *const ErrorType) TokenIndex {
21272133 return self.token;
21282134 }
21292135
2130 pub fn lastToken(self: *ErrorType) TokenIndex {
2136 pub fn lastToken(self: *const ErrorType) TokenIndex {
21312137 return self.token;
21322138 }
21332139 };
......@@ -2140,11 +2146,11 @@ pub const Node = struct {
21402146 return null;
21412147 }
21422148
2143 pub fn firstToken(self: *VarType) TokenIndex {
2149 pub fn firstToken(self: *const VarType) TokenIndex {
21442150 return self.token;
21452151 }
21462152
2147 pub fn lastToken(self: *VarType) TokenIndex {
2153 pub fn lastToken(self: *const VarType) TokenIndex {
21482154 return self.token;
21492155 }
21502156 };
......@@ -2159,11 +2165,11 @@ pub const Node = struct {
21592165 return null;
21602166 }
21612167
2162 pub fn firstToken(self: *DocComment) TokenIndex {
2168 pub fn firstToken(self: *const DocComment) TokenIndex {
21632169 return self.lines.at(0).*;
21642170 }
21652171
2166 pub fn lastToken(self: *DocComment) TokenIndex {
2172 pub fn lastToken(self: *const DocComment) TokenIndex {
21672173 return self.lines.at(self.lines.len - 1).*;
21682174 }
21692175 };
......@@ -2184,11 +2190,11 @@ pub const Node = struct {
21842190 return null;
21852191 }
21862192
2187 pub fn firstToken(self: *TestDecl) TokenIndex {
2193 pub fn firstToken(self: *const TestDecl) TokenIndex {
21882194 return self.test_token;
21892195 }
21902196
2191 pub fn lastToken(self: *TestDecl) TokenIndex {
2197 pub fn lastToken(self: *const TestDecl) TokenIndex {
21922198 return self.body_node.lastToken();
21932199 }
21942200 };