authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-07-25 23:16:13-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-07-30 13:44:36-04:00
logcc4552733351390aecab9ae900beb822237d6041
tree575c7ad933f2ea04c4125704a55dc823839af57e
parent5d4a02c350a18a70cf1f92f6638b5d26689c16b4

introduce std.event.fs for async file system functions

only works on linux so far

10 files changed, 559 insertions(+), 97 deletions(-)

CMakeLists.txt+1
...@@ -460,6 +460,7 @@ set(ZIG_STD_FILES...@@ -460,6 +460,7 @@ set(ZIG_STD_FILES
460 "empty.zig"460 "empty.zig"
461 "event.zig"461 "event.zig"
462 "event/channel.zig"462 "event/channel.zig"
463 "event/fs.zig"
463 "event/future.zig"464 "event/future.zig"
464 "event/group.zig"465 "event/group.zig"
465 "event/lock.zig"466 "event/lock.zig"
std/build.zig+2-2
...@@ -603,10 +603,10 @@ pub const Builder = struct {...@@ -603,10 +603,10 @@ pub const Builder = struct {
603 }603 }
604604
605 fn copyFile(self: *Builder, source_path: []const u8, dest_path: []const u8) !void {605 fn copyFile(self: *Builder, source_path: []const u8, dest_path: []const u8) !void {
606 return self.copyFileMode(source_path, dest_path, os.default_file_mode);606 return self.copyFileMode(source_path, dest_path, os.File.default_mode);
607 }607 }
608608
609 fn copyFileMode(self: *Builder, source_path: []const u8, dest_path: []const u8, mode: os.FileMode) !void {609 fn copyFileMode(self: *Builder, source_path: []const u8, dest_path: []const u8, mode: os.File.Mode) !void {
610 if (self.verbose) {610 if (self.verbose) {
611 warn("cp {} {}\n", source_path, dest_path);611 warn("cp {} {}\n", source_path, dest_path);
612 }612 }
std/debug/index.zig+1-5
...@@ -672,14 +672,10 @@ fn parseFormValueRef(allocator: *mem.Allocator, in_stream: var, comptime T: type...@@ -672,14 +672,10 @@ fn parseFormValueRef(allocator: *mem.Allocator, in_stream: var, comptime T: type
672672
673const ParseFormValueError = error{673const ParseFormValueError = error{
674 EndOfStream,674 EndOfStream,
675 Io,
676 BadFd,
677 Unexpected,
678 InvalidDebugInfo,675 InvalidDebugInfo,
679 EndOfFile,676 EndOfFile,
680 IsDir,
681 OutOfMemory,677 OutOfMemory,
682};678} || std.os.File.ReadError;
683679
684fn parseFormValue(allocator: *mem.Allocator, in_stream: var, form_id: u64, is_64: bool) ParseFormValueError!FormValue {680fn parseFormValue(allocator: *mem.Allocator, in_stream: var, form_id: u64, is_64: bool) ParseFormValueError!FormValue {
685 return switch (form_id) {681 return switch (form_id) {
std/event.zig+10-8
...@@ -1,17 +1,19 @@...@@ -1,17 +1,19 @@
1pub const Channel = @import("event/channel.zig").Channel;
2pub const Future = @import("event/future.zig").Future;
3pub const Group = @import("event/group.zig").Group;
4pub const Lock = @import("event/lock.zig").Lock;
1pub const Locked = @import("event/locked.zig").Locked;5pub const Locked = @import("event/locked.zig").Locked;
2pub const Loop = @import("event/loop.zig").Loop;6pub const Loop = @import("event/loop.zig").Loop;
3pub const Lock = @import("event/lock.zig").Lock;7pub const fs = @import("event/fs.zig");
4pub const tcp = @import("event/tcp.zig");8pub const tcp = @import("event/tcp.zig");
5pub const Channel = @import("event/channel.zig").Channel;
6pub const Group = @import("event/group.zig").Group;
7pub const Future = @import("event/future.zig").Future;
89
9test "import event tests" {10test "import event tests" {
11 _ = @import("event/channel.zig");
12 _ = @import("event/fs.zig");
13 _ = @import("event/future.zig");
14 _ = @import("event/group.zig");
15 _ = @import("event/lock.zig");
10 _ = @import("event/locked.zig");16 _ = @import("event/locked.zig");
11 _ = @import("event/loop.zig");17 _ = @import("event/loop.zig");
12 _ = @import("event/lock.zig");
13 _ = @import("event/tcp.zig");18 _ = @import("event/tcp.zig");
14 _ = @import("event/channel.zig");
15 _ = @import("event/group.zig");
16 _ = @import("event/future.zig");
17}19}
std/event/fs.zig created+343
...@@ -0,0 +1,343 @@
1const std = @import("../index.zig");
2const event = std.event;
3const assert = std.debug.assert;
4const os = std.os;
5const mem = std.mem;
6
7pub const RequestNode = std.atomic.Queue(Request).Node;
8
9pub const Request = struct {
10 msg: Msg,
11 finish: Finish,
12
13 pub const Finish = union(enum) {
14 TickNode: event.Loop.NextTickNode,
15 DeallocCloseOperation: *CloseOperation,
16 NoAction,
17 };
18
19 pub const Msg = union(enum) {
20 PWriteV: PWriteV,
21 PReadV: PReadV,
22 OpenRead: OpenRead,
23 Close: Close,
24 WriteFile: WriteFile,
25 End, // special - means the fs thread should exit
26
27 pub const PWriteV = struct {
28 fd: os.FileHandle,
29 data: []const []const u8,
30 offset: usize,
31 result: Error!void,
32
33 pub const Error = error{};
34 };
35
36 pub const PReadV = struct {
37 fd: os.FileHandle,
38 iov: []os.linux.iovec,
39 offset: usize,
40 result: Error!usize,
41
42 pub const Error = os.File.ReadError;
43 };
44
45 pub const OpenRead = struct {
46 /// must be null terminated. TODO https://github.com/ziglang/zig/issues/265
47 path: []const u8,
48 result: Error!os.FileHandle,
49
50 pub const Error = os.File.OpenError;
51 };
52
53 pub const WriteFile = struct {
54 /// must be null terminated. TODO https://github.com/ziglang/zig/issues/265
55 path: []const u8,
56 contents: []const u8,
57 mode: os.File.Mode,
58 result: Error!void,
59
60 pub const Error = os.File.OpenError || os.File.WriteError;
61 };
62
63 pub const Close = struct {
64 fd: os.FileHandle,
65 };
66 };
67};
68
69/// data - both the outer and inner references - must live until pwritev promise completes.
70pub async fn pwritev(loop: *event.Loop, fd: os.FileHandle, offset: usize, data: []const []const u8) !void {
71 //const data_dupe = try mem.dupe(loop.allocator, []const u8, data);
72 //defer loop.allocator.free(data_dupe);
73
74 // workaround for https://github.com/ziglang/zig/issues/1194
75 var my_handle: promise = undefined;
76 suspend |p| {
77 my_handle = p;
78 resume p;
79 }
80
81 var req_node = RequestNode{
82 .next = undefined,
83 .data = Request{
84 .msg = Request.Msg{
85 .PWriteV = Request.Msg.PWriteV{
86 .fd = fd,
87 .data = data,
88 .offset = offset,
89 .result = undefined,
90 },
91 },
92 .finish = Request.Finish{
93 .TickNode = event.Loop.NextTickNode{
94 .next = undefined,
95 .data = my_handle,
96 },
97 },
98 },
99 };
100
101 suspend |_| {
102 loop.linuxFsRequest(&req_node);
103 }
104
105 return req_node.data.msg.PWriteV.result;
106}
107
108/// data - just the inner references - must live until pwritev promise completes.
109pub async fn preadv(loop: *event.Loop, fd: os.FileHandle, offset: usize, data: []const []u8) !usize {
110 //const data_dupe = try mem.dupe(loop.allocator, []const u8, data);
111 //defer loop.allocator.free(data_dupe);
112
113 // workaround for https://github.com/ziglang/zig/issues/1194
114 var my_handle: promise = undefined;
115 suspend |p| {
116 my_handle = p;
117 resume p;
118 }
119
120 const iovecs = try loop.allocator.alloc(os.linux.iovec, data.len);
121 defer loop.allocator.free(iovecs);
122
123 for (data) |buf, i| {
124 iovecs[i] = os.linux.iovec{
125 .iov_base = buf.ptr,
126 .iov_len = buf.len,
127 };
128 }
129
130 var req_node = RequestNode{
131 .next = undefined,
132 .data = Request{
133 .msg = Request.Msg{
134 .PReadV = Request.Msg.PReadV{
135 .fd = fd,
136 .iov = iovecs,
137 .offset = offset,
138 .result = undefined,
139 },
140 },
141 .finish = Request.Finish{
142 .TickNode = event.Loop.NextTickNode{
143 .next = undefined,
144 .data = my_handle,
145 },
146 },
147 },
148 };
149
150 suspend |_| {
151 loop.linuxFsRequest(&req_node);
152 }
153
154 return req_node.data.msg.PReadV.result;
155}
156
157pub async fn openRead(loop: *event.Loop, path: []const u8) os.File.OpenError!os.FileHandle {
158 // workaround for https://github.com/ziglang/zig/issues/1194
159 var my_handle: promise = undefined;
160 suspend |p| {
161 my_handle = p;
162 resume p;
163 }
164
165 var req_node = RequestNode{
166 .next = undefined,
167 .data = Request{
168 .msg = Request.Msg{
169 .OpenRead = Request.Msg.OpenRead{
170 .path = path,
171 .result = undefined,
172 },
173 },
174 .finish = Request.Finish{
175 .TickNode = event.Loop.NextTickNode{
176 .next = undefined,
177 .data = my_handle,
178 },
179 },
180 },
181 };
182
183 suspend |_| {
184 loop.linuxFsRequest(&req_node);
185 }
186
187 return req_node.data.msg.OpenRead.result;
188}
189
190/// This abstraction helps to close file handles in defer expressions
191/// without suspending. Start a CloseOperation before opening a file.
192pub const CloseOperation = struct {
193 loop: *event.Loop,
194 have_fd: bool,
195 close_req_node: RequestNode,
196
197 pub fn create(loop: *event.Loop) (error{OutOfMemory}!*CloseOperation) {
198 const self = try loop.allocator.createOne(CloseOperation);
199 self.* = CloseOperation{
200 .loop = loop,
201 .have_fd = false,
202 .close_req_node = RequestNode{
203 .next = undefined,
204 .data = Request{
205 .msg = Request.Msg{
206 .Close = Request.Msg.Close{ .fd = undefined },
207 },
208 .finish = Request.Finish{ .DeallocCloseOperation = self },
209 },
210 },
211 };
212 return self;
213 }
214
215 /// Defer this after creating.
216 pub fn deinit(self: *CloseOperation) void {
217 if (self.have_fd) {
218 self.loop.linuxFsRequest(&self.close_req_node);
219 } else {
220 self.loop.allocator.destroy(self);
221 }
222 }
223
224 pub fn setHandle(self: *CloseOperation, handle: os.FileHandle) void {
225 self.close_req_node.data.msg.Close.fd = handle;
226 self.have_fd = true;
227 }
228};
229
230/// contents must remain alive until writeFile completes.
231pub async fn writeFile(loop: *event.Loop, path: []const u8, contents: []const u8) !void {
232 return await (async writeFileMode(loop, path, contents, os.File.default_mode) catch unreachable);
233}
234
235/// contents must remain alive until writeFile completes.
236pub async fn writeFileMode(loop: *event.Loop, path: []const u8, contents: []const u8, mode: os.File.Mode) !void {
237 // workaround for https://github.com/ziglang/zig/issues/1194
238 var my_handle: promise = undefined;
239 suspend |p| {
240 my_handle = p;
241 resume p;
242 }
243
244 const path_with_null = try std.cstr.addNullByte(loop.allocator, path);
245 defer loop.allocator.free(path_with_null);
246
247 var req_node = RequestNode{
248 .next = undefined,
249 .data = Request{
250 .msg = Request.Msg{
251 .WriteFile = Request.Msg.WriteFile{
252 .path = path_with_null[0..path.len],
253 .contents = contents,
254 .mode = mode,
255 .result = undefined,
256 },
257 },
258 .finish = Request.Finish{
259 .TickNode = event.Loop.NextTickNode{
260 .next = undefined,
261 .data = my_handle,
262 },
263 },
264 },
265 };
266
267 suspend |_| {
268 loop.linuxFsRequest(&req_node);
269 }
270
271 return req_node.data.msg.WriteFile.result;
272}
273
274/// The promise resumes when the last data has been confirmed written, but before the file handle
275/// is closed.
276pub async fn readFile(loop: *event.Loop, file_path: []const u8, max_size: usize) ![]u8 {
277 var close_op = try CloseOperation.create(loop);
278 defer close_op.deinit();
279
280 const path_with_null = try std.cstr.addNullByte(loop.allocator, file_path);
281 defer loop.allocator.free(path_with_null);
282
283 const fd = try await (async openRead(loop, path_with_null[0..file_path.len]) catch unreachable);
284 close_op.setHandle(fd);
285
286 var list = std.ArrayList(u8).init(loop.allocator);
287 defer list.deinit();
288
289 while (true) {
290 try list.ensureCapacity(list.len + os.page_size);
291 const buf = list.items[list.len..];
292 const buf_array = [][]u8{buf};
293 const amt = try await (async preadv(loop, fd, list.len, buf_array) catch unreachable);
294 list.len += amt;
295 if (amt < buf.len) {
296 return list.toOwnedSlice();
297 }
298 }
299}
300
301const test_tmp_dir = "std_event_fs_test";
302
303test "write a file, watch it, write it again" {
304 var da = std.heap.DirectAllocator.init();
305 defer da.deinit();
306
307 const allocator = &da.allocator;
308
309 // TODO move this into event loop too
310 try os.makePath(allocator, test_tmp_dir);
311 defer os.deleteTree(allocator, test_tmp_dir) catch {};
312
313 var loop: event.Loop = undefined;
314 try loop.initMultiThreaded(allocator);
315 defer loop.deinit();
316
317 var result: error!void = undefined;
318 const handle = try async<allocator> testFsWatchCantFail(&loop, &result);
319 defer cancel handle;
320
321 loop.run();
322 return result;
323}
324
325async fn testFsWatchCantFail(loop: *event.Loop, result: *(error!void)) void {
326 result.* = await async testFsWatch(loop) catch unreachable;
327}
328
329async fn testFsWatch(loop: *event.Loop) !void {
330 const file_path = try os.path.join(loop.allocator, test_tmp_dir, "file.txt");
331 defer loop.allocator.free(file_path);
332
333 const contents =
334 \\line 1
335 \\line 2
336 ;
337
338 // first just write then read the file
339 try await try async writeFile(loop, file_path, contents);
340
341 const read_contents = try await try async readFile(loop, file_path, 1024 * 1024);
342 assert(mem.eql(u8, read_contents, contents));
343}
std/event/loop.zig+136-51
...@@ -2,10 +2,12 @@ const std = @import("../index.zig");...@@ -2,10 +2,12 @@ const std = @import("../index.zig");
2const builtin = @import("builtin");2const builtin = @import("builtin");
3const assert = std.debug.assert;3const assert = std.debug.assert;
4const mem = std.mem;4const mem = std.mem;
5const posix = std.os.posix;
6const windows = std.os.windows;
7const AtomicRmwOp = builtin.AtomicRmwOp;5const AtomicRmwOp = builtin.AtomicRmwOp;
8const AtomicOrder = builtin.AtomicOrder;6const AtomicOrder = builtin.AtomicOrder;
7const fs = std.event.fs;
8const os = std.os;
9const posix = os.posix;
10const windows = os.windows;
911
10pub const Loop = struct {12pub const Loop = struct {
11 allocator: *mem.Allocator,13 allocator: *mem.Allocator,
...@@ -13,7 +15,7 @@ pub const Loop = struct {...@@ -13,7 +15,7 @@ pub const Loop = struct {
13 os_data: OsData,15 os_data: OsData,
14 final_resume_node: ResumeNode,16 final_resume_node: ResumeNode,
15 pending_event_count: usize,17 pending_event_count: usize,
16 extra_threads: []*std.os.Thread,18 extra_threads: []*os.Thread,
1719
18 // pre-allocated eventfds. all permanently active.20 // pre-allocated eventfds. all permanently active.
19 // this is how we send promises to be resumed on other threads.21 // this is how we send promises to be resumed on other threads.
...@@ -65,7 +67,7 @@ pub const Loop = struct {...@@ -65,7 +67,7 @@ pub const Loop = struct {
65 /// TODO copy elision / named return values so that the threads referencing *Loop67 /// TODO copy elision / named return values so that the threads referencing *Loop
66 /// have the correct pointer value.68 /// have the correct pointer value.
67 pub fn initMultiThreaded(self: *Loop, allocator: *mem.Allocator) !void {69 pub fn initMultiThreaded(self: *Loop, allocator: *mem.Allocator) !void {
68 const core_count = try std.os.cpuCount(allocator);70 const core_count = try os.cpuCount(allocator);
69 return self.initInternal(allocator, core_count);71 return self.initInternal(allocator, core_count);
70 }72 }
7173
...@@ -92,7 +94,7 @@ pub const Loop = struct {...@@ -92,7 +94,7 @@ pub const Loop = struct {
92 );94 );
93 errdefer self.allocator.free(self.eventfd_resume_nodes);95 errdefer self.allocator.free(self.eventfd_resume_nodes);
9496
95 self.extra_threads = try self.allocator.alloc(*std.os.Thread, extra_thread_count);97 self.extra_threads = try self.allocator.alloc(*os.Thread, extra_thread_count);
96 errdefer self.allocator.free(self.extra_threads);98 errdefer self.allocator.free(self.extra_threads);
9799
98 try self.initOsData(extra_thread_count);100 try self.initOsData(extra_thread_count);
...@@ -104,17 +106,34 @@ pub const Loop = struct {...@@ -104,17 +106,34 @@ pub const Loop = struct {
104 self.allocator.free(self.extra_threads);106 self.allocator.free(self.extra_threads);
105 }107 }
106108
107 const InitOsDataError = std.os.LinuxEpollCreateError || mem.Allocator.Error || std.os.LinuxEventFdError ||109 const InitOsDataError = os.LinuxEpollCreateError || mem.Allocator.Error || os.LinuxEventFdError ||
108 std.os.SpawnThreadError || std.os.LinuxEpollCtlError || std.os.BsdKEventError ||110 os.SpawnThreadError || os.LinuxEpollCtlError || os.BsdKEventError ||
109 std.os.WindowsCreateIoCompletionPortError;111 os.WindowsCreateIoCompletionPortError;
110112
111 const wakeup_bytes = []u8{0x1} ** 8;113 const wakeup_bytes = []u8{0x1} ** 8;
112114
113 fn initOsData(self: *Loop, extra_thread_count: usize) InitOsDataError!void {115 fn initOsData(self: *Loop, extra_thread_count: usize) InitOsDataError!void {
114 switch (builtin.os) {116 switch (builtin.os) {
115 builtin.Os.linux => {117 builtin.Os.linux => {
118 self.os_data.fs_queue = std.atomic.Queue(fs.Request).init();
119 self.os_data.fs_queue_len = 0;
120 // we need another thread for the file system because Linux does not have an async
121 // file system I/O API.
122 self.os_data.fs_end_request = fs.RequestNode{
123 .next = undefined,
124 .data = fs.Request{
125 .msg = fs.Request.Msg.End,
126 .finish = fs.Request.Finish.NoAction,
127 },
128 };
129 self.os_data.fs_thread = try os.spawnThread(self, linuxFsRun);
116 errdefer {130 errdefer {
117 while (self.available_eventfd_resume_nodes.pop()) |node| std.os.close(node.data.eventfd);131 self.linuxFsRequest(&self.os_data.fs_end_request);
132 self.os_data.fs_thread.wait();
133 }
134
135 errdefer {
136 while (self.available_eventfd_resume_nodes.pop()) |node| os.close(node.data.eventfd);
118 }137 }
119 for (self.eventfd_resume_nodes) |*eventfd_node| {138 for (self.eventfd_resume_nodes) |*eventfd_node| {
120 eventfd_node.* = std.atomic.Stack(ResumeNode.EventFd).Node{139 eventfd_node.* = std.atomic.Stack(ResumeNode.EventFd).Node{
...@@ -123,7 +142,7 @@ pub const Loop = struct {...@@ -123,7 +142,7 @@ pub const Loop = struct {
123 .id = ResumeNode.Id.EventFd,142 .id = ResumeNode.Id.EventFd,
124 .handle = undefined,143 .handle = undefined,
125 },144 },
126 .eventfd = try std.os.linuxEventFd(1, posix.EFD_CLOEXEC | posix.EFD_NONBLOCK),145 .eventfd = try os.linuxEventFd(1, posix.EFD_CLOEXEC | posix.EFD_NONBLOCK),
127 .epoll_op = posix.EPOLL_CTL_ADD,146 .epoll_op = posix.EPOLL_CTL_ADD,
128 },147 },
129 .next = undefined,148 .next = undefined,
...@@ -131,17 +150,17 @@ pub const Loop = struct {...@@ -131,17 +150,17 @@ pub const Loop = struct {
131 self.available_eventfd_resume_nodes.push(eventfd_node);150 self.available_eventfd_resume_nodes.push(eventfd_node);
132 }151 }
133152
134 self.os_data.epollfd = try std.os.linuxEpollCreate(posix.EPOLL_CLOEXEC);153 self.os_data.epollfd = try os.linuxEpollCreate(posix.EPOLL_CLOEXEC);
135 errdefer std.os.close(self.os_data.epollfd);154 errdefer os.close(self.os_data.epollfd);
136155
137 self.os_data.final_eventfd = try std.os.linuxEventFd(0, posix.EFD_CLOEXEC | posix.EFD_NONBLOCK);156 self.os_data.final_eventfd = try os.linuxEventFd(0, posix.EFD_CLOEXEC | posix.EFD_NONBLOCK);
138 errdefer std.os.close(self.os_data.final_eventfd);157 errdefer os.close(self.os_data.final_eventfd);
139158
140 self.os_data.final_eventfd_event = posix.epoll_event{159 self.os_data.final_eventfd_event = posix.epoll_event{
141 .events = posix.EPOLLIN,160 .events = posix.EPOLLIN,
142 .data = posix.epoll_data{ .ptr = @ptrToInt(&self.final_resume_node) },161 .data = posix.epoll_data{ .ptr = @ptrToInt(&self.final_resume_node) },
143 };162 };
144 try std.os.linuxEpollCtl(163 try os.linuxEpollCtl(
145 self.os_data.epollfd,164 self.os_data.epollfd,
146 posix.EPOLL_CTL_ADD,165 posix.EPOLL_CTL_ADD,
147 self.os_data.final_eventfd,166 self.os_data.final_eventfd,
...@@ -151,19 +170,19 @@ pub const Loop = struct {...@@ -151,19 +170,19 @@ pub const Loop = struct {
151 var extra_thread_index: usize = 0;170 var extra_thread_index: usize = 0;
152 errdefer {171 errdefer {
153 // writing 8 bytes to an eventfd cannot fail172 // writing 8 bytes to an eventfd cannot fail
154 std.os.posixWrite(self.os_data.final_eventfd, wakeup_bytes) catch unreachable;173 os.posixWrite(self.os_data.final_eventfd, wakeup_bytes) catch unreachable;
155 while (extra_thread_index != 0) {174 while (extra_thread_index != 0) {
156 extra_thread_index -= 1;175 extra_thread_index -= 1;
157 self.extra_threads[extra_thread_index].wait();176 self.extra_threads[extra_thread_index].wait();
158 }177 }
159 }178 }
160 while (extra_thread_index < extra_thread_count) : (extra_thread_index += 1) {179 while (extra_thread_index < extra_thread_count) : (extra_thread_index += 1) {
161 self.extra_threads[extra_thread_index] = try std.os.spawnThread(self, workerRun);180 self.extra_threads[extra_thread_index] = try os.spawnThread(self, workerRun);
162 }181 }
163 },182 },
164 builtin.Os.macosx => {183 builtin.Os.macosx => {
165 self.os_data.kqfd = try std.os.bsdKQueue();184 self.os_data.kqfd = try os.bsdKQueue();
166 errdefer std.os.close(self.os_data.kqfd);185 errdefer os.close(self.os_data.kqfd);
167186
168 self.os_data.kevents = try self.allocator.alloc(posix.Kevent, extra_thread_count);187 self.os_data.kevents = try self.allocator.alloc(posix.Kevent, extra_thread_count);
169 errdefer self.allocator.free(self.os_data.kevents);188 errdefer self.allocator.free(self.os_data.kevents);
...@@ -191,7 +210,7 @@ pub const Loop = struct {...@@ -191,7 +210,7 @@ pub const Loop = struct {
191 };210 };
192 self.available_eventfd_resume_nodes.push(eventfd_node);211 self.available_eventfd_resume_nodes.push(eventfd_node);
193 const kevent_array = (*[1]posix.Kevent)(&eventfd_node.data.kevent);212 const kevent_array = (*[1]posix.Kevent)(&eventfd_node.data.kevent);
194 _ = try std.os.bsdKEvent(self.os_data.kqfd, kevent_array, eventlist, null);213 _ = try os.bsdKEvent(self.os_data.kqfd, kevent_array, eventlist, null);
195 eventfd_node.data.kevent.flags = posix.EV_CLEAR | posix.EV_ENABLE;214 eventfd_node.data.kevent.flags = posix.EV_CLEAR | posix.EV_ENABLE;
196 eventfd_node.data.kevent.fflags = posix.NOTE_TRIGGER;215 eventfd_node.data.kevent.fflags = posix.NOTE_TRIGGER;
197 // this one is for waiting for events216 // this one is for waiting for events
...@@ -216,30 +235,30 @@ pub const Loop = struct {...@@ -216,30 +235,30 @@ pub const Loop = struct {
216 .udata = @ptrToInt(&self.final_resume_node),235 .udata = @ptrToInt(&self.final_resume_node),
217 };236 };
218 const kevent_array = (*[1]posix.Kevent)(&self.os_data.final_kevent);237 const kevent_array = (*[1]posix.Kevent)(&self.os_data.final_kevent);
219 _ = try std.os.bsdKEvent(self.os_data.kqfd, kevent_array, eventlist, null);238 _ = try os.bsdKEvent(self.os_data.kqfd, kevent_array, eventlist, null);
220 self.os_data.final_kevent.flags = posix.EV_ENABLE;239 self.os_data.final_kevent.flags = posix.EV_ENABLE;
221 self.os_data.final_kevent.fflags = posix.NOTE_TRIGGER;240 self.os_data.final_kevent.fflags = posix.NOTE_TRIGGER;
222241
223 var extra_thread_index: usize = 0;242 var extra_thread_index: usize = 0;
224 errdefer {243 errdefer {
225 _ = std.os.bsdKEvent(self.os_data.kqfd, kevent_array, eventlist, null) catch unreachable;244 _ = os.bsdKEvent(self.os_data.kqfd, kevent_array, eventlist, null) catch unreachable;
226 while (extra_thread_index != 0) {245 while (extra_thread_index != 0) {
227 extra_thread_index -= 1;246 extra_thread_index -= 1;
228 self.extra_threads[extra_thread_index].wait();247 self.extra_threads[extra_thread_index].wait();
229 }248 }
230 }249 }
231 while (extra_thread_index < extra_thread_count) : (extra_thread_index += 1) {250 while (extra_thread_index < extra_thread_count) : (extra_thread_index += 1) {
232 self.extra_threads[extra_thread_index] = try std.os.spawnThread(self, workerRun);251 self.extra_threads[extra_thread_index] = try os.spawnThread(self, workerRun);
233 }252 }
234 },253 },
235 builtin.Os.windows => {254 builtin.Os.windows => {
236 self.os_data.io_port = try std.os.windowsCreateIoCompletionPort(255 self.os_data.io_port = try os.windowsCreateIoCompletionPort(
237 windows.INVALID_HANDLE_VALUE,256 windows.INVALID_HANDLE_VALUE,
238 null,257 null,
239 undefined,258 undefined,
240 undefined,259 undefined,
241 );260 );
242 errdefer std.os.close(self.os_data.io_port);261 errdefer os.close(self.os_data.io_port);
243262
244 for (self.eventfd_resume_nodes) |*eventfd_node, i| {263 for (self.eventfd_resume_nodes) |*eventfd_node, i| {
245 eventfd_node.* = std.atomic.Stack(ResumeNode.EventFd).Node{264 eventfd_node.* = std.atomic.Stack(ResumeNode.EventFd).Node{
...@@ -262,7 +281,7 @@ pub const Loop = struct {...@@ -262,7 +281,7 @@ pub const Loop = struct {
262 while (i < extra_thread_index) : (i += 1) {281 while (i < extra_thread_index) : (i += 1) {
263 while (true) {282 while (true) {
264 const overlapped = @intToPtr(?*windows.OVERLAPPED, 0x1);283 const overlapped = @intToPtr(?*windows.OVERLAPPED, 0x1);
265 std.os.windowsPostQueuedCompletionStatus(self.os_data.io_port, undefined, @ptrToInt(&self.final_resume_node), overlapped) catch continue;284 os.windowsPostQueuedCompletionStatus(self.os_data.io_port, undefined, @ptrToInt(&self.final_resume_node), overlapped) catch continue;
266 break;285 break;
267 }286 }
268 }287 }
...@@ -272,7 +291,7 @@ pub const Loop = struct {...@@ -272,7 +291,7 @@ pub const Loop = struct {
272 }291 }
273 }292 }
274 while (extra_thread_index < extra_thread_count) : (extra_thread_index += 1) {293 while (extra_thread_index < extra_thread_count) : (extra_thread_index += 1) {
275 self.extra_threads[extra_thread_index] = try std.os.spawnThread(self, workerRun);294 self.extra_threads[extra_thread_index] = try os.spawnThread(self, workerRun);
276 }295 }
277 },296 },
278 else => {},297 else => {},
...@@ -282,17 +301,17 @@ pub const Loop = struct {...@@ -282,17 +301,17 @@ pub const Loop = struct {
282 fn deinitOsData(self: *Loop) void {301 fn deinitOsData(self: *Loop) void {
283 switch (builtin.os) {302 switch (builtin.os) {
284 builtin.Os.linux => {303 builtin.Os.linux => {
285 std.os.close(self.os_data.final_eventfd);304 os.close(self.os_data.final_eventfd);
286 while (self.available_eventfd_resume_nodes.pop()) |node| std.os.close(node.data.eventfd);305 while (self.available_eventfd_resume_nodes.pop()) |node| os.close(node.data.eventfd);
287 std.os.close(self.os_data.epollfd);306 os.close(self.os_data.epollfd);
288 self.allocator.free(self.eventfd_resume_nodes);307 self.allocator.free(self.eventfd_resume_nodes);
289 },308 },
290 builtin.Os.macosx => {309 builtin.Os.macosx => {
291 self.allocator.free(self.os_data.kevents);310 self.allocator.free(self.os_data.kevents);
292 std.os.close(self.os_data.kqfd);311 os.close(self.os_data.kqfd);
293 },312 },
294 builtin.Os.windows => {313 builtin.Os.windows => {
295 std.os.close(self.os_data.io_port);314 os.close(self.os_data.io_port);
296 },315 },
297 else => {},316 else => {},
298 }317 }
...@@ -307,17 +326,17 @@ pub const Loop = struct {...@@ -307,17 +326,17 @@ pub const Loop = struct {
307 try self.modFd(326 try self.modFd(
308 fd,327 fd,
309 posix.EPOLL_CTL_ADD,328 posix.EPOLL_CTL_ADD,
310 std.os.linux.EPOLLIN | std.os.linux.EPOLLOUT | std.os.linux.EPOLLET,329 os.linux.EPOLLIN | os.linux.EPOLLOUT | os.linux.EPOLLET,
311 resume_node,330 resume_node,
312 );331 );
313 }332 }
314333
315 pub fn modFd(self: *Loop, fd: i32, op: u32, events: u32, resume_node: *ResumeNode) !void {334 pub fn modFd(self: *Loop, fd: i32, op: u32, events: u32, resume_node: *ResumeNode) !void {
316 var ev = std.os.linux.epoll_event{335 var ev = os.linux.epoll_event{
317 .events = events,336 .events = events,
318 .data = std.os.linux.epoll_data{ .ptr = @ptrToInt(resume_node) },337 .data = os.linux.epoll_data{ .ptr = @ptrToInt(resume_node) },
319 };338 };
320 try std.os.linuxEpollCtl(self.os_data.epollfd, op, fd, &ev);339 try os.linuxEpollCtl(self.os_data.epollfd, op, fd, &ev);
321 }340 }
322341
323 pub fn removeFd(self: *Loop, fd: i32) void {342 pub fn removeFd(self: *Loop, fd: i32) void {
...@@ -326,7 +345,7 @@ pub const Loop = struct {...@@ -326,7 +345,7 @@ pub const Loop = struct {
326 }345 }
327346
328 fn removeFdNoCounter(self: *Loop, fd: i32) void {347 fn removeFdNoCounter(self: *Loop, fd: i32) void {
329 std.os.linuxEpollCtl(self.os_data.epollfd, std.os.linux.EPOLL_CTL_DEL, fd, undefined) catch {};348 os.linuxEpollCtl(self.os_data.epollfd, os.linux.EPOLL_CTL_DEL, fd, undefined) catch {};
330 }349 }
331350
332 pub async fn waitFd(self: *Loop, fd: i32) !void {351 pub async fn waitFd(self: *Loop, fd: i32) !void {
...@@ -353,7 +372,7 @@ pub const Loop = struct {...@@ -353,7 +372,7 @@ pub const Loop = struct {
353 builtin.Os.macosx => {372 builtin.Os.macosx => {
354 const kevent_array = (*[1]posix.Kevent)(&eventfd_node.kevent);373 const kevent_array = (*[1]posix.Kevent)(&eventfd_node.kevent);
355 const eventlist = ([*]posix.Kevent)(undefined)[0..0];374 const eventlist = ([*]posix.Kevent)(undefined)[0..0];
356 _ = std.os.bsdKEvent(self.os_data.kqfd, kevent_array, eventlist, null) catch {375 _ = os.bsdKEvent(self.os_data.kqfd, kevent_array, eventlist, null) catch {
357 self.next_tick_queue.unget(next_tick_node);376 self.next_tick_queue.unget(next_tick_node);
358 self.available_eventfd_resume_nodes.push(resume_stack_node);377 self.available_eventfd_resume_nodes.push(resume_stack_node);
359 return;378 return;
...@@ -361,8 +380,8 @@ pub const Loop = struct {...@@ -361,8 +380,8 @@ pub const Loop = struct {
361 },380 },
362 builtin.Os.linux => {381 builtin.Os.linux => {
363 // the pending count is already accounted for382 // the pending count is already accounted for
364 const epoll_events = posix.EPOLLONESHOT | std.os.linux.EPOLLIN | std.os.linux.EPOLLOUT |383 const epoll_events = posix.EPOLLONESHOT | os.linux.EPOLLIN | os.linux.EPOLLOUT |
365 std.os.linux.EPOLLET;384 os.linux.EPOLLET;
366 self.modFd(385 self.modFd(
367 eventfd_node.eventfd,386 eventfd_node.eventfd,
368 eventfd_node.epoll_op,387 eventfd_node.epoll_op,
...@@ -379,7 +398,7 @@ pub const Loop = struct {...@@ -379,7 +398,7 @@ pub const Loop = struct {
379 // the consumer code can decide whether to read the completion key.398 // the consumer code can decide whether to read the completion key.
380 // it has to do this for normal I/O, so we match that behavior here.399 // it has to do this for normal I/O, so we match that behavior here.
381 const overlapped = @intToPtr(?*windows.OVERLAPPED, 0x1);400 const overlapped = @intToPtr(?*windows.OVERLAPPED, 0x1);
382 std.os.windowsPostQueuedCompletionStatus(401 os.windowsPostQueuedCompletionStatus(
383 self.os_data.io_port,402 self.os_data.io_port,
384 undefined,403 undefined,
385 eventfd_node.completion_key,404 eventfd_node.completion_key,
...@@ -406,6 +425,9 @@ pub const Loop = struct {...@@ -406,6 +425,9 @@ pub const Loop = struct {
406 self.finishOneEvent(); // the reference we start with425 self.finishOneEvent(); // the reference we start with
407426
408 self.workerRun();427 self.workerRun();
428
429 self.os_data.fs_thread.wait();
430
409 for (self.extra_threads) |extra_thread| {431 for (self.extra_threads) |extra_thread| {
410 extra_thread.wait();432 extra_thread.wait();
411 }433 }
...@@ -453,15 +475,16 @@ pub const Loop = struct {...@@ -453,15 +475,16 @@ pub const Loop = struct {
453 // cause all the threads to stop475 // cause all the threads to stop
454 switch (builtin.os) {476 switch (builtin.os) {
455 builtin.Os.linux => {477 builtin.Os.linux => {
478 self.linuxFsRequest(&self.os_data.fs_end_request);
456 // writing 8 bytes to an eventfd cannot fail479 // writing 8 bytes to an eventfd cannot fail
457 std.os.posixWrite(self.os_data.final_eventfd, wakeup_bytes) catch unreachable;480 os.posixWrite(self.os_data.final_eventfd, wakeup_bytes) catch unreachable;
458 return;481 return;
459 },482 },
460 builtin.Os.macosx => {483 builtin.Os.macosx => {
461 const final_kevent = (*[1]posix.Kevent)(&self.os_data.final_kevent);484 const final_kevent = (*[1]posix.Kevent)(&self.os_data.final_kevent);
462 const eventlist = ([*]posix.Kevent)(undefined)[0..0];485 const eventlist = ([*]posix.Kevent)(undefined)[0..0];
463 // cannot fail because we already added it and this just enables it486 // cannot fail because we already added it and this just enables it
464 _ = std.os.bsdKEvent(self.os_data.kqfd, final_kevent, eventlist, null) catch unreachable;487 _ = os.bsdKEvent(self.os_data.kqfd, final_kevent, eventlist, null) catch unreachable;
465 return;488 return;
466 },489 },
467 builtin.Os.windows => {490 builtin.Os.windows => {
...@@ -469,7 +492,7 @@ pub const Loop = struct {...@@ -469,7 +492,7 @@ pub const Loop = struct {
469 while (i < self.extra_threads.len + 1) : (i += 1) {492 while (i < self.extra_threads.len + 1) : (i += 1) {
470 while (true) {493 while (true) {
471 const overlapped = @intToPtr(?*windows.OVERLAPPED, 0x1);494 const overlapped = @intToPtr(?*windows.OVERLAPPED, 0x1);
472 std.os.windowsPostQueuedCompletionStatus(self.os_data.io_port, undefined, @ptrToInt(&self.final_resume_node), overlapped) catch continue;495 os.windowsPostQueuedCompletionStatus(self.os_data.io_port, undefined, @ptrToInt(&self.final_resume_node), overlapped) catch continue;
473 break;496 break;
474 }497 }
475 }498 }
...@@ -492,8 +515,8 @@ pub const Loop = struct {...@@ -492,8 +515,8 @@ pub const Loop = struct {
492 switch (builtin.os) {515 switch (builtin.os) {
493 builtin.Os.linux => {516 builtin.Os.linux => {
494 // only process 1 event so we don't steal from other threads517 // only process 1 event so we don't steal from other threads
495 var events: [1]std.os.linux.epoll_event = undefined;518 var events: [1]os.linux.epoll_event = undefined;
496 const count = std.os.linuxEpollWait(self.os_data.epollfd, events[0..], -1);519 const count = os.linuxEpollWait(self.os_data.epollfd, events[0..], -1);
497 for (events[0..count]) |ev| {520 for (events[0..count]) |ev| {
498 const resume_node = @intToPtr(*ResumeNode, ev.data.ptr);521 const resume_node = @intToPtr(*ResumeNode, ev.data.ptr);
499 const handle = resume_node.handle;522 const handle = resume_node.handle;
...@@ -516,7 +539,7 @@ pub const Loop = struct {...@@ -516,7 +539,7 @@ pub const Loop = struct {
516 },539 },
517 builtin.Os.macosx => {540 builtin.Os.macosx => {
518 var eventlist: [1]posix.Kevent = undefined;541 var eventlist: [1]posix.Kevent = undefined;
519 const count = std.os.bsdKEvent(self.os_data.kqfd, self.os_data.kevents, eventlist[0..], null) catch unreachable;542 const count = os.bsdKEvent(self.os_data.kqfd, self.os_data.kevents, eventlist[0..], null) catch unreachable;
520 for (eventlist[0..count]) |ev| {543 for (eventlist[0..count]) |ev| {
521 const resume_node = @intToPtr(*ResumeNode, ev.udata);544 const resume_node = @intToPtr(*ResumeNode, ev.udata);
522 const handle = resume_node.handle;545 const handle = resume_node.handle;
...@@ -541,9 +564,9 @@ pub const Loop = struct {...@@ -541,9 +564,9 @@ pub const Loop = struct {
541 while (true) {564 while (true) {
542 var nbytes: windows.DWORD = undefined;565 var nbytes: windows.DWORD = undefined;
543 var overlapped: ?*windows.OVERLAPPED = undefined;566 var overlapped: ?*windows.OVERLAPPED = undefined;
544 switch (std.os.windowsGetQueuedCompletionStatus(self.os_data.io_port, &nbytes, &completion_key, &overlapped, windows.INFINITE)) {567 switch (os.windowsGetQueuedCompletionStatus(self.os_data.io_port, &nbytes, &completion_key, &overlapped, windows.INFINITE)) {
545 std.os.WindowsWaitResult.Aborted => return,568 os.WindowsWaitResult.Aborted => return,
546 std.os.WindowsWaitResult.Normal => {},569 os.WindowsWaitResult.Normal => {},
547 }570 }
548 if (overlapped != null) break;571 if (overlapped != null) break;
549 }572 }
...@@ -569,11 +592,73 @@ pub const Loop = struct {...@@ -569,11 +592,73 @@ pub const Loop = struct {
569 }592 }
570 }593 }
571594
595 fn linuxFsRequest(self: *Loop, request_node: *fs.RequestNode) void {
596 _ = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst);
597 self.os_data.fs_queue.put(request_node);
598 _ = @atomicRmw(i32, &self.os_data.fs_queue_len, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst); // let this wrap
599 const rc = os.linux.futex_wake(@ptrToInt(&self.os_data.fs_queue_len), os.linux.FUTEX_WAKE, 1);
600 switch (os.linux.getErrno(rc)) {
601 0 => {},
602 posix.EINVAL => unreachable,
603 else => unreachable,
604 }
605 }
606
607 fn linuxFsRun(self: *Loop) void {
608 var processed_count: i32 = 0; // we let this wrap
609 while (true) {
610 while (self.os_data.fs_queue.get()) |node| {
611 processed_count +%= 1;
612 switch (node.data.msg) {
613 @TagType(fs.Request.Msg).PWriteV => @panic("TODO"),
614 @TagType(fs.Request.Msg).PReadV => |*msg| {
615 msg.result = os.posix_preadv(msg.fd, msg.iov.ptr, msg.iov.len, msg.offset);
616 },
617 @TagType(fs.Request.Msg).OpenRead => |*msg| {
618 const flags = posix.O_LARGEFILE | posix.O_RDONLY;
619 msg.result = os.posixOpenC(msg.path.ptr, flags, 0);
620 },
621 @TagType(fs.Request.Msg).Close => |*msg| os.close(msg.fd),
622 @TagType(fs.Request.Msg).WriteFile => |*msg| blk: {
623 const flags = posix.O_LARGEFILE | posix.O_WRONLY | posix.O_CREAT |
624 posix.O_CLOEXEC | posix.O_TRUNC;
625 const fd = os.posixOpenC(msg.path.ptr, flags, msg.mode) catch |err| {
626 msg.result = err;
627 break :blk;
628 };
629 defer os.close(fd);
630 msg.result = os.posixWrite(fd, msg.contents);
631 },
632 @TagType(fs.Request.Msg).End => return,
633 }
634 switch (node.data.finish) {
635 @TagType(fs.Request.Finish).TickNode => |*tick_node| self.onNextTick(tick_node),
636 @TagType(fs.Request.Finish).DeallocCloseOperation => |close_op| {
637 self.allocator.destroy(close_op);
638 },
639 @TagType(fs.Request.Finish).NoAction => {},
640 }
641 self.finishOneEvent();
642 }
643 const rc = os.linux.futex_wait(@ptrToInt(&self.os_data.fs_queue_len), os.linux.FUTEX_WAIT, processed_count, null);
644 switch (os.linux.getErrno(rc)) {
645 0 => continue,
646 posix.EINTR => continue,
647 posix.EAGAIN => continue,
648 else => unreachable,
649 }
650 }
651 }
652
572 const OsData = switch (builtin.os) {653 const OsData = switch (builtin.os) {
573 builtin.Os.linux => struct {654 builtin.Os.linux => struct {
574 epollfd: i32,655 epollfd: i32,
575 final_eventfd: i32,656 final_eventfd: i32,
576 final_eventfd_event: std.os.linux.epoll_event,657 final_eventfd_event: os.linux.epoll_event,
658 fs_thread: *os.Thread,
659 fs_queue_len: i32, // we let this wrap
660 fs_queue: std.atomic.Queue(fs.Request),
661 fs_end_request: fs.RequestNode,
577 },662 },
578 builtin.Os.macosx => MacOsData,663 builtin.Os.macosx => MacOsData,
579 builtin.Os.windows => struct {664 builtin.Os.windows => struct {
std/io.zig+7-9
...@@ -415,13 +415,12 @@ pub fn PeekStream(comptime buffer_size: usize, comptime InStreamError: type) typ...@@ -415,13 +415,12 @@ pub fn PeekStream(comptime buffer_size: usize, comptime InStreamError: type) typ
415 self.at_end = (read < left);415 self.at_end = (read < left);
416 return pos + read;416 return pos + read;
417 }417 }
418
419 };418 };
420}419}
421420
422pub const SliceInStream = struct {421pub const SliceInStream = struct {
423 const Self = this;422 const Self = this;
424 pub const Error = error { };423 pub const Error = error{};
425 pub const Stream = InStream(Error);424 pub const Stream = InStream(Error);
426425
427 pub stream: Stream,426 pub stream: Stream,
...@@ -481,13 +480,12 @@ pub const SliceOutStream = struct {...@@ -481,13 +480,12 @@ pub const SliceOutStream = struct {
481480
482 assert(self.pos <= self.slice.len);481 assert(self.pos <= self.slice.len);
483482
484 const n =483 const n = if (self.pos + bytes.len <= self.slice.len)
485 if (self.pos + bytes.len <= self.slice.len)484 bytes.len
486 bytes.len485 else
487 else486 self.slice.len - self.pos;
488 self.slice.len - self.pos;
489487
490 std.mem.copy(u8, self.slice[self.pos..self.pos + n], bytes[0..n]);488 std.mem.copy(u8, self.slice[self.pos .. self.pos + n], bytes[0..n]);
491 self.pos += n;489 self.pos += n;
492490
493 if (n < bytes.len) {491 if (n < bytes.len) {
...@@ -586,7 +584,7 @@ pub const BufferedAtomicFile = struct {...@@ -586,7 +584,7 @@ pub const BufferedAtomicFile = struct {
586 });584 });
587 errdefer allocator.destroy(self);585 errdefer allocator.destroy(self);
588586
589 self.atomic_file = try os.AtomicFile.init(allocator, dest_path, os.default_file_mode);587 self.atomic_file = try os.AtomicFile.init(allocator, dest_path, os.File.default_mode);
590 errdefer self.atomic_file.deinit();588 errdefer self.atomic_file.deinit();
591589
592 self.file_stream = FileOutStream.init(&self.atomic_file.file);590 self.file_stream = FileOutStream.init(&self.atomic_file.file);
std/os/file.zig+29-10
...@@ -15,10 +15,21 @@ pub const File = struct {...@@ -15,10 +15,21 @@ pub const File = struct {
15 /// The OS-specific file descriptor or file handle.15 /// The OS-specific file descriptor or file handle.
16 handle: os.FileHandle,16 handle: os.FileHandle,
1717
18 pub const Mode = switch (builtin.os) {
19 Os.windows => void,
20 else => u32,
21 };
22
23 pub const default_mode = switch (builtin.os) {
24 Os.windows => {},
25 else => 0o666,
26 };
27
18 pub const OpenError = os.WindowsOpenError || os.PosixOpenError;28 pub const OpenError = os.WindowsOpenError || os.PosixOpenError;
1929
20 /// `path` needs to be copied in memory to add a null terminating byte, hence the allocator.30 /// `path` needs to be copied in memory to add a null terminating byte, hence the allocator.
21 /// Call close to clean up.31 /// Call close to clean up.
32 /// TODO deprecated, just use open
22 pub fn openRead(allocator: *mem.Allocator, path: []const u8) OpenError!File {33 pub fn openRead(allocator: *mem.Allocator, path: []const u8) OpenError!File {
23 if (is_posix) {34 if (is_posix) {
24 const flags = posix.O_LARGEFILE | posix.O_RDONLY;35 const flags = posix.O_LARGEFILE | posix.O_RDONLY;
...@@ -39,16 +50,18 @@ pub const File = struct {...@@ -39,16 +50,18 @@ pub const File = struct {
39 }50 }
40 }51 }
4152
42 /// Calls `openWriteMode` with os.default_file_mode for the mode.53 /// Calls `openWriteMode` with os.File.default_mode for the mode.
54 /// TODO deprecated, just use open
43 pub fn openWrite(allocator: *mem.Allocator, path: []const u8) OpenError!File {55 pub fn openWrite(allocator: *mem.Allocator, path: []const u8) OpenError!File {
44 return openWriteMode(allocator, path, os.default_file_mode);56 return openWriteMode(allocator, path, os.File.default_mode);
45 }57 }
4658
47 /// If the path does not exist it will be created.59 /// If the path does not exist it will be created.
48 /// If a file already exists in the destination it will be truncated.60 /// If a file already exists in the destination it will be truncated.
49 /// `path` needs to be copied in memory to add a null terminating byte, hence the allocator.61 /// `path` needs to be copied in memory to add a null terminating byte, hence the allocator.
50 /// Call close to clean up.62 /// Call close to clean up.
51 pub fn openWriteMode(allocator: *mem.Allocator, path: []const u8, file_mode: os.FileMode) OpenError!File {63 /// TODO deprecated, just use open
64 pub fn openWriteMode(allocator: *mem.Allocator, path: []const u8, file_mode: Mode) OpenError!File {
52 if (is_posix) {65 if (is_posix) {
53 const flags = posix.O_LARGEFILE | posix.O_WRONLY | posix.O_CREAT | posix.O_CLOEXEC | posix.O_TRUNC;66 const flags = posix.O_LARGEFILE | posix.O_WRONLY | posix.O_CREAT | posix.O_CLOEXEC | posix.O_TRUNC;
54 const fd = try os.posixOpen(allocator, path, flags, file_mode);67 const fd = try os.posixOpen(allocator, path, flags, file_mode);
...@@ -72,7 +85,8 @@ pub const File = struct {...@@ -72,7 +85,8 @@ pub const File = struct {
72 /// If a file already exists in the destination this returns OpenError.PathAlreadyExists85 /// If a file already exists in the destination this returns OpenError.PathAlreadyExists
73 /// `path` needs to be copied in memory to add a null terminating byte, hence the allocator.86 /// `path` needs to be copied in memory to add a null terminating byte, hence the allocator.
74 /// Call close to clean up.87 /// Call close to clean up.
75 pub fn openWriteNoClobber(allocator: *mem.Allocator, path: []const u8, file_mode: os.FileMode) OpenError!File {88 /// TODO deprecated, just use open
89 pub fn openWriteNoClobber(allocator: *mem.Allocator, path: []const u8, file_mode: Mode) OpenError!File {
76 if (is_posix) {90 if (is_posix) {
77 const flags = posix.O_LARGEFILE | posix.O_WRONLY | posix.O_CREAT | posix.O_CLOEXEC | posix.O_EXCL;91 const flags = posix.O_LARGEFILE | posix.O_WRONLY | posix.O_CREAT | posix.O_CLOEXEC | posix.O_EXCL;
78 const fd = try os.posixOpen(allocator, path, flags, file_mode);92 const fd = try os.posixOpen(allocator, path, flags, file_mode);
...@@ -282,7 +296,7 @@ pub const File = struct {...@@ -282,7 +296,7 @@ pub const File = struct {
282 Unexpected,296 Unexpected,
283 };297 };
284298
285 pub fn mode(self: *File) ModeError!os.FileMode {299 pub fn mode(self: *File) ModeError!Mode {
286 if (is_posix) {300 if (is_posix) {
287 var stat: posix.Stat = undefined;301 var stat: posix.Stat = undefined;
288 const err = posix.getErrno(posix.fstat(self.handle, &stat));302 const err = posix.getErrno(posix.fstat(self.handle, &stat));
...@@ -296,7 +310,7 @@ pub const File = struct {...@@ -296,7 +310,7 @@ pub const File = struct {
296310
297 // TODO: we should be able to cast u16 to ModeError!u32, making this311 // TODO: we should be able to cast u16 to ModeError!u32, making this
298 // explicit cast not necessary312 // explicit cast not necessary
299 return os.FileMode(stat.mode);313 return Mode(stat.mode);
300 } else if (is_windows) {314 } else if (is_windows) {
301 return {};315 return {};
302 } else {316 } else {
...@@ -305,9 +319,11 @@ pub const File = struct {...@@ -305,9 +319,11 @@ pub const File = struct {
305 }319 }
306320
307 pub const ReadError = error{321 pub const ReadError = error{
308 BadFd,322 FileClosed,
309 Io,323 InputOutput,
310 IsDir,324 IsDir,
325 WouldBlock,
326 SystemResources,
311327
312 Unexpected,328 Unexpected,
313 };329 };
...@@ -323,9 +339,12 @@ pub const File = struct {...@@ -323,9 +339,12 @@ pub const File = struct {
323 posix.EINTR => continue,339 posix.EINTR => continue,
324 posix.EINVAL => unreachable,340 posix.EINVAL => unreachable,
325 posix.EFAULT => unreachable,341 posix.EFAULT => unreachable,
326 posix.EBADF => return error.BadFd,342 posix.EAGAIN => return error.WouldBlock,
327 posix.EIO => return error.Io,343 posix.EBADF => return error.FileClosed,
344 posix.EIO => return error.InputOutput,
328 posix.EISDIR => return error.IsDir,345 posix.EISDIR => return error.IsDir,
346 posix.ENOBUFS => return error.SystemResources,
347 posix.ENOMEM => return error.SystemResources,
329 else => return os.unexpectedErrorPosix(read_err),348 else => return os.unexpectedErrorPosix(read_err),
330 }349 }
331 }350 }
std/os/index.zig+22-12
...@@ -38,16 +38,6 @@ pub const path = @import("path.zig");...@@ -38,16 +38,6 @@ pub const path = @import("path.zig");
38pub const File = @import("file.zig").File;38pub const File = @import("file.zig").File;
39pub const time = @import("time.zig");39pub const time = @import("time.zig");
4040
41pub const FileMode = switch (builtin.os) {
42 Os.windows => void,
43 else => u32,
44};
45
46pub const default_file_mode = switch (builtin.os) {
47 Os.windows => {},
48 else => 0o666,
49};
50
51pub const page_size = 4 * 1024;41pub const page_size = 4 * 1024;
5242
53pub const UserInfo = @import("get_user_id.zig").UserInfo;43pub const UserInfo = @import("get_user_id.zig").UserInfo;
...@@ -256,6 +246,26 @@ pub fn posixRead(fd: i32, buf: []u8) !void {...@@ -256,6 +246,26 @@ pub fn posixRead(fd: i32, buf: []u8) !void {
256 }246 }
257}247}
258248
249pub fn posix_preadv(fd: i32, iov: [*]const posix.iovec, count: usize, offset: u64) !usize {
250 while (true) {
251 const rc = posix.preadv(fd, iov, count, offset);
252 const err = posix.getErrno(rc);
253 switch (err) {
254 0 => return rc,
255 posix.EINTR => continue,
256 posix.EINVAL => unreachable,
257 posix.EFAULT => unreachable,
258 posix.EAGAIN => return error.WouldBlock,
259 posix.EBADF => return error.FileClosed,
260 posix.EIO => return error.InputOutput,
261 posix.EISDIR => return error.IsDir,
262 posix.ENOBUFS => return error.SystemResources,
263 posix.ENOMEM => return error.SystemResources,
264 else => return unexpectedErrorPosix(err),
265 }
266 }
267}
268
259pub const PosixWriteError = error{269pub const PosixWriteError = error{
260 WouldBlock,270 WouldBlock,
261 FileClosed,271 FileClosed,
...@@ -853,7 +863,7 @@ pub fn copyFile(allocator: *Allocator, source_path: []const u8, dest_path: []con...@@ -853,7 +863,7 @@ pub fn copyFile(allocator: *Allocator, source_path: []const u8, dest_path: []con
853/// Guaranteed to be atomic. However until https://patchwork.kernel.org/patch/9636735/ is863/// Guaranteed to be atomic. However until https://patchwork.kernel.org/patch/9636735/ is
854/// merged and readily available,864/// merged and readily available,
855/// there is a possibility of power loss or application termination leaving temporary files present865/// there is a possibility of power loss or application termination leaving temporary files present
856pub fn copyFileMode(allocator: *Allocator, source_path: []const u8, dest_path: []const u8, mode: FileMode) !void {866pub fn copyFileMode(allocator: *Allocator, source_path: []const u8, dest_path: []const u8, mode: File.Mode) !void {
857 var in_file = try os.File.openRead(allocator, source_path);867 var in_file = try os.File.openRead(allocator, source_path);
858 defer in_file.close();868 defer in_file.close();
859869
...@@ -879,7 +889,7 @@ pub const AtomicFile = struct {...@@ -879,7 +889,7 @@ pub const AtomicFile = struct {
879889
880 /// dest_path must remain valid for the lifetime of AtomicFile890 /// dest_path must remain valid for the lifetime of AtomicFile
881 /// call finish to atomically replace dest_path with contents891 /// call finish to atomically replace dest_path with contents
882 pub fn init(allocator: *Allocator, dest_path: []const u8, mode: FileMode) !AtomicFile {892 pub fn init(allocator: *Allocator, dest_path: []const u8, mode: File.Mode) !AtomicFile {
883 const dirname = os.path.dirname(dest_path);893 const dirname = os.path.dirname(dest_path);
884894
885 var rand_buf: [12]u8 = undefined;895 var rand_buf: [12]u8 = undefined;
std/os/linux/index.zig+8
...@@ -692,6 +692,10 @@ pub fn futex_wait(uaddr: usize, futex_op: u32, val: i32, timeout: ?*timespec) us...@@ -692,6 +692,10 @@ pub fn futex_wait(uaddr: usize, futex_op: u32, val: i32, timeout: ?*timespec) us
692 return syscall4(SYS_futex, uaddr, futex_op, @bitCast(u32, val), @ptrToInt(timeout));692 return syscall4(SYS_futex, uaddr, futex_op, @bitCast(u32, val), @ptrToInt(timeout));
693}693}
694694
695pub fn futex_wake(uaddr: usize, futex_op: u32, val: i32) usize {
696 return syscall3(SYS_futex, uaddr, futex_op, @bitCast(u32, val));
697}
698
695pub fn getcwd(buf: [*]u8, size: usize) usize {699pub fn getcwd(buf: [*]u8, size: usize) usize {
696 return syscall2(SYS_getcwd, @ptrToInt(buf), size);700 return syscall2(SYS_getcwd, @ptrToInt(buf), size);
697}701}
...@@ -742,6 +746,10 @@ pub fn read(fd: i32, buf: [*]u8, count: usize) usize {...@@ -742,6 +746,10 @@ pub fn read(fd: i32, buf: [*]u8, count: usize) usize {
742 return syscall3(SYS_read, @intCast(usize, fd), @ptrToInt(buf), count);746 return syscall3(SYS_read, @intCast(usize, fd), @ptrToInt(buf), count);
743}747}
744748
749pub fn preadv(fd: i32, iov: [*]const iovec, count: usize, offset: u64) usize {
750 return syscall4(SYS_preadv, @intCast(usize, fd), @ptrToInt(iov), count, offset);
751}
752
745// TODO https://github.com/ziglang/zig/issues/265753// TODO https://github.com/ziglang/zig/issues/265
746pub fn rmdir(path: [*]const u8) usize {754pub fn rmdir(path: [*]const u8) usize {
747 return syscall1(SYS_rmdir, @ptrToInt(path));755 return syscall1(SYS_rmdir, @ptrToInt(path));