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