std: file system watching for linux
This commit is contained in:
parent
a870228ab4
commit
3c8d4e04ea
211
std/event/fs.zig
211
std/event/fs.zig
@ -20,17 +20,18 @@ pub const Request = struct {
|
|||||||
PWriteV: PWriteV,
|
PWriteV: PWriteV,
|
||||||
PReadV: PReadV,
|
PReadV: PReadV,
|
||||||
OpenRead: OpenRead,
|
OpenRead: OpenRead,
|
||||||
|
OpenRW: OpenRW,
|
||||||
Close: Close,
|
Close: Close,
|
||||||
WriteFile: WriteFile,
|
WriteFile: WriteFile,
|
||||||
End, // special - means the fs thread should exit
|
End, // special - means the fs thread should exit
|
||||||
|
|
||||||
pub const PWriteV = struct {
|
pub const PWriteV = struct {
|
||||||
fd: os.FileHandle,
|
fd: os.FileHandle,
|
||||||
data: []const []const u8,
|
iov: []os.linux.iovec_const,
|
||||||
offset: usize,
|
offset: usize,
|
||||||
result: Error!void,
|
result: Error!void,
|
||||||
|
|
||||||
pub const Error = error{};
|
pub const Error = os.File.WriteError;
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const PReadV = struct {
|
pub const PReadV = struct {
|
||||||
@ -50,6 +51,15 @@ pub const Request = struct {
|
|||||||
pub const Error = os.File.OpenError;
|
pub const Error = os.File.OpenError;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub const OpenRW = struct {
|
||||||
|
/// must be null terminated. TODO https://github.com/ziglang/zig/issues/265
|
||||||
|
path: []const u8,
|
||||||
|
result: Error!os.FileHandle,
|
||||||
|
mode: os.File.Mode,
|
||||||
|
|
||||||
|
pub const Error = os.File.OpenError;
|
||||||
|
};
|
||||||
|
|
||||||
pub const WriteFile = struct {
|
pub const WriteFile = struct {
|
||||||
/// must be null terminated. TODO https://github.com/ziglang/zig/issues/265
|
/// must be null terminated. TODO https://github.com/ziglang/zig/issues/265
|
||||||
path: []const u8,
|
path: []const u8,
|
||||||
@ -66,7 +76,7 @@ pub const Request = struct {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
/// data - both the outer and inner references - must live until pwritev promise completes.
|
/// data - just the inner references - must live until pwritev promise completes.
|
||||||
pub async fn pwritev(loop: *event.Loop, fd: os.FileHandle, offset: usize, data: []const []const u8) !void {
|
pub async fn pwritev(loop: *event.Loop, fd: os.FileHandle, offset: usize, data: []const []const u8) !void {
|
||||||
//const data_dupe = try mem.dupe(loop.allocator, []const u8, data);
|
//const data_dupe = try mem.dupe(loop.allocator, []const u8, data);
|
||||||
//defer loop.allocator.free(data_dupe);
|
//defer loop.allocator.free(data_dupe);
|
||||||
@ -78,13 +88,23 @@ pub async fn pwritev(loop: *event.Loop, fd: os.FileHandle, offset: usize, data:
|
|||||||
resume p;
|
resume p;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const iovecs = try loop.allocator.alloc(os.linux.iovec_const, data.len);
|
||||||
|
defer loop.allocator.free(iovecs);
|
||||||
|
|
||||||
|
for (data) |buf, i| {
|
||||||
|
iovecs[i] = os.linux.iovec_const{
|
||||||
|
.iov_base = buf.ptr,
|
||||||
|
.iov_len = buf.len,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
var req_node = RequestNode{
|
var req_node = RequestNode{
|
||||||
.next = undefined,
|
.next = undefined,
|
||||||
.data = Request{
|
.data = Request{
|
||||||
.msg = Request.Msg{
|
.msg = Request.Msg{
|
||||||
.PWriteV = Request.Msg.PWriteV{
|
.PWriteV = Request.Msg.PWriteV{
|
||||||
.fd = fd,
|
.fd = fd,
|
||||||
.data = data,
|
.iov = iovecs,
|
||||||
.offset = offset,
|
.offset = offset,
|
||||||
.result = undefined,
|
.result = undefined,
|
||||||
},
|
},
|
||||||
@ -162,12 +182,15 @@ pub async fn openRead(loop: *event.Loop, path: []const u8) os.File.OpenError!os.
|
|||||||
resume p;
|
resume p;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const path_with_null = try std.cstr.addNullByte(loop.allocator, path);
|
||||||
|
defer loop.allocator.free(path_with_null);
|
||||||
|
|
||||||
var req_node = RequestNode{
|
var req_node = RequestNode{
|
||||||
.next = undefined,
|
.next = undefined,
|
||||||
.data = Request{
|
.data = Request{
|
||||||
.msg = Request.Msg{
|
.msg = Request.Msg{
|
||||||
.OpenRead = Request.Msg.OpenRead{
|
.OpenRead = Request.Msg.OpenRead{
|
||||||
.path = path,
|
.path = path_with_null[0..path.len],
|
||||||
.result = undefined,
|
.result = undefined,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -187,6 +210,48 @@ pub async fn openRead(loop: *event.Loop, path: []const u8) os.File.OpenError!os.
|
|||||||
return req_node.data.msg.OpenRead.result;
|
return req_node.data.msg.OpenRead.result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Creates if does not exist. Does not truncate.
|
||||||
|
pub async fn openReadWrite(
|
||||||
|
loop: *event.Loop,
|
||||||
|
path: []const u8,
|
||||||
|
mode: os.File.Mode,
|
||||||
|
) os.File.OpenError!os.FileHandle {
|
||||||
|
// workaround for https://github.com/ziglang/zig/issues/1194
|
||||||
|
var my_handle: promise = undefined;
|
||||||
|
suspend |p| {
|
||||||
|
my_handle = p;
|
||||||
|
resume p;
|
||||||
|
}
|
||||||
|
|
||||||
|
const path_with_null = try std.cstr.addNullByte(loop.allocator, path);
|
||||||
|
defer loop.allocator.free(path_with_null);
|
||||||
|
|
||||||
|
var req_node = RequestNode{
|
||||||
|
.next = undefined,
|
||||||
|
.data = Request{
|
||||||
|
.msg = Request.Msg{
|
||||||
|
.OpenRW = Request.Msg.OpenRW{
|
||||||
|
.path = path_with_null[0..path.len],
|
||||||
|
.mode = mode,
|
||||||
|
.result = undefined,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
.finish = Request.Finish{
|
||||||
|
.TickNode = event.Loop.NextTickNode{
|
||||||
|
.next = undefined,
|
||||||
|
.data = my_handle,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
suspend |_| {
|
||||||
|
loop.linuxFsRequest(&req_node);
|
||||||
|
}
|
||||||
|
|
||||||
|
return req_node.data.msg.OpenRW.result;
|
||||||
|
}
|
||||||
|
|
||||||
/// This abstraction helps to close file handles in defer expressions
|
/// This abstraction helps to close file handles in defer expressions
|
||||||
/// without suspending. Start a CloseOperation before opening a file.
|
/// without suspending. Start a CloseOperation before opening a file.
|
||||||
pub const CloseOperation = struct {
|
pub const CloseOperation = struct {
|
||||||
@ -302,6 +367,113 @@ pub async fn readFile(loop: *event.Loop, file_path: []const u8, max_size: usize)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub const Watch = struct {
|
||||||
|
channel: *event.Channel(Event),
|
||||||
|
putter: promise,
|
||||||
|
|
||||||
|
pub const Event = union(enum) {
|
||||||
|
CloseWrite,
|
||||||
|
Err: Error,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const Error = error{
|
||||||
|
UserResourceLimitReached,
|
||||||
|
SystemResources,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn destroy(self: *Watch) void {
|
||||||
|
// TODO https://github.com/ziglang/zig/issues/1261
|
||||||
|
cancel self.putter;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn watchFile(loop: *event.Loop, file_path: []const u8) !*Watch {
|
||||||
|
const path_with_null = try std.cstr.addNullByte(loop.allocator, file_path);
|
||||||
|
defer loop.allocator.free(path_with_null);
|
||||||
|
|
||||||
|
const inotify_fd = try os.linuxINotifyInit1(os.linux.IN_NONBLOCK | os.linux.IN_CLOEXEC);
|
||||||
|
errdefer os.close(inotify_fd);
|
||||||
|
|
||||||
|
const wd = try os.linuxINotifyAddWatchC(inotify_fd, path_with_null.ptr, os.linux.IN_CLOSE_WRITE);
|
||||||
|
errdefer os.close(wd);
|
||||||
|
|
||||||
|
const channel = try event.Channel(Watch.Event).create(loop, 0);
|
||||||
|
errdefer channel.destroy();
|
||||||
|
|
||||||
|
var result: *Watch = undefined;
|
||||||
|
_ = try async<loop.allocator> watchEventPutter(inotify_fd, wd, channel, &result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn watchEventPutter(inotify_fd: i32, wd: i32, channel: *event.Channel(Watch.Event), out_watch: **Watch) void {
|
||||||
|
// TODO https://github.com/ziglang/zig/issues/1194
|
||||||
|
var my_handle: promise = undefined;
|
||||||
|
suspend |p| {
|
||||||
|
my_handle = p;
|
||||||
|
resume p;
|
||||||
|
}
|
||||||
|
|
||||||
|
var watch = Watch{
|
||||||
|
.putter = my_handle,
|
||||||
|
.channel = channel,
|
||||||
|
};
|
||||||
|
out_watch.* = &watch;
|
||||||
|
|
||||||
|
const loop = channel.loop;
|
||||||
|
loop.beginOneEvent();
|
||||||
|
|
||||||
|
defer {
|
||||||
|
channel.destroy();
|
||||||
|
os.close(wd);
|
||||||
|
os.close(inotify_fd);
|
||||||
|
loop.finishOneEvent();
|
||||||
|
}
|
||||||
|
|
||||||
|
var event_buf: [4096]u8 align(@alignOf(os.linux.inotify_event)) = undefined;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
const rc = os.linux.read(inotify_fd, &event_buf, event_buf.len);
|
||||||
|
const errno = os.linux.getErrno(rc);
|
||||||
|
switch (errno) {
|
||||||
|
0 => {
|
||||||
|
// can't use @bytesToSlice because of the special variable length name field
|
||||||
|
var ptr = event_buf[0..].ptr;
|
||||||
|
const end_ptr = ptr + event_buf.len;
|
||||||
|
var ev: *os.linux.inotify_event = undefined;
|
||||||
|
while (@ptrToInt(ptr) < @ptrToInt(end_ptr)) : (ptr += @sizeOf(os.linux.inotify_event) + ev.len) {
|
||||||
|
ev = @ptrCast(*os.linux.inotify_event, ptr);
|
||||||
|
if (ev.mask & os.linux.IN_CLOSE_WRITE == os.linux.IN_CLOSE_WRITE) {
|
||||||
|
await (async channel.put(Watch.Event.CloseWrite) catch unreachable);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
os.linux.EINTR => continue,
|
||||||
|
os.linux.EINVAL => unreachable,
|
||||||
|
os.linux.EFAULT => unreachable,
|
||||||
|
os.linux.EAGAIN => {
|
||||||
|
(await (async loop.linuxWaitFd(
|
||||||
|
inotify_fd,
|
||||||
|
os.linux.EPOLLET | os.linux.EPOLLIN,
|
||||||
|
) catch unreachable)) catch |err| {
|
||||||
|
const transformed_err = switch (err) {
|
||||||
|
error.InvalidFileDescriptor => unreachable,
|
||||||
|
error.FileDescriptorAlreadyPresentInSet => unreachable,
|
||||||
|
error.InvalidSyscall => unreachable,
|
||||||
|
error.OperationCausesCircularLoop => unreachable,
|
||||||
|
error.FileDescriptorNotRegistered => unreachable,
|
||||||
|
error.SystemResources => error.SystemResources,
|
||||||
|
error.UserResourceLimitReached => error.UserResourceLimitReached,
|
||||||
|
error.FileDescriptorIncompatibleWithEpoll => unreachable,
|
||||||
|
error.Unexpected => unreachable,
|
||||||
|
};
|
||||||
|
await (async channel.put(Watch.Event{ .Err = transformed_err }) catch unreachable);
|
||||||
|
};
|
||||||
|
},
|
||||||
|
else => unreachable,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const test_tmp_dir = "std_event_fs_test";
|
const test_tmp_dir = "std_event_fs_test";
|
||||||
|
|
||||||
test "write a file, watch it, write it again" {
|
test "write a file, watch it, write it again" {
|
||||||
@ -338,10 +510,39 @@ async fn testFsWatch(loop: *event.Loop) !void {
|
|||||||
\\line 1
|
\\line 1
|
||||||
\\line 2
|
\\line 2
|
||||||
;
|
;
|
||||||
|
const line2_offset = 7;
|
||||||
|
|
||||||
// first just write then read the file
|
// first just write then read the file
|
||||||
try await try async writeFile(loop, file_path, contents);
|
try await try async writeFile(loop, file_path, contents);
|
||||||
|
|
||||||
const read_contents = try await try async readFile(loop, file_path, 1024 * 1024);
|
const read_contents = try await try async readFile(loop, file_path, 1024 * 1024);
|
||||||
assert(mem.eql(u8, read_contents, contents));
|
assert(mem.eql(u8, read_contents, contents));
|
||||||
|
|
||||||
|
// now watch the file
|
||||||
|
var watch = try watchFile(loop, file_path);
|
||||||
|
defer watch.destroy();
|
||||||
|
|
||||||
|
const ev = try async watch.channel.get();
|
||||||
|
var ev_consumed = false;
|
||||||
|
defer if (!ev_consumed) cancel ev;
|
||||||
|
|
||||||
|
// overwrite line 2
|
||||||
|
const fd = try await try async openReadWrite(loop, file_path, os.File.default_mode);
|
||||||
|
{
|
||||||
|
defer os.close(fd);
|
||||||
|
|
||||||
|
try await try async pwritev(loop, fd, line2_offset, []const []const u8{"lorem ipsum"});
|
||||||
|
}
|
||||||
|
|
||||||
|
ev_consumed = true;
|
||||||
|
switch (await ev) {
|
||||||
|
Watch.Event.CloseWrite => {},
|
||||||
|
Watch.Event.Err => |err| return err,
|
||||||
|
}
|
||||||
|
|
||||||
|
const contents_updated = try await try async readFile(loop, file_path, 1024 * 1024);
|
||||||
|
assert(mem.eql(u8, contents_updated,
|
||||||
|
\\line 1
|
||||||
|
\\lorem ipsum
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
@ -318,45 +318,46 @@ pub const Loop = struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// resume_node must live longer than the promise that it holds a reference to.
|
/// resume_node must live longer than the promise that it holds a reference to.
|
||||||
pub fn addFd(self: *Loop, fd: i32, resume_node: *ResumeNode) !void {
|
/// flags must contain EPOLLET
|
||||||
_ = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst);
|
pub fn linuxAddFd(self: *Loop, fd: i32, resume_node: *ResumeNode, flags: u32) !void {
|
||||||
errdefer {
|
assert(flags & posix.EPOLLET == posix.EPOLLET);
|
||||||
self.finishOneEvent();
|
self.beginOneEvent();
|
||||||
}
|
errdefer self.finishOneEvent();
|
||||||
try self.modFd(
|
try self.linuxModFd(
|
||||||
fd,
|
fd,
|
||||||
posix.EPOLL_CTL_ADD,
|
posix.EPOLL_CTL_ADD,
|
||||||
os.linux.EPOLLIN | os.linux.EPOLLOUT | os.linux.EPOLLET,
|
flags,
|
||||||
resume_node,
|
resume_node,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn modFd(self: *Loop, fd: i32, op: u32, events: u32, resume_node: *ResumeNode) !void {
|
pub fn linuxModFd(self: *Loop, fd: i32, op: u32, flags: u32, resume_node: *ResumeNode) !void {
|
||||||
|
assert(flags & posix.EPOLLET == posix.EPOLLET);
|
||||||
var ev = os.linux.epoll_event{
|
var ev = os.linux.epoll_event{
|
||||||
.events = events,
|
.events = flags,
|
||||||
.data = os.linux.epoll_data{ .ptr = @ptrToInt(resume_node) },
|
.data = os.linux.epoll_data{ .ptr = @ptrToInt(resume_node) },
|
||||||
};
|
};
|
||||||
try os.linuxEpollCtl(self.os_data.epollfd, op, fd, &ev);
|
try os.linuxEpollCtl(self.os_data.epollfd, op, fd, &ev);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn removeFd(self: *Loop, fd: i32) void {
|
pub fn linuxRemoveFd(self: *Loop, fd: i32) void {
|
||||||
self.removeFdNoCounter(fd);
|
self.linuxRemoveFdNoCounter(fd);
|
||||||
self.finishOneEvent();
|
self.finishOneEvent();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn removeFdNoCounter(self: *Loop, fd: i32) void {
|
fn linuxRemoveFdNoCounter(self: *Loop, fd: i32) void {
|
||||||
os.linuxEpollCtl(self.os_data.epollfd, os.linux.EPOLL_CTL_DEL, fd, undefined) catch {};
|
os.linuxEpollCtl(self.os_data.epollfd, os.linux.EPOLL_CTL_DEL, fd, undefined) catch {};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn waitFd(self: *Loop, fd: i32) !void {
|
pub async fn linuxWaitFd(self: *Loop, fd: i32, flags: u32) !void {
|
||||||
defer self.removeFd(fd);
|
defer self.linuxRemoveFd(fd);
|
||||||
suspend |p| {
|
suspend |p| {
|
||||||
// TODO explicitly put this memory in the coroutine frame #1194
|
// TODO explicitly put this memory in the coroutine frame #1194
|
||||||
var resume_node = ResumeNode{
|
var resume_node = ResumeNode{
|
||||||
.id = ResumeNode.Id.Basic,
|
.id = ResumeNode.Id.Basic,
|
||||||
.handle = p,
|
.handle = p,
|
||||||
};
|
};
|
||||||
try self.addFd(fd, &resume_node);
|
try self.linuxAddFd(fd, &resume_node, flags);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -382,7 +383,7 @@ pub const Loop = struct {
|
|||||||
// the pending count is already accounted for
|
// the pending count is already accounted for
|
||||||
const epoll_events = posix.EPOLLONESHOT | os.linux.EPOLLIN | os.linux.EPOLLOUT |
|
const epoll_events = posix.EPOLLONESHOT | os.linux.EPOLLIN | os.linux.EPOLLOUT |
|
||||||
os.linux.EPOLLET;
|
os.linux.EPOLLET;
|
||||||
self.modFd(
|
self.linuxModFd(
|
||||||
eventfd_node.eventfd,
|
eventfd_node.eventfd,
|
||||||
eventfd_node.epoll_op,
|
eventfd_node.epoll_op,
|
||||||
epoll_events,
|
epoll_events,
|
||||||
@ -416,7 +417,7 @@ pub const Loop = struct {
|
|||||||
|
|
||||||
/// Bring your own linked list node. This means it can't fail.
|
/// Bring your own linked list node. This means it can't fail.
|
||||||
pub fn onNextTick(self: *Loop, node: *NextTickNode) void {
|
pub fn onNextTick(self: *Loop, node: *NextTickNode) void {
|
||||||
_ = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst);
|
self.beginOneEvent(); // finished in dispatch()
|
||||||
self.next_tick_queue.put(node);
|
self.next_tick_queue.put(node);
|
||||||
self.dispatch();
|
self.dispatch();
|
||||||
}
|
}
|
||||||
@ -470,8 +471,14 @@ pub const Loop = struct {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn finishOneEvent(self: *Loop) void {
|
/// call finishOneEvent when done
|
||||||
if (@atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst) == 1) {
|
pub fn beginOneEvent(self: *Loop) void {
|
||||||
|
_ = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn finishOneEvent(self: *Loop) void {
|
||||||
|
const prev = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst);
|
||||||
|
if (prev == 1) {
|
||||||
// cause all the threads to stop
|
// cause all the threads to stop
|
||||||
switch (builtin.os) {
|
switch (builtin.os) {
|
||||||
builtin.Os.linux => {
|
builtin.Os.linux => {
|
||||||
@ -593,7 +600,7 @@ pub const Loop = struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn linuxFsRequest(self: *Loop, request_node: *fs.RequestNode) void {
|
fn linuxFsRequest(self: *Loop, request_node: *fs.RequestNode) void {
|
||||||
_ = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst);
|
self.beginOneEvent(); // finished in linuxFsRun after processing the msg
|
||||||
self.os_data.fs_queue.put(request_node);
|
self.os_data.fs_queue.put(request_node);
|
||||||
_ = @atomicRmw(i32, &self.os_data.fs_queue_len, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst); // let this wrap
|
_ = @atomicRmw(i32, &self.os_data.fs_queue_len, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst); // let this wrap
|
||||||
const rc = os.linux.futex_wake(@ptrToInt(&self.os_data.fs_queue_len), os.linux.FUTEX_WAKE, 1);
|
const rc = os.linux.futex_wake(@ptrToInt(&self.os_data.fs_queue_len), os.linux.FUTEX_WAKE, 1);
|
||||||
@ -610,14 +617,21 @@ pub const Loop = struct {
|
|||||||
while (self.os_data.fs_queue.get()) |node| {
|
while (self.os_data.fs_queue.get()) |node| {
|
||||||
processed_count +%= 1;
|
processed_count +%= 1;
|
||||||
switch (node.data.msg) {
|
switch (node.data.msg) {
|
||||||
@TagType(fs.Request.Msg).PWriteV => @panic("TODO"),
|
@TagType(fs.Request.Msg).End => return,
|
||||||
|
@TagType(fs.Request.Msg).PWriteV => |*msg| {
|
||||||
|
msg.result = os.posix_pwritev(msg.fd, msg.iov.ptr, msg.iov.len, msg.offset);
|
||||||
|
},
|
||||||
@TagType(fs.Request.Msg).PReadV => |*msg| {
|
@TagType(fs.Request.Msg).PReadV => |*msg| {
|
||||||
msg.result = os.posix_preadv(msg.fd, msg.iov.ptr, msg.iov.len, msg.offset);
|
msg.result = os.posix_preadv(msg.fd, msg.iov.ptr, msg.iov.len, msg.offset);
|
||||||
},
|
},
|
||||||
@TagType(fs.Request.Msg).OpenRead => |*msg| {
|
@TagType(fs.Request.Msg).OpenRead => |*msg| {
|
||||||
const flags = posix.O_LARGEFILE | posix.O_RDONLY;
|
const flags = posix.O_LARGEFILE | posix.O_RDONLY | posix.O_CLOEXEC;
|
||||||
msg.result = os.posixOpenC(msg.path.ptr, flags, 0);
|
msg.result = os.posixOpenC(msg.path.ptr, flags, 0);
|
||||||
},
|
},
|
||||||
|
@TagType(fs.Request.Msg).OpenRW => |*msg| {
|
||||||
|
const flags = posix.O_LARGEFILE | posix.O_RDWR | posix.O_CREAT | posix.O_CLOEXEC;
|
||||||
|
msg.result = os.posixOpenC(msg.path.ptr, flags, msg.mode);
|
||||||
|
},
|
||||||
@TagType(fs.Request.Msg).Close => |*msg| os.close(msg.fd),
|
@TagType(fs.Request.Msg).Close => |*msg| os.close(msg.fd),
|
||||||
@TagType(fs.Request.Msg).WriteFile => |*msg| blk: {
|
@TagType(fs.Request.Msg).WriteFile => |*msg| blk: {
|
||||||
const flags = posix.O_LARGEFILE | posix.O_WRONLY | posix.O_CREAT |
|
const flags = posix.O_LARGEFILE | posix.O_WRONLY | posix.O_CREAT |
|
||||||
@ -629,7 +643,6 @@ pub const Loop = struct {
|
|||||||
defer os.close(fd);
|
defer os.close(fd);
|
||||||
msg.result = os.posixWrite(fd, msg.contents);
|
msg.result = os.posixWrite(fd, msg.contents);
|
||||||
},
|
},
|
||||||
@TagType(fs.Request.Msg).End => return,
|
|
||||||
}
|
}
|
||||||
switch (node.data.finish) {
|
switch (node.data.finish) {
|
||||||
@TagType(fs.Request.Finish).TickNode => |*tick_node| self.onNextTick(tick_node),
|
@TagType(fs.Request.Finish).TickNode => |*tick_node| self.onNextTick(tick_node),
|
||||||
|
@ -55,7 +55,7 @@ pub const Server = struct {
|
|||||||
errdefer cancel self.accept_coro.?;
|
errdefer cancel self.accept_coro.?;
|
||||||
|
|
||||||
self.listen_resume_node.handle = self.accept_coro.?;
|
self.listen_resume_node.handle = self.accept_coro.?;
|
||||||
try self.loop.addFd(sockfd, &self.listen_resume_node);
|
try self.loop.linuxAddFd(sockfd, &self.listen_resume_node, posix.EPOLLIN | posix.EPOLLOUT | posix.EPOLLET);
|
||||||
errdefer self.loop.removeFd(sockfd);
|
errdefer self.loop.removeFd(sockfd);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -116,7 +116,7 @@ pub async fn connect(loop: *Loop, _address: *const std.net.Address) !std.os.File
|
|||||||
errdefer std.os.close(sockfd);
|
errdefer std.os.close(sockfd);
|
||||||
|
|
||||||
try std.os.posixConnectAsync(sockfd, &address.os_addr);
|
try std.os.posixConnectAsync(sockfd, &address.os_addr);
|
||||||
try await try async loop.waitFd(sockfd);
|
try await try async loop.linuxWaitFd(sockfd, posix.EPOLLIN | posix.EPOLLOUT);
|
||||||
try std.os.posixGetSockOptConnectError(sockfd);
|
try std.os.posixGetSockOptConnectError(sockfd);
|
||||||
|
|
||||||
return std.os.File.openHandle(sockfd);
|
return std.os.File.openHandle(sockfd);
|
||||||
@ -181,4 +181,3 @@ async fn doAsyncTest(loop: *Loop, address: *const std.net.Address, server: *Serv
|
|||||||
assert(mem.eql(u8, msg, "hello from server\n"));
|
assert(mem.eql(u8, msg, "hello from server\n"));
|
||||||
server.close();
|
server.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -29,7 +29,6 @@ pub const File = struct {
|
|||||||
|
|
||||||
/// `path` needs to be copied in memory to add a null terminating byte, hence the allocator.
|
/// `path` needs to be copied in memory to add a null terminating byte, hence the allocator.
|
||||||
/// Call close to clean up.
|
/// Call close to clean up.
|
||||||
/// TODO deprecated, just use open
|
|
||||||
pub fn openRead(allocator: *mem.Allocator, path: []const u8) OpenError!File {
|
pub fn openRead(allocator: *mem.Allocator, path: []const u8) OpenError!File {
|
||||||
if (is_posix) {
|
if (is_posix) {
|
||||||
const flags = posix.O_LARGEFILE | posix.O_RDONLY;
|
const flags = posix.O_LARGEFILE | posix.O_RDONLY;
|
||||||
@ -51,7 +50,6 @@ pub const File = struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Calls `openWriteMode` with os.File.default_mode for the mode.
|
/// Calls `openWriteMode` with os.File.default_mode for the mode.
|
||||||
/// TODO deprecated, just use open
|
|
||||||
pub fn openWrite(allocator: *mem.Allocator, path: []const u8) OpenError!File {
|
pub fn openWrite(allocator: *mem.Allocator, path: []const u8) OpenError!File {
|
||||||
return openWriteMode(allocator, path, os.File.default_mode);
|
return openWriteMode(allocator, path, os.File.default_mode);
|
||||||
}
|
}
|
||||||
@ -60,7 +58,6 @@ pub const File = struct {
|
|||||||
/// If a file already exists in the destination it will be truncated.
|
/// If a file already exists in the destination it will be truncated.
|
||||||
/// `path` needs to be copied in memory to add a null terminating byte, hence the allocator.
|
/// `path` needs to be copied in memory to add a null terminating byte, hence the allocator.
|
||||||
/// Call close to clean up.
|
/// Call close to clean up.
|
||||||
/// TODO deprecated, just use open
|
|
||||||
pub fn openWriteMode(allocator: *mem.Allocator, path: []const u8, file_mode: Mode) OpenError!File {
|
pub fn openWriteMode(allocator: *mem.Allocator, path: []const u8, file_mode: Mode) OpenError!File {
|
||||||
if (is_posix) {
|
if (is_posix) {
|
||||||
const flags = posix.O_LARGEFILE | posix.O_WRONLY | posix.O_CREAT | posix.O_CLOEXEC | posix.O_TRUNC;
|
const flags = posix.O_LARGEFILE | posix.O_WRONLY | posix.O_CREAT | posix.O_CLOEXEC | posix.O_TRUNC;
|
||||||
@ -85,7 +82,6 @@ pub const File = struct {
|
|||||||
/// If a file already exists in the destination this returns OpenError.PathAlreadyExists
|
/// If a file already exists in the destination this returns OpenError.PathAlreadyExists
|
||||||
/// `path` needs to be copied in memory to add a null terminating byte, hence the allocator.
|
/// `path` needs to be copied in memory to add a null terminating byte, hence the allocator.
|
||||||
/// Call close to clean up.
|
/// Call close to clean up.
|
||||||
/// TODO deprecated, just use open
|
|
||||||
pub fn openWriteNoClobber(allocator: *mem.Allocator, path: []const u8, file_mode: Mode) OpenError!File {
|
pub fn openWriteNoClobber(allocator: *mem.Allocator, path: []const u8, file_mode: Mode) OpenError!File {
|
||||||
if (is_posix) {
|
if (is_posix) {
|
||||||
const flags = posix.O_LARGEFILE | posix.O_WRONLY | posix.O_CREAT | posix.O_CLOEXEC | posix.O_EXCL;
|
const flags = posix.O_LARGEFILE | posix.O_WRONLY | posix.O_CREAT | posix.O_CLOEXEC | posix.O_EXCL;
|
||||||
|
@ -310,6 +310,29 @@ pub fn posixWrite(fd: i32, bytes: []const u8) !void {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn posix_pwritev(fd: i32, iov: [*]const posix.iovec_const, count: usize, offset: u64) PosixWriteError!void {
|
||||||
|
while (true) {
|
||||||
|
const rc = posix.pwritev(fd, iov, count, offset);
|
||||||
|
const err = posix.getErrno(rc);
|
||||||
|
switch (err) {
|
||||||
|
0 => return,
|
||||||
|
posix.EINTR => continue,
|
||||||
|
posix.EINVAL => unreachable,
|
||||||
|
posix.EFAULT => unreachable,
|
||||||
|
posix.EAGAIN => return PosixWriteError.WouldBlock,
|
||||||
|
posix.EBADF => return PosixWriteError.FileClosed,
|
||||||
|
posix.EDESTADDRREQ => return PosixWriteError.DestinationAddressRequired,
|
||||||
|
posix.EDQUOT => return PosixWriteError.DiskQuota,
|
||||||
|
posix.EFBIG => return PosixWriteError.FileTooBig,
|
||||||
|
posix.EIO => return PosixWriteError.InputOutput,
|
||||||
|
posix.ENOSPC => return PosixWriteError.NoSpaceLeft,
|
||||||
|
posix.EPERM => return PosixWriteError.AccessDenied,
|
||||||
|
posix.EPIPE => return PosixWriteError.BrokenPipe,
|
||||||
|
else => return unexpectedErrorPosix(err),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub const PosixOpenError = error{
|
pub const PosixOpenError = error{
|
||||||
OutOfMemory,
|
OutOfMemory,
|
||||||
AccessDenied,
|
AccessDenied,
|
||||||
@ -2913,3 +2936,44 @@ pub fn bsdKEvent(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn linuxINotifyInit1(flags: u32) !i32 {
|
||||||
|
const rc = linux.inotify_init1(flags);
|
||||||
|
const err = posix.getErrno(rc);
|
||||||
|
switch (err) {
|
||||||
|
0 => return @intCast(i32, rc),
|
||||||
|
posix.EINVAL => unreachable,
|
||||||
|
posix.EMFILE => return error.ProcessFdQuotaExceeded,
|
||||||
|
posix.ENFILE => return error.SystemFdQuotaExceeded,
|
||||||
|
posix.ENOMEM => return error.SystemResources,
|
||||||
|
else => return unexpectedErrorPosix(err),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn linuxINotifyAddWatchC(inotify_fd: i32, pathname: [*]const u8, mask: u32) !i32 {
|
||||||
|
const rc = linux.inotify_add_watch(inotify_fd, pathname, mask);
|
||||||
|
const err = posix.getErrno(rc);
|
||||||
|
switch (err) {
|
||||||
|
0 => return @intCast(i32, rc),
|
||||||
|
posix.EACCES => return error.AccessDenied,
|
||||||
|
posix.EBADF => unreachable,
|
||||||
|
posix.EFAULT => unreachable,
|
||||||
|
posix.EINVAL => unreachable,
|
||||||
|
posix.ENAMETOOLONG => return error.NameTooLong,
|
||||||
|
posix.ENOENT => return error.FileNotFound,
|
||||||
|
posix.ENOMEM => return error.SystemResources,
|
||||||
|
posix.ENOSPC => return error.UserResourceLimitReached,
|
||||||
|
else => return unexpectedErrorPosix(err),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn linuxINotifyRmWatch(inotify_fd: i32, wd: i32) !void {
|
||||||
|
const rc = linux.inotify_rm_watch(inotify_fd, wd);
|
||||||
|
const err = posix.getErrno(rc);
|
||||||
|
switch (err) {
|
||||||
|
0 => return rc,
|
||||||
|
posix.EBADF => unreachable,
|
||||||
|
posix.EINVAL => unreachable,
|
||||||
|
else => unreachable,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -567,6 +567,37 @@ pub const MNT_DETACH = 2;
|
|||||||
pub const MNT_EXPIRE = 4;
|
pub const MNT_EXPIRE = 4;
|
||||||
pub const UMOUNT_NOFOLLOW = 8;
|
pub const UMOUNT_NOFOLLOW = 8;
|
||||||
|
|
||||||
|
pub const IN_CLOEXEC = O_CLOEXEC;
|
||||||
|
pub const IN_NONBLOCK = O_NONBLOCK;
|
||||||
|
|
||||||
|
pub const IN_ACCESS = 0x00000001;
|
||||||
|
pub const IN_MODIFY = 0x00000002;
|
||||||
|
pub const IN_ATTRIB = 0x00000004;
|
||||||
|
pub const IN_CLOSE_WRITE = 0x00000008;
|
||||||
|
pub const IN_CLOSE_NOWRITE = 0x00000010;
|
||||||
|
pub const IN_CLOSE = IN_CLOSE_WRITE | IN_CLOSE_NOWRITE;
|
||||||
|
pub const IN_OPEN = 0x00000020;
|
||||||
|
pub const IN_MOVED_FROM = 0x00000040;
|
||||||
|
pub const IN_MOVED_TO = 0x00000080;
|
||||||
|
pub const IN_MOVE = IN_MOVED_FROM | IN_MOVED_TO;
|
||||||
|
pub const IN_CREATE = 0x00000100;
|
||||||
|
pub const IN_DELETE = 0x00000200;
|
||||||
|
pub const IN_DELETE_SELF = 0x00000400;
|
||||||
|
pub const IN_MOVE_SELF = 0x00000800;
|
||||||
|
pub const IN_ALL_EVENTS = 0x00000fff;
|
||||||
|
|
||||||
|
pub const IN_UNMOUNT = 0x00002000;
|
||||||
|
pub const IN_Q_OVERFLOW = 0x00004000;
|
||||||
|
pub const IN_IGNORED = 0x00008000;
|
||||||
|
|
||||||
|
pub const IN_ONLYDIR = 0x01000000;
|
||||||
|
pub const IN_DONT_FOLLOW = 0x02000000;
|
||||||
|
pub const IN_EXCL_UNLINK = 0x04000000;
|
||||||
|
pub const IN_MASK_ADD = 0x20000000;
|
||||||
|
|
||||||
|
pub const IN_ISDIR = 0x40000000;
|
||||||
|
pub const IN_ONESHOT = 0x80000000;
|
||||||
|
|
||||||
pub const S_IFMT = 0o170000;
|
pub const S_IFMT = 0o170000;
|
||||||
|
|
||||||
pub const S_IFDIR = 0o040000;
|
pub const S_IFDIR = 0o040000;
|
||||||
@ -704,6 +735,18 @@ pub fn getdents(fd: i32, dirp: [*]u8, count: usize) usize {
|
|||||||
return syscall3(SYS_getdents, @intCast(usize, fd), @ptrToInt(dirp), count);
|
return syscall3(SYS_getdents, @intCast(usize, fd), @ptrToInt(dirp), count);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn inotify_init1(flags: u32) usize {
|
||||||
|
return syscall1(SYS_inotify_init1, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn inotify_add_watch(fd: i32, pathname: [*]const u8, mask: u32) usize {
|
||||||
|
return syscall3(SYS_inotify_add_watch, @intCast(usize, fd), @ptrToInt(pathname), mask);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn inotify_rm_watch(fd: i32, wd: i32) usize {
|
||||||
|
return syscall2(SYS_inotify_rm_watch, @intCast(usize, fd), @intCast(usize, wd));
|
||||||
|
}
|
||||||
|
|
||||||
pub fn isatty(fd: i32) bool {
|
pub fn isatty(fd: i32) bool {
|
||||||
var wsz: winsize = undefined;
|
var wsz: winsize = undefined;
|
||||||
return syscall3(SYS_ioctl, @intCast(usize, fd), TIOCGWINSZ, @ptrToInt(&wsz)) == 0;
|
return syscall3(SYS_ioctl, @intCast(usize, fd), TIOCGWINSZ, @ptrToInt(&wsz)) == 0;
|
||||||
@ -750,6 +793,10 @@ pub fn preadv(fd: i32, iov: [*]const iovec, count: usize, offset: u64) usize {
|
|||||||
return syscall4(SYS_preadv, @intCast(usize, fd), @ptrToInt(iov), count, offset);
|
return syscall4(SYS_preadv, @intCast(usize, fd), @ptrToInt(iov), count, offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn pwritev(fd: i32, iov: [*]const iovec_const, count: usize, offset: u64) usize {
|
||||||
|
return syscall4(SYS_pwritev, @intCast(usize, fd), @ptrToInt(iov), count, offset);
|
||||||
|
}
|
||||||
|
|
||||||
// TODO https://github.com/ziglang/zig/issues/265
|
// TODO https://github.com/ziglang/zig/issues/265
|
||||||
pub fn rmdir(path: [*]const u8) usize {
|
pub fn rmdir(path: [*]const u8) usize {
|
||||||
return syscall1(SYS_rmdir, @ptrToInt(path));
|
return syscall1(SYS_rmdir, @ptrToInt(path));
|
||||||
@ -1068,6 +1115,11 @@ pub const iovec = extern struct {
|
|||||||
iov_len: usize,
|
iov_len: usize,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub const iovec_const = extern struct {
|
||||||
|
iov_base: [*]const u8,
|
||||||
|
iov_len: usize,
|
||||||
|
};
|
||||||
|
|
||||||
pub fn getsockname(fd: i32, noalias addr: *sockaddr, noalias len: *socklen_t) usize {
|
pub fn getsockname(fd: i32, noalias addr: *sockaddr, noalias len: *socklen_t) usize {
|
||||||
return syscall3(SYS_getsockname, @intCast(usize, fd), @ptrToInt(addr), @ptrToInt(len));
|
return syscall3(SYS_getsockname, @intCast(usize, fd), @ptrToInt(addr), @ptrToInt(len));
|
||||||
}
|
}
|
||||||
@ -1376,6 +1428,14 @@ pub fn capset(hdrp: *cap_user_header_t, datap: *const cap_user_data_t) usize {
|
|||||||
return syscall2(SYS_capset, @ptrToInt(hdrp), @ptrToInt(datap));
|
return syscall2(SYS_capset, @ptrToInt(hdrp), @ptrToInt(datap));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub const inotify_event = extern struct {
|
||||||
|
wd: i32,
|
||||||
|
mask: u32,
|
||||||
|
cookie: u32,
|
||||||
|
len: u32,
|
||||||
|
//name: [?]u8,
|
||||||
|
};
|
||||||
|
|
||||||
test "import" {
|
test "import" {
|
||||||
if (builtin.os == builtin.Os.linux) {
|
if (builtin.os == builtin.Os.linux) {
|
||||||
_ = @import("test.zig");
|
_ = @import("test.zig");
|
||||||
|
Loading…
x
Reference in New Issue
Block a user