Compare commits
2 commits
6927431726
...
daf58bd4dc
| Author | SHA1 | Date | |
|---|---|---|---|
| daf58bd4dc | |||
| 5971750c4f |
5 changed files with 91 additions and 26 deletions
|
|
@ -189,7 +189,7 @@ fn scan_dir(self: *@This(), allocator: std.mem.Allocator, dir_path: []const u8)
|
||||||
var dir = std.fs.openDirAbsolute(dir_path, .{ .iterate = true }) catch return;
|
var dir = std.fs.openDirAbsolute(dir_path, .{ .iterate = true }) catch return;
|
||||||
defer dir.close();
|
defer dir.close();
|
||||||
|
|
||||||
// Arena for all temporaries — freed in one shot at the end.
|
// Arena for all temporaries - freed in one shot at the end.
|
||||||
var arena = std.heap.ArenaAllocator.init(allocator);
|
var arena = std.heap.ArenaAllocator.init(allocator);
|
||||||
defer arena.deinit();
|
defer arena.deinit();
|
||||||
const tmp = arena.allocator();
|
const tmp = arena.allocator();
|
||||||
|
|
|
||||||
|
|
@ -173,7 +173,7 @@ fn scan_dir(self: *@This(), allocator: std.mem.Allocator, dir_path: []const u8)
|
||||||
var dir = std.fs.openDirAbsolute(dir_path, .{ .iterate = true }) catch return;
|
var dir = std.fs.openDirAbsolute(dir_path, .{ .iterate = true }) catch return;
|
||||||
defer dir.close();
|
defer dir.close();
|
||||||
|
|
||||||
// Arena for all temporaries — freed in one shot at the end.
|
// Arena for all temporaries - freed in one shot at the end.
|
||||||
var arena = std.heap.ArenaAllocator.init(allocator);
|
var arena = std.heap.ArenaAllocator.init(allocator);
|
||||||
defer arena.deinit();
|
defer arena.deinit();
|
||||||
const tmp = arena.allocator();
|
const tmp = arena.allocator();
|
||||||
|
|
@ -242,13 +242,13 @@ fn scan_dir(self: *@This(), allocator: std.mem.Allocator, dir_path: []const u8)
|
||||||
var cit = current_files.iterator();
|
var cit = current_files.iterator();
|
||||||
while (cit.next()) |entry| {
|
while (cit.next()) |entry| {
|
||||||
if (snapshot.getPtr(entry.key_ptr.*)) |stored_mtime| {
|
if (snapshot.getPtr(entry.key_ptr.*)) |stored_mtime| {
|
||||||
// File exists in both — check for modification via mtime change.
|
// File exists in both - check for modification via mtime change.
|
||||||
if (stored_mtime.* != entry.value_ptr.*) {
|
if (stored_mtime.* != entry.value_ptr.*) {
|
||||||
stored_mtime.* = entry.value_ptr.*;
|
stored_mtime.* = entry.value_ptr.*;
|
||||||
try to_modify.append(tmp, entry.key_ptr.*); // from current_files (tmp)
|
try to_modify.append(tmp, entry.key_ptr.*); // from current_files (tmp)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// New file — add to snapshot and to_create list.
|
// New file - add to snapshot and to_create list.
|
||||||
const owned = allocator.dupe(u8, entry.key_ptr.*) catch |e| {
|
const owned = allocator.dupe(u8, entry.key_ptr.*) catch |e| {
|
||||||
return e;
|
return e;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -18,22 +18,25 @@ pub const EventType = enum {
|
||||||
///
|
///
|
||||||
/// Delivery varies by backend:
|
/// Delivery varies by backend:
|
||||||
///
|
///
|
||||||
/// - **INotify**: renames within the watched tree are delivered as a
|
/// - **INotify**: all watches share a single inotify file descriptor, so
|
||||||
/// single atomic `rename` callback with both source and destination
|
/// moves are paired by cookie across all watched roots. Renames between
|
||||||
/// paths. A move out of the tree appears as `deleted`; a move into
|
/// two watched directories - even separate watch roots on the same
|
||||||
/// the tree appears as `created`.
|
/// watcher instance - are delivered as a single atomic `rename`
|
||||||
|
/// callback. A move out of all watched paths appears as `deleted`; a
|
||||||
|
/// move in from an unwatched path appears as `created`.
|
||||||
|
///
|
||||||
|
/// - **Windows**: renames within a single watched root are delivered as a
|
||||||
|
/// single atomic `rename` callback. However, each root uses an
|
||||||
|
/// independent `ReadDirectoryChangesW` handle with no shared cookie, so
|
||||||
|
/// a move between two separately watched roots cannot be paired: it
|
||||||
|
/// appears as `deleted` on the source side and `created` on the
|
||||||
|
/// destination side.
|
||||||
///
|
///
|
||||||
/// - **kqueue / kqueuedir**: when a watched *directory* is itself
|
/// - **kqueue / kqueuedir**: when a watched *directory* is itself
|
||||||
/// renamed, a `renamed` change event is emitted for the old directory
|
/// renamed, a `renamed` change event is emitted for the old directory
|
||||||
/// path (the new path is not known). Renames of *files inside* a
|
/// path (the new path is not known). Renames of *files inside* a
|
||||||
/// watched directory are detected indirectly via directory-level
|
/// watched directory are detected indirectly via directory-level
|
||||||
/// `NOTE_WRITE` events and appear as a `deleted` event for the old
|
/// `NOTE_WRITE` events and appear as `deleted` + `created`.
|
||||||
/// name followed by a `created` event for the new name.
|
|
||||||
///
|
|
||||||
/// - **Windows**: renames within the watched tree are delivered as a
|
|
||||||
/// single atomic `rename` callback, matching INotify behaviour. A
|
|
||||||
/// move out of the tree appears as `deleted`; a move into the tree
|
|
||||||
/// appears as `created`.
|
|
||||||
///
|
///
|
||||||
/// - **FSEvents**: each path involved in a rename receives its own
|
/// - **FSEvents**: each path involved in a rename receives its own
|
||||||
/// `renamed` change event; the two sides are not paired.
|
/// `renamed` change event; the two sides are not paired.
|
||||||
|
|
@ -61,9 +64,9 @@ pub const Error = error{
|
||||||
|
|
||||||
/// Selects how the watcher delivers events to the caller.
|
/// Selects how the watcher delivers events to the caller.
|
||||||
///
|
///
|
||||||
/// - `.threaded` — the backend spawns an internal thread that calls the
|
/// - `.threaded` - the backend spawns an internal thread that calls the
|
||||||
/// handler directly. The caller just needs to keep the `Watcher` alive.
|
/// handler directly. The caller just needs to keep the `Watcher` alive.
|
||||||
/// - `.polling` — no internal thread is created. The caller must poll
|
/// - `.polling` - no internal thread is created. The caller must poll
|
||||||
/// `poll_fd()` for readability and call `handle_read_ready()` whenever
|
/// `poll_fd()` for readability and call `handle_read_ready()` whenever
|
||||||
/// data is available. Currently only supported on Linux (inotify).
|
/// data is available. Currently only supported on Linux (inotify).
|
||||||
pub const InterfaceType = enum { polling, threaded };
|
pub const InterfaceType = enum { polling, threaded };
|
||||||
|
|
|
||||||
|
|
@ -14,15 +14,17 @@ if (-not (Test-Path $NW)) {
|
||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
|
|
||||||
$TESTDIR = Join-Path $env:TEMP "nightwatch_manual_$PID"
|
$TESTDIR = Join-Path $env:TEMP "nightwatch_manual_$PID"
|
||||||
New-Item -ItemType Directory -Path $TESTDIR | Out-Null
|
$TESTDIR2 = Join-Path $env:TEMP "nightwatch_manual2_$PID"
|
||||||
|
New-Item -ItemType Directory -Path $TESTDIR | Out-Null
|
||||||
|
New-Item -ItemType Directory -Path $TESTDIR2 | Out-Null
|
||||||
|
|
||||||
Write-Host "--- watching $TESTDIR ---"
|
Write-Host "--- watching $TESTDIR and $TESTDIR2 ---"
|
||||||
Write-Host "--- starting nightwatch (Ctrl-C to stop early) ---"
|
Write-Host "--- starting nightwatch (Ctrl-C to stop early) ---"
|
||||||
Write-Host ""
|
Write-Host ""
|
||||||
|
|
||||||
# Start nightwatch in background, events go to stdout
|
# Start nightwatch in background watching both dirs, events go to stdout
|
||||||
$proc = Start-Process -FilePath $NW -ArgumentList $TESTDIR -NoNewWindow -PassThru
|
$proc = Start-Process -FilePath $NW -ArgumentList $TESTDIR, $TESTDIR2 -NoNewWindow -PassThru
|
||||||
Start-Sleep -Milliseconds 500
|
Start-Sleep -Milliseconds 500
|
||||||
|
|
||||||
Write-Host "[op] touch file1.txt"
|
Write-Host "[op] touch file1.txt"
|
||||||
|
|
@ -73,8 +75,38 @@ Write-Host "[op] rmdir dirB (and contents)"
|
||||||
Remove-Item -Recurse -Force -Path "$TESTDIR\dirB"
|
Remove-Item -Recurse -Force -Path "$TESTDIR\dirB"
|
||||||
Start-Sleep -Milliseconds 500
|
Start-Sleep -Milliseconds 500
|
||||||
|
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "# cross-root renames (both dirs watched)"
|
||||||
|
Write-Host ""
|
||||||
|
|
||||||
|
Write-Host "[op] mkdir subA in both roots"
|
||||||
|
New-Item -ItemType Directory -Path "$TESTDIR\subA" | Out-Null
|
||||||
|
New-Item -ItemType Directory -Path "$TESTDIR2\subA" | Out-Null
|
||||||
|
Start-Sleep -Milliseconds 400
|
||||||
|
|
||||||
|
Write-Host "[op] touch crossfile.txt in dir1"
|
||||||
|
New-Item -ItemType File -Path "$TESTDIR\crossfile.txt" | Out-Null
|
||||||
|
Start-Sleep -Milliseconds 400
|
||||||
|
|
||||||
|
Write-Host "[op] rename crossfile.txt: dir1 -> dir2 (root to root)"
|
||||||
|
Move-Item -Path "$TESTDIR\crossfile.txt" -Destination "$TESTDIR2\crossfile.txt"
|
||||||
|
Start-Sleep -Milliseconds 400
|
||||||
|
|
||||||
|
Write-Host "[op] touch subA\crosssub.txt in dir1"
|
||||||
|
New-Item -ItemType File -Path "$TESTDIR\subA\crosssub.txt" | Out-Null
|
||||||
|
Start-Sleep -Milliseconds 400
|
||||||
|
|
||||||
|
Write-Host "[op] rename subA\crosssub.txt: dir1\subA -> dir2\subA (subdir to subdir)"
|
||||||
|
Move-Item -Path "$TESTDIR\subA\crosssub.txt" -Destination "$TESTDIR2\subA\crosssub.txt"
|
||||||
|
Start-Sleep -Milliseconds 400
|
||||||
|
|
||||||
|
Write-Host "[op] rename subA: dir1 -> dir2 (subdir across roots)"
|
||||||
|
Move-Item -Path "$TESTDIR\subA" -Destination "$TESTDIR2\subA2"
|
||||||
|
Start-Sleep -Milliseconds 500
|
||||||
|
|
||||||
Write-Host ""
|
Write-Host ""
|
||||||
Write-Host "--- done, stopping nightwatch ---"
|
Write-Host "--- done, stopping nightwatch ---"
|
||||||
Stop-Process -Id $proc.Id -ErrorAction SilentlyContinue
|
Stop-Process -Id $proc.Id -ErrorAction SilentlyContinue
|
||||||
$proc.WaitForExit()
|
$proc.WaitForExit()
|
||||||
Remove-Item -Recurse -Force -Path $TESTDIR
|
Remove-Item -Recurse -Force -Path $TESTDIR
|
||||||
|
Remove-Item -Recurse -Force -Path $TESTDIR2
|
||||||
|
|
|
||||||
|
|
@ -14,12 +14,13 @@ if [ ! -x "$NW" ]; then
|
||||||
fi
|
fi
|
||||||
|
|
||||||
TESTDIR=$(mktemp -d)
|
TESTDIR=$(mktemp -d)
|
||||||
echo "--- watching $TESTDIR ---"
|
TESTDIR2=$(mktemp -d)
|
||||||
|
echo "--- watching $TESTDIR and $TESTDIR2 ---"
|
||||||
echo "--- starting nightwatch (Ctrl-C to stop early) ---"
|
echo "--- starting nightwatch (Ctrl-C to stop early) ---"
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
# Start nightwatch in background, events go to stdout
|
# Start nightwatch in background watching both dirs, events go to stdout
|
||||||
"$NW" "$TESTDIR" &
|
"$NW" "$TESTDIR" "$TESTDIR2" &
|
||||||
NW_PID=$!
|
NW_PID=$!
|
||||||
sleep 0.5
|
sleep 0.5
|
||||||
|
|
||||||
|
|
@ -71,8 +72,37 @@ echo "[op] rmdir dirB (and contents)"
|
||||||
rm -rf "$TESTDIR/dirB"
|
rm -rf "$TESTDIR/dirB"
|
||||||
sleep 0.5
|
sleep 0.5
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "# cross-root renames (both dirs watched)"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
echo "[op] mkdir subA in both roots"
|
||||||
|
mkdir "$TESTDIR/subA"
|
||||||
|
mkdir "$TESTDIR2/subA"
|
||||||
|
sleep 0.4
|
||||||
|
|
||||||
|
echo "[op] touch crossfile.txt in dir1"
|
||||||
|
touch "$TESTDIR/crossfile.txt"
|
||||||
|
sleep 0.4
|
||||||
|
|
||||||
|
echo "[op] rename crossfile.txt: dir1 -> dir2 (root to root)"
|
||||||
|
mv "$TESTDIR/crossfile.txt" "$TESTDIR2/crossfile.txt"
|
||||||
|
sleep 0.4
|
||||||
|
|
||||||
|
echo "[op] touch subA/crosssub.txt in dir1"
|
||||||
|
touch "$TESTDIR/subA/crosssub.txt"
|
||||||
|
sleep 0.4
|
||||||
|
|
||||||
|
echo "[op] rename subA/crosssub.txt: dir1/subA -> dir2/subA (subdir to subdir)"
|
||||||
|
mv "$TESTDIR/subA/crosssub.txt" "$TESTDIR2/subA/crosssub.txt"
|
||||||
|
sleep 0.4
|
||||||
|
|
||||||
|
echo "[op] rename subA: dir1 -> dir2 (subdir across roots)"
|
||||||
|
mv "$TESTDIR/subA" "$TESTDIR2/subA2"
|
||||||
|
sleep 0.5
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
echo "--- done, stopping nightwatch ---"
|
echo "--- done, stopping nightwatch ---"
|
||||||
kill $NW_PID 2>/dev/null
|
kill $NW_PID 2>/dev/null
|
||||||
wait $NW_PID 2>/dev/null
|
wait $NW_PID 2>/dev/null
|
||||||
rm -rf "$TESTDIR"
|
rm -rf "$TESTDIR" "$TESTDIR2"
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue