| ... | ... | @@ -3,7 +3,7 @@ |
| 3 | 3 | // This file is part of [zig](https://ziglang.org/), which is MIT licensed. |
| 4 | 4 | // The MIT license requires this copyright notice to be included in all copies |
| 5 | 5 | // and substantial portions of the software. |
| 6 | | const std = @import("../std.zig"); |
| 6 | const std = @import("std"); |
| 7 | 7 | const builtin = @import("builtin"); |
| 8 | 8 | const event = std.event; |
| 9 | 9 | const assert = std.debug.assert; |
| ... | ... | @@ -24,14 +24,6 @@ const WatchEventId = enum { |
| 24 | 24 | Delete, |
| 25 | 25 | }; |
| 26 | 26 | |
| 27 | | fn eqlString(a: []const u16, b: []const u16) bool { |
| 28 | | return mem.eql(u16, a, b); |
| 29 | | } |
| 30 | | |
| 31 | | fn hashString(s: []const u16) u32 { |
| 32 | | return @truncate(u32, std.hash.Wyhash.hash(0, mem.sliceAsBytes(s))); |
| 33 | | } |
| 34 | | |
| 35 | 27 | const WatchEventError = error{ |
| 36 | 28 | UserResourceLimitReached, |
| 37 | 29 | SystemResources, |
| ... | ... | @@ -69,21 +61,15 @@ pub fn Watch(comptime V: type) type { |
| 69 | 61 | const WindowsOsData = struct { |
| 70 | 62 | table_lock: event.Lock, |
| 71 | 63 | dir_table: DirTable, |
| 72 | | all_putters: std.atomic.Queue(Put), |
| 73 | | ref_count: std.atomic.Int(usize), |
| 74 | | |
| 75 | | const Put = struct { |
| 76 | | putter: anyframe, |
| 77 | | cancelled: bool = false, |
| 78 | | }; |
| 64 | cancelled: bool = false, |
| 79 | 65 | |
| 80 | | const DirTable = std.StringHashMap(*Dir); |
| 81 | | const FileTable = std.HashMap([]const u16, V, hashString, eqlString); |
| 66 | const DirTable = std.StringHashMapUnmanaged(*Dir); |
| 67 | const FileTable = std.StringHashMapUnmanaged(V); |
| 82 | 68 | |
| 83 | 69 | const Dir = struct { |
| 84 | 70 | putter_frame: @Frame(windowsDirReader), |
| 85 | 71 | file_table: FileTable, |
| 86 | | table_lock: event.Lock, |
| 72 | dir_handle: os.windows.HANDLE, |
| 87 | 73 | }; |
| 88 | 74 | }; |
| 89 | 75 | |
| ... | ... | @@ -94,8 +80,8 @@ pub fn Watch(comptime V: type) type { |
| 94 | 80 | table_lock: event.Lock, |
| 95 | 81 | cancelled: bool = false, |
| 96 | 82 | |
| 97 | | const WdTable = std.AutoHashMap(i32, Dir); |
| 98 | | const FileTable = std.StringHashMap(V); |
| 83 | const WdTable = std.AutoHashMapUnmanaged(i32, Dir); |
| 84 | const FileTable = std.StringHashMapUnmanaged(V); |
| 99 | 85 | |
| 100 | 86 | const Dir = struct { |
| 101 | 87 | dirname: []const u8, |
| ... | ... | @@ -148,10 +134,9 @@ pub fn Watch(comptime V: type) type { |
| 148 | 134 | .os_data = OsData{ |
| 149 | 135 | .table_lock = event.Lock{}, |
| 150 | 136 | .dir_table = OsData.DirTable.init(allocator), |
| 151 | | .ref_count = std.atomic.Int(usize).init(1), |
| 152 | | .all_putters = std.atomic.Queue(WindowsOsData.Put).init(), |
| 153 | 137 | }, |
| 154 | 138 | }; |
| 139 | |
| 155 | 140 | var buf = try allocator.alloc(Event.Error!Event, event_buf_count); |
| 156 | 141 | self.channel.init(buf); |
| 157 | 142 | return self; |
| ... | ... | @@ -160,12 +145,15 @@ pub fn Watch(comptime V: type) type { |
| 160 | 145 | .macos, .freebsd, .netbsd, .dragonfly, .openbsd => { |
| 161 | 146 | self.* = Self{ |
| 162 | 147 | .allocator = allocator, |
| 163 | | .channel = channel, |
| 148 | .channel = undefined, |
| 164 | 149 | .os_data = OsData{ |
| 165 | 150 | .table_lock = event.Lock.init(), |
| 166 | 151 | .file_table = OsData.FileTable.init(allocator), |
| 167 | 152 | }, |
| 168 | 153 | }; |
| 154 | |
| 155 | var buf = try allocator.alloc(Event.Error!Event, event_buf_count); |
| 156 | self.channel.init(buf); |
| 169 | 157 | return self; |
| 170 | 158 | }, |
| 171 | 159 | else => @compileError("Unsupported OS"), |
| ... | ... | @@ -206,35 +194,38 @@ pub fn Watch(comptime V: type) type { |
| 206 | 194 | self.allocator.destroy(self); |
| 207 | 195 | }, |
| 208 | 196 | .windows => { |
| 209 | | while (self.os_data.all_putters.get()) |putter_node| { |
| 210 | | putter_node.cancelled = true; |
| 211 | | await putter_node.frame; |
| 197 | self.os_data.cancelled = true; |
| 198 | var dir_it = self.os_data.dir_table.iterator(); |
| 199 | while (dir_it.next()) |dir_entry| { |
| 200 | if (windows.kernel32.CancelIoEx(dir_entry.value.dir_handle, null) != 0) { |
| 201 | // We canceled the pending ReadDirectoryChangesW operation, but our |
| 202 | // frame is still suspending, now waiting indefinitely. |
| 203 | // Thus, it is safe to resume it ourslves |
| 204 | resume dir_entry.value.putter_frame; |
| 205 | } else { |
| 206 | std.debug.assert(windows.kernel32.GetLastError() == .NOT_FOUND); |
| 207 | // We are at another suspend point, we can await safely for the |
| 208 | // function to exit the loop |
| 209 | await dir_entry.value.putter_frame; |
| 210 | } |
| 211 | |
| 212 | self.allocator.free(dir_entry.key); |
| 213 | var file_it = dir_entry.value.file_table.iterator(); |
| 214 | while (file_it.next()) |file_entry| { |
| 215 | self.allocator.free(file_entry.key); |
| 216 | } |
| 217 | dir_entry.value.file_table.deinit(self.allocator); |
| 218 | self.allocator.destroy(dir_entry.value); |
| 212 | 219 | } |
| 213 | | self.deref(); |
| 220 | self.os_data.dir_table.deinit(self.allocator); |
| 221 | self.allocator.free(self.channel.buffer_nodes); |
| 222 | self.channel.deinit(); |
| 223 | self.allocator.destroy(self); |
| 214 | 224 | }, |
| 215 | 225 | else => @compileError("Unsupported OS"), |
| 216 | 226 | } |
| 217 | 227 | } |
| 218 | 228 | |
| 219 | | fn ref(self: *Self) void { |
| 220 | | _ = self.os_data.ref_count.incr(); |
| 221 | | } |
| 222 | | |
| 223 | | fn deref(self: *Self) void { |
| 224 | | if (self.os_data.ref_count.decr() == 1) { |
| 225 | | self.os_data.table_lock.deinit(); |
| 226 | | var it = self.os_data.dir_table.iterator(); |
| 227 | | while (it.next()) |entry| { |
| 228 | | self.allocator.free(entry.key); |
| 229 | | self.allocator.destroy(entry.value); |
| 230 | | } |
| 231 | | self.os_data.dir_table.deinit(); |
| 232 | | self.channel.deinit(); |
| 233 | | self.allocator.destroy(self.channel.buffer_nodes); |
| 234 | | self.allocator.destroy(self); |
| 235 | | } |
| 236 | | } |
| 237 | | |
| 238 | 229 | pub fn addFile(self: *Self, file_path: []const u8, value: V) !?V { |
| 239 | 230 | switch (builtin.os.tag) { |
| 240 | 231 | .macos, .freebsd, .netbsd, .dragonfly, .openbsd => return addFileKEvent(self, file_path, value), |
| ... | ... | @@ -342,7 +333,7 @@ pub fn Watch(comptime V: type) type { |
| 342 | 333 | const held = self.os_data.table_lock.acquire(); |
| 343 | 334 | defer held.release(); |
| 344 | 335 | |
| 345 | | const gop = try self.os_data.wd_table.getOrPut(wd); |
| 336 | const gop = try self.os_data.wd_table.getOrPut(self.allocator, wd); |
| 346 | 337 | if (!gop.found_existing) { |
| 347 | 338 | gop.entry.value = OsData.Dir{ |
| 348 | 339 | .dirname = try self.allocator.dupe(u8, dirname), |
| ... | ... | @@ -351,7 +342,7 @@ pub fn Watch(comptime V: type) type { |
| 351 | 342 | } |
| 352 | 343 | |
| 353 | 344 | const dir = &gop.entry.value; |
| 354 | | const file_table_gop = try dir.file_table.getOrPut(basename); |
| 345 | const file_table_gop = try dir.file_table.getOrPut(self.allocator, basename); |
| 355 | 346 | if (file_table_gop.found_existing) { |
| 356 | 347 | const prev_value = file_table_gop.entry.value; |
| 357 | 348 | file_table_gop.entry.value = value; |
| ... | ... | @@ -365,89 +356,67 @@ pub fn Watch(comptime V: type) type { |
| 365 | 356 | |
| 366 | 357 | fn addFileWindows(self: *Self, file_path: []const u8, value: V) !?V { |
| 367 | 358 | // TODO we might need to convert dirname and basename to canonical file paths ("short"?) |
| 368 | | const dirname = try self.allocator.dupe(u8, std.fs.path.dirname(file_path) orelse "."); |
| 369 | | var dirname_consumed = false; |
| 370 | | defer if (!dirname_consumed) self.allocator.free(dirname); |
| 371 | | |
| 372 | | const dirname_utf16le = try std.unicode.utf8ToUtf16LeWithNull(self.allocator, dirname); |
| 373 | | defer self.allocator.free(dirname_utf16le); |
| 359 | const dirname = std.fs.path.dirname(file_path) orelse "."; |
| 360 | var dirname_path_space: windows.PathSpace = undefined; |
| 361 | dirname_path_space.len = try std.unicode.utf8ToUtf16Le(&dirname_path_space.data, dirname); |
| 362 | dirname_path_space.data[dirname_path_space.len] = 0; |
| 374 | 363 | |
| 375 | | // TODO https://github.com/ziglang/zig/issues/265 |
| 376 | 364 | const basename = std.fs.path.basename(file_path); |
| 377 | | const basename_utf16le_null = try std.unicode.utf8ToUtf16LeWithNull(self.allocator, basename); |
| 378 | | var basename_utf16le_null_consumed = false; |
| 379 | | defer if (!basename_utf16le_null_consumed) self.allocator.free(basename_utf16le_null); |
| 380 | | const basename_utf16le_no_null = basename_utf16le_null[0 .. basename_utf16le_null.len - 1]; |
| 381 | | |
| 382 | | const dir_handle = try windows.OpenFile(dirname_utf16le, .{ |
| 383 | | .dir = std.fs.cwd().fd, |
| 384 | | .access_mask = windows.FILE_LIST_DIRECTORY, |
| 385 | | .creation = windows.FILE_OPEN, |
| 386 | | .io_mode = .blocking, |
| 387 | | .open_dir = true, |
| 388 | | }); |
| 389 | | var dir_handle_consumed = false; |
| 390 | | defer if (!dir_handle_consumed) windows.CloseHandle(dir_handle); |
| 365 | var basename_path_space: windows.PathSpace = undefined; |
| 366 | basename_path_space.len = try std.unicode.utf8ToUtf16Le(&basename_path_space.data, basename); |
| 367 | basename_path_space.data[basename_path_space.len] = 0; |
| 391 | 368 | |
| 392 | 369 | const held = self.os_data.table_lock.acquire(); |
| 393 | 370 | defer held.release(); |
| 394 | 371 | |
| 395 | | const gop = try self.os_data.dir_table.getOrPut(dirname); |
| 372 | const gop = try self.os_data.dir_table.getOrPut(self.allocator, dirname); |
| 396 | 373 | if (gop.found_existing) { |
| 397 | | const dir = gop.kv.value; |
| 398 | | const held_dir_lock = dir.table_lock.acquire(); |
| 399 | | defer held_dir_lock.release(); |
| 374 | const dir = gop.entry.value; |
| 400 | 375 | |
| 401 | | const file_gop = try dir.file_table.getOrPut(basename_utf16le_no_null); |
| 376 | const file_gop = try dir.file_table.getOrPut(self.allocator, basename); |
| 402 | 377 | if (file_gop.found_existing) { |
| 403 | | const prev_value = file_gop.kv.value; |
| 404 | | file_gop.kv.value = value; |
| 378 | const prev_value = file_gop.entry.value; |
| 379 | file_gop.entry.value = value; |
| 405 | 380 | return prev_value; |
| 406 | 381 | } else { |
| 407 | | file_gop.kv.value = value; |
| 408 | | basename_utf16le_null_consumed = true; |
| 382 | file_gop.entry.value = value; |
| 383 | file_gop.entry.key = try self.allocator.dupe(u8, basename); |
| 409 | 384 | return null; |
| 410 | 385 | } |
| 411 | 386 | } else { |
| 412 | 387 | errdefer _ = self.os_data.dir_table.remove(dirname); |
| 388 | const dir_handle = try windows.OpenFile(dirname_path_space.span(), .{ |
| 389 | .dir = std.fs.cwd().fd, |
| 390 | .access_mask = windows.FILE_LIST_DIRECTORY, |
| 391 | .creation = windows.FILE_OPEN, |
| 392 | .io_mode = .evented, |
| 393 | .open_dir = true, |
| 394 | }); |
| 395 | errdefer windows.CloseHandle(dir_handle); |
| 396 | |
| 413 | 397 | const dir = try self.allocator.create(OsData.Dir); |
| 414 | 398 | errdefer self.allocator.destroy(dir); |
| 415 | 399 | |
| 400 | gop.entry.key = try self.allocator.dupe(u8, dirname); |
| 401 | errdefer self.allocator.free(gop.entry.key); |
| 402 | |
| 416 | 403 | dir.* = OsData.Dir{ |
| 417 | 404 | .file_table = OsData.FileTable.init(self.allocator), |
| 418 | | .table_lock = event.Lock.init(), |
| 419 | 405 | .putter_frame = undefined, |
| 406 | .dir_handle = dir_handle, |
| 420 | 407 | }; |
| 421 | | gop.kv.value = dir; |
| 422 | | assert((try dir.file_table.put(basename_utf16le_no_null, value)) == null); |
| 423 | | basename_utf16le_null_consumed = true; |
| 424 | | |
| 425 | | dir.putter_frame = async self.windowsDirReader(dir_handle, dir); |
| 426 | | dir_handle_consumed = true; |
| 427 | | |
| 428 | | dirname_consumed = true; |
| 429 | | |
| 408 | gop.entry.value = dir; |
| 409 | try dir.file_table.put(self.allocator, try self.allocator.dupe(u8, basename), value); |
| 410 | dir.putter_frame = async self.windowsDirReader(dir, gop.entry.key); |
| 430 | 411 | return null; |
| 431 | 412 | } |
| 432 | 413 | } |
| 433 | 414 | |
| 434 | | fn windowsDirReader(self: *Self, dir_handle: windows.HANDLE, dir: *OsData.Dir) void { |
| 435 | | self.ref(); |
| 436 | | defer self.deref(); |
| 437 | | |
| 438 | | defer os.close(dir_handle); |
| 439 | | |
| 440 | | var putter_node = std.atomic.Queue(anyframe).Node{ |
| 441 | | .data = .{ .putter = @frame() }, |
| 442 | | .prev = null, |
| 443 | | .next = null, |
| 444 | | }; |
| 445 | | self.os_data.all_putters.put(&putter_node); |
| 446 | | defer _ = self.os_data.all_putters.remove(&putter_node); |
| 447 | | |
| 415 | fn windowsDirReader(self: *Self, dir: *OsData.Dir, dirname: []const u8) void { |
| 416 | defer os.close(dir.dir_handle); |
| 448 | 417 | var resume_node = Loop.ResumeNode.Basic{ |
| 449 | 418 | .base = Loop.ResumeNode{ |
| 450 | | .id = Loop.ResumeNode.Id.Basic, |
| 419 | .id = .Basic, |
| 451 | 420 | .handle = @frame(), |
| 452 | 421 | .overlapped = windows.OVERLAPPED{ |
| 453 | 422 | .Internal = 0, |
| ... | ... | @@ -458,81 +427,75 @@ pub fn Watch(comptime V: type) type { |
| 458 | 427 | }, |
| 459 | 428 | }, |
| 460 | 429 | }; |
| 461 | | var event_buf: [4096]u8 align(@alignOf(windows.FILE_NOTIFY_INFORMATION)) = undefined; |
| 462 | 430 | |
| 463 | | // TODO handle this error not in the channel but in the setup |
| 464 | | _ = windows.CreateIoCompletionPort( |
| 465 | | dir_handle, |
| 466 | | global_event_loop.os_data.io_port, |
| 467 | | undefined, |
| 468 | | undefined, |
| 469 | | ) catch |err| { |
| 470 | | self.channel.put(err); |
| 471 | | return; |
| 472 | | }; |
| 431 | var event_buf: [4096]u8 align(@alignOf(windows.FILE_NOTIFY_INFORMATION)) = undefined; |
| 473 | 432 | |
| 474 | | while (!putter_node.data.cancelled) { |
| 475 | | { |
| 476 | | // TODO only 1 beginOneEvent for the whole function |
| 477 | | global_event_loop.beginOneEvent(); |
| 478 | | errdefer global_event_loop.finishOneEvent(); |
| 479 | | errdefer { |
| 480 | | _ = windows.kernel32.CancelIoEx(dir_handle, &resume_node.base.overlapped); |
| 481 | | } |
| 482 | | suspend { |
| 483 | | _ = windows.kernel32.ReadDirectoryChangesW( |
| 484 | | dir_handle, |
| 485 | | &event_buf, |
| 486 | | @intCast(windows.DWORD, event_buf.len), |
| 487 | | windows.FALSE, // watch subtree |
| 488 | | windows.FILE_NOTIFY_CHANGE_FILE_NAME | windows.FILE_NOTIFY_CHANGE_DIR_NAME | |
| 489 | | windows.FILE_NOTIFY_CHANGE_ATTRIBUTES | windows.FILE_NOTIFY_CHANGE_SIZE | |
| 490 | | windows.FILE_NOTIFY_CHANGE_LAST_WRITE | windows.FILE_NOTIFY_CHANGE_LAST_ACCESS | |
| 491 | | windows.FILE_NOTIFY_CHANGE_CREATION | windows.FILE_NOTIFY_CHANGE_SECURITY, |
| 492 | | null, // number of bytes transferred (unused for async) |
| 493 | | &resume_node.base.overlapped, |
| 494 | | null, // completion routine - unused because we use IOCP |
| 495 | | ); |
| 496 | | } |
| 433 | global_event_loop.beginOneEvent(); |
| 434 | defer global_event_loop.finishOneEvent(); |
| 435 | |
| 436 | while (!self.os_data.cancelled) main_loop: { |
| 437 | suspend { |
| 438 | _ = windows.kernel32.ReadDirectoryChangesW( |
| 439 | dir.dir_handle, |
| 440 | &event_buf, |
| 441 | event_buf.len, |
| 442 | windows.FALSE, // watch subtree |
| 443 | windows.FILE_NOTIFY_CHANGE_FILE_NAME | windows.FILE_NOTIFY_CHANGE_DIR_NAME | |
| 444 | windows.FILE_NOTIFY_CHANGE_ATTRIBUTES | windows.FILE_NOTIFY_CHANGE_SIZE | |
| 445 | windows.FILE_NOTIFY_CHANGE_LAST_WRITE | windows.FILE_NOTIFY_CHANGE_LAST_ACCESS | |
| 446 | windows.FILE_NOTIFY_CHANGE_CREATION | windows.FILE_NOTIFY_CHANGE_SECURITY, |
| 447 | null, // number of bytes transferred (unused for async) |
| 448 | &resume_node.base.overlapped, |
| 449 | null, // completion routine - unused because we use IOCP |
| 450 | ); |
| 497 | 451 | } |
| 452 | |
| 498 | 453 | var bytes_transferred: windows.DWORD = undefined; |
| 499 | | if (windows.kernel32.GetOverlappedResult(dir_handle, &resume_node.base.overlapped, &bytes_transferred, windows.FALSE) == 0) { |
| 500 | | const err = switch (windows.kernel32.GetLastError()) { |
| 454 | if (windows.kernel32.GetOverlappedResult( |
| 455 | dir.dir_handle, |
| 456 | &resume_node.base.overlapped, |
| 457 | &bytes_transferred, |
| 458 | windows.FALSE, |
| 459 | ) == 0) { |
| 460 | const potential_error = windows.kernel32.GetLastError(); |
| 461 | const err = switch (potential_error) { |
| 462 | .OPERATION_ABORTED, .IO_INCOMPLETE => err_blk: { |
| 463 | if (self.os_data.cancelled) |
| 464 | break :main_loop |
| 465 | else |
| 466 | break :err_blk windows.unexpectedError(potential_error); |
| 467 | }, |
| 501 | 468 | else => |err| windows.unexpectedError(err), |
| 502 | 469 | }; |
| 503 | 470 | self.channel.put(err); |
| 504 | 471 | } else { |
| 505 | | // can't use @bytesToSlice because of the special variable length name field |
| 506 | | var ptr = event_buf[0..].ptr; |
| 472 | var ptr: [*]u8 = &event_buf; |
| 507 | 473 | const end_ptr = ptr + bytes_transferred; |
| 508 | | var ev: *windows.FILE_NOTIFY_INFORMATION = undefined; |
| 509 | | while (@ptrToInt(ptr) < @ptrToInt(end_ptr)) : (ptr += ev.NextEntryOffset) { |
| 510 | | ev = @ptrCast(*windows.FILE_NOTIFY_INFORMATION, ptr); |
| 474 | while (@ptrToInt(ptr) < @ptrToInt(end_ptr)) { |
| 475 | const ev = @ptrCast(*const windows.FILE_NOTIFY_INFORMATION, ptr); |
| 511 | 476 | const emit = switch (ev.Action) { |
| 512 | 477 | windows.FILE_ACTION_REMOVED => WatchEventId.Delete, |
| 513 | | windows.FILE_ACTION_MODIFIED => WatchEventId.CloseWrite, |
| 478 | windows.FILE_ACTION_MODIFIED => .CloseWrite, |
| 514 | 479 | else => null, |
| 515 | 480 | }; |
| 516 | 481 | if (emit) |id| { |
| 517 | | const basename_utf16le = ([*]u16)(&ev.FileName)[0 .. ev.FileNameLength / 2]; |
| 518 | | const user_value = blk: { |
| 519 | | const held = dir.table_lock.acquire(); |
| 520 | | defer held.release(); |
| 521 | | |
| 522 | | if (dir.file_table.get(basename_utf16le)) |entry| { |
| 523 | | break :blk entry.value; |
| 524 | | } else { |
| 525 | | break :blk null; |
| 526 | | } |
| 527 | | }; |
| 528 | | if (user_value) |v| { |
| 482 | const basename_ptr = @ptrCast([*]u16, ptr + @sizeOf(windows.FILE_NOTIFY_INFORMATION)); |
| 483 | const basename_utf16le = basename_ptr[0 .. ev.FileNameLength / 2]; |
| 484 | var basename_data: [std.fs.MAX_PATH_BYTES]u8 = undefined; |
| 485 | const basename = basename_data[0 .. std.unicode.utf16leToUtf8(&basename_data, basename_utf16le) catch unreachable]; |
| 486 | |
| 487 | if (dir.file_table.getEntry(basename)) |entry| { |
| 529 | 488 | self.channel.put(Event{ |
| 530 | 489 | .id = id, |
| 531 | | .data = v, |
| 490 | .data = entry.value, |
| 491 | .dirname = dirname, |
| 492 | .basename = entry.key, |
| 532 | 493 | }); |
| 533 | 494 | } |
| 534 | 495 | } |
| 496 | |
| 535 | 497 | if (ev.NextEntryOffset == 0) break; |
| 498 | ptr = @alignCast(@alignOf(windows.FILE_NOTIFY_INFORMATION), ptr + ev.NextEntryOffset); |
| 536 | 499 | } |
| 537 | 500 | } |
| 538 | 501 | } |
| ... | ... | @@ -554,8 +517,21 @@ pub fn Watch(comptime V: type) type { |
| 554 | 517 | } |
| 555 | 518 | return null; |
| 556 | 519 | }, |
| 520 | .windows => { |
| 521 | const dirname = std.fs.path.dirname(file_path) orelse "."; |
| 522 | const basename = std.fs.path.basename(file_path); |
| 523 | |
| 524 | const held = self.os_data.table_lock.acquire(); |
| 525 | defer held.release(); |
| 526 | |
| 527 | const dir = self.os_data.dir_table.get(dirname) orelse return null; |
| 528 | if (dir.file_table.remove(basename)) |file_entry| { |
| 529 | self.allocator.free(file_entry.key); |
| 530 | return file_entry.value; |
| 531 | } |
| 532 | return null; |
| 533 | }, |
| 557 | 534 | .macos, .freebsd, .netbsd, .dragonfly, .openbsd => @panic("TODO"), |
| 558 | | .windows => return @panic("TODO"), |
| 559 | 535 | else => @compileError("Unsupported OS"), |
| 560 | 536 | } |
| 561 | 537 | } |
| ... | ... | @@ -565,7 +541,7 @@ pub fn Watch(comptime V: type) type { |
| 565 | 541 | |
| 566 | 542 | defer { |
| 567 | 543 | std.debug.assert(self.os_data.wd_table.count() == 0); |
| 568 | | self.os_data.wd_table.deinit(); |
| 544 | self.os_data.wd_table.deinit(self.allocator); |
| 569 | 545 | os.close(self.os_data.inotify_fd); |
| 570 | 546 | self.allocator.free(self.channel.buffer_nodes); |
| 571 | 547 | self.channel.deinit(); |
| ... | ... | @@ -585,9 +561,6 @@ pub fn Watch(comptime V: type) type { |
| 585 | 561 | const basename_ptr = ptr + @sizeOf(os.linux.inotify_event); |
| 586 | 562 | const basename = std.mem.span(@ptrCast([*:0]u8, basename_ptr)); |
| 587 | 563 | |
| 588 | | const held = self.os_data.table_lock.acquire(); |
| 589 | | defer held.release(); |
| 590 | | |
| 591 | 564 | const dir = &self.os_data.wd_table.get(ev.wd).?; |
| 592 | 565 | if (dir.file_table.getEntry(basename)) |file_value| { |
| 593 | 566 | self.channel.put(Event{ |
| ... | ... | @@ -607,17 +580,14 @@ pub fn Watch(comptime V: type) type { |
| 607 | 580 | self.allocator.free(file_entry.key); |
| 608 | 581 | } |
| 609 | 582 | self.allocator.free(wd_entry.value.dirname); |
| 610 | | wd_entry.value.file_table.deinit(); |
| 583 | wd_entry.value.file_table.deinit(self.allocator); |
| 611 | 584 | } |
| 612 | 585 | } else if (ev.mask & os.linux.IN_DELETE == os.linux.IN_DELETE) { |
| 613 | 586 | // File or directory was removed or deleted |
| 614 | 587 | const basename_ptr = ptr + @sizeOf(os.linux.inotify_event); |
| 615 | 588 | const basename = std.mem.span(@ptrCast([*:0]u8, basename_ptr)); |
| 616 | 589 | |
| 617 | | const held = self.os_data.table_lock.acquire(); |
| 618 | | defer held.release(); |
| 619 | 590 | const dir = &self.os_data.wd_table.get(ev.wd).?; |
| 620 | | |
| 621 | 591 | if (dir.file_table.getEntry(basename)) |file_value| { |
| 622 | 592 | self.channel.put(Event{ |
| 623 | 593 | .id = .Delete, |