add documentation to watch
This commit is contained in:
parent
5e09b735c6
commit
1dd2a49daf
103
src/Watch.zig
103
src/Watch.zig
|
@ -10,8 +10,7 @@ const Self = @This();
|
|||
|
||||
fileChanged: *const fn (usize) void,
|
||||
inotify_fd: ?std.os.fd_t = null,
|
||||
// sizeof(std.os.pollfd) == 8, so this is 8k
|
||||
// fds: [MAX_FDS]std.os.pollfd = [_]std.os.pollfd{.{ .fd = 0, .events = 0, .revents = 0 }} ** MAX_FDS,
|
||||
|
||||
nfds_t: usize = 0,
|
||||
wds: [MAX_FDS]i32 = [_]i32{0} ** MAX_FDS,
|
||||
modified: [MAX_FDS]bool = [_]bool{false} ** MAX_FDS,
|
||||
|
@ -35,7 +34,15 @@ pub fn deinit(self: *Self) void {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn watchFds(self: *Self) void {
|
||||
/// starts the file watch. This function will not return, so it is best
|
||||
/// to put this function in its own thread:
|
||||
///
|
||||
/// const watcher_thread = try std.Thread.spawn(.{}, Watch.startWatch, .{&watcher});
|
||||
///
|
||||
/// Due to the nature of the poll(), behavior will almost definitely not work
|
||||
/// well if files are added after the watch begins. A method for doing this
|
||||
/// is intended later
|
||||
pub fn startWatch(self: *Self) void {
|
||||
while (true) {
|
||||
if (self.nfds_t == 0) {
|
||||
std.time.sleep(250);
|
||||
|
@ -53,6 +60,9 @@ pub fn watchFds(self: *Self) void {
|
|||
fds,
|
||||
-1, // Infinite timeout
|
||||
) catch @panic("poll error")) > 0) {
|
||||
|
||||
// fds[0] is inotify, so if we have data in that file descriptor,
|
||||
// we can force the data into an inotify_event structure and act on it
|
||||
if (fds[0].revents & c.POLLIN == c.POLLIN) { // POLLIN means "there is data to read"
|
||||
var event_buf: [4096]u8 align(@alignOf(std.os.linux.inotify_event)) = undefined;
|
||||
// "borrowed" from https://ziglang.org/documentation/master/std/src/std/fs/watch.zig.html#L588
|
||||
|
@ -66,32 +76,7 @@ pub fn watchFds(self: *Self) void {
|
|||
@alignCast(@alignOf(*const std.os.linux.inotify_event), ptr),
|
||||
);
|
||||
|
||||
if (ev.mask & std.os.linux.IN.MODIFY == std.os.linux.IN.MODIFY) {
|
||||
for (self.wds, 0..) |wd, inx| {
|
||||
if (ev.wd == wd)
|
||||
self.modified[inx] = true;
|
||||
}
|
||||
}
|
||||
// attrib added as build process moves in place and modifies attributes
|
||||
// TODO: Also watch MOVED_TO, which is on the directory...
|
||||
if (ev.mask & std.os.linux.IN.CLOSE_WRITE == std.os.linux.IN.CLOSE_WRITE or
|
||||
ev.mask & std.os.linux.IN.ATTRIB == std.os.linux.IN.ATTRIB)
|
||||
{
|
||||
for (self.wds, 0..) |wd, inx| {
|
||||
if (ev.wd == wd)
|
||||
self.fileChanged(inx);
|
||||
}
|
||||
}
|
||||
if (ev.mask & std.os.linux.IN.CLOSE_NOWRITE == std.os.linux.IN.CLOSE_NOWRITE) {
|
||||
for (self.wds, 0..) |wd, inx| {
|
||||
if (ev.wd == wd and self.modified[inx]) {
|
||||
self.modified[inx] = false;
|
||||
self.fileChanged(inx);
|
||||
}
|
||||
}
|
||||
}
|
||||
// see man 2 poll
|
||||
self.handleFile(fds[0]);
|
||||
// Read next event from inotify
|
||||
ptr = @alignCast(
|
||||
@alignOf(std.os.linux.inotify_event),
|
||||
ptr + @sizeOf(std.os.linux.inotify_event) + ev.len,
|
||||
|
@ -102,13 +87,66 @@ pub fn watchFds(self: *Self) void {
|
|||
}
|
||||
}
|
||||
|
||||
/// This will determine whether the inotify event indicates an actionable
|
||||
/// change to the file, and if so, will call self.fileChanged
|
||||
fn processInotifyEvent(self: *Self, ev: *const std.os.linux.inotify_event) void {
|
||||
// If the file was modified, it is good to know, but not
|
||||
// actionable at this time. We can set a modification flag
|
||||
// for later use. This flag and process may be unnecessary...
|
||||
// how can we have a modify followed by CLOSE_NOWRITE?
|
||||
//
|
||||
// TODO: Delete the following
|
||||
if (ev.mask & std.os.linux.IN.MODIFY == std.os.linux.IN.MODIFY) {
|
||||
for (self.wds, 0..) |wd, inx| {
|
||||
if (ev.wd == wd)
|
||||
self.modified[inx] = true;
|
||||
}
|
||||
}
|
||||
if (ev.mask & std.os.linux.IN.CLOSE_NOWRITE == std.os.linux.IN.CLOSE_NOWRITE) {
|
||||
for (self.wds, 0..) |wd, inx| {
|
||||
if (ev.wd == wd and self.modified[inx]) {
|
||||
self.modified[inx] = false;
|
||||
self.fileChanged(inx);
|
||||
}
|
||||
}
|
||||
}
|
||||
// There's a couple ways a file can be modified. The simplest
|
||||
// way is to write(), then close(). For a variety of reasons
|
||||
// due to safety, a lot of programs will write some temporary
|
||||
// file, then copy or move it in place. This will fail to
|
||||
// trigger IN_CLOSE_WRITE, so we need to detect it another
|
||||
// way. The best is to watch for events on the parent directory
|
||||
// to find move events. Note that using copy will trigger
|
||||
// a IN_CLOSE_WRITE. Without building directory watching in,
|
||||
// we can use IN_ATTRIB to satisfy the `zig build` use case,
|
||||
// which modifies attributes after moving the file.
|
||||
//
|
||||
// THIS WILL NOT WORK in the generic sense, and ultimately
|
||||
// we're going to have to watch the directory as well
|
||||
// attrib added as build process moves in place and modifies attributes
|
||||
//
|
||||
// TODO: Also watch MOVED_TO, which is on the directory...
|
||||
if (ev.mask & std.os.linux.IN.CLOSE_WRITE == std.os.linux.IN.CLOSE_WRITE or
|
||||
ev.mask & std.os.linux.IN.ATTRIB == std.os.linux.IN.ATTRIB)
|
||||
{
|
||||
for (self.wds, 0..) |wd, inx| {
|
||||
if (ev.wd == wd)
|
||||
self.fileChanged(inx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// adds a file to watch. The return will be a handle that will be returned
|
||||
/// in the fileChanged event triffered from startWatch
|
||||
pub fn addFileWatch(self: *Self, path: [:0]const u8) !usize {
|
||||
self.inotify_fd = self.inotify_fd orelse try std.os.inotify_init1(std.os.linux.IN.NONBLOCK);
|
||||
errdefer {
|
||||
std.os.close(self.inotify_fd.?);
|
||||
self.inotify_fd = null;
|
||||
}
|
||||
// open 20, close_norite 10, attrib 4
|
||||
// zig build modification pattern: open 20, close_nowrite 10, MOVED_TO (on the directory), attrib 4
|
||||
// unix cp: OPEN, MODIFY, CLOSE_WRITE, ATTRIB
|
||||
// unix mv: MOVED_TO (on the directory)
|
||||
self.wds[self.nfds_t] = try std.os.inotify_add_watchZ(
|
||||
self.inotify_fd.?,
|
||||
path,
|
||||
|
@ -119,8 +157,3 @@ pub fn addFileWatch(self: *Self, path: [:0]const u8) !usize {
|
|||
self.nfds_t += 1;
|
||||
return self.nfds_t - 1;
|
||||
}
|
||||
|
||||
fn handleFile(self: Self, fd: std.os.pollfd) void {
|
||||
_ = fd;
|
||||
_ = self;
|
||||
}
|
||||
|
|
|
@ -135,7 +135,7 @@ pub fn main() !void {
|
|||
try stdout.print("Run `zig build test` to run the tests.\n", .{});
|
||||
|
||||
try bw.flush(); // don't forget to flush!
|
||||
const watcher_thread = try std.Thread.spawn(.{}, Watch.watchFds, .{&watcher});
|
||||
const watcher_thread = try std.Thread.spawn(.{}, Watch.startWatch, .{&watcher});
|
||||
|
||||
while (true) {
|
||||
std.time.sleep(std.time.ns_per_s * 2);
|
||||
|
|
Loading…
Reference in New Issue
Block a user