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,
|
fileChanged: *const fn (usize) void,
|
||||||
inotify_fd: ?std.os.fd_t = null,
|
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,
|
nfds_t: usize = 0,
|
||||||
wds: [MAX_FDS]i32 = [_]i32{0} ** MAX_FDS,
|
wds: [MAX_FDS]i32 = [_]i32{0} ** MAX_FDS,
|
||||||
modified: [MAX_FDS]bool = [_]bool{false} ** 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) {
|
while (true) {
|
||||||
if (self.nfds_t == 0) {
|
if (self.nfds_t == 0) {
|
||||||
std.time.sleep(250);
|
std.time.sleep(250);
|
||||||
|
@ -53,6 +60,9 @@ pub fn watchFds(self: *Self) void {
|
||||||
fds,
|
fds,
|
||||||
-1, // Infinite timeout
|
-1, // Infinite timeout
|
||||||
) catch @panic("poll error")) > 0) {
|
) 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"
|
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;
|
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
|
// "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),
|
@alignCast(@alignOf(*const std.os.linux.inotify_event), ptr),
|
||||||
);
|
);
|
||||||
|
|
||||||
if (ev.mask & std.os.linux.IN.MODIFY == std.os.linux.IN.MODIFY) {
|
// Read next event from inotify
|
||||||
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]);
|
|
||||||
ptr = @alignCast(
|
ptr = @alignCast(
|
||||||
@alignOf(std.os.linux.inotify_event),
|
@alignOf(std.os.linux.inotify_event),
|
||||||
ptr + @sizeOf(std.os.linux.inotify_event) + ev.len,
|
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 {
|
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);
|
self.inotify_fd = self.inotify_fd orelse try std.os.inotify_init1(std.os.linux.IN.NONBLOCK);
|
||||||
errdefer {
|
errdefer {
|
||||||
std.os.close(self.inotify_fd.?);
|
std.os.close(self.inotify_fd.?);
|
||||||
self.inotify_fd = null;
|
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.wds[self.nfds_t] = try std.os.inotify_add_watchZ(
|
||||||
self.inotify_fd.?,
|
self.inotify_fd.?,
|
||||||
path,
|
path,
|
||||||
|
@ -119,8 +157,3 @@ pub fn addFileWatch(self: *Self, path: [:0]const u8) !usize {
|
||||||
self.nfds_t += 1;
|
self.nfds_t += 1;
|
||||||
return 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 stdout.print("Run `zig build test` to run the tests.\n", .{});
|
||||||
|
|
||||||
try bw.flush(); // don't forget to flush!
|
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) {
|
while (true) {
|
||||||
std.time.sleep(std.time.ns_per_s * 2);
|
std.time.sleep(std.time.ns_per_s * 2);
|
||||||
|
|
Loading…
Reference in New Issue
Block a user