| author | |
| committer | |
| log | c4b9466da7592b95246909908619b68db5389ceb |
| tree | 6c55cc3ecb3e289fe2c13e346b70560fceaafbef |
| parent | d927f347de1f5a19545fc235f8779c2326409543 |
| parent | 598e80957e6eccc13ade72ce2693dcd60934763d |
| signature |
introduce std.event.fs for async file system functions46 files changed, 4041 insertions(+), 1047 deletions(-)
CMakeLists.txt+4| ... | ... | @@ -460,11 +460,14 @@ set(ZIG_STD_FILES |
| 460 | 460 | "empty.zig" |
| 461 | 461 | "event.zig" |
| 462 | 462 | "event/channel.zig" |
| 463 | "event/fs.zig" | |
| 463 | 464 | "event/future.zig" |
| 464 | 465 | "event/group.zig" |
| 465 | 466 | "event/lock.zig" |
| 466 | 467 | "event/locked.zig" |
| 467 | 468 | "event/loop.zig" |
| 469 | "event/rwlock.zig" | |
| 470 | "event/rwlocked.zig" | |
| 468 | 471 | "event/tcp.zig" |
| 469 | 472 | "fmt/errol/enum3.zig" |
| 470 | 473 | "fmt/errol/index.zig" |
| ... | ... | @@ -553,6 +556,7 @@ set(ZIG_STD_FILES |
| 553 | 556 | "math/tanh.zig" |
| 554 | 557 | "math/trunc.zig" |
| 555 | 558 | "mem.zig" |
| 559 | "mutex.zig" | |
| 556 | 560 | "net.zig" |
| 557 | 561 | "os/child_process.zig" |
| 558 | 562 | "os/darwin.zig" |
doc/docgen.zig+2-2| ... | ... | @@ -370,9 +370,9 @@ fn genToc(allocator: *mem.Allocator, tokenizer: *Tokenizer) !Toc { |
| 370 | 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 | 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 | 376 | return error.ParseError; |
| 377 | 377 | } |
| 378 | 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 | 19 | var output_path = try await (async comp.createRandomOutputPath(comp.target.objFileExt()) catch unreachable); |
| 20 | 20 | errdefer output_path.deinit(); |
| 21 | 21 | |
| 22 | const llvm_handle = try comp.event_loop_local.getAnyLlvmContext(); | |
| 23 | defer llvm_handle.release(comp.event_loop_local); | |
| 22 | const llvm_handle = try comp.zig_compiler.getAnyLlvmContext(); | |
| 23 | defer llvm_handle.release(comp.zig_compiler); | |
| 24 | 24 | |
| 25 | 25 | const context = llvm_handle.node.data; |
| 26 | 26 |
src-self-hosted/compilation.zig+346-180| ... | ... | @@ -30,9 +30,12 @@ const Package = @import("package.zig").Package; |
| 30 | 30 | const link = @import("link.zig").link; |
| 31 | 31 | const LibCInstallation = @import("libc_installation.zig").LibCInstallation; |
| 32 | 32 | const CInt = @import("c_int.zig").CInt; |
| 33 | const fs = event.fs; | |
| 34 | ||
| 35 | const max_src_size = 2 * 1024 * 1024 * 1024; // 2 GiB | |
| 33 | 36 | |
| 34 | 37 | /// Data that is local to the event loop. |
| 35 | pub const EventLoopLocal = struct { | |
| 38 | pub const ZigCompiler = struct { | |
| 36 | 39 | loop: *event.Loop, |
| 37 | 40 | llvm_handle_pool: std.atomic.Stack(llvm.ContextRef), |
| 38 | 41 | lld_lock: event.Lock, |
| ... | ... | @@ -44,7 +47,7 @@ pub const EventLoopLocal = struct { |
| 44 | 47 | |
| 45 | 48 | var lazy_init_targets = std.lazyInit(void); |
| 46 | 49 | |
| 47 | fn init(loop: *event.Loop) !EventLoopLocal { | |
| 50 | fn init(loop: *event.Loop) !ZigCompiler { | |
| 48 | 51 | lazy_init_targets.get() orelse { |
| 49 | 52 | Target.initializeAll(); |
| 50 | 53 | lazy_init_targets.resolve(); |
| ... | ... | @@ -54,7 +57,7 @@ pub const EventLoopLocal = struct { |
| 54 | 57 | try std.os.getRandomBytes(seed_bytes[0..]); |
| 55 | 58 | const seed = std.mem.readInt(seed_bytes, u64, builtin.Endian.Big); |
| 56 | 59 | |
| 57 | return EventLoopLocal{ | |
| 60 | return ZigCompiler{ | |
| 58 | 61 | .loop = loop, |
| 59 | 62 | .lld_lock = event.Lock.init(loop), |
| 60 | 63 | .llvm_handle_pool = std.atomic.Stack(llvm.ContextRef).init(), |
| ... | ... | @@ -64,7 +67,7 @@ pub const EventLoopLocal = struct { |
| 64 | 67 | } |
| 65 | 68 | |
| 66 | 69 | /// Must be called only after EventLoop.run completes. |
| 67 | fn deinit(self: *EventLoopLocal) void { | |
| 70 | fn deinit(self: *ZigCompiler) void { | |
| 68 | 71 | self.lld_lock.deinit(); |
| 69 | 72 | while (self.llvm_handle_pool.pop()) |node| { |
| 70 | 73 | c.LLVMContextDispose(node.data); |
| ... | ... | @@ -74,7 +77,7 @@ pub const EventLoopLocal = struct { |
| 74 | 77 | |
| 75 | 78 | /// Gets an exclusive handle on any LlvmContext. |
| 76 | 79 | /// Caller must release the handle when done. |
| 77 | pub fn getAnyLlvmContext(self: *EventLoopLocal) !LlvmHandle { | |
| 80 | pub fn getAnyLlvmContext(self: *ZigCompiler) !LlvmHandle { | |
| 78 | 81 | if (self.llvm_handle_pool.pop()) |node| return LlvmHandle{ .node = node }; |
| 79 | 82 | |
| 80 | 83 | const context_ref = c.LLVMContextCreate() orelse return error.OutOfMemory; |
| ... | ... | @@ -89,24 +92,36 @@ pub const EventLoopLocal = struct { |
| 89 | 92 | return LlvmHandle{ .node = node }; |
| 90 | 93 | } |
| 91 | 94 | |
| 92 | pub async fn getNativeLibC(self: *EventLoopLocal) !*LibCInstallation { | |
| 95 | pub async fn getNativeLibC(self: *ZigCompiler) !*LibCInstallation { | |
| 93 | 96 | if (await (async self.native_libc.start() catch unreachable)) |ptr| return ptr; |
| 94 | 97 | try await (async self.native_libc.data.findNative(self.loop) catch unreachable); |
| 95 | 98 | self.native_libc.resolve(); |
| 96 | 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 | }; |
| 99 | 114 | |
| 100 | 115 | pub const LlvmHandle = struct { |
| 101 | 116 | node: *std.atomic.Stack(llvm.ContextRef).Node, |
| 102 | 117 | |
| 103 | pub fn release(self: LlvmHandle, event_loop_local: *EventLoopLocal) void { | |
| 104 | event_loop_local.llvm_handle_pool.push(self.node); | |
| 118 | pub fn release(self: LlvmHandle, zig_compiler: *ZigCompiler) void { | |
| 119 | zig_compiler.llvm_handle_pool.push(self.node); | |
| 105 | 120 | } |
| 106 | 121 | }; |
| 107 | 122 | |
| 108 | 123 | pub const Compilation = struct { |
| 109 | event_loop_local: *EventLoopLocal, | |
| 124 | zig_compiler: *ZigCompiler, | |
| 110 | 125 | loop: *event.Loop, |
| 111 | 126 | name: Buffer, |
| 112 | 127 | llvm_triple: Buffer, |
| ... | ... | @@ -134,7 +149,6 @@ pub const Compilation = struct { |
| 134 | 149 | linker_rdynamic: bool, |
| 135 | 150 | |
| 136 | 151 | clang_argv: []const []const u8, |
| 137 | llvm_argv: []const []const u8, | |
| 138 | 152 | lib_dirs: []const []const u8, |
| 139 | 153 | rpath_list: []const []const u8, |
| 140 | 154 | assembly_files: []const []const u8, |
| ... | ... | @@ -214,6 +228,8 @@ pub const Compilation = struct { |
| 214 | 228 | deinit_group: event.Group(void), |
| 215 | 229 | |
| 216 | 230 | destroy_handle: promise, |
| 231 | main_loop_handle: promise, | |
| 232 | main_loop_future: event.Future(void), | |
| 217 | 233 | |
| 218 | 234 | have_err_ret_tracing: bool, |
| 219 | 235 | |
| ... | ... | @@ -227,6 +243,8 @@ pub const Compilation = struct { |
| 227 | 243 | |
| 228 | 244 | c_int_types: [CInt.list.len]*Type.Int, |
| 229 | 245 | |
| 246 | fs_watch: *fs.Watch(*Scope.Root), | |
| 247 | ||
| 230 | 248 | const IntTypeTable = std.HashMap(*const Type.Int.Key, *Type.Int, Type.Int.Key.hash, Type.Int.Key.eql); |
| 231 | 249 | const ArrayTypeTable = std.HashMap(*const Type.Array.Key, *Type.Array, Type.Array.Key.hash, Type.Array.Key.eql); |
| 232 | 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 | 300 | LibCMissingDynamicLinker, |
| 283 | 301 | InvalidDarwinVersionString, |
| 284 | 302 | UnsupportedLinkArchitecture, |
| 303 | UserResourceLimitReached, | |
| 304 | InvalidUtf8, | |
| 285 | 305 | }; |
| 286 | 306 | |
| 287 | 307 | pub const Event = union(enum) { |
| ... | ... | @@ -318,7 +338,7 @@ pub const Compilation = struct { |
| 318 | 338 | }; |
| 319 | 339 | |
| 320 | 340 | pub fn create( |
| 321 | event_loop_local: *EventLoopLocal, | |
| 341 | zig_compiler: *ZigCompiler, | |
| 322 | 342 | name: []const u8, |
| 323 | 343 | root_src_path: ?[]const u8, |
| 324 | 344 | target: Target, |
| ... | ... | @@ -327,11 +347,45 @@ pub const Compilation = struct { |
| 327 | 347 | is_static: bool, |
| 328 | 348 | zig_lib_dir: []const u8, |
| 329 | 349 | ) !*Compilation { |
| 330 | const loop = event_loop_local.loop; | |
| 331 | const comp = try event_loop_local.loop.allocator.create(Compilation{ | |
| 350 | var optional_comp: ?*Compilation = null; | |
| 351 | const handle = try async<zig_compiler.loop.allocator> createAsync( | |
| 352 | &optional_comp, | |
| 353 | zig_compiler, | |
| 354 | name, | |
| 355 | root_src_path, | |
| 356 | target, | |
| 357 | kind, | |
| 358 | build_mode, | |
| 359 | is_static, | |
| 360 | zig_lib_dir, | |
| 361 | ); | |
| 362 | return optional_comp orelse if (getAwaitResult( | |
| 363 | zig_compiler.loop.allocator, | |
| 364 | handle, | |
| 365 | )) |_| unreachable else |err| err; | |
| 366 | } | |
| 367 | ||
| 368 | async fn createAsync( | |
| 369 | out_comp: *?*Compilation, | |
| 370 | zig_compiler: *ZigCompiler, | |
| 371 | name: []const u8, | |
| 372 | root_src_path: ?[]const u8, | |
| 373 | target: Target, | |
| 374 | kind: Kind, | |
| 375 | build_mode: builtin.Mode, | |
| 376 | is_static: bool, | |
| 377 | zig_lib_dir: []const u8, | |
| 378 | ) !void { | |
| 379 | // workaround for https://github.com/ziglang/zig/issues/1194 | |
| 380 | suspend { | |
| 381 | resume @handle(); | |
| 382 | } | |
| 383 | ||
| 384 | const loop = zig_compiler.loop; | |
| 385 | var comp = Compilation{ | |
| 332 | 386 | .loop = loop, |
| 333 | 387 | .arena_allocator = std.heap.ArenaAllocator.init(loop.allocator), |
| 334 | .event_loop_local = event_loop_local, | |
| 388 | .zig_compiler = zig_compiler, | |
| 335 | 389 | .events = undefined, |
| 336 | 390 | .root_src_path = root_src_path, |
| 337 | 391 | .target = target, |
| ... | ... | @@ -341,6 +395,9 @@ pub const Compilation = struct { |
| 341 | 395 | .zig_lib_dir = zig_lib_dir, |
| 342 | 396 | .zig_std_dir = undefined, |
| 343 | 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), | |
| 344 | 401 | |
| 345 | 402 | .name = undefined, |
| 346 | 403 | .llvm_triple = undefined, |
| ... | ... | @@ -365,7 +422,6 @@ pub const Compilation = struct { |
| 365 | 422 | .is_static = is_static, |
| 366 | 423 | .linker_rdynamic = false, |
| 367 | 424 | .clang_argv = [][]const u8{}, |
| 368 | .llvm_argv = [][]const u8{}, | |
| 369 | 425 | .lib_dirs = [][]const u8{}, |
| 370 | 426 | .rpath_list = [][]const u8{}, |
| 371 | 427 | .assembly_files = [][]const u8{}, |
| ... | ... | @@ -412,25 +468,26 @@ pub const Compilation = struct { |
| 412 | 468 | .std_package = undefined, |
| 413 | 469 | |
| 414 | 470 | .override_libc = null, |
| 415 | .destroy_handle = undefined, | |
| 416 | 471 | .have_err_ret_tracing = false, |
| 417 | 472 | .primitive_type_table = undefined, |
| 418 | }); | |
| 419 | errdefer { | |
| 473 | ||
| 474 | .fs_watch = undefined, | |
| 475 | }; | |
| 476 | comp.link_libs_list = ArrayList(*LinkLib).init(comp.arena()); | |
| 477 | comp.primitive_type_table = TypeTable.init(comp.arena()); | |
| 478 | ||
| 479 | defer { | |
| 420 | 480 | comp.int_type_table.private_data.deinit(); |
| 421 | 481 | comp.array_type_table.private_data.deinit(); |
| 422 | 482 | comp.ptr_type_table.private_data.deinit(); |
| 423 | 483 | comp.fn_type_table.private_data.deinit(); |
| 424 | 484 | comp.arena_allocator.deinit(); |
| 425 | comp.loop.allocator.destroy(comp); | |
| 426 | 485 | } |
| 427 | 486 | |
| 428 | 487 | comp.name = try Buffer.init(comp.arena(), name); |
| 429 | 488 | comp.llvm_triple = try target.getTriple(comp.arena()); |
| 430 | 489 | comp.llvm_target = try Target.llvmTargetFromTriple(comp.llvm_triple); |
| 431 | comp.link_libs_list = ArrayList(*LinkLib).init(comp.arena()); | |
| 432 | 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()); | |
| 434 | 491 | |
| 435 | 492 | const opt_level = switch (build_mode) { |
| 436 | 493 | builtin.Mode.Debug => llvm.CodeGenLevelNone, |
| ... | ... | @@ -444,8 +501,8 @@ pub const Compilation = struct { |
| 444 | 501 | // As a workaround we do not use target native features on Windows. |
| 445 | 502 | var target_specific_cpu_args: ?[*]u8 = null; |
| 446 | 503 | var target_specific_cpu_features: ?[*]u8 = null; |
| 447 | errdefer llvm.DisposeMessage(target_specific_cpu_args); | |
| 448 | errdefer llvm.DisposeMessage(target_specific_cpu_features); | |
| 504 | defer llvm.DisposeMessage(target_specific_cpu_args); | |
| 505 | defer llvm.DisposeMessage(target_specific_cpu_features); | |
| 449 | 506 | if (target == Target.Native and !target.isWindows()) { |
| 450 | 507 | target_specific_cpu_args = llvm.GetHostCPUName() orelse return error.OutOfMemory; |
| 451 | 508 | target_specific_cpu_features = llvm.GetNativeFeatures() orelse return error.OutOfMemory; |
| ... | ... | @@ -460,16 +517,16 @@ pub const Compilation = struct { |
| 460 | 517 | reloc_mode, |
| 461 | 518 | llvm.CodeModelDefault, |
| 462 | 519 | ) orelse return error.OutOfMemory; |
| 463 | errdefer llvm.DisposeTargetMachine(comp.target_machine); | |
| 520 | defer llvm.DisposeTargetMachine(comp.target_machine); | |
| 464 | 521 | |
| 465 | 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); | |
| 467 | 524 | |
| 468 | 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); | |
| 470 | 527 | |
| 471 | 528 | comp.events = try event.Channel(Event).create(comp.loop, 0); |
| 472 | errdefer comp.events.destroy(); | |
| 529 | defer comp.events.destroy(); | |
| 473 | 530 | |
| 474 | 531 | if (root_src_path) |root_src| { |
| 475 | 532 | const dirname = std.os.path.dirname(root_src) orelse "."; |
| ... | ... | @@ -482,11 +539,27 @@ pub const Compilation = struct { |
| 482 | 539 | comp.root_package = try Package.create(comp.arena(), ".", ""); |
| 483 | 540 | } |
| 484 | 541 | |
| 542 | comp.fs_watch = try fs.Watch(*Scope.Root).create(loop, 16); | |
| 543 | defer comp.fs_watch.destroy(); | |
| 544 | ||
| 485 | 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. | |
| 486 | 556 | |
| 487 | comp.destroy_handle = try async<loop.allocator> comp.internalDeinit(); | |
| 557 | await (async comp.deinit_group.wait() catch unreachable); | |
| 488 | 558 | |
| 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 | } |
| 491 | 564 | |
| 492 | 565 | /// it does ref the result because it could be an arbitrary integer size |
| ... | ... | @@ -672,55 +745,28 @@ pub const Compilation = struct { |
| 672 | 745 | assert((try comp.primitive_type_table.put(comp.u8_type.base.name, &comp.u8_type.base)) == null); |
| 673 | 746 | } |
| 674 | 747 | |
| 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 | 748 | pub fn destroy(self: *Compilation) void { |
| 749 | cancel self.main_loop_handle; | |
| 699 | 750 | resume self.destroy_handle; |
| 700 | 751 | } |
| 701 | 752 | |
| 702 | pub fn build(self: *Compilation) !void { | |
| 703 | if (self.llvm_argv.len != 0) { | |
| 704 | var c_compatible_args = try std.cstr.NullTerminated2DArray.fromSlices(self.arena(), [][]const []const u8{ | |
| 705 | [][]const u8{"zig (LLVM option parsing)"}, | |
| 706 | self.llvm_argv, | |
| 707 | }); | |
| 708 | defer c_compatible_args.deinit(); | |
| 709 | // TODO this sets global state | |
| 710 | c.ZigLLVMParseCommandLineOptions(self.llvm_argv.len + 1, c_compatible_args.ptr); | |
| 711 | } | |
| 712 | ||
| 713 | _ = try async<self.gpa()> self.buildAsync(); | |
| 753 | fn start(self: *Compilation) void { | |
| 754 | self.main_loop_future.resolve(); | |
| 714 | 755 | } |
| 715 | 756 | |
| 716 | async fn buildAsync(self: *Compilation) void { | |
| 717 | while (true) { | |
| 718 | // TODO directly awaiting async should guarantee memory allocation elision | |
| 719 | const build_result = await (async self.compileAndLink() catch unreachable); | |
| 757 | async fn mainLoop(self: *Compilation) void { | |
| 758 | // wait until start() is called | |
| 759 | _ = await (async self.main_loop_future.get() catch unreachable); | |
| 720 | 760 | |
| 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 | 767 | // this makes a handy error return trace and stack trace in debug mode |
| 722 | 768 | if (std.debug.runtime_safety) { |
| 723 | build_result catch unreachable; | |
| 769 | link_result catch unreachable; | |
| 724 | 770 | } |
| 725 | 771 | |
| 726 | 772 | const compile_errors = blk: { |
| ... | ... | @@ -729,7 +775,7 @@ pub const Compilation = struct { |
| 729 | 775 | break :blk held.value.toOwnedSlice(); |
| 730 | 776 | }; |
| 731 | 777 | |
| 732 | if (build_result) |_| { | |
| 778 | if (link_result) |_| { | |
| 733 | 779 | if (compile_errors.len == 0) { |
| 734 | 780 | await (async self.events.put(Event.Ok) catch unreachable); |
| 735 | 781 | } else { |
| ... | ... | @@ -742,105 +788,195 @@ pub const Compilation = struct { |
| 742 | 788 | await (async self.events.put(Event{ .Error = err }) catch unreachable); |
| 743 | 789 | } |
| 744 | 790 | |
| 745 | // for now we stop after 1 | |
| 746 | return; | |
| 791 | // First, get an item from the watch channel, waiting on the channel. | |
| 792 | var group = event.Group(BuildError!void).init(self.loop); | |
| 793 | { | |
| 794 | const ev = (await (async self.fs_watch.channel.get() catch unreachable)) catch |err| { | |
| 795 | build_result = err; | |
| 796 | continue; | |
| 797 | }; | |
| 798 | const root_scope = ev.data; | |
| 799 | group.call(rebuildFile, self, root_scope) catch |err| { | |
| 800 | build_result = err; | |
| 801 | continue; | |
| 802 | }; | |
| 803 | } | |
| 804 | // Next, get all the items from the channel that are buffered up. | |
| 805 | while (await (async self.fs_watch.channel.getOrNull() catch unreachable)) |ev_or_err| { | |
| 806 | if (ev_or_err) |ev| { | |
| 807 | const root_scope = ev.data; | |
| 808 | group.call(rebuildFile, self, root_scope) catch |err| { | |
| 809 | build_result = err; | |
| 810 | continue; | |
| 811 | }; | |
| 812 | } else |err| { | |
| 813 | build_result = err; | |
| 814 | continue; | |
| 815 | } | |
| 816 | } | |
| 817 | build_result = await (async group.wait() catch unreachable); | |
| 747 | 818 | } |
| 748 | 819 | } |
| 749 | 820 | |
| 750 | async fn compileAndLink(self: *Compilation) !void { | |
| 751 | if (self.root_src_path) |root_src_path| { | |
| 752 | // TODO async/await os.path.real | |
| 753 | const root_src_real_path = os.path.real(self.gpa(), root_src_path) catch |err| { | |
| 754 | try printError("unable to get real path '{}': {}", root_src_path, err); | |
| 755 | return err; | |
| 821 | async fn rebuildFile(self: *Compilation, root_scope: *Scope.Root) !void { | |
| 822 | const tree_scope = blk: { | |
| 823 | const source_code = (await (async fs.readFile( | |
| 824 | self.loop, | |
| 825 | root_scope.realpath, | |
| 826 | max_src_size, | |
| 827 | ) catch unreachable)) catch |err| { | |
| 828 | try self.addCompileErrorCli(root_scope.realpath, "unable to open: {}", @errorName(err)); | |
| 829 | return; | |
| 756 | 830 | }; |
| 757 | const root_scope = blk: { | |
| 758 | errdefer self.gpa().free(root_src_real_path); | |
| 831 | errdefer self.gpa().free(source_code); | |
| 759 | 832 | |
| 760 | // TODO async/await readFileAlloc() | |
| 761 | const source_code = io.readFileAlloc(self.gpa(), root_src_real_path) catch |err| { | |
| 762 | try printError("unable to open '{}': {}", root_src_real_path, err); | |
| 763 | return err; | |
| 764 | }; | |
| 765 | errdefer self.gpa().free(source_code); | |
| 833 | const tree = try self.gpa().createOne(ast.Tree); | |
| 834 | tree.* = try std.zig.parse(self.gpa(), source_code); | |
| 835 | errdefer { | |
| 836 | tree.deinit(); | |
| 837 | self.gpa().destroy(tree); | |
| 838 | } | |
| 766 | 839 | |
| 767 | const tree = try self.gpa().createOne(ast.Tree); | |
| 768 | tree.* = try std.zig.parse(self.gpa(), source_code); | |
| 769 | errdefer { | |
| 770 | tree.deinit(); | |
| 771 | self.gpa().destroy(tree); | |
| 772 | } | |
| 840 | break :blk try Scope.AstTree.create(self, tree, root_scope); | |
| 841 | }; | |
| 842 | defer tree_scope.base.deref(self); | |
| 773 | 843 | |
| 774 | break :blk try Scope.Root.create(self, tree, root_src_real_path); | |
| 775 | }; | |
| 776 | defer root_scope.base.deref(self); | |
| 777 | const tree = root_scope.tree; | |
| 844 | var error_it = tree_scope.tree.errors.iterator(0); | |
| 845 | while (error_it.next()) |parse_error| { | |
| 846 | const msg = try Msg.createFromParseErrorAndScope(self, tree_scope, parse_error); | |
| 847 | errdefer msg.destroy(); | |
| 778 | 848 | |
| 779 | var error_it = tree.errors.iterator(0); | |
| 780 | while (error_it.next()) |parse_error| { | |
| 781 | const msg = try Msg.createFromParseErrorAndScope(self, root_scope, parse_error); | |
| 782 | errdefer msg.destroy(); | |
| 849 | try await (async self.addCompileErrorAsync(msg) catch unreachable); | |
| 850 | } | |
| 851 | if (tree_scope.tree.errors.len != 0) { | |
| 852 | return; | |
| 853 | } | |
| 783 | 854 | |
| 784 | try await (async self.addCompileErrorAsync(msg) catch unreachable); | |
| 785 | } | |
| 786 | if (tree.errors.len != 0) { | |
| 787 | return; | |
| 788 | } | |
| 855 | const locked_table = await (async root_scope.decls.table.acquireWrite() catch unreachable); | |
| 856 | defer locked_table.release(); | |
| 789 | 857 | |
| 790 | const decls = try Scope.Decls.create(self, &root_scope.base); | |
| 791 | defer decls.base.deref(self); | |
| 858 | var decl_group = event.Group(BuildError!void).init(self.loop); | |
| 859 | defer decl_group.deinit(); | |
| 792 | 860 | |
| 793 | var decl_group = event.Group(BuildError!void).init(self.loop); | |
| 794 | var decl_group_consumed = false; | |
| 795 | errdefer if (!decl_group_consumed) decl_group.cancelAll(); | |
| 861 | try await try async self.rebuildChangedDecls( | |
| 862 | &decl_group, | |
| 863 | locked_table.value, | |
| 864 | root_scope.decls, | |
| 865 | &tree_scope.tree.root_node.decls, | |
| 866 | tree_scope, | |
| 867 | ); | |
| 796 | 868 | |
| 797 | var it = tree.root_node.decls.iterator(0); | |
| 798 | while (it.next()) |decl_ptr| { | |
| 799 | const decl = decl_ptr.*; | |
| 800 | switch (decl.id) { | |
| 801 | ast.Node.Id.Comptime => { | |
| 802 | const comptime_node = @fieldParentPtr(ast.Node.Comptime, "base", decl); | |
| 869 | try await (async decl_group.wait() catch unreachable); | |
| 870 | } | |
| 803 | 871 | |
| 804 | try self.prelink_group.call(addCompTimeBlock, self, &decls.base, comptime_node); | |
| 805 | }, | |
| 806 | ast.Node.Id.VarDecl => @panic("TODO"), | |
| 807 | ast.Node.Id.FnProto => { | |
| 808 | const fn_proto = @fieldParentPtr(ast.Node.FnProto, "base", decl); | |
| 809 | ||
| 810 | const name = if (fn_proto.name_token) |name_token| tree.tokenSlice(name_token) else { | |
| 811 | try self.addCompileError(root_scope, Span{ | |
| 812 | .first = fn_proto.fn_token, | |
| 813 | .last = fn_proto.fn_token + 1, | |
| 814 | }, "missing function name"); | |
| 815 | continue; | |
| 816 | }; | |
| 872 | async fn rebuildChangedDecls( | |
| 873 | self: *Compilation, | |
| 874 | group: *event.Group(BuildError!void), | |
| 875 | locked_table: *Decl.Table, | |
| 876 | decl_scope: *Scope.Decls, | |
| 877 | ast_decls: *ast.Node.Root.DeclList, | |
| 878 | tree_scope: *Scope.AstTree, | |
| 879 | ) !void { | |
| 880 | var existing_decls = try locked_table.clone(); | |
| 881 | defer existing_decls.deinit(); | |
| 882 | ||
| 883 | var ast_it = ast_decls.iterator(0); | |
| 884 | while (ast_it.next()) |decl_ptr| { | |
| 885 | const decl = decl_ptr.*; | |
| 886 | switch (decl.id) { | |
| 887 | ast.Node.Id.Comptime => { | |
| 888 | const comptime_node = @fieldParentPtr(ast.Node.Comptime, "base", decl); | |
| 889 | ||
| 890 | // TODO connect existing comptime decls to updated source files | |
| 891 | ||
| 892 | try self.prelink_group.call(addCompTimeBlock, self, tree_scope, &decl_scope.base, comptime_node); | |
| 893 | }, | |
| 894 | ast.Node.Id.VarDecl => @panic("TODO"), | |
| 895 | ast.Node.Id.FnProto => { | |
| 896 | const fn_proto = @fieldParentPtr(ast.Node.FnProto, "base", decl); | |
| 897 | ||
| 898 | const name = if (fn_proto.name_token) |name_token| tree_scope.tree.tokenSlice(name_token) else { | |
| 899 | try self.addCompileError(tree_scope, Span{ | |
| 900 | .first = fn_proto.fn_token, | |
| 901 | .last = fn_proto.fn_token + 1, | |
| 902 | }, "missing function name"); | |
| 903 | continue; | |
| 904 | }; | |
| 817 | 905 | |
| 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 | 929 | const fn_decl = try self.gpa().create(Decl.Fn{ |
| 819 | 930 | .base = Decl{ |
| 820 | 931 | .id = Decl.Id.Fn, |
| 821 | 932 | .name = name, |
| 822 | .visib = parseVisibToken(tree, fn_proto.visib_token), | |
| 933 | .visib = parseVisibToken(tree_scope.tree, fn_proto.visib_token), | |
| 823 | 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 | 938 | .value = Decl.Fn.Val{ .Unresolved = {} }, |
| 827 | 939 | .fn_proto = fn_proto, |
| 828 | 940 | }); |
| 941 | tree_scope.base.ref(); | |
| 829 | 942 | errdefer self.gpa().destroy(fn_decl); |
| 830 | 943 | |
| 831 | try decl_group.call(addTopLevelDecl, self, decls, &fn_decl.base); | |
| 832 | }, | |
| 833 | ast.Node.Id.TestDecl => @panic("TODO"), | |
| 834 | else => unreachable, | |
| 835 | } | |
| 944 | try group.call(addTopLevelDecl, self, &fn_decl.base, locked_table); | |
| 945 | } | |
| 946 | }, | |
| 947 | ast.Node.Id.TestDecl => @panic("TODO"), | |
| 948 | else => unreachable, | |
| 836 | 949 | } |
| 837 | decl_group_consumed = true; | |
| 838 | try await (async decl_group.wait() catch unreachable); | |
| 950 | } | |
| 951 | ||
| 952 | var existing_decl_it = existing_decls.iterator(); | |
| 953 | while (existing_decl_it.next()) |entry| { | |
| 954 | // this decl was deleted | |
| 955 | const existing_decl = entry.value; | |
| 956 | @panic("TODO handle decl deletion"); | |
| 957 | } | |
| 958 | } | |
| 959 | ||
| 960 | async fn initialCompile(self: *Compilation) !void { | |
| 961 | if (self.root_src_path) |root_src_path| { | |
| 962 | const root_scope = blk: { | |
| 963 | // TODO async/await os.path.real | |
| 964 | const root_src_real_path = os.path.real(self.gpa(), root_src_path) catch |err| { | |
| 965 | try self.addCompileErrorCli(root_src_path, "unable to open: {}", @errorName(err)); | |
| 966 | return; | |
| 967 | }; | |
| 968 | errdefer self.gpa().free(root_src_real_path); | |
| 969 | ||
| 970 | break :blk try Scope.Root.create(self, root_src_real_path); | |
| 971 | }; | |
| 972 | defer root_scope.base.deref(self); | |
| 839 | 973 | |
| 840 | // Now other code can rely on the decls scope having a complete list of names. | |
| 841 | decls.name_future.resolve(); | |
| 974 | assert((try await try async self.fs_watch.addFile(root_scope.realpath, root_scope)) == null); | |
| 975 | try await try async self.rebuildFile(root_scope); | |
| 842 | 976 | } |
| 977 | } | |
| 843 | 978 | |
| 979 | async fn maybeLink(self: *Compilation) !void { | |
| 844 | 980 | (await (async self.prelink_group.wait() catch unreachable)) catch |err| switch (err) { |
| 845 | 981 | error.SemanticAnalysisFailed => {}, |
| 846 | 982 | else => return err, |
| ... | ... | @@ -861,6 +997,7 @@ pub const Compilation = struct { |
| 861 | 997 | /// caller takes ownership of resulting Code |
| 862 | 998 | async fn genAndAnalyzeCode( |
| 863 | 999 | comp: *Compilation, |
| 1000 | tree_scope: *Scope.AstTree, | |
| 864 | 1001 | scope: *Scope, |
| 865 | 1002 | node: *ast.Node, |
| 866 | 1003 | expected_type: ?*Type, |
| ... | ... | @@ -868,6 +1005,7 @@ pub const Compilation = struct { |
| 868 | 1005 | const unanalyzed_code = try await (async ir.gen( |
| 869 | 1006 | comp, |
| 870 | 1007 | node, |
| 1008 | tree_scope, | |
| 871 | 1009 | scope, |
| 872 | 1010 | ) catch unreachable); |
| 873 | 1011 | defer unanalyzed_code.destroy(comp.gpa()); |
| ... | ... | @@ -894,6 +1032,7 @@ pub const Compilation = struct { |
| 894 | 1032 | |
| 895 | 1033 | async fn addCompTimeBlock( |
| 896 | 1034 | comp: *Compilation, |
| 1035 | tree_scope: *Scope.AstTree, | |
| 897 | 1036 | scope: *Scope, |
| 898 | 1037 | comptime_node: *ast.Node.Comptime, |
| 899 | 1038 | ) !void { |
| ... | ... | @@ -902,6 +1041,7 @@ pub const Compilation = struct { |
| 902 | 1041 | |
| 903 | 1042 | const analyzed_code = (await (async genAndAnalyzeCode( |
| 904 | 1043 | comp, |
| 1044 | tree_scope, | |
| 905 | 1045 | scope, |
| 906 | 1046 | comptime_node.expr, |
| 907 | 1047 | &void_type.base, |
| ... | ... | @@ -914,38 +1054,42 @@ pub const Compilation = struct { |
| 914 | 1054 | analyzed_code.destroy(comp.gpa()); |
| 915 | 1055 | } |
| 916 | 1056 | |
| 917 | async fn addTopLevelDecl(self: *Compilation, decls: *Scope.Decls, decl: *Decl) !void { | |
| 918 | const tree = decl.findRootScope().tree; | |
| 919 | const is_export = decl.isExported(tree); | |
| 920 | ||
| 921 | var add_to_table_resolved = false; | |
| 922 | const add_to_table = async self.addDeclToTable(decls, decl) catch unreachable; | |
| 923 | errdefer if (!add_to_table_resolved) cancel add_to_table; // TODO https://github.com/ziglang/zig/issues/1261 | |
| 1057 | async fn addTopLevelDecl( | |
| 1058 | self: *Compilation, | |
| 1059 | decl: *Decl, | |
| 1060 | locked_table: *Decl.Table, | |
| 1061 | ) !void { | |
| 1062 | const is_export = decl.isExported(decl.tree_scope.tree); | |
| 924 | 1063 | |
| 925 | 1064 | if (is_export) { |
| 926 | 1065 | try self.prelink_group.call(verifyUniqueSymbol, self, decl); |
| 927 | 1066 | try self.prelink_group.call(resolveDecl, self, decl); |
| 928 | 1067 | } |
| 929 | 1068 | |
| 930 | add_to_table_resolved = true; | |
| 931 | try await add_to_table; | |
| 1069 | const gop = try locked_table.getOrPut(decl.name); | |
| 1070 | if (gop.found_existing) { | |
| 1071 | try self.addCompileError(decl.tree_scope, decl.getSpan(), "redefinition of '{}'", decl.name); | |
| 1072 | // TODO note: other definition here | |
| 1073 | } else { | |
| 1074 | gop.kv.value = decl; | |
| 1075 | } | |
| 932 | 1076 | } |
| 933 | 1077 | |
| 934 | async fn addDeclToTable(self: *Compilation, decls: *Scope.Decls, decl: *Decl) !void { | |
| 935 | const held = await (async decls.table.acquire() catch unreachable); | |
| 936 | defer held.release(); | |
| 1078 | fn addCompileError(self: *Compilation, tree_scope: *Scope.AstTree, span: Span, comptime fmt: []const u8, args: ...) !void { | |
| 1079 | const text = try std.fmt.allocPrint(self.gpa(), fmt, args); | |
| 1080 | errdefer self.gpa().free(text); | |
| 937 | 1081 | |
| 938 | if (try held.value.put(decl.name, decl)) |other_decl| { | |
| 939 | try self.addCompileError(decls.base.findRoot(), decl.getSpan(), "redefinition of '{}'", decl.name); | |
| 940 | // TODO note: other definition here | |
| 941 | } | |
| 1082 | const msg = try Msg.createFromScope(self, tree_scope, span, text); | |
| 1083 | errdefer msg.destroy(); | |
| 1084 | ||
| 1085 | try self.prelink_group.call(addCompileErrorAsync, self, msg); | |
| 942 | 1086 | } |
| 943 | 1087 | |
| 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 | 1089 | const text = try std.fmt.allocPrint(self.gpa(), fmt, args); |
| 946 | 1090 | errdefer self.gpa().free(text); |
| 947 | 1091 | |
| 948 | const msg = try Msg.createFromScope(self, root, span, text); | |
| 1092 | const msg = try Msg.createFromCli(self, realpath, text); | |
| 949 | 1093 | errdefer msg.destroy(); |
| 950 | 1094 | |
| 951 | 1095 | try self.prelink_group.call(addCompileErrorAsync, self, msg); |
| ... | ... | @@ -969,7 +1113,7 @@ pub const Compilation = struct { |
| 969 | 1113 | |
| 970 | 1114 | if (try exported_symbol_names.value.put(decl.name, decl)) |other_decl| { |
| 971 | 1115 | try self.addCompileError( |
| 972 | decl.findRootScope(), | |
| 1116 | decl.tree_scope, | |
| 973 | 1117 | decl.getSpan(), |
| 974 | 1118 | "exported symbol collision: '{}'", |
| 975 | 1119 | decl.name, |
| ... | ... | @@ -1019,7 +1163,7 @@ pub const Compilation = struct { |
| 1019 | 1163 | async fn startFindingNativeLibC(self: *Compilation) void { |
| 1020 | 1164 | await (async self.loop.yield() catch unreachable); |
| 1021 | 1165 | // 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 | } |
| 1024 | 1168 | |
| 1025 | 1169 | /// General Purpose Allocator. Must free when done. |
| ... | ... | @@ -1077,7 +1221,7 @@ pub const Compilation = struct { |
| 1077 | 1221 | var rand_bytes: [9]u8 = undefined; |
| 1078 | 1222 | |
| 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 | 1225 | defer held.release(); |
| 1082 | 1226 | |
| 1083 | 1227 | held.value.random.bytes(rand_bytes[0..]); |
| ... | ... | @@ -1093,18 +1237,24 @@ pub const Compilation = struct { |
| 1093 | 1237 | } |
| 1094 | 1238 | |
| 1095 | 1239 | /// Returns a value which has been ref()'d once |
| 1096 | async fn analyzeConstValue(comp: *Compilation, scope: *Scope, node: *ast.Node, expected_type: *Type) !*Value { | |
| 1097 | const analyzed_code = try await (async comp.genAndAnalyzeCode(scope, node, expected_type) catch unreachable); | |
| 1240 | async fn analyzeConstValue( | |
| 1241 | comp: *Compilation, | |
| 1242 | tree_scope: *Scope.AstTree, | |
| 1243 | scope: *Scope, | |
| 1244 | node: *ast.Node, | |
| 1245 | expected_type: *Type, | |
| 1246 | ) !*Value { | |
| 1247 | const analyzed_code = try await (async comp.genAndAnalyzeCode(tree_scope, scope, node, expected_type) catch unreachable); | |
| 1098 | 1248 | defer analyzed_code.destroy(comp.gpa()); |
| 1099 | 1249 | |
| 1100 | 1250 | return analyzed_code.getCompTimeResult(comp); |
| 1101 | 1251 | } |
| 1102 | 1252 | |
| 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 | 1254 | const meta_type = &Type.MetaType.get(comp).base; |
| 1105 | 1255 | defer meta_type.base.deref(comp); |
| 1106 | 1256 | |
| 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 | 1258 | errdefer result_val.base.deref(comp); |
| 1109 | 1259 | |
| 1110 | 1260 | return result_val.cast(Type).?; |
| ... | ... | @@ -1120,13 +1270,6 @@ pub const Compilation = struct { |
| 1120 | 1270 | } |
| 1121 | 1271 | }; |
| 1122 | 1272 | |
| 1123 | fn 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 | ||
| 1130 | 1273 | fn parseVisibToken(tree: *ast.Tree, optional_token_index: ?ast.TokenIndex) Visib { |
| 1131 | 1274 | if (optional_token_index) |token_index| { |
| 1132 | 1275 | const token = tree.tokens.at(token_index); |
| ... | ... | @@ -1150,12 +1293,14 @@ async fn generateDecl(comp: *Compilation, decl: *Decl) !void { |
| 1150 | 1293 | } |
| 1151 | 1294 | |
| 1152 | 1295 | async fn generateDeclFn(comp: *Compilation, fn_decl: *Decl.Fn) !void { |
| 1296 | const tree_scope = fn_decl.base.tree_scope; | |
| 1297 | ||
| 1153 | 1298 | const body_node = fn_decl.fn_proto.body_node orelse return await (async generateDeclFnProto(comp, fn_decl) catch unreachable); |
| 1154 | 1299 | |
| 1155 | 1300 | const fndef_scope = try Scope.FnDef.create(comp, fn_decl.base.parent_scope); |
| 1156 | 1301 | defer fndef_scope.base.deref(comp); |
| 1157 | 1302 | |
| 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 | 1304 | defer fn_type.base.base.deref(comp); |
| 1160 | 1305 | |
| 1161 | 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 | 1313 | symbol_name_consumed = true; |
| 1169 | 1314 | |
| 1170 | 1315 | // Define local parameter variables |
| 1171 | const root_scope = fn_decl.base.findRootScope(); | |
| 1172 | 1316 | for (fn_type.key.data.Normal.params) |param, i| { |
| 1173 | 1317 | //AstNode *param_decl_node = get_param_decl_node(fn_table_entry, i); |
| 1174 | 1318 | const param_decl = @fieldParentPtr(ast.Node.ParamDecl, "base", fn_decl.fn_proto.params.at(i).*); |
| 1175 | 1319 | const name_token = param_decl.name_token orelse { |
| 1176 | try comp.addCompileError(root_scope, Span{ | |
| 1320 | try comp.addCompileError(tree_scope, Span{ | |
| 1177 | 1321 | .first = param_decl.firstToken(), |
| 1178 | 1322 | .last = param_decl.type_node.firstToken(), |
| 1179 | 1323 | }, "missing parameter name"); |
| 1180 | 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); | |
| 1183 | 1327 | |
| 1184 | 1328 | // if (is_noalias && get_codegen_ptr_type(param_type) == nullptr) { |
| 1185 | 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 | 1345 | } |
| 1202 | 1346 | |
| 1203 | 1347 | const analyzed_code = try await (async comp.genAndAnalyzeCode( |
| 1348 | tree_scope, | |
| 1204 | 1349 | fn_val.child_scope, |
| 1205 | 1350 | body_node, |
| 1206 | 1351 | fn_type.key.data.Normal.return_type, |
| ... | ... | @@ -1231,12 +1376,17 @@ fn getZigDir(allocator: *mem.Allocator) ![]u8 { |
| 1231 | 1376 | return os.getAppDataDir(allocator, "zig"); |
| 1232 | 1377 | } |
| 1233 | 1378 | |
| 1234 | async fn analyzeFnType(comp: *Compilation, scope: *Scope, fn_proto: *ast.Node.FnProto) !*Type.Fn { | |
| 1379 | async fn analyzeFnType( | |
| 1380 | comp: *Compilation, | |
| 1381 | tree_scope: *Scope.AstTree, | |
| 1382 | scope: *Scope, | |
| 1383 | fn_proto: *ast.Node.FnProto, | |
| 1384 | ) !*Type.Fn { | |
| 1235 | 1385 | const return_type_node = switch (fn_proto.return_type) { |
| 1236 | 1386 | ast.Node.FnProto.ReturnType.Explicit => |n| n, |
| 1237 | 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 | 1390 | return_type.base.deref(comp); |
| 1241 | 1391 | |
| 1242 | 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 | 1402 | var it = fn_proto.params.iterator(0); |
| 1253 | 1403 | while (it.next()) |param_node_ptr| { |
| 1254 | 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 | 1406 | errdefer param_type.base.deref(comp); |
| 1257 | 1407 | try params.append(Type.Fn.Param{ |
| 1258 | 1408 | .typ = param_type, |
| ... | ... | @@ -1289,7 +1439,12 @@ async fn analyzeFnType(comp: *Compilation, scope: *Scope, fn_proto: *ast.Node.Fn |
| 1289 | 1439 | } |
| 1290 | 1440 | |
| 1291 | 1441 | async 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 | 1448 | defer fn_type.base.base.deref(comp); |
| 1294 | 1449 | |
| 1295 | 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 | 1456 | fn_decl.value = Decl.Fn.Val{ .FnProto = fn_proto_val }; |
| 1302 | 1457 | symbol_name_consumed = true; |
| 1303 | 1458 | } |
| 1459 | ||
| 1460 | // TODO these are hacks which should probably be solved by the language | |
| 1461 | fn 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 | ||
| 1467 | async 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 | 17 | resolution: event.Future(Compilation.BuildError!void), |
| 18 | 18 | parent_scope: *Scope, |
| 19 | 19 | |
| 20 | // TODO when we destroy the decl, deref the tree scope | |
| 21 | tree_scope: *Scope.AstTree, | |
| 22 | ||
| 20 | 23 | pub const Table = std.HashMap([]const u8, *Decl, mem.hash_slice_u8, mem.eql_slice_u8); |
| 21 | 24 | |
| 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 | 30 | pub fn isExported(base: *const Decl, tree: *ast.Tree) bool { |
| 23 | 31 | switch (base.id) { |
| 24 | 32 | Id.Fn => { |
| ... | ... | @@ -95,4 +103,3 @@ pub const Decl = struct { |
| 95 | 103 | base: Decl, |
| 96 | 104 | }; |
| 97 | 105 | }; |
| 98 |
src-self-hosted/errmsg.zig+86-39| ... | ... | @@ -33,35 +33,48 @@ pub const Span = struct { |
| 33 | 33 | }; |
| 34 | 34 | |
| 35 | 35 | pub const Msg = struct { |
| 36 | span: Span, | |
| 37 | 36 | text: []u8, |
| 37 | realpath: []u8, | |
| 38 | 38 | data: Data, |
| 39 | 39 | |
| 40 | 40 | const Data = union(enum) { |
| 41 | Cli: Cli, | |
| 41 | 42 | PathAndTree: PathAndTree, |
| 42 | 43 | ScopeAndComp: ScopeAndComp, |
| 43 | 44 | }; |
| 44 | 45 | |
| 45 | 46 | const PathAndTree = struct { |
| 46 | realpath: []const u8, | |
| 47 | span: Span, | |
| 47 | 48 | tree: *ast.Tree, |
| 48 | 49 | allocator: *mem.Allocator, |
| 49 | 50 | }; |
| 50 | 51 | |
| 51 | 52 | const ScopeAndComp = struct { |
| 52 | root_scope: *Scope.Root, | |
| 53 | span: Span, | |
| 54 | tree_scope: *Scope.AstTree, | |
| 53 | 55 | compilation: *Compilation, |
| 54 | 56 | }; |
| 55 | 57 | |
| 58 | const Cli = struct { | |
| 59 | allocator: *mem.Allocator, | |
| 60 | }; | |
| 61 | ||
| 56 | 62 | pub fn destroy(self: *Msg) void { |
| 57 | 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 | 69 | Data.PathAndTree => |path_and_tree| { |
| 59 | 70 | path_and_tree.allocator.free(self.text); |
| 71 | path_and_tree.allocator.free(self.realpath); | |
| 60 | 72 | path_and_tree.allocator.destroy(self); |
| 61 | 73 | }, |
| 62 | 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 | 76 | scope_and_comp.compilation.gpa().free(self.text); |
| 77 | scope_and_comp.compilation.gpa().free(self.realpath); | |
| 65 | 78 | scope_and_comp.compilation.gpa().destroy(self); |
| 66 | 79 | }, |
| 67 | 80 | } |
| ... | ... | @@ -69,6 +82,7 @@ pub const Msg = struct { |
| 69 | 82 | |
| 70 | 83 | fn getAllocator(self: *const Msg) *mem.Allocator { |
| 71 | 84 | switch (self.data) { |
| 85 | Data.Cli => |cli| return cli.allocator, | |
| 72 | 86 | Data.PathAndTree => |path_and_tree| { |
| 73 | 87 | return path_and_tree.allocator; |
| 74 | 88 | }, |
| ... | ... | @@ -78,71 +92,93 @@ pub const Msg = struct { |
| 78 | 92 | } |
| 79 | 93 | } |
| 80 | 94 | |
| 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 | 95 | pub fn getTree(self: *const Msg) *ast.Tree { |
| 93 | 96 | switch (self.data) { |
| 97 | Data.Cli => unreachable, | |
| 94 | 98 | Data.PathAndTree => |path_and_tree| { |
| 95 | 99 | return path_and_tree.tree; |
| 96 | 100 | }, |
| 97 | 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 | } |
| 102 | 106 | |
| 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 | 115 | /// Takes ownership of text |
| 104 | /// References root_scope, and derefs when the msg is freed | |
| 105 | pub fn createFromScope(comp: *Compilation, root_scope: *Scope.Root, span: Span, text: []u8) !*Msg { | |
| 116 | /// References tree_scope, and derefs when the msg is freed | |
| 117 | pub fn createFromScope(comp: *Compilation, tree_scope: *Scope.AstTree, span: Span, text: []u8) !*Msg { | |
| 118 | const realpath = try mem.dupe(comp.gpa(), u8, tree_scope.root().realpath); | |
| 119 | errdefer comp.gpa().free(realpath); | |
| 120 | ||
| 106 | 121 | const msg = try comp.gpa().create(Msg{ |
| 107 | 122 | .text = text, |
| 108 | .span = span, | |
| 123 | .realpath = realpath, | |
| 109 | 124 | .data = Data{ |
| 110 | 125 | .ScopeAndComp = ScopeAndComp{ |
| 111 | .root_scope = root_scope, | |
| 126 | .tree_scope = tree_scope, | |
| 112 | 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 | 149 | return msg; |
| 118 | 150 | } |
| 119 | 151 | |
| 120 | 152 | pub fn createFromParseErrorAndScope( |
| 121 | 153 | comp: *Compilation, |
| 122 | root_scope: *Scope.Root, | |
| 154 | tree_scope: *Scope.AstTree, | |
| 123 | 155 | parse_error: *const ast.Error, |
| 124 | 156 | ) !*Msg { |
| 125 | 157 | const loc_token = parse_error.loc(); |
| 126 | 158 | var text_buf = try std.Buffer.initSize(comp.gpa(), 0); |
| 127 | 159 | defer text_buf.deinit(); |
| 128 | 160 | |
| 161 | const realpath_copy = try mem.dupe(comp.gpa(), u8, tree_scope.root().realpath); | |
| 162 | errdefer comp.gpa().free(realpath_copy); | |
| 163 | ||
| 129 | 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); | |
| 131 | 166 | |
| 132 | 167 | const msg = try comp.gpa().create(Msg{ |
| 133 | 168 | .text = undefined, |
| 134 | .span = Span{ | |
| 135 | .first = loc_token, | |
| 136 | .last = loc_token, | |
| 137 | }, | |
| 169 | .realpath = realpath_copy, | |
| 138 | 170 | .data = Data{ |
| 139 | 171 | .ScopeAndComp = ScopeAndComp{ |
| 140 | .root_scope = root_scope, | |
| 172 | .tree_scope = tree_scope, | |
| 141 | 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 | 182 | msg.text = text_buf.toOwnedSlice(); |
| 147 | 183 | return msg; |
| 148 | 184 | } |
| ... | ... | @@ -161,22 +197,25 @@ pub const Msg = struct { |
| 161 | 197 | var text_buf = try std.Buffer.initSize(allocator, 0); |
| 162 | 198 | defer text_buf.deinit(); |
| 163 | 199 | |
| 200 | const realpath_copy = try mem.dupe(allocator, u8, realpath); | |
| 201 | errdefer allocator.free(realpath_copy); | |
| 202 | ||
| 164 | 203 | var out_stream = &std.io.BufferOutStream.init(&text_buf).stream; |
| 165 | 204 | try parse_error.render(&tree.tokens, out_stream); |
| 166 | 205 | |
| 167 | 206 | const msg = try allocator.create(Msg{ |
| 168 | 207 | .text = undefined, |
| 208 | .realpath = realpath_copy, | |
| 169 | 209 | .data = Data{ |
| 170 | 210 | .PathAndTree = PathAndTree{ |
| 171 | 211 | .allocator = allocator, |
| 172 | .realpath = realpath, | |
| 173 | 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 | 220 | msg.text = text_buf.toOwnedSlice(); |
| 182 | 221 | errdefer allocator.destroy(msg); |
| ... | ... | @@ -185,20 +224,28 @@ pub const Msg = struct { |
| 185 | 224 | } |
| 186 | 225 | |
| 187 | 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 | 235 | const allocator = msg.getAllocator(); |
| 189 | const realpath = msg.getRealPath(); | |
| 190 | 236 | const tree = msg.getTree(); |
| 191 | 237 | |
| 192 | 238 | const cwd = try os.getCwd(allocator); |
| 193 | 239 | defer allocator.free(cwd); |
| 194 | 240 | |
| 195 | const relpath = try os.path.relative(allocator, cwd, realpath); | |
| 241 | const relpath = try os.path.relative(allocator, cwd, msg.realpath); | |
| 196 | 242 | defer allocator.free(relpath); |
| 197 | 243 | |
| 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(); | |
| 199 | 246 | |
| 200 | const first_token = tree.tokens.at(msg.span.first); | |
| 201 | const last_token = tree.tokens.at(msg.span.last); | |
| 247 | const first_token = tree.tokens.at(span.first); | |
| 248 | const last_token = tree.tokens.at(span.last); | |
| 202 | 249 | const start_loc = tree.tokenLocationPtr(0, first_token); |
| 203 | 250 | const end_loc = tree.tokenLocationPtr(first_token.end, last_token); |
| 204 | 251 | if (!color_on) { |
src-self-hosted/ir.zig+26-22| ... | ... | @@ -961,6 +961,7 @@ pub const Code = struct { |
| 961 | 961 | basic_block_list: std.ArrayList(*BasicBlock), |
| 962 | 962 | arena: std.heap.ArenaAllocator, |
| 963 | 963 | return_type: ?*Type, |
| 964 | tree_scope: *Scope.AstTree, | |
| 964 | 965 | |
| 965 | 966 | /// allocator is comp.gpa() |
| 966 | 967 | pub fn destroy(self: *Code, allocator: *Allocator) void { |
| ... | ... | @@ -990,14 +991,14 @@ pub const Code = struct { |
| 990 | 991 | return ret_value.val.KnownValue.getRef(); |
| 991 | 992 | } |
| 992 | 993 | try comp.addCompileError( |
| 993 | ret_value.scope.findRoot(), | |
| 994 | self.tree_scope, | |
| 994 | 995 | ret_value.span, |
| 995 | 996 | "unable to evaluate constant expression", |
| 996 | 997 | ); |
| 997 | 998 | return error.SemanticAnalysisFailed; |
| 998 | 999 | } else if (inst.hasSideEffects()) { |
| 999 | 1000 | try comp.addCompileError( |
| 1000 | inst.scope.findRoot(), | |
| 1001 | self.tree_scope, | |
| 1001 | 1002 | inst.span, |
| 1002 | 1003 | "unable to evaluate constant expression", |
| 1003 | 1004 | ); |
| ... | ... | @@ -1013,25 +1014,24 @@ pub const Builder = struct { |
| 1013 | 1014 | code: *Code, |
| 1014 | 1015 | current_basic_block: *BasicBlock, |
| 1015 | 1016 | next_debug_id: usize, |
| 1016 | root_scope: *Scope.Root, | |
| 1017 | 1017 | is_comptime: bool, |
| 1018 | 1018 | is_async: bool, |
| 1019 | 1019 | begin_scope: ?*Scope, |
| 1020 | 1020 | |
| 1021 | 1021 | pub const Error = Analyze.Error; |
| 1022 | 1022 | |
| 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 | 1024 | const code = try comp.gpa().create(Code{ |
| 1025 | 1025 | .basic_block_list = undefined, |
| 1026 | 1026 | .arena = std.heap.ArenaAllocator.init(comp.gpa()), |
| 1027 | 1027 | .return_type = null, |
| 1028 | .tree_scope = tree_scope, | |
| 1028 | 1029 | }); |
| 1029 | 1030 | code.basic_block_list = std.ArrayList(*BasicBlock).init(&code.arena.allocator); |
| 1030 | 1031 | errdefer code.destroy(comp.gpa()); |
| 1031 | 1032 | |
| 1032 | 1033 | return Builder{ |
| 1033 | 1034 | .comp = comp, |
| 1034 | .root_scope = root_scope, | |
| 1035 | 1035 | .current_basic_block = undefined, |
| 1036 | 1036 | .code = code, |
| 1037 | 1037 | .next_debug_id = 0, |
| ... | ... | @@ -1292,6 +1292,7 @@ pub const Builder = struct { |
| 1292 | 1292 | Scope.Id.FnDef => return false, |
| 1293 | 1293 | Scope.Id.Decls => unreachable, |
| 1294 | 1294 | Scope.Id.Root => unreachable, |
| 1295 | Scope.Id.AstTree => unreachable, | |
| 1295 | 1296 | Scope.Id.Block, |
| 1296 | 1297 | Scope.Id.Defer, |
| 1297 | 1298 | Scope.Id.DeferExpr, |
| ... | ... | @@ -1302,7 +1303,7 @@ pub const Builder = struct { |
| 1302 | 1303 | } |
| 1303 | 1304 | |
| 1304 | 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); | |
| 1306 | 1307 | |
| 1307 | 1308 | var base: u8 = undefined; |
| 1308 | 1309 | var rest: []const u8 = undefined; |
| ... | ... | @@ -1341,7 +1342,7 @@ pub const Builder = struct { |
| 1341 | 1342 | } |
| 1342 | 1343 | |
| 1343 | 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 | 1346 | const src_span = Span.token(str_lit.token); |
| 1346 | 1347 | |
| 1347 | 1348 | var bad_index: usize = undefined; |
| ... | ... | @@ -1349,7 +1350,7 @@ pub const Builder = struct { |
| 1349 | 1350 | error.OutOfMemory => return error.OutOfMemory, |
| 1350 | 1351 | error.InvalidCharacter => { |
| 1351 | 1352 | try irb.comp.addCompileError( |
| 1352 | irb.root_scope, | |
| 1353 | irb.code.tree_scope, | |
| 1353 | 1354 | src_span, |
| 1354 | 1355 | "invalid character in string literal: '{c}'", |
| 1355 | 1356 | str_token[bad_index], |
| ... | ... | @@ -1427,7 +1428,7 @@ pub const Builder = struct { |
| 1427 | 1428 | |
| 1428 | 1429 | if (statement_node.cast(ast.Node.Defer)) |defer_node| { |
| 1429 | 1430 | // 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 | 1432 | const kind = switch (defer_token.id) { |
| 1432 | 1433 | Token.Id.Keyword_defer => Scope.Defer.Kind.ScopeExit, |
| 1433 | 1434 | Token.Id.Keyword_errdefer => Scope.Defer.Kind.ErrorExit, |
| ... | ... | @@ -1513,7 +1514,7 @@ pub const Builder = struct { |
| 1513 | 1514 | const src_span = Span.token(control_flow_expr.ltoken); |
| 1514 | 1515 | if (scope.findFnDef() == null) { |
| 1515 | 1516 | try irb.comp.addCompileError( |
| 1516 | irb.root_scope, | |
| 1517 | irb.code.tree_scope, | |
| 1517 | 1518 | src_span, |
| 1518 | 1519 | "return expression outside function definition", |
| 1519 | 1520 | ); |
| ... | ... | @@ -1523,7 +1524,7 @@ pub const Builder = struct { |
| 1523 | 1524 | if (scope.findDeferExpr()) |scope_defer_expr| { |
| 1524 | 1525 | if (!scope_defer_expr.reported_err) { |
| 1525 | 1526 | try irb.comp.addCompileError( |
| 1526 | irb.root_scope, | |
| 1527 | irb.code.tree_scope, | |
| 1527 | 1528 | src_span, |
| 1528 | 1529 | "cannot return from defer expression", |
| 1529 | 1530 | ); |
| ... | ... | @@ -1599,7 +1600,7 @@ pub const Builder = struct { |
| 1599 | 1600 | |
| 1600 | 1601 | pub async fn genIdentifier(irb: *Builder, identifier: *ast.Node.Identifier, scope: *Scope, lval: LVal) !*Inst { |
| 1601 | 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); | |
| 1603 | 1604 | |
| 1604 | 1605 | //if (buf_eql_str(variable_name, "_") && lval == LValPtr) { |
| 1605 | 1606 | // IrInstructionConst *const_instruction = ir_build_instruction<IrInstructionConst>(irb, scope, node); |
| ... | ... | @@ -1622,7 +1623,7 @@ pub const Builder = struct { |
| 1622 | 1623 | } |
| 1623 | 1624 | } else |err| switch (err) { |
| 1624 | 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 | 1627 | return error.SemanticAnalysisFailed; |
| 1627 | 1628 | }, |
| 1628 | 1629 | error.OutOfMemory => return error.OutOfMemory, |
| ... | ... | @@ -1656,7 +1657,7 @@ pub const Builder = struct { |
| 1656 | 1657 | // TODO put a variable of same name with invalid type in global scope |
| 1657 | 1658 | // so that future references to this same name will find a variable with an invalid type |
| 1658 | 1659 | |
| 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 | 1661 | return error.SemanticAnalysisFailed; |
| 1661 | 1662 | } |
| 1662 | 1663 | |
| ... | ... | @@ -1689,6 +1690,7 @@ pub const Builder = struct { |
| 1689 | 1690 | => scope = scope.parent orelse break, |
| 1690 | 1691 | |
| 1691 | 1692 | Scope.Id.DeferExpr => unreachable, |
| 1693 | Scope.Id.AstTree => unreachable, | |
| 1692 | 1694 | } |
| 1693 | 1695 | } |
| 1694 | 1696 | return result; |
| ... | ... | @@ -1740,6 +1742,7 @@ pub const Builder = struct { |
| 1740 | 1742 | => scope = scope.parent orelse return is_noreturn, |
| 1741 | 1743 | |
| 1742 | 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 | 1932 | Scope.Id.Root => return Ident.NotFound, |
| 1930 | 1933 | Scope.Id.Decls => { |
| 1931 | 1934 | const decls = @fieldParentPtr(Scope.Decls, "base", s); |
| 1932 | const table = await (async decls.getTableReadOnly() catch unreachable); | |
| 1933 | if (table.get(name)) |entry| { | |
| 1935 | const locked_table = await (async decls.table.acquireRead() catch unreachable); | |
| 1936 | defer locked_table.release(); | |
| 1937 | if (locked_table.value.get(name)) |entry| { | |
| 1934 | 1938 | return Ident{ .Decl = entry.value }; |
| 1935 | 1939 | } |
| 1936 | 1940 | }, |
| ... | ... | @@ -1967,8 +1971,8 @@ const Analyze = struct { |
| 1967 | 1971 | OutOfMemory, |
| 1968 | 1972 | }; |
| 1969 | 1973 | |
| 1970 | pub fn init(comp: *Compilation, root_scope: *Scope.Root, explicit_return_type: ?*Type) !Analyze { | |
| 1971 | var irb = try Builder.init(comp, root_scope, null); | |
| 1974 | pub fn init(comp: *Compilation, tree_scope: *Scope.AstTree, explicit_return_type: ?*Type) !Analyze { | |
| 1975 | var irb = try Builder.init(comp, tree_scope, null); | |
| 1972 | 1976 | errdefer irb.abort(); |
| 1973 | 1977 | |
| 1974 | 1978 | return Analyze{ |
| ... | ... | @@ -2046,7 +2050,7 @@ const Analyze = struct { |
| 2046 | 2050 | } |
| 2047 | 2051 | |
| 2048 | 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 | } |
| 2051 | 2055 | |
| 2052 | 2056 | fn resolvePeerTypes(self: *Analyze, expected_type: ?*Type, peers: []const *Inst) Analyze.Error!*Type { |
| ... | ... | @@ -2534,9 +2538,10 @@ const Analyze = struct { |
| 2534 | 2538 | pub async fn gen( |
| 2535 | 2539 | comp: *Compilation, |
| 2536 | 2540 | body_node: *ast.Node, |
| 2541 | tree_scope: *Scope.AstTree, | |
| 2537 | 2542 | scope: *Scope, |
| 2538 | 2543 | ) !*Code { |
| 2539 | var irb = try Builder.init(comp, scope.findRoot(), scope); | |
| 2544 | var irb = try Builder.init(comp, tree_scope, scope); | |
| 2540 | 2545 | errdefer irb.abort(); |
| 2541 | 2546 | |
| 2542 | 2547 | const entry_block = try irb.createBasicBlock(scope, c"Entry"); |
| ... | ... | @@ -2554,9 +2559,8 @@ pub async fn gen( |
| 2554 | 2559 | |
| 2555 | 2560 | pub async fn analyze(comp: *Compilation, old_code: *Code, expected_type: ?*Type) !*Code { |
| 2556 | 2561 | const old_entry_bb = old_code.basic_block_list.at(0); |
| 2557 | const root_scope = old_entry_bb.scope.findRoot(); | |
| 2558 | 2562 | |
| 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 | 2564 | errdefer ira.abort(); |
| 2561 | 2565 | |
| 2562 | 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 | 143 | pub async fn findNative(self: *LibCInstallation, loop: *event.Loop) !void { |
| 144 | 144 | self.initEmpty(); |
| 145 | 145 | var group = event.Group(FindError!void).init(loop); |
| 146 | errdefer group.cancelAll(); | |
| 146 | errdefer group.deinit(); | |
| 147 | 147 | var windows_sdk: ?*c.ZigWindowsSDK = null; |
| 148 | 148 | errdefer if (windows_sdk) |sdk| c.zig_free_windows_sdk(@ptrCast(?[*]c.ZigWindowsSDK, sdk)); |
| 149 | 149 | |
| ... | ... | @@ -313,7 +313,7 @@ pub const LibCInstallation = struct { |
| 313 | 313 | }, |
| 314 | 314 | }; |
| 315 | 315 | var group = event.Group(FindError!void).init(loop); |
| 316 | errdefer group.cancelAll(); | |
| 316 | errdefer group.deinit(); | |
| 317 | 317 | for (dyn_tests) |*dyn_test| { |
| 318 | 318 | try group.call(testNativeDynamicLinker, self, loop, dyn_test); |
| 319 | 319 | } |
| ... | ... | @@ -341,7 +341,6 @@ pub const LibCInstallation = struct { |
| 341 | 341 | } |
| 342 | 342 | } |
| 343 | 343 | |
| 344 | ||
| 345 | 344 | async fn findNativeKernel32LibDir(self: *LibCInstallation, loop: *event.Loop, sdk: *c.ZigWindowsSDK) FindError!void { |
| 346 | 345 | var search_buf: [2]Search = undefined; |
| 347 | 346 | const searches = fillSearch(&search_buf, sdk); |
| ... | ... | @@ -450,7 +449,6 @@ fn fillSearch(search_buf: *[2]Search, sdk: *c.ZigWindowsSDK) []Search { |
| 450 | 449 | return search_buf[0..search_end]; |
| 451 | 450 | } |
| 452 | 451 | |
| 453 | ||
| 454 | 452 | fn fileExists(allocator: *std.mem.Allocator, path: []const u8) !bool { |
| 455 | 453 | if (std.os.File.access(allocator, path)) |_| { |
| 456 | 454 | return true; |
src-self-hosted/link.zig+2-2| ... | ... | @@ -61,7 +61,7 @@ pub async fn link(comp: *Compilation) !void { |
| 61 | 61 | ctx.libc = ctx.comp.override_libc orelse blk: { |
| 62 | 62 | switch (comp.target) { |
| 63 | 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 | 66 | else => return error.LibCRequiredButNotProvidedOrFound, |
| 67 | 67 | } |
| ... | ... | @@ -83,7 +83,7 @@ pub async fn link(comp: *Compilation) !void { |
| 83 | 83 | |
| 84 | 84 | { |
| 85 | 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 | 87 | defer held.release(); |
| 88 | 88 | |
| 89 | 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 | 14 | const introspect = @import("introspect.zig"); |
| 15 | 15 | const Args = arg.Args; |
| 16 | 16 | const Flag = arg.Flag; |
| 17 | const EventLoopLocal = @import("compilation.zig").EventLoopLocal; | |
| 17 | const ZigCompiler = @import("compilation.zig").ZigCompiler; | |
| 18 | 18 | const Compilation = @import("compilation.zig").Compilation; |
| 19 | 19 | const Target = @import("target.zig").Target; |
| 20 | 20 | const errmsg = @import("errmsg.zig"); |
| ... | ... | @@ -24,6 +24,8 @@ var stderr_file: os.File = undefined; |
| 24 | 24 | var stderr: *io.OutStream(io.FileOutStream.Error) = undefined; |
| 25 | 25 | var stdout: *io.OutStream(io.FileOutStream.Error) = undefined; |
| 26 | 26 | |
| 27 | const max_src_size = 2 * 1024 * 1024 * 1024; // 2 GiB | |
| 28 | ||
| 27 | 29 | const usage = |
| 28 | 30 | \\usage: zig [command] [options] |
| 29 | 31 | \\ |
| ... | ... | @@ -371,6 +373,16 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Co |
| 371 | 373 | os.exit(1); |
| 372 | 374 | } |
| 373 | 375 | |
| 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 | 386 | const zig_lib_dir = introspect.resolveZigLibDir(allocator) catch os.exit(1); |
| 375 | 387 | defer allocator.free(zig_lib_dir); |
| 376 | 388 | |
| ... | ... | @@ -380,11 +392,11 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Co |
| 380 | 392 | try loop.initMultiThreaded(allocator); |
| 381 | 393 | defer loop.deinit(); |
| 382 | 394 | |
| 383 | var event_loop_local = try EventLoopLocal.init(&loop); | |
| 384 | defer event_loop_local.deinit(); | |
| 395 | var zig_compiler = try ZigCompiler.init(&loop); | |
| 396 | defer zig_compiler.deinit(); | |
| 385 | 397 | |
| 386 | 398 | var comp = try Compilation.create( |
| 387 | &event_loop_local, | |
| 399 | &zig_compiler, | |
| 388 | 400 | root_name, |
| 389 | 401 | root_source_file, |
| 390 | 402 | Target.Native, |
| ... | ... | @@ -413,16 +425,6 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Co |
| 413 | 425 | comp.linker_script = flags.single("linker-script"); |
| 414 | 426 | comp.each_lib_rpath = flags.present("each-lib-rpath"); |
| 415 | 427 | |
| 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 | 428 | comp.clang_argv = clang_argv_buf.toSliceConst(); |
| 427 | 429 | |
| 428 | 430 | comp.strip = flags.present("strip"); |
| ... | ... | @@ -465,30 +467,34 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Co |
| 465 | 467 | comp.link_out_file = flags.single("output"); |
| 466 | 468 | comp.link_objects = link_objects; |
| 467 | 469 | |
| 468 | try comp.build(); | |
| 470 | comp.start(); | |
| 469 | 471 | const process_build_events_handle = try async<loop.allocator> processBuildEvents(comp, color); |
| 470 | 472 | defer cancel process_build_events_handle; |
| 471 | 473 | loop.run(); |
| 472 | 474 | } |
| 473 | 475 | |
| 474 | 476 | async fn processBuildEvents(comp: *Compilation, color: errmsg.Color) void { |
| 475 | // TODO directly awaiting async should guarantee memory allocation elision | |
| 476 | const build_event = await (async comp.events.get() catch unreachable); | |
| 477 | ||
| 478 | switch (build_event) { | |
| 479 | Compilation.Event.Ok => { | |
| 480 | return; | |
| 481 | }, | |
| 482 | Compilation.Event.Error => |err| { | |
| 483 | std.debug.warn("build failed: {}\n", @errorName(err)); | |
| 484 | os.exit(1); | |
| 485 | }, | |
| 486 | Compilation.Event.Fail => |msgs| { | |
| 487 | for (msgs) |msg| { | |
| 488 | defer msg.destroy(); | |
| 489 | msg.printToFile(&stderr_file, color) catch os.exit(1); | |
| 490 | } | |
| 491 | }, | |
| 477 | var count: usize = 0; | |
| 478 | while (true) { | |
| 479 | // TODO directly awaiting async should guarantee memory allocation elision | |
| 480 | const build_event = await (async comp.events.get() catch unreachable); | |
| 481 | count += 1; | |
| 482 | ||
| 483 | switch (build_event) { | |
| 484 | Compilation.Event.Ok => { | |
| 485 | stderr.print("Build {} succeeded\n", count) catch os.exit(1); | |
| 486 | }, | |
| 487 | Compilation.Event.Error => |err| { | |
| 488 | stderr.print("Build {} failed: {}\n", count, @errorName(err)) catch os.exit(1); | |
| 489 | }, | |
| 490 | Compilation.Event.Fail => |msgs| { | |
| 491 | stderr.print("Build {} compile errors:\n", count) catch os.exit(1); | |
| 492 | for (msgs) |msg| { | |
| 493 | defer msg.destroy(); | |
| 494 | msg.printToFile(&stderr_file, color) catch os.exit(1); | |
| 495 | } | |
| 496 | }, | |
| 497 | } | |
| 492 | 498 | } |
| 493 | 499 | } |
| 494 | 500 | |
| ... | ... | @@ -528,33 +534,12 @@ const args_fmt_spec = []Flag{ |
| 528 | 534 | }; |
| 529 | 535 | |
| 530 | 536 | const Fmt = struct { |
| 531 | seen: std.HashMap([]const u8, void, mem.hash_slice_u8, mem.eql_slice_u8), | |
| 532 | queue: std.LinkedList([]const u8), | |
| 537 | seen: event.Locked(SeenMap), | |
| 533 | 538 | any_error: bool, |
| 539 | color: errmsg.Color, | |
| 540 | loop: *event.Loop, | |
| 534 | 541 | |
| 535 | // file_path must outlive Fmt | |
| 536 | fn addToQueue(self: *Fmt, file_path: []const u8) !void { | |
| 537 | const new_node = try self.seen.allocator.create(std.LinkedList([]const u8).Node{ | |
| 538 | .prev = undefined, | |
| 539 | .next = undefined, | |
| 540 | .data = file_path, | |
| 541 | }); | |
| 542 | ||
| 543 | if (try self.seen.put(file_path, {})) |_| return; | |
| 544 | ||
| 545 | self.queue.append(new_node); | |
| 546 | } | |
| 547 | ||
| 548 | fn addDirToQueue(self: *Fmt, file_path: []const u8) !void { | |
| 549 | var dir = try std.os.Dir.open(self.seen.allocator, file_path); | |
| 550 | defer dir.close(); | |
| 551 | while (try dir.next()) |entry| { | |
| 552 | if (entry.kind == std.os.Dir.Entry.Kind.Directory or mem.endsWith(u8, entry.name, ".zig")) { | |
| 553 | const full_path = try os.path.join(self.seen.allocator, file_path, entry.name); | |
| 554 | try self.addToQueue(full_path); | |
| 555 | } | |
| 556 | } | |
| 557 | } | |
| 542 | const SeenMap = std.HashMap([]const u8, void, mem.hash_slice_u8, mem.eql_slice_u8); | |
| 558 | 543 | }; |
| 559 | 544 | |
| 560 | 545 | fn 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 | 572 | try loop.initMultiThreaded(allocator); |
| 588 | 573 | defer loop.deinit(); |
| 589 | 574 | |
| 590 | var event_loop_local = try EventLoopLocal.init(&loop); | |
| 591 | defer event_loop_local.deinit(); | |
| 575 | var zig_compiler = try ZigCompiler.init(&loop); | |
| 576 | defer zig_compiler.deinit(); | |
| 592 | 577 | |
| 593 | const handle = try async<loop.allocator> findLibCAsync(&event_loop_local); | |
| 578 | const handle = try async<loop.allocator> findLibCAsync(&zig_compiler); | |
| 594 | 579 | defer cancel handle; |
| 595 | 580 | |
| 596 | 581 | loop.run(); |
| 597 | 582 | } |
| 598 | 583 | |
| 599 | async fn findLibCAsync(event_loop_local: *EventLoopLocal) void { | |
| 600 | const libc = (await (async event_loop_local.getNativeLibC() catch unreachable)) catch |err| { | |
| 584 | async fn findLibCAsync(zig_compiler: *ZigCompiler) void { | |
| 585 | const libc = (await (async zig_compiler.getNativeLibC() catch unreachable)) catch |err| { | |
| 601 | 586 | stderr.print("unable to find libc: {}\n", @errorName(err)) catch os.exit(1); |
| 602 | 587 | os.exit(1); |
| 603 | 588 | }; |
| ... | ... | @@ -636,7 +621,7 @@ fn cmdFmt(allocator: *Allocator, args: []const []const u8) !void { |
| 636 | 621 | var stdin_file = try io.getStdIn(); |
| 637 | 622 | var stdin = io.FileInStream.init(&stdin_file); |
| 638 | 623 | |
| 639 | const source_code = try stdin.stream.readAllAlloc(allocator, @maxValue(usize)); | |
| 624 | const source_code = try stdin.stream.readAllAlloc(allocator, max_src_size); | |
| 640 | 625 | defer allocator.free(source_code); |
| 641 | 626 | |
| 642 | 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 | 650 | os.exit(1); |
| 666 | 651 | } |
| 667 | 652 | |
| 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 | ||
| 669 | async 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 | ||
| 678 | const 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 | ||
| 699 | async fn asyncFmtMain( | |
| 700 | loop: *event.Loop, | |
| 701 | flags: *const Args, | |
| 702 | color: errmsg.Color, | |
| 703 | ) FmtError!void { | |
| 704 | suspend { | |
| 705 | resume @handle(); | |
| 706 | } | |
| 668 | 707 | var fmt = Fmt{ |
| 669 | .seen = std.HashMap([]const u8, void, mem.hash_slice_u8, mem.eql_slice_u8).init(allocator), | |
| 670 | .queue = std.LinkedList([]const u8).init(), | |
| 708 | .seen = event.Locked(Fmt.SeenMap).init(loop, Fmt.SeenMap.init(loop.allocator)), | |
| 671 | 709 | .any_error = false, |
| 710 | .color = color, | |
| 711 | .loop = loop, | |
| 672 | 712 | }; |
| 673 | 713 | |
| 714 | var group = event.Group(FmtError!void).init(loop); | |
| 674 | 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 | } | |
| 677 | 723 | |
| 678 | while (fmt.queue.popFirst()) |node| { | |
| 679 | const file_path = node.data; | |
| 724 | async fn fmtPath(fmt: *Fmt, file_path_ref: []const u8) FmtError!void { | |
| 725 | const file_path = try std.mem.dupe(fmt.loop.allocator, u8, file_path_ref); | |
| 726 | defer fmt.loop.allocator.free(file_path); | |
| 680 | 727 | |
| 681 | var file = try os.File.openRead(allocator, file_path); | |
| 682 | defer file.close(); | |
| 728 | { | |
| 729 | const held = await (async fmt.seen.acquire() catch unreachable); | |
| 730 | defer held.release(); | |
| 683 | 731 | |
| 684 | const source_code = io.readFileAlloc(allocator, file_path) catch |err| switch (err) { | |
| 685 | error.IsDir => { | |
| 686 | try fmt.addDirToQueue(file_path); | |
| 687 | continue; | |
| 688 | }, | |
| 689 | else => { | |
| 690 | try stderr.print("unable to open '{}': {}\n", file_path, err); | |
| 691 | fmt.any_error = true; | |
| 692 | continue; | |
| 693 | }, | |
| 694 | }; | |
| 695 | defer allocator.free(source_code); | |
| 732 | if (try held.value.put(file_path, {})) |_| return; | |
| 733 | } | |
| 696 | 734 | |
| 697 | var tree = std.zig.parse(allocator, source_code) catch |err| { | |
| 698 | try stderr.print("error parsing file '{}': {}\n", file_path, err); | |
| 735 | const source_code = (await try async event.fs.readFile( | |
| 736 | fmt.loop, | |
| 737 | file_path, | |
| 738 | max_src_size, | |
| 739 | )) catch |err| switch (err) { | |
| 740 | error.IsDir => { | |
| 741 | // TODO make event based (and dir.next()) | |
| 742 | var dir = try std.os.Dir.open(fmt.loop.allocator, file_path); | |
| 743 | defer dir.close(); | |
| 744 | ||
| 745 | var group = event.Group(FmtError!void).init(fmt.loop); | |
| 746 | while (try dir.next()) |entry| { | |
| 747 | if (entry.kind == std.os.Dir.Entry.Kind.Directory or mem.endsWith(u8, entry.name, ".zig")) { | |
| 748 | const full_path = try os.path.join(fmt.loop.allocator, file_path, entry.name); | |
| 749 | try group.call(fmtPath, fmt, full_path); | |
| 750 | } | |
| 751 | } | |
| 752 | return await (async group.wait() catch unreachable); | |
| 753 | }, | |
| 754 | else => { | |
| 755 | // TODO lock stderr printing | |
| 756 | try stderr.print("unable to open '{}': {}\n", file_path, err); | |
| 699 | 757 | fmt.any_error = true; |
| 700 | continue; | |
| 701 | }; | |
| 702 | defer tree.deinit(); | |
| 703 | ||
| 704 | var error_it = tree.errors.iterator(0); | |
| 705 | while (error_it.next()) |parse_error| { | |
| 706 | const msg = try errmsg.Msg.createFromParseError(allocator, parse_error, &tree, file_path); | |
| 707 | defer msg.destroy(); | |
| 758 | return; | |
| 759 | }, | |
| 760 | }; | |
| 761 | defer fmt.loop.allocator.free(source_code); | |
| 708 | 762 | |
| 709 | try msg.printToFile(&stderr_file, color); | |
| 710 | } | |
| 711 | if (tree.errors.len != 0) { | |
| 712 | fmt.any_error = true; | |
| 713 | continue; | |
| 714 | } | |
| 763 | var tree = std.zig.parse(fmt.loop.allocator, source_code) catch |err| { | |
| 764 | try stderr.print("error parsing file '{}': {}\n", file_path, err); | |
| 765 | fmt.any_error = true; | |
| 766 | return; | |
| 767 | }; | |
| 768 | defer tree.deinit(); | |
| 715 | 769 | |
| 716 | const baf = try io.BufferedAtomicFile.create(allocator, file_path); | |
| 717 | defer baf.destroy(); | |
| 770 | var error_it = tree.errors.iterator(0); | |
| 771 | while (error_it.next()) |parse_error| { | |
| 772 | const msg = try errmsg.Msg.createFromParseError(fmt.loop.allocator, parse_error, &tree, file_path); | |
| 773 | defer fmt.loop.allocator.destroy(msg); | |
| 718 | 774 | |
| 719 | const anything_changed = try std.zig.render(allocator, baf.stream(), &tree); | |
| 720 | if (anything_changed) { | |
| 721 | try stderr.print("{}\n", file_path); | |
| 722 | try baf.finish(); | |
| 723 | } | |
| 775 | try msg.printToFile(&stderr_file, fmt.color); | |
| 776 | } | |
| 777 | if (tree.errors.len != 0) { | |
| 778 | fmt.any_error = true; | |
| 779 | return; | |
| 724 | 780 | } |
| 725 | 781 | |
| 726 | if (fmt.any_error) { | |
| 727 | os.exit(1); | |
| 782 | // TODO make this evented | |
| 783 | const baf = try io.BufferedAtomicFile.create(fmt.loop.allocator, file_path); | |
| 784 | defer baf.destroy(); | |
| 785 | ||
| 786 | const anything_changed = try std.zig.render(fmt.loop.allocator, baf.stream(), &tree); | |
| 787 | if (anything_changed) { | |
| 788 | try stderr.print("{}\n", file_path); | |
| 789 | try baf.finish(); | |
| 728 | 790 | } |
| 729 | 791 | } |
| 730 | 792 |
src-self-hosted/scope.zig+45-21| ... | ... | @@ -36,6 +36,7 @@ pub const Scope = struct { |
| 36 | 36 | Id.Defer => @fieldParentPtr(Defer, "base", base).destroy(comp), |
| 37 | 37 | Id.DeferExpr => @fieldParentPtr(DeferExpr, "base", base).destroy(comp), |
| 38 | 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 | 63 | Id.CompTime, |
| 63 | 64 | Id.Var, |
| 64 | 65 | => scope = scope.parent.?, |
| 66 | ||
| 67 | Id.AstTree => unreachable, | |
| 65 | 68 | } |
| 66 | 69 | } |
| 67 | 70 | } |
| ... | ... | @@ -82,6 +85,8 @@ pub const Scope = struct { |
| 82 | 85 | Id.Root, |
| 83 | 86 | Id.Var, |
| 84 | 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 | 102 | |
| 98 | 103 | pub const Id = enum { |
| 99 | 104 | Root, |
| 105 | AstTree, | |
| 100 | 106 | Decls, |
| 101 | 107 | Block, |
| 102 | 108 | FnDef, |
| ... | ... | @@ -108,13 +114,12 @@ pub const Scope = struct { |
| 108 | 114 | |
| 109 | 115 | pub const Root = struct { |
| 110 | 116 | base: Scope, |
| 111 | tree: *ast.Tree, | |
| 112 | 117 | realpath: []const u8, |
| 118 | decls: *Decls, | |
| 113 | 119 | |
| 114 | 120 | /// Creates a Root scope with 1 reference |
| 115 | 121 | /// Takes ownership of realpath |
| 116 | /// Takes ownership of tree, will deinit and destroy when done. | |
| 117 | pub fn create(comp: *Compilation, tree: *ast.Tree, realpath: []u8) !*Root { | |
| 122 | pub fn create(comp: *Compilation, realpath: []u8) !*Root { | |
| 118 | 123 | const self = try comp.gpa().createOne(Root); |
| 119 | 124 | self.* = Root{ |
| 120 | 125 | .base = Scope{ |
| ... | ... | @@ -122,41 +127,65 @@ pub const Scope = struct { |
| 122 | 127 | .parent = null, |
| 123 | 128 | .ref_count = std.atomic.Int(usize).init(1), |
| 124 | 129 | }, |
| 125 | .tree = tree, | |
| 126 | 130 | .realpath = realpath, |
| 131 | .decls = undefined, | |
| 127 | 132 | }; |
| 128 | ||
| 133 | errdefer comp.gpa().destroy(self); | |
| 134 | self.decls = try Decls.create(comp, &self.base); | |
| 129 | 135 | return self; |
| 130 | 136 | } |
| 131 | 137 | |
| 132 | 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 | 164 | comp.gpa().free(self.tree.source); |
| 134 | 165 | self.tree.deinit(); |
| 135 | 166 | comp.gpa().destroy(self.tree); |
| 136 | comp.gpa().free(self.realpath); | |
| 137 | 167 | comp.gpa().destroy(self); |
| 138 | 168 | } |
| 169 | ||
| 170 | pub fn root(self: *AstTree) *Root { | |
| 171 | return self.base.findRoot(); | |
| 172 | } | |
| 139 | 173 | }; |
| 140 | 174 | |
| 141 | 175 | pub const Decls = struct { |
| 142 | 176 | base: Scope, |
| 143 | 177 | |
| 144 | /// The lock must be respected for writing. However once name_future resolves, | |
| 145 | /// readers can freely access it. | |
| 146 | table: event.Locked(Decl.Table), | |
| 147 | ||
| 148 | /// Once this future is resolved, the table is complete and available for unlocked | |
| 149 | /// read-only access. It does not mean all the decls are resolved; it means only that | |
| 150 | /// the table has all the names. Each decl in the table has its own resolution state. | |
| 151 | name_future: event.Future(void), | |
| 178 | /// This table remains Write Locked when the names are incomplete or possibly outdated. | |
| 179 | /// So if a reader manages to grab a lock, it can be sure that the set of names is complete | |
| 180 | /// and correct. | |
| 181 | table: event.RwLocked(Decl.Table), | |
| 152 | 182 | |
| 153 | 183 | /// Creates a Decls scope with 1 reference |
| 154 | 184 | pub fn create(comp: *Compilation, parent: *Scope) !*Decls { |
| 155 | 185 | const self = try comp.gpa().createOne(Decls); |
| 156 | 186 | self.* = Decls{ |
| 157 | 187 | .base = undefined, |
| 158 | .table = event.Locked(Decl.Table).init(comp.loop, Decl.Table.init(comp.gpa())), | |
| 159 | .name_future = event.Future(void).init(comp.loop), | |
| 188 | .table = event.RwLocked(Decl.Table).init(comp.loop, Decl.Table.init(comp.gpa())), | |
| 160 | 189 | }; |
| 161 | 190 | self.base.init(Id.Decls, parent); |
| 162 | 191 | return self; |
| ... | ... | @@ -166,11 +195,6 @@ pub const Scope = struct { |
| 166 | 195 | self.table.deinit(); |
| 167 | 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 | }; |
| 175 | 199 | |
| 176 | 200 | pub const Block = struct { |
src-self-hosted/test.zig+16-15| ... | ... | @@ -6,7 +6,7 @@ const Compilation = @import("compilation.zig").Compilation; |
| 6 | 6 | const introspect = @import("introspect.zig"); |
| 7 | 7 | const assertOrPanic = std.debug.assertOrPanic; |
| 8 | 8 | const errmsg = @import("errmsg.zig"); |
| 9 | const EventLoopLocal = @import("compilation.zig").EventLoopLocal; | |
| 9 | const ZigCompiler = @import("compilation.zig").ZigCompiler; | |
| 10 | 10 | |
| 11 | 11 | var ctx: TestContext = undefined; |
| 12 | 12 | |
| ... | ... | @@ -25,7 +25,7 @@ const allocator = std.heap.c_allocator; |
| 25 | 25 | |
| 26 | 26 | pub const TestContext = struct { |
| 27 | 27 | loop: std.event.Loop, |
| 28 | event_loop_local: EventLoopLocal, | |
| 28 | zig_compiler: ZigCompiler, | |
| 29 | 29 | zig_lib_dir: []u8, |
| 30 | 30 | file_index: std.atomic.Int(usize), |
| 31 | 31 | group: std.event.Group(error!void), |
| ... | ... | @@ -37,20 +37,20 @@ pub const TestContext = struct { |
| 37 | 37 | self.* = TestContext{ |
| 38 | 38 | .any_err = {}, |
| 39 | 39 | .loop = undefined, |
| 40 | .event_loop_local = undefined, | |
| 40 | .zig_compiler = undefined, | |
| 41 | 41 | .zig_lib_dir = undefined, |
| 42 | 42 | .group = undefined, |
| 43 | 43 | .file_index = std.atomic.Int(usize).init(0), |
| 44 | 44 | }; |
| 45 | 45 | |
| 46 | try self.loop.initMultiThreaded(allocator); | |
| 46 | try self.loop.initSingleThreaded(allocator); | |
| 47 | 47 | errdefer self.loop.deinit(); |
| 48 | 48 | |
| 49 | self.event_loop_local = try EventLoopLocal.init(&self.loop); | |
| 50 | errdefer self.event_loop_local.deinit(); | |
| 49 | self.zig_compiler = try ZigCompiler.init(&self.loop); | |
| 50 | errdefer self.zig_compiler.deinit(); | |
| 51 | 51 | |
| 52 | 52 | self.group = std.event.Group(error!void).init(&self.loop); |
| 53 | errdefer self.group.cancelAll(); | |
| 53 | errdefer self.group.deinit(); | |
| 54 | 54 | |
| 55 | 55 | self.zig_lib_dir = try introspect.resolveZigLibDir(allocator); |
| 56 | 56 | errdefer allocator.free(self.zig_lib_dir); |
| ... | ... | @@ -62,7 +62,7 @@ pub const TestContext = struct { |
| 62 | 62 | fn deinit(self: *TestContext) void { |
| 63 | 63 | std.os.deleteTree(allocator, tmp_dir_name) catch {}; |
| 64 | 64 | allocator.free(self.zig_lib_dir); |
| 65 | self.event_loop_local.deinit(); | |
| 65 | self.zig_compiler.deinit(); | |
| 66 | 66 | self.loop.deinit(); |
| 67 | 67 | } |
| 68 | 68 | |
| ... | ... | @@ -97,7 +97,7 @@ pub const TestContext = struct { |
| 97 | 97 | try std.io.writeFile(allocator, file1_path, source); |
| 98 | 98 | |
| 99 | 99 | var comp = try Compilation.create( |
| 100 | &self.event_loop_local, | |
| 100 | &self.zig_compiler, | |
| 101 | 101 | "test", |
| 102 | 102 | file1_path, |
| 103 | 103 | Target.Native, |
| ... | ... | @@ -108,7 +108,7 @@ pub const TestContext = struct { |
| 108 | 108 | ); |
| 109 | 109 | errdefer comp.destroy(); |
| 110 | 110 | |
| 111 | try comp.build(); | |
| 111 | comp.start(); | |
| 112 | 112 | |
| 113 | 113 | try self.group.call(getModuleEvent, comp, source, path, line, column, msg); |
| 114 | 114 | } |
| ... | ... | @@ -131,7 +131,7 @@ pub const TestContext = struct { |
| 131 | 131 | try std.io.writeFile(allocator, file1_path, source); |
| 132 | 132 | |
| 133 | 133 | var comp = try Compilation.create( |
| 134 | &self.event_loop_local, | |
| 134 | &self.zig_compiler, | |
| 135 | 135 | "test", |
| 136 | 136 | file1_path, |
| 137 | 137 | Target.Native, |
| ... | ... | @@ -144,7 +144,7 @@ pub const TestContext = struct { |
| 144 | 144 | |
| 145 | 145 | _ = try comp.addLinkLib("c", true); |
| 146 | 146 | comp.link_out_file = output_file; |
| 147 | try comp.build(); | |
| 147 | comp.start(); | |
| 148 | 148 | |
| 149 | 149 | try self.group.call(getModuleEventSuccess, comp, output_file, expected_output); |
| 150 | 150 | } |
| ... | ... | @@ -212,9 +212,10 @@ pub const TestContext = struct { |
| 212 | 212 | Compilation.Event.Fail => |msgs| { |
| 213 | 213 | assertOrPanic(msgs.len != 0); |
| 214 | 214 | for (msgs) |msg| { |
| 215 | if (mem.endsWith(u8, msg.getRealPath(), path) and mem.eql(u8, msg.text, text)) { | |
| 216 | const first_token = msg.getTree().tokens.at(msg.span.first); | |
| 217 | const last_token = msg.getTree().tokens.at(msg.span.first); | |
| 215 | if (mem.endsWith(u8, msg.realpath, path) and mem.eql(u8, msg.text, text)) { | |
| 216 | const span = msg.getSpan(); | |
| 217 | const first_token = msg.getTree().tokens.at(span.first); | |
| 218 | const last_token = msg.getTree().tokens.at(span.first); | |
| 218 | 219 | const start_loc = msg.getTree().tokenLocationPtr(0, first_token); |
| 219 | 220 | if (start_loc.line + 1 == line and start_loc.column + 1 == column) { |
| 220 | 221 | return; |
src-self-hosted/type.zig+2-2| ... | ... | @@ -184,8 +184,8 @@ pub const Type = struct { |
| 184 | 184 | if (await (async base.abi_alignment.start() catch unreachable)) |ptr| return ptr.*; |
| 185 | 185 | |
| 186 | 186 | { |
| 187 | const held = try comp.event_loop_local.getAnyLlvmContext(); | |
| 188 | defer held.release(comp.event_loop_local); | |
| 187 | const held = try comp.zig_compiler.getAnyLlvmContext(); | |
| 188 | defer held.release(comp.zig_compiler); | |
| 189 | 189 | |
| 190 | 190 | const llvm_context = held.node.data; |
| 191 | 191 |
std/atomic/queue.zig+60-24| ... | ... | @@ -1,40 +1,38 @@ |
| 1 | const std = @import("../index.zig"); | |
| 1 | 2 | const builtin = @import("builtin"); |
| 2 | 3 | const AtomicOrder = builtin.AtomicOrder; |
| 3 | 4 | const AtomicRmwOp = builtin.AtomicRmwOp; |
| 5 | const assert = std.debug.assert; | |
| 4 | 6 | |
| 5 | 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. | |
| 7 | 9 | pub fn Queue(comptime T: type) type { |
| 8 | 10 | return struct { |
| 9 | 11 | head: ?*Node, |
| 10 | 12 | tail: ?*Node, |
| 11 | lock: u8, | |
| 13 | mutex: std.Mutex, | |
| 12 | 14 | |
| 13 | 15 | pub const Self = this; |
| 14 | ||
| 15 | pub const Node = struct { | |
| 16 | next: ?*Node, | |
| 17 | data: T, | |
| 18 | }; | |
| 16 | pub const Node = std.LinkedList(T).Node; | |
| 19 | 17 | |
| 20 | 18 | pub fn init() Self { |
| 21 | 19 | return Self{ |
| 22 | 20 | .head = null, |
| 23 | 21 | .tail = null, |
| 24 | .lock = 0, | |
| 22 | .mutex = std.Mutex.init(), | |
| 25 | 23 | }; |
| 26 | 24 | } |
| 27 | 25 | |
| 28 | 26 | pub fn put(self: *Self, node: *Node) void { |
| 29 | 27 | node.next = null; |
| 30 | 28 | |
| 31 | while (@atomicRmw(u8, &self.lock, builtin.AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst) != 0) {} | |
| 32 | defer assert(@atomicRmw(u8, &self.lock, builtin.AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst) == 1); | |
| 29 | const held = self.mutex.acquire(); | |
| 30 | defer held.release(); | |
| 33 | 31 | |
| 34 | const opt_tail = self.tail; | |
| 32 | node.prev = self.tail; | |
| 35 | 33 | self.tail = node; |
| 36 | if (opt_tail) |tail| { | |
| 37 | tail.next = node; | |
| 34 | if (node.prev) |prev_tail| { | |
| 35 | prev_tail.next = node; | |
| 38 | 36 | } else { |
| 39 | 37 | assert(self.head == null); |
| 40 | 38 | self.head = node; |
| ... | ... | @@ -42,18 +40,27 @@ pub fn Queue(comptime T: type) type { |
| 42 | 40 | } |
| 43 | 41 | |
| 44 | 42 | pub fn get(self: *Self) ?*Node { |
| 45 | while (@atomicRmw(u8, &self.lock, builtin.AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst) != 0) {} | |
| 46 | defer assert(@atomicRmw(u8, &self.lock, builtin.AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst) == 1); | |
| 43 | const held = self.mutex.acquire(); | |
| 44 | defer held.release(); | |
| 47 | 45 | |
| 48 | 46 | const head = self.head orelse return null; |
| 49 | 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 | 56 | return head; |
| 52 | 57 | } |
| 53 | 58 | |
| 54 | 59 | pub fn unget(self: *Self, node: *Node) void { |
| 55 | while (@atomicRmw(u8, &self.lock, builtin.AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst) != 0) {} | |
| 56 | defer assert(@atomicRmw(u8, &self.lock, builtin.AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst) == 1); | |
| 60 | node.prev = null; | |
| 61 | ||
| 62 | const held = self.mutex.acquire(); | |
| 63 | defer held.release(); | |
| 57 | 64 | |
| 58 | 65 | const opt_head = self.head; |
| 59 | 66 | self.head = node; |
| ... | ... | @@ -65,13 +72,39 @@ pub fn Queue(comptime T: type) type { |
| 65 | 72 | } |
| 66 | 73 | } |
| 67 | 74 | |
| 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 | 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 | } |
| 71 | 104 | |
| 72 | 105 | pub fn dump(self: *Self) void { |
| 73 | while (@atomicRmw(u8, &self.lock, builtin.AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst) != 0) {} | |
| 74 | defer assert(@atomicRmw(u8, &self.lock, builtin.AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst) == 1); | |
| 106 | const held = self.mutex.acquire(); | |
| 107 | defer held.release(); | |
| 75 | 108 | |
| 76 | 109 | std.debug.warn("head: "); |
| 77 | 110 | dumpRecursive(self.head, 0); |
| ... | ... | @@ -93,9 +126,6 @@ pub fn Queue(comptime T: type) type { |
| 93 | 126 | }; |
| 94 | 127 | } |
| 95 | 128 | |
| 96 | const std = @import("../index.zig"); | |
| 97 | const assert = std.debug.assert; | |
| 98 | ||
| 99 | 129 | const Context = struct { |
| 100 | 130 | allocator: *std.mem.Allocator, |
| 101 | 131 | queue: *Queue(i32), |
| ... | ... | @@ -169,6 +199,7 @@ fn startPuts(ctx: *Context) u8 { |
| 169 | 199 | std.os.time.sleep(0, 1); // let the os scheduler be our fuzz |
| 170 | 200 | const x = @bitCast(i32, r.random.scalar(u32)); |
| 171 | 201 | const node = ctx.allocator.create(Queue(i32).Node{ |
| 202 | .prev = undefined, | |
| 172 | 203 | .next = undefined, |
| 173 | 204 | .data = x, |
| 174 | 205 | }) catch unreachable; |
| ... | ... | @@ -198,12 +229,14 @@ test "std.atomic.Queue single-threaded" { |
| 198 | 229 | var node_0 = Queue(i32).Node{ |
| 199 | 230 | .data = 0, |
| 200 | 231 | .next = undefined, |
| 232 | .prev = undefined, | |
| 201 | 233 | }; |
| 202 | 234 | queue.put(&node_0); |
| 203 | 235 | |
| 204 | 236 | var node_1 = Queue(i32).Node{ |
| 205 | 237 | .data = 1, |
| 206 | 238 | .next = undefined, |
| 239 | .prev = undefined, | |
| 207 | 240 | }; |
| 208 | 241 | queue.put(&node_1); |
| 209 | 242 | |
| ... | ... | @@ -212,12 +245,14 @@ test "std.atomic.Queue single-threaded" { |
| 212 | 245 | var node_2 = Queue(i32).Node{ |
| 213 | 246 | .data = 2, |
| 214 | 247 | .next = undefined, |
| 248 | .prev = undefined, | |
| 215 | 249 | }; |
| 216 | 250 | queue.put(&node_2); |
| 217 | 251 | |
| 218 | 252 | var node_3 = Queue(i32).Node{ |
| 219 | 253 | .data = 3, |
| 220 | 254 | .next = undefined, |
| 255 | .prev = undefined, | |
| 221 | 256 | }; |
| 222 | 257 | queue.put(&node_3); |
| 223 | 258 | |
| ... | ... | @@ -228,6 +263,7 @@ test "std.atomic.Queue single-threaded" { |
| 228 | 263 | var node_4 = Queue(i32).Node{ |
| 229 | 264 | .data = 4, |
| 230 | 265 | .next = undefined, |
| 266 | .prev = undefined, | |
| 231 | 267 | }; |
| 232 | 268 | queue.put(&node_4); |
| 233 | 269 |
std/build.zig+61-52| ... | ... | @@ -424,60 +424,69 @@ pub const Builder = struct { |
| 424 | 424 | return mode; |
| 425 | 425 | } |
| 426 | 426 | |
| 427 | pub fn addUserInputOption(self: *Builder, name: []const u8, value: []const u8) bool { | |
| 428 | if (self.user_input_options.put(name, UserInputOption{ | |
| 429 | .name = name, | |
| 430 | .value = UserValue{ .Scalar = value }, | |
| 431 | .used = false, | |
| 432 | }) catch unreachable) |*prev_value| { | |
| 433 | // option already exists | |
| 434 | switch (prev_value.value) { | |
| 435 | UserValue.Scalar => |s| { | |
| 436 | // turn it into a list | |
| 437 | var list = ArrayList([]const u8).init(self.allocator); | |
| 438 | list.append(s) catch unreachable; | |
| 439 | list.append(value) catch unreachable; | |
| 440 | _ = self.user_input_options.put(name, UserInputOption{ | |
| 441 | .name = name, | |
| 442 | .value = UserValue{ .List = list }, | |
| 443 | .used = false, | |
| 444 | }) catch unreachable; | |
| 445 | }, | |
| 446 | UserValue.List => |*list| { | |
| 447 | // append to the list | |
| 448 | list.append(value) catch unreachable; | |
| 449 | _ = self.user_input_options.put(name, UserInputOption{ | |
| 450 | .name = name, | |
| 451 | .value = UserValue{ .List = list.* }, | |
| 452 | .used = false, | |
| 453 | }) catch unreachable; | |
| 454 | }, | |
| 455 | UserValue.Flag => { | |
| 456 | warn("Option '-D{}={}' conflicts with flag '-D{}'.\n", name, value, name); | |
| 457 | return true; | |
| 458 | }, | |
| 459 | } | |
| 427 | pub fn addUserInputOption(self: *Builder, name: []const u8, value: []const u8) !bool { | |
| 428 | const gop = try self.user_input_options.getOrPut(name); | |
| 429 | if (!gop.found_existing) { | |
| 430 | gop.kv.value = UserInputOption{ | |
| 431 | .name = name, | |
| 432 | .value = UserValue{ .Scalar = value }, | |
| 433 | .used = false, | |
| 434 | }; | |
| 435 | return false; | |
| 436 | } | |
| 437 | ||
| 438 | // option already exists | |
| 439 | switch (gop.kv.value.value) { | |
| 440 | UserValue.Scalar => |s| { | |
| 441 | // turn it into a list | |
| 442 | var list = ArrayList([]const u8).init(self.allocator); | |
| 443 | list.append(s) catch unreachable; | |
| 444 | list.append(value) catch unreachable; | |
| 445 | _ = self.user_input_options.put(name, UserInputOption{ | |
| 446 | .name = name, | |
| 447 | .value = UserValue{ .List = list }, | |
| 448 | .used = false, | |
| 449 | }) catch unreachable; | |
| 450 | }, | |
| 451 | UserValue.List => |*list| { | |
| 452 | // append to the list | |
| 453 | list.append(value) catch unreachable; | |
| 454 | _ = self.user_input_options.put(name, UserInputOption{ | |
| 455 | .name = name, | |
| 456 | .value = UserValue{ .List = list.* }, | |
| 457 | .used = false, | |
| 458 | }) catch unreachable; | |
| 459 | }, | |
| 460 | UserValue.Flag => { | |
| 461 | warn("Option '-D{}={}' conflicts with flag '-D{}'.\n", name, value, name); | |
| 462 | return true; | |
| 463 | }, | |
| 460 | 464 | } |
| 461 | 465 | return false; |
| 462 | 466 | } |
| 463 | 467 | |
| 464 | pub fn addUserInputFlag(self: *Builder, name: []const u8) bool { | |
| 465 | if (self.user_input_options.put(name, UserInputOption{ | |
| 466 | .name = name, | |
| 467 | .value = UserValue{ .Flag = {} }, | |
| 468 | .used = false, | |
| 469 | }) catch unreachable) |*prev_value| { | |
| 470 | switch (prev_value.value) { | |
| 471 | UserValue.Scalar => |s| { | |
| 472 | warn("Flag '-D{}' conflicts with option '-D{}={}'.\n", name, name, s); | |
| 473 | return true; | |
| 474 | }, | |
| 475 | UserValue.List => { | |
| 476 | warn("Flag '-D{}' conflicts with multiple options of the same name.\n", name); | |
| 477 | return true; | |
| 478 | }, | |
| 479 | UserValue.Flag => {}, | |
| 480 | } | |
| 468 | pub fn addUserInputFlag(self: *Builder, name: []const u8) !bool { | |
| 469 | const gop = try self.user_input_options.getOrPut(name); | |
| 470 | if (!gop.found_existing) { | |
| 471 | gop.kv.value = UserInputOption{ | |
| 472 | .name = name, | |
| 473 | .value = UserValue{ .Flag = {} }, | |
| 474 | .used = false, | |
| 475 | }; | |
| 476 | return false; | |
| 477 | } | |
| 478 | ||
| 479 | // option already exists | |
| 480 | switch (gop.kv.value.value) { | |
| 481 | UserValue.Scalar => |s| { | |
| 482 | warn("Flag '-D{}' conflicts with option '-D{}={}'.\n", name, name, s); | |
| 483 | return true; | |
| 484 | }, | |
| 485 | UserValue.List => { | |
| 486 | warn("Flag '-D{}' conflicts with multiple options of the same name.\n", name); | |
| 487 | return true; | |
| 488 | }, | |
| 489 | UserValue.Flag => {}, | |
| 481 | 490 | } |
| 482 | 491 | return false; |
| 483 | 492 | } |
| ... | ... | @@ -603,10 +612,10 @@ pub const Builder = struct { |
| 603 | 612 | } |
| 604 | 613 | |
| 605 | 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 | } |
| 608 | 617 | |
| 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 | 619 | if (self.verbose) { |
| 611 | 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 | 30 | pub extern "c" fn sysctlbyname(name: [*]const u8, oldp: ?*c_void, oldlenp: ?*usize, newp: ?*c_void, newlen: usize) c_int; |
| 31 | 31 | pub extern "c" fn sysctlnametomib(name: [*]const u8, mibp: ?*c_int, sizep: ?*usize) c_int; |
| 32 | 32 | |
| 33 | pub extern "c" fn bind(socket: c_int, address: ?*const sockaddr, address_len: socklen_t) c_int; | |
| 34 | pub extern "c" fn socket(domain: c_int, type: c_int, protocol: c_int) c_int; | |
| 35 | ||
| 33 | 36 | pub use @import("../os/darwin/errno.zig"); |
| 34 | 37 | |
| 35 | 38 | pub const _errno = __error; |
| 36 | 39 | |
| 40 | pub const in_port_t = u16; | |
| 41 | pub const sa_family_t = u8; | |
| 42 | pub const socklen_t = u32; | |
| 43 | pub const sockaddr = extern union { | |
| 44 | in: sockaddr_in, | |
| 45 | in6: sockaddr_in6, | |
| 46 | }; | |
| 47 | pub 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 | }; | |
| 54 | pub 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 | ||
| 37 | 63 | pub const timeval = extern struct { |
| 38 | 64 | tv_sec: isize, |
| 39 | 65 | tv_usec: isize, |
| ... | ... | @@ -98,14 +124,6 @@ pub const dirent = extern struct { |
| 98 | 124 | d_name: u8, // field address is address of first byte of name |
| 99 | 125 | }; |
| 100 | 126 | |
| 101 | pub const sockaddr = extern struct { | |
| 102 | sa_len: u8, | |
| 103 | sa_family: sa_family_t, | |
| 104 | sa_data: [14]u8, | |
| 105 | }; | |
| 106 | ||
| 107 | pub const sa_family_t = u8; | |
| 108 | ||
| 109 | 127 | pub const pthread_attr_t = extern struct { |
| 110 | 128 | __sig: c_long, |
| 111 | 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 | 21 | pub extern "c" fn open(path: [*]const u8, oflag: c_int, ...) c_int; |
| 22 | 22 | pub extern "c" fn raise(sig: c_int) c_int; |
| 23 | 23 | pub extern "c" fn read(fd: c_int, buf: *c_void, nbyte: usize) isize; |
| 24 | pub extern "c" fn pread(fd: c_int, buf: *c_void, nbyte: usize, offset: u64) isize; | |
| 24 | 25 | pub extern "c" fn stat(noalias path: [*]const u8, noalias buf: *Stat) c_int; |
| 25 | 26 | pub extern "c" fn write(fd: c_int, buf: *const c_void, nbyte: usize) isize; |
| 27 | pub extern "c" fn pwrite(fd: c_int, buf: *const c_void, nbyte: usize, offset: u64) isize; | |
| 26 | 28 | pub extern "c" fn mmap(addr: ?*c_void, len: usize, prot: c_int, flags: c_int, fd: c_int, offset: isize) ?*c_void; |
| 27 | 29 | pub extern "c" fn munmap(addr: *c_void, len: usize) c_int; |
| 28 | 30 | pub 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 | 23 | var stderr_file: os.File = undefined; |
| 24 | 24 | var stderr_file_out_stream: io.FileOutStream = undefined; |
| 25 | 25 | var stderr_stream: ?*io.OutStream(io.FileOutStream.Error) = null; |
| 26 | var stderr_mutex = std.Mutex.init(); | |
| 26 | 27 | pub fn warn(comptime fmt: []const u8, args: ...) void { |
| 28 | const held = stderr_mutex.acquire(); | |
| 29 | defer held.release(); | |
| 27 | 30 | const stderr = getStderrStream() catch return; |
| 28 | 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 | 675 | |
| 673 | 676 | const ParseFormValueError = error{ |
| 674 | 677 | EndOfStream, |
| 675 | Io, | |
| 676 | BadFd, | |
| 677 | Unexpected, | |
| 678 | 678 | InvalidDebugInfo, |
| 679 | 679 | EndOfFile, |
| 680 | IsDir, | |
| 681 | 680 | OutOfMemory, |
| 682 | }; | |
| 681 | } || std.os.File.ReadError; | |
| 683 | 682 | |
| 684 | 683 | fn parseFormValue(allocator: *mem.Allocator, in_stream: var, form_id: u64, is_64: bool) ParseFormValueError!FormValue { |
| 685 | 684 | return switch (form_id) { |
std/event.zig+14-8| ... | ... | @@ -1,17 +1,23 @@ |
| 1 | pub const Channel = @import("event/channel.zig").Channel; | |
| 2 | pub const Future = @import("event/future.zig").Future; | |
| 3 | pub const Group = @import("event/group.zig").Group; | |
| 4 | pub const Lock = @import("event/lock.zig").Lock; | |
| 1 | 5 | pub const Locked = @import("event/locked.zig").Locked; |
| 6 | pub const RwLock = @import("event/rwlock.zig").RwLock; | |
| 7 | pub const RwLocked = @import("event/rwlocked.zig").RwLocked; | |
| 2 | 8 | pub const Loop = @import("event/loop.zig").Loop; |
| 3 | pub const Lock = @import("event/lock.zig").Lock; | |
| 9 | pub const fs = @import("event/fs.zig"); | |
| 4 | 10 | pub const tcp = @import("event/tcp.zig"); |
| 5 | pub const Channel = @import("event/channel.zig").Channel; | |
| 6 | pub const Group = @import("event/group.zig").Group; | |
| 7 | pub const Future = @import("event/future.zig").Future; | |
| 8 | 11 | |
| 9 | 12 | test "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 | 18 | _ = @import("event/locked.zig"); |
| 19 | _ = @import("event/rwlock.zig"); | |
| 20 | _ = @import("event/rwlocked.zig"); | |
| 11 | 21 | _ = @import("event/loop.zig"); |
| 12 | _ = @import("event/lock.zig"); | |
| 13 | 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 | 5 | const AtomicOrder = builtin.AtomicOrder; |
| 6 | 6 | const Loop = std.event.Loop; |
| 7 | 7 | |
| 8 | /// many producer, many consumer, thread-safe, lock-free, runtime configurable buffer size | |
| 8 | /// many producer, many consumer, thread-safe, runtime configurable buffer size | |
| 9 | 9 | /// when buffer is empty, consumers suspend and are resumed by producers |
| 10 | 10 | /// when buffer is full, producers suspend and are resumed by consumers |
| 11 | 11 | pub fn Channel(comptime T: type) type { |
| ... | ... | @@ -13,6 +13,7 @@ pub fn Channel(comptime T: type) type { |
| 13 | 13 | loop: *Loop, |
| 14 | 14 | |
| 15 | 15 | getters: std.atomic.Queue(GetNode), |
| 16 | or_null_queue: std.atomic.Queue(*std.atomic.Queue(GetNode).Node), | |
| 16 | 17 | putters: std.atomic.Queue(PutNode), |
| 17 | 18 | get_count: usize, |
| 18 | 19 | put_count: usize, |
| ... | ... | @@ -26,8 +27,22 @@ pub fn Channel(comptime T: type) type { |
| 26 | 27 | |
| 27 | 28 | const SelfChannel = this; |
| 28 | 29 | const GetNode = struct { |
| 29 | ptr: *T, | |
| 30 | 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 | 47 | const PutNode = struct { |
| 33 | 48 | data: T, |
| ... | ... | @@ -48,6 +63,7 @@ pub fn Channel(comptime T: type) type { |
| 48 | 63 | .need_dispatch = 0, |
| 49 | 64 | .getters = std.atomic.Queue(GetNode).init(), |
| 50 | 65 | .putters = std.atomic.Queue(PutNode).init(), |
| 66 | .or_null_queue = std.atomic.Queue(*std.atomic.Queue(GetNode).Node).init(), | |
| 51 | 67 | .get_count = 0, |
| 52 | 68 | .put_count = 0, |
| 53 | 69 | }); |
| ... | ... | @@ -71,18 +87,29 @@ pub fn Channel(comptime T: type) type { |
| 71 | 87 | /// puts a data item in the channel. The promise completes when the value has been added to the |
| 72 | 88 | /// buffer, or in the case of a zero size buffer, when the item has been retrieved by a getter. |
| 73 | 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 | 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 | 113 | self.putters.put(&queue_node); |
| 87 | 114 | _ = @atomicRmw(usize, &self.put_count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst); |
| 88 | 115 | |
| ... | ... | @@ -93,23 +120,95 @@ pub fn Channel(comptime T: type) type { |
| 93 | 120 | /// await this function to get an item from the channel. If the buffer is empty, the promise will |
| 94 | 121 | /// complete when the next item is put in the channel. |
| 95 | 122 | pub async fn get(self: *SelfChannel) T { |
| 123 | // TODO fix this workaround | |
| 124 | suspend { | |
| 125 | resume @handle(); | |
| 126 | } | |
| 127 | ||
| 96 | 128 | // TODO integrate this function with named return values |
| 97 | 129 | // so we can get rid of this extra result copy |
| 98 | 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 | 175 | suspend { |
| 100 | var my_tick_node = Loop.NextTickNode{ | |
| 101 | .next = undefined, | |
| 102 | .data = @handle(), | |
| 103 | }; | |
| 104 | var queue_node = std.atomic.Queue(GetNode).Node{ | |
| 105 | .data = GetNode{ | |
| 176 | resume @handle(); | |
| 177 | } | |
| 178 | ||
| 179 | // TODO integrate this function with named return values | |
| 180 | // so we can get rid of this extra result copy | |
| 181 | var result: ?T = null; | |
| 182 | var my_tick_node = Loop.NextTickNode.init(@handle()); | |
| 183 | var or_null_node = std.atomic.Queue(*std.atomic.Queue(GetNode).Node).Node.init(undefined); | |
| 184 | var queue_node = std.atomic.Queue(GetNode).Node.init(GetNode{ | |
| 185 | .tick_node = &my_tick_node, | |
| 186 | .data = GetNode.Data{ | |
| 187 | .OrNull = GetNode.OrNull{ | |
| 106 | 188 | .ptr = &result, |
| 107 | .tick_node = &my_tick_node, | |
| 189 | .or_null = &or_null_node, | |
| 108 | 190 | }, |
| 109 | .next = undefined, | |
| 110 | }; | |
| 191 | }, | |
| 192 | }); | |
| 193 | or_null_node.data = &queue_node; | |
| 194 | ||
| 195 | // TODO test canceling getOrNull | |
| 196 | errdefer { | |
| 197 | _ = self.or_null_queue.remove(&or_null_node); | |
| 198 | _ = @atomicRmw(usize, &self.get_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst); | |
| 199 | const need_dispatch = !self.getters.remove(&queue_node); | |
| 200 | self.loop.cancelOnNextTick(&my_tick_node); | |
| 201 | if (need_dispatch) { | |
| 202 | // oops we made the get_count incorrect for a period of time. fix by dispatching. | |
| 203 | _ = @atomicRmw(usize, &self.get_count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst); | |
| 204 | self.dispatch(); | |
| 205 | } | |
| 206 | } | |
| 207 | ||
| 208 | suspend { | |
| 111 | 209 | self.getters.put(&queue_node); |
| 112 | 210 | _ = @atomicRmw(usize, &self.get_count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst); |
| 211 | self.or_null_queue.put(&or_null_node); | |
| 113 | 212 | |
| 114 | 213 | self.dispatch(); |
| 115 | 214 | } |
| ... | ... | @@ -139,7 +238,15 @@ pub fn Channel(comptime T: type) type { |
| 139 | 238 | if (get_count == 0) break :one_dispatch; |
| 140 | 239 | |
| 141 | 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 | 250 | self.loop.onNextTick(get_node.tick_node); |
| 144 | 251 | self.buffer_len -= 1; |
| 145 | 252 | |
| ... | ... | @@ -151,7 +258,15 @@ pub fn Channel(comptime T: type) type { |
| 151 | 258 | const get_node = &self.getters.get().?.data; |
| 152 | 259 | const put_node = &self.putters.get().?.data; |
| 153 | 260 | |
| 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 | 270 | self.loop.onNextTick(get_node.tick_node); |
| 156 | 271 | self.loop.onNextTick(put_node.tick_node); |
| 157 | 272 | |
| ... | ... | @@ -176,6 +291,16 @@ pub fn Channel(comptime T: type) type { |
| 176 | 291 | _ = @atomicRmw(usize, &self.get_count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst); |
| 177 | 292 | _ = @atomicRmw(usize, &self.put_count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst); |
| 178 | 293 | |
| 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 | 304 | // clear need-dispatch flag |
| 180 | 305 | const need_dispatch = @atomicRmw(u8, &self.need_dispatch, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst); |
| 181 | 306 | if (need_dispatch != 0) continue; |
| ... | ... | @@ -226,6 +351,15 @@ async fn testChannelGetter(loop: *Loop, channel: *Channel(i32)) void { |
| 226 | 351 | const value2_promise = try async channel.get(); |
| 227 | 352 | const value2 = await value2_promise; |
| 228 | 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 | } |
| 230 | 364 | |
| 231 | 365 | async fn testChannelPutter(channel: *Channel(i32)) void { |
| ... | ... | @@ -233,3 +367,6 @@ async fn testChannelPutter(channel: *Channel(i32)) void { |
| 233 | 367 | await (async channel.put(4567) catch @panic("out of memory")); |
| 234 | 368 | } |
| 235 | 369 | |
| 370 | async 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 @@ |
| 1 | const builtin = @import("builtin"); | |
| 2 | const std = @import("../index.zig"); | |
| 3 | const event = std.event; | |
| 4 | const assert = std.debug.assert; | |
| 5 | const os = std.os; | |
| 6 | const mem = std.mem; | |
| 7 | const posix = os.posix; | |
| 8 | const windows = os.windows; | |
| 9 | const Loop = event.Loop; | |
| 10 | ||
| 11 | pub const RequestNode = std.atomic.Queue(Request).Node; | |
| 12 | ||
| 13 | pub 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. | |
| 76 | pub 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. | |
| 88 | pub 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 | ||
| 103 | pub 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. | |
| 152 | pub 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. | |
| 200 | pub 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 | ||
| 212 | pub 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 | ||
| 239 | pub 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. | |
| 285 | pub 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 | ||
| 332 | pub 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 | ||
| 377 | pub 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. | |
| 399 | pub 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. | |
| 404 | pub 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. | |
| 426 | pub 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`. | |
| 456 | pub 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 | |
| 578 | pub 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. | |
| 583 | pub 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 | ||
| 594 | async 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 | ||
| 608 | async 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. | |
| 651 | pub 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 | ||
| 679 | pub const WatchEventId = enum { | |
| 680 | CloseWrite, | |
| 681 | Delete, | |
| 682 | }; | |
| 683 | ||
| 684 | pub const WatchEventError = error{ | |
| 685 | UserResourceLimitReached, | |
| 686 | SystemResources, | |
| 687 | AccessDenied, | |
| 688 | Unexpected, // TODO remove this possibility | |
| 689 | }; | |
| 690 | ||
| 691 | pub 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 | ||
| 1288 | const test_tmp_dir = "std_event_fs_test"; | |
| 1289 | ||
| 1290 | test "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 | ||
| 1312 | async fn testFsWatchCantFail(loop: *Loop, result: *(error!void)) void { | |
| 1313 | result.* = await async testFsWatch(loop) catch unreachable; | |
| 1314 | } | |
| 1315 | ||
| 1316 | async 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 | 29 | }; |
| 30 | 30 | } |
| 31 | 31 | |
| 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 | 43 | /// Add a promise to the group. Thread-safe. |
| 33 | 44 | pub fn add(self: *Self, handle: promise->ReturnType) (error{OutOfMemory}!void) { |
| 34 | 45 | const node = try self.lock.loop.allocator.create(Stack.Node{ |
| ... | ... | @@ -88,7 +99,7 @@ pub fn Group(comptime ReturnType: type) type { |
| 88 | 99 | await node.data; |
| 89 | 100 | } else { |
| 90 | 101 | (await node.data) catch |err| { |
| 91 | self.cancelAll(); | |
| 102 | self.deinit(); | |
| 92 | 103 | return err; |
| 93 | 104 | }; |
| 94 | 105 | } |
| ... | ... | @@ -100,25 +111,12 @@ pub fn Group(comptime ReturnType: type) type { |
| 100 | 111 | await handle; |
| 101 | 112 | } else { |
| 102 | 113 | (await handle) catch |err| { |
| 103 | self.cancelAll(); | |
| 114 | self.deinit(); | |
| 104 | 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 | } |
| 124 | 122 |
std/event/lock.zig+10-5| ... | ... | @@ -9,6 +9,7 @@ const Loop = std.event.Loop; |
| 9 | 9 | /// Thread-safe async/await lock. |
| 10 | 10 | /// Does not make any syscalls - coroutines which are waiting for the lock are suspended, and |
| 11 | 11 | /// are resumed when the lock is released, in order. |
| 12 | /// Allows only one actor to hold the lock. | |
| 12 | 13 | pub const Lock = struct { |
| 13 | 14 | loop: *Loop, |
| 14 | 15 | shared_bit: u8, // TODO make this a bool |
| ... | ... | @@ -90,13 +91,14 @@ pub const Lock = struct { |
| 90 | 91 | } |
| 91 | 92 | |
| 92 | 93 | pub async fn acquire(self: *Lock) Held { |
| 94 | // TODO explicitly put this memory in the coroutine frame #1194 | |
| 93 | 95 | suspend { |
| 94 | // TODO explicitly put this memory in the coroutine frame #1194 | |
| 95 | var my_tick_node = Loop.NextTickNode{ | |
| 96 | .data = @handle(), | |
| 97 | .next = undefined, | |
| 98 | }; | |
| 96 | resume @handle(); | |
| 97 | } | |
| 98 | var my_tick_node = Loop.NextTickNode.init(@handle()); | |
| 99 | 99 | |
| 100 | errdefer _ = self.queue.remove(&my_tick_node); // TODO test canceling an acquire | |
| 101 | suspend { | |
| 100 | 102 | self.queue.put(&my_tick_node); |
| 101 | 103 | |
| 102 | 104 | // 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 | 148 | } |
| 147 | 149 | const handle1 = async lockRunner(lock) catch @panic("out of memory"); |
| 148 | 150 | var tick_node1 = Loop.NextTickNode{ |
| 151 | .prev = undefined, | |
| 149 | 152 | .next = undefined, |
| 150 | 153 | .data = handle1, |
| 151 | 154 | }; |
| ... | ... | @@ -153,6 +156,7 @@ async fn testLock(loop: *Loop, lock: *Lock) void { |
| 153 | 156 | |
| 154 | 157 | const handle2 = async lockRunner(lock) catch @panic("out of memory"); |
| 155 | 158 | var tick_node2 = Loop.NextTickNode{ |
| 159 | .prev = undefined, | |
| 156 | 160 | .next = undefined, |
| 157 | 161 | .data = handle2, |
| 158 | 162 | }; |
| ... | ... | @@ -160,6 +164,7 @@ async fn testLock(loop: *Loop, lock: *Lock) void { |
| 160 | 164 | |
| 161 | 165 | const handle3 = async lockRunner(lock) catch @panic("out of memory"); |
| 162 | 166 | var tick_node3 = Loop.NextTickNode{ |
| 167 | .prev = undefined, | |
| 163 | 168 | .next = undefined, |
| 164 | 169 | .data = handle3, |
| 165 | 170 | }; |
std/event/loop.zig+337-99| ... | ... | @@ -2,10 +2,12 @@ const std = @import("../index.zig"); |
| 2 | 2 | const builtin = @import("builtin"); |
| 3 | 3 | const assert = std.debug.assert; |
| 4 | 4 | const mem = std.mem; |
| 5 | const posix = std.os.posix; | |
| 6 | const windows = std.os.windows; | |
| 7 | 5 | const AtomicRmwOp = builtin.AtomicRmwOp; |
| 8 | 6 | const AtomicOrder = builtin.AtomicOrder; |
| 7 | const fs = std.event.fs; | |
| 8 | const os = std.os; | |
| 9 | const posix = os.posix; | |
| 10 | const windows = os.windows; | |
| 9 | 11 | |
| 10 | 12 | pub const Loop = struct { |
| 11 | 13 | allocator: *mem.Allocator, |
| ... | ... | @@ -13,7 +15,7 @@ pub const Loop = struct { |
| 13 | 15 | os_data: OsData, |
| 14 | 16 | final_resume_node: ResumeNode, |
| 15 | 17 | pending_event_count: usize, |
| 16 | extra_threads: []*std.os.Thread, | |
| 18 | extra_threads: []*os.Thread, | |
| 17 | 19 | |
| 18 | 20 | // pre-allocated eventfds. all permanently active. |
| 19 | 21 | // this is how we send promises to be resumed on other threads. |
| ... | ... | @@ -50,6 +52,22 @@ pub const Loop = struct { |
| 50 | 52 | base: ResumeNode, |
| 51 | 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 | }; |
| 54 | 72 | |
| 55 | 73 | /// After initialization, call run(). |
| ... | ... | @@ -65,7 +83,7 @@ pub const Loop = struct { |
| 65 | 83 | /// TODO copy elision / named return values so that the threads referencing *Loop |
| 66 | 84 | /// have the correct pointer value. |
| 67 | 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 | 87 | return self.initInternal(allocator, core_count); |
| 70 | 88 | } |
| 71 | 89 | |
| ... | ... | @@ -92,7 +110,7 @@ pub const Loop = struct { |
| 92 | 110 | ); |
| 93 | 111 | errdefer self.allocator.free(self.eventfd_resume_nodes); |
| 94 | 112 | |
| 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 | 114 | errdefer self.allocator.free(self.extra_threads); |
| 97 | 115 | |
| 98 | 116 | try self.initOsData(extra_thread_count); |
| ... | ... | @@ -104,17 +122,30 @@ pub const Loop = struct { |
| 104 | 122 | self.allocator.free(self.extra_threads); |
| 105 | 123 | } |
| 106 | 124 | |
| 107 | const InitOsDataError = std.os.LinuxEpollCreateError || mem.Allocator.Error || std.os.LinuxEventFdError || | |
| 108 | std.os.SpawnThreadError || std.os.LinuxEpollCtlError || std.os.BsdKEventError || | |
| 109 | std.os.WindowsCreateIoCompletionPortError; | |
| 125 | const InitOsDataError = os.LinuxEpollCreateError || mem.Allocator.Error || os.LinuxEventFdError || | |
| 126 | os.SpawnThreadError || os.LinuxEpollCtlError || os.BsdKEventError || | |
| 127 | os.WindowsCreateIoCompletionPortError; | |
| 110 | 128 | |
| 111 | 129 | const wakeup_bytes = []u8{0x1} ** 8; |
| 112 | 130 | |
| 113 | 131 | fn initOsData(self: *Loop, extra_thread_count: usize) InitOsDataError!void { |
| 114 | 132 | switch (builtin.os) { |
| 115 | 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 | 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 | 150 | for (self.eventfd_resume_nodes) |*eventfd_node| { |
| 120 | 151 | eventfd_node.* = std.atomic.Stack(ResumeNode.EventFd).Node{ |
| ... | ... | @@ -123,7 +154,7 @@ pub const Loop = struct { |
| 123 | 154 | .id = ResumeNode.Id.EventFd, |
| 124 | 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 | 158 | .epoll_op = posix.EPOLL_CTL_ADD, |
| 128 | 159 | }, |
| 129 | 160 | .next = undefined, |
| ... | ... | @@ -131,44 +162,62 @@ pub const Loop = struct { |
| 131 | 162 | self.available_eventfd_resume_nodes.push(eventfd_node); |
| 132 | 163 | } |
| 133 | 164 | |
| 134 | self.os_data.epollfd = try std.os.linuxEpollCreate(posix.EPOLL_CLOEXEC); | |
| 135 | errdefer std.os.close(self.os_data.epollfd); | |
| 165 | self.os_data.epollfd = try os.linuxEpollCreate(posix.EPOLL_CLOEXEC); | |
| 166 | errdefer os.close(self.os_data.epollfd); | |
| 136 | 167 | |
| 137 | self.os_data.final_eventfd = try std.os.linuxEventFd(0, posix.EFD_CLOEXEC | posix.EFD_NONBLOCK); | |
| 138 | errdefer std.os.close(self.os_data.final_eventfd); | |
| 168 | self.os_data.final_eventfd = try os.linuxEventFd(0, posix.EFD_CLOEXEC | posix.EFD_NONBLOCK); | |
| 169 | errdefer os.close(self.os_data.final_eventfd); | |
| 139 | 170 | |
| 140 | 171 | self.os_data.final_eventfd_event = posix.epoll_event{ |
| 141 | 172 | .events = posix.EPOLLIN, |
| 142 | 173 | .data = posix.epoll_data{ .ptr = @ptrToInt(&self.final_resume_node) }, |
| 143 | 174 | }; |
| 144 | try std.os.linuxEpollCtl( | |
| 175 | try os.linuxEpollCtl( | |
| 145 | 176 | self.os_data.epollfd, |
| 146 | 177 | posix.EPOLL_CTL_ADD, |
| 147 | 178 | self.os_data.final_eventfd, |
| 148 | 179 | &self.os_data.final_eventfd_event, |
| 149 | 180 | ); |
| 150 | 181 | |
| 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 | 188 | var extra_thread_index: usize = 0; |
| 152 | 189 | errdefer { |
| 153 | 190 | // 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 | 192 | while (extra_thread_index != 0) { |
| 156 | 193 | extra_thread_index -= 1; |
| 157 | 194 | self.extra_threads[extra_thread_index].wait(); |
| 158 | 195 | } |
| 159 | 196 | } |
| 160 | 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 | 201 | builtin.Os.macosx => { |
| 165 | self.os_data.kqfd = try std.os.bsdKQueue(); | |
| 166 | errdefer std.os.close(self.os_data.kqfd); | |
| 167 | ||
| 168 | self.os_data.kevents = try self.allocator.alloc(posix.Kevent, extra_thread_count); | |
| 169 | errdefer self.allocator.free(self.os_data.kevents); | |
| 202 | self.os_data.kqfd = try os.bsdKQueue(); | |
| 203 | errdefer os.close(self.os_data.kqfd); | |
| 204 | ||
| 205 | self.os_data.fs_kqfd = try os.bsdKQueue(); | |
| 206 | errdefer os.close(self.os_data.fs_kqfd); | |
| 207 | ||
| 208 | self.os_data.fs_queue = std.atomic.Queue(fs.Request).init(); | |
| 209 | // we need another thread for the file system because Darwin does not have an async | |
| 210 | // file system I/O API. | |
| 211 | self.os_data.fs_end_request = fs.RequestNode{ | |
| 212 | .prev = undefined, | |
| 213 | .next = undefined, | |
| 214 | .data = fs.Request{ | |
| 215 | .msg = fs.Request.Msg.End, | |
| 216 | .finish = fs.Request.Finish.NoAction, | |
| 217 | }, | |
| 218 | }; | |
| 170 | 219 | |
| 171 | const eventlist = ([*]posix.Kevent)(undefined)[0..0]; | |
| 220 | const empty_kevs = ([*]posix.Kevent)(undefined)[0..0]; | |
| 172 | 221 | |
| 173 | 222 | for (self.eventfd_resume_nodes) |*eventfd_node, i| { |
| 174 | 223 | eventfd_node.* = std.atomic.Stack(ResumeNode.EventFd).Node{ |
| ... | ... | @@ -191,18 +240,9 @@ pub const Loop = struct { |
| 191 | 240 | }; |
| 192 | 241 | self.available_eventfd_resume_nodes.push(eventfd_node); |
| 193 | 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 | 244 | eventfd_node.data.kevent.flags = posix.EV_CLEAR | posix.EV_ENABLE; |
| 196 | 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 | } |
| 207 | 247 | |
| 208 | 248 | // Pre-add so that we cannot get error.SystemResources |
| ... | ... | @@ -215,31 +255,55 @@ pub const Loop = struct { |
| 215 | 255 | .data = 0, |
| 216 | 256 | .udata = @ptrToInt(&self.final_resume_node), |
| 217 | 257 | }; |
| 218 | const kevent_array = (*[1]posix.Kevent)(&self.os_data.final_kevent); | |
| 219 | _ = try std.os.bsdKEvent(self.os_data.kqfd, kevent_array, eventlist, null); | |
| 258 | const final_kev_arr = (*[1]posix.Kevent)(&self.os_data.final_kevent); | |
| 259 | _ = try os.bsdKEvent(self.os_data.kqfd, final_kev_arr, empty_kevs, null); | |
| 220 | 260 | self.os_data.final_kevent.flags = posix.EV_ENABLE; |
| 221 | 261 | self.os_data.final_kevent.fflags = posix.NOTE_TRIGGER; |
| 222 | 262 | |
| 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 | 287 | var extra_thread_index: usize = 0; |
| 224 | 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 | 290 | while (extra_thread_index != 0) { |
| 227 | 291 | extra_thread_index -= 1; |
| 228 | 292 | self.extra_threads[extra_thread_index].wait(); |
| 229 | 293 | } |
| 230 | 294 | } |
| 231 | 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 | 299 | builtin.Os.windows => { |
| 236 | self.os_data.io_port = try std.os.windowsCreateIoCompletionPort( | |
| 300 | self.os_data.io_port = try os.windowsCreateIoCompletionPort( | |
| 237 | 301 | windows.INVALID_HANDLE_VALUE, |
| 238 | 302 | null, |
| 239 | 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); | |
| 243 | 307 | |
| 244 | 308 | for (self.eventfd_resume_nodes) |*eventfd_node, i| { |
| 245 | 309 | eventfd_node.* = std.atomic.Stack(ResumeNode.EventFd).Node{ |
| ... | ... | @@ -262,7 +326,7 @@ pub const Loop = struct { |
| 262 | 326 | while (i < extra_thread_index) : (i += 1) { |
| 263 | 327 | while (true) { |
| 264 | 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 | 330 | break; |
| 267 | 331 | } |
| 268 | 332 | } |
| ... | ... | @@ -272,7 +336,7 @@ pub const Loop = struct { |
| 272 | 336 | } |
| 273 | 337 | } |
| 274 | 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 | 342 | else => {}, |
| ... | ... | @@ -282,63 +346,113 @@ pub const Loop = struct { |
| 282 | 346 | fn deinitOsData(self: *Loop) void { |
| 283 | 347 | switch (builtin.os) { |
| 284 | 348 | builtin.Os.linux => { |
| 285 | std.os.close(self.os_data.final_eventfd); | |
| 286 | while (self.available_eventfd_resume_nodes.pop()) |node| std.os.close(node.data.eventfd); | |
| 287 | std.os.close(self.os_data.epollfd); | |
| 349 | os.close(self.os_data.final_eventfd); | |
| 350 | while (self.available_eventfd_resume_nodes.pop()) |node| os.close(node.data.eventfd); | |
| 351 | os.close(self.os_data.epollfd); | |
| 288 | 352 | self.allocator.free(self.eventfd_resume_nodes); |
| 289 | 353 | }, |
| 290 | 354 | builtin.Os.macosx => { |
| 291 | self.allocator.free(self.os_data.kevents); | |
| 292 | std.os.close(self.os_data.kqfd); | |
| 355 | os.close(self.os_data.kqfd); | |
| 356 | os.close(self.os_data.fs_kqfd); | |
| 293 | 357 | }, |
| 294 | 358 | builtin.Os.windows => { |
| 295 | std.os.close(self.os_data.io_port); | |
| 359 | os.close(self.os_data.io_port); | |
| 296 | 360 | }, |
| 297 | 361 | else => {}, |
| 298 | 362 | } |
| 299 | 363 | } |
| 300 | 364 | |
| 301 | 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 { | |
| 303 | _ = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst); | |
| 304 | errdefer { | |
| 305 | self.finishOneEvent(); | |
| 306 | } | |
| 307 | try self.modFd( | |
| 366 | /// flags must contain EPOLLET | |
| 367 | pub fn linuxAddFd(self: *Loop, fd: i32, resume_node: *ResumeNode, flags: u32) !void { | |
| 368 | assert(flags & posix.EPOLLET == posix.EPOLLET); | |
| 369 | self.beginOneEvent(); | |
| 370 | errdefer self.finishOneEvent(); | |
| 371 | try self.linuxModFd( | |
| 308 | 372 | fd, |
| 309 | 373 | posix.EPOLL_CTL_ADD, |
| 310 | std.os.linux.EPOLLIN | std.os.linux.EPOLLOUT | std.os.linux.EPOLLET, | |
| 374 | flags, | |
| 311 | 375 | resume_node, |
| 312 | 376 | ); |
| 313 | 377 | } |
| 314 | 378 | |
| 315 | pub fn modFd(self: *Loop, fd: i32, op: u32, events: u32, resume_node: *ResumeNode) !void { | |
| 316 | var ev = std.os.linux.epoll_event{ | |
| 317 | .events = events, | |
| 318 | .data = std.os.linux.epoll_data{ .ptr = @ptrToInt(resume_node) }, | |
| 379 | pub fn linuxModFd(self: *Loop, fd: i32, op: u32, flags: u32, resume_node: *ResumeNode) !void { | |
| 380 | assert(flags & posix.EPOLLET == posix.EPOLLET); | |
| 381 | var ev = os.linux.epoll_event{ | |
| 382 | .events = flags, | |
| 383 | .data = os.linux.epoll_data{ .ptr = @ptrToInt(resume_node) }, | |
| 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 | } |
| 322 | 387 | |
| 323 | pub fn removeFd(self: *Loop, fd: i32) void { | |
| 324 | self.removeFdNoCounter(fd); | |
| 388 | pub fn linuxRemoveFd(self: *Loop, fd: i32) void { | |
| 389 | os.linuxEpollCtl(self.os_data.epollfd, os.linux.EPOLL_CTL_DEL, fd, undefined) catch {}; | |
| 325 | 390 | self.finishOneEvent(); |
| 326 | 391 | } |
| 327 | 392 | |
| 328 | fn removeFdNoCounter(self: *Loop, fd: i32) void { | |
| 329 | std.os.linuxEpollCtl(self.os_data.epollfd, std.os.linux.EPOLL_CTL_DEL, fd, undefined) catch {}; | |
| 393 | pub async fn linuxWaitFd(self: *Loop, fd: i32, flags: u32) !void { | |
| 394 | defer self.linuxRemoveFd(fd); | |
| 395 | suspend { | |
| 396 | // TODO explicitly put this memory in the coroutine frame #1194 | |
| 397 | var resume_node = ResumeNode.Basic{ | |
| 398 | .base = ResumeNode{ | |
| 399 | .id = ResumeNode.Id.Basic, | |
| 400 | .handle = @handle(), | |
| 401 | }, | |
| 402 | }; | |
| 403 | try self.linuxAddFd(fd, &resume_node.base, flags); | |
| 404 | } | |
| 330 | 405 | } |
| 331 | 406 | |
| 332 | pub async fn waitFd(self: *Loop, fd: i32) !void { | |
| 333 | defer self.removeFd(fd); | |
| 407 | pub async fn bsdWaitKev(self: *Loop, ident: usize, filter: i16, fflags: u32) !posix.Kevent { | |
| 408 | // TODO #1194 | |
| 334 | 409 | suspend { |
| 335 | // TODO explicitly put this memory in the coroutine frame #1194 | |
| 336 | var resume_node = ResumeNode{ | |
| 410 | resume @handle(); | |
| 411 | } | |
| 412 | var resume_node = ResumeNode.Basic{ | |
| 413 | .base = ResumeNode{ | |
| 337 | 414 | .id = ResumeNode.Id.Basic, |
| 338 | 415 | .handle = @handle(), |
| 339 | }; | |
| 340 | try self.addFd(fd, &resume_node); | |
| 416 | }, | |
| 417 | .kev = undefined, | |
| 418 | }; | |
| 419 | defer self.bsdRemoveKev(ident, filter); | |
| 420 | suspend { | |
| 421 | try self.bsdAddKev(&resume_node, ident, filter, fflags); | |
| 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 | } |
| 343 | 457 | |
| 344 | 458 | fn dispatch(self: *Loop) void { |
| ... | ... | @@ -352,8 +466,8 @@ pub const Loop = struct { |
| 352 | 466 | switch (builtin.os) { |
| 353 | 467 | builtin.Os.macosx => { |
| 354 | 468 | const kevent_array = (*[1]posix.Kevent)(&eventfd_node.kevent); |
| 355 | const eventlist = ([*]posix.Kevent)(undefined)[0..0]; | |
| 356 | _ = std.os.bsdKEvent(self.os_data.kqfd, kevent_array, eventlist, null) catch { | |
| 469 | const empty_kevs = ([*]posix.Kevent)(undefined)[0..0]; | |
| 470 | _ = os.bsdKEvent(self.os_data.kqfd, kevent_array, empty_kevs, null) catch { | |
| 357 | 471 | self.next_tick_queue.unget(next_tick_node); |
| 358 | 472 | self.available_eventfd_resume_nodes.push(resume_stack_node); |
| 359 | 473 | return; |
| ... | ... | @@ -361,9 +475,9 @@ pub const Loop = struct { |
| 361 | 475 | }, |
| 362 | 476 | builtin.Os.linux => { |
| 363 | 477 | // the pending count is already accounted for |
| 364 | const epoll_events = posix.EPOLLONESHOT | std.os.linux.EPOLLIN | std.os.linux.EPOLLOUT | | |
| 365 | std.os.linux.EPOLLET; | |
| 366 | self.modFd( | |
| 478 | const epoll_events = posix.EPOLLONESHOT | os.linux.EPOLLIN | os.linux.EPOLLOUT | | |
| 479 | os.linux.EPOLLET; | |
| 480 | self.linuxModFd( | |
| 367 | 481 | eventfd_node.eventfd, |
| 368 | 482 | eventfd_node.epoll_op, |
| 369 | 483 | epoll_events, |
| ... | ... | @@ -379,7 +493,7 @@ pub const Loop = struct { |
| 379 | 493 | // the consumer code can decide whether to read the completion key. |
| 380 | 494 | // it has to do this for normal I/O, so we match that behavior here. |
| 381 | 495 | const overlapped = @intToPtr(?*windows.OVERLAPPED, 0x1); |
| 382 | std.os.windowsPostQueuedCompletionStatus( | |
| 496 | os.windowsPostQueuedCompletionStatus( | |
| 383 | 497 | self.os_data.io_port, |
| 384 | 498 | undefined, |
| 385 | 499 | eventfd_node.completion_key, |
| ... | ... | @@ -397,15 +511,29 @@ pub const Loop = struct { |
| 397 | 511 | |
| 398 | 512 | /// Bring your own linked list node. This means it can't fail. |
| 399 | 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 | 515 | self.next_tick_queue.put(node); |
| 402 | 516 | self.dispatch(); |
| 403 | 517 | } |
| 404 | 518 | |
| 519 | pub fn cancelOnNextTick(self: *Loop, node: *NextTickNode) void { | |
| 520 | if (self.next_tick_queue.remove(node)) { | |
| 521 | self.finishOneEvent(); | |
| 522 | } | |
| 523 | } | |
| 524 | ||
| 405 | 525 | pub fn run(self: *Loop) void { |
| 406 | 526 | self.finishOneEvent(); // the reference we start with |
| 407 | 527 | |
| 408 | 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 | 537 | for (self.extra_threads) |extra_thread| { |
| 410 | 538 | extra_thread.wait(); |
| 411 | 539 | } |
| ... | ... | @@ -420,6 +548,7 @@ pub const Loop = struct { |
| 420 | 548 | suspend { |
| 421 | 549 | handle.* = @handle(); |
| 422 | 550 | var my_tick_node = Loop.NextTickNode{ |
| 551 | .prev = undefined, | |
| 423 | 552 | .next = undefined, |
| 424 | 553 | .data = @handle(), |
| 425 | 554 | }; |
| ... | ... | @@ -441,6 +570,7 @@ pub const Loop = struct { |
| 441 | 570 | pub async fn yield(self: *Loop) void { |
| 442 | 571 | suspend { |
| 443 | 572 | var my_tick_node = Loop.NextTickNode{ |
| 573 | .prev = undefined, | |
| 444 | 574 | .next = undefined, |
| 445 | 575 | .data = @handle(), |
| 446 | 576 | }; |
| ... | ... | @@ -448,20 +578,28 @@ pub const Loop = struct { |
| 448 | 578 | } |
| 449 | 579 | } |
| 450 | 580 | |
| 451 | fn finishOneEvent(self: *Loop) void { | |
| 452 | if (@atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst) == 1) { | |
| 581 | /// call finishOneEvent when done | |
| 582 | pub fn beginOneEvent(self: *Loop) void { | |
| 583 | _ = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst); | |
| 584 | } | |
| 585 | ||
| 586 | pub fn finishOneEvent(self: *Loop) void { | |
| 587 | const prev = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst); | |
| 588 | if (prev == 1) { | |
| 453 | 589 | // cause all the threads to stop |
| 454 | 590 | switch (builtin.os) { |
| 455 | 591 | builtin.Os.linux => { |
| 592 | self.posixFsRequest(&self.os_data.fs_end_request); | |
| 456 | 593 | // 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 | 595 | return; |
| 459 | 596 | }, |
| 460 | 597 | builtin.Os.macosx => { |
| 598 | self.posixFsRequest(&self.os_data.fs_end_request); | |
| 461 | 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 | 601 | // 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 | 603 | return; |
| 466 | 604 | }, |
| 467 | 605 | builtin.Os.windows => { |
| ... | ... | @@ -469,7 +607,7 @@ pub const Loop = struct { |
| 469 | 607 | while (i < self.extra_threads.len + 1) : (i += 1) { |
| 470 | 608 | while (true) { |
| 471 | 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 | 611 | break; |
| 474 | 612 | } |
| 475 | 613 | } |
| ... | ... | @@ -492,8 +630,8 @@ pub const Loop = struct { |
| 492 | 630 | switch (builtin.os) { |
| 493 | 631 | builtin.Os.linux => { |
| 494 | 632 | // only process 1 event so we don't steal from other threads |
| 495 | var events: [1]std.os.linux.epoll_event = undefined; | |
| 496 | const count = std.os.linuxEpollWait(self.os_data.epollfd, events[0..], -1); | |
| 633 | var events: [1]os.linux.epoll_event = undefined; | |
| 634 | const count = os.linuxEpollWait(self.os_data.epollfd, events[0..], -1); | |
| 497 | 635 | for (events[0..count]) |ev| { |
| 498 | 636 | const resume_node = @intToPtr(*ResumeNode, ev.data.ptr); |
| 499 | 637 | const handle = resume_node.handle; |
| ... | ... | @@ -516,13 +654,17 @@ pub const Loop = struct { |
| 516 | 654 | }, |
| 517 | 655 | builtin.Os.macosx => { |
| 518 | 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 | 659 | for (eventlist[0..count]) |ev| { |
| 521 | 660 | const resume_node = @intToPtr(*ResumeNode, ev.udata); |
| 522 | 661 | const handle = resume_node.handle; |
| 523 | 662 | const resume_node_id = resume_node.id; |
| 524 | 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 | 668 | ResumeNode.Id.Stop => return, |
| 527 | 669 | ResumeNode.Id.EventFd => { |
| 528 | 670 | const event_fd_node = @fieldParentPtr(ResumeNode.EventFd, "base", resume_node); |
| ... | ... | @@ -541,9 +683,10 @@ pub const Loop = struct { |
| 541 | 683 | while (true) { |
| 542 | 684 | var nbytes: windows.DWORD = undefined; |
| 543 | 685 | var overlapped: ?*windows.OVERLAPPED = undefined; |
| 544 | switch (std.os.windowsGetQueuedCompletionStatus(self.os_data.io_port, &nbytes, &completion_key, &overlapped, windows.INFINITE)) { | |
| 545 | std.os.WindowsWaitResult.Aborted => return, | |
| 546 | std.os.WindowsWaitResult.Normal => {}, | |
| 686 | switch (os.windowsGetQueuedCompletionStatus(self.os_data.io_port, &nbytes, &completion_key, &overlapped, windows.INFINITE)) { | |
| 687 | os.WindowsWaitResult.Aborted => return, | |
| 688 | os.WindowsWaitResult.Normal => {}, | |
| 689 | os.WindowsWaitResult.Cancelled => continue, | |
| 547 | 690 | } |
| 548 | 691 | if (overlapped != null) break; |
| 549 | 692 | } |
| ... | ... | @@ -560,21 +703,101 @@ pub const Loop = struct { |
| 560 | 703 | }, |
| 561 | 704 | } |
| 562 | 705 | resume handle; |
| 563 | if (resume_node_id == ResumeNode.Id.EventFd) { | |
| 564 | self.finishOneEvent(); | |
| 565 | } | |
| 706 | self.finishOneEvent(); | |
| 566 | 707 | }, |
| 567 | 708 | else => @compileError("unsupported OS"), |
| 568 | 709 | } |
| 569 | 710 | } |
| 570 | 711 | } |
| 571 | 712 | |
| 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 | 799 | const OsData = switch (builtin.os) { |
| 573 | builtin.Os.linux => struct { | |
| 574 | epollfd: i32, | |
| 575 | final_eventfd: i32, | |
| 576 | final_eventfd_event: std.os.linux.epoll_event, | |
| 577 | }, | |
| 800 | builtin.Os.linux => LinuxOsData, | |
| 578 | 801 | builtin.Os.macosx => MacOsData, |
| 579 | 802 | builtin.Os.windows => struct { |
| 580 | 803 | io_port: windows.HANDLE, |
| ... | ... | @@ -586,7 +809,22 @@ pub const Loop = struct { |
| 586 | 809 | const MacOsData = struct { |
| 587 | 810 | kqfd: i32, |
| 588 | 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 | }; |
| 592 | 830 |
std/event/rwlock.zig created+296| ... | ... | @@ -0,0 +1,296 @@ |
| 1 | const std = @import("../index.zig"); | |
| 2 | const builtin = @import("builtin"); | |
| 3 | const assert = std.debug.assert; | |
| 4 | const mem = std.mem; | |
| 5 | const AtomicRmwOp = builtin.AtomicRmwOp; | |
| 6 | const AtomicOrder = builtin.AtomicOrder; | |
| 7 | const 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. | |
| 15 | pub 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 | ||
| 213 | test "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 | ||
| 234 | async 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 | ||
| 260 | const shared_it_count = 10; | |
| 261 | var shared_test_data = [1]i32{0} ** 10; | |
| 262 | var shared_test_index: usize = 0; | |
| 263 | var shared_count: usize = 0; | |
| 264 | ||
| 265 | async 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 | ||
| 283 | async 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 @@ |
| 1 | const std = @import("../index.zig"); | |
| 2 | const RwLock = std.event.RwLock; | |
| 3 | const 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. | |
| 8 | pub 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 | 55 | errdefer cancel self.accept_coro.?; |
| 56 | 56 | |
| 57 | 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 | 59 | errdefer self.loop.removeFd(sockfd); |
| 60 | 60 | } |
| 61 | 61 | |
| 62 | 62 | /// Stop listening |
| 63 | 63 | pub fn close(self: *Server) void { |
| 64 | self.loop.removeFd(self.sockfd.?); | |
| 64 | self.loop.linuxRemoveFd(self.sockfd.?); | |
| 65 | 65 | std.os.close(self.sockfd.?); |
| 66 | 66 | } |
| 67 | 67 | |
| ... | ... | @@ -116,7 +116,7 @@ pub async fn connect(loop: *Loop, _address: *const std.net.Address) !std.os.File |
| 116 | 116 | errdefer std.os.close(sockfd); |
| 117 | 117 | |
| 118 | 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 | 120 | try std.os.posixGetSockOptConnectError(sockfd); |
| 121 | 121 | |
| 122 | 122 | return std.os.File.openHandle(sockfd); |
| ... | ... | @@ -181,4 +181,3 @@ async fn doAsyncTest(loop: *Loop, address: *const std.net.Address, server: *Serv |
| 181 | 181 | assert(mem.eql(u8, msg, "hello from server\n")); |
| 182 | 182 | server.close(); |
| 183 | 183 | } |
| 184 |
std/hash_map.zig+257-57| ... | ... | @@ -9,6 +9,10 @@ const builtin = @import("builtin"); |
| 9 | 9 | const want_modification_safety = builtin.mode != builtin.Mode.ReleaseFast; |
| 10 | 10 | const debug_u32 = if (want_modification_safety) u32 else void; |
| 11 | 11 | |
| 12 | pub fn AutoHashMap(comptime K: type, comptime V: type) type { | |
| 13 | return HashMap(K, V, getAutoHashFn(K), getAutoEqlFn(K)); | |
| 14 | } | |
| 15 | ||
| 12 | 16 | pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u32, comptime eql: fn (a: K, b: K) bool) type { |
| 13 | 17 | return struct { |
| 14 | 18 | entries: []Entry, |
| ... | ... | @@ -20,13 +24,22 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3 |
| 20 | 24 | |
| 21 | 25 | const Self = this; |
| 22 | 26 | |
| 23 | pub const Entry = struct { | |
| 24 | used: bool, | |
| 25 | distance_from_start_index: usize, | |
| 27 | pub const KV = struct { | |
| 26 | 28 | key: K, |
| 27 | 29 | value: V, |
| 28 | 30 | }; |
| 29 | 31 | |
| 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 | 43 | pub const Iterator = struct { |
| 31 | 44 | hm: *const Self, |
| 32 | 45 | // 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 | 49 | // used to detect concurrent modification |
| 37 | 50 | initial_modification_count: debug_u32, |
| 38 | 51 | |
| 39 | pub fn next(it: *Iterator) ?*Entry { | |
| 52 | pub fn next(it: *Iterator) ?*KV { | |
| 40 | 53 | if (want_modification_safety) { |
| 41 | 54 | 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 | 59 | if (entry.used) { |
| 47 | 60 | it.index += 1; |
| 48 | 61 | it.count += 1; |
| 49 | return entry; | |
| 62 | return &entry.kv; | |
| 50 | 63 | } |
| 51 | 64 | } |
| 52 | 65 | unreachable; // no next item |
| ... | ... | @@ -71,7 +84,7 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3 |
| 71 | 84 | }; |
| 72 | 85 | } |
| 73 | 86 | |
| 74 | pub fn deinit(hm: *const Self) void { | |
| 87 | pub fn deinit(hm: Self) void { | |
| 75 | 88 | hm.allocator.free(hm.entries); |
| 76 | 89 | } |
| 77 | 90 | |
| ... | ... | @@ -84,34 +97,65 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3 |
| 84 | 97 | hm.incrementModificationCount(); |
| 85 | 98 | } |
| 86 | 99 | |
| 87 | pub fn count(hm: *const Self) usize { | |
| 88 | return hm.size; | |
| 100 | pub fn count(self: Self) usize { | |
| 101 | return self.size; | |
| 89 | 102 | } |
| 90 | 103 | |
| 91 | /// Returns the value that was already there. | |
| 92 | pub fn put(hm: *Self, key: K, value: *const V) !?V { | |
| 93 | if (hm.entries.len == 0) { | |
| 94 | try hm.initCapacity(16); | |
| 104 | /// If key exists this function cannot fail. | |
| 105 | /// If there is an existing item with `key`, then the result | |
| 106 | /// kv pointer points to it, and found_existing is true. | |
| 107 | /// Otherwise, puts a new item with undefined value, and | |
| 108 | /// the kv pointer points to it. Caller should then initialize | |
| 109 | /// the data. | |
| 110 | pub fn getOrPut(self: *Self, key: K) !GetOrPutResult { | |
| 111 | // TODO this implementation can be improved - we should only | |
| 112 | // have to hash once and find the entry once. | |
| 113 | if (self.get(key)) |kv| { | |
| 114 | return GetOrPutResult{ | |
| 115 | .kv = kv, | |
| 116 | .found_existing = true, | |
| 117 | }; | |
| 118 | } | |
| 119 | self.incrementModificationCount(); | |
| 120 | try self.ensureCapacity(); | |
| 121 | const put_result = self.internalPut(key); | |
| 122 | assert(put_result.old_kv == null); | |
| 123 | return GetOrPutResult{ | |
| 124 | .kv = &put_result.new_entry.kv, | |
| 125 | .found_existing = false, | |
| 126 | }; | |
| 127 | } | |
| 128 | ||
| 129 | fn ensureCapacity(self: *Self) !void { | |
| 130 | if (self.entries.len == 0) { | |
| 131 | return self.initCapacity(16); | |
| 95 | 132 | } |
| 96 | hm.incrementModificationCount(); | |
| 97 | 133 | |
| 98 | 134 | // if we get too full (60%), double the capacity |
| 99 | if (hm.size * 5 >= hm.entries.len * 3) { | |
| 100 | const old_entries = hm.entries; | |
| 101 | try hm.initCapacity(hm.entries.len * 2); | |
| 135 | if (self.size * 5 >= self.entries.len * 3) { | |
| 136 | const old_entries = self.entries; | |
| 137 | try self.initCapacity(self.entries.len * 2); | |
| 102 | 138 | // dump all of the old elements into the new table |
| 103 | 139 | for (old_entries) |*old_entry| { |
| 104 | 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(); | |
| 110 | 152 | |
| 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 | } |
| 113 | 157 | |
| 114 | pub fn get(hm: *const Self, key: K) ?*Entry { | |
| 158 | pub fn get(hm: *const Self, key: K) ?*KV { | |
| 115 | 159 | if (hm.entries.len == 0) { |
| 116 | 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 | 166 | return hm.get(key) != null; |
| 123 | 167 | } |
| 124 | 168 | |
| 125 | pub fn remove(hm: *Self, key: K) ?*Entry { | |
| 169 | pub fn remove(hm: *Self, key: K) ?*KV { | |
| 126 | 170 | if (hm.entries.len == 0) return null; |
| 127 | 171 | hm.incrementModificationCount(); |
| 128 | 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 | 178 | |
| 135 | 179 | if (!entry.used) return null; |
| 136 | 180 | |
| 137 | if (!eql(entry.key, key)) continue; | |
| 181 | if (!eql(entry.kv.key, key)) continue; | |
| 138 | 182 | |
| 139 | 183 | while (roll_over < hm.entries.len) : (roll_over += 1) { |
| 140 | 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 | 186 | if (!next_entry.used or next_entry.distance_from_start_index == 0) { |
| 143 | 187 | entry.used = false; |
| 144 | 188 | hm.size -= 1; |
| 145 | return entry; | |
| 189 | return &entry.kv; | |
| 146 | 190 | } |
| 147 | 191 | entry.* = next_entry.*; |
| 148 | 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 | 207 | }; |
| 164 | 208 | } |
| 165 | 209 | |
| 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 | 220 | fn initCapacity(hm: *Self, capacity: usize) !void { |
| 167 | 221 | hm.entries = try hm.allocator.alloc(Entry, capacity); |
| 168 | 222 | hm.size = 0; |
| ... | ... | @@ -178,60 +232,81 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3 |
| 178 | 232 | } |
| 179 | 233 | } |
| 180 | 234 | |
| 181 | /// Returns the value that was already there. | |
| 182 | fn internalPut(hm: *Self, orig_key: K, orig_value: *const V) ?V { | |
| 235 | const InternalPutResult = struct { | |
| 236 | new_entry: *Entry, | |
| 237 | old_kv: ?KV, | |
| 238 | }; | |
| 239 | ||
| 240 | /// Returns a pointer to the new entry. | |
| 241 | /// Asserts that there is enough space for the new item. | |
| 242 | fn internalPut(self: *Self, orig_key: K) InternalPutResult { | |
| 183 | 243 | var key = orig_key; |
| 184 | var value = orig_value.*; | |
| 185 | const start_index = hm.keyToIndex(key); | |
| 244 | var value: V = undefined; | |
| 245 | const start_index = self.keyToIndex(key); | |
| 186 | 246 | var roll_over: usize = 0; |
| 187 | 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 | 254 | roll_over += 1; |
| 190 | 255 | distance_from_start_index += 1; |
| 191 | 256 | }) { |
| 192 | const index = (start_index + roll_over) % hm.entries.len; | |
| 193 | const entry = &hm.entries[index]; | |
| 257 | const index = (start_index + roll_over) % self.entries.len; | |
| 258 | const entry = &self.entries[index]; | |
| 194 | 259 | |
| 195 | if (entry.used and !eql(entry.key, key)) { | |
| 260 | if (entry.used and !eql(entry.kv.key, key)) { | |
| 196 | 261 | if (entry.distance_from_start_index < distance_from_start_index) { |
| 197 | 262 | // robin hood to the rescue |
| 198 | 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 | 269 | entry.* = Entry{ |
| 201 | 270 | .used = true, |
| 202 | 271 | .distance_from_start_index = distance_from_start_index, |
| 203 | .key = key, | |
| 204 | .value = value, | |
| 272 | .kv = KV{ | |
| 273 | .key = key, | |
| 274 | .value = value, | |
| 275 | }, | |
| 205 | 276 | }; |
| 206 | key = tmp.key; | |
| 207 | value = tmp.value; | |
| 277 | key = tmp.kv.key; | |
| 278 | value = tmp.kv.value; | |
| 208 | 279 | distance_from_start_index = tmp.distance_from_start_index; |
| 209 | 280 | } |
| 210 | 281 | continue; |
| 211 | 282 | } |
| 212 | 283 | |
| 213 | var result: ?V = null; | |
| 214 | 284 | if (entry.used) { |
| 215 | result = entry.value; | |
| 285 | result.old_kv = entry.kv; | |
| 216 | 286 | } else { |
| 217 | 287 | // adding an entry. otherwise overwriting old value with |
| 218 | 288 | // same key |
| 219 | hm.size += 1; | |
| 289 | self.size += 1; | |
| 220 | 290 | } |
| 221 | 291 | |
| 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 | 296 | entry.* = Entry{ |
| 224 | 297 | .used = true, |
| 225 | 298 | .distance_from_start_index = distance_from_start_index, |
| 226 | .key = key, | |
| 227 | .value = value, | |
| 299 | .kv = KV{ | |
| 300 | .key = key, | |
| 301 | .value = value, | |
| 302 | }, | |
| 228 | 303 | }; |
| 229 | 304 | return result; |
| 230 | 305 | } |
| 231 | 306 | unreachable; // put into a full map |
| 232 | 307 | } |
| 233 | 308 | |
| 234 | fn internalGet(hm: *const Self, key: K) ?*Entry { | |
| 309 | fn internalGet(hm: Self, key: K) ?*KV { | |
| 235 | 310 | const start_index = hm.keyToIndex(key); |
| 236 | 311 | { |
| 237 | 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 | 315 | const entry = &hm.entries[index]; |
| 241 | 316 | |
| 242 | 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 | 321 | return null; |
| 247 | 322 | } |
| 248 | 323 | |
| 249 | fn keyToIndex(hm: *const Self, key: K) usize { | |
| 324 | fn keyToIndex(hm: Self, key: K) usize { | |
| 250 | 325 | return usize(hash(key)) % hm.entries.len; |
| 251 | 326 | } |
| 252 | 327 | }; |
| ... | ... | @@ -256,7 +331,7 @@ test "basic hash map usage" { |
| 256 | 331 | var direct_allocator = std.heap.DirectAllocator.init(); |
| 257 | 332 | defer direct_allocator.deinit(); |
| 258 | 333 | |
| 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 | 335 | defer map.deinit(); |
| 261 | 336 | |
| 262 | 337 | assert((try map.put(1, 11)) == null); |
| ... | ... | @@ -265,8 +340,19 @@ test "basic hash map usage" { |
| 265 | 340 | assert((try map.put(4, 44)) == null); |
| 266 | 341 | assert((try map.put(5, 55)) == null); |
| 267 | 342 | |
| 268 | assert((try map.put(5, 66)).? == 55); | |
| 269 | assert((try map.put(5, 55)).? == 66); | |
| 343 | assert((try map.put(5, 66)).?.value == 55); | |
| 344 | assert((try map.put(5, 55)).?.value == 66); | |
| 345 | ||
| 346 | const gop1 = try map.getOrPut(5); | |
| 347 | assert(gop1.found_existing == true); | |
| 348 | assert(gop1.kv.value == 55); | |
| 349 | gop1.kv.value = 77; | |
| 350 | assert(map.get(5).?.value == 77); | |
| 351 | ||
| 352 | const gop2 = try map.getOrPut(99); | |
| 353 | assert(gop2.found_existing == false); | |
| 354 | gop2.kv.value = 42; | |
| 355 | assert(map.get(99).?.value == 42); | |
| 270 | 356 | |
| 271 | 357 | assert(map.contains(2)); |
| 272 | 358 | assert(map.get(2).?.value == 22); |
| ... | ... | @@ -279,7 +365,7 @@ test "iterator hash map" { |
| 279 | 365 | var direct_allocator = std.heap.DirectAllocator.init(); |
| 280 | 366 | defer direct_allocator.deinit(); |
| 281 | 367 | |
| 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 | 369 | defer reset_map.deinit(); |
| 284 | 370 | |
| 285 | 371 | assert((try reset_map.put(1, 11)) == null); |
| ... | ... | @@ -287,14 +373,14 @@ test "iterator hash map" { |
| 287 | 373 | assert((try reset_map.put(3, 33)) == null); |
| 288 | 374 | |
| 289 | 375 | var keys = []i32{ |
| 290 | 1, | |
| 291 | 2, | |
| 292 | 376 | 3, |
| 377 | 2, | |
| 378 | 1, | |
| 293 | 379 | }; |
| 294 | 380 | var values = []i32{ |
| 295 | 11, | |
| 296 | 22, | |
| 297 | 381 | 33, |
| 382 | 22, | |
| 383 | 11, | |
| 298 | 384 | }; |
| 299 | 385 | |
| 300 | 386 | var it = reset_map.iterator(); |
| ... | ... | @@ -322,10 +408,124 @@ test "iterator hash map" { |
| 322 | 408 | assert(entry.value == values[0]); |
| 323 | 409 | } |
| 324 | 410 | |
| 325 | fn hash_i32(x: i32) u32 { | |
| 326 | return @bitCast(u32, x); | |
| 411 | pub fn getAutoHashFn(comptime K: type) (fn (K) u32) { | |
| 412 | return struct { | |
| 413 | fn hash(key: K) u32 { | |
| 414 | comptime var rng = comptime std.rand.DefaultPrng.init(0); | |
| 415 | return autoHash(key, &rng.random, u32); | |
| 416 | } | |
| 417 | }.hash; | |
| 418 | } | |
| 419 | ||
| 420 | pub 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 | |
| 429 | pub 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 | } |
| 328 | 487 | |
| 329 | fn eql_i32(a: i32, b: i32) bool { | |
| 330 | return a == b; | |
| 488 | pub fn autoEql(a: var, b: @typeOf(a)) bool { | |
| 489 | switch (@typeInfo(@typeOf(a))) { | |
| 490 | builtin.TypeId.NoReturn, | |
| 491 | builtin.TypeId.Opaque, | |
| 492 | builtin.TypeId.Undefined, | |
| 493 | builtin.TypeId.ArgTuple, | |
| 494 | => @compileError("cannot test equality of this type"), | |
| 495 | builtin.TypeId.Void, | |
| 496 | builtin.TypeId.Null, | |
| 497 | => return true, | |
| 498 | builtin.TypeId.Bool, | |
| 499 | builtin.TypeId.Int, | |
| 500 | builtin.TypeId.Float, | |
| 501 | builtin.TypeId.ComptimeFloat, | |
| 502 | builtin.TypeId.ComptimeInt, | |
| 503 | builtin.TypeId.Namespace, | |
| 504 | builtin.TypeId.Block, | |
| 505 | builtin.TypeId.Promise, | |
| 506 | builtin.TypeId.Enum, | |
| 507 | builtin.TypeId.BoundFn, | |
| 508 | builtin.TypeId.Fn, | |
| 509 | builtin.TypeId.ErrorSet, | |
| 510 | builtin.TypeId.Type, | |
| 511 | => return a == b, | |
| 512 | ||
| 513 | builtin.TypeId.Pointer => |info| switch (info.size) { | |
| 514 | builtin.TypeInfo.Pointer.Size.One => @compileError("TODO auto eql for single item pointers"), | |
| 515 | builtin.TypeInfo.Pointer.Size.Many => @compileError("TODO auto eql for many item pointers"), | |
| 516 | builtin.TypeInfo.Pointer.Size.Slice => { | |
| 517 | if (a.len != b.len) return false; | |
| 518 | for (a) |a_item, i| { | |
| 519 | if (!autoEql(a_item, b[i])) return false; | |
| 520 | } | |
| 521 | return true; | |
| 522 | }, | |
| 523 | }, | |
| 524 | ||
| 525 | builtin.TypeId.Optional => @compileError("TODO auto eql for optionals"), | |
| 526 | builtin.TypeId.Array => @compileError("TODO auto eql for arrays"), | |
| 527 | builtin.TypeId.Struct => @compileError("TODO auto eql for structs"), | |
| 528 | builtin.TypeId.Union => @compileError("TODO auto eql for unions"), | |
| 529 | builtin.TypeId.ErrorUnion => @compileError("TODO auto eql for unions"), | |
| 530 | } | |
| 331 | 531 | } |
std/index.zig+3-1| ... | ... | @@ -5,10 +5,11 @@ pub const BufSet = @import("buf_set.zig").BufSet; |
| 5 | 5 | pub const Buffer = @import("buffer.zig").Buffer; |
| 6 | 6 | pub const BufferOutStream = @import("buffer.zig").BufferOutStream; |
| 7 | 7 | pub const HashMap = @import("hash_map.zig").HashMap; |
| 8 | pub const AutoHashMap = @import("hash_map.zig").AutoHashMap; | |
| 8 | 9 | pub const LinkedList = @import("linked_list.zig").LinkedList; |
| 9 | pub const IntrusiveLinkedList = @import("linked_list.zig").IntrusiveLinkedList; | |
| 10 | 10 | pub const SegmentedList = @import("segmented_list.zig").SegmentedList; |
| 11 | 11 | pub const DynLib = @import("dynamic_library.zig").DynLib; |
| 12 | pub const Mutex = @import("mutex.zig").Mutex; | |
| 12 | 13 | |
| 13 | 14 | pub const atomic = @import("atomic/index.zig"); |
| 14 | 15 | pub const base64 = @import("base64.zig"); |
| ... | ... | @@ -49,6 +50,7 @@ test "std" { |
| 49 | 50 | _ = @import("hash_map.zig"); |
| 50 | 51 | _ = @import("linked_list.zig"); |
| 51 | 52 | _ = @import("segmented_list.zig"); |
| 53 | _ = @import("mutex.zig"); | |
| 52 | 54 | |
| 53 | 55 | _ = @import("base64.zig"); |
| 54 | 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 | 415 | self.at_end = (read < left); |
| 416 | 416 | return pos + read; |
| 417 | 417 | } |
| 418 | ||
| 419 | 418 | }; |
| 420 | 419 | } |
| 421 | 420 | |
| 422 | 421 | pub const SliceInStream = struct { |
| 423 | 422 | const Self = this; |
| 424 | pub const Error = error { }; | |
| 423 | pub const Error = error{}; | |
| 425 | 424 | pub const Stream = InStream(Error); |
| 426 | 425 | |
| 427 | 426 | pub stream: Stream, |
| ... | ... | @@ -481,13 +480,12 @@ pub const SliceOutStream = struct { |
| 481 | 480 | |
| 482 | 481 | assert(self.pos <= self.slice.len); |
| 483 | 482 | |
| 484 | const n = | |
| 485 | if (self.pos + bytes.len <= self.slice.len) | |
| 486 | bytes.len | |
| 487 | else | |
| 488 | self.slice.len - self.pos; | |
| 483 | const n = if (self.pos + bytes.len <= self.slice.len) | |
| 484 | bytes.len | |
| 485 | else | |
| 486 | self.slice.len - self.pos; | |
| 489 | 487 | |
| 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 | 489 | self.pos += n; |
| 492 | 490 | |
| 493 | 491 | if (n < bytes.len) { |
| ... | ... | @@ -586,7 +584,7 @@ pub const BufferedAtomicFile = struct { |
| 586 | 584 | }); |
| 587 | 585 | errdefer allocator.destroy(self); |
| 588 | 586 | |
| 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 | 588 | errdefer self.atomic_file.deinit(); |
| 591 | 589 | |
| 592 | 590 | self.file_stream = FileOutStream.init(&self.atomic_file.file); |
std/json.zig+1-1| ... | ... | @@ -1318,7 +1318,7 @@ pub const Parser = struct { |
| 1318 | 1318 | _ = p.stack.pop(); |
| 1319 | 1319 | |
| 1320 | 1320 | var object = &p.stack.items[p.stack.len - 1].Object; |
| 1321 | _ = try object.put(key, value); | |
| 1321 | _ = try object.put(key, value.*); | |
| 1322 | 1322 | p.state = State.ObjectKey; |
| 1323 | 1323 | }, |
| 1324 | 1324 | // Array Parent -> [ ..., <array>, value ] |
std/linked_list.zig+4-97| ... | ... | @@ -4,18 +4,8 @@ const assert = debug.assert; |
| 4 | 4 | const mem = std.mem; |
| 5 | 5 | const Allocator = mem.Allocator; |
| 6 | 6 | |
| 7 | /// Generic non-intrusive doubly linked list. | |
| 8 | pub fn LinkedList(comptime T: type) type { | |
| 9 | return BaseLinkedList(T, void, ""); | |
| 10 | } | |
| 11 | ||
| 12 | /// Generic intrusive doubly linked list. | |
| 13 | pub fn IntrusiveLinkedList(comptime ParentType: type, comptime field_name: []const u8) type { | |
| 14 | return BaseLinkedList(void, ParentType, field_name); | |
| 15 | } | |
| 16 | ||
| 17 | 7 | /// Generic doubly linked list. |
| 18 | fn BaseLinkedList(comptime T: type, comptime ParentType: type, comptime field_name: []const u8) type { | |
| 8 | pub fn LinkedList(comptime T: type) type { | |
| 19 | 9 | return struct { |
| 20 | 10 | const Self = this; |
| 21 | 11 | |
| ... | ... | @@ -25,23 +15,13 @@ fn BaseLinkedList(comptime T: type, comptime ParentType: type, comptime field_na |
| 25 | 15 | next: ?*Node, |
| 26 | 16 | data: T, |
| 27 | 17 | |
| 28 | pub fn init(value: *const T) Node { | |
| 18 | pub fn init(data: T) Node { | |
| 29 | 19 | return Node{ |
| 30 | 20 | .prev = null, |
| 31 | 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 | }; |
| 46 | 26 | |
| 47 | 27 | first: ?*Node, |
| ... | ... | @@ -60,10 +40,6 @@ fn BaseLinkedList(comptime T: type, comptime ParentType: type, comptime field_na |
| 60 | 40 | }; |
| 61 | 41 | } |
| 62 | 42 | |
| 63 | fn isIntrusive() bool { | |
| 64 | return ParentType != void or field_name.len != 0; | |
| 65 | } | |
| 66 | ||
| 67 | 43 | /// Insert a new node after an existing one. |
| 68 | 44 | /// |
| 69 | 45 | /// Arguments: |
| ... | ... | @@ -192,7 +168,6 @@ fn BaseLinkedList(comptime T: type, comptime ParentType: type, comptime field_na |
| 192 | 168 | /// Returns: |
| 193 | 169 | /// A pointer to the new node. |
| 194 | 170 | pub fn allocateNode(list: *Self, allocator: *Allocator) !*Node { |
| 195 | comptime assert(!isIntrusive()); | |
| 196 | 171 | return allocator.create(Node(undefined)); |
| 197 | 172 | } |
| 198 | 173 | |
| ... | ... | @@ -202,7 +177,6 @@ fn BaseLinkedList(comptime T: type, comptime ParentType: type, comptime field_na |
| 202 | 177 | /// node: Pointer to the node to deallocate. |
| 203 | 178 | /// allocator: Dynamic memory allocator. |
| 204 | 179 | pub fn destroyNode(list: *Self, node: *Node, allocator: *Allocator) void { |
| 205 | comptime assert(!isIntrusive()); | |
| 206 | 180 | allocator.destroy(node); |
| 207 | 181 | } |
| 208 | 182 | |
| ... | ... | @@ -214,8 +188,7 @@ fn BaseLinkedList(comptime T: type, comptime ParentType: type, comptime field_na |
| 214 | 188 | /// |
| 215 | 189 | /// Returns: |
| 216 | 190 | /// A pointer to the new node. |
| 217 | pub fn createNode(list: *Self, data: *const T, allocator: *Allocator) !*Node { | |
| 218 | comptime assert(!isIntrusive()); | |
| 191 | pub fn createNode(list: *Self, data: T, allocator: *Allocator) !*Node { | |
| 219 | 192 | var node = try list.allocateNode(allocator); |
| 220 | 193 | node.* = Node.init(data); |
| 221 | 194 | return node; |
| ... | ... | @@ -274,69 +247,3 @@ test "basic linked list test" { |
| 274 | 247 | assert(list.last.?.data == 4); |
| 275 | 248 | assert(list.len == 2); |
| 276 | 249 | } |
| 277 | ||
| 278 | const ElementList = IntrusiveLinkedList(Element, "link"); | |
| 279 | const Element = struct { | |
| 280 | value: u32, | |
| 281 | link: IntrusiveLinkedList(Element, "link").Node, | |
| 282 | }; | |
| 283 | ||
| 284 | test "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 | 577 | } |
| 578 | 578 | } |
| 579 | 579 | |
| 580 | return buf[0..buf_index]; | |
| 580 | return allocator.shrink(u8, buf, buf_index); | |
| 581 | 581 | } |
| 582 | 582 | |
| 583 | 583 | test "mem.join" { |
std/mutex.zig created+27| ... | ... | @@ -0,0 +1,27 @@ |
| 1 | const std = @import("index.zig"); | |
| 2 | const builtin = @import("builtin"); | |
| 3 | const AtomicOrder = builtin.AtomicOrder; | |
| 4 | const AtomicRmwOp = builtin.AtomicRmwOp; | |
| 5 | const assert = std.debug.assert; | |
| 6 | ||
| 7 | /// TODO use syscalls instead of a spinlock | |
| 8 | pub 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 | 482 | /// data is mach absolute time units |
| 483 | 483 | pub const NOTE_MACHTIME = 0x00000100; |
| 484 | 484 | |
| 485 | pub const AF_UNSPEC: c_int = 0; | |
| 486 | pub const AF_LOCAL: c_int = 1; | |
| 487 | pub const AF_UNIX: c_int = AF_LOCAL; | |
| 488 | pub const AF_INET: c_int = 2; | |
| 489 | pub const AF_SYS_CONTROL: c_int = 2; | |
| 490 | pub const AF_IMPLINK: c_int = 3; | |
| 491 | pub const AF_PUP: c_int = 4; | |
| 492 | pub const AF_CHAOS: c_int = 5; | |
| 493 | pub const AF_NS: c_int = 6; | |
| 494 | pub const AF_ISO: c_int = 7; | |
| 495 | pub const AF_OSI: c_int = AF_ISO; | |
| 496 | pub const AF_ECMA: c_int = 8; | |
| 497 | pub const AF_DATAKIT: c_int = 9; | |
| 498 | pub const AF_CCITT: c_int = 10; | |
| 499 | pub const AF_SNA: c_int = 11; | |
| 500 | pub const AF_DECnet: c_int = 12; | |
| 501 | pub const AF_DLI: c_int = 13; | |
| 502 | pub const AF_LAT: c_int = 14; | |
| 503 | pub const AF_HYLINK: c_int = 15; | |
| 504 | pub const AF_APPLETALK: c_int = 16; | |
| 505 | pub const AF_ROUTE: c_int = 17; | |
| 506 | pub const AF_LINK: c_int = 18; | |
| 507 | pub const AF_XTP: c_int = 19; | |
| 508 | pub const AF_COIP: c_int = 20; | |
| 509 | pub const AF_CNT: c_int = 21; | |
| 510 | pub const AF_RTIP: c_int = 22; | |
| 511 | pub const AF_IPX: c_int = 23; | |
| 512 | pub const AF_SIP: c_int = 24; | |
| 513 | pub const AF_PIP: c_int = 25; | |
| 514 | pub const AF_ISDN: c_int = 28; | |
| 515 | pub const AF_E164: c_int = AF_ISDN; | |
| 516 | pub const AF_KEY: c_int = 29; | |
| 517 | pub const AF_INET6: c_int = 30; | |
| 518 | pub const AF_NATM: c_int = 31; | |
| 519 | pub const AF_SYSTEM: c_int = 32; | |
| 520 | pub const AF_NETBIOS: c_int = 33; | |
| 521 | pub const AF_PPP: c_int = 34; | |
| 522 | pub const AF_MAX: c_int = 40; | |
| 523 | ||
| 524 | pub const PF_UNSPEC: c_int = AF_UNSPEC; | |
| 525 | pub const PF_LOCAL: c_int = AF_LOCAL; | |
| 526 | pub const PF_UNIX: c_int = PF_LOCAL; | |
| 527 | pub const PF_INET: c_int = AF_INET; | |
| 528 | pub const PF_IMPLINK: c_int = AF_IMPLINK; | |
| 529 | pub const PF_PUP: c_int = AF_PUP; | |
| 530 | pub const PF_CHAOS: c_int = AF_CHAOS; | |
| 531 | pub const PF_NS: c_int = AF_NS; | |
| 532 | pub const PF_ISO: c_int = AF_ISO; | |
| 533 | pub const PF_OSI: c_int = AF_ISO; | |
| 534 | pub const PF_ECMA: c_int = AF_ECMA; | |
| 535 | pub const PF_DATAKIT: c_int = AF_DATAKIT; | |
| 536 | pub const PF_CCITT: c_int = AF_CCITT; | |
| 537 | pub const PF_SNA: c_int = AF_SNA; | |
| 538 | pub const PF_DECnet: c_int = AF_DECnet; | |
| 539 | pub const PF_DLI: c_int = AF_DLI; | |
| 540 | pub const PF_LAT: c_int = AF_LAT; | |
| 541 | pub const PF_HYLINK: c_int = AF_HYLINK; | |
| 542 | pub const PF_APPLETALK: c_int = AF_APPLETALK; | |
| 543 | pub const PF_ROUTE: c_int = AF_ROUTE; | |
| 544 | pub const PF_LINK: c_int = AF_LINK; | |
| 545 | pub const PF_XTP: c_int = AF_XTP; | |
| 546 | pub const PF_COIP: c_int = AF_COIP; | |
| 547 | pub const PF_CNT: c_int = AF_CNT; | |
| 548 | pub const PF_SIP: c_int = AF_SIP; | |
| 549 | pub const PF_IPX: c_int = AF_IPX; | |
| 550 | pub const PF_RTIP: c_int = AF_RTIP; | |
| 551 | pub const PF_PIP: c_int = AF_PIP; | |
| 552 | pub const PF_ISDN: c_int = AF_ISDN; | |
| 553 | pub const PF_KEY: c_int = AF_KEY; | |
| 554 | pub const PF_INET6: c_int = AF_INET6; | |
| 555 | pub const PF_NATM: c_int = AF_NATM; | |
| 556 | pub const PF_SYSTEM: c_int = AF_SYSTEM; | |
| 557 | pub const PF_NETBIOS: c_int = AF_NETBIOS; | |
| 558 | pub const PF_PPP: c_int = AF_PPP; | |
| 559 | pub const PF_MAX: c_int = AF_MAX; | |
| 560 | ||
| 561 | pub const SYSPROTO_EVENT: c_int = 1; | |
| 562 | pub const SYSPROTO_CONTROL: c_int = 2; | |
| 563 | ||
| 564 | pub const SOCK_STREAM: c_int = 1; | |
| 565 | pub const SOCK_DGRAM: c_int = 2; | |
| 566 | pub const SOCK_RAW: c_int = 3; | |
| 567 | pub const SOCK_RDM: c_int = 4; | |
| 568 | pub const SOCK_SEQPACKET: c_int = 5; | |
| 569 | pub const SOCK_MAXADDRLEN: c_int = 255; | |
| 485 | pub const AF_UNSPEC = 0; | |
| 486 | pub const AF_LOCAL = 1; | |
| 487 | pub const AF_UNIX = AF_LOCAL; | |
| 488 | pub const AF_INET = 2; | |
| 489 | pub const AF_SYS_CONTROL = 2; | |
| 490 | pub const AF_IMPLINK = 3; | |
| 491 | pub const AF_PUP = 4; | |
| 492 | pub const AF_CHAOS = 5; | |
| 493 | pub const AF_NS = 6; | |
| 494 | pub const AF_ISO = 7; | |
| 495 | pub const AF_OSI = AF_ISO; | |
| 496 | pub const AF_ECMA = 8; | |
| 497 | pub const AF_DATAKIT = 9; | |
| 498 | pub const AF_CCITT = 10; | |
| 499 | pub const AF_SNA = 11; | |
| 500 | pub const AF_DECnet = 12; | |
| 501 | pub const AF_DLI = 13; | |
| 502 | pub const AF_LAT = 14; | |
| 503 | pub const AF_HYLINK = 15; | |
| 504 | pub const AF_APPLETALK = 16; | |
| 505 | pub const AF_ROUTE = 17; | |
| 506 | pub const AF_LINK = 18; | |
| 507 | pub const AF_XTP = 19; | |
| 508 | pub const AF_COIP = 20; | |
| 509 | pub const AF_CNT = 21; | |
| 510 | pub const AF_RTIP = 22; | |
| 511 | pub const AF_IPX = 23; | |
| 512 | pub const AF_SIP = 24; | |
| 513 | pub const AF_PIP = 25; | |
| 514 | pub const AF_ISDN = 28; | |
| 515 | pub const AF_E164 = AF_ISDN; | |
| 516 | pub const AF_KEY = 29; | |
| 517 | pub const AF_INET6 = 30; | |
| 518 | pub const AF_NATM = 31; | |
| 519 | pub const AF_SYSTEM = 32; | |
| 520 | pub const AF_NETBIOS = 33; | |
| 521 | pub const AF_PPP = 34; | |
| 522 | pub const AF_MAX = 40; | |
| 523 | ||
| 524 | pub const PF_UNSPEC = AF_UNSPEC; | |
| 525 | pub const PF_LOCAL = AF_LOCAL; | |
| 526 | pub const PF_UNIX = PF_LOCAL; | |
| 527 | pub const PF_INET = AF_INET; | |
| 528 | pub const PF_IMPLINK = AF_IMPLINK; | |
| 529 | pub const PF_PUP = AF_PUP; | |
| 530 | pub const PF_CHAOS = AF_CHAOS; | |
| 531 | pub const PF_NS = AF_NS; | |
| 532 | pub const PF_ISO = AF_ISO; | |
| 533 | pub const PF_OSI = AF_ISO; | |
| 534 | pub const PF_ECMA = AF_ECMA; | |
| 535 | pub const PF_DATAKIT = AF_DATAKIT; | |
| 536 | pub const PF_CCITT = AF_CCITT; | |
| 537 | pub const PF_SNA = AF_SNA; | |
| 538 | pub const PF_DECnet = AF_DECnet; | |
| 539 | pub const PF_DLI = AF_DLI; | |
| 540 | pub const PF_LAT = AF_LAT; | |
| 541 | pub const PF_HYLINK = AF_HYLINK; | |
| 542 | pub const PF_APPLETALK = AF_APPLETALK; | |
| 543 | pub const PF_ROUTE = AF_ROUTE; | |
| 544 | pub const PF_LINK = AF_LINK; | |
| 545 | pub const PF_XTP = AF_XTP; | |
| 546 | pub const PF_COIP = AF_COIP; | |
| 547 | pub const PF_CNT = AF_CNT; | |
| 548 | pub const PF_SIP = AF_SIP; | |
| 549 | pub const PF_IPX = AF_IPX; | |
| 550 | pub const PF_RTIP = AF_RTIP; | |
| 551 | pub const PF_PIP = AF_PIP; | |
| 552 | pub const PF_ISDN = AF_ISDN; | |
| 553 | pub const PF_KEY = AF_KEY; | |
| 554 | pub const PF_INET6 = AF_INET6; | |
| 555 | pub const PF_NATM = AF_NATM; | |
| 556 | pub const PF_SYSTEM = AF_SYSTEM; | |
| 557 | pub const PF_NETBIOS = AF_NETBIOS; | |
| 558 | pub const PF_PPP = AF_PPP; | |
| 559 | pub const PF_MAX = AF_MAX; | |
| 560 | ||
| 561 | pub const SYSPROTO_EVENT = 1; | |
| 562 | pub const SYSPROTO_CONTROL = 2; | |
| 563 | ||
| 564 | pub const SOCK_STREAM = 1; | |
| 565 | pub const SOCK_DGRAM = 2; | |
| 566 | pub const SOCK_RAW = 3; | |
| 567 | pub const SOCK_RDM = 4; | |
| 568 | pub const SOCK_SEQPACKET = 5; | |
| 569 | pub const SOCK_MAXADDRLEN = 255; | |
| 570 | ||
| 571 | pub const IPPROTO_ICMP = 1; | |
| 572 | pub const IPPROTO_ICMPV6 = 58; | |
| 573 | pub const IPPROTO_TCP = 6; | |
| 574 | pub const IPPROTO_UDP = 17; | |
| 575 | pub const IPPROTO_IP = 0; | |
| 576 | pub const IPPROTO_IPV6 = 41; | |
| 570 | 577 | |
| 571 | 578 | fn wstatus(x: i32) i32 { |
| 572 | 579 | return x & 0o177; |
| ... | ... | @@ -605,6 +612,11 @@ pub fn abort() noreturn { |
| 605 | 612 | c.abort(); |
| 606 | 613 | } |
| 607 | 614 | |
| 615 | // bind(int socket, const struct sockaddr *address, socklen_t address_len) | |
| 616 | pub fn bind(fd: i32, addr: *const sockaddr, len: socklen_t) usize { | |
| 617 | return errnoWrap(c.bind(@bitCast(c_int, fd), addr, len)); | |
| 618 | } | |
| 619 | ||
| 608 | 620 | pub fn exit(code: i32) noreturn { |
| 609 | 621 | c.exit(code); |
| 610 | 622 | } |
| ... | ... | @@ -634,6 +646,10 @@ pub fn read(fd: i32, buf: [*]u8, nbyte: usize) usize { |
| 634 | 646 | return errnoWrap(c.read(fd, @ptrCast(*c_void, buf), nbyte)); |
| 635 | 647 | } |
| 636 | 648 | |
| 649 | pub 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 | ||
| 637 | 653 | pub fn stat(noalias path: [*]const u8, noalias buf: *stat) usize { |
| 638 | 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 | 658 | return errnoWrap(c.write(fd, @ptrCast(*const c_void, buf), nbyte)); |
| 643 | 659 | } |
| 644 | 660 | |
| 661 | pub 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 | ||
| 645 | 665 | pub fn mmap(address: ?[*]u8, length: usize, prot: usize, flags: u32, fd: i32, offset: isize) usize { |
| 646 | 666 | const ptr_result = c.mmap( |
| 647 | 667 | @ptrCast(*c_void, address), |
| ... | ... | @@ -805,6 +825,20 @@ pub fn sigaction(sig: u5, noalias act: *const Sigaction, noalias oact: ?*Sigacti |
| 805 | 825 | return result; |
| 806 | 826 | } |
| 807 | 827 | |
| 828 | pub 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 | ||
| 832 | pub const iovec = extern struct { | |
| 833 | iov_base: [*]u8, | |
| 834 | iov_len: usize, | |
| 835 | }; | |
| 836 | ||
| 837 | pub const iovec_const = extern struct { | |
| 838 | iov_base: [*]const u8, | |
| 839 | iov_len: usize, | |
| 840 | }; | |
| 841 | ||
| 808 | 842 | pub const sigset_t = c.sigset_t; |
| 809 | 843 | pub const empty_sigset = sigset_t(0); |
| 810 | 844 | |
| ... | ... | @@ -812,8 +846,13 @@ pub const timespec = c.timespec; |
| 812 | 846 | pub const Stat = c.Stat; |
| 813 | 847 | pub const dirent = c.dirent; |
| 814 | 848 | |
| 849 | pub const in_port_t = c.in_port_t; | |
| 815 | 850 | pub const sa_family_t = c.sa_family_t; |
| 851 | pub const socklen_t = c.socklen_t; | |
| 852 | ||
| 816 | 853 | pub const sockaddr = c.sockaddr; |
| 854 | pub const sockaddr_in = c.sockaddr_in; | |
| 855 | pub const sockaddr_in6 = c.sockaddr_in6; | |
| 817 | 856 | |
| 818 | 857 | /// Renamed from `kevent` to `Kevent` to avoid conflict with the syscall. |
| 819 | 858 | pub const Kevent = c.Kevent; |
std/os/file.zig+26-11| ... | ... | @@ -15,6 +15,16 @@ pub const File = struct { |
| 15 | 15 | /// The OS-specific file descriptor or file handle. |
| 16 | 16 | handle: os.FileHandle, |
| 17 | 17 | |
| 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 | 28 | pub const OpenError = os.WindowsOpenError || os.PosixOpenError; |
| 19 | 29 | |
| 20 | 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 | 49 | } |
| 40 | 50 | } |
| 41 | 51 | |
| 42 | /// Calls `openWriteMode` with os.default_file_mode for the mode. | |
| 52 | /// Calls `openWriteMode` with os.File.default_mode for the mode. | |
| 43 | 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 | } |
| 46 | 56 | |
| 47 | 57 | /// If the path does not exist it will be created. |
| 48 | 58 | /// If a file already exists in the destination it will be truncated. |
| 49 | 59 | /// `path` needs to be copied in memory to add a null terminating byte, hence the allocator. |
| 50 | 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 | 62 | if (is_posix) { |
| 53 | 63 | const flags = posix.O_LARGEFILE | posix.O_WRONLY | posix.O_CREAT | posix.O_CLOEXEC | posix.O_TRUNC; |
| 54 | 64 | const fd = try os.posixOpen(allocator, path, flags, file_mode); |
| ... | ... | @@ -72,7 +82,7 @@ pub const File = struct { |
| 72 | 82 | /// If a file already exists in the destination this returns OpenError.PathAlreadyExists |
| 73 | 83 | /// `path` needs to be copied in memory to add a null terminating byte, hence the allocator. |
| 74 | 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 | 86 | if (is_posix) { |
| 77 | 87 | const flags = posix.O_LARGEFILE | posix.O_WRONLY | posix.O_CREAT | posix.O_CLOEXEC | posix.O_EXCL; |
| 78 | 88 | const fd = try os.posixOpen(allocator, path, flags, file_mode); |
| ... | ... | @@ -282,7 +292,7 @@ pub const File = struct { |
| 282 | 292 | Unexpected, |
| 283 | 293 | }; |
| 284 | 294 | |
| 285 | pub fn mode(self: *File) ModeError!os.FileMode { | |
| 295 | pub fn mode(self: *File) ModeError!Mode { | |
| 286 | 296 | if (is_posix) { |
| 287 | 297 | var stat: posix.Stat = undefined; |
| 288 | 298 | const err = posix.getErrno(posix.fstat(self.handle, &stat)); |
| ... | ... | @@ -296,7 +306,7 @@ pub const File = struct { |
| 296 | 306 | |
| 297 | 307 | // TODO: we should be able to cast u16 to ModeError!u32, making this |
| 298 | 308 | // explicit cast not necessary |
| 299 | return os.FileMode(stat.mode); | |
| 309 | return Mode(stat.mode); | |
| 300 | 310 | } else if (is_windows) { |
| 301 | 311 | return {}; |
| 302 | 312 | } else { |
| ... | ... | @@ -305,9 +315,11 @@ pub const File = struct { |
| 305 | 315 | } |
| 306 | 316 | |
| 307 | 317 | pub const ReadError = error{ |
| 308 | BadFd, | |
| 309 | Io, | |
| 318 | FileClosed, | |
| 319 | InputOutput, | |
| 310 | 320 | IsDir, |
| 321 | WouldBlock, | |
| 322 | SystemResources, | |
| 311 | 323 | |
| 312 | 324 | Unexpected, |
| 313 | 325 | }; |
| ... | ... | @@ -323,9 +335,12 @@ pub const File = struct { |
| 323 | 335 | posix.EINTR => continue, |
| 324 | 336 | posix.EINVAL => unreachable, |
| 325 | 337 | posix.EFAULT => unreachable, |
| 326 | posix.EBADF => return error.BadFd, | |
| 327 | posix.EIO => return error.Io, | |
| 338 | posix.EAGAIN => return error.WouldBlock, | |
| 339 | posix.EBADF => return error.FileClosed, | |
| 340 | posix.EIO => return error.InputOutput, | |
| 328 | 341 | posix.EISDIR => return error.IsDir, |
| 342 | posix.ENOBUFS => return error.SystemResources, | |
| 343 | posix.ENOMEM => return error.SystemResources, | |
| 329 | 344 | else => return os.unexpectedErrorPosix(read_err), |
| 330 | 345 | } |
| 331 | 346 | } |
| ... | ... | @@ -338,7 +353,7 @@ pub const File = struct { |
| 338 | 353 | while (index < buffer.len) { |
| 339 | 354 | const want_read_count = @intCast(windows.DWORD, math.min(windows.DWORD(@maxValue(windows.DWORD)), buffer.len - index)); |
| 340 | 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 | 357 | const err = windows.GetLastError(); |
| 343 | 358 | return switch (err) { |
| 344 | 359 | windows.ERROR.OPERATION_ABORTED => continue, |
std/os/index.zig+169-12| ... | ... | @@ -38,16 +38,6 @@ pub const path = @import("path.zig"); |
| 38 | 38 | pub const File = @import("file.zig").File; |
| 39 | 39 | pub const time = @import("time.zig"); |
| 40 | 40 | |
| 41 | pub const FileMode = switch (builtin.os) { | |
| 42 | Os.windows => void, | |
| 43 | else => u32, | |
| 44 | }; | |
| 45 | ||
| 46 | pub const default_file_mode = switch (builtin.os) { | |
| 47 | Os.windows => {}, | |
| 48 | else => 0o666, | |
| 49 | }; | |
| 50 | ||
| 51 | 41 | pub const page_size = 4 * 1024; |
| 52 | 42 | |
| 53 | 43 | pub const UserInfo = @import("get_user_id.zig").UserInfo; |
| ... | ... | @@ -256,6 +246,67 @@ pub fn posixRead(fd: i32, buf: []u8) !void { |
| 256 | 246 | } |
| 257 | 247 | } |
| 258 | 248 | |
| 249 | /// Number of bytes read is returned. Upon reading end-of-file, zero is returned. | |
| 250 | pub 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 | ||
| 259 | 310 | pub const PosixWriteError = error{ |
| 260 | 311 | WouldBlock, |
| 261 | 312 | FileClosed, |
| ... | ... | @@ -300,6 +351,71 @@ pub fn posixWrite(fd: i32, bytes: []const u8) !void { |
| 300 | 351 | } |
| 301 | 352 | } |
| 302 | 353 | |
| 354 | pub 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 | ||
| 303 | 419 | pub const PosixOpenError = error{ |
| 304 | 420 | OutOfMemory, |
| 305 | 421 | AccessDenied, |
| ... | ... | @@ -853,7 +969,7 @@ pub fn copyFile(allocator: *Allocator, source_path: []const u8, dest_path: []con |
| 853 | 969 | /// Guaranteed to be atomic. However until https://patchwork.kernel.org/patch/9636735/ is |
| 854 | 970 | /// merged and readily available, |
| 855 | 971 | /// there is a possibility of power loss or application termination leaving temporary files present |
| 856 | pub fn copyFileMode(allocator: *Allocator, source_path: []const u8, dest_path: []const u8, mode: FileMode) !void { | |
| 972 | pub fn copyFileMode(allocator: *Allocator, source_path: []const u8, dest_path: []const u8, mode: File.Mode) !void { | |
| 857 | 973 | var in_file = try os.File.openRead(allocator, source_path); |
| 858 | 974 | defer in_file.close(); |
| 859 | 975 | |
| ... | ... | @@ -879,7 +995,7 @@ pub const AtomicFile = struct { |
| 879 | 995 | |
| 880 | 996 | /// dest_path must remain valid for the lifetime of AtomicFile |
| 881 | 997 | /// 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 | 999 | const dirname = os.path.dirname(dest_path); |
| 884 | 1000 | |
| 885 | 1001 | var rand_buf: [12]u8 = undefined; |
| ... | ... | @@ -2943,3 +3059,44 @@ pub fn bsdKEvent( |
| 2943 | 3059 | } |
| 2944 | 3060 | } |
| 2945 | 3061 | } |
| 3062 | ||
| 3063 | pub 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 | ||
| 3076 | pub 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 | ||
| 3093 | pub 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 | 567 | pub const MNT_EXPIRE = 4; |
| 568 | 568 | pub const UMOUNT_NOFOLLOW = 8; |
| 569 | 569 | |
| 570 | pub const IN_CLOEXEC = O_CLOEXEC; | |
| 571 | pub const IN_NONBLOCK = O_NONBLOCK; | |
| 572 | ||
| 573 | pub const IN_ACCESS = 0x00000001; | |
| 574 | pub const IN_MODIFY = 0x00000002; | |
| 575 | pub const IN_ATTRIB = 0x00000004; | |
| 576 | pub const IN_CLOSE_WRITE = 0x00000008; | |
| 577 | pub const IN_CLOSE_NOWRITE = 0x00000010; | |
| 578 | pub const IN_CLOSE = IN_CLOSE_WRITE | IN_CLOSE_NOWRITE; | |
| 579 | pub const IN_OPEN = 0x00000020; | |
| 580 | pub const IN_MOVED_FROM = 0x00000040; | |
| 581 | pub const IN_MOVED_TO = 0x00000080; | |
| 582 | pub const IN_MOVE = IN_MOVED_FROM | IN_MOVED_TO; | |
| 583 | pub const IN_CREATE = 0x00000100; | |
| 584 | pub const IN_DELETE = 0x00000200; | |
| 585 | pub const IN_DELETE_SELF = 0x00000400; | |
| 586 | pub const IN_MOVE_SELF = 0x00000800; | |
| 587 | pub const IN_ALL_EVENTS = 0x00000fff; | |
| 588 | ||
| 589 | pub const IN_UNMOUNT = 0x00002000; | |
| 590 | pub const IN_Q_OVERFLOW = 0x00004000; | |
| 591 | pub const IN_IGNORED = 0x00008000; | |
| 592 | ||
| 593 | pub const IN_ONLYDIR = 0x01000000; | |
| 594 | pub const IN_DONT_FOLLOW = 0x02000000; | |
| 595 | pub const IN_EXCL_UNLINK = 0x04000000; | |
| 596 | pub const IN_MASK_ADD = 0x20000000; | |
| 597 | ||
| 598 | pub const IN_ISDIR = 0x40000000; | |
| 599 | pub const IN_ONESHOT = 0x80000000; | |
| 600 | ||
| 570 | 601 | pub const S_IFMT = 0o170000; |
| 571 | 602 | |
| 572 | 603 | pub const S_IFDIR = 0o040000; |
| ... | ... | @@ -692,6 +723,10 @@ pub fn futex_wait(uaddr: usize, futex_op: u32, val: i32, timeout: ?*timespec) us |
| 692 | 723 | return syscall4(SYS_futex, uaddr, futex_op, @bitCast(u32, val), @ptrToInt(timeout)); |
| 693 | 724 | } |
| 694 | 725 | |
| 726 | pub fn futex_wake(uaddr: usize, futex_op: u32, val: i32) usize { | |
| 727 | return syscall3(SYS_futex, uaddr, futex_op, @bitCast(u32, val)); | |
| 728 | } | |
| 729 | ||
| 695 | 730 | pub fn getcwd(buf: [*]u8, size: usize) usize { |
| 696 | 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 | 735 | return syscall3(SYS_getdents, @intCast(usize, fd), @ptrToInt(dirp), count); |
| 701 | 736 | } |
| 702 | 737 | |
| 738 | pub fn inotify_init1(flags: u32) usize { | |
| 739 | return syscall1(SYS_inotify_init1, flags); | |
| 740 | } | |
| 741 | ||
| 742 | pub 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 | ||
| 746 | pub fn inotify_rm_watch(fd: i32, wd: i32) usize { | |
| 747 | return syscall2(SYS_inotify_rm_watch, @intCast(usize, fd), @intCast(usize, wd)); | |
| 748 | } | |
| 749 | ||
| 703 | 750 | pub fn isatty(fd: i32) bool { |
| 704 | 751 | var wsz: winsize = undefined; |
| 705 | 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 | 789 | return syscall3(SYS_read, @intCast(usize, fd), @ptrToInt(buf), count); |
| 743 | 790 | } |
| 744 | 791 | |
| 792 | pub 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 | ||
| 796 | pub 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 | 800 | // TODO https://github.com/ziglang/zig/issues/265 |
| 746 | 801 | pub fn rmdir(path: [*]const u8) usize { |
| 747 | 802 | return syscall1(SYS_rmdir, @ptrToInt(path)); |
| ... | ... | @@ -1064,6 +1119,11 @@ pub const iovec = extern struct { |
| 1064 | 1119 | iov_len: usize, |
| 1065 | 1120 | }; |
| 1066 | 1121 | |
| 1122 | pub const iovec_const = extern struct { | |
| 1123 | iov_base: [*]const u8, | |
| 1124 | iov_len: usize, | |
| 1125 | }; | |
| 1126 | ||
| 1067 | 1127 | pub fn getsockname(fd: i32, noalias addr: *sockaddr, noalias len: *socklen_t) usize { |
| 1068 | 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 | 1432 | return syscall2(SYS_capset, @ptrToInt(hdrp), @ptrToInt(datap)); |
| 1373 | 1433 | } |
| 1374 | 1434 | |
| 1435 | pub const inotify_event = extern struct { | |
| 1436 | wd: i32, | |
| 1437 | mask: u32, | |
| 1438 | cookie: u32, | |
| 1439 | len: u32, | |
| 1440 | //name: [?]u8, | |
| 1441 | }; | |
| 1442 | ||
| 1375 | 1443 | test "import" { |
| 1376 | 1444 | if (builtin.os == builtin.Os.linux) { |
| 1377 | 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 | 506 | result_index += 1; |
| 507 | 507 | } |
| 508 | 508 | |
| 509 | return result[0..result_index]; | |
| 509 | return allocator.shrink(u8, result, result_index); | |
| 510 | 510 | } |
| 511 | 511 | |
| 512 | 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 | 67 | pub const OVERLAPPED = extern struct { |
| 68 | 68 | Internal: ULONG_PTR, |
| 69 | 69 | InternalHigh: ULONG_PTR, |
| 70 | Pointer: PVOID, | |
| 71 | hEvent: HANDLE, | |
| 70 | Offset: DWORD, | |
| 71 | OffsetHigh: DWORD, | |
| 72 | hEvent: ?HANDLE, | |
| 72 | 73 | }; |
| 73 | 74 | pub const LPOVERLAPPED = *OVERLAPPED; |
| 74 | 75 | |
| ... | ... | @@ -350,3 +351,15 @@ pub const E_ACCESSDENIED = @bitCast(c_long, c_ulong(0x80070005)); |
| 350 | 351 | pub const E_HANDLE = @bitCast(c_long, c_ulong(0x80070006)); |
| 351 | 352 | pub const E_OUTOFMEMORY = @bitCast(c_long, c_ulong(0x8007000E)); |
| 352 | 353 | pub const E_INVALIDARG = @bitCast(c_long, c_ulong(0x80070057)); |
| 354 | ||
| 355 | pub const FILE_FLAG_BACKUP_SEMANTICS = 0x02000000; | |
| 356 | pub const FILE_FLAG_DELETE_ON_CLOSE = 0x04000000; | |
| 357 | pub const FILE_FLAG_NO_BUFFERING = 0x20000000; | |
| 358 | pub const FILE_FLAG_OPEN_NO_RECALL = 0x00100000; | |
| 359 | pub const FILE_FLAG_OPEN_REPARSE_POINT = 0x00200000; | |
| 360 | pub const FILE_FLAG_OVERLAPPED = 0x40000000; | |
| 361 | pub const FILE_FLAG_POSIX_SEMANTICS = 0x0100000; | |
| 362 | pub const FILE_FLAG_RANDOM_ACCESS = 0x10000000; | |
| 363 | pub const FILE_FLAG_SESSION_AWARE = 0x00800000; | |
| 364 | pub const FILE_FLAG_SEQUENTIAL_SCAN = 0x08000000; | |
| 365 | pub const FILE_FLAG_WRITE_THROUGH = 0x80000000; |
std/os/windows/kernel32.zig+62-5| ... | ... | @@ -1,5 +1,8 @@ |
| 1 | 1 | use @import("index.zig"); |
| 2 | 2 | |
| 3 | ||
| 4 | pub extern "kernel32" stdcallcc fn CancelIoEx(hFile: HANDLE, lpOverlapped: LPOVERLAPPED) BOOL; | |
| 5 | ||
| 3 | 6 | pub extern "kernel32" stdcallcc fn CloseHandle(hObject: HANDLE) BOOL; |
| 4 | 7 | |
| 5 | 8 | pub extern "kernel32" stdcallcc fn CreateDirectoryA( |
| ... | ... | @@ -8,7 +11,17 @@ pub extern "kernel32" stdcallcc fn CreateDirectoryA( |
| 8 | 11 | ) BOOL; |
| 9 | 12 | |
| 10 | 13 | pub 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 | ||
| 23 | pub extern "kernel32" stdcallcc fn CreateFileW( | |
| 24 | lpFileName: [*]const u16, // TODO null terminated pointer type | |
| 12 | 25 | dwDesiredAccess: DWORD, |
| 13 | 26 | dwShareMode: DWORD, |
| 14 | 27 | lpSecurityAttributes: ?LPSECURITY_ATTRIBUTES, |
| ... | ... | @@ -94,6 +107,9 @@ pub extern "kernel32" stdcallcc fn GetFinalPathNameByHandleA( |
| 94 | 107 | dwFlags: DWORD, |
| 95 | 108 | ) DWORD; |
| 96 | 109 | |
| 110 | ||
| 111 | pub extern "kernel32" stdcallcc fn GetOverlappedResult(hFile: HANDLE, lpOverlapped: *OVERLAPPED, lpNumberOfBytesTransferred: *DWORD, bWait: BOOL) BOOL; | |
| 112 | ||
| 97 | 113 | pub extern "kernel32" stdcallcc fn GetProcessHeap() ?HANDLE; |
| 98 | 114 | pub extern "kernel32" stdcallcc fn GetQueuedCompletionStatus(CompletionPort: HANDLE, lpNumberOfBytesTransferred: LPDWORD, lpCompletionKey: *ULONG_PTR, lpOverlapped: *?*OVERLAPPED, dwMilliseconds: DWORD) BOOL; |
| 99 | 115 | |
| ... | ... | @@ -104,7 +120,6 @@ pub extern "kernel32" stdcallcc fn HeapCreate(flOptions: DWORD, dwInitialSize: S |
| 104 | 120 | pub extern "kernel32" stdcallcc fn HeapDestroy(hHeap: HANDLE) BOOL; |
| 105 | 121 | pub extern "kernel32" stdcallcc fn HeapReAlloc(hHeap: HANDLE, dwFlags: DWORD, lpMem: *c_void, dwBytes: SIZE_T) ?*c_void; |
| 106 | 122 | pub extern "kernel32" stdcallcc fn HeapSize(hHeap: HANDLE, dwFlags: DWORD, lpMem: *const c_void) SIZE_T; |
| 107 | pub extern "kernel32" stdcallcc fn HeapValidate(hHeap: HANDLE, dwFlags: DWORD, lpMem: *const c_void) BOOL; | |
| 108 | 123 | pub extern "kernel32" stdcallcc fn HeapCompact(hHeap: HANDLE, dwFlags: DWORD) SIZE_T; |
| 109 | 124 | pub extern "kernel32" stdcallcc fn HeapSummary(hHeap: HANDLE, dwFlags: DWORD, lpSummary: LPHEAP_SUMMARY) BOOL; |
| 110 | 125 | |
| ... | ... | @@ -114,6 +129,8 @@ pub extern "kernel32" stdcallcc fn HeapAlloc(hHeap: HANDLE, dwFlags: DWORD, dwBy |
| 114 | 129 | |
| 115 | 130 | pub extern "kernel32" stdcallcc fn HeapFree(hHeap: HANDLE, dwFlags: DWORD, lpMem: *c_void) BOOL; |
| 116 | 131 | |
| 132 | pub extern "kernel32" stdcallcc fn HeapValidate(hHeap: HANDLE, dwFlags: DWORD, lpMem: ?*const c_void) BOOL; | |
| 133 | ||
| 117 | 134 | pub extern "kernel32" stdcallcc fn MoveFileExA( |
| 118 | 135 | lpExistingFileName: LPCSTR, |
| 119 | 136 | lpNewFileName: LPCSTR, |
| ... | ... | @@ -126,11 +143,22 @@ pub extern "kernel32" stdcallcc fn QueryPerformanceCounter(lpPerformanceCount: * |
| 126 | 143 | |
| 127 | 144 | pub extern "kernel32" stdcallcc fn QueryPerformanceFrequency(lpFrequency: *LARGE_INTEGER) BOOL; |
| 128 | 145 | |
| 146 | pub 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 | ||
| 129 | 157 | pub extern "kernel32" stdcallcc fn ReadFile( |
| 130 | 158 | in_hFile: HANDLE, |
| 131 | out_lpBuffer: *c_void, | |
| 159 | out_lpBuffer: [*]u8, | |
| 132 | 160 | in_nNumberOfBytesToRead: DWORD, |
| 133 | out_lpNumberOfBytesRead: *DWORD, | |
| 161 | out_lpNumberOfBytesRead: ?*DWORD, | |
| 134 | 162 | in_out_lpOverlapped: ?*OVERLAPPED, |
| 135 | 163 | ) BOOL; |
| 136 | 164 | |
| ... | ... | @@ -153,13 +181,42 @@ pub extern "kernel32" stdcallcc fn WaitForSingleObject(hHandle: HANDLE, dwMillis |
| 153 | 181 | |
| 154 | 182 | pub extern "kernel32" stdcallcc fn WriteFile( |
| 155 | 183 | in_hFile: HANDLE, |
| 156 | in_lpBuffer: *const c_void, | |
| 184 | in_lpBuffer: [*]const u8, | |
| 157 | 185 | in_nNumberOfBytesToWrite: DWORD, |
| 158 | 186 | out_lpNumberOfBytesWritten: ?*DWORD, |
| 159 | 187 | in_out_lpOverlapped: ?*OVERLAPPED, |
| 160 | 188 | ) BOOL; |
| 161 | 189 | |
| 190 | pub extern "kernel32" stdcallcc fn WriteFileEx(hFile: HANDLE, lpBuffer: [*]const u8, nNumberOfBytesToWrite: DWORD, lpOverlapped: LPOVERLAPPED, lpCompletionRoutine: LPOVERLAPPED_COMPLETION_ROUTINE) BOOL; | |
| 191 | ||
| 162 | 192 | //TODO: call unicode versions instead of relying on ANSI code page |
| 163 | 193 | pub extern "kernel32" stdcallcc fn LoadLibraryA(lpLibFileName: LPCSTR) ?HMODULE; |
| 164 | 194 | |
| 165 | 195 | pub extern "kernel32" stdcallcc fn FreeLibrary(hModule: HMODULE) BOOL; |
| 196 | ||
| 197 | ||
| 198 | pub const FILE_NOTIFY_INFORMATION = extern struct { | |
| 199 | NextEntryOffset: DWORD, | |
| 200 | Action: DWORD, | |
| 201 | FileNameLength: DWORD, | |
| 202 | FileName: [1]WCHAR, | |
| 203 | }; | |
| 204 | ||
| 205 | pub const FILE_ACTION_ADDED = 0x00000001; | |
| 206 | pub const FILE_ACTION_REMOVED = 0x00000002; | |
| 207 | pub const FILE_ACTION_MODIFIED = 0x00000003; | |
| 208 | pub const FILE_ACTION_RENAMED_OLD_NAME = 0x00000004; | |
| 209 | pub const FILE_ACTION_RENAMED_NEW_NAME = 0x00000005; | |
| 210 | ||
| 211 | pub const LPOVERLAPPED_COMPLETION_ROUTINE = ?extern fn(DWORD, DWORD, *OVERLAPPED) void; | |
| 212 | ||
| 213 | pub const FILE_LIST_DIRECTORY = 1; | |
| 214 | ||
| 215 | pub const FILE_NOTIFY_CHANGE_CREATION = 64; | |
| 216 | pub const FILE_NOTIFY_CHANGE_SIZE = 8; | |
| 217 | pub const FILE_NOTIFY_CHANGE_SECURITY = 256; | |
| 218 | pub const FILE_NOTIFY_CHANGE_LAST_ACCESS = 32; | |
| 219 | pub const FILE_NOTIFY_CHANGE_LAST_WRITE = 16; | |
| 220 | pub const FILE_NOTIFY_CHANGE_DIR_NAME = 2; | |
| 221 | pub const FILE_NOTIFY_CHANGE_FILE_NAME = 1; | |
| 222 | pub 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 | 36 | pub const WriteError = error{ |
| 37 | 37 | SystemResources, |
| 38 | 38 | OperationAborted, |
| 39 | IoPending, | |
| 40 | 39 | BrokenPipe, |
| 41 | 40 | Unexpected, |
| 42 | 41 | }; |
| 43 | 42 | |
| 44 | 43 | pub 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 | 45 | const err = windows.GetLastError(); |
| 47 | 46 | return switch (err) { |
| 48 | 47 | windows.ERROR.INVALID_USER_BUFFER => WriteError.SystemResources, |
| 49 | 48 | windows.ERROR.NOT_ENOUGH_MEMORY => WriteError.SystemResources, |
| 50 | 49 | windows.ERROR.OPERATION_ABORTED => WriteError.OperationAborted, |
| 51 | 50 | windows.ERROR.NOT_ENOUGH_QUOTA => WriteError.SystemResources, |
| 52 | windows.ERROR.IO_PENDING => WriteError.IoPending, | |
| 51 | windows.ERROR.IO_PENDING => unreachable, | |
| 53 | 52 | windows.ERROR.BROKEN_PIPE => WriteError.BrokenPipe, |
| 54 | 53 | else => os.unexpectedErrorWindows(err), |
| 55 | 54 | }; |
| ... | ... | @@ -221,6 +220,7 @@ pub fn windowsCreateIoCompletionPort(file_handle: windows.HANDLE, existing_compl |
| 221 | 220 | const handle = windows.CreateIoCompletionPort(file_handle, existing_completion_port, completion_key, concurrent_thread_count) orelse { |
| 222 | 221 | const err = windows.GetLastError(); |
| 223 | 222 | switch (err) { |
| 223 | windows.ERROR.INVALID_PARAMETER => unreachable, | |
| 224 | 224 | else => return os.unexpectedErrorWindows(err), |
| 225 | 225 | } |
| 226 | 226 | }; |
| ... | ... | @@ -238,21 +238,24 @@ pub fn windowsPostQueuedCompletionStatus(completion_port: windows.HANDLE, bytes_ |
| 238 | 238 | } |
| 239 | 239 | } |
| 240 | 240 | |
| 241 | pub const WindowsWaitResult = error{ | |
| 241 | pub const WindowsWaitResult = enum{ | |
| 242 | 242 | Normal, |
| 243 | 243 | Aborted, |
| 244 | Cancelled, | |
| 244 | 245 | }; |
| 245 | 246 | |
| 246 | 247 | pub fn windowsGetQueuedCompletionStatus(completion_port: windows.HANDLE, bytes_transferred_count: *windows.DWORD, lpCompletionKey: *usize, lpOverlapped: *?*windows.OVERLAPPED, dwMilliseconds: windows.DWORD) WindowsWaitResult { |
| 247 | 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(); | |
| 250 | if (err != windows.ERROR.ABANDONED_WAIT_0) { | |
| 251 | std.debug.warn("err: {}\n", err); | |
| 249 | const err = windows.GetLastError(); | |
| 250 | switch (err) { | |
| 251 | windows.ERROR.ABANDONED_WAIT_0 => return WindowsWaitResult.Aborted, | |
| 252 | windows.ERROR.OPERATION_ABORTED => return WindowsWaitResult.Cancelled, | |
| 253 | else => { | |
| 254 | if (std.debug.runtime_safety) { | |
| 255 | std.debug.panic("unexpected error: {}\n", err); | |
| 256 | } | |
| 252 | 257 | } |
| 253 | assert(err == windows.ERROR.ABANDONED_WAIT_0); | |
| 254 | 258 | } |
| 255 | return WindowsWaitResult.Aborted; | |
| 256 | 259 | } |
| 257 | 260 | return WindowsWaitResult.Normal; |
| 258 | 261 | } |
std/segmented_list.zig+13-5| ... | ... | @@ -2,7 +2,7 @@ const std = @import("index.zig"); |
| 2 | 2 | const assert = std.debug.assert; |
| 3 | 3 | const Allocator = std.mem.Allocator; |
| 4 | 4 | |
| 5 | // Imagine that `fn at(self: &Self, index: usize) &T` is a customer asking for a box | |
| 5 | // Imagine that `fn at(self: *Self, index: usize) &T` is a customer asking for a box | |
| 6 | 6 | // from a warehouse, based on a flat array, boxes ordered from 0 to N - 1. |
| 7 | 7 | // But the warehouse actually stores boxes in shelves of increasing powers of 2 sizes. |
| 8 | 8 | // 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 | 93 | |
| 94 | 94 | pub const prealloc_count = prealloc_item_count; |
| 95 | 95 | |
| 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 | 104 | /// Deinitialize with `deinit` |
| 97 | 105 | pub fn init(allocator: *Allocator) Self { |
| 98 | 106 | return Self{ |
| ... | ... | @@ -109,7 +117,7 @@ pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type |
| 109 | 117 | self.* = undefined; |
| 110 | 118 | } |
| 111 | 119 | |
| 112 | pub fn at(self: *Self, i: usize) *T { | |
| 120 | pub fn at(self: var, i: usize) AtType(@typeOf(self)) { | |
| 113 | 121 | assert(i < self.len); |
| 114 | 122 | return self.uncheckedAt(i); |
| 115 | 123 | } |
| ... | ... | @@ -133,7 +141,7 @@ pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type |
| 133 | 141 | if (self.len == 0) return null; |
| 134 | 142 | |
| 135 | 143 | const index = self.len - 1; |
| 136 | const result = self.uncheckedAt(index).*; | |
| 144 | const result = uncheckedAt(self, index).*; | |
| 137 | 145 | self.len = index; |
| 138 | 146 | return result; |
| 139 | 147 | } |
| ... | ... | @@ -141,7 +149,7 @@ pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type |
| 141 | 149 | pub fn addOne(self: *Self) !*T { |
| 142 | 150 | const new_length = self.len + 1; |
| 143 | 151 | try self.growCapacity(new_length); |
| 144 | const result = self.uncheckedAt(self.len); | |
| 152 | const result = uncheckedAt(self, self.len); | |
| 145 | 153 | self.len = new_length; |
| 146 | 154 | return result; |
| 147 | 155 | } |
| ... | ... | @@ -193,7 +201,7 @@ pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type |
| 193 | 201 | self.dynamic_segments = self.allocator.shrink([*]T, self.dynamic_segments, new_cap_shelf_count); |
| 194 | 202 | } |
| 195 | 203 | |
| 196 | pub fn uncheckedAt(self: *Self, index: usize) *T { | |
| 204 | pub fn uncheckedAt(self: var, index: usize) AtType(@typeOf(self)) { | |
| 197 | 205 | if (index < prealloc_item_count) { |
| 198 | 206 | return &self.prealloc_segment[index]; |
| 199 | 207 | } |
std/special/build_runner.zig+2-2| ... | ... | @@ -72,10 +72,10 @@ pub fn main() !void { |
| 72 | 72 | if (mem.indexOfScalar(u8, option_contents, '=')) |name_end| { |
| 73 | 73 | const option_name = option_contents[0..name_end]; |
| 74 | 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 | 76 | return usageAndErr(&builder, false, try stderr_stream); |
| 77 | 77 | } else { |
| 78 | if (builder.addUserInputFlag(option_contents)) | |
| 78 | if (try builder.addUserInputFlag(option_contents)) | |
| 79 | 79 | return usageAndErr(&builder, false, try stderr_stream); |
| 80 | 80 | } |
| 81 | 81 | } else if (mem.startsWith(u8, arg, "-")) { |
std/unicode.zig+19-1| ... | ... | @@ -188,6 +188,7 @@ pub const Utf8View = struct { |
| 188 | 188 | return Utf8View{ .bytes = s }; |
| 189 | 189 | } |
| 190 | 190 | |
| 191 | /// TODO: https://github.com/ziglang/zig/issues/425 | |
| 191 | 192 | pub fn initComptime(comptime s: []const u8) Utf8View { |
| 192 | 193 | if (comptime init(s)) |r| { |
| 193 | 194 | return r; |
| ... | ... | @@ -199,7 +200,7 @@ pub const Utf8View = struct { |
| 199 | 200 | } |
| 200 | 201 | } |
| 201 | 202 | |
| 202 | pub fn iterator(s: *const Utf8View) Utf8Iterator { | |
| 203 | pub fn iterator(s: Utf8View) Utf8Iterator { | |
| 203 | 204 | return Utf8Iterator{ |
| 204 | 205 | .bytes = s.bytes, |
| 205 | 206 | .i = 0, |
| ... | ... | @@ -530,3 +531,20 @@ test "utf16leToUtf8" { |
| 530 | 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 | |
| 537 | pub 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 | 32 | return self.source[token.start..token.end]; |
| 33 | 33 | } |
| 34 | 34 | |
| 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 | 41 | pub const Location = struct { |
| 36 | 42 | line: usize, |
| 37 | 43 | column: usize, |
| ... | ... | @@ -338,7 +344,7 @@ pub const Node = struct { |
| 338 | 344 | unreachable; |
| 339 | 345 | } |
| 340 | 346 | |
| 341 | pub fn firstToken(base: *Node) TokenIndex { | |
| 347 | pub fn firstToken(base: *const Node) TokenIndex { | |
| 342 | 348 | comptime var i = 0; |
| 343 | 349 | inline while (i < @memberCount(Id)) : (i += 1) { |
| 344 | 350 | if (base.id == @field(Id, @memberName(Id, i))) { |
| ... | ... | @@ -349,7 +355,7 @@ pub const Node = struct { |
| 349 | 355 | unreachable; |
| 350 | 356 | } |
| 351 | 357 | |
| 352 | pub fn lastToken(base: *Node) TokenIndex { | |
| 358 | pub fn lastToken(base: *const Node) TokenIndex { | |
| 353 | 359 | comptime var i = 0; |
| 354 | 360 | inline while (i < @memberCount(Id)) : (i += 1) { |
| 355 | 361 | if (base.id == @field(Id, @memberName(Id, i))) { |
| ... | ... | @@ -473,11 +479,11 @@ pub const Node = struct { |
| 473 | 479 | return null; |
| 474 | 480 | } |
| 475 | 481 | |
| 476 | pub fn firstToken(self: *Root) TokenIndex { | |
| 482 | pub fn firstToken(self: *const Root) TokenIndex { | |
| 477 | 483 | return if (self.decls.len == 0) self.eof_token else (self.decls.at(0).*).firstToken(); |
| 478 | 484 | } |
| 479 | 485 | |
| 480 | pub fn lastToken(self: *Root) TokenIndex { | |
| 486 | pub fn lastToken(self: *const Root) TokenIndex { | |
| 481 | 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 | 524 | return null; |
| 519 | 525 | } |
| 520 | 526 | |
| 521 | pub fn firstToken(self: *VarDecl) TokenIndex { | |
| 527 | pub fn firstToken(self: *const VarDecl) TokenIndex { | |
| 522 | 528 | if (self.visib_token) |visib_token| return visib_token; |
| 523 | 529 | if (self.comptime_token) |comptime_token| return comptime_token; |
| 524 | 530 | if (self.extern_export_token) |extern_export_token| return extern_export_token; |
| ... | ... | @@ -526,7 +532,7 @@ pub const Node = struct { |
| 526 | 532 | return self.mut_token; |
| 527 | 533 | } |
| 528 | 534 | |
| 529 | pub fn lastToken(self: *VarDecl) TokenIndex { | |
| 535 | pub fn lastToken(self: *const VarDecl) TokenIndex { | |
| 530 | 536 | return self.semicolon_token; |
| 531 | 537 | } |
| 532 | 538 | }; |
| ... | ... | @@ -548,12 +554,12 @@ pub const Node = struct { |
| 548 | 554 | return null; |
| 549 | 555 | } |
| 550 | 556 | |
| 551 | pub fn firstToken(self: *Use) TokenIndex { | |
| 557 | pub fn firstToken(self: *const Use) TokenIndex { | |
| 552 | 558 | if (self.visib_token) |visib_token| return visib_token; |
| 553 | 559 | return self.use_token; |
| 554 | 560 | } |
| 555 | 561 | |
| 556 | pub fn lastToken(self: *Use) TokenIndex { | |
| 562 | pub fn lastToken(self: *const Use) TokenIndex { | |
| 557 | 563 | return self.semicolon_token; |
| 558 | 564 | } |
| 559 | 565 | }; |
| ... | ... | @@ -575,11 +581,11 @@ pub const Node = struct { |
| 575 | 581 | return null; |
| 576 | 582 | } |
| 577 | 583 | |
| 578 | pub fn firstToken(self: *ErrorSetDecl) TokenIndex { | |
| 584 | pub fn firstToken(self: *const ErrorSetDecl) TokenIndex { | |
| 579 | 585 | return self.error_token; |
| 580 | 586 | } |
| 581 | 587 | |
| 582 | pub fn lastToken(self: *ErrorSetDecl) TokenIndex { | |
| 588 | pub fn lastToken(self: *const ErrorSetDecl) TokenIndex { | |
| 583 | 589 | return self.rbrace_token; |
| 584 | 590 | } |
| 585 | 591 | }; |
| ... | ... | @@ -618,14 +624,14 @@ pub const Node = struct { |
| 618 | 624 | return null; |
| 619 | 625 | } |
| 620 | 626 | |
| 621 | pub fn firstToken(self: *ContainerDecl) TokenIndex { | |
| 627 | pub fn firstToken(self: *const ContainerDecl) TokenIndex { | |
| 622 | 628 | if (self.layout_token) |layout_token| { |
| 623 | 629 | return layout_token; |
| 624 | 630 | } |
| 625 | 631 | return self.kind_token; |
| 626 | 632 | } |
| 627 | 633 | |
| 628 | pub fn lastToken(self: *ContainerDecl) TokenIndex { | |
| 634 | pub fn lastToken(self: *const ContainerDecl) TokenIndex { | |
| 629 | 635 | return self.rbrace_token; |
| 630 | 636 | } |
| 631 | 637 | }; |
| ... | ... | @@ -646,12 +652,12 @@ pub const Node = struct { |
| 646 | 652 | return null; |
| 647 | 653 | } |
| 648 | 654 | |
| 649 | pub fn firstToken(self: *StructField) TokenIndex { | |
| 655 | pub fn firstToken(self: *const StructField) TokenIndex { | |
| 650 | 656 | if (self.visib_token) |visib_token| return visib_token; |
| 651 | 657 | return self.name_token; |
| 652 | 658 | } |
| 653 | 659 | |
| 654 | pub fn lastToken(self: *StructField) TokenIndex { | |
| 660 | pub fn lastToken(self: *const StructField) TokenIndex { | |
| 655 | 661 | return self.type_expr.lastToken(); |
| 656 | 662 | } |
| 657 | 663 | }; |
| ... | ... | @@ -679,11 +685,11 @@ pub const Node = struct { |
| 679 | 685 | return null; |
| 680 | 686 | } |
| 681 | 687 | |
| 682 | pub fn firstToken(self: *UnionTag) TokenIndex { | |
| 688 | pub fn firstToken(self: *const UnionTag) TokenIndex { | |
| 683 | 689 | return self.name_token; |
| 684 | 690 | } |
| 685 | 691 | |
| 686 | pub fn lastToken(self: *UnionTag) TokenIndex { | |
| 692 | pub fn lastToken(self: *const UnionTag) TokenIndex { | |
| 687 | 693 | if (self.value_expr) |value_expr| { |
| 688 | 694 | return value_expr.lastToken(); |
| 689 | 695 | } |
| ... | ... | @@ -712,11 +718,11 @@ pub const Node = struct { |
| 712 | 718 | return null; |
| 713 | 719 | } |
| 714 | 720 | |
| 715 | pub fn firstToken(self: *EnumTag) TokenIndex { | |
| 721 | pub fn firstToken(self: *const EnumTag) TokenIndex { | |
| 716 | 722 | return self.name_token; |
| 717 | 723 | } |
| 718 | 724 | |
| 719 | pub fn lastToken(self: *EnumTag) TokenIndex { | |
| 725 | pub fn lastToken(self: *const EnumTag) TokenIndex { | |
| 720 | 726 | if (self.value) |value| { |
| 721 | 727 | return value.lastToken(); |
| 722 | 728 | } |
| ... | ... | @@ -741,11 +747,11 @@ pub const Node = struct { |
| 741 | 747 | return null; |
| 742 | 748 | } |
| 743 | 749 | |
| 744 | pub fn firstToken(self: *ErrorTag) TokenIndex { | |
| 750 | pub fn firstToken(self: *const ErrorTag) TokenIndex { | |
| 745 | 751 | return self.name_token; |
| 746 | 752 | } |
| 747 | 753 | |
| 748 | pub fn lastToken(self: *ErrorTag) TokenIndex { | |
| 754 | pub fn lastToken(self: *const ErrorTag) TokenIndex { | |
| 749 | 755 | return self.name_token; |
| 750 | 756 | } |
| 751 | 757 | }; |
| ... | ... | @@ -758,11 +764,11 @@ pub const Node = struct { |
| 758 | 764 | return null; |
| 759 | 765 | } |
| 760 | 766 | |
| 761 | pub fn firstToken(self: *Identifier) TokenIndex { | |
| 767 | pub fn firstToken(self: *const Identifier) TokenIndex { | |
| 762 | 768 | return self.token; |
| 763 | 769 | } |
| 764 | 770 | |
| 765 | pub fn lastToken(self: *Identifier) TokenIndex { | |
| 771 | pub fn lastToken(self: *const Identifier) TokenIndex { | |
| 766 | 772 | return self.token; |
| 767 | 773 | } |
| 768 | 774 | }; |
| ... | ... | @@ -784,11 +790,11 @@ pub const Node = struct { |
| 784 | 790 | return null; |
| 785 | 791 | } |
| 786 | 792 | |
| 787 | pub fn firstToken(self: *AsyncAttribute) TokenIndex { | |
| 793 | pub fn firstToken(self: *const AsyncAttribute) TokenIndex { | |
| 788 | 794 | return self.async_token; |
| 789 | 795 | } |
| 790 | 796 | |
| 791 | pub fn lastToken(self: *AsyncAttribute) TokenIndex { | |
| 797 | pub fn lastToken(self: *const AsyncAttribute) TokenIndex { | |
| 792 | 798 | if (self.rangle_bracket) |rangle_bracket| { |
| 793 | 799 | return rangle_bracket; |
| 794 | 800 | } |
| ... | ... | @@ -856,7 +862,7 @@ pub const Node = struct { |
| 856 | 862 | return null; |
| 857 | 863 | } |
| 858 | 864 | |
| 859 | pub fn firstToken(self: *FnProto) TokenIndex { | |
| 865 | pub fn firstToken(self: *const FnProto) TokenIndex { | |
| 860 | 866 | if (self.visib_token) |visib_token| return visib_token; |
| 861 | 867 | if (self.async_attr) |async_attr| return async_attr.firstToken(); |
| 862 | 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 | 871 | return self.fn_token; |
| 866 | 872 | } |
| 867 | 873 | |
| 868 | pub fn lastToken(self: *FnProto) TokenIndex { | |
| 874 | pub fn lastToken(self: *const FnProto) TokenIndex { | |
| 869 | 875 | if (self.body_node) |body_node| return body_node.lastToken(); |
| 870 | 876 | switch (self.return_type) { |
| 871 | 877 | // TODO allow this and next prong to share bodies since the types are the same |
| ... | ... | @@ -896,11 +902,11 @@ pub const Node = struct { |
| 896 | 902 | return null; |
| 897 | 903 | } |
| 898 | 904 | |
| 899 | pub fn firstToken(self: *PromiseType) TokenIndex { | |
| 905 | pub fn firstToken(self: *const PromiseType) TokenIndex { | |
| 900 | 906 | return self.promise_token; |
| 901 | 907 | } |
| 902 | 908 | |
| 903 | pub fn lastToken(self: *PromiseType) TokenIndex { | |
| 909 | pub fn lastToken(self: *const PromiseType) TokenIndex { | |
| 904 | 910 | if (self.result) |result| return result.return_type.lastToken(); |
| 905 | 911 | return self.promise_token; |
| 906 | 912 | } |
| ... | ... | @@ -923,14 +929,14 @@ pub const Node = struct { |
| 923 | 929 | return null; |
| 924 | 930 | } |
| 925 | 931 | |
| 926 | pub fn firstToken(self: *ParamDecl) TokenIndex { | |
| 932 | pub fn firstToken(self: *const ParamDecl) TokenIndex { | |
| 927 | 933 | if (self.comptime_token) |comptime_token| return comptime_token; |
| 928 | 934 | if (self.noalias_token) |noalias_token| return noalias_token; |
| 929 | 935 | if (self.name_token) |name_token| return name_token; |
| 930 | 936 | return self.type_node.firstToken(); |
| 931 | 937 | } |
| 932 | 938 | |
| 933 | pub fn lastToken(self: *ParamDecl) TokenIndex { | |
| 939 | pub fn lastToken(self: *const ParamDecl) TokenIndex { | |
| 934 | 940 | if (self.var_args_token) |var_args_token| return var_args_token; |
| 935 | 941 | return self.type_node.lastToken(); |
| 936 | 942 | } |
| ... | ... | @@ -954,7 +960,7 @@ pub const Node = struct { |
| 954 | 960 | return null; |
| 955 | 961 | } |
| 956 | 962 | |
| 957 | pub fn firstToken(self: *Block) TokenIndex { | |
| 963 | pub fn firstToken(self: *const Block) TokenIndex { | |
| 958 | 964 | if (self.label) |label| { |
| 959 | 965 | return label; |
| 960 | 966 | } |
| ... | ... | @@ -962,7 +968,7 @@ pub const Node = struct { |
| 962 | 968 | return self.lbrace; |
| 963 | 969 | } |
| 964 | 970 | |
| 965 | pub fn lastToken(self: *Block) TokenIndex { | |
| 971 | pub fn lastToken(self: *const Block) TokenIndex { | |
| 966 | 972 | return self.rbrace; |
| 967 | 973 | } |
| 968 | 974 | }; |
| ... | ... | @@ -981,11 +987,11 @@ pub const Node = struct { |
| 981 | 987 | return null; |
| 982 | 988 | } |
| 983 | 989 | |
| 984 | pub fn firstToken(self: *Defer) TokenIndex { | |
| 990 | pub fn firstToken(self: *const Defer) TokenIndex { | |
| 985 | 991 | return self.defer_token; |
| 986 | 992 | } |
| 987 | 993 | |
| 988 | pub fn lastToken(self: *Defer) TokenIndex { | |
| 994 | pub fn lastToken(self: *const Defer) TokenIndex { | |
| 989 | 995 | return self.expr.lastToken(); |
| 990 | 996 | } |
| 991 | 997 | }; |
| ... | ... | @@ -1005,11 +1011,11 @@ pub const Node = struct { |
| 1005 | 1011 | return null; |
| 1006 | 1012 | } |
| 1007 | 1013 | |
| 1008 | pub fn firstToken(self: *Comptime) TokenIndex { | |
| 1014 | pub fn firstToken(self: *const Comptime) TokenIndex { | |
| 1009 | 1015 | return self.comptime_token; |
| 1010 | 1016 | } |
| 1011 | 1017 | |
| 1012 | pub fn lastToken(self: *Comptime) TokenIndex { | |
| 1018 | pub fn lastToken(self: *const Comptime) TokenIndex { | |
| 1013 | 1019 | return self.expr.lastToken(); |
| 1014 | 1020 | } |
| 1015 | 1021 | }; |
| ... | ... | @@ -1029,11 +1035,11 @@ pub const Node = struct { |
| 1029 | 1035 | return null; |
| 1030 | 1036 | } |
| 1031 | 1037 | |
| 1032 | pub fn firstToken(self: *Payload) TokenIndex { | |
| 1038 | pub fn firstToken(self: *const Payload) TokenIndex { | |
| 1033 | 1039 | return self.lpipe; |
| 1034 | 1040 | } |
| 1035 | 1041 | |
| 1036 | pub fn lastToken(self: *Payload) TokenIndex { | |
| 1042 | pub fn lastToken(self: *const Payload) TokenIndex { | |
| 1037 | 1043 | return self.rpipe; |
| 1038 | 1044 | } |
| 1039 | 1045 | }; |
| ... | ... | @@ -1054,11 +1060,11 @@ pub const Node = struct { |
| 1054 | 1060 | return null; |
| 1055 | 1061 | } |
| 1056 | 1062 | |
| 1057 | pub fn firstToken(self: *PointerPayload) TokenIndex { | |
| 1063 | pub fn firstToken(self: *const PointerPayload) TokenIndex { | |
| 1058 | 1064 | return self.lpipe; |
| 1059 | 1065 | } |
| 1060 | 1066 | |
| 1061 | pub fn lastToken(self: *PointerPayload) TokenIndex { | |
| 1067 | pub fn lastToken(self: *const PointerPayload) TokenIndex { | |
| 1062 | 1068 | return self.rpipe; |
| 1063 | 1069 | } |
| 1064 | 1070 | }; |
| ... | ... | @@ -1085,11 +1091,11 @@ pub const Node = struct { |
| 1085 | 1091 | return null; |
| 1086 | 1092 | } |
| 1087 | 1093 | |
| 1088 | pub fn firstToken(self: *PointerIndexPayload) TokenIndex { | |
| 1094 | pub fn firstToken(self: *const PointerIndexPayload) TokenIndex { | |
| 1089 | 1095 | return self.lpipe; |
| 1090 | 1096 | } |
| 1091 | 1097 | |
| 1092 | pub fn lastToken(self: *PointerIndexPayload) TokenIndex { | |
| 1098 | pub fn lastToken(self: *const PointerIndexPayload) TokenIndex { | |
| 1093 | 1099 | return self.rpipe; |
| 1094 | 1100 | } |
| 1095 | 1101 | }; |
| ... | ... | @@ -1114,11 +1120,11 @@ pub const Node = struct { |
| 1114 | 1120 | return null; |
| 1115 | 1121 | } |
| 1116 | 1122 | |
| 1117 | pub fn firstToken(self: *Else) TokenIndex { | |
| 1123 | pub fn firstToken(self: *const Else) TokenIndex { | |
| 1118 | 1124 | return self.else_token; |
| 1119 | 1125 | } |
| 1120 | 1126 | |
| 1121 | pub fn lastToken(self: *Else) TokenIndex { | |
| 1127 | pub fn lastToken(self: *const Else) TokenIndex { | |
| 1122 | 1128 | return self.body.lastToken(); |
| 1123 | 1129 | } |
| 1124 | 1130 | }; |
| ... | ... | @@ -1146,11 +1152,11 @@ pub const Node = struct { |
| 1146 | 1152 | return null; |
| 1147 | 1153 | } |
| 1148 | 1154 | |
| 1149 | pub fn firstToken(self: *Switch) TokenIndex { | |
| 1155 | pub fn firstToken(self: *const Switch) TokenIndex { | |
| 1150 | 1156 | return self.switch_token; |
| 1151 | 1157 | } |
| 1152 | 1158 | |
| 1153 | pub fn lastToken(self: *Switch) TokenIndex { | |
| 1159 | pub fn lastToken(self: *const Switch) TokenIndex { | |
| 1154 | 1160 | return self.rbrace; |
| 1155 | 1161 | } |
| 1156 | 1162 | }; |
| ... | ... | @@ -1181,11 +1187,11 @@ pub const Node = struct { |
| 1181 | 1187 | return null; |
| 1182 | 1188 | } |
| 1183 | 1189 | |
| 1184 | pub fn firstToken(self: *SwitchCase) TokenIndex { | |
| 1190 | pub fn firstToken(self: *const SwitchCase) TokenIndex { | |
| 1185 | 1191 | return (self.items.at(0).*).firstToken(); |
| 1186 | 1192 | } |
| 1187 | 1193 | |
| 1188 | pub fn lastToken(self: *SwitchCase) TokenIndex { | |
| 1194 | pub fn lastToken(self: *const SwitchCase) TokenIndex { | |
| 1189 | 1195 | return self.expr.lastToken(); |
| 1190 | 1196 | } |
| 1191 | 1197 | }; |
| ... | ... | @@ -1198,11 +1204,11 @@ pub const Node = struct { |
| 1198 | 1204 | return null; |
| 1199 | 1205 | } |
| 1200 | 1206 | |
| 1201 | pub fn firstToken(self: *SwitchElse) TokenIndex { | |
| 1207 | pub fn firstToken(self: *const SwitchElse) TokenIndex { | |
| 1202 | 1208 | return self.token; |
| 1203 | 1209 | } |
| 1204 | 1210 | |
| 1205 | pub fn lastToken(self: *SwitchElse) TokenIndex { | |
| 1211 | pub fn lastToken(self: *const SwitchElse) TokenIndex { | |
| 1206 | 1212 | return self.token; |
| 1207 | 1213 | } |
| 1208 | 1214 | }; |
| ... | ... | @@ -1245,7 +1251,7 @@ pub const Node = struct { |
| 1245 | 1251 | return null; |
| 1246 | 1252 | } |
| 1247 | 1253 | |
| 1248 | pub fn firstToken(self: *While) TokenIndex { | |
| 1254 | pub fn firstToken(self: *const While) TokenIndex { | |
| 1249 | 1255 | if (self.label) |label| { |
| 1250 | 1256 | return label; |
| 1251 | 1257 | } |
| ... | ... | @@ -1257,7 +1263,7 @@ pub const Node = struct { |
| 1257 | 1263 | return self.while_token; |
| 1258 | 1264 | } |
| 1259 | 1265 | |
| 1260 | pub fn lastToken(self: *While) TokenIndex { | |
| 1266 | pub fn lastToken(self: *const While) TokenIndex { | |
| 1261 | 1267 | if (self.@"else") |@"else"| { |
| 1262 | 1268 | return @"else".body.lastToken(); |
| 1263 | 1269 | } |
| ... | ... | @@ -1298,7 +1304,7 @@ pub const Node = struct { |
| 1298 | 1304 | return null; |
| 1299 | 1305 | } |
| 1300 | 1306 | |
| 1301 | pub fn firstToken(self: *For) TokenIndex { | |
| 1307 | pub fn firstToken(self: *const For) TokenIndex { | |
| 1302 | 1308 | if (self.label) |label| { |
| 1303 | 1309 | return label; |
| 1304 | 1310 | } |
| ... | ... | @@ -1310,7 +1316,7 @@ pub const Node = struct { |
| 1310 | 1316 | return self.for_token; |
| 1311 | 1317 | } |
| 1312 | 1318 | |
| 1313 | pub fn lastToken(self: *For) TokenIndex { | |
| 1319 | pub fn lastToken(self: *const For) TokenIndex { | |
| 1314 | 1320 | if (self.@"else") |@"else"| { |
| 1315 | 1321 | return @"else".body.lastToken(); |
| 1316 | 1322 | } |
| ... | ... | @@ -1349,11 +1355,11 @@ pub const Node = struct { |
| 1349 | 1355 | return null; |
| 1350 | 1356 | } |
| 1351 | 1357 | |
| 1352 | pub fn firstToken(self: *If) TokenIndex { | |
| 1358 | pub fn firstToken(self: *const If) TokenIndex { | |
| 1353 | 1359 | return self.if_token; |
| 1354 | 1360 | } |
| 1355 | 1361 | |
| 1356 | pub fn lastToken(self: *If) TokenIndex { | |
| 1362 | pub fn lastToken(self: *const If) TokenIndex { | |
| 1357 | 1363 | if (self.@"else") |@"else"| { |
| 1358 | 1364 | return @"else".body.lastToken(); |
| 1359 | 1365 | } |
| ... | ... | @@ -1480,11 +1486,11 @@ pub const Node = struct { |
| 1480 | 1486 | return null; |
| 1481 | 1487 | } |
| 1482 | 1488 | |
| 1483 | pub fn firstToken(self: *InfixOp) TokenIndex { | |
| 1489 | pub fn firstToken(self: *const InfixOp) TokenIndex { | |
| 1484 | 1490 | return self.lhs.firstToken(); |
| 1485 | 1491 | } |
| 1486 | 1492 | |
| 1487 | pub fn lastToken(self: *InfixOp) TokenIndex { | |
| 1493 | pub fn lastToken(self: *const InfixOp) TokenIndex { | |
| 1488 | 1494 | return self.rhs.lastToken(); |
| 1489 | 1495 | } |
| 1490 | 1496 | }; |
| ... | ... | @@ -1570,11 +1576,11 @@ pub const Node = struct { |
| 1570 | 1576 | return null; |
| 1571 | 1577 | } |
| 1572 | 1578 | |
| 1573 | pub fn firstToken(self: *PrefixOp) TokenIndex { | |
| 1579 | pub fn firstToken(self: *const PrefixOp) TokenIndex { | |
| 1574 | 1580 | return self.op_token; |
| 1575 | 1581 | } |
| 1576 | 1582 | |
| 1577 | pub fn lastToken(self: *PrefixOp) TokenIndex { | |
| 1583 | pub fn lastToken(self: *const PrefixOp) TokenIndex { | |
| 1578 | 1584 | return self.rhs.lastToken(); |
| 1579 | 1585 | } |
| 1580 | 1586 | }; |
| ... | ... | @@ -1594,11 +1600,11 @@ pub const Node = struct { |
| 1594 | 1600 | return null; |
| 1595 | 1601 | } |
| 1596 | 1602 | |
| 1597 | pub fn firstToken(self: *FieldInitializer) TokenIndex { | |
| 1603 | pub fn firstToken(self: *const FieldInitializer) TokenIndex { | |
| 1598 | 1604 | return self.period_token; |
| 1599 | 1605 | } |
| 1600 | 1606 | |
| 1601 | pub fn lastToken(self: *FieldInitializer) TokenIndex { | |
| 1607 | pub fn lastToken(self: *const FieldInitializer) TokenIndex { | |
| 1602 | 1608 | return self.expr.lastToken(); |
| 1603 | 1609 | } |
| 1604 | 1610 | }; |
| ... | ... | @@ -1673,7 +1679,7 @@ pub const Node = struct { |
| 1673 | 1679 | return null; |
| 1674 | 1680 | } |
| 1675 | 1681 | |
| 1676 | pub fn firstToken(self: *SuffixOp) TokenIndex { | |
| 1682 | pub fn firstToken(self: *const SuffixOp) TokenIndex { | |
| 1677 | 1683 | switch (self.op) { |
| 1678 | 1684 | @TagType(Op).Call => |*call_info| if (call_info.async_attr) |async_attr| return async_attr.firstToken(), |
| 1679 | 1685 | else => {}, |
| ... | ... | @@ -1681,7 +1687,7 @@ pub const Node = struct { |
| 1681 | 1687 | return self.lhs.firstToken(); |
| 1682 | 1688 | } |
| 1683 | 1689 | |
| 1684 | pub fn lastToken(self: *SuffixOp) TokenIndex { | |
| 1690 | pub fn lastToken(self: *const SuffixOp) TokenIndex { | |
| 1685 | 1691 | return self.rtoken; |
| 1686 | 1692 | } |
| 1687 | 1693 | }; |
| ... | ... | @@ -1701,11 +1707,11 @@ pub const Node = struct { |
| 1701 | 1707 | return null; |
| 1702 | 1708 | } |
| 1703 | 1709 | |
| 1704 | pub fn firstToken(self: *GroupedExpression) TokenIndex { | |
| 1710 | pub fn firstToken(self: *const GroupedExpression) TokenIndex { | |
| 1705 | 1711 | return self.lparen; |
| 1706 | 1712 | } |
| 1707 | 1713 | |
| 1708 | pub fn lastToken(self: *GroupedExpression) TokenIndex { | |
| 1714 | pub fn lastToken(self: *const GroupedExpression) TokenIndex { | |
| 1709 | 1715 | return self.rparen; |
| 1710 | 1716 | } |
| 1711 | 1717 | }; |
| ... | ... | @@ -1749,11 +1755,11 @@ pub const Node = struct { |
| 1749 | 1755 | return null; |
| 1750 | 1756 | } |
| 1751 | 1757 | |
| 1752 | pub fn firstToken(self: *ControlFlowExpression) TokenIndex { | |
| 1758 | pub fn firstToken(self: *const ControlFlowExpression) TokenIndex { | |
| 1753 | 1759 | return self.ltoken; |
| 1754 | 1760 | } |
| 1755 | 1761 | |
| 1756 | pub fn lastToken(self: *ControlFlowExpression) TokenIndex { | |
| 1762 | pub fn lastToken(self: *const ControlFlowExpression) TokenIndex { | |
| 1757 | 1763 | if (self.rhs) |rhs| { |
| 1758 | 1764 | return rhs.lastToken(); |
| 1759 | 1765 | } |
| ... | ... | @@ -1792,11 +1798,11 @@ pub const Node = struct { |
| 1792 | 1798 | return null; |
| 1793 | 1799 | } |
| 1794 | 1800 | |
| 1795 | pub fn firstToken(self: *Suspend) TokenIndex { | |
| 1801 | pub fn firstToken(self: *const Suspend) TokenIndex { | |
| 1796 | 1802 | return self.suspend_token; |
| 1797 | 1803 | } |
| 1798 | 1804 | |
| 1799 | pub fn lastToken(self: *Suspend) TokenIndex { | |
| 1805 | pub fn lastToken(self: *const Suspend) TokenIndex { | |
| 1800 | 1806 | if (self.body) |body| { |
| 1801 | 1807 | return body.lastToken(); |
| 1802 | 1808 | } |
| ... | ... | @@ -1813,11 +1819,11 @@ pub const Node = struct { |
| 1813 | 1819 | return null; |
| 1814 | 1820 | } |
| 1815 | 1821 | |
| 1816 | pub fn firstToken(self: *IntegerLiteral) TokenIndex { | |
| 1822 | pub fn firstToken(self: *const IntegerLiteral) TokenIndex { | |
| 1817 | 1823 | return self.token; |
| 1818 | 1824 | } |
| 1819 | 1825 | |
| 1820 | pub fn lastToken(self: *IntegerLiteral) TokenIndex { | |
| 1826 | pub fn lastToken(self: *const IntegerLiteral) TokenIndex { | |
| 1821 | 1827 | return self.token; |
| 1822 | 1828 | } |
| 1823 | 1829 | }; |
| ... | ... | @@ -1830,11 +1836,11 @@ pub const Node = struct { |
| 1830 | 1836 | return null; |
| 1831 | 1837 | } |
| 1832 | 1838 | |
| 1833 | pub fn firstToken(self: *FloatLiteral) TokenIndex { | |
| 1839 | pub fn firstToken(self: *const FloatLiteral) TokenIndex { | |
| 1834 | 1840 | return self.token; |
| 1835 | 1841 | } |
| 1836 | 1842 | |
| 1837 | pub fn lastToken(self: *FloatLiteral) TokenIndex { | |
| 1843 | pub fn lastToken(self: *const FloatLiteral) TokenIndex { | |
| 1838 | 1844 | return self.token; |
| 1839 | 1845 | } |
| 1840 | 1846 | }; |
| ... | ... | @@ -1856,11 +1862,11 @@ pub const Node = struct { |
| 1856 | 1862 | return null; |
| 1857 | 1863 | } |
| 1858 | 1864 | |
| 1859 | pub fn firstToken(self: *BuiltinCall) TokenIndex { | |
| 1865 | pub fn firstToken(self: *const BuiltinCall) TokenIndex { | |
| 1860 | 1866 | return self.builtin_token; |
| 1861 | 1867 | } |
| 1862 | 1868 | |
| 1863 | pub fn lastToken(self: *BuiltinCall) TokenIndex { | |
| 1869 | pub fn lastToken(self: *const BuiltinCall) TokenIndex { | |
| 1864 | 1870 | return self.rparen_token; |
| 1865 | 1871 | } |
| 1866 | 1872 | }; |
| ... | ... | @@ -1873,11 +1879,11 @@ pub const Node = struct { |
| 1873 | 1879 | return null; |
| 1874 | 1880 | } |
| 1875 | 1881 | |
| 1876 | pub fn firstToken(self: *StringLiteral) TokenIndex { | |
| 1882 | pub fn firstToken(self: *const StringLiteral) TokenIndex { | |
| 1877 | 1883 | return self.token; |
| 1878 | 1884 | } |
| 1879 | 1885 | |
| 1880 | pub fn lastToken(self: *StringLiteral) TokenIndex { | |
| 1886 | pub fn lastToken(self: *const StringLiteral) TokenIndex { | |
| 1881 | 1887 | return self.token; |
| 1882 | 1888 | } |
| 1883 | 1889 | }; |
| ... | ... | @@ -1892,11 +1898,11 @@ pub const Node = struct { |
| 1892 | 1898 | return null; |
| 1893 | 1899 | } |
| 1894 | 1900 | |
| 1895 | pub fn firstToken(self: *MultilineStringLiteral) TokenIndex { | |
| 1901 | pub fn firstToken(self: *const MultilineStringLiteral) TokenIndex { | |
| 1896 | 1902 | return self.lines.at(0).*; |
| 1897 | 1903 | } |
| 1898 | 1904 | |
| 1899 | pub fn lastToken(self: *MultilineStringLiteral) TokenIndex { | |
| 1905 | pub fn lastToken(self: *const MultilineStringLiteral) TokenIndex { | |
| 1900 | 1906 | return self.lines.at(self.lines.len - 1).*; |
| 1901 | 1907 | } |
| 1902 | 1908 | }; |
| ... | ... | @@ -1909,11 +1915,11 @@ pub const Node = struct { |
| 1909 | 1915 | return null; |
| 1910 | 1916 | } |
| 1911 | 1917 | |
| 1912 | pub fn firstToken(self: *CharLiteral) TokenIndex { | |
| 1918 | pub fn firstToken(self: *const CharLiteral) TokenIndex { | |
| 1913 | 1919 | return self.token; |
| 1914 | 1920 | } |
| 1915 | 1921 | |
| 1916 | pub fn lastToken(self: *CharLiteral) TokenIndex { | |
| 1922 | pub fn lastToken(self: *const CharLiteral) TokenIndex { | |
| 1917 | 1923 | return self.token; |
| 1918 | 1924 | } |
| 1919 | 1925 | }; |
| ... | ... | @@ -1926,11 +1932,11 @@ pub const Node = struct { |
| 1926 | 1932 | return null; |
| 1927 | 1933 | } |
| 1928 | 1934 | |
| 1929 | pub fn firstToken(self: *BoolLiteral) TokenIndex { | |
| 1935 | pub fn firstToken(self: *const BoolLiteral) TokenIndex { | |
| 1930 | 1936 | return self.token; |
| 1931 | 1937 | } |
| 1932 | 1938 | |
| 1933 | pub fn lastToken(self: *BoolLiteral) TokenIndex { | |
| 1939 | pub fn lastToken(self: *const BoolLiteral) TokenIndex { | |
| 1934 | 1940 | return self.token; |
| 1935 | 1941 | } |
| 1936 | 1942 | }; |
| ... | ... | @@ -1943,11 +1949,11 @@ pub const Node = struct { |
| 1943 | 1949 | return null; |
| 1944 | 1950 | } |
| 1945 | 1951 | |
| 1946 | pub fn firstToken(self: *NullLiteral) TokenIndex { | |
| 1952 | pub fn firstToken(self: *const NullLiteral) TokenIndex { | |
| 1947 | 1953 | return self.token; |
| 1948 | 1954 | } |
| 1949 | 1955 | |
| 1950 | pub fn lastToken(self: *NullLiteral) TokenIndex { | |
| 1956 | pub fn lastToken(self: *const NullLiteral) TokenIndex { | |
| 1951 | 1957 | return self.token; |
| 1952 | 1958 | } |
| 1953 | 1959 | }; |
| ... | ... | @@ -1960,11 +1966,11 @@ pub const Node = struct { |
| 1960 | 1966 | return null; |
| 1961 | 1967 | } |
| 1962 | 1968 | |
| 1963 | pub fn firstToken(self: *UndefinedLiteral) TokenIndex { | |
| 1969 | pub fn firstToken(self: *const UndefinedLiteral) TokenIndex { | |
| 1964 | 1970 | return self.token; |
| 1965 | 1971 | } |
| 1966 | 1972 | |
| 1967 | pub fn lastToken(self: *UndefinedLiteral) TokenIndex { | |
| 1973 | pub fn lastToken(self: *const UndefinedLiteral) TokenIndex { | |
| 1968 | 1974 | return self.token; |
| 1969 | 1975 | } |
| 1970 | 1976 | }; |
| ... | ... | @@ -1977,11 +1983,11 @@ pub const Node = struct { |
| 1977 | 1983 | return null; |
| 1978 | 1984 | } |
| 1979 | 1985 | |
| 1980 | pub fn firstToken(self: *ThisLiteral) TokenIndex { | |
| 1986 | pub fn firstToken(self: *const ThisLiteral) TokenIndex { | |
| 1981 | 1987 | return self.token; |
| 1982 | 1988 | } |
| 1983 | 1989 | |
| 1984 | pub fn lastToken(self: *ThisLiteral) TokenIndex { | |
| 1990 | pub fn lastToken(self: *const ThisLiteral) TokenIndex { | |
| 1985 | 1991 | return self.token; |
| 1986 | 1992 | } |
| 1987 | 1993 | }; |
| ... | ... | @@ -2022,11 +2028,11 @@ pub const Node = struct { |
| 2022 | 2028 | return null; |
| 2023 | 2029 | } |
| 2024 | 2030 | |
| 2025 | pub fn firstToken(self: *AsmOutput) TokenIndex { | |
| 2031 | pub fn firstToken(self: *const AsmOutput) TokenIndex { | |
| 2026 | 2032 | return self.lbracket; |
| 2027 | 2033 | } |
| 2028 | 2034 | |
| 2029 | pub fn lastToken(self: *AsmOutput) TokenIndex { | |
| 2035 | pub fn lastToken(self: *const AsmOutput) TokenIndex { | |
| 2030 | 2036 | return self.rparen; |
| 2031 | 2037 | } |
| 2032 | 2038 | }; |
| ... | ... | @@ -2054,11 +2060,11 @@ pub const Node = struct { |
| 2054 | 2060 | return null; |
| 2055 | 2061 | } |
| 2056 | 2062 | |
| 2057 | pub fn firstToken(self: *AsmInput) TokenIndex { | |
| 2063 | pub fn firstToken(self: *const AsmInput) TokenIndex { | |
| 2058 | 2064 | return self.lbracket; |
| 2059 | 2065 | } |
| 2060 | 2066 | |
| 2061 | pub fn lastToken(self: *AsmInput) TokenIndex { | |
| 2067 | pub fn lastToken(self: *const AsmInput) TokenIndex { | |
| 2062 | 2068 | return self.rparen; |
| 2063 | 2069 | } |
| 2064 | 2070 | }; |
| ... | ... | @@ -2089,11 +2095,11 @@ pub const Node = struct { |
| 2089 | 2095 | return null; |
| 2090 | 2096 | } |
| 2091 | 2097 | |
| 2092 | pub fn firstToken(self: *Asm) TokenIndex { | |
| 2098 | pub fn firstToken(self: *const Asm) TokenIndex { | |
| 2093 | 2099 | return self.asm_token; |
| 2094 | 2100 | } |
| 2095 | 2101 | |
| 2096 | pub fn lastToken(self: *Asm) TokenIndex { | |
| 2102 | pub fn lastToken(self: *const Asm) TokenIndex { | |
| 2097 | 2103 | return self.rparen; |
| 2098 | 2104 | } |
| 2099 | 2105 | }; |
| ... | ... | @@ -2106,11 +2112,11 @@ pub const Node = struct { |
| 2106 | 2112 | return null; |
| 2107 | 2113 | } |
| 2108 | 2114 | |
| 2109 | pub fn firstToken(self: *Unreachable) TokenIndex { | |
| 2115 | pub fn firstToken(self: *const Unreachable) TokenIndex { | |
| 2110 | 2116 | return self.token; |
| 2111 | 2117 | } |
| 2112 | 2118 | |
| 2113 | pub fn lastToken(self: *Unreachable) TokenIndex { | |
| 2119 | pub fn lastToken(self: *const Unreachable) TokenIndex { | |
| 2114 | 2120 | return self.token; |
| 2115 | 2121 | } |
| 2116 | 2122 | }; |
| ... | ... | @@ -2123,11 +2129,11 @@ pub const Node = struct { |
| 2123 | 2129 | return null; |
| 2124 | 2130 | } |
| 2125 | 2131 | |
| 2126 | pub fn firstToken(self: *ErrorType) TokenIndex { | |
| 2132 | pub fn firstToken(self: *const ErrorType) TokenIndex { | |
| 2127 | 2133 | return self.token; |
| 2128 | 2134 | } |
| 2129 | 2135 | |
| 2130 | pub fn lastToken(self: *ErrorType) TokenIndex { | |
| 2136 | pub fn lastToken(self: *const ErrorType) TokenIndex { | |
| 2131 | 2137 | return self.token; |
| 2132 | 2138 | } |
| 2133 | 2139 | }; |
| ... | ... | @@ -2140,11 +2146,11 @@ pub const Node = struct { |
| 2140 | 2146 | return null; |
| 2141 | 2147 | } |
| 2142 | 2148 | |
| 2143 | pub fn firstToken(self: *VarType) TokenIndex { | |
| 2149 | pub fn firstToken(self: *const VarType) TokenIndex { | |
| 2144 | 2150 | return self.token; |
| 2145 | 2151 | } |
| 2146 | 2152 | |
| 2147 | pub fn lastToken(self: *VarType) TokenIndex { | |
| 2153 | pub fn lastToken(self: *const VarType) TokenIndex { | |
| 2148 | 2154 | return self.token; |
| 2149 | 2155 | } |
| 2150 | 2156 | }; |
| ... | ... | @@ -2159,11 +2165,11 @@ pub const Node = struct { |
| 2159 | 2165 | return null; |
| 2160 | 2166 | } |
| 2161 | 2167 | |
| 2162 | pub fn firstToken(self: *DocComment) TokenIndex { | |
| 2168 | pub fn firstToken(self: *const DocComment) TokenIndex { | |
| 2163 | 2169 | return self.lines.at(0).*; |
| 2164 | 2170 | } |
| 2165 | 2171 | |
| 2166 | pub fn lastToken(self: *DocComment) TokenIndex { | |
| 2172 | pub fn lastToken(self: *const DocComment) TokenIndex { | |
| 2167 | 2173 | return self.lines.at(self.lines.len - 1).*; |
| 2168 | 2174 | } |
| 2169 | 2175 | }; |
| ... | ... | @@ -2184,11 +2190,11 @@ pub const Node = struct { |
| 2184 | 2190 | return null; |
| 2185 | 2191 | } |
| 2186 | 2192 | |
| 2187 | pub fn firstToken(self: *TestDecl) TokenIndex { | |
| 2193 | pub fn firstToken(self: *const TestDecl) TokenIndex { | |
| 2188 | 2194 | return self.test_token; |
| 2189 | 2195 | } |
| 2190 | 2196 | |
| 2191 | pub fn lastToken(self: *TestDecl) TokenIndex { | |
| 2197 | pub fn lastToken(self: *const TestDecl) TokenIndex { | |
| 2192 | 2198 | return self.body_node.lastToken(); |
| 2193 | 2199 | } |
| 2194 | 2200 | }; |