refactor: use polling interface on linux in nightwatch cli
This commit is contained in:
parent
6f8cfc946b
commit
dd2bbb66ed
4 changed files with 58 additions and 35 deletions
34
src/main.zig
34
src/main.zig
|
|
@ -2,6 +2,11 @@ const std = @import("std");
|
||||||
const builtin = @import("builtin");
|
const builtin = @import("builtin");
|
||||||
const nightwatch = @import("nightwatch");
|
const nightwatch = @import("nightwatch");
|
||||||
|
|
||||||
|
const Watcher = switch (builtin.os.tag) {
|
||||||
|
.linux => nightwatch.Create(.polling),
|
||||||
|
else => nightwatch.Default,
|
||||||
|
};
|
||||||
|
|
||||||
const is_posix = switch (builtin.os.tag) {
|
const is_posix = switch (builtin.os.tag) {
|
||||||
.linux, .macos, .freebsd, .openbsd, .netbsd, .dragonfly => true,
|
.linux, .macos, .freebsd, .openbsd, .netbsd, .dragonfly => true,
|
||||||
.windows => false,
|
.windows => false,
|
||||||
|
|
@ -16,16 +21,23 @@ fn posix_sighandler(_: c_int) callconv(.c) void {
|
||||||
}
|
}
|
||||||
|
|
||||||
const CliHandler = struct {
|
const CliHandler = struct {
|
||||||
handler: nightwatch.Handler,
|
handler: Watcher.Handler,
|
||||||
out: std.fs.File,
|
out: std.fs.File,
|
||||||
ignore: []const []const u8,
|
ignore: []const []const u8,
|
||||||
|
|
||||||
const vtable = nightwatch.Handler.VTable{
|
const vtable: Watcher.Handler.VTable = switch (Watcher.interface_type) {
|
||||||
.change = change_cb,
|
.polling => .{
|
||||||
.rename = rename_cb,
|
.change = change_cb,
|
||||||
|
.rename = rename_cb,
|
||||||
|
.wait_readable = wait_readable_cb,
|
||||||
|
},
|
||||||
|
.threaded => .{
|
||||||
|
.change = change_cb,
|
||||||
|
.rename = rename_cb,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
fn change_cb(h: *nightwatch.Handler, path: []const u8, event_type: nightwatch.EventType, object_type: nightwatch.ObjectType) error{HandlerFailed}!void {
|
fn change_cb(h: *Watcher.Handler, path: []const u8, event_type: nightwatch.EventType, object_type: nightwatch.ObjectType) error{HandlerFailed}!void {
|
||||||
const self: *CliHandler = @fieldParentPtr("handler", h);
|
const self: *CliHandler = @fieldParentPtr("handler", h);
|
||||||
for (self.ignore) |ignored| {
|
for (self.ignore) |ignored| {
|
||||||
if (std.mem.eql(u8, path, ignored)) return;
|
if (std.mem.eql(u8, path, ignored)) return;
|
||||||
|
|
@ -47,7 +59,7 @@ const CliHandler = struct {
|
||||||
stdout.interface.print("{s} {s} {s}\n", .{ event_label, type_label, path }) catch return error.HandlerFailed;
|
stdout.interface.print("{s} {s} {s}\n", .{ event_label, type_label, path }) catch return error.HandlerFailed;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn rename_cb(h: *nightwatch.Handler, src: []const u8, dst: []const u8, object_type: nightwatch.ObjectType) error{HandlerFailed}!void {
|
fn rename_cb(h: *Watcher.Handler, src: []const u8, dst: []const u8, object_type: nightwatch.ObjectType) error{HandlerFailed}!void {
|
||||||
const self: *CliHandler = @fieldParentPtr("handler", h);
|
const self: *CliHandler = @fieldParentPtr("handler", h);
|
||||||
for (self.ignore) |ignored| {
|
for (self.ignore) |ignored| {
|
||||||
if (std.mem.eql(u8, src, ignored) or std.mem.eql(u8, dst, ignored)) return;
|
if (std.mem.eql(u8, src, ignored) or std.mem.eql(u8, dst, ignored)) return;
|
||||||
|
|
@ -63,7 +75,7 @@ const CliHandler = struct {
|
||||||
stdout.interface.print("rename {s} {s} -> {s}\n", .{ type_label, src, dst }) catch return error.HandlerFailed;
|
stdout.interface.print("rename {s} {s} -> {s}\n", .{ type_label, src, dst }) catch return error.HandlerFailed;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn wait_readable_cb(_: *nightwatch.Handler) error{HandlerFailed}!nightwatch.ReadableStatus {
|
fn wait_readable_cb(_: *Watcher.Handler) error{HandlerFailed}!Watcher.Handler.ReadableStatus {
|
||||||
return .will_notify;
|
return .will_notify;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -215,7 +227,11 @@ pub fn main() !void {
|
||||||
.ignore = ignore_list.items,
|
.ignore = ignore_list.items,
|
||||||
};
|
};
|
||||||
|
|
||||||
var watcher = try nightwatch.Default.init(allocator, &cli_handler.handler);
|
var watcher = switch (builtin.os.tag) {
|
||||||
|
.linux => try nightwatch.Create(.polling).init(allocator, &cli_handler.handler),
|
||||||
|
else => try nightwatch.Default.init(allocator, &cli_handler.handler),
|
||||||
|
};
|
||||||
|
|
||||||
defer watcher.deinit();
|
defer watcher.deinit();
|
||||||
|
|
||||||
for (watch_paths.items) |path| {
|
for (watch_paths.items) |path| {
|
||||||
|
|
@ -226,7 +242,7 @@ pub fn main() !void {
|
||||||
try stderr.interface.print("on watch: {s}\n", .{path});
|
try stderr.interface.print("on watch: {s}\n", .{path});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (nightwatch.linux_poll_mode) {
|
if (nightwatch.Default.interface_type == .polling) {
|
||||||
try run_linux(&watcher);
|
try run_linux(&watcher);
|
||||||
} else if (builtin.os.tag == .windows) {
|
} else if (builtin.os.tag == .windows) {
|
||||||
run_windows();
|
run_windows();
|
||||||
|
|
|
||||||
|
|
@ -6,10 +6,7 @@ const types = @import("types.zig");
|
||||||
pub const EventType = types.EventType;
|
pub const EventType = types.EventType;
|
||||||
pub const ObjectType = types.ObjectType;
|
pub const ObjectType = types.ObjectType;
|
||||||
pub const Error = types.Error;
|
pub const Error = types.Error;
|
||||||
pub const ReadableStatus = types.ReadableStatus;
|
|
||||||
pub const InterfaceType = types.InterfaceType;
|
pub const InterfaceType = types.InterfaceType;
|
||||||
pub const Handler = types.Handler;
|
|
||||||
pub const PollingHandler = types.PollingHandler;
|
|
||||||
|
|
||||||
pub const Variant = switch (builtin.os.tag) {
|
pub const Variant = switch (builtin.os.tag) {
|
||||||
.linux => InterfaceType,
|
.linux => InterfaceType,
|
||||||
|
|
@ -49,13 +46,21 @@ pub fn Create(comptime variant: Variant) type {
|
||||||
},
|
},
|
||||||
else => @compileError("unsupported OS"),
|
else => @compileError("unsupported OS"),
|
||||||
};
|
};
|
||||||
pub const interfaceType: InterfaceType = switch (builtin.os.tag) {
|
pub const interface_type: InterfaceType = switch (builtin.os.tag) {
|
||||||
.linux => variant,
|
.linux => variant,
|
||||||
else => .threaded,
|
else => .threaded,
|
||||||
};
|
};
|
||||||
|
pub const Handler = switch (interface_type) {
|
||||||
|
.threaded => types.Handler,
|
||||||
|
.polling => types.PollingHandler,
|
||||||
|
};
|
||||||
|
pub const InterceptorType = switch (interface_type) {
|
||||||
|
.threaded => Interceptor,
|
||||||
|
.polling => PollingInterceptor,
|
||||||
|
};
|
||||||
|
|
||||||
allocator: std.mem.Allocator,
|
allocator: std.mem.Allocator,
|
||||||
interceptor: *Interceptor,
|
interceptor: *InterceptorType,
|
||||||
|
|
||||||
/// True if the current backend detects file content modifications in real time.
|
/// True if the current backend detects file content modifications in real time.
|
||||||
/// False only when kqueue_dir_only=true, where directory-level watches are used
|
/// False only when kqueue_dir_only=true, where directory-level watches are used
|
||||||
|
|
@ -63,10 +68,10 @@ pub fn Create(comptime variant: Variant) type {
|
||||||
pub const detects_file_modifications = Backend.detects_file_modifications;
|
pub const detects_file_modifications = Backend.detects_file_modifications;
|
||||||
|
|
||||||
pub fn init(allocator: std.mem.Allocator, handler: *Handler) !@This() {
|
pub fn init(allocator: std.mem.Allocator, handler: *Handler) !@This() {
|
||||||
const ic = try allocator.create(Interceptor);
|
const ic = try allocator.create(InterceptorType);
|
||||||
errdefer allocator.destroy(ic);
|
errdefer allocator.destroy(ic);
|
||||||
ic.* = .{
|
ic.* = .{
|
||||||
.handler = .{ .vtable = &Interceptor.vtable },
|
.handler = .{ .vtable = &InterceptorType.vtable },
|
||||||
.user_handler = handler,
|
.user_handler = handler,
|
||||||
.allocator = allocator,
|
.allocator = allocator,
|
||||||
.backend = undefined,
|
.backend = undefined,
|
||||||
|
|
@ -153,7 +158,7 @@ pub fn Create(comptime variant: Variant) type {
|
||||||
return self.user_handler.rename(src, dst, object_type);
|
return self.user_handler.rename(src, dst, object_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn wait_readable_cb(h: *Handler) error{HandlerFailed}!ReadableStatus {
|
fn wait_readable_cb(h: *Handler) error{HandlerFailed}!Handler.ReadableStatus {
|
||||||
const self: *Interceptor = @fieldParentPtr("handler", h);
|
const self: *Interceptor = @fieldParentPtr("handler", h);
|
||||||
return self.user_handler.wait_readable();
|
return self.user_handler.wait_readable();
|
||||||
}
|
}
|
||||||
|
|
@ -171,8 +176,10 @@ pub fn Create(comptime variant: Variant) type {
|
||||||
.wait_readable = wait_readable_cb,
|
.wait_readable = wait_readable_cb,
|
||||||
};
|
};
|
||||||
|
|
||||||
fn change_cb(h: *Handler, path: []const u8, event_type: EventType, object_type: ObjectType) error{HandlerFailed}!void {
|
const PollingHandler = types.PollingHandler;
|
||||||
const self: *Interceptor = @fieldParentPtr("handler", h);
|
|
||||||
|
fn change_cb(h: *PollingHandler, path: []const u8, event_type: EventType, object_type: ObjectType) error{HandlerFailed}!void {
|
||||||
|
const self: *PollingInterceptor = @fieldParentPtr("handler", h);
|
||||||
if (event_type == .created and object_type == .dir and !Backend.watches_recursively) {
|
if (event_type == .created and object_type == .dir and !Backend.watches_recursively) {
|
||||||
self.backend.add_watch(self.allocator, path) catch {};
|
self.backend.add_watch(self.allocator, path) catch {};
|
||||||
recurse_watch(&self.backend, self.allocator, path);
|
recurse_watch(&self.backend, self.allocator, path);
|
||||||
|
|
@ -180,13 +187,13 @@ pub fn Create(comptime variant: Variant) type {
|
||||||
return self.user_handler.change(path, event_type, object_type);
|
return self.user_handler.change(path, event_type, object_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn rename_cb(h: *Handler, src: []const u8, dst: []const u8, object_type: ObjectType) error{HandlerFailed}!void {
|
fn rename_cb(h: *PollingHandler, src: []const u8, dst: []const u8, object_type: ObjectType) error{HandlerFailed}!void {
|
||||||
const self: *Interceptor = @fieldParentPtr("handler", h);
|
const self: *PollingInterceptor = @fieldParentPtr("handler", h);
|
||||||
return self.user_handler.rename(src, dst, object_type);
|
return self.user_handler.rename(src, dst, object_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn wait_readable_cb(h: *Handler) error{HandlerFailed}!ReadableStatus {
|
fn wait_readable_cb(h: *PollingHandler) error{HandlerFailed}!PollingHandler.ReadableStatus {
|
||||||
const self: *Interceptor = @fieldParentPtr("handler", h);
|
const self: *PollingInterceptor = @fieldParentPtr("handler", h);
|
||||||
return self.user_handler.wait_readable();
|
return self.user_handler.wait_readable();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ const RecordedEvent = union(enum) {
|
||||||
};
|
};
|
||||||
|
|
||||||
const TestHandler = struct {
|
const TestHandler = struct {
|
||||||
handler: nw.Handler,
|
handler: Watcher.Handler,
|
||||||
allocator: std.mem.Allocator,
|
allocator: std.mem.Allocator,
|
||||||
events: std.ArrayListUnmanaged(RecordedEvent),
|
events: std.ArrayListUnmanaged(RecordedEvent),
|
||||||
|
|
||||||
|
|
@ -46,12 +46,12 @@ const TestHandler = struct {
|
||||||
// vtable
|
// vtable
|
||||||
// -----------------------------------------------------------------------
|
// -----------------------------------------------------------------------
|
||||||
|
|
||||||
const vtable = nw.Handler.VTable{
|
const vtable = Watcher.Handler.VTable{
|
||||||
.change = change_cb,
|
.change = change_cb,
|
||||||
.rename = rename_cb,
|
.rename = rename_cb,
|
||||||
};
|
};
|
||||||
|
|
||||||
fn change_cb(handler: *nw.Handler, path: []const u8, event_type: nw.EventType, object_type: nw.ObjectType) error{HandlerFailed}!void {
|
fn change_cb(handler: *Watcher.Handler, path: []const u8, event_type: nw.EventType, object_type: nw.ObjectType) error{HandlerFailed}!void {
|
||||||
const self: *TestHandler = @fieldParentPtr("handler", handler);
|
const self: *TestHandler = @fieldParentPtr("handler", handler);
|
||||||
const owned = self.allocator.dupe(u8, path) catch return error.HandlerFailed;
|
const owned = self.allocator.dupe(u8, path) catch return error.HandlerFailed;
|
||||||
self.events.append(self.allocator, .{
|
self.events.append(self.allocator, .{
|
||||||
|
|
@ -62,7 +62,7 @@ const TestHandler = struct {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn rename_cb(handler: *nw.Handler, src: []const u8, dst: []const u8, _: nw.ObjectType) error{HandlerFailed}!void {
|
fn rename_cb(handler: *Watcher.Handler, src: []const u8, dst: []const u8, _: nw.ObjectType) error{HandlerFailed}!void {
|
||||||
const self: *TestHandler = @fieldParentPtr("handler", handler);
|
const self: *TestHandler = @fieldParentPtr("handler", handler);
|
||||||
const owned_src = self.allocator.dupe(u8, src) catch return error.HandlerFailed;
|
const owned_src = self.allocator.dupe(u8, src) catch return error.HandlerFailed;
|
||||||
errdefer self.allocator.free(owned_src);
|
errdefer self.allocator.free(owned_src);
|
||||||
|
|
@ -78,7 +78,7 @@ const TestHandler = struct {
|
||||||
// On Linux the inotify backend calls wait_readable() inside arm() and
|
// On Linux the inotify backend calls wait_readable() inside arm() and
|
||||||
// after each read-drain. We return `will_notify` so it parks; the test
|
// after each read-drain. We return `will_notify` so it parks; the test
|
||||||
// then calls handle_read_ready() explicitly to drive event delivery.
|
// then calls handle_read_ready() explicitly to drive event delivery.
|
||||||
fn wait_readable_cb(handler: *nw.Handler) error{HandlerFailed}!nw.ReadableStatus {
|
fn wait_readable_cb(handler: *Watcher.Handler) error{HandlerFailed}!nw.ReadableStatus {
|
||||||
_ = handler;
|
_ = handler;
|
||||||
return .will_notify;
|
return .will_notify;
|
||||||
}
|
}
|
||||||
|
|
@ -180,7 +180,7 @@ fn removeTempDir(path: []const u8) void {
|
||||||
/// - polling watchers: call handle_read_ready() so events are processed.
|
/// - polling watchers: call handle_read_ready() so events are processed.
|
||||||
/// - threaded watchers: the backend uses its own thread/callback; sleep briefly.
|
/// - threaded watchers: the backend uses its own thread/callback; sleep briefly.
|
||||||
fn drainEvents(watcher: *Watcher) !void {
|
fn drainEvents(watcher: *Watcher) !void {
|
||||||
switch (Watcher.interfaceType) {
|
switch (Watcher.interface_type) {
|
||||||
.polling => try watcher.handle_read_ready(),
|
.polling => try watcher.handle_read_ready(),
|
||||||
.threaded => std.Thread.sleep(300 * std.time.ns_per_ms),
|
.threaded => std.Thread.sleep(300 * std.time.ns_per_ms),
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -56,20 +56,20 @@ pub const PollingHandler = struct {
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const VTable = struct {
|
pub const VTable = struct {
|
||||||
change: *const fn (handler: *Handler, path: []const u8, event_type: EventType, object_type: ObjectType) error{HandlerFailed}!void,
|
change: *const fn (handler: *PollingHandler, path: []const u8, event_type: EventType, object_type: ObjectType) error{HandlerFailed}!void,
|
||||||
rename: *const fn (handler: *Handler, src_path: []const u8, dst_path: []const u8, object_type: ObjectType) error{HandlerFailed}!void,
|
rename: *const fn (handler: *PollingHandler, src_path: []const u8, dst_path: []const u8, object_type: ObjectType) error{HandlerFailed}!void,
|
||||||
wait_readable: *const fn (handler: *Handler) error{HandlerFailed}!ReadableStatus,
|
wait_readable: *const fn (handler: *PollingHandler) error{HandlerFailed}!ReadableStatus,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn change(handler: *Handler, path: []const u8, event_type: EventType, object_type: ObjectType) error{HandlerFailed}!void {
|
pub fn change(handler: *PollingHandler, path: []const u8, event_type: EventType, object_type: ObjectType) error{HandlerFailed}!void {
|
||||||
return handler.vtable.change(handler, path, event_type, object_type);
|
return handler.vtable.change(handler, path, event_type, object_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn rename(handler: *Handler, src_path: []const u8, dst_path: []const u8, object_type: ObjectType) error{HandlerFailed}!void {
|
pub fn rename(handler: *PollingHandler, src_path: []const u8, dst_path: []const u8, object_type: ObjectType) error{HandlerFailed}!void {
|
||||||
return handler.vtable.rename(handler, src_path, dst_path, object_type);
|
return handler.vtable.rename(handler, src_path, dst_path, object_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn wait_readable(handler: *Handler) error{HandlerFailed}!ReadableStatus {
|
pub fn wait_readable(handler: *PollingHandler) error{HandlerFailed}!ReadableStatus {
|
||||||
return handler.vtable.wait_readable(handler);
|
return handler.vtable.wait_readable(handler);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue