| ... | ... | @@ -242,8 +242,9 @@ const Os = switch (builtin.os.tag) { |
| 242 | 242 | |
| 243 | 243 | /// Keyed differently but indexes correspond 1:1 with `dir_table`. |
| 244 | 244 | handle_table: HandleTable, |
| 245 | | dir_list: std.ArrayListUnmanaged(*Directory), |
| 245 | dir_list: std.AutoArrayHashMapUnmanaged(usize, *Directory), |
| 246 | 246 | io_cp: ?windows.HANDLE, |
| 247 | counter: usize = 0, |
| 247 | 248 | |
| 248 | 249 | const HandleTable = std.AutoArrayHashMapUnmanaged(FileId, ReactionSet); |
| 249 | 250 | |
| ... | ... | @@ -260,7 +261,8 @@ const Os = switch (builtin.os.tag) { |
| 260 | 261 | // https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-readdirectorychangesw#remarks |
| 261 | 262 | buffer: [64 * 1024]u8 align(@alignOf(windows.FILE_NOTIFY_INFORMATION)) = undefined, |
| 262 | 263 | |
| 263 | | fn readChanges(self: *@This()) !void { |
| 264 | /// Start listening for events, buffer field will be overwritten eventually. |
| 265 | fn startListening(self: *@This()) !void { |
| 264 | 266 | const r = windows.kernel32.ReadDirectoryChangesW( |
| 265 | 267 | self.handle, |
| 266 | 268 | @ptrCast(&self.buffer), |
| ... | ... | @@ -394,6 +396,7 @@ const Os = switch (builtin.os.tag) { |
| 394 | 396 | if (bytes_returned == 0) { |
| 395 | 397 | std.log.warn("file system watch queue overflowed; falling back to fstat", .{}); |
| 396 | 398 | markAllFilesDirty(w, gpa); |
| 399 | try dir.startListening(); |
| 397 | 400 | return true; |
| 398 | 401 | } |
| 399 | 402 | var file_name_buf: [std.fs.max_path_bytes]u8 = undefined; |
| ... | ... | @@ -418,7 +421,8 @@ const Os = switch (builtin.os.tag) { |
| 418 | 421 | offset += notify.NextEntryOffset; |
| 419 | 422 | } |
| 420 | 423 | |
| 421 | | try dir.readChanges(); |
| 424 | // We call this now since at this point we have finished reading dir.buffer. |
| 425 | try dir.startListening(); |
| 422 | 426 | return any_dirty; |
| 423 | 427 | } |
| 424 | 428 | |
| ... | ... | @@ -444,12 +448,14 @@ const Os = switch (builtin.os.tag) { |
| 444 | 448 | } else { |
| 445 | 449 | assert(dh_gop.index == gop.index); |
| 446 | 450 | dh_gop.value_ptr.* = .{}; |
| 447 | | try dir.readChanges(); |
| 448 | | try w.os.dir_list.insert(gpa, dh_gop.index, dir); |
| 451 | try dir.startListening(); |
| 452 | const key = w.os.counter; |
| 453 | w.os.counter +%= 1; |
| 454 | try w.os.dir_list.put(gpa, key, dir); |
| 449 | 455 | w.os.io_cp = try windows.CreateIoCompletionPort( |
| 450 | 456 | dir.handle, |
| 451 | 457 | w.os.io_cp, |
| 452 | | dh_gop.index, |
| 458 | key, |
| 453 | 459 | 0, |
| 454 | 460 | ); |
| 455 | 461 | } |
| ... | ... | @@ -495,8 +501,8 @@ const Os = switch (builtin.os.tag) { |
| 495 | 501 | } |
| 496 | 502 | } |
| 497 | 503 | |
| 498 | | w.os.dir_list.items[i].deinit(gpa); |
| 499 | | _ = w.os.dir_list.swapRemove(i); |
| 504 | w.os.dir_list.values()[i].deinit(gpa); |
| 505 | w.os.dir_list.swapRemoveAt(i); |
| 500 | 506 | w.dir_table.swapRemoveAt(i); |
| 501 | 507 | w.os.handle_table.swapRemoveAt(i); |
| 502 | 508 | } |
| ... | ... | @@ -653,7 +659,12 @@ pub fn wait(w: *Watch, gpa: Allocator, timeout: Timeout) !WaitResult { |
| 653 | 659 | .Normal => { |
| 654 | 660 | if (bytes_transferred == 0) |
| 655 | 661 | break error.Unexpected; |
| 656 | | break if (try Os.markDirtySteps(w, gpa, w.os.dir_list.items[key])) |
| 662 | |
| 663 | // This 'orelse' detects a race condition that happens when we receive a |
| 664 | // completion notification for a directory that no longer exists in our list. |
| 665 | const dir = w.os.dir_list.get(key) orelse break .clean; |
| 666 | |
| 667 | break if (try Os.markDirtySteps(w, gpa, dir)) |
| 657 | 668 | .dirty |
| 658 | 669 | else |
| 659 | 670 | .clean; |