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...@@ -460,11 +460,14 @@ set(ZIG_STD_FILES
460 "empty.zig"460 "empty.zig"
461 "event.zig"461 "event.zig"
462 "event/channel.zig"462 "event/channel.zig"
463 "event/fs.zig"
463 "event/future.zig"464 "event/future.zig"
464 "event/group.zig"465 "event/group.zig"
465 "event/lock.zig"466 "event/lock.zig"
466 "event/locked.zig"467 "event/locked.zig"
467 "event/loop.zig"468 "event/loop.zig"
469 "event/rwlock.zig"
470 "event/rwlocked.zig"
468 "event/tcp.zig"471 "event/tcp.zig"
469 "fmt/errol/enum3.zig"472 "fmt/errol/enum3.zig"
470 "fmt/errol/index.zig"473 "fmt/errol/index.zig"
...@@ -553,6 +556,7 @@ set(ZIG_STD_FILES...@@ -553,6 +556,7 @@ set(ZIG_STD_FILES
553 "math/tanh.zig"556 "math/tanh.zig"
554 "math/trunc.zig"557 "math/trunc.zig"
555 "mem.zig"558 "mem.zig"
559 "mutex.zig"
556 "net.zig"560 "net.zig"
557 "os/child_process.zig"561 "os/child_process.zig"
558 "os/darwin.zig"562 "os/darwin.zig"
doc/docgen.zig+2-2
...@@ -370,9 +370,9 @@ fn genToc(allocator: *mem.Allocator, tokenizer: *Tokenizer) !Toc {...@@ -370,9 +370,9 @@ fn genToc(allocator: *mem.Allocator, tokenizer: *Tokenizer) !Toc {
370 .n = header_stack_size,370 .n = header_stack_size,
371 },371 },
372 });372 });
373 if (try urls.put(urlized, tag_token)) |other_tag_token| {373 if (try urls.put(urlized, tag_token)) |entry| {
374 parseError(tokenizer, tag_token, "duplicate header url: #{}", urlized) catch {};374 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 {};
376 return error.ParseError;376 return error.ParseError;
377 }377 }
378 if (last_action == Action.Open) {378 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)...@@ -19,8 +19,8 @@ pub async fn renderToLlvm(comp: *Compilation, fn_val: *Value.Fn, code: *ir.Code)
19 var output_path = try await (async comp.createRandomOutputPath(comp.target.objFileExt()) catch unreachable);19 var output_path = try await (async comp.createRandomOutputPath(comp.target.objFileExt()) catch unreachable);
20 errdefer output_path.deinit();20 errdefer output_path.deinit();
2121
22 const llvm_handle = try comp.event_loop_local.getAnyLlvmContext();22 const llvm_handle = try comp.zig_compiler.getAnyLlvmContext();
23 defer llvm_handle.release(comp.event_loop_local);23 defer llvm_handle.release(comp.zig_compiler);
2424
25 const context = llvm_handle.node.data;25 const context = llvm_handle.node.data;
2626
src-self-hosted/compilation.zig+346-180
...@@ -30,9 +30,12 @@ const Package = @import("package.zig").Package;...@@ -30,9 +30,12 @@ const Package = @import("package.zig").Package;
30const link = @import("link.zig").link;30const link = @import("link.zig").link;
31const LibCInstallation = @import("libc_installation.zig").LibCInstallation;31const LibCInstallation = @import("libc_installation.zig").LibCInstallation;
32const CInt = @import("c_int.zig").CInt;32const CInt = @import("c_int.zig").CInt;
33const fs = event.fs;
34
35const max_src_size = 2 * 1024 * 1024 * 1024; // 2 GiB
3336
34/// Data that is local to the event loop.37/// Data that is local to the event loop.
35pub const EventLoopLocal = struct {38pub const ZigCompiler = struct {
36 loop: *event.Loop,39 loop: *event.Loop,
37 llvm_handle_pool: std.atomic.Stack(llvm.ContextRef),40 llvm_handle_pool: std.atomic.Stack(llvm.ContextRef),
38 lld_lock: event.Lock,41 lld_lock: event.Lock,
...@@ -44,7 +47,7 @@ pub const EventLoopLocal = struct {...@@ -44,7 +47,7 @@ pub const EventLoopLocal = struct {
4447
45 var lazy_init_targets = std.lazyInit(void);48 var lazy_init_targets = std.lazyInit(void);
4649
47 fn init(loop: *event.Loop) !EventLoopLocal {50 fn init(loop: *event.Loop) !ZigCompiler {
48 lazy_init_targets.get() orelse {51 lazy_init_targets.get() orelse {
49 Target.initializeAll();52 Target.initializeAll();
50 lazy_init_targets.resolve();53 lazy_init_targets.resolve();
...@@ -54,7 +57,7 @@ pub const EventLoopLocal = struct {...@@ -54,7 +57,7 @@ pub const EventLoopLocal = struct {
54 try std.os.getRandomBytes(seed_bytes[0..]);57 try std.os.getRandomBytes(seed_bytes[0..]);
55 const seed = std.mem.readInt(seed_bytes, u64, builtin.Endian.Big);58 const seed = std.mem.readInt(seed_bytes, u64, builtin.Endian.Big);
5659
57 return EventLoopLocal{60 return ZigCompiler{
58 .loop = loop,61 .loop = loop,
59 .lld_lock = event.Lock.init(loop),62 .lld_lock = event.Lock.init(loop),
60 .llvm_handle_pool = std.atomic.Stack(llvm.ContextRef).init(),63 .llvm_handle_pool = std.atomic.Stack(llvm.ContextRef).init(),
...@@ -64,7 +67,7 @@ pub const EventLoopLocal = struct {...@@ -64,7 +67,7 @@ pub const EventLoopLocal = struct {
64 }67 }
6568
66 /// Must be called only after EventLoop.run completes.69 /// Must be called only after EventLoop.run completes.
67 fn deinit(self: *EventLoopLocal) void {70 fn deinit(self: *ZigCompiler) void {
68 self.lld_lock.deinit();71 self.lld_lock.deinit();
69 while (self.llvm_handle_pool.pop()) |node| {72 while (self.llvm_handle_pool.pop()) |node| {
70 c.LLVMContextDispose(node.data);73 c.LLVMContextDispose(node.data);
...@@ -74,7 +77,7 @@ pub const EventLoopLocal = struct {...@@ -74,7 +77,7 @@ pub const EventLoopLocal = struct {
7477
75 /// Gets an exclusive handle on any LlvmContext.78 /// Gets an exclusive handle on any LlvmContext.
76 /// Caller must release the handle when done.79 /// Caller must release the handle when done.
77 pub fn getAnyLlvmContext(self: *EventLoopLocal) !LlvmHandle {80 pub fn getAnyLlvmContext(self: *ZigCompiler) !LlvmHandle {
78 if (self.llvm_handle_pool.pop()) |node| return LlvmHandle{ .node = node };81 if (self.llvm_handle_pool.pop()) |node| return LlvmHandle{ .node = node };
7982
80 const context_ref = c.LLVMContextCreate() orelse return error.OutOfMemory;83 const context_ref = c.LLVMContextCreate() orelse return error.OutOfMemory;
...@@ -89,24 +92,36 @@ pub const EventLoopLocal = struct {...@@ -89,24 +92,36 @@ pub const EventLoopLocal = struct {
89 return LlvmHandle{ .node = node };92 return LlvmHandle{ .node = node };
90 }93 }
9194
92 pub async fn getNativeLibC(self: *EventLoopLocal) !*LibCInstallation {95 pub async fn getNativeLibC(self: *ZigCompiler) !*LibCInstallation {
93 if (await (async self.native_libc.start() catch unreachable)) |ptr| return ptr;96 if (await (async self.native_libc.start() catch unreachable)) |ptr| return ptr;
94 try await (async self.native_libc.data.findNative(self.loop) catch unreachable);97 try await (async self.native_libc.data.findNative(self.loop) catch unreachable);
95 self.native_libc.resolve();98 self.native_libc.resolve();
96 return &self.native_libc.data;99 return &self.native_libc.data;
97 }100 }
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 }
98};113};
99114
100pub const LlvmHandle = struct {115pub const LlvmHandle = struct {
101 node: *std.atomic.Stack(llvm.ContextRef).Node,116 node: *std.atomic.Stack(llvm.ContextRef).Node,
102117
103 pub fn release(self: LlvmHandle, event_loop_local: *EventLoopLocal) void {118 pub fn release(self: LlvmHandle, zig_compiler: *ZigCompiler) void {
104 event_loop_local.llvm_handle_pool.push(self.node);119 zig_compiler.llvm_handle_pool.push(self.node);
105 }120 }
106};121};
107122
108pub const Compilation = struct {123pub const Compilation = struct {
109 event_loop_local: *EventLoopLocal,124 zig_compiler: *ZigCompiler,
110 loop: *event.Loop,125 loop: *event.Loop,
111 name: Buffer,126 name: Buffer,
112 llvm_triple: Buffer,127 llvm_triple: Buffer,
...@@ -134,7 +149,6 @@ pub const Compilation = struct {...@@ -134,7 +149,6 @@ pub const Compilation = struct {
134 linker_rdynamic: bool,149 linker_rdynamic: bool,
135150
136 clang_argv: []const []const u8,151 clang_argv: []const []const u8,
137 llvm_argv: []const []const u8,
138 lib_dirs: []const []const u8,152 lib_dirs: []const []const u8,
139 rpath_list: []const []const u8,153 rpath_list: []const []const u8,
140 assembly_files: []const []const u8,154 assembly_files: []const []const u8,
...@@ -214,6 +228,8 @@ pub const Compilation = struct {...@@ -214,6 +228,8 @@ pub const Compilation = struct {
214 deinit_group: event.Group(void),228 deinit_group: event.Group(void),
215229
216 destroy_handle: promise,230 destroy_handle: promise,
231 main_loop_handle: promise,
232 main_loop_future: event.Future(void),
217233
218 have_err_ret_tracing: bool,234 have_err_ret_tracing: bool,
219235
...@@ -227,6 +243,8 @@ pub const Compilation = struct {...@@ -227,6 +243,8 @@ pub const Compilation = struct {
227243
228 c_int_types: [CInt.list.len]*Type.Int,244 c_int_types: [CInt.list.len]*Type.Int,
229245
246 fs_watch: *fs.Watch(*Scope.Root),
247
230 const IntTypeTable = std.HashMap(*const Type.Int.Key, *Type.Int, Type.Int.Key.hash, Type.Int.Key.eql);248 const IntTypeTable = std.HashMap(*const Type.Int.Key, *Type.Int, Type.Int.Key.hash, Type.Int.Key.eql);
231 const ArrayTypeTable = std.HashMap(*const Type.Array.Key, *Type.Array, Type.Array.Key.hash, Type.Array.Key.eql);249 const ArrayTypeTable = std.HashMap(*const Type.Array.Key, *Type.Array, Type.Array.Key.hash, Type.Array.Key.eql);
232 const PtrTypeTable = std.HashMap(*const Type.Pointer.Key, *Type.Pointer, Type.Pointer.Key.hash, Type.Pointer.Key.eql);250 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 {...@@ -282,6 +300,8 @@ pub const Compilation = struct {
282 LibCMissingDynamicLinker,300 LibCMissingDynamicLinker,
283 InvalidDarwinVersionString,301 InvalidDarwinVersionString,
284 UnsupportedLinkArchitecture,302 UnsupportedLinkArchitecture,
303 UserResourceLimitReached,
304 InvalidUtf8,
285 };305 };
286306
287 pub const Event = union(enum) {307 pub const Event = union(enum) {
...@@ -318,7 +338,7 @@ pub const Compilation = struct {...@@ -318,7 +338,7 @@ pub const Compilation = struct {
318 };338 };
319339
320 pub fn create(340 pub fn create(
321 event_loop_local: *EventLoopLocal,341 zig_compiler: *ZigCompiler,
322 name: []const u8,342 name: []const u8,
323 root_src_path: ?[]const u8,343 root_src_path: ?[]const u8,
324 target: Target,344 target: Target,
...@@ -327,11 +347,45 @@ pub const Compilation = struct {...@@ -327,11 +347,45 @@ pub const Compilation = struct {
327 is_static: bool,347 is_static: bool,
328 zig_lib_dir: []const u8,348 zig_lib_dir: []const u8,
329 ) !*Compilation {349 ) !*Compilation {
330 const loop = event_loop_local.loop;350 var optional_comp: ?*Compilation = null;
331 const comp = try event_loop_local.loop.allocator.create(Compilation{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{
332 .loop = loop,386 .loop = loop,
333 .arena_allocator = std.heap.ArenaAllocator.init(loop.allocator),387 .arena_allocator = std.heap.ArenaAllocator.init(loop.allocator),
334 .event_loop_local = event_loop_local,388 .zig_compiler = zig_compiler,
335 .events = undefined,389 .events = undefined,
336 .root_src_path = root_src_path,390 .root_src_path = root_src_path,
337 .target = target,391 .target = target,
...@@ -341,6 +395,9 @@ pub const Compilation = struct {...@@ -341,6 +395,9 @@ pub const Compilation = struct {
341 .zig_lib_dir = zig_lib_dir,395 .zig_lib_dir = zig_lib_dir,
342 .zig_std_dir = undefined,396 .zig_std_dir = undefined,
343 .tmp_dir = event.Future(BuildError![]u8).init(loop),397 .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
345 .name = undefined,402 .name = undefined,
346 .llvm_triple = undefined,403 .llvm_triple = undefined,
...@@ -365,7 +422,6 @@ pub const Compilation = struct {...@@ -365,7 +422,6 @@ pub const Compilation = struct {
365 .is_static = is_static,422 .is_static = is_static,
366 .linker_rdynamic = false,423 .linker_rdynamic = false,
367 .clang_argv = [][]const u8{},424 .clang_argv = [][]const u8{},
368 .llvm_argv = [][]const u8{},
369 .lib_dirs = [][]const u8{},425 .lib_dirs = [][]const u8{},
370 .rpath_list = [][]const u8{},426 .rpath_list = [][]const u8{},
371 .assembly_files = [][]const u8{},427 .assembly_files = [][]const u8{},
...@@ -412,25 +468,26 @@ pub const Compilation = struct {...@@ -412,25 +468,26 @@ pub const Compilation = struct {
412 .std_package = undefined,468 .std_package = undefined,
413469
414 .override_libc = null,470 .override_libc = null,
415 .destroy_handle = undefined,
416 .have_err_ret_tracing = false,471 .have_err_ret_tracing = false,
417 .primitive_type_table = undefined,472 .primitive_type_table = undefined,
418 });473
419 errdefer {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 {
420 comp.int_type_table.private_data.deinit();480 comp.int_type_table.private_data.deinit();
421 comp.array_type_table.private_data.deinit();481 comp.array_type_table.private_data.deinit();
422 comp.ptr_type_table.private_data.deinit();482 comp.ptr_type_table.private_data.deinit();
423 comp.fn_type_table.private_data.deinit();483 comp.fn_type_table.private_data.deinit();
424 comp.arena_allocator.deinit();484 comp.arena_allocator.deinit();
425 comp.loop.allocator.destroy(comp);
426 }485 }
427486
428 comp.name = try Buffer.init(comp.arena(), name);487 comp.name = try Buffer.init(comp.arena(), name);
429 comp.llvm_triple = try target.getTriple(comp.arena());488 comp.llvm_triple = try target.getTriple(comp.arena());
430 comp.llvm_target = try Target.llvmTargetFromTriple(comp.llvm_triple);489 comp.llvm_target = try Target.llvmTargetFromTriple(comp.llvm_triple);
431 comp.link_libs_list = ArrayList(*LinkLib).init(comp.arena());
432 comp.zig_std_dir = try std.os.path.join(comp.arena(), zig_lib_dir, "std");490 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
435 const opt_level = switch (build_mode) {492 const opt_level = switch (build_mode) {
436 builtin.Mode.Debug => llvm.CodeGenLevelNone,493 builtin.Mode.Debug => llvm.CodeGenLevelNone,
...@@ -444,8 +501,8 @@ pub const Compilation = struct {...@@ -444,8 +501,8 @@ pub const Compilation = struct {
444 // As a workaround we do not use target native features on Windows.501 // As a workaround we do not use target native features on Windows.
445 var target_specific_cpu_args: ?[*]u8 = null;502 var target_specific_cpu_args: ?[*]u8 = null;
446 var target_specific_cpu_features: ?[*]u8 = null;503 var target_specific_cpu_features: ?[*]u8 = null;
447 errdefer llvm.DisposeMessage(target_specific_cpu_args);504 defer llvm.DisposeMessage(target_specific_cpu_args);
448 errdefer llvm.DisposeMessage(target_specific_cpu_features);505 defer llvm.DisposeMessage(target_specific_cpu_features);
449 if (target == Target.Native and !target.isWindows()) {506 if (target == Target.Native and !target.isWindows()) {
450 target_specific_cpu_args = llvm.GetHostCPUName() orelse return error.OutOfMemory;507 target_specific_cpu_args = llvm.GetHostCPUName() orelse return error.OutOfMemory;
451 target_specific_cpu_features = llvm.GetNativeFeatures() orelse return error.OutOfMemory;508 target_specific_cpu_features = llvm.GetNativeFeatures() orelse return error.OutOfMemory;
...@@ -460,16 +517,16 @@ pub const Compilation = struct {...@@ -460,16 +517,16 @@ pub const Compilation = struct {
460 reloc_mode,517 reloc_mode,
461 llvm.CodeModelDefault,518 llvm.CodeModelDefault,
462 ) orelse return error.OutOfMemory;519 ) orelse return error.OutOfMemory;
463 errdefer llvm.DisposeTargetMachine(comp.target_machine);520 defer llvm.DisposeTargetMachine(comp.target_machine);
464521
465 comp.target_data_ref = llvm.CreateTargetDataLayout(comp.target_machine) orelse return error.OutOfMemory;522 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
468 comp.target_layout_str = llvm.CopyStringRepOfTargetData(comp.target_data_ref) orelse return error.OutOfMemory;525 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
471 comp.events = try event.Channel(Event).create(comp.loop, 0);528 comp.events = try event.Channel(Event).create(comp.loop, 0);
472 errdefer comp.events.destroy();529 defer comp.events.destroy();
473530
474 if (root_src_path) |root_src| {531 if (root_src_path) |root_src| {
475 const dirname = std.os.path.dirname(root_src) orelse ".";532 const dirname = std.os.path.dirname(root_src) orelse ".";
...@@ -482,11 +539,27 @@ pub const Compilation = struct {...@@ -482,11 +539,27 @@ pub const Compilation = struct {
482 comp.root_package = try Package.create(comp.arena(), ".", "");539 comp.root_package = try Package.create(comp.arena(), ".", "");
483 }540 }
484541
542 comp.fs_watch = try fs.Watch(*Scope.Root).create(loop, 16);
543 defer comp.fs_watch.destroy();
544
485 try comp.initTypes();545 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 |_| {};
490 }563 }
491564
492 /// it does ref the result because it could be an arbitrary integer size565 /// it does ref the result because it could be an arbitrary integer size
...@@ -672,55 +745,28 @@ pub const Compilation = struct {...@@ -672,55 +745,28 @@ pub const Compilation = struct {
672 assert((try comp.primitive_type_table.put(comp.u8_type.base.name, &comp.u8_type.base)) == null);745 assert((try comp.primitive_type_table.put(comp.u8_type.base.name, &comp.u8_type.base)) == null);
673 }746 }
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
698 pub fn destroy(self: *Compilation) void {748 pub fn destroy(self: *Compilation) void {
749 cancel self.main_loop_handle;
699 resume self.destroy_handle;750 resume self.destroy_handle;
700 }751 }
701752
702 pub fn build(self: *Compilation) !void {753 fn start(self: *Compilation) void {
703 if (self.llvm_argv.len != 0) {754 self.main_loop_future.resolve();
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();
714 }755 }
715756
716 async fn buildAsync(self: *Compilation) void {757 async fn mainLoop(self: *Compilation) void {
717 while (true) {758 // wait until start() is called
718 // TODO directly awaiting async should guarantee memory allocation elision759 _ = await (async self.main_loop_future.get() catch unreachable);
719 const build_result = await (async self.compileAndLink() 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;
721 // this makes a handy error return trace and stack trace in debug mode767 // this makes a handy error return trace and stack trace in debug mode
722 if (std.debug.runtime_safety) {768 if (std.debug.runtime_safety) {
723 build_result catch unreachable;769 link_result catch unreachable;
724 }770 }
725771
726 const compile_errors = blk: {772 const compile_errors = blk: {
...@@ -729,7 +775,7 @@ pub const Compilation = struct {...@@ -729,7 +775,7 @@ pub const Compilation = struct {
729 break :blk held.value.toOwnedSlice();775 break :blk held.value.toOwnedSlice();
730 };776 };
731777
732 if (build_result) |_| {778 if (link_result) |_| {
733 if (compile_errors.len == 0) {779 if (compile_errors.len == 0) {
734 await (async self.events.put(Event.Ok) catch unreachable);780 await (async self.events.put(Event.Ok) catch unreachable);
735 } else {781 } else {
...@@ -742,105 +788,195 @@ pub const Compilation = struct {...@@ -742,105 +788,195 @@ pub const Compilation = struct {
742 await (async self.events.put(Event{ .Error = err }) catch unreachable);788 await (async self.events.put(Event{ .Error = err }) catch unreachable);
743 }789 }
744790
745 // for now we stop after 1791 // First, get an item from the watch channel, waiting on the channel.
746 return;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);
747 }818 }
748 }819 }
749820
750 async fn compileAndLink(self: *Compilation) !void {821 async fn rebuildFile(self: *Compilation, root_scope: *Scope.Root) !void {
751 if (self.root_src_path) |root_src_path| {822 const tree_scope = blk: {
752 // TODO async/await os.path.real823 const source_code = (await (async fs.readFile(
753 const root_src_real_path = os.path.real(self.gpa(), root_src_path) catch |err| {824 self.loop,
754 try printError("unable to get real path '{}': {}", root_src_path, err);825 root_scope.realpath,
755 return err;826 max_src_size,
827 ) catch unreachable)) catch |err| {
828 try self.addCompileErrorCli(root_scope.realpath, "unable to open: {}", @errorName(err));
829 return;
756 };830 };
757 const root_scope = blk: {831 errdefer self.gpa().free(source_code);
758 errdefer self.gpa().free(root_src_real_path);
759832
760 // TODO async/await readFileAlloc()833 const tree = try self.gpa().createOne(ast.Tree);
761 const source_code = io.readFileAlloc(self.gpa(), root_src_real_path) catch |err| {834 tree.* = try std.zig.parse(self.gpa(), source_code);
762 try printError("unable to open '{}': {}", root_src_real_path, err);835 errdefer {
763 return err;836 tree.deinit();
764 };837 self.gpa().destroy(tree);
765 errdefer self.gpa().free(source_code);838 }
766839
767 const tree = try self.gpa().createOne(ast.Tree);840 break :blk try Scope.AstTree.create(self, tree, root_scope);
768 tree.* = try std.zig.parse(self.gpa(), source_code);841 };
769 errdefer {842 defer tree_scope.base.deref(self);
770 tree.deinit();
771 self.gpa().destroy(tree);
772 }
773843
774 break :blk try Scope.Root.create(self, tree, root_src_real_path);844 var error_it = tree_scope.tree.errors.iterator(0);
775 };845 while (error_it.next()) |parse_error| {
776 defer root_scope.base.deref(self);846 const msg = try Msg.createFromParseErrorAndScope(self, tree_scope, parse_error);
777 const tree = root_scope.tree;847 errdefer msg.destroy();
778848
779 var error_it = tree.errors.iterator(0);849 try await (async self.addCompileErrorAsync(msg) catch unreachable);
780 while (error_it.next()) |parse_error| {850 }
781 const msg = try Msg.createFromParseErrorAndScope(self, root_scope, parse_error);851 if (tree_scope.tree.errors.len != 0) {
782 errdefer msg.destroy();852 return;
853 }
783854
784 try await (async self.addCompileErrorAsync(msg) catch unreachable);855 const locked_table = await (async root_scope.decls.table.acquireWrite() catch unreachable);
785 }856 defer locked_table.release();
786 if (tree.errors.len != 0) {
787 return;
788 }
789857
790 const decls = try Scope.Decls.create(self, &root_scope.base);858 var decl_group = event.Group(BuildError!void).init(self.loop);
791 defer decls.base.deref(self);859 defer decl_group.deinit();
792860
793 var decl_group = event.Group(BuildError!void).init(self.loop);861 try await try async self.rebuildChangedDecls(
794 var decl_group_consumed = false;862 &decl_group,
795 errdefer if (!decl_group_consumed) decl_group.cancelAll();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);869 try await (async decl_group.wait() catch unreachable);
798 while (it.next()) |decl_ptr| {870 }
799 const decl = decl_ptr.*;
800 switch (decl.id) {
801 ast.Node.Id.Comptime => {
802 const comptime_node = @fieldParentPtr(ast.Node.Comptime, "base", decl);
803871
804 try self.prelink_group.call(addCompTimeBlock, self, &decls.base, comptime_node);872 async fn rebuildChangedDecls(
805 },873 self: *Compilation,
806 ast.Node.Id.VarDecl => @panic("TODO"),874 group: *event.Group(BuildError!void),
807 ast.Node.Id.FnProto => {875 locked_table: *Decl.Table,
808 const fn_proto = @fieldParentPtr(ast.Node.FnProto, "base", decl);876 decl_scope: *Scope.Decls,
809877 ast_decls: *ast.Node.Root.DeclList,
810 const name = if (fn_proto.name_token) |name_token| tree.tokenSlice(name_token) else {878 tree_scope: *Scope.AstTree,
811 try self.addCompileError(root_scope, Span{879 ) !void {
812 .first = fn_proto.fn_token,880 var existing_decls = try locked_table.clone();
813 .last = fn_proto.fn_token + 1,881 defer existing_decls.deinit();
814 }, "missing function name");882
815 continue;883 var ast_it = ast_decls.iterator(0);
816 };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
818 const fn_decl = try self.gpa().create(Decl.Fn{929 const fn_decl = try self.gpa().create(Decl.Fn{
819 .base = Decl{930 .base = Decl{
820 .id = Decl.Id.Fn,931 .id = Decl.Id.Fn,
821 .name = name,932 .name = name,
822 .visib = parseVisibToken(tree, fn_proto.visib_token),933 .visib = parseVisibToken(tree_scope.tree, fn_proto.visib_token),
823 .resolution = event.Future(BuildError!void).init(self.loop),934 .resolution = event.Future(BuildError!void).init(self.loop),
824 .parent_scope = &decls.base,935 .parent_scope = &decl_scope.base,
936 .tree_scope = tree_scope,
825 },937 },
826 .value = Decl.Fn.Val{ .Unresolved = {} },938 .value = Decl.Fn.Val{ .Unresolved = {} },
827 .fn_proto = fn_proto,939 .fn_proto = fn_proto,
828 });940 });
941 tree_scope.base.ref();
829 errdefer self.gpa().destroy(fn_decl);942 errdefer self.gpa().destroy(fn_decl);
830943
831 try decl_group.call(addTopLevelDecl, self, decls, &fn_decl.base);944 try group.call(addTopLevelDecl, self, &fn_decl.base, locked_table);
832 },945 }
833 ast.Node.Id.TestDecl => @panic("TODO"),946 },
834 else => unreachable,947 ast.Node.Id.TestDecl => @panic("TODO"),
835 }948 else => unreachable,
836 }949 }
837 decl_group_consumed = true;950 }
838 try await (async decl_group.wait() catch unreachable);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.974 assert((try await try async self.fs_watch.addFile(root_scope.realpath, root_scope)) == null);
841 decls.name_future.resolve();975 try await try async self.rebuildFile(root_scope);
842 }976 }
977 }
843978
979 async fn maybeLink(self: *Compilation) !void {
844 (await (async self.prelink_group.wait() catch unreachable)) catch |err| switch (err) {980 (await (async self.prelink_group.wait() catch unreachable)) catch |err| switch (err) {
845 error.SemanticAnalysisFailed => {},981 error.SemanticAnalysisFailed => {},
846 else => return err,982 else => return err,
...@@ -861,6 +997,7 @@ pub const Compilation = struct {...@@ -861,6 +997,7 @@ pub const Compilation = struct {
861 /// caller takes ownership of resulting Code997 /// caller takes ownership of resulting Code
862 async fn genAndAnalyzeCode(998 async fn genAndAnalyzeCode(
863 comp: *Compilation,999 comp: *Compilation,
1000 tree_scope: *Scope.AstTree,
864 scope: *Scope,1001 scope: *Scope,
865 node: *ast.Node,1002 node: *ast.Node,
866 expected_type: ?*Type,1003 expected_type: ?*Type,
...@@ -868,6 +1005,7 @@ pub const Compilation = struct {...@@ -868,6 +1005,7 @@ pub const Compilation = struct {
868 const unanalyzed_code = try await (async ir.gen(1005 const unanalyzed_code = try await (async ir.gen(
869 comp,1006 comp,
870 node,1007 node,
1008 tree_scope,
871 scope,1009 scope,
872 ) catch unreachable);1010 ) catch unreachable);
873 defer unanalyzed_code.destroy(comp.gpa());1011 defer unanalyzed_code.destroy(comp.gpa());
...@@ -894,6 +1032,7 @@ pub const Compilation = struct {...@@ -894,6 +1032,7 @@ pub const Compilation = struct {
8941032
895 async fn addCompTimeBlock(1033 async fn addCompTimeBlock(
896 comp: *Compilation,1034 comp: *Compilation,
1035 tree_scope: *Scope.AstTree,
897 scope: *Scope,1036 scope: *Scope,
898 comptime_node: *ast.Node.Comptime,1037 comptime_node: *ast.Node.Comptime,
899 ) !void {1038 ) !void {
...@@ -902,6 +1041,7 @@ pub const Compilation = struct {...@@ -902,6 +1041,7 @@ pub const Compilation = struct {
9021041
903 const analyzed_code = (await (async genAndAnalyzeCode(1042 const analyzed_code = (await (async genAndAnalyzeCode(
904 comp,1043 comp,
1044 tree_scope,
905 scope,1045 scope,
906 comptime_node.expr,1046 comptime_node.expr,
907 &void_type.base,1047 &void_type.base,
...@@ -914,38 +1054,42 @@ pub const Compilation = struct {...@@ -914,38 +1054,42 @@ pub const Compilation = struct {
914 analyzed_code.destroy(comp.gpa());1054 analyzed_code.destroy(comp.gpa());
915 }1055 }
9161056
917 async fn addTopLevelDecl(self: *Compilation, decls: *Scope.Decls, decl: *Decl) !void {1057 async fn addTopLevelDecl(
918 const tree = decl.findRootScope().tree;1058 self: *Compilation,
919 const is_export = decl.isExported(tree);1059 decl: *Decl,
9201060 locked_table: *Decl.Table,
921 var add_to_table_resolved = false;1061 ) !void {
922 const add_to_table = async self.addDeclToTable(decls, decl) catch unreachable;1062 const is_export = decl.isExported(decl.tree_scope.tree);
923 errdefer if (!add_to_table_resolved) cancel add_to_table; // TODO https://github.com/ziglang/zig/issues/1261
9241063
925 if (is_export) {1064 if (is_export) {
926 try self.prelink_group.call(verifyUniqueSymbol, self, decl);1065 try self.prelink_group.call(verifyUniqueSymbol, self, decl);
927 try self.prelink_group.call(resolveDecl, self, decl);1066 try self.prelink_group.call(resolveDecl, self, decl);
928 }1067 }
9291068
930 add_to_table_resolved = true;1069 const gop = try locked_table.getOrPut(decl.name);
931 try await add_to_table;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 }
932 }1076 }
9331077
934 async fn addDeclToTable(self: *Compilation, decls: *Scope.Decls, decl: *Decl) !void {1078 fn addCompileError(self: *Compilation, tree_scope: *Scope.AstTree, span: Span, comptime fmt: []const u8, args: ...) !void {
935 const held = await (async decls.table.acquire() catch unreachable);1079 const text = try std.fmt.allocPrint(self.gpa(), fmt, args);
936 defer held.release();1080 errdefer self.gpa().free(text);
9371081
938 if (try held.value.put(decl.name, decl)) |other_decl| {1082 const msg = try Msg.createFromScope(self, tree_scope, span, text);
939 try self.addCompileError(decls.base.findRoot(), decl.getSpan(), "redefinition of '{}'", decl.name);1083 errdefer msg.destroy();
940 // TODO note: other definition here1084
941 }1085 try self.prelink_group.call(addCompileErrorAsync, self, msg);
942 }1086 }
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 {
945 const text = try std.fmt.allocPrint(self.gpa(), fmt, args);1089 const text = try std.fmt.allocPrint(self.gpa(), fmt, args);
946 errdefer self.gpa().free(text);1090 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);
949 errdefer msg.destroy();1093 errdefer msg.destroy();
9501094
951 try self.prelink_group.call(addCompileErrorAsync, self, msg);1095 try self.prelink_group.call(addCompileErrorAsync, self, msg);
...@@ -969,7 +1113,7 @@ pub const Compilation = struct {...@@ -969,7 +1113,7 @@ pub const Compilation = struct {
9691113
970 if (try exported_symbol_names.value.put(decl.name, decl)) |other_decl| {1114 if (try exported_symbol_names.value.put(decl.name, decl)) |other_decl| {
971 try self.addCompileError(1115 try self.addCompileError(
972 decl.findRootScope(),1116 decl.tree_scope,
973 decl.getSpan(),1117 decl.getSpan(),
974 "exported symbol collision: '{}'",1118 "exported symbol collision: '{}'",
975 decl.name,1119 decl.name,
...@@ -1019,7 +1163,7 @@ pub const Compilation = struct {...@@ -1019,7 +1163,7 @@ pub const Compilation = struct {
1019 async fn startFindingNativeLibC(self: *Compilation) void {1163 async fn startFindingNativeLibC(self: *Compilation) void {
1020 await (async self.loop.yield() catch unreachable);1164 await (async self.loop.yield() catch unreachable);
1021 // we don't care if it fails, we're just trying to kick off the future resolution1165 // 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;
1023 }1167 }
10241168
1025 /// General Purpose Allocator. Must free when done.1169 /// General Purpose Allocator. Must free when done.
...@@ -1077,7 +1221,7 @@ pub const Compilation = struct {...@@ -1077,7 +1221,7 @@ pub const Compilation = struct {
1077 var rand_bytes: [9]u8 = undefined;1221 var rand_bytes: [9]u8 = undefined;
10781222
1079 {1223 {
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);
1081 defer held.release();1225 defer held.release();
10821226
1083 held.value.random.bytes(rand_bytes[0..]);1227 held.value.random.bytes(rand_bytes[0..]);
...@@ -1093,18 +1237,24 @@ pub const Compilation = struct {...@@ -1093,18 +1237,24 @@ pub const Compilation = struct {
1093 }1237 }
10941238
1095 /// Returns a value which has been ref()'d once1239 /// Returns a value which has been ref()'d once
1096 async fn analyzeConstValue(comp: *Compilation, scope: *Scope, node: *ast.Node, expected_type: *Type) !*Value {1240 async fn analyzeConstValue(
1097 const analyzed_code = try await (async comp.genAndAnalyzeCode(scope, node, expected_type) catch unreachable);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);
1098 defer analyzed_code.destroy(comp.gpa());1248 defer analyzed_code.destroy(comp.gpa());
10991249
1100 return analyzed_code.getCompTimeResult(comp);1250 return analyzed_code.getCompTimeResult(comp);
1101 }1251 }
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 {
1104 const meta_type = &Type.MetaType.get(comp).base;1254 const meta_type = &Type.MetaType.get(comp).base;
1105 defer meta_type.base.deref(comp);1255 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);
1108 errdefer result_val.base.deref(comp);1258 errdefer result_val.base.deref(comp);
11091259
1110 return result_val.cast(Type).?;1260 return result_val.cast(Type).?;
...@@ -1120,13 +1270,6 @@ pub const Compilation = struct {...@@ -1120,13 +1270,6 @@ pub const Compilation = struct {
1120 }1270 }
1121};1271};
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
1130fn parseVisibToken(tree: *ast.Tree, optional_token_index: ?ast.TokenIndex) Visib {1273fn parseVisibToken(tree: *ast.Tree, optional_token_index: ?ast.TokenIndex) Visib {
1131 if (optional_token_index) |token_index| {1274 if (optional_token_index) |token_index| {
1132 const token = tree.tokens.at(token_index);1275 const token = tree.tokens.at(token_index);
...@@ -1150,12 +1293,14 @@ async fn generateDecl(comp: *Compilation, decl: *Decl) !void {...@@ -1150,12 +1293,14 @@ async fn generateDecl(comp: *Compilation, decl: *Decl) !void {
1150}1293}
11511294
1152async fn generateDeclFn(comp: *Compilation, fn_decl: *Decl.Fn) !void {1295async fn generateDeclFn(comp: *Compilation, fn_decl: *Decl.Fn) !void {
1296 const tree_scope = fn_decl.base.tree_scope;
1297
1153 const body_node = fn_decl.fn_proto.body_node orelse return await (async generateDeclFnProto(comp, fn_decl) catch unreachable);1298 const body_node = fn_decl.fn_proto.body_node orelse return await (async generateDeclFnProto(comp, fn_decl) catch unreachable);
11541299
1155 const fndef_scope = try Scope.FnDef.create(comp, fn_decl.base.parent_scope);1300 const fndef_scope = try Scope.FnDef.create(comp, fn_decl.base.parent_scope);
1156 defer fndef_scope.base.deref(comp);1301 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);
1159 defer fn_type.base.base.deref(comp);1304 defer fn_type.base.base.deref(comp);
11601305
1161 var symbol_name = try std.Buffer.init(comp.gpa(), fn_decl.base.name);1306 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 {...@@ -1168,18 +1313,17 @@ async fn generateDeclFn(comp: *Compilation, fn_decl: *Decl.Fn) !void {
1168 symbol_name_consumed = true;1313 symbol_name_consumed = true;
11691314
1170 // Define local parameter variables1315 // Define local parameter variables
1171 const root_scope = fn_decl.base.findRootScope();
1172 for (fn_type.key.data.Normal.params) |param, i| {1316 for (fn_type.key.data.Normal.params) |param, i| {
1173 //AstNode *param_decl_node = get_param_decl_node(fn_table_entry, i);1317 //AstNode *param_decl_node = get_param_decl_node(fn_table_entry, i);
1174 const param_decl = @fieldParentPtr(ast.Node.ParamDecl, "base", fn_decl.fn_proto.params.at(i).*);1318 const param_decl = @fieldParentPtr(ast.Node.ParamDecl, "base", fn_decl.fn_proto.params.at(i).*);
1175 const name_token = param_decl.name_token orelse {1319 const name_token = param_decl.name_token orelse {
1176 try comp.addCompileError(root_scope, Span{1320 try comp.addCompileError(tree_scope, Span{
1177 .first = param_decl.firstToken(),1321 .first = param_decl.firstToken(),
1178 .last = param_decl.type_node.firstToken(),1322 .last = param_decl.type_node.firstToken(),
1179 }, "missing parameter name");1323 }, "missing parameter name");
1180 return error.SemanticAnalysisFailed;1324 return error.SemanticAnalysisFailed;
1181 };1325 };
1182 const param_name = root_scope.tree.tokenSlice(name_token);1326 const param_name = tree_scope.tree.tokenSlice(name_token);
11831327
1184 // if (is_noalias && get_codegen_ptr_type(param_type) == nullptr) {1328 // if (is_noalias && get_codegen_ptr_type(param_type) == nullptr) {
1185 // add_node_error(g, param_decl_node, buf_sprintf("noalias on non-pointer parameter"));1329 // 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 {...@@ -1201,6 +1345,7 @@ async fn generateDeclFn(comp: *Compilation, fn_decl: *Decl.Fn) !void {
1201 }1345 }
12021346
1203 const analyzed_code = try await (async comp.genAndAnalyzeCode(1347 const analyzed_code = try await (async comp.genAndAnalyzeCode(
1348 tree_scope,
1204 fn_val.child_scope,1349 fn_val.child_scope,
1205 body_node,1350 body_node,
1206 fn_type.key.data.Normal.return_type,1351 fn_type.key.data.Normal.return_type,
...@@ -1231,12 +1376,17 @@ fn getZigDir(allocator: *mem.Allocator) ![]u8 {...@@ -1231,12 +1376,17 @@ fn getZigDir(allocator: *mem.Allocator) ![]u8 {
1231 return os.getAppDataDir(allocator, "zig");1376 return os.getAppDataDir(allocator, "zig");
1232}1377}
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 {
1235 const return_type_node = switch (fn_proto.return_type) {1385 const return_type_node = switch (fn_proto.return_type) {
1236 ast.Node.FnProto.ReturnType.Explicit => |n| n,1386 ast.Node.FnProto.ReturnType.Explicit => |n| n,
1237 ast.Node.FnProto.ReturnType.InferErrorSet => |n| n,1387 ast.Node.FnProto.ReturnType.InferErrorSet => |n| n,
1238 };1388 };
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);
1240 return_type.base.deref(comp);1390 return_type.base.deref(comp);
12411391
1242 var params = ArrayList(Type.Fn.Param).init(comp.gpa());1392 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...@@ -1252,7 +1402,7 @@ async fn analyzeFnType(comp: *Compilation, scope: *Scope, fn_proto: *ast.Node.Fn
1252 var it = fn_proto.params.iterator(0);1402 var it = fn_proto.params.iterator(0);
1253 while (it.next()) |param_node_ptr| {1403 while (it.next()) |param_node_ptr| {
1254 const param_node = param_node_ptr.*.cast(ast.Node.ParamDecl).?;1404 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);
1256 errdefer param_type.base.deref(comp);1406 errdefer param_type.base.deref(comp);
1257 try params.append(Type.Fn.Param{1407 try params.append(Type.Fn.Param{
1258 .typ = param_type,1408 .typ = param_type,
...@@ -1289,7 +1439,12 @@ async fn analyzeFnType(comp: *Compilation, scope: *Scope, fn_proto: *ast.Node.Fn...@@ -1289,7 +1439,12 @@ async fn analyzeFnType(comp: *Compilation, scope: *Scope, fn_proto: *ast.Node.Fn
1289}1439}
12901440
1291async fn generateDeclFnProto(comp: *Compilation, fn_decl: *Decl.Fn) !void {1441async 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);
1293 defer fn_type.base.base.deref(comp);1448 defer fn_type.base.base.deref(comp);
12941449
1295 var symbol_name = try std.Buffer.init(comp.gpa(), fn_decl.base.name);1450 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 {...@@ -1301,3 +1456,14 @@ async fn generateDeclFnProto(comp: *Compilation, fn_decl: *Decl.Fn) !void {
1301 fn_decl.value = Decl.Fn.Val{ .FnProto = fn_proto_val };1456 fn_decl.value = Decl.Fn.Val{ .FnProto = fn_proto_val };
1302 symbol_name_consumed = true;1457 symbol_name_consumed = true;
1303}1458}
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 {...@@ -17,8 +17,16 @@ pub const Decl = struct {
17 resolution: event.Future(Compilation.BuildError!void),17 resolution: event.Future(Compilation.BuildError!void),
18 parent_scope: *Scope,18 parent_scope: *Scope,
1919
20 // TODO when we destroy the decl, deref the tree scope
21 tree_scope: *Scope.AstTree,
22
20 pub const Table = std.HashMap([]const u8, *Decl, mem.hash_slice_u8, mem.eql_slice_u8);23 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
22 pub fn isExported(base: *const Decl, tree: *ast.Tree) bool {30 pub fn isExported(base: *const Decl, tree: *ast.Tree) bool {
23 switch (base.id) {31 switch (base.id) {
24 Id.Fn => {32 Id.Fn => {
...@@ -95,4 +103,3 @@ pub const Decl = struct {...@@ -95,4 +103,3 @@ pub const Decl = struct {
95 base: Decl,103 base: Decl,
96 };104 };
97};105};
98
src-self-hosted/errmsg.zig+86-39
...@@ -33,35 +33,48 @@ pub const Span = struct {...@@ -33,35 +33,48 @@ pub const Span = struct {
33};33};
3434
35pub const Msg = struct {35pub const Msg = struct {
36 span: Span,
37 text: []u8,36 text: []u8,
37 realpath: []u8,
38 data: Data,38 data: Data,
3939
40 const Data = union(enum) {40 const Data = union(enum) {
41 Cli: Cli,
41 PathAndTree: PathAndTree,42 PathAndTree: PathAndTree,
42 ScopeAndComp: ScopeAndComp,43 ScopeAndComp: ScopeAndComp,
43 };44 };
4445
45 const PathAndTree = struct {46 const PathAndTree = struct {
46 realpath: []const u8,47 span: Span,
47 tree: *ast.Tree,48 tree: *ast.Tree,
48 allocator: *mem.Allocator,49 allocator: *mem.Allocator,
49 };50 };
5051
51 const ScopeAndComp = struct {52 const ScopeAndComp = struct {
52 root_scope: *Scope.Root,53 span: Span,
54 tree_scope: *Scope.AstTree,
53 compilation: *Compilation,55 compilation: *Compilation,
54 };56 };
5557
58 const Cli = struct {
59 allocator: *mem.Allocator,
60 };
61
56 pub fn destroy(self: *Msg) void {62 pub fn destroy(self: *Msg) void {
57 switch (self.data) {63 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 },
58 Data.PathAndTree => |path_and_tree| {69 Data.PathAndTree => |path_and_tree| {
59 path_and_tree.allocator.free(self.text);70 path_and_tree.allocator.free(self.text);
71 path_and_tree.allocator.free(self.realpath);
60 path_and_tree.allocator.destroy(self);72 path_and_tree.allocator.destroy(self);
61 },73 },
62 Data.ScopeAndComp => |scope_and_comp| {74 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);
64 scope_and_comp.compilation.gpa().free(self.text);76 scope_and_comp.compilation.gpa().free(self.text);
77 scope_and_comp.compilation.gpa().free(self.realpath);
65 scope_and_comp.compilation.gpa().destroy(self);78 scope_and_comp.compilation.gpa().destroy(self);
66 },79 },
67 }80 }
...@@ -69,6 +82,7 @@ pub const Msg = struct {...@@ -69,6 +82,7 @@ pub const Msg = struct {
6982
70 fn getAllocator(self: *const Msg) *mem.Allocator {83 fn getAllocator(self: *const Msg) *mem.Allocator {
71 switch (self.data) {84 switch (self.data) {
85 Data.Cli => |cli| return cli.allocator,
72 Data.PathAndTree => |path_and_tree| {86 Data.PathAndTree => |path_and_tree| {
73 return path_and_tree.allocator;87 return path_and_tree.allocator;
74 },88 },
...@@ -78,71 +92,93 @@ pub const Msg = struct {...@@ -78,71 +92,93 @@ pub const Msg = struct {
78 }92 }
79 }93 }
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
92 pub fn getTree(self: *const Msg) *ast.Tree {95 pub fn getTree(self: *const Msg) *ast.Tree {
93 switch (self.data) {96 switch (self.data) {
97 Data.Cli => unreachable,
94 Data.PathAndTree => |path_and_tree| {98 Data.PathAndTree => |path_and_tree| {
95 return path_and_tree.tree;99 return path_and_tree.tree;
96 },100 },
97 Data.ScopeAndComp => |scope_and_comp| {101 Data.ScopeAndComp => |scope_and_comp| {
98 return scope_and_comp.root_scope.tree;102 return scope_and_comp.tree_scope.tree;
99 },103 },
100 }104 }
101 }105 }
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
103 /// Takes ownership of text115 /// Takes ownership of text
104 /// References root_scope, and derefs when the msg is freed116 /// References tree_scope, and derefs when the msg is freed
105 pub fn createFromScope(comp: *Compilation, root_scope: *Scope.Root, span: Span, text: []u8) !*Msg {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
106 const msg = try comp.gpa().create(Msg{121 const msg = try comp.gpa().create(Msg{
107 .text = text,122 .text = text,
108 .span = span,123 .realpath = realpath,
109 .data = Data{124 .data = Data{
110 .ScopeAndComp = ScopeAndComp{125 .ScopeAndComp = ScopeAndComp{
111 .root_scope = root_scope,126 .tree_scope = tree_scope,
112 .compilation = comp,127 .compilation = comp,
128 .span = span,
113 },129 },
114 },130 },
115 });131 });
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 });
117 return msg;149 return msg;
118 }150 }
119151
120 pub fn createFromParseErrorAndScope(152 pub fn createFromParseErrorAndScope(
121 comp: *Compilation,153 comp: *Compilation,
122 root_scope: *Scope.Root,154 tree_scope: *Scope.AstTree,
123 parse_error: *const ast.Error,155 parse_error: *const ast.Error,
124 ) !*Msg {156 ) !*Msg {
125 const loc_token = parse_error.loc();157 const loc_token = parse_error.loc();
126 var text_buf = try std.Buffer.initSize(comp.gpa(), 0);158 var text_buf = try std.Buffer.initSize(comp.gpa(), 0);
127 defer text_buf.deinit();159 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
129 var out_stream = &std.io.BufferOutStream.init(&text_buf).stream;164 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
132 const msg = try comp.gpa().create(Msg{167 const msg = try comp.gpa().create(Msg{
133 .text = undefined,168 .text = undefined,
134 .span = Span{169 .realpath = realpath_copy,
135 .first = loc_token,
136 .last = loc_token,
137 },
138 .data = Data{170 .data = Data{
139 .ScopeAndComp = ScopeAndComp{171 .ScopeAndComp = ScopeAndComp{
140 .root_scope = root_scope,172 .tree_scope = tree_scope,
141 .compilation = comp,173 .compilation = comp,
174 .span = Span{
175 .first = loc_token,
176 .last = loc_token,
177 },
142 },178 },
143 },179 },
144 });180 });
145 root_scope.base.ref();181 tree_scope.base.ref();
146 msg.text = text_buf.toOwnedSlice();182 msg.text = text_buf.toOwnedSlice();
147 return msg;183 return msg;
148 }184 }
...@@ -161,22 +197,25 @@ pub const Msg = struct {...@@ -161,22 +197,25 @@ pub const Msg = struct {
161 var text_buf = try std.Buffer.initSize(allocator, 0);197 var text_buf = try std.Buffer.initSize(allocator, 0);
162 defer text_buf.deinit();198 defer text_buf.deinit();
163199
200 const realpath_copy = try mem.dupe(allocator, u8, realpath);
201 errdefer allocator.free(realpath_copy);
202
164 var out_stream = &std.io.BufferOutStream.init(&text_buf).stream;203 var out_stream = &std.io.BufferOutStream.init(&text_buf).stream;
165 try parse_error.render(&tree.tokens, out_stream);204 try parse_error.render(&tree.tokens, out_stream);
166205
167 const msg = try allocator.create(Msg{206 const msg = try allocator.create(Msg{
168 .text = undefined,207 .text = undefined,
208 .realpath = realpath_copy,
169 .data = Data{209 .data = Data{
170 .PathAndTree = PathAndTree{210 .PathAndTree = PathAndTree{
171 .allocator = allocator,211 .allocator = allocator,
172 .realpath = realpath,
173 .tree = tree,212 .tree = tree,
213 .span = Span{
214 .first = loc_token,
215 .last = loc_token,
216 },
174 },217 },
175 },218 },
176 .span = Span{
177 .first = loc_token,
178 .last = loc_token,
179 },
180 });219 });
181 msg.text = text_buf.toOwnedSlice();220 msg.text = text_buf.toOwnedSlice();
182 errdefer allocator.destroy(msg);221 errdefer allocator.destroy(msg);
...@@ -185,20 +224,28 @@ pub const Msg = struct {...@@ -185,20 +224,28 @@ pub const Msg = struct {
185 }224 }
186225
187 pub fn printToStream(msg: *const Msg, stream: var, color_on: bool) !void {226 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
188 const allocator = msg.getAllocator();235 const allocator = msg.getAllocator();
189 const realpath = msg.getRealPath();
190 const tree = msg.getTree();236 const tree = msg.getTree();
191237
192 const cwd = try os.getCwd(allocator);238 const cwd = try os.getCwd(allocator);
193 defer allocator.free(cwd);239 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);
196 defer allocator.free(relpath);242 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);247 const first_token = tree.tokens.at(span.first);
201 const last_token = tree.tokens.at(msg.span.last);248 const last_token = tree.tokens.at(span.last);
202 const start_loc = tree.tokenLocationPtr(0, first_token);249 const start_loc = tree.tokenLocationPtr(0, first_token);
203 const end_loc = tree.tokenLocationPtr(first_token.end, last_token);250 const end_loc = tree.tokenLocationPtr(first_token.end, last_token);
204 if (!color_on) {251 if (!color_on) {
src-self-hosted/ir.zig+26-22
...@@ -961,6 +961,7 @@ pub const Code = struct {...@@ -961,6 +961,7 @@ pub const Code = struct {
961 basic_block_list: std.ArrayList(*BasicBlock),961 basic_block_list: std.ArrayList(*BasicBlock),
962 arena: std.heap.ArenaAllocator,962 arena: std.heap.ArenaAllocator,
963 return_type: ?*Type,963 return_type: ?*Type,
964 tree_scope: *Scope.AstTree,
964965
965 /// allocator is comp.gpa()966 /// allocator is comp.gpa()
966 pub fn destroy(self: *Code, allocator: *Allocator) void {967 pub fn destroy(self: *Code, allocator: *Allocator) void {
...@@ -990,14 +991,14 @@ pub const Code = struct {...@@ -990,14 +991,14 @@ pub const Code = struct {
990 return ret_value.val.KnownValue.getRef();991 return ret_value.val.KnownValue.getRef();
991 }992 }
992 try comp.addCompileError(993 try comp.addCompileError(
993 ret_value.scope.findRoot(),994 self.tree_scope,
994 ret_value.span,995 ret_value.span,
995 "unable to evaluate constant expression",996 "unable to evaluate constant expression",
996 );997 );
997 return error.SemanticAnalysisFailed;998 return error.SemanticAnalysisFailed;
998 } else if (inst.hasSideEffects()) {999 } else if (inst.hasSideEffects()) {
999 try comp.addCompileError(1000 try comp.addCompileError(
1000 inst.scope.findRoot(),1001 self.tree_scope,
1001 inst.span,1002 inst.span,
1002 "unable to evaluate constant expression",1003 "unable to evaluate constant expression",
1003 );1004 );
...@@ -1013,25 +1014,24 @@ pub const Builder = struct {...@@ -1013,25 +1014,24 @@ pub const Builder = struct {
1013 code: *Code,1014 code: *Code,
1014 current_basic_block: *BasicBlock,1015 current_basic_block: *BasicBlock,
1015 next_debug_id: usize,1016 next_debug_id: usize,
1016 root_scope: *Scope.Root,
1017 is_comptime: bool,1017 is_comptime: bool,
1018 is_async: bool,1018 is_async: bool,
1019 begin_scope: ?*Scope,1019 begin_scope: ?*Scope,
10201020
1021 pub const Error = Analyze.Error;1021 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 {
1024 const code = try comp.gpa().create(Code{1024 const code = try comp.gpa().create(Code{
1025 .basic_block_list = undefined,1025 .basic_block_list = undefined,
1026 .arena = std.heap.ArenaAllocator.init(comp.gpa()),1026 .arena = std.heap.ArenaAllocator.init(comp.gpa()),
1027 .return_type = null,1027 .return_type = null,
1028 .tree_scope = tree_scope,
1028 });1029 });
1029 code.basic_block_list = std.ArrayList(*BasicBlock).init(&code.arena.allocator);1030 code.basic_block_list = std.ArrayList(*BasicBlock).init(&code.arena.allocator);
1030 errdefer code.destroy(comp.gpa());1031 errdefer code.destroy(comp.gpa());
10311032
1032 return Builder{1033 return Builder{
1033 .comp = comp,1034 .comp = comp,
1034 .root_scope = root_scope,
1035 .current_basic_block = undefined,1035 .current_basic_block = undefined,
1036 .code = code,1036 .code = code,
1037 .next_debug_id = 0,1037 .next_debug_id = 0,
...@@ -1292,6 +1292,7 @@ pub const Builder = struct {...@@ -1292,6 +1292,7 @@ pub const Builder = struct {
1292 Scope.Id.FnDef => return false,1292 Scope.Id.FnDef => return false,
1293 Scope.Id.Decls => unreachable,1293 Scope.Id.Decls => unreachable,
1294 Scope.Id.Root => unreachable,1294 Scope.Id.Root => unreachable,
1295 Scope.Id.AstTree => unreachable,
1295 Scope.Id.Block,1296 Scope.Id.Block,
1296 Scope.Id.Defer,1297 Scope.Id.Defer,
1297 Scope.Id.DeferExpr,1298 Scope.Id.DeferExpr,
...@@ -1302,7 +1303,7 @@ pub const Builder = struct {...@@ -1302,7 +1303,7 @@ pub const Builder = struct {
1302 }1303 }
13031304
1304 pub fn genIntLit(irb: *Builder, int_lit: *ast.Node.IntegerLiteral, scope: *Scope) !*Inst {1305 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
1307 var base: u8 = undefined;1308 var base: u8 = undefined;
1308 var rest: []const u8 = undefined;1309 var rest: []const u8 = undefined;
...@@ -1341,7 +1342,7 @@ pub const Builder = struct {...@@ -1341,7 +1342,7 @@ pub const Builder = struct {
1341 }1342 }
13421343
1343 pub async fn genStrLit(irb: *Builder, str_lit: *ast.Node.StringLiteral, scope: *Scope) !*Inst {1344 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);
1345 const src_span = Span.token(str_lit.token);1346 const src_span = Span.token(str_lit.token);
13461347
1347 var bad_index: usize = undefined;1348 var bad_index: usize = undefined;
...@@ -1349,7 +1350,7 @@ pub const Builder = struct {...@@ -1349,7 +1350,7 @@ pub const Builder = struct {
1349 error.OutOfMemory => return error.OutOfMemory,1350 error.OutOfMemory => return error.OutOfMemory,
1350 error.InvalidCharacter => {1351 error.InvalidCharacter => {
1351 try irb.comp.addCompileError(1352 try irb.comp.addCompileError(
1352 irb.root_scope,1353 irb.code.tree_scope,
1353 src_span,1354 src_span,
1354 "invalid character in string literal: '{c}'",1355 "invalid character in string literal: '{c}'",
1355 str_token[bad_index],1356 str_token[bad_index],
...@@ -1427,7 +1428,7 @@ pub const Builder = struct {...@@ -1427,7 +1428,7 @@ pub const Builder = struct {
14271428
1428 if (statement_node.cast(ast.Node.Defer)) |defer_node| {1429 if (statement_node.cast(ast.Node.Defer)) |defer_node| {
1429 // defer starts a new scope1430 // 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);
1431 const kind = switch (defer_token.id) {1432 const kind = switch (defer_token.id) {
1432 Token.Id.Keyword_defer => Scope.Defer.Kind.ScopeExit,1433 Token.Id.Keyword_defer => Scope.Defer.Kind.ScopeExit,
1433 Token.Id.Keyword_errdefer => Scope.Defer.Kind.ErrorExit,1434 Token.Id.Keyword_errdefer => Scope.Defer.Kind.ErrorExit,
...@@ -1513,7 +1514,7 @@ pub const Builder = struct {...@@ -1513,7 +1514,7 @@ pub const Builder = struct {
1513 const src_span = Span.token(control_flow_expr.ltoken);1514 const src_span = Span.token(control_flow_expr.ltoken);
1514 if (scope.findFnDef() == null) {1515 if (scope.findFnDef() == null) {
1515 try irb.comp.addCompileError(1516 try irb.comp.addCompileError(
1516 irb.root_scope,1517 irb.code.tree_scope,
1517 src_span,1518 src_span,
1518 "return expression outside function definition",1519 "return expression outside function definition",
1519 );1520 );
...@@ -1523,7 +1524,7 @@ pub const Builder = struct {...@@ -1523,7 +1524,7 @@ pub const Builder = struct {
1523 if (scope.findDeferExpr()) |scope_defer_expr| {1524 if (scope.findDeferExpr()) |scope_defer_expr| {
1524 if (!scope_defer_expr.reported_err) {1525 if (!scope_defer_expr.reported_err) {
1525 try irb.comp.addCompileError(1526 try irb.comp.addCompileError(
1526 irb.root_scope,1527 irb.code.tree_scope,
1527 src_span,1528 src_span,
1528 "cannot return from defer expression",1529 "cannot return from defer expression",
1529 );1530 );
...@@ -1599,7 +1600,7 @@ pub const Builder = struct {...@@ -1599,7 +1600,7 @@ pub const Builder = struct {
15991600
1600 pub async fn genIdentifier(irb: *Builder, identifier: *ast.Node.Identifier, scope: *Scope, lval: LVal) !*Inst {1601 pub async fn genIdentifier(irb: *Builder, identifier: *ast.Node.Identifier, scope: *Scope, lval: LVal) !*Inst {
1601 const src_span = Span.token(identifier.token);1602 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
1604 //if (buf_eql_str(variable_name, "_") && lval == LValPtr) {1605 //if (buf_eql_str(variable_name, "_") && lval == LValPtr) {
1605 // IrInstructionConst *const_instruction = ir_build_instruction<IrInstructionConst>(irb, scope, node);1606 // IrInstructionConst *const_instruction = ir_build_instruction<IrInstructionConst>(irb, scope, node);
...@@ -1622,7 +1623,7 @@ pub const Builder = struct {...@@ -1622,7 +1623,7 @@ pub const Builder = struct {
1622 }1623 }
1623 } else |err| switch (err) {1624 } else |err| switch (err) {
1624 error.Overflow => {1625 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");
1626 return error.SemanticAnalysisFailed;1627 return error.SemanticAnalysisFailed;
1627 },1628 },
1628 error.OutOfMemory => return error.OutOfMemory,1629 error.OutOfMemory => return error.OutOfMemory,
...@@ -1656,7 +1657,7 @@ pub const Builder = struct {...@@ -1656,7 +1657,7 @@ pub const Builder = struct {
1656 // TODO put a variable of same name with invalid type in global scope1657 // TODO put a variable of same name with invalid type in global scope
1657 // so that future references to this same name will find a variable with an invalid type1658 // 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);
1660 return error.SemanticAnalysisFailed;1661 return error.SemanticAnalysisFailed;
1661 }1662 }
16621663
...@@ -1689,6 +1690,7 @@ pub const Builder = struct {...@@ -1689,6 +1690,7 @@ pub const Builder = struct {
1689 => scope = scope.parent orelse break,1690 => scope = scope.parent orelse break,
16901691
1691 Scope.Id.DeferExpr => unreachable,1692 Scope.Id.DeferExpr => unreachable,
1693 Scope.Id.AstTree => unreachable,
1692 }1694 }
1693 }1695 }
1694 return result;1696 return result;
...@@ -1740,6 +1742,7 @@ pub const Builder = struct {...@@ -1740,6 +1742,7 @@ pub const Builder = struct {
1740 => scope = scope.parent orelse return is_noreturn,1742 => scope = scope.parent orelse return is_noreturn,
17411743
1742 Scope.Id.DeferExpr => unreachable,1744 Scope.Id.DeferExpr => unreachable,
1745 Scope.Id.AstTree => unreachable,
1743 }1746 }
1744 }1747 }
1745 }1748 }
...@@ -1929,8 +1932,9 @@ pub const Builder = struct {...@@ -1929,8 +1932,9 @@ pub const Builder = struct {
1929 Scope.Id.Root => return Ident.NotFound,1932 Scope.Id.Root => return Ident.NotFound,
1930 Scope.Id.Decls => {1933 Scope.Id.Decls => {
1931 const decls = @fieldParentPtr(Scope.Decls, "base", s);1934 const decls = @fieldParentPtr(Scope.Decls, "base", s);
1932 const table = await (async decls.getTableReadOnly() catch unreachable);1935 const locked_table = await (async decls.table.acquireRead() catch unreachable);
1933 if (table.get(name)) |entry| {1936 defer locked_table.release();
1937 if (locked_table.value.get(name)) |entry| {
1934 return Ident{ .Decl = entry.value };1938 return Ident{ .Decl = entry.value };
1935 }1939 }
1936 },1940 },
...@@ -1967,8 +1971,8 @@ const Analyze = struct {...@@ -1967,8 +1971,8 @@ const Analyze = struct {
1967 OutOfMemory,1971 OutOfMemory,
1968 };1972 };
19691973
1970 pub fn init(comp: *Compilation, root_scope: *Scope.Root, explicit_return_type: ?*Type) !Analyze {1974 pub fn init(comp: *Compilation, tree_scope: *Scope.AstTree, explicit_return_type: ?*Type) !Analyze {
1971 var irb = try Builder.init(comp, root_scope, null);1975 var irb = try Builder.init(comp, tree_scope, null);
1972 errdefer irb.abort();1976 errdefer irb.abort();
19731977
1974 return Analyze{1978 return Analyze{
...@@ -2046,7 +2050,7 @@ const Analyze = struct {...@@ -2046,7 +2050,7 @@ const Analyze = struct {
2046 }2050 }
20472051
2048 fn addCompileError(self: *Analyze, span: Span, comptime fmt: []const u8, args: ...) !void {2052 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);
2050 }2054 }
20512055
2052 fn resolvePeerTypes(self: *Analyze, expected_type: ?*Type, peers: []const *Inst) Analyze.Error!*Type {2056 fn resolvePeerTypes(self: *Analyze, expected_type: ?*Type, peers: []const *Inst) Analyze.Error!*Type {
...@@ -2534,9 +2538,10 @@ const Analyze = struct {...@@ -2534,9 +2538,10 @@ const Analyze = struct {
2534pub async fn gen(2538pub async fn gen(
2535 comp: *Compilation,2539 comp: *Compilation,
2536 body_node: *ast.Node,2540 body_node: *ast.Node,
2541 tree_scope: *Scope.AstTree,
2537 scope: *Scope,2542 scope: *Scope,
2538) !*Code {2543) !*Code {
2539 var irb = try Builder.init(comp, scope.findRoot(), scope);2544 var irb = try Builder.init(comp, tree_scope, scope);
2540 errdefer irb.abort();2545 errdefer irb.abort();
25412546
2542 const entry_block = try irb.createBasicBlock(scope, c"Entry");2547 const entry_block = try irb.createBasicBlock(scope, c"Entry");
...@@ -2554,9 +2559,8 @@ pub async fn gen(...@@ -2554,9 +2559,8 @@ pub async fn gen(
25542559
2555pub async fn analyze(comp: *Compilation, old_code: *Code, expected_type: ?*Type) !*Code {2560pub async fn analyze(comp: *Compilation, old_code: *Code, expected_type: ?*Type) !*Code {
2556 const old_entry_bb = old_code.basic_block_list.at(0);2561 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);
2560 errdefer ira.abort();2564 errdefer ira.abort();
25612565
2562 const new_entry_bb = try ira.getNewBasicBlock(old_entry_bb, null);2566 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 {...@@ -143,7 +143,7 @@ pub const LibCInstallation = struct {
143 pub async fn findNative(self: *LibCInstallation, loop: *event.Loop) !void {143 pub async fn findNative(self: *LibCInstallation, loop: *event.Loop) !void {
144 self.initEmpty();144 self.initEmpty();
145 var group = event.Group(FindError!void).init(loop);145 var group = event.Group(FindError!void).init(loop);
146 errdefer group.cancelAll();146 errdefer group.deinit();
147 var windows_sdk: ?*c.ZigWindowsSDK = null;147 var windows_sdk: ?*c.ZigWindowsSDK = null;
148 errdefer if (windows_sdk) |sdk| c.zig_free_windows_sdk(@ptrCast(?[*]c.ZigWindowsSDK, sdk));148 errdefer if (windows_sdk) |sdk| c.zig_free_windows_sdk(@ptrCast(?[*]c.ZigWindowsSDK, sdk));
149149
...@@ -313,7 +313,7 @@ pub const LibCInstallation = struct {...@@ -313,7 +313,7 @@ pub const LibCInstallation = struct {
313 },313 },
314 };314 };
315 var group = event.Group(FindError!void).init(loop);315 var group = event.Group(FindError!void).init(loop);
316 errdefer group.cancelAll();316 errdefer group.deinit();
317 for (dyn_tests) |*dyn_test| {317 for (dyn_tests) |*dyn_test| {
318 try group.call(testNativeDynamicLinker, self, loop, dyn_test);318 try group.call(testNativeDynamicLinker, self, loop, dyn_test);
319 }319 }
...@@ -341,7 +341,6 @@ pub const LibCInstallation = struct {...@@ -341,7 +341,6 @@ pub const LibCInstallation = struct {
341 }341 }
342 }342 }
343343
344
345 async fn findNativeKernel32LibDir(self: *LibCInstallation, loop: *event.Loop, sdk: *c.ZigWindowsSDK) FindError!void {344 async fn findNativeKernel32LibDir(self: *LibCInstallation, loop: *event.Loop, sdk: *c.ZigWindowsSDK) FindError!void {
346 var search_buf: [2]Search = undefined;345 var search_buf: [2]Search = undefined;
347 const searches = fillSearch(&search_buf, sdk);346 const searches = fillSearch(&search_buf, sdk);
...@@ -450,7 +449,6 @@ fn fillSearch(search_buf: *[2]Search, sdk: *c.ZigWindowsSDK) []Search {...@@ -450,7 +449,6 @@ fn fillSearch(search_buf: *[2]Search, sdk: *c.ZigWindowsSDK) []Search {
450 return search_buf[0..search_end];449 return search_buf[0..search_end];
451}450}
452451
453
454fn fileExists(allocator: *std.mem.Allocator, path: []const u8) !bool {452fn fileExists(allocator: *std.mem.Allocator, path: []const u8) !bool {
455 if (std.os.File.access(allocator, path)) |_| {453 if (std.os.File.access(allocator, path)) |_| {
456 return true;454 return true;
src-self-hosted/link.zig+2-2
...@@ -61,7 +61,7 @@ pub async fn link(comp: *Compilation) !void {...@@ -61,7 +61,7 @@ pub async fn link(comp: *Compilation) !void {
61 ctx.libc = ctx.comp.override_libc orelse blk: {61 ctx.libc = ctx.comp.override_libc orelse blk: {
62 switch (comp.target) {62 switch (comp.target) {
63 Target.Native => {63 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;
65 },65 },
66 else => return error.LibCRequiredButNotProvidedOrFound,66 else => return error.LibCRequiredButNotProvidedOrFound,
67 }67 }
...@@ -83,7 +83,7 @@ pub async fn link(comp: *Compilation) !void {...@@ -83,7 +83,7 @@ pub async fn link(comp: *Compilation) !void {
8383
84 {84 {
85 // LLD is not thread-safe, so we grab a global lock.85 // 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);
87 defer held.release();87 defer held.release();
8888
89 // Not evented I/O. LLD does its own multithreading internally.89 // 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");...@@ -14,7 +14,7 @@ const c = @import("c.zig");
14const introspect = @import("introspect.zig");14const introspect = @import("introspect.zig");
15const Args = arg.Args;15const Args = arg.Args;
16const Flag = arg.Flag;16const Flag = arg.Flag;
17const EventLoopLocal = @import("compilation.zig").EventLoopLocal;17const ZigCompiler = @import("compilation.zig").ZigCompiler;
18const Compilation = @import("compilation.zig").Compilation;18const Compilation = @import("compilation.zig").Compilation;
19const Target = @import("target.zig").Target;19const Target = @import("target.zig").Target;
20const errmsg = @import("errmsg.zig");20const errmsg = @import("errmsg.zig");
...@@ -24,6 +24,8 @@ var stderr_file: os.File = undefined;...@@ -24,6 +24,8 @@ var stderr_file: os.File = undefined;
24var stderr: *io.OutStream(io.FileOutStream.Error) = undefined;24var stderr: *io.OutStream(io.FileOutStream.Error) = undefined;
25var stdout: *io.OutStream(io.FileOutStream.Error) = undefined;25var stdout: *io.OutStream(io.FileOutStream.Error) = undefined;
2626
27const max_src_size = 2 * 1024 * 1024 * 1024; // 2 GiB
28
27const usage =29const usage =
28 \\usage: zig [command] [options]30 \\usage: zig [command] [options]
29 \\31 \\
...@@ -371,6 +373,16 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Co...@@ -371,6 +373,16 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Co
371 os.exit(1);373 os.exit(1);
372 }374 }
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
374 const zig_lib_dir = introspect.resolveZigLibDir(allocator) catch os.exit(1);386 const zig_lib_dir = introspect.resolveZigLibDir(allocator) catch os.exit(1);
375 defer allocator.free(zig_lib_dir);387 defer allocator.free(zig_lib_dir);
376388
...@@ -380,11 +392,11 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Co...@@ -380,11 +392,11 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Co
380 try loop.initMultiThreaded(allocator);392 try loop.initMultiThreaded(allocator);
381 defer loop.deinit();393 defer loop.deinit();
382394
383 var event_loop_local = try EventLoopLocal.init(&loop);395 var zig_compiler = try ZigCompiler.init(&loop);
384 defer event_loop_local.deinit();396 defer zig_compiler.deinit();
385397
386 var comp = try Compilation.create(398 var comp = try Compilation.create(
387 &event_loop_local,399 &zig_compiler,
388 root_name,400 root_name,
389 root_source_file,401 root_source_file,
390 Target.Native,402 Target.Native,
...@@ -413,16 +425,6 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Co...@@ -413,16 +425,6 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Co
413 comp.linker_script = flags.single("linker-script");425 comp.linker_script = flags.single("linker-script");
414 comp.each_lib_rpath = flags.present("each-lib-rpath");426 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;
426 comp.clang_argv = clang_argv_buf.toSliceConst();428 comp.clang_argv = clang_argv_buf.toSliceConst();
427429
428 comp.strip = flags.present("strip");430 comp.strip = flags.present("strip");
...@@ -465,30 +467,34 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Co...@@ -465,30 +467,34 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Co
465 comp.link_out_file = flags.single("output");467 comp.link_out_file = flags.single("output");
466 comp.link_objects = link_objects;468 comp.link_objects = link_objects;
467469
468 try comp.build();470 comp.start();
469 const process_build_events_handle = try async<loop.allocator> processBuildEvents(comp, color);471 const process_build_events_handle = try async<loop.allocator> processBuildEvents(comp, color);
470 defer cancel process_build_events_handle;472 defer cancel process_build_events_handle;
471 loop.run();473 loop.run();
472}474}
473475
474async fn processBuildEvents(comp: *Compilation, color: errmsg.Color) void {476async fn processBuildEvents(comp: *Compilation, color: errmsg.Color) void {
475 // TODO directly awaiting async should guarantee memory allocation elision477 var count: usize = 0;
476 const build_event = await (async comp.events.get() catch unreachable);478 while (true) {
477479 // TODO directly awaiting async should guarantee memory allocation elision
478 switch (build_event) {480 const build_event = await (async comp.events.get() catch unreachable);
479 Compilation.Event.Ok => {481 count += 1;
480 return;482
481 },483 switch (build_event) {
482 Compilation.Event.Error => |err| {484 Compilation.Event.Ok => {
483 std.debug.warn("build failed: {}\n", @errorName(err));485 stderr.print("Build {} succeeded\n", count) catch os.exit(1);
484 os.exit(1);486 },
485 },487 Compilation.Event.Error => |err| {
486 Compilation.Event.Fail => |msgs| {488 stderr.print("Build {} failed: {}\n", count, @errorName(err)) catch os.exit(1);
487 for (msgs) |msg| {489 },
488 defer msg.destroy();490 Compilation.Event.Fail => |msgs| {
489 msg.printToFile(&stderr_file, color) catch os.exit(1);491 stderr.print("Build {} compile errors:\n", count) catch os.exit(1);
490 }492 for (msgs) |msg| {
491 },493 defer msg.destroy();
494 msg.printToFile(&stderr_file, color) catch os.exit(1);
495 }
496 },
497 }
492 }498 }
493}499}
494500
...@@ -528,33 +534,12 @@ const args_fmt_spec = []Flag{...@@ -528,33 +534,12 @@ const args_fmt_spec = []Flag{
528};534};
529535
530const Fmt = struct {536const Fmt = struct {
531 seen: std.HashMap([]const u8, void, mem.hash_slice_u8, mem.eql_slice_u8),537 seen: event.Locked(SeenMap),
532 queue: std.LinkedList([]const u8),
533 any_error: bool,538 any_error: bool,
539 color: errmsg.Color,
540 loop: *event.Loop,
534541
535 // file_path must outlive Fmt542 const SeenMap = std.HashMap([]const u8, void, mem.hash_slice_u8, mem.eql_slice_u8);
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 }
558};543};
559544
560fn parseLibcPaths(allocator: *Allocator, libc: *LibCInstallation, libc_paths_file: []const u8) void {545fn parseLibcPaths(allocator: *Allocator, libc: *LibCInstallation, libc_paths_file: []const u8) void {
...@@ -587,17 +572,17 @@ fn cmdLibC(allocator: *Allocator, args: []const []const u8) !void {...@@ -587,17 +572,17 @@ fn cmdLibC(allocator: *Allocator, args: []const []const u8) !void {
587 try loop.initMultiThreaded(allocator);572 try loop.initMultiThreaded(allocator);
588 defer loop.deinit();573 defer loop.deinit();
589574
590 var event_loop_local = try EventLoopLocal.init(&loop);575 var zig_compiler = try ZigCompiler.init(&loop);
591 defer event_loop_local.deinit();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);
594 defer cancel handle;579 defer cancel handle;
595580
596 loop.run();581 loop.run();
597}582}
598583
599async fn findLibCAsync(event_loop_local: *EventLoopLocal) void {584async fn findLibCAsync(zig_compiler: *ZigCompiler) void {
600 const libc = (await (async event_loop_local.getNativeLibC() catch unreachable)) catch |err| {585 const libc = (await (async zig_compiler.getNativeLibC() catch unreachable)) catch |err| {
601 stderr.print("unable to find libc: {}\n", @errorName(err)) catch os.exit(1);586 stderr.print("unable to find libc: {}\n", @errorName(err)) catch os.exit(1);
602 os.exit(1);587 os.exit(1);
603 };588 };
...@@ -636,7 +621,7 @@ fn cmdFmt(allocator: *Allocator, args: []const []const u8) !void {...@@ -636,7 +621,7 @@ fn cmdFmt(allocator: *Allocator, args: []const []const u8) !void {
636 var stdin_file = try io.getStdIn();621 var stdin_file = try io.getStdIn();
637 var stdin = io.FileInStream.init(&stdin_file);622 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);
640 defer allocator.free(source_code);625 defer allocator.free(source_code);
641626
642 var tree = std.zig.parse(allocator, source_code) catch |err| {627 var tree = std.zig.parse(allocator, source_code) catch |err| {
...@@ -665,66 +650,143 @@ fn cmdFmt(allocator: *Allocator, args: []const []const u8) !void {...@@ -665,66 +650,143 @@ fn cmdFmt(allocator: *Allocator, args: []const []const u8) !void {
665 os.exit(1);650 os.exit(1);
666 }651 }
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 }
668 var fmt = Fmt{707 var fmt = Fmt{
669 .seen = std.HashMap([]const u8, void, mem.hash_slice_u8, mem.eql_slice_u8).init(allocator),708 .seen = event.Locked(Fmt.SeenMap).init(loop, Fmt.SeenMap.init(loop.allocator)),
670 .queue = std.LinkedList([]const u8).init(),
671 .any_error = false,709 .any_error = false,
710 .color = color,
711 .loop = loop,
672 };712 };
673713
714 var group = event.Group(FmtError!void).init(loop);
674 for (flags.positionals.toSliceConst()) |file_path| {715 for (flags.positionals.toSliceConst()) |file_path| {
675 try fmt.addToQueue(file_path);716 try group.call(fmtPath, &fmt, file_path);
676 }717 }
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| {724async fn fmtPath(fmt: *Fmt, file_path_ref: []const u8) FmtError!void {
679 const file_path = node.data;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);728 {
682 defer file.close();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) {732 if (try held.value.put(file_path, {})) |_| return;
685 error.IsDir => {733 }
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);
696734
697 var tree = std.zig.parse(allocator, source_code) catch |err| {735 const source_code = (await try async event.fs.readFile(
698 try stderr.print("error parsing file '{}': {}\n", file_path, err);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);
699 fmt.any_error = true;757 fmt.any_error = true;
700 continue;758 return;
701 };759 },
702 defer tree.deinit();760 };
703761 defer fmt.loop.allocator.free(source_code);
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();
708762
709 try msg.printToFile(&stderr_file, color);763 var tree = std.zig.parse(fmt.loop.allocator, source_code) catch |err| {
710 }764 try stderr.print("error parsing file '{}': {}\n", file_path, err);
711 if (tree.errors.len != 0) {765 fmt.any_error = true;
712 fmt.any_error = true;766 return;
713 continue;767 };
714 }768 defer tree.deinit();
715769
716 const baf = try io.BufferedAtomicFile.create(allocator, file_path);770 var error_it = tree.errors.iterator(0);
717 defer baf.destroy();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);775 try msg.printToFile(&stderr_file, fmt.color);
720 if (anything_changed) {776 }
721 try stderr.print("{}\n", file_path);777 if (tree.errors.len != 0) {
722 try baf.finish();778 fmt.any_error = true;
723 }779 return;
724 }780 }
725781
726 if (fmt.any_error) {782 // TODO make this evented
727 os.exit(1);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();
728 }790 }
729}791}
730792
src-self-hosted/scope.zig+45-21
...@@ -36,6 +36,7 @@ pub const Scope = struct {...@@ -36,6 +36,7 @@ pub const Scope = struct {
36 Id.Defer => @fieldParentPtr(Defer, "base", base).destroy(comp),36 Id.Defer => @fieldParentPtr(Defer, "base", base).destroy(comp),
37 Id.DeferExpr => @fieldParentPtr(DeferExpr, "base", base).destroy(comp),37 Id.DeferExpr => @fieldParentPtr(DeferExpr, "base", base).destroy(comp),
38 Id.Var => @fieldParentPtr(Var, "base", base).destroy(comp),38 Id.Var => @fieldParentPtr(Var, "base", base).destroy(comp),
39 Id.AstTree => @fieldParentPtr(AstTree, "base", base).destroy(comp),
39 }40 }
40 }41 }
41 }42 }
...@@ -62,6 +63,8 @@ pub const Scope = struct {...@@ -62,6 +63,8 @@ pub const Scope = struct {
62 Id.CompTime,63 Id.CompTime,
63 Id.Var,64 Id.Var,
64 => scope = scope.parent.?,65 => scope = scope.parent.?,
66
67 Id.AstTree => unreachable,
65 }68 }
66 }69 }
67 }70 }
...@@ -82,6 +85,8 @@ pub const Scope = struct {...@@ -82,6 +85,8 @@ pub const Scope = struct {
82 Id.Root,85 Id.Root,
83 Id.Var,86 Id.Var,
84 => scope = scope.parent orelse return null,87 => scope = scope.parent orelse return null,
88
89 Id.AstTree => unreachable,
85 }90 }
86 }91 }
87 }92 }
...@@ -97,6 +102,7 @@ pub const Scope = struct {...@@ -97,6 +102,7 @@ pub const Scope = struct {
97102
98 pub const Id = enum {103 pub const Id = enum {
99 Root,104 Root,
105 AstTree,
100 Decls,106 Decls,
101 Block,107 Block,
102 FnDef,108 FnDef,
...@@ -108,13 +114,12 @@ pub const Scope = struct {...@@ -108,13 +114,12 @@ pub const Scope = struct {
108114
109 pub const Root = struct {115 pub const Root = struct {
110 base: Scope,116 base: Scope,
111 tree: *ast.Tree,
112 realpath: []const u8,117 realpath: []const u8,
118 decls: *Decls,
113119
114 /// Creates a Root scope with 1 reference120 /// Creates a Root scope with 1 reference
115 /// Takes ownership of realpath121 /// Takes ownership of realpath
116 /// Takes ownership of tree, will deinit and destroy when done.122 pub fn create(comp: *Compilation, realpath: []u8) !*Root {
117 pub fn create(comp: *Compilation, tree: *ast.Tree, realpath: []u8) !*Root {
118 const self = try comp.gpa().createOne(Root);123 const self = try comp.gpa().createOne(Root);
119 self.* = Root{124 self.* = Root{
120 .base = Scope{125 .base = Scope{
...@@ -122,41 +127,65 @@ pub const Scope = struct {...@@ -122,41 +127,65 @@ pub const Scope = struct {
122 .parent = null,127 .parent = null,
123 .ref_count = std.atomic.Int(usize).init(1),128 .ref_count = std.atomic.Int(usize).init(1),
124 },129 },
125 .tree = tree,
126 .realpath = realpath,130 .realpath = realpath,
131 .decls = undefined,
127 };132 };
128133 errdefer comp.gpa().destroy(self);
134 self.decls = try Decls.create(comp, &self.base);
129 return self;135 return self;
130 }136 }
131137
132 pub fn destroy(self: *Root, comp: *Compilation) void {138 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 {
133 comp.gpa().free(self.tree.source);164 comp.gpa().free(self.tree.source);
134 self.tree.deinit();165 self.tree.deinit();
135 comp.gpa().destroy(self.tree);166 comp.gpa().destroy(self.tree);
136 comp.gpa().free(self.realpath);
137 comp.gpa().destroy(self);167 comp.gpa().destroy(self);
138 }168 }
169
170 pub fn root(self: *AstTree) *Root {
171 return self.base.findRoot();
172 }
139 };173 };
140174
141 pub const Decls = struct {175 pub const Decls = struct {
142 base: Scope,176 base: Scope,
143177
144 /// The lock must be respected for writing. However once name_future resolves,178 /// This table remains Write Locked when the names are incomplete or possibly outdated.
145 /// readers can freely access it.179 /// So if a reader manages to grab a lock, it can be sure that the set of names is complete
146 table: event.Locked(Decl.Table),180 /// and correct.
147181 table: event.RwLocked(Decl.Table),
148 /// Once this future is resolved, the table is complete and available for unlocked
149 /// read-only access. It does not mean all the decls are resolved; it means only that
150 /// the table has all the names. Each decl in the table has its own resolution state.
151 name_future: event.Future(void),
152182
153 /// Creates a Decls scope with 1 reference183 /// Creates a Decls scope with 1 reference
154 pub fn create(comp: *Compilation, parent: *Scope) !*Decls {184 pub fn create(comp: *Compilation, parent: *Scope) !*Decls {
155 const self = try comp.gpa().createOne(Decls);185 const self = try comp.gpa().createOne(Decls);
156 self.* = Decls{186 self.* = Decls{
157 .base = undefined,187 .base = undefined,
158 .table = event.Locked(Decl.Table).init(comp.loop, Decl.Table.init(comp.gpa())),188 .table = event.RwLocked(Decl.Table).init(comp.loop, Decl.Table.init(comp.gpa())),
159 .name_future = event.Future(void).init(comp.loop),
160 };189 };
161 self.base.init(Id.Decls, parent);190 self.base.init(Id.Decls, parent);
162 return self;191 return self;
...@@ -166,11 +195,6 @@ pub const Scope = struct {...@@ -166,11 +195,6 @@ pub const Scope = struct {
166 self.table.deinit();195 self.table.deinit();
167 comp.gpa().destroy(self);196 comp.gpa().destroy(self);
168 }197 }
169
170 pub async fn getTableReadOnly(self: *Decls) *Decl.Table {
171 _ = await (async self.name_future.get() catch unreachable);
172 return &self.table.private_data;
173 }
174 };198 };
175199
176 pub const Block = struct {200 pub const Block = struct {
src-self-hosted/test.zig+16-15
...@@ -6,7 +6,7 @@ const Compilation = @import("compilation.zig").Compilation;...@@ -6,7 +6,7 @@ const Compilation = @import("compilation.zig").Compilation;
6const introspect = @import("introspect.zig");6const introspect = @import("introspect.zig");
7const assertOrPanic = std.debug.assertOrPanic;7const assertOrPanic = std.debug.assertOrPanic;
8const errmsg = @import("errmsg.zig");8const errmsg = @import("errmsg.zig");
9const EventLoopLocal = @import("compilation.zig").EventLoopLocal;9const ZigCompiler = @import("compilation.zig").ZigCompiler;
1010
11var ctx: TestContext = undefined;11var ctx: TestContext = undefined;
1212
...@@ -25,7 +25,7 @@ const allocator = std.heap.c_allocator;...@@ -25,7 +25,7 @@ const allocator = std.heap.c_allocator;
2525
26pub const TestContext = struct {26pub const TestContext = struct {
27 loop: std.event.Loop,27 loop: std.event.Loop,
28 event_loop_local: EventLoopLocal,28 zig_compiler: ZigCompiler,
29 zig_lib_dir: []u8,29 zig_lib_dir: []u8,
30 file_index: std.atomic.Int(usize),30 file_index: std.atomic.Int(usize),
31 group: std.event.Group(error!void),31 group: std.event.Group(error!void),
...@@ -37,20 +37,20 @@ pub const TestContext = struct {...@@ -37,20 +37,20 @@ pub const TestContext = struct {
37 self.* = TestContext{37 self.* = TestContext{
38 .any_err = {},38 .any_err = {},
39 .loop = undefined,39 .loop = undefined,
40 .event_loop_local = undefined,40 .zig_compiler = undefined,
41 .zig_lib_dir = undefined,41 .zig_lib_dir = undefined,
42 .group = undefined,42 .group = undefined,
43 .file_index = std.atomic.Int(usize).init(0),43 .file_index = std.atomic.Int(usize).init(0),
44 };44 };
4545
46 try self.loop.initMultiThreaded(allocator);46 try self.loop.initSingleThreaded(allocator);
47 errdefer self.loop.deinit();47 errdefer self.loop.deinit();
4848
49 self.event_loop_local = try EventLoopLocal.init(&self.loop);49 self.zig_compiler = try ZigCompiler.init(&self.loop);
50 errdefer self.event_loop_local.deinit();50 errdefer self.zig_compiler.deinit();
5151
52 self.group = std.event.Group(error!void).init(&self.loop);52 self.group = std.event.Group(error!void).init(&self.loop);
53 errdefer self.group.cancelAll();53 errdefer self.group.deinit();
5454
55 self.zig_lib_dir = try introspect.resolveZigLibDir(allocator);55 self.zig_lib_dir = try introspect.resolveZigLibDir(allocator);
56 errdefer allocator.free(self.zig_lib_dir);56 errdefer allocator.free(self.zig_lib_dir);
...@@ -62,7 +62,7 @@ pub const TestContext = struct {...@@ -62,7 +62,7 @@ pub const TestContext = struct {
62 fn deinit(self: *TestContext) void {62 fn deinit(self: *TestContext) void {
63 std.os.deleteTree(allocator, tmp_dir_name) catch {};63 std.os.deleteTree(allocator, tmp_dir_name) catch {};
64 allocator.free(self.zig_lib_dir);64 allocator.free(self.zig_lib_dir);
65 self.event_loop_local.deinit();65 self.zig_compiler.deinit();
66 self.loop.deinit();66 self.loop.deinit();
67 }67 }
6868
...@@ -97,7 +97,7 @@ pub const TestContext = struct {...@@ -97,7 +97,7 @@ pub const TestContext = struct {
97 try std.io.writeFile(allocator, file1_path, source);97 try std.io.writeFile(allocator, file1_path, source);
9898
99 var comp = try Compilation.create(99 var comp = try Compilation.create(
100 &self.event_loop_local,100 &self.zig_compiler,
101 "test",101 "test",
102 file1_path,102 file1_path,
103 Target.Native,103 Target.Native,
...@@ -108,7 +108,7 @@ pub const TestContext = struct {...@@ -108,7 +108,7 @@ pub const TestContext = struct {
108 );108 );
109 errdefer comp.destroy();109 errdefer comp.destroy();
110110
111 try comp.build();111 comp.start();
112112
113 try self.group.call(getModuleEvent, comp, source, path, line, column, msg);113 try self.group.call(getModuleEvent, comp, source, path, line, column, msg);
114 }114 }
...@@ -131,7 +131,7 @@ pub const TestContext = struct {...@@ -131,7 +131,7 @@ pub const TestContext = struct {
131 try std.io.writeFile(allocator, file1_path, source);131 try std.io.writeFile(allocator, file1_path, source);
132132
133 var comp = try Compilation.create(133 var comp = try Compilation.create(
134 &self.event_loop_local,134 &self.zig_compiler,
135 "test",135 "test",
136 file1_path,136 file1_path,
137 Target.Native,137 Target.Native,
...@@ -144,7 +144,7 @@ pub const TestContext = struct {...@@ -144,7 +144,7 @@ pub const TestContext = struct {
144144
145 _ = try comp.addLinkLib("c", true);145 _ = try comp.addLinkLib("c", true);
146 comp.link_out_file = output_file;146 comp.link_out_file = output_file;
147 try comp.build();147 comp.start();
148148
149 try self.group.call(getModuleEventSuccess, comp, output_file, expected_output);149 try self.group.call(getModuleEventSuccess, comp, output_file, expected_output);
150 }150 }
...@@ -212,9 +212,10 @@ pub const TestContext = struct {...@@ -212,9 +212,10 @@ pub const TestContext = struct {
212 Compilation.Event.Fail => |msgs| {212 Compilation.Event.Fail => |msgs| {
213 assertOrPanic(msgs.len != 0);213 assertOrPanic(msgs.len != 0);
214 for (msgs) |msg| {214 for (msgs) |msg| {
215 if (mem.endsWith(u8, msg.getRealPath(), path) and mem.eql(u8, msg.text, text)) {215 if (mem.endsWith(u8, msg.realpath, path) and mem.eql(u8, msg.text, text)) {
216 const first_token = msg.getTree().tokens.at(msg.span.first);216 const span = msg.getSpan();
217 const last_token = msg.getTree().tokens.at(msg.span.first);217 const first_token = msg.getTree().tokens.at(span.first);
218 const last_token = msg.getTree().tokens.at(span.first);
218 const start_loc = msg.getTree().tokenLocationPtr(0, first_token);219 const start_loc = msg.getTree().tokenLocationPtr(0, first_token);
219 if (start_loc.line + 1 == line and start_loc.column + 1 == column) {220 if (start_loc.line + 1 == line and start_loc.column + 1 == column) {
220 return;221 return;
src-self-hosted/type.zig+2-2
...@@ -184,8 +184,8 @@ pub const Type = struct {...@@ -184,8 +184,8 @@ pub const Type = struct {
184 if (await (async base.abi_alignment.start() catch unreachable)) |ptr| return ptr.*;184 if (await (async base.abi_alignment.start() catch unreachable)) |ptr| return ptr.*;
185185
186 {186 {
187 const held = try comp.event_loop_local.getAnyLlvmContext();187 const held = try comp.zig_compiler.getAnyLlvmContext();
188 defer held.release(comp.event_loop_local);188 defer held.release(comp.zig_compiler);
189189
190 const llvm_context = held.node.data;190 const llvm_context = held.node.data;
191191
std/atomic/queue.zig+60-24
...@@ -1,40 +1,38 @@...@@ -1,40 +1,38 @@
1const std = @import("../index.zig");
1const builtin = @import("builtin");2const builtin = @import("builtin");
2const AtomicOrder = builtin.AtomicOrder;3const AtomicOrder = builtin.AtomicOrder;
3const AtomicRmwOp = builtin.AtomicRmwOp;4const AtomicRmwOp = builtin.AtomicRmwOp;
5const assert = std.debug.assert;
46
5/// Many producer, many consumer, non-allocating, thread-safe.7/// Many producer, many consumer, non-allocating, thread-safe.
6/// Uses a spinlock to protect get() and put().8/// Uses a mutex to protect access.
7pub fn Queue(comptime T: type) type {9pub fn Queue(comptime T: type) type {
8 return struct {10 return struct {
9 head: ?*Node,11 head: ?*Node,
10 tail: ?*Node,12 tail: ?*Node,
11 lock: u8,13 mutex: std.Mutex,
1214
13 pub const Self = this;15 pub const Self = this;
1416 pub const Node = std.LinkedList(T).Node;
15 pub const Node = struct {
16 next: ?*Node,
17 data: T,
18 };
1917
20 pub fn init() Self {18 pub fn init() Self {
21 return Self{19 return Self{
22 .head = null,20 .head = null,
23 .tail = null,21 .tail = null,
24 .lock = 0,22 .mutex = std.Mutex.init(),
25 };23 };
26 }24 }
2725
28 pub fn put(self: *Self, node: *Node) void {26 pub fn put(self: *Self, node: *Node) void {
29 node.next = null;27 node.next = null;
3028
31 while (@atomicRmw(u8, &self.lock, builtin.AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst) != 0) {}29 const held = self.mutex.acquire();
32 defer assert(@atomicRmw(u8, &self.lock, builtin.AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst) == 1);30 defer held.release();
3331
34 const opt_tail = self.tail;32 node.prev = self.tail;
35 self.tail = node;33 self.tail = node;
36 if (opt_tail) |tail| {34 if (node.prev) |prev_tail| {
37 tail.next = node;35 prev_tail.next = node;
38 } else {36 } else {
39 assert(self.head == null);37 assert(self.head == null);
40 self.head = node;38 self.head = node;
...@@ -42,18 +40,27 @@ pub fn Queue(comptime T: type) type {...@@ -42,18 +40,27 @@ pub fn Queue(comptime T: type) type {
42 }40 }
4341
44 pub fn get(self: *Self) ?*Node {42 pub fn get(self: *Self) ?*Node {
45 while (@atomicRmw(u8, &self.lock, builtin.AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst) != 0) {}43 const held = self.mutex.acquire();
46 defer assert(@atomicRmw(u8, &self.lock, builtin.AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst) == 1);44 defer held.release();
4745
48 const head = self.head orelse return null;46 const head = self.head orelse return null;
49 self.head = head.next;47 self.head = head.next;
50 if (head.next == null) self.tail = null;48 if (head.next) |new_head| {
49 new_head.prev = null;
50 } else {
51 self.tail = null;
52 }
53 // This way, a get() and a remove() are thread-safe with each other.
54 head.prev = null;
55 head.next = null;
51 return head;56 return head;
52 }57 }
5358
54 pub fn unget(self: *Self, node: *Node) void {59 pub fn unget(self: *Self, node: *Node) void {
55 while (@atomicRmw(u8, &self.lock, builtin.AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst) != 0) {}60 node.prev = null;
56 defer assert(@atomicRmw(u8, &self.lock, builtin.AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst) == 1);61
62 const held = self.mutex.acquire();
63 defer held.release();
5764
58 const opt_head = self.head;65 const opt_head = self.head;
59 self.head = node;66 self.head = node;
...@@ -65,13 +72,39 @@ pub fn Queue(comptime T: type) type {...@@ -65,13 +72,39 @@ pub fn Queue(comptime T: type) type {
65 }72 }
66 }73 }
6774
75 /// Thread-safe with get() and remove(). Returns whether node was actually removed.
76 pub fn remove(self: *Self, node: *Node) bool {
77 const held = self.mutex.acquire();
78 defer held.release();
79
80 if (node.prev == null and node.next == null and self.head != node) {
81 return false;
82 }
83
84 if (node.prev) |prev| {
85 prev.next = node.next;
86 } else {
87 self.head = node.next;
88 }
89 if (node.next) |next| {
90 next.prev = node.prev;
91 } else {
92 self.tail = node.prev;
93 }
94 node.prev = null;
95 node.next = null;
96 return true;
97 }
98
68 pub fn isEmpty(self: *Self) bool {99 pub fn isEmpty(self: *Self) bool {
69 return @atomicLoad(?*Node, &self.head, builtin.AtomicOrder.SeqCst) != null;100 const held = self.mutex.acquire();
101 defer held.release();
102 return self.head != null;
70 }103 }
71104
72 pub fn dump(self: *Self) void {105 pub fn dump(self: *Self) void {
73 while (@atomicRmw(u8, &self.lock, builtin.AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst) != 0) {}106 const held = self.mutex.acquire();
74 defer assert(@atomicRmw(u8, &self.lock, builtin.AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst) == 1);107 defer held.release();
75108
76 std.debug.warn("head: ");109 std.debug.warn("head: ");
77 dumpRecursive(self.head, 0);110 dumpRecursive(self.head, 0);
...@@ -93,9 +126,6 @@ pub fn Queue(comptime T: type) type {...@@ -93,9 +126,6 @@ pub fn Queue(comptime T: type) type {
93 };126 };
94}127}
95128
96const std = @import("../index.zig");
97const assert = std.debug.assert;
98
99const Context = struct {129const Context = struct {
100 allocator: *std.mem.Allocator,130 allocator: *std.mem.Allocator,
101 queue: *Queue(i32),131 queue: *Queue(i32),
...@@ -169,6 +199,7 @@ fn startPuts(ctx: *Context) u8 {...@@ -169,6 +199,7 @@ fn startPuts(ctx: *Context) u8 {
169 std.os.time.sleep(0, 1); // let the os scheduler be our fuzz199 std.os.time.sleep(0, 1); // let the os scheduler be our fuzz
170 const x = @bitCast(i32, r.random.scalar(u32));200 const x = @bitCast(i32, r.random.scalar(u32));
171 const node = ctx.allocator.create(Queue(i32).Node{201 const node = ctx.allocator.create(Queue(i32).Node{
202 .prev = undefined,
172 .next = undefined,203 .next = undefined,
173 .data = x,204 .data = x,
174 }) catch unreachable;205 }) catch unreachable;
...@@ -198,12 +229,14 @@ test "std.atomic.Queue single-threaded" {...@@ -198,12 +229,14 @@ test "std.atomic.Queue single-threaded" {
198 var node_0 = Queue(i32).Node{229 var node_0 = Queue(i32).Node{
199 .data = 0,230 .data = 0,
200 .next = undefined,231 .next = undefined,
232 .prev = undefined,
201 };233 };
202 queue.put(&node_0);234 queue.put(&node_0);
203235
204 var node_1 = Queue(i32).Node{236 var node_1 = Queue(i32).Node{
205 .data = 1,237 .data = 1,
206 .next = undefined,238 .next = undefined,
239 .prev = undefined,
207 };240 };
208 queue.put(&node_1);241 queue.put(&node_1);
209242
...@@ -212,12 +245,14 @@ test "std.atomic.Queue single-threaded" {...@@ -212,12 +245,14 @@ test "std.atomic.Queue single-threaded" {
212 var node_2 = Queue(i32).Node{245 var node_2 = Queue(i32).Node{
213 .data = 2,246 .data = 2,
214 .next = undefined,247 .next = undefined,
248 .prev = undefined,
215 };249 };
216 queue.put(&node_2);250 queue.put(&node_2);
217251
218 var node_3 = Queue(i32).Node{252 var node_3 = Queue(i32).Node{
219 .data = 3,253 .data = 3,
220 .next = undefined,254 .next = undefined,
255 .prev = undefined,
221 };256 };
222 queue.put(&node_3);257 queue.put(&node_3);
223258
...@@ -228,6 +263,7 @@ test "std.atomic.Queue single-threaded" {...@@ -228,6 +263,7 @@ test "std.atomic.Queue single-threaded" {
228 var node_4 = Queue(i32).Node{263 var node_4 = Queue(i32).Node{
229 .data = 4,264 .data = 4,
230 .next = undefined,265 .next = undefined,
266 .prev = undefined,
231 };267 };
232 queue.put(&node_4);268 queue.put(&node_4);
233269
std/build.zig+61-52
...@@ -424,60 +424,69 @@ pub const Builder = struct {...@@ -424,60 +424,69 @@ pub const Builder = struct {
424 return mode;424 return mode;
425 }425 }
426426
427 pub fn addUserInputOption(self: *Builder, name: []const u8, value: []const u8) bool {427 pub fn addUserInputOption(self: *Builder, name: []const u8, value: []const u8) !bool {
428 if (self.user_input_options.put(name, UserInputOption{428 const gop = try self.user_input_options.getOrPut(name);
429 .name = name,429 if (!gop.found_existing) {
430 .value = UserValue{ .Scalar = value },430 gop.kv.value = UserInputOption{
431 .used = false,431 .name = name,
432 }) catch unreachable) |*prev_value| {432 .value = UserValue{ .Scalar = value },
433 // option already exists433 .used = false,
434 switch (prev_value.value) {434 };
435 UserValue.Scalar => |s| {435 return false;
436 // turn it into a list436 }
437 var list = ArrayList([]const u8).init(self.allocator);437
438 list.append(s) catch unreachable;438 // option already exists
439 list.append(value) catch unreachable;439 switch (gop.kv.value.value) {
440 _ = self.user_input_options.put(name, UserInputOption{440 UserValue.Scalar => |s| {
441 .name = name,441 // turn it into a list
442 .value = UserValue{ .List = list },442 var list = ArrayList([]const u8).init(self.allocator);
443 .used = false,443 list.append(s) catch unreachable;
444 }) catch unreachable;444 list.append(value) catch unreachable;
445 },445 _ = self.user_input_options.put(name, UserInputOption{
446 UserValue.List => |*list| {446 .name = name,
447 // append to the list447 .value = UserValue{ .List = list },
448 list.append(value) catch unreachable;448 .used = false,
449 _ = self.user_input_options.put(name, UserInputOption{449 }) catch unreachable;
450 .name = name,450 },
451 .value = UserValue{ .List = list.* },451 UserValue.List => |*list| {
452 .used = false,452 // append to the list
453 }) catch unreachable;453 list.append(value) catch unreachable;
454 },454 _ = self.user_input_options.put(name, UserInputOption{
455 UserValue.Flag => {455 .name = name,
456 warn("Option '-D{}={}' conflicts with flag '-D{}'.\n", name, value, name);456 .value = UserValue{ .List = list.* },
457 return true;457 .used = false,
458 },458 }) catch unreachable;
459 }459 },
460 UserValue.Flag => {
461 warn("Option '-D{}={}' conflicts with flag '-D{}'.\n", name, value, name);
462 return true;
463 },
460 }464 }
461 return false;465 return false;
462 }466 }
463467
464 pub fn addUserInputFlag(self: *Builder, name: []const u8) bool {468 pub fn addUserInputFlag(self: *Builder, name: []const u8) !bool {
465 if (self.user_input_options.put(name, UserInputOption{469 const gop = try self.user_input_options.getOrPut(name);
466 .name = name,470 if (!gop.found_existing) {
467 .value = UserValue{ .Flag = {} },471 gop.kv.value = UserInputOption{
468 .used = false,472 .name = name,
469 }) catch unreachable) |*prev_value| {473 .value = UserValue{ .Flag = {} },
470 switch (prev_value.value) {474 .used = false,
471 UserValue.Scalar => |s| {475 };
472 warn("Flag '-D{}' conflicts with option '-D{}={}'.\n", name, name, s);476 return false;
473 return true;477 }
474 },478
475 UserValue.List => {479 // option already exists
476 warn("Flag '-D{}' conflicts with multiple options of the same name.\n", name);480 switch (gop.kv.value.value) {
477 return true;481 UserValue.Scalar => |s| {
478 },482 warn("Flag '-D{}' conflicts with option '-D{}={}'.\n", name, name, s);
479 UserValue.Flag => {},483 return true;
480 }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 => {},
481 }490 }
482 return false;491 return false;
483 }492 }
...@@ -603,10 +612,10 @@ pub const Builder = struct {...@@ -603,10 +612,10 @@ pub const Builder = struct {
603 }612 }
604613
605 fn copyFile(self: *Builder, source_path: []const u8, dest_path: []const u8) !void {614 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);
607 }616 }
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 {
610 if (self.verbose) {619 if (self.verbose) {
611 warn("cp {} {}\n", source_path, dest_path);620 warn("cp {} {}\n", source_path, dest_path);
612 }621 }
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...@@ -30,10 +30,36 @@ pub extern "c" fn sysctl(name: [*]c_int, namelen: c_uint, oldp: ?*c_void, oldlen
30pub extern "c" fn sysctlbyname(name: [*]const u8, oldp: ?*c_void, oldlenp: ?*usize, newp: ?*c_void, newlen: usize) c_int;30pub extern "c" fn sysctlbyname(name: [*]const u8, oldp: ?*c_void, oldlenp: ?*usize, newp: ?*c_void, newlen: usize) c_int;
31pub extern "c" fn sysctlnametomib(name: [*]const u8, mibp: ?*c_int, sizep: ?*usize) c_int;31pub 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
33pub use @import("../os/darwin/errno.zig");36pub use @import("../os/darwin/errno.zig");
3437
35pub const _errno = __error;38pub 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
37pub const timeval = extern struct {63pub const timeval = extern struct {
38 tv_sec: isize,64 tv_sec: isize,
39 tv_usec: isize,65 tv_usec: isize,
...@@ -98,14 +124,6 @@ pub const dirent = extern struct {...@@ -98,14 +124,6 @@ pub const dirent = extern struct {
98 d_name: u8, // field address is address of first byte of name124 d_name: u8, // field address is address of first byte of name
99};125};
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
109pub const pthread_attr_t = extern struct {127pub const pthread_attr_t = extern struct {
110 __sig: c_long,128 __sig: c_long,
111 __opaque: [56]u8,129 __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;...@@ -21,8 +21,10 @@ pub extern "c" fn lseek(fd: c_int, offset: isize, whence: c_int) isize;
21pub extern "c" fn open(path: [*]const u8, oflag: c_int, ...) c_int;21pub extern "c" fn open(path: [*]const u8, oflag: c_int, ...) c_int;
22pub extern "c" fn raise(sig: c_int) c_int;22pub extern "c" fn raise(sig: c_int) c_int;
23pub extern "c" fn read(fd: c_int, buf: *c_void, nbyte: usize) isize;23pub 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;
24pub extern "c" fn stat(noalias path: [*]const u8, noalias buf: *Stat) c_int;25pub extern "c" fn stat(noalias path: [*]const u8, noalias buf: *Stat) c_int;
25pub extern "c" fn write(fd: c_int, buf: *const c_void, nbyte: usize) isize;26pub 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;
26pub extern "c" fn mmap(addr: ?*c_void, len: usize, prot: c_int, flags: c_int, fd: c_int, offset: isize) ?*c_void;28pub extern "c" fn mmap(addr: ?*c_void, len: usize, prot: c_int, flags: c_int, fd: c_int, offset: isize) ?*c_void;
27pub extern "c" fn munmap(addr: *c_void, len: usize) c_int;29pub extern "c" fn munmap(addr: *c_void, len: usize) c_int;
28pub extern "c" fn unlink(path: [*]const u8) c_int;30pub 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) {...@@ -23,7 +23,10 @@ pub const runtime_safety = switch (builtin.mode) {
23var stderr_file: os.File = undefined;23var stderr_file: os.File = undefined;
24var stderr_file_out_stream: io.FileOutStream = undefined;24var stderr_file_out_stream: io.FileOutStream = undefined;
25var stderr_stream: ?*io.OutStream(io.FileOutStream.Error) = null;25var stderr_stream: ?*io.OutStream(io.FileOutStream.Error) = null;
26var stderr_mutex = std.Mutex.init();
26pub fn warn(comptime fmt: []const u8, args: ...) void {27pub fn warn(comptime fmt: []const u8, args: ...) void {
28 const held = stderr_mutex.acquire();
29 defer held.release();
27 const stderr = getStderrStream() catch return;30 const stderr = getStderrStream() catch return;
28 stderr.print(fmt, args) catch return;31 stderr.print(fmt, args) catch return;
29}32}
...@@ -672,14 +675,10 @@ fn parseFormValueRef(allocator: *mem.Allocator, in_stream: var, comptime T: type...@@ -672,14 +675,10 @@ fn parseFormValueRef(allocator: *mem.Allocator, in_stream: var, comptime T: type
672675
673const ParseFormValueError = error{676const ParseFormValueError = error{
674 EndOfStream,677 EndOfStream,
675 Io,
676 BadFd,
677 Unexpected,
678 InvalidDebugInfo,678 InvalidDebugInfo,
679 EndOfFile,679 EndOfFile,
680 IsDir,
681 OutOfMemory,680 OutOfMemory,
682};681} || std.os.File.ReadError;
683682
684fn parseFormValue(allocator: *mem.Allocator, in_stream: var, form_id: u64, is_64: bool) ParseFormValueError!FormValue {683fn parseFormValue(allocator: *mem.Allocator, in_stream: var, form_id: u64, is_64: bool) ParseFormValueError!FormValue {
685 return switch (form_id) {684 return switch (form_id) {
std/event.zig+14-8
...@@ -1,17 +1,23 @@...@@ -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;
1pub const Locked = @import("event/locked.zig").Locked;5pub const Locked = @import("event/locked.zig").Locked;
6pub const RwLock = @import("event/rwlock.zig").RwLock;
7pub const RwLocked = @import("event/rwlocked.zig").RwLocked;
2pub const Loop = @import("event/loop.zig").Loop;8pub const Loop = @import("event/loop.zig").Loop;
3pub const Lock = @import("event/lock.zig").Lock;9pub const fs = @import("event/fs.zig");
4pub const tcp = @import("event/tcp.zig");10pub 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
9test "import event tests" {12test "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");
10 _ = @import("event/locked.zig");18 _ = @import("event/locked.zig");
19 _ = @import("event/rwlock.zig");
20 _ = @import("event/rwlocked.zig");
11 _ = @import("event/loop.zig");21 _ = @import("event/loop.zig");
12 _ = @import("event/lock.zig");
13 _ = @import("event/tcp.zig");22 _ = @import("event/tcp.zig");
14 _ = @import("event/channel.zig");
15 _ = @import("event/group.zig");
16 _ = @import("event/future.zig");
17}23}
std/event/channel.zig+161-24
...@@ -5,7 +5,7 @@ const AtomicRmwOp = builtin.AtomicRmwOp;...@@ -5,7 +5,7 @@ const AtomicRmwOp = builtin.AtomicRmwOp;
5const AtomicOrder = builtin.AtomicOrder;5const AtomicOrder = builtin.AtomicOrder;
6const Loop = std.event.Loop;6const Loop = std.event.Loop;
77
8/// many producer, many consumer, thread-safe, lock-free, runtime configurable buffer size8/// many producer, many consumer, thread-safe, runtime configurable buffer size
9/// when buffer is empty, consumers suspend and are resumed by producers9/// when buffer is empty, consumers suspend and are resumed by producers
10/// when buffer is full, producers suspend and are resumed by consumers10/// when buffer is full, producers suspend and are resumed by consumers
11pub fn Channel(comptime T: type) type {11pub fn Channel(comptime T: type) type {
...@@ -13,6 +13,7 @@ pub fn Channel(comptime T: type) type {...@@ -13,6 +13,7 @@ pub fn Channel(comptime T: type) type {
13 loop: *Loop,13 loop: *Loop,
1414
15 getters: std.atomic.Queue(GetNode),15 getters: std.atomic.Queue(GetNode),
16 or_null_queue: std.atomic.Queue(*std.atomic.Queue(GetNode).Node),
16 putters: std.atomic.Queue(PutNode),17 putters: std.atomic.Queue(PutNode),
17 get_count: usize,18 get_count: usize,
18 put_count: usize,19 put_count: usize,
...@@ -26,8 +27,22 @@ pub fn Channel(comptime T: type) type {...@@ -26,8 +27,22 @@ pub fn Channel(comptime T: type) type {
2627
27 const SelfChannel = this;28 const SelfChannel = this;
28 const GetNode = struct {29 const GetNode = struct {
29 ptr: *T,
30 tick_node: *Loop.NextTickNode,30 tick_node: *Loop.NextTickNode,
31 data: Data,
32
33 const Data = union(enum) {
34 Normal: Normal,
35 OrNull: OrNull,
36 };
37
38 const Normal = struct {
39 ptr: *T,
40 };
41
42 const OrNull = struct {
43 ptr: *?T,
44 or_null: *std.atomic.Queue(*std.atomic.Queue(GetNode).Node).Node,
45 };
31 };46 };
32 const PutNode = struct {47 const PutNode = struct {
33 data: T,48 data: T,
...@@ -48,6 +63,7 @@ pub fn Channel(comptime T: type) type {...@@ -48,6 +63,7 @@ pub fn Channel(comptime T: type) type {
48 .need_dispatch = 0,63 .need_dispatch = 0,
49 .getters = std.atomic.Queue(GetNode).init(),64 .getters = std.atomic.Queue(GetNode).init(),
50 .putters = std.atomic.Queue(PutNode).init(),65 .putters = std.atomic.Queue(PutNode).init(),
66 .or_null_queue = std.atomic.Queue(*std.atomic.Queue(GetNode).Node).init(),
51 .get_count = 0,67 .get_count = 0,
52 .put_count = 0,68 .put_count = 0,
53 });69 });
...@@ -71,18 +87,29 @@ pub fn Channel(comptime T: type) type {...@@ -71,18 +87,29 @@ pub fn Channel(comptime T: type) type {
71 /// puts a data item in the channel. The promise completes when the value has been added to the87 /// puts a data item in the channel. The promise completes when the value has been added to the
72 /// buffer, or in the case of a zero size buffer, when the item has been retrieved by a getter.88 /// buffer, or in the case of a zero size buffer, when the item has been retrieved by a getter.
73 pub async fn put(self: *SelfChannel, data: T) void {89 pub async fn put(self: *SelfChannel, data: T) void {
90 // TODO fix this workaround
91 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 }
74 suspend {112 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 };
86 self.putters.put(&queue_node);113 self.putters.put(&queue_node);
87 _ = @atomicRmw(usize, &self.put_count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst);114 _ = @atomicRmw(usize, &self.put_count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst);
88115
...@@ -93,23 +120,95 @@ pub fn Channel(comptime T: type) type {...@@ -93,23 +120,95 @@ pub fn Channel(comptime T: type) type {
93 /// await this function to get an item from the channel. If the buffer is empty, the promise will120 /// await this function to get an item from the channel. If the buffer is empty, the promise will
94 /// complete when the next item is put in the channel.121 /// complete when the next item is put in the channel.
95 pub async fn get(self: *SelfChannel) T {122 pub async fn get(self: *SelfChannel) T {
123 // TODO fix this workaround
124 suspend {
125 resume @handle();
126 }
127
96 // TODO integrate this function with named return values128 // TODO integrate this function with named return values
97 // so we can get rid of this extra result copy129 // so we can get rid of this extra result copy
98 var result: T = undefined;130 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
99 suspend {175 suspend {
100 var my_tick_node = Loop.NextTickNode{176 resume @handle();
101 .next = undefined,177 }
102 .data = @handle(),178
103 };179 // TODO integrate this function with named return values
104 var queue_node = std.atomic.Queue(GetNode).Node{180 // so we can get rid of this extra result copy
105 .data = GetNode{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{
106 .ptr = &result,188 .ptr = &result,
107 .tick_node = &my_tick_node,189 .or_null = &or_null_node,
108 },190 },
109 .next = undefined,191 },
110 };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 {
111 self.getters.put(&queue_node);209 self.getters.put(&queue_node);
112 _ = @atomicRmw(usize, &self.get_count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst);210 _ = @atomicRmw(usize, &self.get_count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst);
211 self.or_null_queue.put(&or_null_node);
113212
114 self.dispatch();213 self.dispatch();
115 }214 }
...@@ -139,7 +238,15 @@ pub fn Channel(comptime T: type) type {...@@ -139,7 +238,15 @@ pub fn Channel(comptime T: type) type {
139 if (get_count == 0) break :one_dispatch;238 if (get_count == 0) break :one_dispatch;
140239
141 const get_node = &self.getters.get().?.data;240 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 }
143 self.loop.onNextTick(get_node.tick_node);250 self.loop.onNextTick(get_node.tick_node);
144 self.buffer_len -= 1;251 self.buffer_len -= 1;
145252
...@@ -151,7 +258,15 @@ pub fn Channel(comptime T: type) type {...@@ -151,7 +258,15 @@ pub fn Channel(comptime T: type) type {
151 const get_node = &self.getters.get().?.data;258 const get_node = &self.getters.get().?.data;
152 const put_node = &self.putters.get().?.data;259 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 }
155 self.loop.onNextTick(get_node.tick_node);270 self.loop.onNextTick(get_node.tick_node);
156 self.loop.onNextTick(put_node.tick_node);271 self.loop.onNextTick(put_node.tick_node);
157272
...@@ -176,6 +291,16 @@ pub fn Channel(comptime T: type) type {...@@ -176,6 +291,16 @@ pub fn Channel(comptime T: type) type {
176 _ = @atomicRmw(usize, &self.get_count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst);291 _ = @atomicRmw(usize, &self.get_count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst);
177 _ = @atomicRmw(usize, &self.put_count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst);292 _ = @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
179 // clear need-dispatch flag304 // clear need-dispatch flag
180 const need_dispatch = @atomicRmw(u8, &self.need_dispatch, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst);305 const need_dispatch = @atomicRmw(u8, &self.need_dispatch, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst);
181 if (need_dispatch != 0) continue;306 if (need_dispatch != 0) continue;
...@@ -226,6 +351,15 @@ async fn testChannelGetter(loop: *Loop, channel: *Channel(i32)) void {...@@ -226,6 +351,15 @@ async fn testChannelGetter(loop: *Loop, channel: *Channel(i32)) void {
226 const value2_promise = try async channel.get();351 const value2_promise = try async channel.get();
227 const value2 = await value2_promise;352 const value2 = await value2_promise;
228 assert(value2 == 4567);353 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;
229}363}
230364
231async fn testChannelPutter(channel: *Channel(i32)) void {365async fn testChannelPutter(channel: *Channel(i32)) void {
...@@ -233,3 +367,6 @@ async fn testChannelPutter(channel: *Channel(i32)) void {...@@ -233,3 +367,6 @@ async fn testChannelPutter(channel: *Channel(i32)) void {
233 await (async channel.put(4567) catch @panic("out of memory"));367 await (async channel.put(4567) catch @panic("out of memory"));
234}368}
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 {...@@ -29,6 +29,17 @@ pub fn Group(comptime ReturnType: type) type {
29 };29 };
30 }30 }
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
32 /// Add a promise to the group. Thread-safe.43 /// Add a promise to the group. Thread-safe.
33 pub fn add(self: *Self, handle: promise->ReturnType) (error{OutOfMemory}!void) {44 pub fn add(self: *Self, handle: promise->ReturnType) (error{OutOfMemory}!void) {
34 const node = try self.lock.loop.allocator.create(Stack.Node{45 const node = try self.lock.loop.allocator.create(Stack.Node{
...@@ -88,7 +99,7 @@ pub fn Group(comptime ReturnType: type) type {...@@ -88,7 +99,7 @@ pub fn Group(comptime ReturnType: type) type {
88 await node.data;99 await node.data;
89 } else {100 } else {
90 (await node.data) catch |err| {101 (await node.data) catch |err| {
91 self.cancelAll();102 self.deinit();
92 return err;103 return err;
93 };104 };
94 }105 }
...@@ -100,25 +111,12 @@ pub fn Group(comptime ReturnType: type) type {...@@ -100,25 +111,12 @@ pub fn Group(comptime ReturnType: type) type {
100 await handle;111 await handle;
101 } else {112 } else {
102 (await handle) catch |err| {113 (await handle) catch |err| {
103 self.cancelAll();114 self.deinit();
104 return err;115 return err;
105 };116 };
106 }117 }
107 }118 }
108 }119 }
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 }
122 };120 };
123}121}
124122
std/event/lock.zig+10-5
...@@ -9,6 +9,7 @@ const Loop = std.event.Loop;...@@ -9,6 +9,7 @@ const Loop = std.event.Loop;
9/// Thread-safe async/await lock.9/// Thread-safe async/await lock.
10/// Does not make any syscalls - coroutines which are waiting for the lock are suspended, and10/// 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.11/// are resumed when the lock is released, in order.
12/// Allows only one actor to hold the lock.
12pub const Lock = struct {13pub const Lock = struct {
13 loop: *Loop,14 loop: *Loop,
14 shared_bit: u8, // TODO make this a bool15 shared_bit: u8, // TODO make this a bool
...@@ -90,13 +91,14 @@ pub const Lock = struct {...@@ -90,13 +91,14 @@ pub const Lock = struct {
90 }91 }
9192
92 pub async fn acquire(self: *Lock) Held {93 pub async fn acquire(self: *Lock) Held {
94 // TODO explicitly put this memory in the coroutine frame #1194
93 suspend {95 suspend {
94 // TODO explicitly put this memory in the coroutine frame #119496 resume @handle();
95 var my_tick_node = Loop.NextTickNode{97 }
96 .data = @handle(),98 var my_tick_node = Loop.NextTickNode.init(@handle());
97 .next = undefined,
98 };
9999
100 errdefer _ = self.queue.remove(&my_tick_node); // TODO test canceling an acquire
101 suspend {
100 self.queue.put(&my_tick_node);102 self.queue.put(&my_tick_node);
101103
102 // At this point, we are in the queue, so we might have already been resumed and this coroutine104 // 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 {...@@ -146,6 +148,7 @@ async fn testLock(loop: *Loop, lock: *Lock) void {
146 }148 }
147 const handle1 = async lockRunner(lock) catch @panic("out of memory");149 const handle1 = async lockRunner(lock) catch @panic("out of memory");
148 var tick_node1 = Loop.NextTickNode{150 var tick_node1 = Loop.NextTickNode{
151 .prev = undefined,
149 .next = undefined,152 .next = undefined,
150 .data = handle1,153 .data = handle1,
151 };154 };
...@@ -153,6 +156,7 @@ async fn testLock(loop: *Loop, lock: *Lock) void {...@@ -153,6 +156,7 @@ async fn testLock(loop: *Loop, lock: *Lock) void {
153156
154 const handle2 = async lockRunner(lock) catch @panic("out of memory");157 const handle2 = async lockRunner(lock) catch @panic("out of memory");
155 var tick_node2 = Loop.NextTickNode{158 var tick_node2 = Loop.NextTickNode{
159 .prev = undefined,
156 .next = undefined,160 .next = undefined,
157 .data = handle2,161 .data = handle2,
158 };162 };
...@@ -160,6 +164,7 @@ async fn testLock(loop: *Loop, lock: *Lock) void {...@@ -160,6 +164,7 @@ async fn testLock(loop: *Loop, lock: *Lock) void {
160164
161 const handle3 = async lockRunner(lock) catch @panic("out of memory");165 const handle3 = async lockRunner(lock) catch @panic("out of memory");
162 var tick_node3 = Loop.NextTickNode{166 var tick_node3 = Loop.NextTickNode{
167 .prev = undefined,
163 .next = undefined,168 .next = undefined,
164 .data = handle3,169 .data = handle3,
165 };170 };
std/event/loop.zig+337-99
...@@ -2,10 +2,12 @@ const std = @import("../index.zig");...@@ -2,10 +2,12 @@ const std = @import("../index.zig");
2const builtin = @import("builtin");2const builtin = @import("builtin");
3const assert = std.debug.assert;3const assert = std.debug.assert;
4const mem = std.mem;4const mem = std.mem;
5const posix = std.os.posix;
6const windows = std.os.windows;
7const AtomicRmwOp = builtin.AtomicRmwOp;5const AtomicRmwOp = builtin.AtomicRmwOp;
8const AtomicOrder = builtin.AtomicOrder;6const AtomicOrder = builtin.AtomicOrder;
7const fs = std.event.fs;
8const os = std.os;
9const posix = os.posix;
10const windows = os.windows;
911
10pub const Loop = struct {12pub const Loop = struct {
11 allocator: *mem.Allocator,13 allocator: *mem.Allocator,
...@@ -13,7 +15,7 @@ pub const Loop = struct {...@@ -13,7 +15,7 @@ pub const Loop = struct {
13 os_data: OsData,15 os_data: OsData,
14 final_resume_node: ResumeNode,16 final_resume_node: ResumeNode,
15 pending_event_count: usize,17 pending_event_count: usize,
16 extra_threads: []*std.os.Thread,18 extra_threads: []*os.Thread,
1719
18 // pre-allocated eventfds. all permanently active.20 // pre-allocated eventfds. all permanently active.
19 // this is how we send promises to be resumed on other threads.21 // this is how we send promises to be resumed on other threads.
...@@ -50,6 +52,22 @@ pub const Loop = struct {...@@ -50,6 +52,22 @@ pub const Loop = struct {
50 base: ResumeNode,52 base: ResumeNode,
51 kevent: posix.Kevent,53 kevent: posix.Kevent,
52 };54 };
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 };
53 };71 };
5472
55 /// After initialization, call run().73 /// After initialization, call run().
...@@ -65,7 +83,7 @@ pub const Loop = struct {...@@ -65,7 +83,7 @@ pub const Loop = struct {
65 /// TODO copy elision / named return values so that the threads referencing *Loop83 /// TODO copy elision / named return values so that the threads referencing *Loop
66 /// have the correct pointer value.84 /// have the correct pointer value.
67 pub fn initMultiThreaded(self: *Loop, allocator: *mem.Allocator) !void {85 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);
69 return self.initInternal(allocator, core_count);87 return self.initInternal(allocator, core_count);
70 }88 }
7189
...@@ -92,7 +110,7 @@ pub const Loop = struct {...@@ -92,7 +110,7 @@ pub const Loop = struct {
92 );110 );
93 errdefer self.allocator.free(self.eventfd_resume_nodes);111 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);
96 errdefer self.allocator.free(self.extra_threads);114 errdefer self.allocator.free(self.extra_threads);
97115
98 try self.initOsData(extra_thread_count);116 try self.initOsData(extra_thread_count);
...@@ -104,17 +122,30 @@ pub const Loop = struct {...@@ -104,17 +122,30 @@ pub const Loop = struct {
104 self.allocator.free(self.extra_threads);122 self.allocator.free(self.extra_threads);
105 }123 }
106124
107 const InitOsDataError = std.os.LinuxEpollCreateError || mem.Allocator.Error || std.os.LinuxEventFdError ||125 const InitOsDataError = os.LinuxEpollCreateError || mem.Allocator.Error || os.LinuxEventFdError ||
108 std.os.SpawnThreadError || std.os.LinuxEpollCtlError || std.os.BsdKEventError ||126 os.SpawnThreadError || os.LinuxEpollCtlError || os.BsdKEventError ||
109 std.os.WindowsCreateIoCompletionPortError;127 os.WindowsCreateIoCompletionPortError;
110128
111 const wakeup_bytes = []u8{0x1} ** 8;129 const wakeup_bytes = []u8{0x1} ** 8;
112130
113 fn initOsData(self: *Loop, extra_thread_count: usize) InitOsDataError!void {131 fn initOsData(self: *Loop, extra_thread_count: usize) InitOsDataError!void {
114 switch (builtin.os) {132 switch (builtin.os) {
115 builtin.Os.linux => {133 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
116 errdefer {147 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);
118 }149 }
119 for (self.eventfd_resume_nodes) |*eventfd_node| {150 for (self.eventfd_resume_nodes) |*eventfd_node| {
120 eventfd_node.* = std.atomic.Stack(ResumeNode.EventFd).Node{151 eventfd_node.* = std.atomic.Stack(ResumeNode.EventFd).Node{
...@@ -123,7 +154,7 @@ pub const Loop = struct {...@@ -123,7 +154,7 @@ pub const Loop = struct {
123 .id = ResumeNode.Id.EventFd,154 .id = ResumeNode.Id.EventFd,
124 .handle = undefined,155 .handle = undefined,
125 },156 },
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),
127 .epoll_op = posix.EPOLL_CTL_ADD,158 .epoll_op = posix.EPOLL_CTL_ADD,
128 },159 },
129 .next = undefined,160 .next = undefined,
...@@ -131,44 +162,62 @@ pub const Loop = struct {...@@ -131,44 +162,62 @@ pub const Loop = struct {
131 self.available_eventfd_resume_nodes.push(eventfd_node);162 self.available_eventfd_resume_nodes.push(eventfd_node);
132 }163 }
133164
134 self.os_data.epollfd = try std.os.linuxEpollCreate(posix.EPOLL_CLOEXEC);165 self.os_data.epollfd = try os.linuxEpollCreate(posix.EPOLL_CLOEXEC);
135 errdefer std.os.close(self.os_data.epollfd);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);168 self.os_data.final_eventfd = try os.linuxEventFd(0, posix.EFD_CLOEXEC | posix.EFD_NONBLOCK);
138 errdefer std.os.close(self.os_data.final_eventfd);169 errdefer os.close(self.os_data.final_eventfd);
139170
140 self.os_data.final_eventfd_event = posix.epoll_event{171 self.os_data.final_eventfd_event = posix.epoll_event{
141 .events = posix.EPOLLIN,172 .events = posix.EPOLLIN,
142 .data = posix.epoll_data{ .ptr = @ptrToInt(&self.final_resume_node) },173 .data = posix.epoll_data{ .ptr = @ptrToInt(&self.final_resume_node) },
143 };174 };
144 try std.os.linuxEpollCtl(175 try os.linuxEpollCtl(
145 self.os_data.epollfd,176 self.os_data.epollfd,
146 posix.EPOLL_CTL_ADD,177 posix.EPOLL_CTL_ADD,
147 self.os_data.final_eventfd,178 self.os_data.final_eventfd,
148 &self.os_data.final_eventfd_event,179 &self.os_data.final_eventfd_event,
149 );180 );
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
151 var extra_thread_index: usize = 0;188 var extra_thread_index: usize = 0;
152 errdefer {189 errdefer {
153 // writing 8 bytes to an eventfd cannot fail190 // 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;
155 while (extra_thread_index != 0) {192 while (extra_thread_index != 0) {
156 extra_thread_index -= 1;193 extra_thread_index -= 1;
157 self.extra_threads[extra_thread_index].wait();194 self.extra_threads[extra_thread_index].wait();
158 }195 }
159 }196 }
160 while (extra_thread_index < extra_thread_count) : (extra_thread_index += 1) {197 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);
162 }199 }
163 },200 },
164 builtin.Os.macosx => {201 builtin.Os.macosx => {
165 self.os_data.kqfd = try std.os.bsdKQueue();202 self.os_data.kqfd = try os.bsdKQueue();
166 errdefer std.os.close(self.os_data.kqfd);203 errdefer os.close(self.os_data.kqfd);
167204
168 self.os_data.kevents = try self.allocator.alloc(posix.Kevent, extra_thread_count);205 self.os_data.fs_kqfd = try os.bsdKQueue();
169 errdefer self.allocator.free(self.os_data.kevents);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
173 for (self.eventfd_resume_nodes) |*eventfd_node, i| {222 for (self.eventfd_resume_nodes) |*eventfd_node, i| {
174 eventfd_node.* = std.atomic.Stack(ResumeNode.EventFd).Node{223 eventfd_node.* = std.atomic.Stack(ResumeNode.EventFd).Node{
...@@ -191,18 +240,9 @@ pub const Loop = struct {...@@ -191,18 +240,9 @@ pub const Loop = struct {
191 };240 };
192 self.available_eventfd_resume_nodes.push(eventfd_node);241 self.available_eventfd_resume_nodes.push(eventfd_node);
193 const kevent_array = (*[1]posix.Kevent)(&eventfd_node.data.kevent);242 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);
195 eventfd_node.data.kevent.flags = posix.EV_CLEAR | posix.EV_ENABLE;244 eventfd_node.data.kevent.flags = posix.EV_CLEAR | posix.EV_ENABLE;
196 eventfd_node.data.kevent.fflags = posix.NOTE_TRIGGER;245 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 };
206 }246 }
207247
208 // Pre-add so that we cannot get error.SystemResources248 // Pre-add so that we cannot get error.SystemResources
...@@ -215,31 +255,55 @@ pub const Loop = struct {...@@ -215,31 +255,55 @@ pub const Loop = struct {
215 .data = 0,255 .data = 0,
216 .udata = @ptrToInt(&self.final_resume_node),256 .udata = @ptrToInt(&self.final_resume_node),
217 };257 };
218 const kevent_array = (*[1]posix.Kevent)(&self.os_data.final_kevent);258 const final_kev_arr = (*[1]posix.Kevent)(&self.os_data.final_kevent);
219 _ = try std.os.bsdKEvent(self.os_data.kqfd, kevent_array, eventlist, null);259 _ = try os.bsdKEvent(self.os_data.kqfd, final_kev_arr, empty_kevs, null);
220 self.os_data.final_kevent.flags = posix.EV_ENABLE;260 self.os_data.final_kevent.flags = posix.EV_ENABLE;
221 self.os_data.final_kevent.fflags = posix.NOTE_TRIGGER;261 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
223 var extra_thread_index: usize = 0;287 var extra_thread_index: usize = 0;
224 errdefer {288 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;
226 while (extra_thread_index != 0) {290 while (extra_thread_index != 0) {
227 extra_thread_index -= 1;291 extra_thread_index -= 1;
228 self.extra_threads[extra_thread_index].wait();292 self.extra_threads[extra_thread_index].wait();
229 }293 }
230 }294 }
231 while (extra_thread_index < extra_thread_count) : (extra_thread_index += 1) {295 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);
233 }297 }
234 },298 },
235 builtin.Os.windows => {299 builtin.Os.windows => {
236 self.os_data.io_port = try std.os.windowsCreateIoCompletionPort(300 self.os_data.io_port = try os.windowsCreateIoCompletionPort(
237 windows.INVALID_HANDLE_VALUE,301 windows.INVALID_HANDLE_VALUE,
238 null,302 null,
239 undefined,303 undefined,
240 undefined,304 @maxValue(windows.DWORD),
241 );305 );
242 errdefer std.os.close(self.os_data.io_port);306 errdefer os.close(self.os_data.io_port);
243307
244 for (self.eventfd_resume_nodes) |*eventfd_node, i| {308 for (self.eventfd_resume_nodes) |*eventfd_node, i| {
245 eventfd_node.* = std.atomic.Stack(ResumeNode.EventFd).Node{309 eventfd_node.* = std.atomic.Stack(ResumeNode.EventFd).Node{
...@@ -262,7 +326,7 @@ pub const Loop = struct {...@@ -262,7 +326,7 @@ pub const Loop = struct {
262 while (i < extra_thread_index) : (i += 1) {326 while (i < extra_thread_index) : (i += 1) {
263 while (true) {327 while (true) {
264 const overlapped = @intToPtr(?*windows.OVERLAPPED, 0x1);328 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;
266 break;330 break;
267 }331 }
268 }332 }
...@@ -272,7 +336,7 @@ pub const Loop = struct {...@@ -272,7 +336,7 @@ pub const Loop = struct {
272 }336 }
273 }337 }
274 while (extra_thread_index < extra_thread_count) : (extra_thread_index += 1) {338 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);
276 }340 }
277 },341 },
278 else => {},342 else => {},
...@@ -282,63 +346,113 @@ pub const Loop = struct {...@@ -282,63 +346,113 @@ pub const Loop = struct {
282 fn deinitOsData(self: *Loop) void {346 fn deinitOsData(self: *Loop) void {
283 switch (builtin.os) {347 switch (builtin.os) {
284 builtin.Os.linux => {348 builtin.Os.linux => {
285 std.os.close(self.os_data.final_eventfd);349 os.close(self.os_data.final_eventfd);
286 while (self.available_eventfd_resume_nodes.pop()) |node| std.os.close(node.data.eventfd);350 while (self.available_eventfd_resume_nodes.pop()) |node| os.close(node.data.eventfd);
287 std.os.close(self.os_data.epollfd);351 os.close(self.os_data.epollfd);
288 self.allocator.free(self.eventfd_resume_nodes);352 self.allocator.free(self.eventfd_resume_nodes);
289 },353 },
290 builtin.Os.macosx => {354 builtin.Os.macosx => {
291 self.allocator.free(self.os_data.kevents);355 os.close(self.os_data.kqfd);
292 std.os.close(self.os_data.kqfd);356 os.close(self.os_data.fs_kqfd);
293 },357 },
294 builtin.Os.windows => {358 builtin.Os.windows => {
295 std.os.close(self.os_data.io_port);359 os.close(self.os_data.io_port);
296 },360 },
297 else => {},361 else => {},
298 }362 }
299 }363 }
300364
301 /// resume_node must live longer than the promise that it holds a reference to.365 /// 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 {366 /// flags must contain EPOLLET
303 _ = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst);367 pub fn linuxAddFd(self: *Loop, fd: i32, resume_node: *ResumeNode, flags: u32) !void {
304 errdefer {368 assert(flags & posix.EPOLLET == posix.EPOLLET);
305 self.finishOneEvent();369 self.beginOneEvent();
306 }370 errdefer self.finishOneEvent();
307 try self.modFd(371 try self.linuxModFd(
308 fd,372 fd,
309 posix.EPOLL_CTL_ADD,373 posix.EPOLL_CTL_ADD,
310 std.os.linux.EPOLLIN | std.os.linux.EPOLLOUT | std.os.linux.EPOLLET,374 flags,
311 resume_node,375 resume_node,
312 );376 );
313 }377 }
314378
315 pub fn modFd(self: *Loop, fd: i32, op: u32, events: u32, resume_node: *ResumeNode) !void {379 pub fn linuxModFd(self: *Loop, fd: i32, op: u32, flags: u32, resume_node: *ResumeNode) !void {
316 var ev = std.os.linux.epoll_event{380 assert(flags & posix.EPOLLET == posix.EPOLLET);
317 .events = events,381 var ev = os.linux.epoll_event{
318 .data = std.os.linux.epoll_data{ .ptr = @ptrToInt(resume_node) },382 .events = flags,
383 .data = os.linux.epoll_data{ .ptr = @ptrToInt(resume_node) },
319 };384 };
320 try std.os.linuxEpollCtl(self.os_data.epollfd, op, fd, &ev);385 try os.linuxEpollCtl(self.os_data.epollfd, op, fd, &ev);
321 }386 }
322387
323 pub fn removeFd(self: *Loop, fd: i32) void {388 pub fn linuxRemoveFd(self: *Loop, fd: i32) void {
324 self.removeFdNoCounter(fd);389 os.linuxEpollCtl(self.os_data.epollfd, os.linux.EPOLL_CTL_DEL, fd, undefined) catch {};
325 self.finishOneEvent();390 self.finishOneEvent();
326 }391 }
327392
328 fn removeFdNoCounter(self: *Loop, fd: i32) void {393 pub async fn linuxWaitFd(self: *Loop, fd: i32, flags: u32) !void {
329 std.os.linuxEpollCtl(self.os_data.epollfd, std.os.linux.EPOLL_CTL_DEL, fd, undefined) catch {};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 }
330 }405 }
331406
332 pub async fn waitFd(self: *Loop, fd: i32) !void {407 pub async fn bsdWaitKev(self: *Loop, ident: usize, filter: i16, fflags: u32) !posix.Kevent {
333 defer self.removeFd(fd);408 // TODO #1194
334 suspend {409 suspend {
335 // TODO explicitly put this memory in the coroutine frame #1194410 resume @handle();
336 var resume_node = ResumeNode{411 }
412 var resume_node = ResumeNode.Basic{
413 .base = ResumeNode{
337 .id = ResumeNode.Id.Basic,414 .id = ResumeNode.Id.Basic,
338 .handle = @handle(),415 .handle = @handle(),
339 };416 },
340 try self.addFd(fd, &resume_node);417 .kev = undefined,
418 };
419 defer self.bsdRemoveKev(ident, filter);
420 suspend {
421 try self.bsdAddKev(&resume_node, ident, filter, fflags);
341 }422 }
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();
342 }456 }
343457
344 fn dispatch(self: *Loop) void {458 fn dispatch(self: *Loop) void {
...@@ -352,8 +466,8 @@ pub const Loop = struct {...@@ -352,8 +466,8 @@ pub const Loop = struct {
352 switch (builtin.os) {466 switch (builtin.os) {
353 builtin.Os.macosx => {467 builtin.Os.macosx => {
354 const kevent_array = (*[1]posix.Kevent)(&eventfd_node.kevent);468 const kevent_array = (*[1]posix.Kevent)(&eventfd_node.kevent);
355 const eventlist = ([*]posix.Kevent)(undefined)[0..0];469 const empty_kevs = ([*]posix.Kevent)(undefined)[0..0];
356 _ = std.os.bsdKEvent(self.os_data.kqfd, kevent_array, eventlist, null) catch {470 _ = os.bsdKEvent(self.os_data.kqfd, kevent_array, empty_kevs, null) catch {
357 self.next_tick_queue.unget(next_tick_node);471 self.next_tick_queue.unget(next_tick_node);
358 self.available_eventfd_resume_nodes.push(resume_stack_node);472 self.available_eventfd_resume_nodes.push(resume_stack_node);
359 return;473 return;
...@@ -361,9 +475,9 @@ pub const Loop = struct {...@@ -361,9 +475,9 @@ pub const Loop = struct {
361 },475 },
362 builtin.Os.linux => {476 builtin.Os.linux => {
363 // the pending count is already accounted for477 // the pending count is already accounted for
364 const epoll_events = posix.EPOLLONESHOT | std.os.linux.EPOLLIN | std.os.linux.EPOLLOUT |478 const epoll_events = posix.EPOLLONESHOT | os.linux.EPOLLIN | os.linux.EPOLLOUT |
365 std.os.linux.EPOLLET;479 os.linux.EPOLLET;
366 self.modFd(480 self.linuxModFd(
367 eventfd_node.eventfd,481 eventfd_node.eventfd,
368 eventfd_node.epoll_op,482 eventfd_node.epoll_op,
369 epoll_events,483 epoll_events,
...@@ -379,7 +493,7 @@ pub const Loop = struct {...@@ -379,7 +493,7 @@ pub const Loop = struct {
379 // the consumer code can decide whether to read the completion key.493 // the consumer code can decide whether to read the completion key.
380 // it has to do this for normal I/O, so we match that behavior here.494 // it has to do this for normal I/O, so we match that behavior here.
381 const overlapped = @intToPtr(?*windows.OVERLAPPED, 0x1);495 const overlapped = @intToPtr(?*windows.OVERLAPPED, 0x1);
382 std.os.windowsPostQueuedCompletionStatus(496 os.windowsPostQueuedCompletionStatus(
383 self.os_data.io_port,497 self.os_data.io_port,
384 undefined,498 undefined,
385 eventfd_node.completion_key,499 eventfd_node.completion_key,
...@@ -397,15 +511,29 @@ pub const Loop = struct {...@@ -397,15 +511,29 @@ pub const Loop = struct {
397511
398 /// Bring your own linked list node. This means it can't fail.512 /// Bring your own linked list node. This means it can't fail.
399 pub fn onNextTick(self: *Loop, node: *NextTickNode) void {513 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()
401 self.next_tick_queue.put(node);515 self.next_tick_queue.put(node);
402 self.dispatch();516 self.dispatch();
403 }517 }
404518
519 pub fn cancelOnNextTick(self: *Loop, node: *NextTickNode) void {
520 if (self.next_tick_queue.remove(node)) {
521 self.finishOneEvent();
522 }
523 }
524
405 pub fn run(self: *Loop) void {525 pub fn run(self: *Loop) void {
406 self.finishOneEvent(); // the reference we start with526 self.finishOneEvent(); // the reference we start with
407527
408 self.workerRun();528 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
409 for (self.extra_threads) |extra_thread| {537 for (self.extra_threads) |extra_thread| {
410 extra_thread.wait();538 extra_thread.wait();
411 }539 }
...@@ -420,6 +548,7 @@ pub const Loop = struct {...@@ -420,6 +548,7 @@ pub const Loop = struct {
420 suspend {548 suspend {
421 handle.* = @handle();549 handle.* = @handle();
422 var my_tick_node = Loop.NextTickNode{550 var my_tick_node = Loop.NextTickNode{
551 .prev = undefined,
423 .next = undefined,552 .next = undefined,
424 .data = @handle(),553 .data = @handle(),
425 };554 };
...@@ -441,6 +570,7 @@ pub const Loop = struct {...@@ -441,6 +570,7 @@ pub const Loop = struct {
441 pub async fn yield(self: *Loop) void {570 pub async fn yield(self: *Loop) void {
442 suspend {571 suspend {
443 var my_tick_node = Loop.NextTickNode{572 var my_tick_node = Loop.NextTickNode{
573 .prev = undefined,
444 .next = undefined,574 .next = undefined,
445 .data = @handle(),575 .data = @handle(),
446 };576 };
...@@ -448,20 +578,28 @@ pub const Loop = struct {...@@ -448,20 +578,28 @@ pub const Loop = struct {
448 }578 }
449 }579 }
450580
451 fn finishOneEvent(self: *Loop) void {581 /// call finishOneEvent when done
452 if (@atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst) == 1) {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) {
453 // cause all the threads to stop589 // cause all the threads to stop
454 switch (builtin.os) {590 switch (builtin.os) {
455 builtin.Os.linux => {591 builtin.Os.linux => {
592 self.posixFsRequest(&self.os_data.fs_end_request);
456 // writing 8 bytes to an eventfd cannot fail593 // 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;
458 return;595 return;
459 },596 },
460 builtin.Os.macosx => {597 builtin.Os.macosx => {
598 self.posixFsRequest(&self.os_data.fs_end_request);
461 const final_kevent = (*[1]posix.Kevent)(&self.os_data.final_kevent);599 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];
463 // cannot fail because we already added it and this just enables it601 // 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;
465 return;603 return;
466 },604 },
467 builtin.Os.windows => {605 builtin.Os.windows => {
...@@ -469,7 +607,7 @@ pub const Loop = struct {...@@ -469,7 +607,7 @@ pub const Loop = struct {
469 while (i < self.extra_threads.len + 1) : (i += 1) {607 while (i < self.extra_threads.len + 1) : (i += 1) {
470 while (true) {608 while (true) {
471 const overlapped = @intToPtr(?*windows.OVERLAPPED, 0x1);609 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;
473 break;611 break;
474 }612 }
475 }613 }
...@@ -492,8 +630,8 @@ pub const Loop = struct {...@@ -492,8 +630,8 @@ pub const Loop = struct {
492 switch (builtin.os) {630 switch (builtin.os) {
493 builtin.Os.linux => {631 builtin.Os.linux => {
494 // only process 1 event so we don't steal from other threads632 // only process 1 event so we don't steal from other threads
495 var events: [1]std.os.linux.epoll_event = undefined;633 var events: [1]os.linux.epoll_event = undefined;
496 const count = std.os.linuxEpollWait(self.os_data.epollfd, events[0..], -1);634 const count = os.linuxEpollWait(self.os_data.epollfd, events[0..], -1);
497 for (events[0..count]) |ev| {635 for (events[0..count]) |ev| {
498 const resume_node = @intToPtr(*ResumeNode, ev.data.ptr);636 const resume_node = @intToPtr(*ResumeNode, ev.data.ptr);
499 const handle = resume_node.handle;637 const handle = resume_node.handle;
...@@ -516,13 +654,17 @@ pub const Loop = struct {...@@ -516,13 +654,17 @@ pub const Loop = struct {
516 },654 },
517 builtin.Os.macosx => {655 builtin.Os.macosx => {
518 var eventlist: [1]posix.Kevent = undefined;656 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;
520 for (eventlist[0..count]) |ev| {659 for (eventlist[0..count]) |ev| {
521 const resume_node = @intToPtr(*ResumeNode, ev.udata);660 const resume_node = @intToPtr(*ResumeNode, ev.udata);
522 const handle = resume_node.handle;661 const handle = resume_node.handle;
523 const resume_node_id = resume_node.id;662 const resume_node_id = resume_node.id;
524 switch (resume_node_id) {663 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 },
526 ResumeNode.Id.Stop => return,668 ResumeNode.Id.Stop => return,
527 ResumeNode.Id.EventFd => {669 ResumeNode.Id.EventFd => {
528 const event_fd_node = @fieldParentPtr(ResumeNode.EventFd, "base", resume_node);670 const event_fd_node = @fieldParentPtr(ResumeNode.EventFd, "base", resume_node);
...@@ -541,9 +683,10 @@ pub const Loop = struct {...@@ -541,9 +683,10 @@ pub const Loop = struct {
541 while (true) {683 while (true) {
542 var nbytes: windows.DWORD = undefined;684 var nbytes: windows.DWORD = undefined;
543 var overlapped: ?*windows.OVERLAPPED = undefined;685 var overlapped: ?*windows.OVERLAPPED = undefined;
544 switch (std.os.windowsGetQueuedCompletionStatus(self.os_data.io_port, &nbytes, &completion_key, &overlapped, windows.INFINITE)) {686 switch (os.windowsGetQueuedCompletionStatus(self.os_data.io_port, &nbytes, &completion_key, &overlapped, windows.INFINITE)) {
545 std.os.WindowsWaitResult.Aborted => return,687 os.WindowsWaitResult.Aborted => return,
546 std.os.WindowsWaitResult.Normal => {},688 os.WindowsWaitResult.Normal => {},
689 os.WindowsWaitResult.Cancelled => continue,
547 }690 }
548 if (overlapped != null) break;691 if (overlapped != null) break;
549 }692 }
...@@ -560,21 +703,101 @@ pub const Loop = struct {...@@ -560,21 +703,101 @@ pub const Loop = struct {
560 },703 },
561 }704 }
562 resume handle;705 resume handle;
563 if (resume_node_id == ResumeNode.Id.EventFd) {706 self.finishOneEvent();
564 self.finishOneEvent();
565 }
566 },707 },
567 else => @compileError("unsupported OS"),708 else => @compileError("unsupported OS"),
568 }709 }
569 }710 }
570 }711 }
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
572 const OsData = switch (builtin.os) {799 const OsData = switch (builtin.os) {
573 builtin.Os.linux => struct {800 builtin.Os.linux => LinuxOsData,
574 epollfd: i32,
575 final_eventfd: i32,
576 final_eventfd_event: std.os.linux.epoll_event,
577 },
578 builtin.Os.macosx => MacOsData,801 builtin.Os.macosx => MacOsData,
579 builtin.Os.windows => struct {802 builtin.Os.windows => struct {
580 io_port: windows.HANDLE,803 io_port: windows.HANDLE,
...@@ -586,7 +809,22 @@ pub const Loop = struct {...@@ -586,7 +809,22 @@ pub const Loop = struct {
586 const MacOsData = struct {809 const MacOsData = struct {
587 kqfd: i32,810 kqfd: i32,
588 final_kevent: posix.Kevent,811 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,
590 };828 };
591};829};
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 {...@@ -55,13 +55,13 @@ pub const Server = struct {
55 errdefer cancel self.accept_coro.?;55 errdefer cancel self.accept_coro.?;
5656
57 self.listen_resume_node.handle = self.accept_coro.?;57 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);
59 errdefer self.loop.removeFd(sockfd);59 errdefer self.loop.removeFd(sockfd);
60 }60 }
6161
62 /// Stop listening62 /// Stop listening
63 pub fn close(self: *Server) void {63 pub fn close(self: *Server) void {
64 self.loop.removeFd(self.sockfd.?);64 self.loop.linuxRemoveFd(self.sockfd.?);
65 std.os.close(self.sockfd.?);65 std.os.close(self.sockfd.?);
66 }66 }
6767
...@@ -116,7 +116,7 @@ pub async fn connect(loop: *Loop, _address: *const std.net.Address) !std.os.File...@@ -116,7 +116,7 @@ pub async fn connect(loop: *Loop, _address: *const std.net.Address) !std.os.File
116 errdefer std.os.close(sockfd);116 errdefer std.os.close(sockfd);
117117
118 try std.os.posixConnectAsync(sockfd, &address.os_addr);118 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);
120 try std.os.posixGetSockOptConnectError(sockfd);120 try std.os.posixGetSockOptConnectError(sockfd);
121121
122 return std.os.File.openHandle(sockfd);122 return std.os.File.openHandle(sockfd);
...@@ -181,4 +181,3 @@ async fn doAsyncTest(loop: *Loop, address: *const std.net.Address, server: *Serv...@@ -181,4 +181,3 @@ async fn doAsyncTest(loop: *Loop, address: *const std.net.Address, server: *Serv
181 assert(mem.eql(u8, msg, "hello from server\n"));181 assert(mem.eql(u8, msg, "hello from server\n"));
182 server.close();182 server.close();
183}183}
184
std/hash_map.zig+257-57
...@@ -9,6 +9,10 @@ const builtin = @import("builtin");...@@ -9,6 +9,10 @@ const builtin = @import("builtin");
9const want_modification_safety = builtin.mode != builtin.Mode.ReleaseFast;9const want_modification_safety = builtin.mode != builtin.Mode.ReleaseFast;
10const debug_u32 = if (want_modification_safety) u32 else void;10const 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
12pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u32, comptime eql: fn (a: K, b: K) bool) type {16pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u32, comptime eql: fn (a: K, b: K) bool) type {
13 return struct {17 return struct {
14 entries: []Entry,18 entries: []Entry,
...@@ -20,13 +24,22 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3...@@ -20,13 +24,22 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3
2024
21 const Self = this;25 const Self = this;
2226
23 pub const Entry = struct {27 pub const KV = struct {
24 used: bool,
25 distance_from_start_index: usize,
26 key: K,28 key: K,
27 value: V,29 value: V,
28 };30 };
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
30 pub const Iterator = struct {43 pub const Iterator = struct {
31 hm: *const Self,44 hm: *const Self,
32 // how many items have we returned45 // 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...@@ -36,7 +49,7 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3
36 // used to detect concurrent modification49 // used to detect concurrent modification
37 initial_modification_count: debug_u32,50 initial_modification_count: debug_u32,
3851
39 pub fn next(it: *Iterator) ?*Entry {52 pub fn next(it: *Iterator) ?*KV {
40 if (want_modification_safety) {53 if (want_modification_safety) {
41 assert(it.initial_modification_count == it.hm.modification_count); // concurrent modification54 assert(it.initial_modification_count == it.hm.modification_count); // concurrent modification
42 }55 }
...@@ -46,7 +59,7 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3...@@ -46,7 +59,7 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3
46 if (entry.used) {59 if (entry.used) {
47 it.index += 1;60 it.index += 1;
48 it.count += 1;61 it.count += 1;
49 return entry;62 return &entry.kv;
50 }63 }
51 }64 }
52 unreachable; // no next item65 unreachable; // no next item
...@@ -71,7 +84,7 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3...@@ -71,7 +84,7 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3
71 };84 };
72 }85 }
7386
74 pub fn deinit(hm: *const Self) void {87 pub fn deinit(hm: Self) void {
75 hm.allocator.free(hm.entries);88 hm.allocator.free(hm.entries);
76 }89 }
7790
...@@ -84,34 +97,65 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3...@@ -84,34 +97,65 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3
84 hm.incrementModificationCount();97 hm.incrementModificationCount();
85 }98 }
8699
87 pub fn count(hm: *const Self) usize {100 pub fn count(self: Self) usize {
88 return hm.size;101 return self.size;
89 }102 }
90103
91 /// Returns the value that was already there.104 /// If key exists this function cannot fail.
92 pub fn put(hm: *Self, key: K, value: *const V) !?V {105 /// If there is an existing item with `key`, then the result
93 if (hm.entries.len == 0) {106 /// kv pointer points to it, and found_existing is true.
94 try hm.initCapacity(16);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);
95 }132 }
96 hm.incrementModificationCount();
97133
98 // if we get too full (60%), double the capacity134 // if we get too full (60%), double the capacity
99 if (hm.size * 5 >= hm.entries.len * 3) {135 if (self.size * 5 >= self.entries.len * 3) {
100 const old_entries = hm.entries;136 const old_entries = self.entries;
101 try hm.initCapacity(hm.entries.len * 2);137 try self.initCapacity(self.entries.len * 2);
102 // dump all of the old elements into the new table138 // dump all of the old elements into the new table
103 for (old_entries) |*old_entry| {139 for (old_entries) |*old_entry| {
104 if (old_entry.used) {140 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;
106 }142 }
107 }143 }
108 hm.allocator.free(old_entries);144 self.allocator.free(old_entries);
109 }145 }
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;
112 }156 }
113157
114 pub fn get(hm: *const Self, key: K) ?*Entry {158 pub fn get(hm: *const Self, key: K) ?*KV {
115 if (hm.entries.len == 0) {159 if (hm.entries.len == 0) {
116 return null;160 return null;
117 }161 }
...@@ -122,7 +166,7 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3...@@ -122,7 +166,7 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3
122 return hm.get(key) != null;166 return hm.get(key) != null;
123 }167 }
124168
125 pub fn remove(hm: *Self, key: K) ?*Entry {169 pub fn remove(hm: *Self, key: K) ?*KV {
126 if (hm.entries.len == 0) return null;170 if (hm.entries.len == 0) return null;
127 hm.incrementModificationCount();171 hm.incrementModificationCount();
128 const start_index = hm.keyToIndex(key);172 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...@@ -134,7 +178,7 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3
134178
135 if (!entry.used) return null;179 if (!entry.used) return null;
136180
137 if (!eql(entry.key, key)) continue;181 if (!eql(entry.kv.key, key)) continue;
138182
139 while (roll_over < hm.entries.len) : (roll_over += 1) {183 while (roll_over < hm.entries.len) : (roll_over += 1) {
140 const next_index = (start_index + roll_over + 1) % hm.entries.len;184 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...@@ -142,7 +186,7 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3
142 if (!next_entry.used or next_entry.distance_from_start_index == 0) {186 if (!next_entry.used or next_entry.distance_from_start_index == 0) {
143 entry.used = false;187 entry.used = false;
144 hm.size -= 1;188 hm.size -= 1;
145 return entry;189 return &entry.kv;
146 }190 }
147 entry.* = next_entry.*;191 entry.* = next_entry.*;
148 entry.distance_from_start_index -= 1;192 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...@@ -163,6 +207,16 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3
163 };207 };
164 }208 }
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
166 fn initCapacity(hm: *Self, capacity: usize) !void {220 fn initCapacity(hm: *Self, capacity: usize) !void {
167 hm.entries = try hm.allocator.alloc(Entry, capacity);221 hm.entries = try hm.allocator.alloc(Entry, capacity);
168 hm.size = 0;222 hm.size = 0;
...@@ -178,60 +232,81 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3...@@ -178,60 +232,81 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3
178 }232 }
179 }233 }
180234
181 /// Returns the value that was already there.235 const InternalPutResult = struct {
182 fn internalPut(hm: *Self, orig_key: K, orig_value: *const V) ?V {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 {
183 var key = orig_key;243 var key = orig_key;
184 var value = orig_value.*;244 var value: V = undefined;
185 const start_index = hm.keyToIndex(key);245 const start_index = self.keyToIndex(key);
186 var roll_over: usize = 0;246 var roll_over: usize = 0;
187 var distance_from_start_index: usize = 0;247 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) : ({
189 roll_over += 1;254 roll_over += 1;
190 distance_from_start_index += 1;255 distance_from_start_index += 1;
191 }) {256 }) {
192 const index = (start_index + roll_over) % hm.entries.len;257 const index = (start_index + roll_over) % self.entries.len;
193 const entry = &hm.entries[index];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)) {
196 if (entry.distance_from_start_index < distance_from_start_index) {261 if (entry.distance_from_start_index < distance_from_start_index) {
197 // robin hood to the rescue262 // robin hood to the rescue
198 const tmp = entry.*;263 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 }
200 entry.* = Entry{269 entry.* = Entry{
201 .used = true,270 .used = true,
202 .distance_from_start_index = distance_from_start_index,271 .distance_from_start_index = distance_from_start_index,
203 .key = key,272 .kv = KV{
204 .value = value,273 .key = key,
274 .value = value,
275 },
205 };276 };
206 key = tmp.key;277 key = tmp.kv.key;
207 value = tmp.value;278 value = tmp.kv.value;
208 distance_from_start_index = tmp.distance_from_start_index;279 distance_from_start_index = tmp.distance_from_start_index;
209 }280 }
210 continue;281 continue;
211 }282 }
212283
213 var result: ?V = null;
214 if (entry.used) {284 if (entry.used) {
215 result = entry.value;285 result.old_kv = entry.kv;
216 } else {286 } else {
217 // adding an entry. otherwise overwriting old value with287 // adding an entry. otherwise overwriting old value with
218 // same key288 // same key
219 hm.size += 1;289 self.size += 1;
220 }290 }
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 }
223 entry.* = Entry{296 entry.* = Entry{
224 .used = true,297 .used = true,
225 .distance_from_start_index = distance_from_start_index,298 .distance_from_start_index = distance_from_start_index,
226 .key = key,299 .kv = KV{
227 .value = value,300 .key = key,
301 .value = value,
302 },
228 };303 };
229 return result;304 return result;
230 }305 }
231 unreachable; // put into a full map306 unreachable; // put into a full map
232 }307 }
233308
234 fn internalGet(hm: *const Self, key: K) ?*Entry {309 fn internalGet(hm: Self, key: K) ?*KV {
235 const start_index = hm.keyToIndex(key);310 const start_index = hm.keyToIndex(key);
236 {311 {
237 var roll_over: usize = 0;312 var roll_over: usize = 0;
...@@ -240,13 +315,13 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3...@@ -240,13 +315,13 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3
240 const entry = &hm.entries[index];315 const entry = &hm.entries[index];
241316
242 if (!entry.used) return null;317 if (!entry.used) return null;
243 if (eql(entry.key, key)) return entry;318 if (eql(entry.kv.key, key)) return &entry.kv;
244 }319 }
245 }320 }
246 return null;321 return null;
247 }322 }
248323
249 fn keyToIndex(hm: *const Self, key: K) usize {324 fn keyToIndex(hm: Self, key: K) usize {
250 return usize(hash(key)) % hm.entries.len;325 return usize(hash(key)) % hm.entries.len;
251 }326 }
252 };327 };
...@@ -256,7 +331,7 @@ test "basic hash map usage" {...@@ -256,7 +331,7 @@ test "basic hash map usage" {
256 var direct_allocator = std.heap.DirectAllocator.init();331 var direct_allocator = std.heap.DirectAllocator.init();
257 defer direct_allocator.deinit();332 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);
260 defer map.deinit();335 defer map.deinit();
261336
262 assert((try map.put(1, 11)) == null);337 assert((try map.put(1, 11)) == null);
...@@ -265,8 +340,19 @@ test "basic hash map usage" {...@@ -265,8 +340,19 @@ test "basic hash map usage" {
265 assert((try map.put(4, 44)) == null);340 assert((try map.put(4, 44)) == null);
266 assert((try map.put(5, 55)) == null);341 assert((try map.put(5, 55)) == null);
267342
268 assert((try map.put(5, 66)).? == 55);343 assert((try map.put(5, 66)).?.value == 55);
269 assert((try map.put(5, 55)).? == 66);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
271 assert(map.contains(2));357 assert(map.contains(2));
272 assert(map.get(2).?.value == 22);358 assert(map.get(2).?.value == 22);
...@@ -279,7 +365,7 @@ test "iterator hash map" {...@@ -279,7 +365,7 @@ test "iterator hash map" {
279 var direct_allocator = std.heap.DirectAllocator.init();365 var direct_allocator = std.heap.DirectAllocator.init();
280 defer direct_allocator.deinit();366 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);
283 defer reset_map.deinit();369 defer reset_map.deinit();
284370
285 assert((try reset_map.put(1, 11)) == null);371 assert((try reset_map.put(1, 11)) == null);
...@@ -287,14 +373,14 @@ test "iterator hash map" {...@@ -287,14 +373,14 @@ test "iterator hash map" {
287 assert((try reset_map.put(3, 33)) == null);373 assert((try reset_map.put(3, 33)) == null);
288374
289 var keys = []i32{375 var keys = []i32{
290 1,
291 2,
292 3,376 3,
377 2,
378 1,
293 };379 };
294 var values = []i32{380 var values = []i32{
295 11,
296 22,
297 33,381 33,
382 22,
383 11,
298 };384 };
299385
300 var it = reset_map.iterator();386 var it = reset_map.iterator();
...@@ -322,10 +408,124 @@ test "iterator hash map" {...@@ -322,10 +408,124 @@ test "iterator hash map" {
322 assert(entry.value == values[0]);408 assert(entry.value == values[0]);
323}409}
324410
325fn hash_i32(x: i32) u32 {411pub fn getAutoHashFn(comptime K: type) (fn (K) u32) {
326 return @bitCast(u32, x);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 }
327}486}
328487
329fn eql_i32(a: i32, b: i32) bool {488pub fn autoEql(a: var, b: @typeOf(a)) bool {
330 return a == b;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 }
331}531}
std/index.zig+3-1
...@@ -5,10 +5,11 @@ pub const BufSet = @import("buf_set.zig").BufSet;...@@ -5,10 +5,11 @@ pub const BufSet = @import("buf_set.zig").BufSet;
5pub const Buffer = @import("buffer.zig").Buffer;5pub const Buffer = @import("buffer.zig").Buffer;
6pub const BufferOutStream = @import("buffer.zig").BufferOutStream;6pub const BufferOutStream = @import("buffer.zig").BufferOutStream;
7pub const HashMap = @import("hash_map.zig").HashMap;7pub const HashMap = @import("hash_map.zig").HashMap;
8pub const AutoHashMap = @import("hash_map.zig").AutoHashMap;
8pub const LinkedList = @import("linked_list.zig").LinkedList;9pub const LinkedList = @import("linked_list.zig").LinkedList;
9pub const IntrusiveLinkedList = @import("linked_list.zig").IntrusiveLinkedList;
10pub const SegmentedList = @import("segmented_list.zig").SegmentedList;10pub const SegmentedList = @import("segmented_list.zig").SegmentedList;
11pub const DynLib = @import("dynamic_library.zig").DynLib;11pub const DynLib = @import("dynamic_library.zig").DynLib;
12pub const Mutex = @import("mutex.zig").Mutex;
1213
13pub const atomic = @import("atomic/index.zig");14pub const atomic = @import("atomic/index.zig");
14pub const base64 = @import("base64.zig");15pub const base64 = @import("base64.zig");
...@@ -49,6 +50,7 @@ test "std" {...@@ -49,6 +50,7 @@ test "std" {
49 _ = @import("hash_map.zig");50 _ = @import("hash_map.zig");
50 _ = @import("linked_list.zig");51 _ = @import("linked_list.zig");
51 _ = @import("segmented_list.zig");52 _ = @import("segmented_list.zig");
53 _ = @import("mutex.zig");
5254
53 _ = @import("base64.zig");55 _ = @import("base64.zig");
54 _ = @import("build.zig");56 _ = @import("build.zig");
std/io.zig+7-9
...@@ -415,13 +415,12 @@ pub fn PeekStream(comptime buffer_size: usize, comptime InStreamError: type) typ...@@ -415,13 +415,12 @@ pub fn PeekStream(comptime buffer_size: usize, comptime InStreamError: type) typ
415 self.at_end = (read < left);415 self.at_end = (read < left);
416 return pos + read;416 return pos + read;
417 }417 }
418
419 };418 };
420}419}
421420
422pub const SliceInStream = struct {421pub const SliceInStream = struct {
423 const Self = this;422 const Self = this;
424 pub const Error = error { };423 pub const Error = error{};
425 pub const Stream = InStream(Error);424 pub const Stream = InStream(Error);
426425
427 pub stream: Stream,426 pub stream: Stream,
...@@ -481,13 +480,12 @@ pub const SliceOutStream = struct {...@@ -481,13 +480,12 @@ pub const SliceOutStream = struct {
481480
482 assert(self.pos <= self.slice.len);481 assert(self.pos <= self.slice.len);
483482
484 const n =483 const n = if (self.pos + bytes.len <= self.slice.len)
485 if (self.pos + bytes.len <= self.slice.len)484 bytes.len
486 bytes.len485 else
487 else486 self.slice.len - self.pos;
488 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]);
491 self.pos += n;489 self.pos += n;
492490
493 if (n < bytes.len) {491 if (n < bytes.len) {
...@@ -586,7 +584,7 @@ pub const BufferedAtomicFile = struct {...@@ -586,7 +584,7 @@ pub const BufferedAtomicFile = struct {
586 });584 });
587 errdefer allocator.destroy(self);585 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);
590 errdefer self.atomic_file.deinit();588 errdefer self.atomic_file.deinit();
591589
592 self.file_stream = FileOutStream.init(&self.atomic_file.file);590 self.file_stream = FileOutStream.init(&self.atomic_file.file);
std/json.zig+1-1
...@@ -1318,7 +1318,7 @@ pub const Parser = struct {...@@ -1318,7 +1318,7 @@ pub const Parser = struct {
1318 _ = p.stack.pop();1318 _ = p.stack.pop();
13191319
1320 var object = &p.stack.items[p.stack.len - 1].Object;1320 var object = &p.stack.items[p.stack.len - 1].Object;
1321 _ = try object.put(key, value);1321 _ = try object.put(key, value.*);
1322 p.state = State.ObjectKey;1322 p.state = State.ObjectKey;
1323 },1323 },
1324 // Array Parent -> [ ..., <array>, value ]1324 // Array Parent -> [ ..., <array>, value ]
std/linked_list.zig+4-97
...@@ -4,18 +4,8 @@ const assert = debug.assert;...@@ -4,18 +4,8 @@ const assert = debug.assert;
4const mem = std.mem;4const mem = std.mem;
5const Allocator = mem.Allocator;5const Allocator = mem.Allocator;
66
7/// Generic non-intrusive doubly linked list.
8pub fn LinkedList(comptime T: type) type {
9 return BaseLinkedList(T, void, "");
10}
11
12/// Generic intrusive doubly linked list.
13pub fn IntrusiveLinkedList(comptime ParentType: type, comptime field_name: []const u8) type {
14 return BaseLinkedList(void, ParentType, field_name);
15}
16
17/// Generic doubly linked list.7/// Generic doubly linked list.
18fn BaseLinkedList(comptime T: type, comptime ParentType: type, comptime field_name: []const u8) type {8pub fn LinkedList(comptime T: type) type {
19 return struct {9 return struct {
20 const Self = this;10 const Self = this;
2111
...@@ -25,23 +15,13 @@ fn BaseLinkedList(comptime T: type, comptime ParentType: type, comptime field_na...@@ -25,23 +15,13 @@ fn BaseLinkedList(comptime T: type, comptime ParentType: type, comptime field_na
25 next: ?*Node,15 next: ?*Node,
26 data: T,16 data: T,
2717
28 pub fn init(value: *const T) Node {18 pub fn init(data: T) Node {
29 return Node{19 return Node{
30 .prev = null,20 .prev = null,
31 .next = null,21 .next = null,
32 .data = value.*,22 .data = data,
33 };23 };
34 }24 }
35
36 pub fn initIntrusive() Node {
37 // TODO: when #678 is solved this can become `init`.
38 return Node.init({});
39 }
40
41 pub fn toData(node: *Node) *ParentType {
42 comptime assert(isIntrusive());
43 return @fieldParentPtr(ParentType, field_name, node);
44 }
45 };25 };
4626
47 first: ?*Node,27 first: ?*Node,
...@@ -60,10 +40,6 @@ fn BaseLinkedList(comptime T: type, comptime ParentType: type, comptime field_na...@@ -60,10 +40,6 @@ fn BaseLinkedList(comptime T: type, comptime ParentType: type, comptime field_na
60 };40 };
61 }41 }
6242
63 fn isIntrusive() bool {
64 return ParentType != void or field_name.len != 0;
65 }
66
67 /// Insert a new node after an existing one.43 /// Insert a new node after an existing one.
68 ///44 ///
69 /// Arguments:45 /// Arguments:
...@@ -192,7 +168,6 @@ fn BaseLinkedList(comptime T: type, comptime ParentType: type, comptime field_na...@@ -192,7 +168,6 @@ fn BaseLinkedList(comptime T: type, comptime ParentType: type, comptime field_na
192 /// Returns:168 /// Returns:
193 /// A pointer to the new node.169 /// A pointer to the new node.
194 pub fn allocateNode(list: *Self, allocator: *Allocator) !*Node {170 pub fn allocateNode(list: *Self, allocator: *Allocator) !*Node {
195 comptime assert(!isIntrusive());
196 return allocator.create(Node(undefined));171 return allocator.create(Node(undefined));
197 }172 }
198173
...@@ -202,7 +177,6 @@ fn BaseLinkedList(comptime T: type, comptime ParentType: type, comptime field_na...@@ -202,7 +177,6 @@ fn BaseLinkedList(comptime T: type, comptime ParentType: type, comptime field_na
202 /// node: Pointer to the node to deallocate.177 /// node: Pointer to the node to deallocate.
203 /// allocator: Dynamic memory allocator.178 /// allocator: Dynamic memory allocator.
204 pub fn destroyNode(list: *Self, node: *Node, allocator: *Allocator) void {179 pub fn destroyNode(list: *Self, node: *Node, allocator: *Allocator) void {
205 comptime assert(!isIntrusive());
206 allocator.destroy(node);180 allocator.destroy(node);
207 }181 }
208182
...@@ -214,8 +188,7 @@ fn BaseLinkedList(comptime T: type, comptime ParentType: type, comptime field_na...@@ -214,8 +188,7 @@ fn BaseLinkedList(comptime T: type, comptime ParentType: type, comptime field_na
214 ///188 ///
215 /// Returns:189 /// Returns:
216 /// A pointer to the new node.190 /// A pointer to the new node.
217 pub fn createNode(list: *Self, data: *const T, allocator: *Allocator) !*Node {191 pub fn createNode(list: *Self, data: T, allocator: *Allocator) !*Node {
218 comptime assert(!isIntrusive());
219 var node = try list.allocateNode(allocator);192 var node = try list.allocateNode(allocator);
220 node.* = Node.init(data);193 node.* = Node.init(data);
221 return node;194 return node;
...@@ -274,69 +247,3 @@ test "basic linked list test" {...@@ -274,69 +247,3 @@ test "basic linked list test" {
274 assert(list.last.?.data == 4);247 assert(list.last.?.data == 4);
275 assert(list.len == 2);248 assert(list.len == 2);
276}249}
277
278const ElementList = IntrusiveLinkedList(Element, "link");
279const Element = struct {
280 value: u32,
281 link: IntrusiveLinkedList(Element, "link").Node,
282};
283
284test "basic intrusive linked list test" {
285 const allocator = debug.global_allocator;
286 var list = ElementList.init();
287
288 var one = Element{
289 .value = 1,
290 .link = ElementList.Node.initIntrusive(),
291 };
292 var two = Element{
293 .value = 2,
294 .link = ElementList.Node.initIntrusive(),
295 };
296 var three = Element{
297 .value = 3,
298 .link = ElementList.Node.initIntrusive(),
299 };
300 var four = Element{
301 .value = 4,
302 .link = ElementList.Node.initIntrusive(),
303 };
304 var five = Element{
305 .value = 5,
306 .link = ElementList.Node.initIntrusive(),
307 };
308
309 list.append(&two.link); // {2}
310 list.append(&five.link); // {2, 5}
311 list.prepend(&one.link); // {1, 2, 5}
312 list.insertBefore(&five.link, &four.link); // {1, 2, 4, 5}
313 list.insertAfter(&two.link, &three.link); // {1, 2, 3, 4, 5}
314
315 // Traverse forwards.
316 {
317 var it = list.first;
318 var index: u32 = 1;
319 while (it) |node| : (it = node.next) {
320 assert(node.toData().value == index);
321 index += 1;
322 }
323 }
324
325 // Traverse backwards.
326 {
327 var it = list.last;
328 var index: u32 = 1;
329 while (it) |node| : (it = node.prev) {
330 assert(node.toData().value == (6 - index));
331 index += 1;
332 }
333 }
334
335 var first = list.popFirst(); // {2, 3, 4, 5}
336 var last = list.pop(); // {2, 3, 4}
337 list.remove(&three.link); // {2, 4}
338
339 assert(list.first.?.toData().value == 2);
340 assert(list.last.?.toData().value == 4);
341 assert(list.len == 2);
342}
std/mem.zig+1-1
...@@ -577,7 +577,7 @@ pub fn join(allocator: *Allocator, sep: u8, strings: ...) ![]u8 {...@@ -577,7 +577,7 @@ pub fn join(allocator: *Allocator, sep: u8, strings: ...) ![]u8 {
577 }577 }
578 }578 }
579579
580 return buf[0..buf_index];580 return allocator.shrink(u8, buf, buf_index);
581}581}
582582
583test "mem.join" {583test "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;...@@ -482,91 +482,98 @@ pub const NOTE_MACH_CONTINUOUS_TIME = 0x00000080;
482/// data is mach absolute time units482/// data is mach absolute time units
483pub const NOTE_MACHTIME = 0x00000100;483pub const NOTE_MACHTIME = 0x00000100;
484484
485pub const AF_UNSPEC: c_int = 0;485pub const AF_UNSPEC = 0;
486pub const AF_LOCAL: c_int = 1;486pub const AF_LOCAL = 1;
487pub const AF_UNIX: c_int = AF_LOCAL;487pub const AF_UNIX = AF_LOCAL;
488pub const AF_INET: c_int = 2;488pub const AF_INET = 2;
489pub const AF_SYS_CONTROL: c_int = 2;489pub const AF_SYS_CONTROL = 2;
490pub const AF_IMPLINK: c_int = 3;490pub const AF_IMPLINK = 3;
491pub const AF_PUP: c_int = 4;491pub const AF_PUP = 4;
492pub const AF_CHAOS: c_int = 5;492pub const AF_CHAOS = 5;
493pub const AF_NS: c_int = 6;493pub const AF_NS = 6;
494pub const AF_ISO: c_int = 7;494pub const AF_ISO = 7;
495pub const AF_OSI: c_int = AF_ISO;495pub const AF_OSI = AF_ISO;
496pub const AF_ECMA: c_int = 8;496pub const AF_ECMA = 8;
497pub const AF_DATAKIT: c_int = 9;497pub const AF_DATAKIT = 9;
498pub const AF_CCITT: c_int = 10;498pub const AF_CCITT = 10;
499pub const AF_SNA: c_int = 11;499pub const AF_SNA = 11;
500pub const AF_DECnet: c_int = 12;500pub const AF_DECnet = 12;
501pub const AF_DLI: c_int = 13;501pub const AF_DLI = 13;
502pub const AF_LAT: c_int = 14;502pub const AF_LAT = 14;
503pub const AF_HYLINK: c_int = 15;503pub const AF_HYLINK = 15;
504pub const AF_APPLETALK: c_int = 16;504pub const AF_APPLETALK = 16;
505pub const AF_ROUTE: c_int = 17;505pub const AF_ROUTE = 17;
506pub const AF_LINK: c_int = 18;506pub const AF_LINK = 18;
507pub const AF_XTP: c_int = 19;507pub const AF_XTP = 19;
508pub const AF_COIP: c_int = 20;508pub const AF_COIP = 20;
509pub const AF_CNT: c_int = 21;509pub const AF_CNT = 21;
510pub const AF_RTIP: c_int = 22;510pub const AF_RTIP = 22;
511pub const AF_IPX: c_int = 23;511pub const AF_IPX = 23;
512pub const AF_SIP: c_int = 24;512pub const AF_SIP = 24;
513pub const AF_PIP: c_int = 25;513pub const AF_PIP = 25;
514pub const AF_ISDN: c_int = 28;514pub const AF_ISDN = 28;
515pub const AF_E164: c_int = AF_ISDN;515pub const AF_E164 = AF_ISDN;
516pub const AF_KEY: c_int = 29;516pub const AF_KEY = 29;
517pub const AF_INET6: c_int = 30;517pub const AF_INET6 = 30;
518pub const AF_NATM: c_int = 31;518pub const AF_NATM = 31;
519pub const AF_SYSTEM: c_int = 32;519pub const AF_SYSTEM = 32;
520pub const AF_NETBIOS: c_int = 33;520pub const AF_NETBIOS = 33;
521pub const AF_PPP: c_int = 34;521pub const AF_PPP = 34;
522pub const AF_MAX: c_int = 40;522pub const AF_MAX = 40;
523523
524pub const PF_UNSPEC: c_int = AF_UNSPEC;524pub const PF_UNSPEC = AF_UNSPEC;
525pub const PF_LOCAL: c_int = AF_LOCAL;525pub const PF_LOCAL = AF_LOCAL;
526pub const PF_UNIX: c_int = PF_LOCAL;526pub const PF_UNIX = PF_LOCAL;
527pub const PF_INET: c_int = AF_INET;527pub const PF_INET = AF_INET;
528pub const PF_IMPLINK: c_int = AF_IMPLINK;528pub const PF_IMPLINK = AF_IMPLINK;
529pub const PF_PUP: c_int = AF_PUP;529pub const PF_PUP = AF_PUP;
530pub const PF_CHAOS: c_int = AF_CHAOS;530pub const PF_CHAOS = AF_CHAOS;
531pub const PF_NS: c_int = AF_NS;531pub const PF_NS = AF_NS;
532pub const PF_ISO: c_int = AF_ISO;532pub const PF_ISO = AF_ISO;
533pub const PF_OSI: c_int = AF_ISO;533pub const PF_OSI = AF_ISO;
534pub const PF_ECMA: c_int = AF_ECMA;534pub const PF_ECMA = AF_ECMA;
535pub const PF_DATAKIT: c_int = AF_DATAKIT;535pub const PF_DATAKIT = AF_DATAKIT;
536pub const PF_CCITT: c_int = AF_CCITT;536pub const PF_CCITT = AF_CCITT;
537pub const PF_SNA: c_int = AF_SNA;537pub const PF_SNA = AF_SNA;
538pub const PF_DECnet: c_int = AF_DECnet;538pub const PF_DECnet = AF_DECnet;
539pub const PF_DLI: c_int = AF_DLI;539pub const PF_DLI = AF_DLI;
540pub const PF_LAT: c_int = AF_LAT;540pub const PF_LAT = AF_LAT;
541pub const PF_HYLINK: c_int = AF_HYLINK;541pub const PF_HYLINK = AF_HYLINK;
542pub const PF_APPLETALK: c_int = AF_APPLETALK;542pub const PF_APPLETALK = AF_APPLETALK;
543pub const PF_ROUTE: c_int = AF_ROUTE;543pub const PF_ROUTE = AF_ROUTE;
544pub const PF_LINK: c_int = AF_LINK;544pub const PF_LINK = AF_LINK;
545pub const PF_XTP: c_int = AF_XTP;545pub const PF_XTP = AF_XTP;
546pub const PF_COIP: c_int = AF_COIP;546pub const PF_COIP = AF_COIP;
547pub const PF_CNT: c_int = AF_CNT;547pub const PF_CNT = AF_CNT;
548pub const PF_SIP: c_int = AF_SIP;548pub const PF_SIP = AF_SIP;
549pub const PF_IPX: c_int = AF_IPX;549pub const PF_IPX = AF_IPX;
550pub const PF_RTIP: c_int = AF_RTIP;550pub const PF_RTIP = AF_RTIP;
551pub const PF_PIP: c_int = AF_PIP;551pub const PF_PIP = AF_PIP;
552pub const PF_ISDN: c_int = AF_ISDN;552pub const PF_ISDN = AF_ISDN;
553pub const PF_KEY: c_int = AF_KEY;553pub const PF_KEY = AF_KEY;
554pub const PF_INET6: c_int = AF_INET6;554pub const PF_INET6 = AF_INET6;
555pub const PF_NATM: c_int = AF_NATM;555pub const PF_NATM = AF_NATM;
556pub const PF_SYSTEM: c_int = AF_SYSTEM;556pub const PF_SYSTEM = AF_SYSTEM;
557pub const PF_NETBIOS: c_int = AF_NETBIOS;557pub const PF_NETBIOS = AF_NETBIOS;
558pub const PF_PPP: c_int = AF_PPP;558pub const PF_PPP = AF_PPP;
559pub const PF_MAX: c_int = AF_MAX;559pub const PF_MAX = AF_MAX;
560560
561pub const SYSPROTO_EVENT: c_int = 1;561pub const SYSPROTO_EVENT = 1;
562pub const SYSPROTO_CONTROL: c_int = 2;562pub const SYSPROTO_CONTROL = 2;
563563
564pub const SOCK_STREAM: c_int = 1;564pub const SOCK_STREAM = 1;
565pub const SOCK_DGRAM: c_int = 2;565pub const SOCK_DGRAM = 2;
566pub const SOCK_RAW: c_int = 3;566pub const SOCK_RAW = 3;
567pub const SOCK_RDM: c_int = 4;567pub const SOCK_RDM = 4;
568pub const SOCK_SEQPACKET: c_int = 5;568pub const SOCK_SEQPACKET = 5;
569pub const SOCK_MAXADDRLEN: c_int = 255;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
571fn wstatus(x: i32) i32 {578fn wstatus(x: i32) i32 {
572 return x & 0o177;579 return x & 0o177;
...@@ -605,6 +612,11 @@ pub fn abort() noreturn {...@@ -605,6 +612,11 @@ pub fn abort() noreturn {
605 c.abort();612 c.abort();
606}613}
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
608pub fn exit(code: i32) noreturn {620pub fn exit(code: i32) noreturn {
609 c.exit(code);621 c.exit(code);
610}622}
...@@ -634,6 +646,10 @@ pub fn read(fd: i32, buf: [*]u8, nbyte: usize) usize {...@@ -634,6 +646,10 @@ pub fn read(fd: i32, buf: [*]u8, nbyte: usize) usize {
634 return errnoWrap(c.read(fd, @ptrCast(*c_void, buf), nbyte));646 return errnoWrap(c.read(fd, @ptrCast(*c_void, buf), nbyte));
635}647}
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
637pub fn stat(noalias path: [*]const u8, noalias buf: *stat) usize {653pub fn stat(noalias path: [*]const u8, noalias buf: *stat) usize {
638 return errnoWrap(c.stat(path, buf));654 return errnoWrap(c.stat(path, buf));
639}655}
...@@ -642,6 +658,10 @@ pub fn write(fd: i32, buf: [*]const u8, nbyte: usize) usize {...@@ -642,6 +658,10 @@ pub fn write(fd: i32, buf: [*]const u8, nbyte: usize) usize {
642 return errnoWrap(c.write(fd, @ptrCast(*const c_void, buf), nbyte));658 return errnoWrap(c.write(fd, @ptrCast(*const c_void, buf), nbyte));
643}659}
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
645pub fn mmap(address: ?[*]u8, length: usize, prot: usize, flags: u32, fd: i32, offset: isize) usize {665pub fn mmap(address: ?[*]u8, length: usize, prot: usize, flags: u32, fd: i32, offset: isize) usize {
646 const ptr_result = c.mmap(666 const ptr_result = c.mmap(
647 @ptrCast(*c_void, address),667 @ptrCast(*c_void, address),
...@@ -805,6 +825,20 @@ pub fn sigaction(sig: u5, noalias act: *const Sigaction, noalias oact: ?*Sigacti...@@ -805,6 +825,20 @@ pub fn sigaction(sig: u5, noalias act: *const Sigaction, noalias oact: ?*Sigacti
805 return result;825 return result;
806}826}
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
808pub const sigset_t = c.sigset_t;842pub const sigset_t = c.sigset_t;
809pub const empty_sigset = sigset_t(0);843pub const empty_sigset = sigset_t(0);
810844
...@@ -812,8 +846,13 @@ pub const timespec = c.timespec;...@@ -812,8 +846,13 @@ pub const timespec = c.timespec;
812pub const Stat = c.Stat;846pub const Stat = c.Stat;
813pub const dirent = c.dirent;847pub const dirent = c.dirent;
814848
849pub const in_port_t = c.in_port_t;
815pub const sa_family_t = c.sa_family_t;850pub const sa_family_t = c.sa_family_t;
851pub const socklen_t = c.socklen_t;
852
816pub const sockaddr = c.sockaddr;853pub const sockaddr = c.sockaddr;
854pub const sockaddr_in = c.sockaddr_in;
855pub const sockaddr_in6 = c.sockaddr_in6;
817856
818/// Renamed from `kevent` to `Kevent` to avoid conflict with the syscall.857/// Renamed from `kevent` to `Kevent` to avoid conflict with the syscall.
819pub const Kevent = c.Kevent;858pub const Kevent = c.Kevent;
std/os/file.zig+26-11
...@@ -15,6 +15,16 @@ pub const File = struct {...@@ -15,6 +15,16 @@ pub const File = struct {
15 /// The OS-specific file descriptor or file handle.15 /// The OS-specific file descriptor or file handle.
16 handle: os.FileHandle,16 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
18 pub const OpenError = os.WindowsOpenError || os.PosixOpenError;28 pub const OpenError = os.WindowsOpenError || os.PosixOpenError;
1929
20 /// `path` needs to be copied in memory to add a null terminating byte, hence the allocator.30 /// `path` needs to be copied in memory to add a null terminating byte, hence the allocator.
...@@ -39,16 +49,16 @@ pub const File = struct {...@@ -39,16 +49,16 @@ pub const File = struct {
39 }49 }
40 }50 }
4151
42 /// Calls `openWriteMode` with os.default_file_mode for the mode.52 /// Calls `openWriteMode` with os.File.default_mode for the mode.
43 pub fn openWrite(allocator: *mem.Allocator, path: []const u8) OpenError!File {53 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);
45 }55 }
4656
47 /// If the path does not exist it will be created.57 /// If the path does not exist it will be created.
48 /// If a file already exists in the destination it will be truncated.58 /// If a file already exists in the destination it will be truncated.
49 /// `path` needs to be copied in memory to add a null terminating byte, hence the allocator.59 /// `path` needs to be copied in memory to add a null terminating byte, hence the allocator.
50 /// Call close to clean up.60 /// 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 {
52 if (is_posix) {62 if (is_posix) {
53 const flags = posix.O_LARGEFILE | posix.O_WRONLY | posix.O_CREAT | posix.O_CLOEXEC | posix.O_TRUNC;63 const flags = posix.O_LARGEFILE | posix.O_WRONLY | posix.O_CREAT | posix.O_CLOEXEC | posix.O_TRUNC;
54 const fd = try os.posixOpen(allocator, path, flags, file_mode);64 const fd = try os.posixOpen(allocator, path, flags, file_mode);
...@@ -72,7 +82,7 @@ pub const File = struct {...@@ -72,7 +82,7 @@ pub const File = struct {
72 /// If a file already exists in the destination this returns OpenError.PathAlreadyExists82 /// If a file already exists in the destination this returns OpenError.PathAlreadyExists
73 /// `path` needs to be copied in memory to add a null terminating byte, hence the allocator.83 /// `path` needs to be copied in memory to add a null terminating byte, hence the allocator.
74 /// Call close to clean up.84 /// 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 {
76 if (is_posix) {86 if (is_posix) {
77 const flags = posix.O_LARGEFILE | posix.O_WRONLY | posix.O_CREAT | posix.O_CLOEXEC | posix.O_EXCL;87 const flags = posix.O_LARGEFILE | posix.O_WRONLY | posix.O_CREAT | posix.O_CLOEXEC | posix.O_EXCL;
78 const fd = try os.posixOpen(allocator, path, flags, file_mode);88 const fd = try os.posixOpen(allocator, path, flags, file_mode);
...@@ -282,7 +292,7 @@ pub const File = struct {...@@ -282,7 +292,7 @@ pub const File = struct {
282 Unexpected,292 Unexpected,
283 };293 };
284294
285 pub fn mode(self: *File) ModeError!os.FileMode {295 pub fn mode(self: *File) ModeError!Mode {
286 if (is_posix) {296 if (is_posix) {
287 var stat: posix.Stat = undefined;297 var stat: posix.Stat = undefined;
288 const err = posix.getErrno(posix.fstat(self.handle, &stat));298 const err = posix.getErrno(posix.fstat(self.handle, &stat));
...@@ -296,7 +306,7 @@ pub const File = struct {...@@ -296,7 +306,7 @@ pub const File = struct {
296306
297 // TODO: we should be able to cast u16 to ModeError!u32, making this307 // TODO: we should be able to cast u16 to ModeError!u32, making this
298 // explicit cast not necessary308 // explicit cast not necessary
299 return os.FileMode(stat.mode);309 return Mode(stat.mode);
300 } else if (is_windows) {310 } else if (is_windows) {
301 return {};311 return {};
302 } else {312 } else {
...@@ -305,9 +315,11 @@ pub const File = struct {...@@ -305,9 +315,11 @@ pub const File = struct {
305 }315 }
306316
307 pub const ReadError = error{317 pub const ReadError = error{
308 BadFd,318 FileClosed,
309 Io,319 InputOutput,
310 IsDir,320 IsDir,
321 WouldBlock,
322 SystemResources,
311323
312 Unexpected,324 Unexpected,
313 };325 };
...@@ -323,9 +335,12 @@ pub const File = struct {...@@ -323,9 +335,12 @@ pub const File = struct {
323 posix.EINTR => continue,335 posix.EINTR => continue,
324 posix.EINVAL => unreachable,336 posix.EINVAL => unreachable,
325 posix.EFAULT => unreachable,337 posix.EFAULT => unreachable,
326 posix.EBADF => return error.BadFd,338 posix.EAGAIN => return error.WouldBlock,
327 posix.EIO => return error.Io,339 posix.EBADF => return error.FileClosed,
340 posix.EIO => return error.InputOutput,
328 posix.EISDIR => return error.IsDir,341 posix.EISDIR => return error.IsDir,
342 posix.ENOBUFS => return error.SystemResources,
343 posix.ENOMEM => return error.SystemResources,
329 else => return os.unexpectedErrorPosix(read_err),344 else => return os.unexpectedErrorPosix(read_err),
330 }345 }
331 }346 }
...@@ -338,7 +353,7 @@ pub const File = struct {...@@ -338,7 +353,7 @@ pub const File = struct {
338 while (index < buffer.len) {353 while (index < buffer.len) {
339 const want_read_count = @intCast(windows.DWORD, math.min(windows.DWORD(@maxValue(windows.DWORD)), buffer.len - index));354 const want_read_count = @intCast(windows.DWORD, math.min(windows.DWORD(@maxValue(windows.DWORD)), buffer.len - index));
340 var amt_read: windows.DWORD = undefined;355 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) {
342 const err = windows.GetLastError();357 const err = windows.GetLastError();
343 return switch (err) {358 return switch (err) {
344 windows.ERROR.OPERATION_ABORTED => continue,359 windows.ERROR.OPERATION_ABORTED => continue,
std/os/index.zig+169-12
...@@ -38,16 +38,6 @@ pub const path = @import("path.zig");...@@ -38,16 +38,6 @@ pub const path = @import("path.zig");
38pub const File = @import("file.zig").File;38pub const File = @import("file.zig").File;
39pub const time = @import("time.zig");39pub 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
51pub const page_size = 4 * 1024;41pub const page_size = 4 * 1024;
5242
53pub const UserInfo = @import("get_user_id.zig").UserInfo;43pub const UserInfo = @import("get_user_id.zig").UserInfo;
...@@ -256,6 +246,67 @@ pub fn posixRead(fd: i32, buf: []u8) !void {...@@ -256,6 +246,67 @@ pub fn posixRead(fd: i32, buf: []u8) !void {
256 }246 }
257}247}
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
259pub const PosixWriteError = error{310pub const PosixWriteError = error{
260 WouldBlock,311 WouldBlock,
261 FileClosed,312 FileClosed,
...@@ -300,6 +351,71 @@ pub fn posixWrite(fd: i32, bytes: []const u8) !void {...@@ -300,6 +351,71 @@ pub fn posixWrite(fd: i32, bytes: []const u8) !void {
300 }351 }
301}352}
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
303pub const PosixOpenError = error{419pub const PosixOpenError = error{
304 OutOfMemory,420 OutOfMemory,
305 AccessDenied,421 AccessDenied,
...@@ -853,7 +969,7 @@ pub fn copyFile(allocator: *Allocator, source_path: []const u8, dest_path: []con...@@ -853,7 +969,7 @@ pub fn copyFile(allocator: *Allocator, source_path: []const u8, dest_path: []con
853/// Guaranteed to be atomic. However until https://patchwork.kernel.org/patch/9636735/ is969/// Guaranteed to be atomic. However until https://patchwork.kernel.org/patch/9636735/ is
854/// merged and readily available,970/// merged and readily available,
855/// there is a possibility of power loss or application termination leaving temporary files present971/// 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 {
857 var in_file = try os.File.openRead(allocator, source_path);973 var in_file = try os.File.openRead(allocator, source_path);
858 defer in_file.close();974 defer in_file.close();
859975
...@@ -879,7 +995,7 @@ pub const AtomicFile = struct {...@@ -879,7 +995,7 @@ pub const AtomicFile = struct {
879995
880 /// dest_path must remain valid for the lifetime of AtomicFile996 /// dest_path must remain valid for the lifetime of AtomicFile
881 /// call finish to atomically replace dest_path with contents997 /// 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 {
883 const dirname = os.path.dirname(dest_path);999 const dirname = os.path.dirname(dest_path);
8841000
885 var rand_buf: [12]u8 = undefined;1001 var rand_buf: [12]u8 = undefined;
...@@ -2943,3 +3059,44 @@ pub fn bsdKEvent(...@@ -2943,3 +3059,44 @@ pub fn bsdKEvent(
2943 }3059 }
2944 }3060 }
2945}3061}
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;...@@ -567,6 +567,37 @@ pub const MNT_DETACH = 2;
567pub const MNT_EXPIRE = 4;567pub const MNT_EXPIRE = 4;
568pub const UMOUNT_NOFOLLOW = 8;568pub 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
570pub const S_IFMT = 0o170000;601pub const S_IFMT = 0o170000;
571602
572pub const S_IFDIR = 0o040000;603pub const S_IFDIR = 0o040000;
...@@ -692,6 +723,10 @@ pub fn futex_wait(uaddr: usize, futex_op: u32, val: i32, timeout: ?*timespec) us...@@ -692,6 +723,10 @@ pub fn futex_wait(uaddr: usize, futex_op: u32, val: i32, timeout: ?*timespec) us
692 return syscall4(SYS_futex, uaddr, futex_op, @bitCast(u32, val), @ptrToInt(timeout));723 return syscall4(SYS_futex, uaddr, futex_op, @bitCast(u32, val), @ptrToInt(timeout));
693}724}
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
695pub fn getcwd(buf: [*]u8, size: usize) usize {730pub fn getcwd(buf: [*]u8, size: usize) usize {
696 return syscall2(SYS_getcwd, @ptrToInt(buf), size);731 return syscall2(SYS_getcwd, @ptrToInt(buf), size);
697}732}
...@@ -700,6 +735,18 @@ pub fn getdents(fd: i32, dirp: [*]u8, count: usize) usize {...@@ -700,6 +735,18 @@ pub fn getdents(fd: i32, dirp: [*]u8, count: usize) usize {
700 return syscall3(SYS_getdents, @intCast(usize, fd), @ptrToInt(dirp), count);735 return syscall3(SYS_getdents, @intCast(usize, fd), @ptrToInt(dirp), count);
701}736}
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
703pub fn isatty(fd: i32) bool {750pub fn isatty(fd: i32) bool {
704 var wsz: winsize = undefined;751 var wsz: winsize = undefined;
705 return syscall3(SYS_ioctl, @intCast(usize, fd), TIOCGWINSZ, @ptrToInt(&wsz)) == 0;752 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 {...@@ -742,6 +789,14 @@ pub fn read(fd: i32, buf: [*]u8, count: usize) usize {
742 return syscall3(SYS_read, @intCast(usize, fd), @ptrToInt(buf), count);789 return syscall3(SYS_read, @intCast(usize, fd), @ptrToInt(buf), count);
743}790}
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
745// TODO https://github.com/ziglang/zig/issues/265800// TODO https://github.com/ziglang/zig/issues/265
746pub fn rmdir(path: [*]const u8) usize {801pub fn rmdir(path: [*]const u8) usize {
747 return syscall1(SYS_rmdir, @ptrToInt(path));802 return syscall1(SYS_rmdir, @ptrToInt(path));
...@@ -1064,6 +1119,11 @@ pub const iovec = extern struct {...@@ -1064,6 +1119,11 @@ pub const iovec = extern struct {
1064 iov_len: usize,1119 iov_len: usize,
1065};1120};
10661121
1122pub const iovec_const = extern struct {
1123 iov_base: [*]const u8,
1124 iov_len: usize,
1125};
1126
1067pub fn getsockname(fd: i32, noalias addr: *sockaddr, noalias len: *socklen_t) usize {1127pub fn getsockname(fd: i32, noalias addr: *sockaddr, noalias len: *socklen_t) usize {
1068 return syscall3(SYS_getsockname, @intCast(usize, fd), @ptrToInt(addr), @ptrToInt(len));1128 return syscall3(SYS_getsockname, @intCast(usize, fd), @ptrToInt(addr), @ptrToInt(len));
1069}1129}
...@@ -1372,6 +1432,14 @@ pub fn capset(hdrp: *cap_user_header_t, datap: *const cap_user_data_t) usize {...@@ -1372,6 +1432,14 @@ pub fn capset(hdrp: *cap_user_header_t, datap: *const cap_user_data_t) usize {
1372 return syscall2(SYS_capset, @ptrToInt(hdrp), @ptrToInt(datap));1432 return syscall2(SYS_capset, @ptrToInt(hdrp), @ptrToInt(datap));
1373}1433}
13741434
1435pub const inotify_event = extern struct {
1436 wd: i32,
1437 mask: u32,
1438 cookie: u32,
1439 len: u32,
1440 //name: [?]u8,
1441};
1442
1375test "import" {1443test "import" {
1376 if (builtin.os == builtin.Os.linux) {1444 if (builtin.os == builtin.Os.linux) {
1377 _ = @import("test.zig");1445 _ = @import("test.zig");
std/os/path.zig+1-1
...@@ -506,7 +506,7 @@ pub fn resolveWindows(allocator: *Allocator, paths: []const []const u8) ![]u8 {...@@ -506,7 +506,7 @@ pub fn resolveWindows(allocator: *Allocator, paths: []const []const u8) ![]u8 {
506 result_index += 1;506 result_index += 1;
507 }507 }
508508
509 return result[0..result_index];509 return allocator.shrink(u8, result, result_index);
510}510}
511511
512/// This function is like a series of `cd` statements executed one after another.512/// 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));...@@ -67,8 +67,9 @@ pub const INVALID_FILE_ATTRIBUTES = DWORD(@maxValue(DWORD));
67pub const OVERLAPPED = extern struct {67pub const OVERLAPPED = extern struct {
68 Internal: ULONG_PTR,68 Internal: ULONG_PTR,
69 InternalHigh: ULONG_PTR,69 InternalHigh: ULONG_PTR,
70 Pointer: PVOID,70 Offset: DWORD,
71 hEvent: HANDLE,71 OffsetHigh: DWORD,
72 hEvent: ?HANDLE,
72};73};
73pub const LPOVERLAPPED = *OVERLAPPED;74pub const LPOVERLAPPED = *OVERLAPPED;
7475
...@@ -350,3 +351,15 @@ pub const E_ACCESSDENIED = @bitCast(c_long, c_ulong(0x80070005));...@@ -350,3 +351,15 @@ pub const E_ACCESSDENIED = @bitCast(c_long, c_ulong(0x80070005));
350pub const E_HANDLE = @bitCast(c_long, c_ulong(0x80070006));351pub const E_HANDLE = @bitCast(c_long, c_ulong(0x80070006));
351pub const E_OUTOFMEMORY = @bitCast(c_long, c_ulong(0x8007000E));352pub const E_OUTOFMEMORY = @bitCast(c_long, c_ulong(0x8007000E));
352pub const E_INVALIDARG = @bitCast(c_long, c_ulong(0x80070057));353pub 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 @@...@@ -1,5 +1,8 @@
1use @import("index.zig");1use @import("index.zig");
22
3
4pub extern "kernel32" stdcallcc fn CancelIoEx(hFile: HANDLE, lpOverlapped: LPOVERLAPPED) BOOL;
5
3pub extern "kernel32" stdcallcc fn CloseHandle(hObject: HANDLE) BOOL;6pub extern "kernel32" stdcallcc fn CloseHandle(hObject: HANDLE) BOOL;
47
5pub extern "kernel32" stdcallcc fn CreateDirectoryA(8pub extern "kernel32" stdcallcc fn CreateDirectoryA(
...@@ -8,7 +11,17 @@ pub extern "kernel32" stdcallcc fn CreateDirectoryA(...@@ -8,7 +11,17 @@ pub extern "kernel32" stdcallcc fn CreateDirectoryA(
8) BOOL;11) BOOL;
912
10pub extern "kernel32" stdcallcc fn CreateFileA(13pub 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
12 dwDesiredAccess: DWORD,25 dwDesiredAccess: DWORD,
13 dwShareMode: DWORD,26 dwShareMode: DWORD,
14 lpSecurityAttributes: ?LPSECURITY_ATTRIBUTES,27 lpSecurityAttributes: ?LPSECURITY_ATTRIBUTES,
...@@ -94,6 +107,9 @@ pub extern "kernel32" stdcallcc fn GetFinalPathNameByHandleA(...@@ -94,6 +107,9 @@ pub extern "kernel32" stdcallcc fn GetFinalPathNameByHandleA(
94 dwFlags: DWORD,107 dwFlags: DWORD,
95) DWORD;108) DWORD;
96109
110
111pub extern "kernel32" stdcallcc fn GetOverlappedResult(hFile: HANDLE, lpOverlapped: *OVERLAPPED, lpNumberOfBytesTransferred: *DWORD, bWait: BOOL) BOOL;
112
97pub extern "kernel32" stdcallcc fn GetProcessHeap() ?HANDLE;113pub extern "kernel32" stdcallcc fn GetProcessHeap() ?HANDLE;
98pub extern "kernel32" stdcallcc fn GetQueuedCompletionStatus(CompletionPort: HANDLE, lpNumberOfBytesTransferred: LPDWORD, lpCompletionKey: *ULONG_PTR, lpOverlapped: *?*OVERLAPPED, dwMilliseconds: DWORD) BOOL;114pub 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...@@ -104,7 +120,6 @@ pub extern "kernel32" stdcallcc fn HeapCreate(flOptions: DWORD, dwInitialSize: S
104pub extern "kernel32" stdcallcc fn HeapDestroy(hHeap: HANDLE) BOOL;120pub extern "kernel32" stdcallcc fn HeapDestroy(hHeap: HANDLE) BOOL;
105pub extern "kernel32" stdcallcc fn HeapReAlloc(hHeap: HANDLE, dwFlags: DWORD, lpMem: *c_void, dwBytes: SIZE_T) ?*c_void;121pub extern "kernel32" stdcallcc fn HeapReAlloc(hHeap: HANDLE, dwFlags: DWORD, lpMem: *c_void, dwBytes: SIZE_T) ?*c_void;
106pub extern "kernel32" stdcallcc fn HeapSize(hHeap: HANDLE, dwFlags: DWORD, lpMem: *const c_void) SIZE_T;122pub 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;
108pub extern "kernel32" stdcallcc fn HeapCompact(hHeap: HANDLE, dwFlags: DWORD) SIZE_T;123pub extern "kernel32" stdcallcc fn HeapCompact(hHeap: HANDLE, dwFlags: DWORD) SIZE_T;
109pub extern "kernel32" stdcallcc fn HeapSummary(hHeap: HANDLE, dwFlags: DWORD, lpSummary: LPHEAP_SUMMARY) BOOL;124pub 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...@@ -114,6 +129,8 @@ pub extern "kernel32" stdcallcc fn HeapAlloc(hHeap: HANDLE, dwFlags: DWORD, dwBy
114129
115pub extern "kernel32" stdcallcc fn HeapFree(hHeap: HANDLE, dwFlags: DWORD, lpMem: *c_void) BOOL;130pub 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
117pub extern "kernel32" stdcallcc fn MoveFileExA(134pub extern "kernel32" stdcallcc fn MoveFileExA(
118 lpExistingFileName: LPCSTR,135 lpExistingFileName: LPCSTR,
119 lpNewFileName: LPCSTR,136 lpNewFileName: LPCSTR,
...@@ -126,11 +143,22 @@ pub extern "kernel32" stdcallcc fn QueryPerformanceCounter(lpPerformanceCount: *...@@ -126,11 +143,22 @@ pub extern "kernel32" stdcallcc fn QueryPerformanceCounter(lpPerformanceCount: *
126143
127pub extern "kernel32" stdcallcc fn QueryPerformanceFrequency(lpFrequency: *LARGE_INTEGER) BOOL;144pub 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
129pub extern "kernel32" stdcallcc fn ReadFile(157pub extern "kernel32" stdcallcc fn ReadFile(
130 in_hFile: HANDLE,158 in_hFile: HANDLE,
131 out_lpBuffer: *c_void,159 out_lpBuffer: [*]u8,
132 in_nNumberOfBytesToRead: DWORD,160 in_nNumberOfBytesToRead: DWORD,
133 out_lpNumberOfBytesRead: *DWORD,161 out_lpNumberOfBytesRead: ?*DWORD,
134 in_out_lpOverlapped: ?*OVERLAPPED,162 in_out_lpOverlapped: ?*OVERLAPPED,
135) BOOL;163) BOOL;
136164
...@@ -153,13 +181,42 @@ pub extern "kernel32" stdcallcc fn WaitForSingleObject(hHandle: HANDLE, dwMillis...@@ -153,13 +181,42 @@ pub extern "kernel32" stdcallcc fn WaitForSingleObject(hHandle: HANDLE, dwMillis
153181
154pub extern "kernel32" stdcallcc fn WriteFile(182pub extern "kernel32" stdcallcc fn WriteFile(
155 in_hFile: HANDLE,183 in_hFile: HANDLE,
156 in_lpBuffer: *const c_void,184 in_lpBuffer: [*]const u8,
157 in_nNumberOfBytesToWrite: DWORD,185 in_nNumberOfBytesToWrite: DWORD,
158 out_lpNumberOfBytesWritten: ?*DWORD,186 out_lpNumberOfBytesWritten: ?*DWORD,
159 in_out_lpOverlapped: ?*OVERLAPPED,187 in_out_lpOverlapped: ?*OVERLAPPED,
160) BOOL;188) BOOL;
161189
190pub extern "kernel32" stdcallcc fn WriteFileEx(hFile: HANDLE, lpBuffer: [*]const u8, nNumberOfBytesToWrite: DWORD, lpOverlapped: LPOVERLAPPED, lpCompletionRoutine: LPOVERLAPPED_COMPLETION_ROUTINE) BOOL;
191
162//TODO: call unicode versions instead of relying on ANSI code page192//TODO: call unicode versions instead of relying on ANSI code page
163pub extern "kernel32" stdcallcc fn LoadLibraryA(lpLibFileName: LPCSTR) ?HMODULE;193pub extern "kernel32" stdcallcc fn LoadLibraryA(lpLibFileName: LPCSTR) ?HMODULE;
164194
165pub extern "kernel32" stdcallcc fn FreeLibrary(hModule: HMODULE) BOOL;195pub 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 {...@@ -36,20 +36,19 @@ pub fn windowsClose(handle: windows.HANDLE) void {
36pub const WriteError = error{36pub const WriteError = error{
37 SystemResources,37 SystemResources,
38 OperationAborted,38 OperationAborted,
39 IoPending,
40 BrokenPipe,39 BrokenPipe,
41 Unexpected,40 Unexpected,
42};41};
4342
44pub fn windowsWrite(handle: windows.HANDLE, bytes: []const u8) WriteError!void {43pub 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) {
46 const err = windows.GetLastError();45 const err = windows.GetLastError();
47 return switch (err) {46 return switch (err) {
48 windows.ERROR.INVALID_USER_BUFFER => WriteError.SystemResources,47 windows.ERROR.INVALID_USER_BUFFER => WriteError.SystemResources,
49 windows.ERROR.NOT_ENOUGH_MEMORY => WriteError.SystemResources,48 windows.ERROR.NOT_ENOUGH_MEMORY => WriteError.SystemResources,
50 windows.ERROR.OPERATION_ABORTED => WriteError.OperationAborted,49 windows.ERROR.OPERATION_ABORTED => WriteError.OperationAborted,
51 windows.ERROR.NOT_ENOUGH_QUOTA => WriteError.SystemResources,50 windows.ERROR.NOT_ENOUGH_QUOTA => WriteError.SystemResources,
52 windows.ERROR.IO_PENDING => WriteError.IoPending,51 windows.ERROR.IO_PENDING => unreachable,
53 windows.ERROR.BROKEN_PIPE => WriteError.BrokenPipe,52 windows.ERROR.BROKEN_PIPE => WriteError.BrokenPipe,
54 else => os.unexpectedErrorWindows(err),53 else => os.unexpectedErrorWindows(err),
55 };54 };
...@@ -221,6 +220,7 @@ pub fn windowsCreateIoCompletionPort(file_handle: windows.HANDLE, existing_compl...@@ -221,6 +220,7 @@ pub fn windowsCreateIoCompletionPort(file_handle: windows.HANDLE, existing_compl
221 const handle = windows.CreateIoCompletionPort(file_handle, existing_completion_port, completion_key, concurrent_thread_count) orelse {220 const handle = windows.CreateIoCompletionPort(file_handle, existing_completion_port, completion_key, concurrent_thread_count) orelse {
222 const err = windows.GetLastError();221 const err = windows.GetLastError();
223 switch (err) {222 switch (err) {
223 windows.ERROR.INVALID_PARAMETER => unreachable,
224 else => return os.unexpectedErrorWindows(err),224 else => return os.unexpectedErrorWindows(err),
225 }225 }
226 };226 };
...@@ -238,21 +238,24 @@ pub fn windowsPostQueuedCompletionStatus(completion_port: windows.HANDLE, bytes_...@@ -238,21 +238,24 @@ pub fn windowsPostQueuedCompletionStatus(completion_port: windows.HANDLE, bytes_
238 }238 }
239}239}
240240
241pub const WindowsWaitResult = error{241pub const WindowsWaitResult = enum{
242 Normal,242 Normal,
243 Aborted,243 Aborted,
244 Cancelled,
244};245};
245246
246pub fn windowsGetQueuedCompletionStatus(completion_port: windows.HANDLE, bytes_transferred_count: *windows.DWORD, lpCompletionKey: *usize, lpOverlapped: *?*windows.OVERLAPPED, dwMilliseconds: windows.DWORD) WindowsWaitResult {247pub fn windowsGetQueuedCompletionStatus(completion_port: windows.HANDLE, bytes_transferred_count: *windows.DWORD, lpCompletionKey: *usize, lpOverlapped: *?*windows.OVERLAPPED, dwMilliseconds: windows.DWORD) WindowsWaitResult {
247 if (windows.GetQueuedCompletionStatus(completion_port, bytes_transferred_count, lpCompletionKey, lpOverlapped, dwMilliseconds) == windows.FALSE) {248 if (windows.GetQueuedCompletionStatus(completion_port, bytes_transferred_count, lpCompletionKey, lpOverlapped, dwMilliseconds) == windows.FALSE) {
248 if (std.debug.runtime_safety) {249 const err = windows.GetLastError();
249 const err = windows.GetLastError();250 switch (err) {
250 if (err != windows.ERROR.ABANDONED_WAIT_0) {251 windows.ERROR.ABANDONED_WAIT_0 => return WindowsWaitResult.Aborted,
251 std.debug.warn("err: {}\n", err);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 }
252 }257 }
253 assert(err == windows.ERROR.ABANDONED_WAIT_0);
254 }258 }
255 return WindowsWaitResult.Aborted;
256 }259 }
257 return WindowsWaitResult.Normal;260 return WindowsWaitResult.Normal;
258}261}
std/segmented_list.zig+13-5
...@@ -2,7 +2,7 @@ const std = @import("index.zig");...@@ -2,7 +2,7 @@ const std = @import("index.zig");
2const assert = std.debug.assert;2const assert = std.debug.assert;
3const Allocator = std.mem.Allocator;3const Allocator = std.mem.Allocator;
44
5// Imagine that `fn at(self: &Self, index: usize) &T` is a customer asking for a box5// Imagine that `fn at(self: *Self, index: usize) &T` is a customer asking for a box
6// from a warehouse, based on a flat array, boxes ordered from 0 to N - 1.6// from a warehouse, based on a flat array, boxes ordered from 0 to N - 1.
7// But the warehouse actually stores boxes in shelves of increasing powers of 2 sizes.7// But the warehouse actually stores boxes in shelves of increasing powers of 2 sizes.
8// So when the customer requests a box index, we have to translate it to shelf index8// 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...@@ -93,6 +93,14 @@ pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type
9393
94 pub const prealloc_count = prealloc_item_count;94 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
96 /// Deinitialize with `deinit`104 /// Deinitialize with `deinit`
97 pub fn init(allocator: *Allocator) Self {105 pub fn init(allocator: *Allocator) Self {
98 return Self{106 return Self{
...@@ -109,7 +117,7 @@ pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type...@@ -109,7 +117,7 @@ pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type
109 self.* = undefined;117 self.* = undefined;
110 }118 }
111119
112 pub fn at(self: *Self, i: usize) *T {120 pub fn at(self: var, i: usize) AtType(@typeOf(self)) {
113 assert(i < self.len);121 assert(i < self.len);
114 return self.uncheckedAt(i);122 return self.uncheckedAt(i);
115 }123 }
...@@ -133,7 +141,7 @@ pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type...@@ -133,7 +141,7 @@ pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type
133 if (self.len == 0) return null;141 if (self.len == 0) return null;
134142
135 const index = self.len - 1;143 const index = self.len - 1;
136 const result = self.uncheckedAt(index).*;144 const result = uncheckedAt(self, index).*;
137 self.len = index;145 self.len = index;
138 return result;146 return result;
139 }147 }
...@@ -141,7 +149,7 @@ pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type...@@ -141,7 +149,7 @@ pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type
141 pub fn addOne(self: *Self) !*T {149 pub fn addOne(self: *Self) !*T {
142 const new_length = self.len + 1;150 const new_length = self.len + 1;
143 try self.growCapacity(new_length);151 try self.growCapacity(new_length);
144 const result = self.uncheckedAt(self.len);152 const result = uncheckedAt(self, self.len);
145 self.len = new_length;153 self.len = new_length;
146 return result;154 return result;
147 }155 }
...@@ -193,7 +201,7 @@ pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type...@@ -193,7 +201,7 @@ pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type
193 self.dynamic_segments = self.allocator.shrink([*]T, self.dynamic_segments, new_cap_shelf_count);201 self.dynamic_segments = self.allocator.shrink([*]T, self.dynamic_segments, new_cap_shelf_count);
194 }202 }
195203
196 pub fn uncheckedAt(self: *Self, index: usize) *T {204 pub fn uncheckedAt(self: var, index: usize) AtType(@typeOf(self)) {
197 if (index < prealloc_item_count) {205 if (index < prealloc_item_count) {
198 return &self.prealloc_segment[index];206 return &self.prealloc_segment[index];
199 }207 }
std/special/build_runner.zig+2-2
...@@ -72,10 +72,10 @@ pub fn main() !void {...@@ -72,10 +72,10 @@ pub fn main() !void {
72 if (mem.indexOfScalar(u8, option_contents, '=')) |name_end| {72 if (mem.indexOfScalar(u8, option_contents, '=')) |name_end| {
73 const option_name = option_contents[0..name_end];73 const option_name = option_contents[0..name_end];
74 const option_value = option_contents[name_end + 1 ..];74 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))
76 return usageAndErr(&builder, false, try stderr_stream);76 return usageAndErr(&builder, false, try stderr_stream);
77 } else {77 } else {
78 if (builder.addUserInputFlag(option_contents))78 if (try builder.addUserInputFlag(option_contents))
79 return usageAndErr(&builder, false, try stderr_stream);79 return usageAndErr(&builder, false, try stderr_stream);
80 }80 }
81 } else if (mem.startsWith(u8, arg, "-")) {81 } else if (mem.startsWith(u8, arg, "-")) {
std/unicode.zig+19-1
...@@ -188,6 +188,7 @@ pub const Utf8View = struct {...@@ -188,6 +188,7 @@ pub const Utf8View = struct {
188 return Utf8View{ .bytes = s };188 return Utf8View{ .bytes = s };
189 }189 }
190190
191 /// TODO: https://github.com/ziglang/zig/issues/425
191 pub fn initComptime(comptime s: []const u8) Utf8View {192 pub fn initComptime(comptime s: []const u8) Utf8View {
192 if (comptime init(s)) |r| {193 if (comptime init(s)) |r| {
193 return r;194 return r;
...@@ -199,7 +200,7 @@ pub const Utf8View = struct {...@@ -199,7 +200,7 @@ pub const Utf8View = struct {
199 }200 }
200 }201 }
201202
202 pub fn iterator(s: *const Utf8View) Utf8Iterator {203 pub fn iterator(s: Utf8View) Utf8Iterator {
203 return Utf8Iterator{204 return Utf8Iterator{
204 .bytes = s.bytes,205 .bytes = s.bytes,
205 .i = 0,206 .i = 0,
...@@ -530,3 +531,20 @@ test "utf16leToUtf8" {...@@ -530,3 +531,20 @@ test "utf16leToUtf8" {
530 assert(mem.eql(u8, utf8, "\xf4\x8f\xb0\x80"));531 assert(mem.eql(u8, utf8, "\xf4\x8f\xb0\x80"));
531 }532 }
532}533}
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 {...@@ -32,6 +32,12 @@ pub const Tree = struct {
32 return self.source[token.start..token.end];32 return self.source[token.start..token.end];
33 }33 }
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
35 pub const Location = struct {41 pub const Location = struct {
36 line: usize,42 line: usize,
37 column: usize,43 column: usize,
...@@ -338,7 +344,7 @@ pub const Node = struct {...@@ -338,7 +344,7 @@ pub const Node = struct {
338 unreachable;344 unreachable;
339 }345 }
340346
341 pub fn firstToken(base: *Node) TokenIndex {347 pub fn firstToken(base: *const Node) TokenIndex {
342 comptime var i = 0;348 comptime var i = 0;
343 inline while (i < @memberCount(Id)) : (i += 1) {349 inline while (i < @memberCount(Id)) : (i += 1) {
344 if (base.id == @field(Id, @memberName(Id, i))) {350 if (base.id == @field(Id, @memberName(Id, i))) {
...@@ -349,7 +355,7 @@ pub const Node = struct {...@@ -349,7 +355,7 @@ pub const Node = struct {
349 unreachable;355 unreachable;
350 }356 }
351357
352 pub fn lastToken(base: *Node) TokenIndex {358 pub fn lastToken(base: *const Node) TokenIndex {
353 comptime var i = 0;359 comptime var i = 0;
354 inline while (i < @memberCount(Id)) : (i += 1) {360 inline while (i < @memberCount(Id)) : (i += 1) {
355 if (base.id == @field(Id, @memberName(Id, i))) {361 if (base.id == @field(Id, @memberName(Id, i))) {
...@@ -473,11 +479,11 @@ pub const Node = struct {...@@ -473,11 +479,11 @@ pub const Node = struct {
473 return null;479 return null;
474 }480 }
475481
476 pub fn firstToken(self: *Root) TokenIndex {482 pub fn firstToken(self: *const Root) TokenIndex {
477 return if (self.decls.len == 0) self.eof_token else (self.decls.at(0).*).firstToken();483 return if (self.decls.len == 0) self.eof_token else (self.decls.at(0).*).firstToken();
478 }484 }
479485
480 pub fn lastToken(self: *Root) TokenIndex {486 pub fn lastToken(self: *const Root) TokenIndex {
481 return if (self.decls.len == 0) self.eof_token else (self.decls.at(self.decls.len - 1).*).lastToken();487 return if (self.decls.len == 0) self.eof_token else (self.decls.at(self.decls.len - 1).*).lastToken();
482 }488 }
483 };489 };
...@@ -518,7 +524,7 @@ pub const Node = struct {...@@ -518,7 +524,7 @@ pub const Node = struct {
518 return null;524 return null;
519 }525 }
520526
521 pub fn firstToken(self: *VarDecl) TokenIndex {527 pub fn firstToken(self: *const VarDecl) TokenIndex {
522 if (self.visib_token) |visib_token| return visib_token;528 if (self.visib_token) |visib_token| return visib_token;
523 if (self.comptime_token) |comptime_token| return comptime_token;529 if (self.comptime_token) |comptime_token| return comptime_token;
524 if (self.extern_export_token) |extern_export_token| return extern_export_token;530 if (self.extern_export_token) |extern_export_token| return extern_export_token;
...@@ -526,7 +532,7 @@ pub const Node = struct {...@@ -526,7 +532,7 @@ pub const Node = struct {
526 return self.mut_token;532 return self.mut_token;
527 }533 }
528534
529 pub fn lastToken(self: *VarDecl) TokenIndex {535 pub fn lastToken(self: *const VarDecl) TokenIndex {
530 return self.semicolon_token;536 return self.semicolon_token;
531 }537 }
532 };538 };
...@@ -548,12 +554,12 @@ pub const Node = struct {...@@ -548,12 +554,12 @@ pub const Node = struct {
548 return null;554 return null;
549 }555 }
550556
551 pub fn firstToken(self: *Use) TokenIndex {557 pub fn firstToken(self: *const Use) TokenIndex {
552 if (self.visib_token) |visib_token| return visib_token;558 if (self.visib_token) |visib_token| return visib_token;
553 return self.use_token;559 return self.use_token;
554 }560 }
555561
556 pub fn lastToken(self: *Use) TokenIndex {562 pub fn lastToken(self: *const Use) TokenIndex {
557 return self.semicolon_token;563 return self.semicolon_token;
558 }564 }
559 };565 };
...@@ -575,11 +581,11 @@ pub const Node = struct {...@@ -575,11 +581,11 @@ pub const Node = struct {
575 return null;581 return null;
576 }582 }
577583
578 pub fn firstToken(self: *ErrorSetDecl) TokenIndex {584 pub fn firstToken(self: *const ErrorSetDecl) TokenIndex {
579 return self.error_token;585 return self.error_token;
580 }586 }
581587
582 pub fn lastToken(self: *ErrorSetDecl) TokenIndex {588 pub fn lastToken(self: *const ErrorSetDecl) TokenIndex {
583 return self.rbrace_token;589 return self.rbrace_token;
584 }590 }
585 };591 };
...@@ -618,14 +624,14 @@ pub const Node = struct {...@@ -618,14 +624,14 @@ pub const Node = struct {
618 return null;624 return null;
619 }625 }
620626
621 pub fn firstToken(self: *ContainerDecl) TokenIndex {627 pub fn firstToken(self: *const ContainerDecl) TokenIndex {
622 if (self.layout_token) |layout_token| {628 if (self.layout_token) |layout_token| {
623 return layout_token;629 return layout_token;
624 }630 }
625 return self.kind_token;631 return self.kind_token;
626 }632 }
627633
628 pub fn lastToken(self: *ContainerDecl) TokenIndex {634 pub fn lastToken(self: *const ContainerDecl) TokenIndex {
629 return self.rbrace_token;635 return self.rbrace_token;
630 }636 }
631 };637 };
...@@ -646,12 +652,12 @@ pub const Node = struct {...@@ -646,12 +652,12 @@ pub const Node = struct {
646 return null;652 return null;
647 }653 }
648654
649 pub fn firstToken(self: *StructField) TokenIndex {655 pub fn firstToken(self: *const StructField) TokenIndex {
650 if (self.visib_token) |visib_token| return visib_token;656 if (self.visib_token) |visib_token| return visib_token;
651 return self.name_token;657 return self.name_token;
652 }658 }
653659
654 pub fn lastToken(self: *StructField) TokenIndex {660 pub fn lastToken(self: *const StructField) TokenIndex {
655 return self.type_expr.lastToken();661 return self.type_expr.lastToken();
656 }662 }
657 };663 };
...@@ -679,11 +685,11 @@ pub const Node = struct {...@@ -679,11 +685,11 @@ pub const Node = struct {
679 return null;685 return null;
680 }686 }
681687
682 pub fn firstToken(self: *UnionTag) TokenIndex {688 pub fn firstToken(self: *const UnionTag) TokenIndex {
683 return self.name_token;689 return self.name_token;
684 }690 }
685691
686 pub fn lastToken(self: *UnionTag) TokenIndex {692 pub fn lastToken(self: *const UnionTag) TokenIndex {
687 if (self.value_expr) |value_expr| {693 if (self.value_expr) |value_expr| {
688 return value_expr.lastToken();694 return value_expr.lastToken();
689 }695 }
...@@ -712,11 +718,11 @@ pub const Node = struct {...@@ -712,11 +718,11 @@ pub const Node = struct {
712 return null;718 return null;
713 }719 }
714720
715 pub fn firstToken(self: *EnumTag) TokenIndex {721 pub fn firstToken(self: *const EnumTag) TokenIndex {
716 return self.name_token;722 return self.name_token;
717 }723 }
718724
719 pub fn lastToken(self: *EnumTag) TokenIndex {725 pub fn lastToken(self: *const EnumTag) TokenIndex {
720 if (self.value) |value| {726 if (self.value) |value| {
721 return value.lastToken();727 return value.lastToken();
722 }728 }
...@@ -741,11 +747,11 @@ pub const Node = struct {...@@ -741,11 +747,11 @@ pub const Node = struct {
741 return null;747 return null;
742 }748 }
743749
744 pub fn firstToken(self: *ErrorTag) TokenIndex {750 pub fn firstToken(self: *const ErrorTag) TokenIndex {
745 return self.name_token;751 return self.name_token;
746 }752 }
747753
748 pub fn lastToken(self: *ErrorTag) TokenIndex {754 pub fn lastToken(self: *const ErrorTag) TokenIndex {
749 return self.name_token;755 return self.name_token;
750 }756 }
751 };757 };
...@@ -758,11 +764,11 @@ pub const Node = struct {...@@ -758,11 +764,11 @@ pub const Node = struct {
758 return null;764 return null;
759 }765 }
760766
761 pub fn firstToken(self: *Identifier) TokenIndex {767 pub fn firstToken(self: *const Identifier) TokenIndex {
762 return self.token;768 return self.token;
763 }769 }
764770
765 pub fn lastToken(self: *Identifier) TokenIndex {771 pub fn lastToken(self: *const Identifier) TokenIndex {
766 return self.token;772 return self.token;
767 }773 }
768 };774 };
...@@ -784,11 +790,11 @@ pub const Node = struct {...@@ -784,11 +790,11 @@ pub const Node = struct {
784 return null;790 return null;
785 }791 }
786792
787 pub fn firstToken(self: *AsyncAttribute) TokenIndex {793 pub fn firstToken(self: *const AsyncAttribute) TokenIndex {
788 return self.async_token;794 return self.async_token;
789 }795 }
790796
791 pub fn lastToken(self: *AsyncAttribute) TokenIndex {797 pub fn lastToken(self: *const AsyncAttribute) TokenIndex {
792 if (self.rangle_bracket) |rangle_bracket| {798 if (self.rangle_bracket) |rangle_bracket| {
793 return rangle_bracket;799 return rangle_bracket;
794 }800 }
...@@ -856,7 +862,7 @@ pub const Node = struct {...@@ -856,7 +862,7 @@ pub const Node = struct {
856 return null;862 return null;
857 }863 }
858864
859 pub fn firstToken(self: *FnProto) TokenIndex {865 pub fn firstToken(self: *const FnProto) TokenIndex {
860 if (self.visib_token) |visib_token| return visib_token;866 if (self.visib_token) |visib_token| return visib_token;
861 if (self.async_attr) |async_attr| return async_attr.firstToken();867 if (self.async_attr) |async_attr| return async_attr.firstToken();
862 if (self.extern_export_inline_token) |extern_export_inline_token| return extern_export_inline_token;868 if (self.extern_export_inline_token) |extern_export_inline_token| return extern_export_inline_token;
...@@ -865,7 +871,7 @@ pub const Node = struct {...@@ -865,7 +871,7 @@ pub const Node = struct {
865 return self.fn_token;871 return self.fn_token;
866 }872 }
867873
868 pub fn lastToken(self: *FnProto) TokenIndex {874 pub fn lastToken(self: *const FnProto) TokenIndex {
869 if (self.body_node) |body_node| return body_node.lastToken();875 if (self.body_node) |body_node| return body_node.lastToken();
870 switch (self.return_type) {876 switch (self.return_type) {
871 // TODO allow this and next prong to share bodies since the types are the same877 // TODO allow this and next prong to share bodies since the types are the same
...@@ -896,11 +902,11 @@ pub const Node = struct {...@@ -896,11 +902,11 @@ pub const Node = struct {
896 return null;902 return null;
897 }903 }
898904
899 pub fn firstToken(self: *PromiseType) TokenIndex {905 pub fn firstToken(self: *const PromiseType) TokenIndex {
900 return self.promise_token;906 return self.promise_token;
901 }907 }
902908
903 pub fn lastToken(self: *PromiseType) TokenIndex {909 pub fn lastToken(self: *const PromiseType) TokenIndex {
904 if (self.result) |result| return result.return_type.lastToken();910 if (self.result) |result| return result.return_type.lastToken();
905 return self.promise_token;911 return self.promise_token;
906 }912 }
...@@ -923,14 +929,14 @@ pub const Node = struct {...@@ -923,14 +929,14 @@ pub const Node = struct {
923 return null;929 return null;
924 }930 }
925931
926 pub fn firstToken(self: *ParamDecl) TokenIndex {932 pub fn firstToken(self: *const ParamDecl) TokenIndex {
927 if (self.comptime_token) |comptime_token| return comptime_token;933 if (self.comptime_token) |comptime_token| return comptime_token;
928 if (self.noalias_token) |noalias_token| return noalias_token;934 if (self.noalias_token) |noalias_token| return noalias_token;
929 if (self.name_token) |name_token| return name_token;935 if (self.name_token) |name_token| return name_token;
930 return self.type_node.firstToken();936 return self.type_node.firstToken();
931 }937 }
932938
933 pub fn lastToken(self: *ParamDecl) TokenIndex {939 pub fn lastToken(self: *const ParamDecl) TokenIndex {
934 if (self.var_args_token) |var_args_token| return var_args_token;940 if (self.var_args_token) |var_args_token| return var_args_token;
935 return self.type_node.lastToken();941 return self.type_node.lastToken();
936 }942 }
...@@ -954,7 +960,7 @@ pub const Node = struct {...@@ -954,7 +960,7 @@ pub const Node = struct {
954 return null;960 return null;
955 }961 }
956962
957 pub fn firstToken(self: *Block) TokenIndex {963 pub fn firstToken(self: *const Block) TokenIndex {
958 if (self.label) |label| {964 if (self.label) |label| {
959 return label;965 return label;
960 }966 }
...@@ -962,7 +968,7 @@ pub const Node = struct {...@@ -962,7 +968,7 @@ pub const Node = struct {
962 return self.lbrace;968 return self.lbrace;
963 }969 }
964970
965 pub fn lastToken(self: *Block) TokenIndex {971 pub fn lastToken(self: *const Block) TokenIndex {
966 return self.rbrace;972 return self.rbrace;
967 }973 }
968 };974 };
...@@ -981,11 +987,11 @@ pub const Node = struct {...@@ -981,11 +987,11 @@ pub const Node = struct {
981 return null;987 return null;
982 }988 }
983989
984 pub fn firstToken(self: *Defer) TokenIndex {990 pub fn firstToken(self: *const Defer) TokenIndex {
985 return self.defer_token;991 return self.defer_token;
986 }992 }
987993
988 pub fn lastToken(self: *Defer) TokenIndex {994 pub fn lastToken(self: *const Defer) TokenIndex {
989 return self.expr.lastToken();995 return self.expr.lastToken();
990 }996 }
991 };997 };
...@@ -1005,11 +1011,11 @@ pub const Node = struct {...@@ -1005,11 +1011,11 @@ pub const Node = struct {
1005 return null;1011 return null;
1006 }1012 }
10071013
1008 pub fn firstToken(self: *Comptime) TokenIndex {1014 pub fn firstToken(self: *const Comptime) TokenIndex {
1009 return self.comptime_token;1015 return self.comptime_token;
1010 }1016 }
10111017
1012 pub fn lastToken(self: *Comptime) TokenIndex {1018 pub fn lastToken(self: *const Comptime) TokenIndex {
1013 return self.expr.lastToken();1019 return self.expr.lastToken();
1014 }1020 }
1015 };1021 };
...@@ -1029,11 +1035,11 @@ pub const Node = struct {...@@ -1029,11 +1035,11 @@ pub const Node = struct {
1029 return null;1035 return null;
1030 }1036 }
10311037
1032 pub fn firstToken(self: *Payload) TokenIndex {1038 pub fn firstToken(self: *const Payload) TokenIndex {
1033 return self.lpipe;1039 return self.lpipe;
1034 }1040 }
10351041
1036 pub fn lastToken(self: *Payload) TokenIndex {1042 pub fn lastToken(self: *const Payload) TokenIndex {
1037 return self.rpipe;1043 return self.rpipe;
1038 }1044 }
1039 };1045 };
...@@ -1054,11 +1060,11 @@ pub const Node = struct {...@@ -1054,11 +1060,11 @@ pub const Node = struct {
1054 return null;1060 return null;
1055 }1061 }
10561062
1057 pub fn firstToken(self: *PointerPayload) TokenIndex {1063 pub fn firstToken(self: *const PointerPayload) TokenIndex {
1058 return self.lpipe;1064 return self.lpipe;
1059 }1065 }
10601066
1061 pub fn lastToken(self: *PointerPayload) TokenIndex {1067 pub fn lastToken(self: *const PointerPayload) TokenIndex {
1062 return self.rpipe;1068 return self.rpipe;
1063 }1069 }
1064 };1070 };
...@@ -1085,11 +1091,11 @@ pub const Node = struct {...@@ -1085,11 +1091,11 @@ pub const Node = struct {
1085 return null;1091 return null;
1086 }1092 }
10871093
1088 pub fn firstToken(self: *PointerIndexPayload) TokenIndex {1094 pub fn firstToken(self: *const PointerIndexPayload) TokenIndex {
1089 return self.lpipe;1095 return self.lpipe;
1090 }1096 }
10911097
1092 pub fn lastToken(self: *PointerIndexPayload) TokenIndex {1098 pub fn lastToken(self: *const PointerIndexPayload) TokenIndex {
1093 return self.rpipe;1099 return self.rpipe;
1094 }1100 }
1095 };1101 };
...@@ -1114,11 +1120,11 @@ pub const Node = struct {...@@ -1114,11 +1120,11 @@ pub const Node = struct {
1114 return null;1120 return null;
1115 }1121 }
11161122
1117 pub fn firstToken(self: *Else) TokenIndex {1123 pub fn firstToken(self: *const Else) TokenIndex {
1118 return self.else_token;1124 return self.else_token;
1119 }1125 }
11201126
1121 pub fn lastToken(self: *Else) TokenIndex {1127 pub fn lastToken(self: *const Else) TokenIndex {
1122 return self.body.lastToken();1128 return self.body.lastToken();
1123 }1129 }
1124 };1130 };
...@@ -1146,11 +1152,11 @@ pub const Node = struct {...@@ -1146,11 +1152,11 @@ pub const Node = struct {
1146 return null;1152 return null;
1147 }1153 }
11481154
1149 pub fn firstToken(self: *Switch) TokenIndex {1155 pub fn firstToken(self: *const Switch) TokenIndex {
1150 return self.switch_token;1156 return self.switch_token;
1151 }1157 }
11521158
1153 pub fn lastToken(self: *Switch) TokenIndex {1159 pub fn lastToken(self: *const Switch) TokenIndex {
1154 return self.rbrace;1160 return self.rbrace;
1155 }1161 }
1156 };1162 };
...@@ -1181,11 +1187,11 @@ pub const Node = struct {...@@ -1181,11 +1187,11 @@ pub const Node = struct {
1181 return null;1187 return null;
1182 }1188 }
11831189
1184 pub fn firstToken(self: *SwitchCase) TokenIndex {1190 pub fn firstToken(self: *const SwitchCase) TokenIndex {
1185 return (self.items.at(0).*).firstToken();1191 return (self.items.at(0).*).firstToken();
1186 }1192 }
11871193
1188 pub fn lastToken(self: *SwitchCase) TokenIndex {1194 pub fn lastToken(self: *const SwitchCase) TokenIndex {
1189 return self.expr.lastToken();1195 return self.expr.lastToken();
1190 }1196 }
1191 };1197 };
...@@ -1198,11 +1204,11 @@ pub const Node = struct {...@@ -1198,11 +1204,11 @@ pub const Node = struct {
1198 return null;1204 return null;
1199 }1205 }
12001206
1201 pub fn firstToken(self: *SwitchElse) TokenIndex {1207 pub fn firstToken(self: *const SwitchElse) TokenIndex {
1202 return self.token;1208 return self.token;
1203 }1209 }
12041210
1205 pub fn lastToken(self: *SwitchElse) TokenIndex {1211 pub fn lastToken(self: *const SwitchElse) TokenIndex {
1206 return self.token;1212 return self.token;
1207 }1213 }
1208 };1214 };
...@@ -1245,7 +1251,7 @@ pub const Node = struct {...@@ -1245,7 +1251,7 @@ pub const Node = struct {
1245 return null;1251 return null;
1246 }1252 }
12471253
1248 pub fn firstToken(self: *While) TokenIndex {1254 pub fn firstToken(self: *const While) TokenIndex {
1249 if (self.label) |label| {1255 if (self.label) |label| {
1250 return label;1256 return label;
1251 }1257 }
...@@ -1257,7 +1263,7 @@ pub const Node = struct {...@@ -1257,7 +1263,7 @@ pub const Node = struct {
1257 return self.while_token;1263 return self.while_token;
1258 }1264 }
12591265
1260 pub fn lastToken(self: *While) TokenIndex {1266 pub fn lastToken(self: *const While) TokenIndex {
1261 if (self.@"else") |@"else"| {1267 if (self.@"else") |@"else"| {
1262 return @"else".body.lastToken();1268 return @"else".body.lastToken();
1263 }1269 }
...@@ -1298,7 +1304,7 @@ pub const Node = struct {...@@ -1298,7 +1304,7 @@ pub const Node = struct {
1298 return null;1304 return null;
1299 }1305 }
13001306
1301 pub fn firstToken(self: *For) TokenIndex {1307 pub fn firstToken(self: *const For) TokenIndex {
1302 if (self.label) |label| {1308 if (self.label) |label| {
1303 return label;1309 return label;
1304 }1310 }
...@@ -1310,7 +1316,7 @@ pub const Node = struct {...@@ -1310,7 +1316,7 @@ pub const Node = struct {
1310 return self.for_token;1316 return self.for_token;
1311 }1317 }
13121318
1313 pub fn lastToken(self: *For) TokenIndex {1319 pub fn lastToken(self: *const For) TokenIndex {
1314 if (self.@"else") |@"else"| {1320 if (self.@"else") |@"else"| {
1315 return @"else".body.lastToken();1321 return @"else".body.lastToken();
1316 }1322 }
...@@ -1349,11 +1355,11 @@ pub const Node = struct {...@@ -1349,11 +1355,11 @@ pub const Node = struct {
1349 return null;1355 return null;
1350 }1356 }
13511357
1352 pub fn firstToken(self: *If) TokenIndex {1358 pub fn firstToken(self: *const If) TokenIndex {
1353 return self.if_token;1359 return self.if_token;
1354 }1360 }
13551361
1356 pub fn lastToken(self: *If) TokenIndex {1362 pub fn lastToken(self: *const If) TokenIndex {
1357 if (self.@"else") |@"else"| {1363 if (self.@"else") |@"else"| {
1358 return @"else".body.lastToken();1364 return @"else".body.lastToken();
1359 }1365 }
...@@ -1480,11 +1486,11 @@ pub const Node = struct {...@@ -1480,11 +1486,11 @@ pub const Node = struct {
1480 return null;1486 return null;
1481 }1487 }
14821488
1483 pub fn firstToken(self: *InfixOp) TokenIndex {1489 pub fn firstToken(self: *const InfixOp) TokenIndex {
1484 return self.lhs.firstToken();1490 return self.lhs.firstToken();
1485 }1491 }
14861492
1487 pub fn lastToken(self: *InfixOp) TokenIndex {1493 pub fn lastToken(self: *const InfixOp) TokenIndex {
1488 return self.rhs.lastToken();1494 return self.rhs.lastToken();
1489 }1495 }
1490 };1496 };
...@@ -1570,11 +1576,11 @@ pub const Node = struct {...@@ -1570,11 +1576,11 @@ pub const Node = struct {
1570 return null;1576 return null;
1571 }1577 }
15721578
1573 pub fn firstToken(self: *PrefixOp) TokenIndex {1579 pub fn firstToken(self: *const PrefixOp) TokenIndex {
1574 return self.op_token;1580 return self.op_token;
1575 }1581 }
15761582
1577 pub fn lastToken(self: *PrefixOp) TokenIndex {1583 pub fn lastToken(self: *const PrefixOp) TokenIndex {
1578 return self.rhs.lastToken();1584 return self.rhs.lastToken();
1579 }1585 }
1580 };1586 };
...@@ -1594,11 +1600,11 @@ pub const Node = struct {...@@ -1594,11 +1600,11 @@ pub const Node = struct {
1594 return null;1600 return null;
1595 }1601 }
15961602
1597 pub fn firstToken(self: *FieldInitializer) TokenIndex {1603 pub fn firstToken(self: *const FieldInitializer) TokenIndex {
1598 return self.period_token;1604 return self.period_token;
1599 }1605 }
16001606
1601 pub fn lastToken(self: *FieldInitializer) TokenIndex {1607 pub fn lastToken(self: *const FieldInitializer) TokenIndex {
1602 return self.expr.lastToken();1608 return self.expr.lastToken();
1603 }1609 }
1604 };1610 };
...@@ -1673,7 +1679,7 @@ pub const Node = struct {...@@ -1673,7 +1679,7 @@ pub const Node = struct {
1673 return null;1679 return null;
1674 }1680 }
16751681
1676 pub fn firstToken(self: *SuffixOp) TokenIndex {1682 pub fn firstToken(self: *const SuffixOp) TokenIndex {
1677 switch (self.op) {1683 switch (self.op) {
1678 @TagType(Op).Call => |*call_info| if (call_info.async_attr) |async_attr| return async_attr.firstToken(),1684 @TagType(Op).Call => |*call_info| if (call_info.async_attr) |async_attr| return async_attr.firstToken(),
1679 else => {},1685 else => {},
...@@ -1681,7 +1687,7 @@ pub const Node = struct {...@@ -1681,7 +1687,7 @@ pub const Node = struct {
1681 return self.lhs.firstToken();1687 return self.lhs.firstToken();
1682 }1688 }
16831689
1684 pub fn lastToken(self: *SuffixOp) TokenIndex {1690 pub fn lastToken(self: *const SuffixOp) TokenIndex {
1685 return self.rtoken;1691 return self.rtoken;
1686 }1692 }
1687 };1693 };
...@@ -1701,11 +1707,11 @@ pub const Node = struct {...@@ -1701,11 +1707,11 @@ pub const Node = struct {
1701 return null;1707 return null;
1702 }1708 }
17031709
1704 pub fn firstToken(self: *GroupedExpression) TokenIndex {1710 pub fn firstToken(self: *const GroupedExpression) TokenIndex {
1705 return self.lparen;1711 return self.lparen;
1706 }1712 }
17071713
1708 pub fn lastToken(self: *GroupedExpression) TokenIndex {1714 pub fn lastToken(self: *const GroupedExpression) TokenIndex {
1709 return self.rparen;1715 return self.rparen;
1710 }1716 }
1711 };1717 };
...@@ -1749,11 +1755,11 @@ pub const Node = struct {...@@ -1749,11 +1755,11 @@ pub const Node = struct {
1749 return null;1755 return null;
1750 }1756 }
17511757
1752 pub fn firstToken(self: *ControlFlowExpression) TokenIndex {1758 pub fn firstToken(self: *const ControlFlowExpression) TokenIndex {
1753 return self.ltoken;1759 return self.ltoken;
1754 }1760 }
17551761
1756 pub fn lastToken(self: *ControlFlowExpression) TokenIndex {1762 pub fn lastToken(self: *const ControlFlowExpression) TokenIndex {
1757 if (self.rhs) |rhs| {1763 if (self.rhs) |rhs| {
1758 return rhs.lastToken();1764 return rhs.lastToken();
1759 }1765 }
...@@ -1792,11 +1798,11 @@ pub const Node = struct {...@@ -1792,11 +1798,11 @@ pub const Node = struct {
1792 return null;1798 return null;
1793 }1799 }
17941800
1795 pub fn firstToken(self: *Suspend) TokenIndex {1801 pub fn firstToken(self: *const Suspend) TokenIndex {
1796 return self.suspend_token;1802 return self.suspend_token;
1797 }1803 }
17981804
1799 pub fn lastToken(self: *Suspend) TokenIndex {1805 pub fn lastToken(self: *const Suspend) TokenIndex {
1800 if (self.body) |body| {1806 if (self.body) |body| {
1801 return body.lastToken();1807 return body.lastToken();
1802 }1808 }
...@@ -1813,11 +1819,11 @@ pub const Node = struct {...@@ -1813,11 +1819,11 @@ pub const Node = struct {
1813 return null;1819 return null;
1814 }1820 }
18151821
1816 pub fn firstToken(self: *IntegerLiteral) TokenIndex {1822 pub fn firstToken(self: *const IntegerLiteral) TokenIndex {
1817 return self.token;1823 return self.token;
1818 }1824 }
18191825
1820 pub fn lastToken(self: *IntegerLiteral) TokenIndex {1826 pub fn lastToken(self: *const IntegerLiteral) TokenIndex {
1821 return self.token;1827 return self.token;
1822 }1828 }
1823 };1829 };
...@@ -1830,11 +1836,11 @@ pub const Node = struct {...@@ -1830,11 +1836,11 @@ pub const Node = struct {
1830 return null;1836 return null;
1831 }1837 }
18321838
1833 pub fn firstToken(self: *FloatLiteral) TokenIndex {1839 pub fn firstToken(self: *const FloatLiteral) TokenIndex {
1834 return self.token;1840 return self.token;
1835 }1841 }
18361842
1837 pub fn lastToken(self: *FloatLiteral) TokenIndex {1843 pub fn lastToken(self: *const FloatLiteral) TokenIndex {
1838 return self.token;1844 return self.token;
1839 }1845 }
1840 };1846 };
...@@ -1856,11 +1862,11 @@ pub const Node = struct {...@@ -1856,11 +1862,11 @@ pub const Node = struct {
1856 return null;1862 return null;
1857 }1863 }
18581864
1859 pub fn firstToken(self: *BuiltinCall) TokenIndex {1865 pub fn firstToken(self: *const BuiltinCall) TokenIndex {
1860 return self.builtin_token;1866 return self.builtin_token;
1861 }1867 }
18621868
1863 pub fn lastToken(self: *BuiltinCall) TokenIndex {1869 pub fn lastToken(self: *const BuiltinCall) TokenIndex {
1864 return self.rparen_token;1870 return self.rparen_token;
1865 }1871 }
1866 };1872 };
...@@ -1873,11 +1879,11 @@ pub const Node = struct {...@@ -1873,11 +1879,11 @@ pub const Node = struct {
1873 return null;1879 return null;
1874 }1880 }
18751881
1876 pub fn firstToken(self: *StringLiteral) TokenIndex {1882 pub fn firstToken(self: *const StringLiteral) TokenIndex {
1877 return self.token;1883 return self.token;
1878 }1884 }
18791885
1880 pub fn lastToken(self: *StringLiteral) TokenIndex {1886 pub fn lastToken(self: *const StringLiteral) TokenIndex {
1881 return self.token;1887 return self.token;
1882 }1888 }
1883 };1889 };
...@@ -1892,11 +1898,11 @@ pub const Node = struct {...@@ -1892,11 +1898,11 @@ pub const Node = struct {
1892 return null;1898 return null;
1893 }1899 }
18941900
1895 pub fn firstToken(self: *MultilineStringLiteral) TokenIndex {1901 pub fn firstToken(self: *const MultilineStringLiteral) TokenIndex {
1896 return self.lines.at(0).*;1902 return self.lines.at(0).*;
1897 }1903 }
18981904
1899 pub fn lastToken(self: *MultilineStringLiteral) TokenIndex {1905 pub fn lastToken(self: *const MultilineStringLiteral) TokenIndex {
1900 return self.lines.at(self.lines.len - 1).*;1906 return self.lines.at(self.lines.len - 1).*;
1901 }1907 }
1902 };1908 };
...@@ -1909,11 +1915,11 @@ pub const Node = struct {...@@ -1909,11 +1915,11 @@ pub const Node = struct {
1909 return null;1915 return null;
1910 }1916 }
19111917
1912 pub fn firstToken(self: *CharLiteral) TokenIndex {1918 pub fn firstToken(self: *const CharLiteral) TokenIndex {
1913 return self.token;1919 return self.token;
1914 }1920 }
19151921
1916 pub fn lastToken(self: *CharLiteral) TokenIndex {1922 pub fn lastToken(self: *const CharLiteral) TokenIndex {
1917 return self.token;1923 return self.token;
1918 }1924 }
1919 };1925 };
...@@ -1926,11 +1932,11 @@ pub const Node = struct {...@@ -1926,11 +1932,11 @@ pub const Node = struct {
1926 return null;1932 return null;
1927 }1933 }
19281934
1929 pub fn firstToken(self: *BoolLiteral) TokenIndex {1935 pub fn firstToken(self: *const BoolLiteral) TokenIndex {
1930 return self.token;1936 return self.token;
1931 }1937 }
19321938
1933 pub fn lastToken(self: *BoolLiteral) TokenIndex {1939 pub fn lastToken(self: *const BoolLiteral) TokenIndex {
1934 return self.token;1940 return self.token;
1935 }1941 }
1936 };1942 };
...@@ -1943,11 +1949,11 @@ pub const Node = struct {...@@ -1943,11 +1949,11 @@ pub const Node = struct {
1943 return null;1949 return null;
1944 }1950 }
19451951
1946 pub fn firstToken(self: *NullLiteral) TokenIndex {1952 pub fn firstToken(self: *const NullLiteral) TokenIndex {
1947 return self.token;1953 return self.token;
1948 }1954 }
19491955
1950 pub fn lastToken(self: *NullLiteral) TokenIndex {1956 pub fn lastToken(self: *const NullLiteral) TokenIndex {
1951 return self.token;1957 return self.token;
1952 }1958 }
1953 };1959 };
...@@ -1960,11 +1966,11 @@ pub const Node = struct {...@@ -1960,11 +1966,11 @@ pub const Node = struct {
1960 return null;1966 return null;
1961 }1967 }
19621968
1963 pub fn firstToken(self: *UndefinedLiteral) TokenIndex {1969 pub fn firstToken(self: *const UndefinedLiteral) TokenIndex {
1964 return self.token;1970 return self.token;
1965 }1971 }
19661972
1967 pub fn lastToken(self: *UndefinedLiteral) TokenIndex {1973 pub fn lastToken(self: *const UndefinedLiteral) TokenIndex {
1968 return self.token;1974 return self.token;
1969 }1975 }
1970 };1976 };
...@@ -1977,11 +1983,11 @@ pub const Node = struct {...@@ -1977,11 +1983,11 @@ pub const Node = struct {
1977 return null;1983 return null;
1978 }1984 }
19791985
1980 pub fn firstToken(self: *ThisLiteral) TokenIndex {1986 pub fn firstToken(self: *const ThisLiteral) TokenIndex {
1981 return self.token;1987 return self.token;
1982 }1988 }
19831989
1984 pub fn lastToken(self: *ThisLiteral) TokenIndex {1990 pub fn lastToken(self: *const ThisLiteral) TokenIndex {
1985 return self.token;1991 return self.token;
1986 }1992 }
1987 };1993 };
...@@ -2022,11 +2028,11 @@ pub const Node = struct {...@@ -2022,11 +2028,11 @@ pub const Node = struct {
2022 return null;2028 return null;
2023 }2029 }
20242030
2025 pub fn firstToken(self: *AsmOutput) TokenIndex {2031 pub fn firstToken(self: *const AsmOutput) TokenIndex {
2026 return self.lbracket;2032 return self.lbracket;
2027 }2033 }
20282034
2029 pub fn lastToken(self: *AsmOutput) TokenIndex {2035 pub fn lastToken(self: *const AsmOutput) TokenIndex {
2030 return self.rparen;2036 return self.rparen;
2031 }2037 }
2032 };2038 };
...@@ -2054,11 +2060,11 @@ pub const Node = struct {...@@ -2054,11 +2060,11 @@ pub const Node = struct {
2054 return null;2060 return null;
2055 }2061 }
20562062
2057 pub fn firstToken(self: *AsmInput) TokenIndex {2063 pub fn firstToken(self: *const AsmInput) TokenIndex {
2058 return self.lbracket;2064 return self.lbracket;
2059 }2065 }
20602066
2061 pub fn lastToken(self: *AsmInput) TokenIndex {2067 pub fn lastToken(self: *const AsmInput) TokenIndex {
2062 return self.rparen;2068 return self.rparen;
2063 }2069 }
2064 };2070 };
...@@ -2089,11 +2095,11 @@ pub const Node = struct {...@@ -2089,11 +2095,11 @@ pub const Node = struct {
2089 return null;2095 return null;
2090 }2096 }
20912097
2092 pub fn firstToken(self: *Asm) TokenIndex {2098 pub fn firstToken(self: *const Asm) TokenIndex {
2093 return self.asm_token;2099 return self.asm_token;
2094 }2100 }
20952101
2096 pub fn lastToken(self: *Asm) TokenIndex {2102 pub fn lastToken(self: *const Asm) TokenIndex {
2097 return self.rparen;2103 return self.rparen;
2098 }2104 }
2099 };2105 };
...@@ -2106,11 +2112,11 @@ pub const Node = struct {...@@ -2106,11 +2112,11 @@ pub const Node = struct {
2106 return null;2112 return null;
2107 }2113 }
21082114
2109 pub fn firstToken(self: *Unreachable) TokenIndex {2115 pub fn firstToken(self: *const Unreachable) TokenIndex {
2110 return self.token;2116 return self.token;
2111 }2117 }
21122118
2113 pub fn lastToken(self: *Unreachable) TokenIndex {2119 pub fn lastToken(self: *const Unreachable) TokenIndex {
2114 return self.token;2120 return self.token;
2115 }2121 }
2116 };2122 };
...@@ -2123,11 +2129,11 @@ pub const Node = struct {...@@ -2123,11 +2129,11 @@ pub const Node = struct {
2123 return null;2129 return null;
2124 }2130 }
21252131
2126 pub fn firstToken(self: *ErrorType) TokenIndex {2132 pub fn firstToken(self: *const ErrorType) TokenIndex {
2127 return self.token;2133 return self.token;
2128 }2134 }
21292135
2130 pub fn lastToken(self: *ErrorType) TokenIndex {2136 pub fn lastToken(self: *const ErrorType) TokenIndex {
2131 return self.token;2137 return self.token;
2132 }2138 }
2133 };2139 };
...@@ -2140,11 +2146,11 @@ pub const Node = struct {...@@ -2140,11 +2146,11 @@ pub const Node = struct {
2140 return null;2146 return null;
2141 }2147 }
21422148
2143 pub fn firstToken(self: *VarType) TokenIndex {2149 pub fn firstToken(self: *const VarType) TokenIndex {
2144 return self.token;2150 return self.token;
2145 }2151 }
21462152
2147 pub fn lastToken(self: *VarType) TokenIndex {2153 pub fn lastToken(self: *const VarType) TokenIndex {
2148 return self.token;2154 return self.token;
2149 }2155 }
2150 };2156 };
...@@ -2159,11 +2165,11 @@ pub const Node = struct {...@@ -2159,11 +2165,11 @@ pub const Node = struct {
2159 return null;2165 return null;
2160 }2166 }
21612167
2162 pub fn firstToken(self: *DocComment) TokenIndex {2168 pub fn firstToken(self: *const DocComment) TokenIndex {
2163 return self.lines.at(0).*;2169 return self.lines.at(0).*;
2164 }2170 }
21652171
2166 pub fn lastToken(self: *DocComment) TokenIndex {2172 pub fn lastToken(self: *const DocComment) TokenIndex {
2167 return self.lines.at(self.lines.len - 1).*;2173 return self.lines.at(self.lines.len - 1).*;
2168 }2174 }
2169 };2175 };
...@@ -2184,11 +2190,11 @@ pub const Node = struct {...@@ -2184,11 +2190,11 @@ pub const Node = struct {
2184 return null;2190 return null;
2185 }2191 }
21862192
2187 pub fn firstToken(self: *TestDecl) TokenIndex {2193 pub fn firstToken(self: *const TestDecl) TokenIndex {
2188 return self.test_token;2194 return self.test_token;
2189 }2195 }
21902196
2191 pub fn lastToken(self: *TestDecl) TokenIndex {2197 pub fn lastToken(self: *const TestDecl) TokenIndex {
2192 return self.body_node.lastToken();2198 return self.body_node.lastToken();
2193 }2199 }
2194 };2200 };