aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/c.zig85
-rw-r--r--src/darwin_c.zig204
-rw-r--r--src/global.zig3
-rw-r--r--src/watcher.zig153
4 files changed, 238 insertions, 207 deletions
diff --git a/src/c.zig b/src/c.zig
index 1ac7f44ea..967d1dea6 100644
--- a/src/c.zig
+++ b/src/c.zig
@@ -4,3 +4,88 @@ pub usingnamespace switch (std.Target.current.os.tag) {
.macos => @import("./darwin_c.zig"),
else => struct {},
};
+
+usingnamespace std.c;
+const builtin = @import("builtin");
+const os = std.os;
+const mem = std.mem;
+const Stat = std.fs.File.Stat;
+const Kind = std.fs.File.Kind;
+const StatError = std.fs.File.StatError;
+const errno = os.errno;
+const zeroes = mem.zeroes;
+
+pub extern "c" fn chmod([*c]const u8, mode_t) c_int;
+pub extern "c" fn fchmod(c_int, mode_t) c_int;
+pub extern "c" fn umask(mode_t) mode_t;
+pub extern "c" fn fchmodat(c_int, [*c]const u8, mode_t, c_int) c_int;
+
+pub extern "c" fn lstat([*c]const u8, [*c]libc_stat) c_int;
+pub extern "c" fn lstat64([*c]const u8, [*c]libc_stat) c_int;
+
+pub fn lstat_absolute(path: [:0]const u8) StatError!Stat {
+ if (builtin.os.tag == .windows) {
+ var io_status_block: windows.IO_STATUS_BLOCK = undefined;
+ var info: windows.FILE_ALL_INFORMATION = undefined;
+ const rc = windows.ntdll.NtQueryInformationFile(self.handle, &io_status_block, &info, @sizeOf(windows.FILE_ALL_INFORMATION), .FileAllInformation);
+ switch (rc) {
+ .SUCCESS => {},
+ .BUFFER_OVERFLOW => {},
+ .INVALID_PARAMETER => unreachable,
+ .ACCESS_DENIED => return error.AccessDenied,
+ else => return windows.unexpectedStatus(rc),
+ }
+ return Stat{
+ .inode = info.InternalInformation.IndexNumber,
+ .size = @bitCast(u64, info.StandardInformation.EndOfFile),
+ .mode = 0,
+ .kind = if (info.StandardInformation.Directory == 0) .File else .Directory,
+ .atime = windows.fromSysTime(info.BasicInformation.LastAccessTime),
+ .mtime = windows.fromSysTime(info.BasicInformation.LastWriteTime),
+ .ctime = windows.fromSysTime(info.BasicInformation.CreationTime),
+ };
+ }
+
+ var st = zeroes(libc_stat);
+ switch (errno(lstat64(path.ptr, &st))) {
+ .SUCCESS => {},
+ // .EINVAL => unreachable,
+ .BADF => unreachable, // Always a race condition.
+ .NOMEM => return error.SystemResources,
+ .ACCES => return error.AccessDenied,
+ else => |err| return os.unexpectedErrno(err),
+ }
+
+ const atime = st.atime();
+ const mtime = st.mtime();
+ const ctime = st.ctime();
+ return Stat{
+ .inode = st.ino,
+ .size = @bitCast(u64, st.size),
+ .mode = st.mode,
+ .kind = switch (builtin.os.tag) {
+ .wasi => switch (st.filetype) {
+ os.FILETYPE_BLOCK_DEVICE => Kind.BlockDevice,
+ os.FILETYPE_CHARACTER_DEVICE => Kind.CharacterDevice,
+ os.FILETYPE_DIRECTORY => Kind.Directory,
+ os.FILETYPE_SYMBOLIC_LINK => Kind.SymLink,
+ os.FILETYPE_REGULAR_FILE => Kind.File,
+ os.FILETYPE_SOCKET_STREAM, os.FILETYPE_SOCKET_DGRAM => Kind.UnixDomainSocket,
+ else => Kind.Unknown,
+ },
+ else => switch (st.mode & os.S_IFMT) {
+ os.S_IFBLK => Kind.BlockDevice,
+ os.S_IFCHR => Kind.CharacterDevice,
+ os.S_IFDIR => Kind.Directory,
+ os.S_IFIFO => Kind.NamedPipe,
+ os.S_IFLNK => Kind.SymLink,
+ os.S_IFREG => Kind.File,
+ os.S_IFSOCK => Kind.UnixDomainSocket,
+ else => Kind.Unknown,
+ },
+ },
+ .atime = @as(i128, atime.tv_sec) * std.time.ns_per_s + atime.tv_nsec,
+ .mtime = @as(i128, mtime.tv_sec) * std.time.ns_per_s + mtime.tv_nsec,
+ .ctime = @as(i128, ctime.tv_sec) * std.time.ns_per_s + ctime.tv_nsec,
+ };
+}
diff --git a/src/darwin_c.zig b/src/darwin_c.zig
index 3a949d24a..559ebe8da 100644
--- a/src/darwin_c.zig
+++ b/src/darwin_c.zig
@@ -8,6 +8,7 @@ const Kind = std.fs.File.Kind;
const StatError = std.fs.File.StatError;
const errno = os.errno;
const zeroes = mem.zeroes;
+
// int clonefileat(int src_dirfd, const char * src, int dst_dirfd, const char * dst, int flags);
pub extern "c" fn clonefileat(c_int, [*c]const u8, c_int, [*c]const u8, uint32_t: c_int) c_int;
// int fclonefileat(int srcfd, int dst_dirfd, const char * dst, int flags);
@@ -15,147 +16,72 @@ pub extern "c" fn fclonefileat(c_int, c_int, [*c]const u8, uint32_t: c_int) c_in
// int clonefile(const char * src, const char * dst, int flags);
pub extern "c" fn clonefile([*c]const u8, [*c]const u8, uint32_t: c_int) c_int;
-pub extern "c" fn chmod([*c]const u8, mode_t) c_int;
-pub extern "c" fn fchmod(c_int, mode_t) c_int;
-pub extern "c" fn umask(mode_t) mode_t;
-pub extern "c" fn fchmodat(c_int, [*c]const u8, mode_t, c_int) c_int;
-
-pub extern "c" fn lstat([*c]const u8, [*c]libc_stat) c_int;
-pub extern "c" fn lstat64([*c]const u8, [*c]libc_stat) c_int;
-
-pub fn lstat_absolute(path: [:0]const u8) StatError!Stat {
- if (builtin.os.tag == .windows) {
- var io_status_block: windows.IO_STATUS_BLOCK = undefined;
- var info: windows.FILE_ALL_INFORMATION = undefined;
- const rc = windows.ntdll.NtQueryInformationFile(self.handle, &io_status_block, &info, @sizeOf(windows.FILE_ALL_INFORMATION), .FileAllInformation);
- switch (rc) {
- .SUCCESS => {},
- .BUFFER_OVERFLOW => {},
- .INVALID_PARAMETER => unreachable,
- .ACCESS_DENIED => return error.AccessDenied,
- else => return windows.unexpectedStatus(rc),
- }
- return Stat{
- .inode = info.InternalInformation.IndexNumber,
- .size = @bitCast(u64, info.StandardInformation.EndOfFile),
- .mode = 0,
- .kind = if (info.StandardInformation.Directory == 0) .File else .Directory,
- .atime = windows.fromSysTime(info.BasicInformation.LastAccessTime),
- .mtime = windows.fromSysTime(info.BasicInformation.LastWriteTime),
- .ctime = windows.fromSysTime(info.BasicInformation.CreationTime),
- };
- }
-
- var st = zeroes(libc_stat);
- switch (errno(lstat64(path.ptr, &st))) {
- .SUCCESS => {},
- // .EINVAL => unreachable,
- .BADF => unreachable, // Always a race condition.
- .NOMEM => return error.SystemResources,
- .ACCES => return error.AccessDenied,
- else => |err| return os.unexpectedErrno(err),
- }
-
- const atime = st.atime();
- const mtime = st.mtime();
- const ctime = st.ctime();
- return Stat{
- .inode = st.ino,
- .size = @bitCast(u64, st.size),
- .mode = st.mode,
- .kind = switch (builtin.os.tag) {
- .wasi => switch (st.filetype) {
- os.FILETYPE_BLOCK_DEVICE => Kind.BlockDevice,
- os.FILETYPE_CHARACTER_DEVICE => Kind.CharacterDevice,
- os.FILETYPE_DIRECTORY => Kind.Directory,
- os.FILETYPE_SYMBOLIC_LINK => Kind.SymLink,
- os.FILETYPE_REGULAR_FILE => Kind.File,
- os.FILETYPE_SOCKET_STREAM, os.FILETYPE_SOCKET_DGRAM => Kind.UnixDomainSocket,
- else => Kind.Unknown,
- },
- else => switch (st.mode & os.S_IFMT) {
- os.S_IFBLK => Kind.BlockDevice,
- os.S_IFCHR => Kind.CharacterDevice,
- os.S_IFDIR => Kind.Directory,
- os.S_IFIFO => Kind.NamedPipe,
- os.S_IFLNK => Kind.SymLink,
- os.S_IFREG => Kind.File,
- os.S_IFSOCK => Kind.UnixDomainSocket,
- else => Kind.Unknown,
- },
- },
- .atime = @as(i128, atime.tv_sec) * std.time.ns_per_s + atime.tv_nsec,
- .mtime = @as(i128, mtime.tv_sec) * std.time.ns_per_s + mtime.tv_nsec,
- .ctime = @as(i128, ctime.tv_sec) * std.time.ns_per_s + ctime.tv_nsec,
- };
-}
+// pub fn stat_absolute(path: [:0]const u8) StatError!Stat {
+// if (builtin.os.tag == .windows) {
+// var io_status_block: windows.IO_STATUS_BLOCK = undefined;
+// var info: windows.FILE_ALL_INFORMATION = undefined;
+// const rc = windows.ntdll.NtQueryInformationFile(self.handle, &io_status_block, &info, @sizeOf(windows.FILE_ALL_INFORMATION), .FileAllInformation);
+// switch (rc) {
+// .SUCCESS => {},
+// .BUFFER_OVERFLOW => {},
+// .INVALID_PARAMETER => unreachable,
+// .ACCESS_DENIED => return error.AccessDenied,
+// else => return windows.unexpectedStatus(rc),
+// }
+// return Stat{
+// .inode = info.InternalInformation.IndexNumber,
+// .size = @bitCast(u64, info.StandardInformation.EndOfFile),
+// .mode = 0,
+// .kind = if (info.StandardInformation.Directory == 0) .File else .Directory,
+// .atime = windows.fromSysTime(info.BasicInformation.LastAccessTime),
+// .mtime = windows.fromSysTime(info.BasicInformation.LastWriteTime),
+// .ctime = windows.fromSysTime(info.BasicInformation.CreationTime),
+// };
+// }
-pub fn stat_absolute(path: [:0]const u8) StatError!Stat {
- if (builtin.os.tag == .windows) {
- var io_status_block: windows.IO_STATUS_BLOCK = undefined;
- var info: windows.FILE_ALL_INFORMATION = undefined;
- const rc = windows.ntdll.NtQueryInformationFile(self.handle, &io_status_block, &info, @sizeOf(windows.FILE_ALL_INFORMATION), .FileAllInformation);
- switch (rc) {
- .SUCCESS => {},
- .BUFFER_OVERFLOW => {},
- .INVALID_PARAMETER => unreachable,
- .ACCESS_DENIED => return error.AccessDenied,
- else => return windows.unexpectedStatus(rc),
- }
- return Stat{
- .inode = info.InternalInformation.IndexNumber,
- .size = @bitCast(u64, info.StandardInformation.EndOfFile),
- .mode = 0,
- .kind = if (info.StandardInformation.Directory == 0) .File else .Directory,
- .atime = windows.fromSysTime(info.BasicInformation.LastAccessTime),
- .mtime = windows.fromSysTime(info.BasicInformation.LastWriteTime),
- .ctime = windows.fromSysTime(info.BasicInformation.CreationTime),
- };
- }
+// var st = zeroes(libc_stat);
+// switch (errno(stat(path.ptr, &st))) {
+// 0 => {},
+// // .EINVAL => unreachable,
+// .EBADF => unreachable, // Always a race condition.
+// .ENOMEM => return error.SystemResources,
+// .EACCES => return error.AccessDenied,
+// else => |err| return os.unexpectedErrno(err),
+// }
- var st = zeroes(libc_stat);
- switch (errno(stat(path.ptr, &st))) {
- 0 => {},
- // .EINVAL => unreachable,
- .EBADF => unreachable, // Always a race condition.
- .ENOMEM => return error.SystemResources,
- .EACCES => return error.AccessDenied,
- else => |err| return os.unexpectedErrno(err),
- }
-
- const atime = st.atime();
- const mtime = st.mtime();
- const ctime = st.ctime();
- return Stat{
- .inode = st.ino,
- .size = @bitCast(u64, st.size),
- .mode = st.mode,
- .kind = switch (builtin.os.tag) {
- .wasi => switch (st.filetype) {
- os.FILETYPE_BLOCK_DEVICE => Kind.BlockDevice,
- os.FILETYPE_CHARACTER_DEVICE => Kind.CharacterDevice,
- os.FILETYPE_DIRECTORY => Kind.Directory,
- os.FILETYPE_SYMBOLIC_LINK => Kind.SymLink,
- os.FILETYPE_REGULAR_FILE => Kind.File,
- os.FILETYPE_SOCKET_STREAM, os.FILETYPE_SOCKET_DGRAM => Kind.UnixDomainSocket,
- else => Kind.Unknown,
- },
- else => switch (st.mode & os.S_IFMT) {
- os.S_IFBLK => Kind.BlockDevice,
- os.S_IFCHR => Kind.CharacterDevice,
- os.S_IFDIR => Kind.Directory,
- os.S_IFIFO => Kind.NamedPipe,
- os.S_IFLNK => Kind.SymLink,
- os.S_IFREG => Kind.File,
- os.S_IFSOCK => Kind.UnixDomainSocket,
- else => Kind.Unknown,
- },
- },
- .atime = @as(i128, atime.tv_sec) * std.time.ns_per_s + atime.tv_nsec,
- .mtime = @as(i128, mtime.tv_sec) * std.time.ns_per_s + mtime.tv_nsec,
- .ctime = @as(i128, ctime.tv_sec) * std.time.ns_per_s + ctime.tv_nsec,
- };
-}
+// const atime = st.atime();
+// const mtime = st.mtime();
+// const ctime = st.ctime();
+// return Stat{
+// .inode = st.ino,
+// .size = @bitCast(u64, st.size),
+// .mode = st.mode,
+// .kind = switch (builtin.os.tag) {
+// .wasi => switch (st.filetype) {
+// os.FILETYPE_BLOCK_DEVICE => Kind.BlockDevice,
+// os.FILETYPE_CHARACTER_DEVICE => Kind.CharacterDevice,
+// os.FILETYPE_DIRECTORY => Kind.Directory,
+// os.FILETYPE_SYMBOLIC_LINK => Kind.SymLink,
+// os.FILETYPE_REGULAR_FILE => Kind.File,
+// os.FILETYPE_SOCKET_STREAM, os.FILETYPE_SOCKET_DGRAM => Kind.UnixDomainSocket,
+// else => Kind.Unknown,
+// },
+// else => switch (st.mode & os.S_IFMT) {
+// os.S_IFBLK => Kind.BlockDevice,
+// os.S_IFCHR => Kind.CharacterDevice,
+// os.S_IFDIR => Kind.Directory,
+// os.S_IFIFO => Kind.NamedPipe,
+// os.S_IFLNK => Kind.SymLink,
+// os.S_IFREG => Kind.File,
+// os.S_IFSOCK => Kind.UnixDomainSocket,
+// else => Kind.Unknown,
+// },
+// },
+// .atime = @as(i128, atime.tv_sec) * std.time.ns_per_s + atime.tv_nsec,
+// .mtime = @as(i128, mtime.tv_sec) * std.time.ns_per_s + mtime.tv_nsec,
+// .ctime = @as(i128, ctime.tv_sec) * std.time.ns_per_s + ctime.tv_nsec,
+// };
+// }
pub const struct_fstore = extern struct {
fst_flags: c_uint,
diff --git a/src/global.zig b/src/global.zig
index 1fec9ac5c..236751a6b 100644
--- a/src/global.zig
+++ b/src/global.zig
@@ -4,7 +4,8 @@ pub usingnamespace @import("strings.zig");
pub const default_allocator: *std.mem.Allocator = if (isTest) std.heap.c_allocator else @import("./memory_allocator.zig").c_allocator;
pub const C = @import("c.zig");
-pub usingnamespace @import("env.zig");
+pub const Environment = @import("env.zig");
+pub usingnamespace Environment;
pub const FeatureFlags = @import("feature_flags.zig");
const root = @import("root");
diff --git a/src/watcher.zig b/src/watcher.zig
index 105c29b12..7805222f2 100644
--- a/src/watcher.zig
+++ b/src/watcher.zig
@@ -5,6 +5,7 @@ const options = @import("./options.zig");
const IndexType = @import("./allocators.zig").IndexType;
const os = std.os;
+
const KEvent = std.os.Kevent;
const Mutex = @import("./lock.zig").Lock;
@@ -12,6 +13,8 @@ const WatchItemIndex = u16;
const NoWatchItem: WatchItemIndex = std.math.maxInt(WatchItemIndex);
const PackageJSON = @import("./resolver/package_json.zig").PackageJSON;
+const WATCHER_MAX_LIST = 8096;
+
pub const WatchItem = struct {
file_path: string,
// filepath hash for quick comparison
@@ -49,6 +52,20 @@ pub const WatchEvent = struct {
pub const Watchlist = std.MultiArrayList(WatchItem);
+const DarwinWatcher = struct {
+
+ // Internal
+ changelist: [128]KEvent = undefined,
+
+ // Everything being watched
+ eventlist: [WATCHER_MAX_LIST]KEvent = undefined,
+};
+
+const PlatformWatcher = if (Environment.isMac)
+ DarwinWatcher
+else
+ void;
+
// This implementation only works on macOS, for now.
// The Internet seems to suggest basically always using FSEvents instead of kqueue
// It seems like the main concern is max open file descriptors
@@ -57,23 +74,17 @@ pub fn NewWatcher(comptime ContextType: type) type {
return struct {
const Watcher = @This();
- const KEventArrayList = std.ArrayList(KEvent);
- const WATCHER_MAX_LIST = 8096;
-
watchlist: Watchlist,
watched_count: usize = 0,
mutex: Mutex,
- // Internal
- changelist: [128]KEvent = undefined,
+ eventlist_used: usize = 0,
+
+ platform: PlatformWatcher = PlatformWatcher{},
// User-facing
watch_events: [128]WatchEvent = undefined,
- // Everything being watched
- eventlist: [WATCHER_MAX_LIST]KEvent = undefined,
- eventlist_used: usize = 0,
-
fs: *Fs.FileSystem,
// this is what kqueue knows about
fd: StoredFileDescriptorType,
@@ -108,9 +119,11 @@ pub fn NewWatcher(comptime ContextType: type) type {
pub fn getQueue(this: *Watcher) !StoredFileDescriptorType {
if (this.fd == 0) {
- this.fd = try os.kqueue();
- if (this.fd == 0) {
- return error.WatcherFailed;
+ if (Environment.isMac) {
+ this.fd = try os.kqueue();
+ if (this.fd == 0) {
+ return error.WatcherFailed;
+ }
}
}
@@ -202,28 +215,31 @@ pub fn NewWatcher(comptime ContextType: type) type {
fn _watchLoop(this: *Watcher) !void {
const time = std.time;
- std.debug.assert(this.fd > 0);
+ if (Environment.isMac) {
+ std.debug.assert(this.fd > 0);
- var changelist_array: [1]KEvent = std.mem.zeroes([1]KEvent);
- var changelist = &changelist_array;
- while (true) {
- defer Output.flush();
- var code = std.os.system.kevent(
- try this.getQueue(),
- @as([*]KEvent, changelist),
- 0,
- @as([*]KEvent, changelist),
- 1,
+ var changelist_array: [1]KEvent = std.mem.zeroes([1]KEvent);
+ var changelist = &changelist_array;
+ while (true) {
+ defer Output.flush();
- null,
- );
+ _ = std.os.system.kevent(
+ try this.getQueue(),
+ @as([*]KEvent, changelist),
+ 0,
+ @as([*]KEvent, changelist),
+ 1,
- var watchevents = this.watch_events[0..1];
- for (changelist) |event, i| {
- watchevents[i].fromKEvent(&event);
- }
+ null,
+ );
+
+ var watchevents = this.watch_events[0..1];
+ for (changelist) |event, i| {
+ watchevents[i].fromKEvent(&event);
+ }
- this.ctx.onFileUpdate(watchevents, this.watchlist);
+ this.ctx.onFileUpdate(watchevents, this.watchlist);
+ }
}
}
@@ -266,7 +282,7 @@ pub fn NewWatcher(comptime ContextType: type) type {
const index = this.eventlist_used;
const watchlist_id = this.watchlist.len;
- if (isMac) {
+ if (Environment.isMac) {
// https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/kqueue.2.html
var event = std.mem.zeroes(KEvent);
@@ -290,7 +306,7 @@ pub fn NewWatcher(comptime ContextType: type) type {
// Store the hash for fast filtering later
event.udata = @intCast(usize, watchlist_id);
- this.eventlist[index] = event;
+ this.platform.eventlist[index] = event;
// This took a lot of work to figure out the right permutation
// Basically:
@@ -298,9 +314,9 @@ pub fn NewWatcher(comptime ContextType: type) type {
// our while(true) loop above receives notification of changes to any of the events created here.
_ = std.os.system.kevent(
try this.getQueue(),
- this.eventlist[index .. index + 1].ptr,
+ this.platform.eventlist[index .. index + 1].ptr,
1,
- this.eventlist[index .. index + 1].ptr,
+ this.platform.eventlist[index .. index + 1].ptr,
0,
null,
);
@@ -337,39 +353,42 @@ pub fn NewWatcher(comptime ContextType: type) type {
const index = this.eventlist_used;
const watchlist_id = this.watchlist.len;
- // https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/kqueue.2.html
- var event = std.mem.zeroes(KEvent);
-
- event.flags = os.EV_ADD | os.EV_CLEAR | os.EV_ENABLE;
- // we want to know about the vnode
- event.filter = std.os.EVFILT_VNODE;
-
- // monitor:
- // - Write
- // - Rename
- // - Delete
- event.fflags = std.os.NOTE_WRITE | std.os.NOTE_RENAME | std.os.NOTE_DELETE;
-
- // id
- event.ident = @intCast(usize, fd);
-
- this.eventlist_used += 1;
- // Store the hash for fast filtering later
- event.udata = @intCast(usize, watchlist_id);
- this.eventlist[index] = event;
-
- // This took a lot of work to figure out the right permutation
- // Basically:
- // - We register the event here.
- // our while(true) loop above receives notification of changes to any of the events created here.
- _ = std.os.system.kevent(
- try this.getQueue(),
- this.eventlist[index .. index + 1].ptr,
- 1,
- this.eventlist[index .. index + 1].ptr,
- 0,
- null,
- );
+ if (Environment.isMac) {
+
+ // https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/kqueue.2.html
+ var event = std.mem.zeroes(KEvent);
+
+ event.flags = os.EV_ADD | os.EV_CLEAR | os.EV_ENABLE;
+ // we want to know about the vnode
+ event.filter = std.os.EVFILT_VNODE;
+
+ // monitor:
+ // - Write
+ // - Rename
+ // - Delete
+ event.fflags = std.os.NOTE_WRITE | std.os.NOTE_RENAME | std.os.NOTE_DELETE;
+
+ // id
+ event.ident = @intCast(usize, fd);
+
+ this.eventlist_used += 1;
+ // Store the hash for fast filtering later
+ event.udata = @intCast(usize, watchlist_id);
+ this.platform.eventlist[index] = event;
+
+ // This took a lot of work to figure out the right permutation
+ // Basically:
+ // - We register the event here.
+ // our while(true) loop above receives notification of changes to any of the events created here.
+ _ = std.os.system.kevent(
+ try this.getQueue(),
+ this.platform.eventlist[index .. index + 1].ptr,
+ 1,
+ this.platform.eventlist[index .. index + 1].ptr,
+ 0,
+ null,
+ );
+ }
this.watchlist.appendAssumeCapacity(.{
.file_path = if (copy_file_path) try this.allocator.dupe(u8, file_path) else file_path,