tigerbeetle-node 0.4.2 → 0.5.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +19 -5
- package/dist/benchmark.js.map +1 -1
- package/dist/index.d.ts +18 -16
- package/dist/index.js +35 -13
- package/dist/index.js.map +1 -1
- package/dist/test.js +12 -0
- package/dist/test.js.map +1 -1
- package/package.json +2 -2
- package/scripts/postinstall.sh +2 -2
- package/src/benchmark.ts +2 -2
- package/src/index.ts +29 -4
- package/src/node.zig +120 -17
- package/src/test.ts +14 -0
- package/src/tigerbeetle/scripts/install.sh +1 -1
- package/src/tigerbeetle/scripts/install_zig.bat +109 -0
- package/src/tigerbeetle/scripts/install_zig.sh +4 -2
- package/src/tigerbeetle/scripts/lint.zig +8 -2
- package/src/tigerbeetle/scripts/vopr.bat +48 -0
- package/src/tigerbeetle/src/benchmark.zig +10 -8
- package/src/tigerbeetle/src/cli.zig +6 -4
- package/src/tigerbeetle/src/config.zig +2 -2
- package/src/tigerbeetle/src/demo.zig +119 -89
- package/src/tigerbeetle/src/demo_01_create_accounts.zig +5 -3
- package/src/tigerbeetle/src/demo_02_lookup_accounts.zig +2 -3
- package/src/tigerbeetle/src/demo_03_create_transfers.zig +5 -3
- package/src/tigerbeetle/src/demo_04_create_transfers_two_phase_commit.zig +5 -3
- package/src/tigerbeetle/src/demo_05_accept_transfers.zig +5 -3
- package/src/tigerbeetle/src/demo_06_reject_transfers.zig +5 -3
- package/src/tigerbeetle/src/demo_07_lookup_transfers.zig +7 -0
- package/src/tigerbeetle/src/io/benchmark.zig +238 -0
- package/src/tigerbeetle/src/{io_darwin.zig → io/darwin.zig} +89 -124
- package/src/tigerbeetle/src/io/linux.zig +933 -0
- package/src/tigerbeetle/src/io/test.zig +621 -0
- package/src/tigerbeetle/src/io.zig +7 -1328
- package/src/tigerbeetle/src/main.zig +18 -10
- package/src/tigerbeetle/src/message_bus.zig +43 -60
- package/src/tigerbeetle/src/message_pool.zig +3 -2
- package/src/tigerbeetle/src/ring_buffer.zig +135 -68
- package/src/tigerbeetle/src/simulator.zig +41 -37
- package/src/tigerbeetle/src/state_machine.zig +851 -26
- package/src/tigerbeetle/src/storage.zig +49 -46
- package/src/tigerbeetle/src/test/cluster.zig +2 -2
- package/src/tigerbeetle/src/test/message_bus.zig +6 -6
- package/src/tigerbeetle/src/test/network.zig +3 -3
- package/src/tigerbeetle/src/test/packet_simulator.zig +32 -29
- package/src/tigerbeetle/src/test/state_checker.zig +2 -2
- package/src/tigerbeetle/src/test/state_machine.zig +4 -0
- package/src/tigerbeetle/src/test/storage.zig +39 -19
- package/src/tigerbeetle/src/test/time.zig +2 -2
- package/src/tigerbeetle/src/tigerbeetle.zig +6 -129
- package/src/tigerbeetle/src/time.zig +6 -5
- package/src/tigerbeetle/src/vsr/client.zig +11 -11
- package/src/tigerbeetle/src/vsr/clock.zig +26 -43
- package/src/tigerbeetle/src/vsr/journal.zig +7 -6
- package/src/tigerbeetle/src/vsr/marzullo.zig +6 -3
- package/src/tigerbeetle/src/vsr/replica.zig +51 -48
- package/src/tigerbeetle/src/vsr.zig +24 -20
- package/src/translate.zig +55 -55
|
@@ -0,0 +1,933 @@
|
|
|
1
|
+
const std = @import("std");
|
|
2
|
+
const assert = std.debug.assert;
|
|
3
|
+
const os = std.os;
|
|
4
|
+
const linux = os.linux;
|
|
5
|
+
const IO_Uring = linux.IO_Uring;
|
|
6
|
+
const io_uring_cqe = linux.io_uring_cqe;
|
|
7
|
+
const io_uring_sqe = linux.io_uring_sqe;
|
|
8
|
+
|
|
9
|
+
const FIFO = @import("../fifo.zig").FIFO;
|
|
10
|
+
const buffer_limit = @import("../io.zig").buffer_limit;
|
|
11
|
+
|
|
12
|
+
pub const IO = struct {
|
|
13
|
+
ring: IO_Uring,
|
|
14
|
+
|
|
15
|
+
/// Operations not yet submitted to the kernel and waiting on available space in the
|
|
16
|
+
/// submission queue.
|
|
17
|
+
unqueued: FIFO(Completion) = .{},
|
|
18
|
+
|
|
19
|
+
/// Completions that are ready to have their callbacks run.
|
|
20
|
+
completed: FIFO(Completion) = .{},
|
|
21
|
+
|
|
22
|
+
pub fn init(entries: u12, flags: u32) !IO {
|
|
23
|
+
return IO{ .ring = try IO_Uring.init(entries, flags) };
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
pub fn deinit(self: *IO) void {
|
|
27
|
+
self.ring.deinit();
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/// Pass all queued submissions to the kernel and peek for completions.
|
|
31
|
+
pub fn tick(self: *IO) !void {
|
|
32
|
+
// We assume that all timeouts submitted by `run_for_ns()` will be reaped by `run_for_ns()`
|
|
33
|
+
// and that `tick()` and `run_for_ns()` cannot be run concurrently.
|
|
34
|
+
// Therefore `timeouts` here will never be decremented and `etime` will always be false.
|
|
35
|
+
var timeouts: usize = 0;
|
|
36
|
+
var etime = false;
|
|
37
|
+
|
|
38
|
+
try self.flush(0, &timeouts, &etime);
|
|
39
|
+
assert(etime == false);
|
|
40
|
+
|
|
41
|
+
// Flush any SQEs that were queued while running completion callbacks in `flush()`:
|
|
42
|
+
// This is an optimization to avoid delaying submissions until the next tick.
|
|
43
|
+
// At the same time, we do not flush any ready CQEs since SQEs may complete synchronously.
|
|
44
|
+
// We guard against an io_uring_enter() syscall if we know we do not have any queued SQEs.
|
|
45
|
+
// We cannot use `self.ring.sq_ready()` here since this counts flushed and unflushed SQEs.
|
|
46
|
+
const queued = self.ring.sq.sqe_tail -% self.ring.sq.sqe_head;
|
|
47
|
+
if (queued > 0) {
|
|
48
|
+
try self.flush_submissions(0, &timeouts, &etime);
|
|
49
|
+
assert(etime == false);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/// Pass all queued submissions to the kernel and run for `nanoseconds`.
|
|
54
|
+
/// The `nanoseconds` argument is a u63 to allow coercion to the i64 used
|
|
55
|
+
/// in the kernel_timespec struct.
|
|
56
|
+
pub fn run_for_ns(self: *IO, nanoseconds: u63) !void {
|
|
57
|
+
// We must use the same clock source used by io_uring (CLOCK_MONOTONIC) since we specify the
|
|
58
|
+
// timeout below as an absolute value. Otherwise, we may deadlock if the clock sources are
|
|
59
|
+
// dramatically different. Any kernel that supports io_uring will support CLOCK_MONOTONIC.
|
|
60
|
+
var current_ts: os.timespec = undefined;
|
|
61
|
+
os.clock_gettime(os.CLOCK.MONOTONIC, ¤t_ts) catch unreachable;
|
|
62
|
+
// The absolute CLOCK_MONOTONIC time after which we may return from this function:
|
|
63
|
+
const timeout_ts: os.linux.kernel_timespec = .{
|
|
64
|
+
.tv_sec = current_ts.tv_sec,
|
|
65
|
+
.tv_nsec = current_ts.tv_nsec + nanoseconds,
|
|
66
|
+
};
|
|
67
|
+
var timeouts: usize = 0;
|
|
68
|
+
var etime = false;
|
|
69
|
+
while (!etime) {
|
|
70
|
+
const timeout_sqe = self.ring.get_sqe() catch blk: {
|
|
71
|
+
// The submission queue is full, so flush submissions to make space:
|
|
72
|
+
try self.flush_submissions(0, &timeouts, &etime);
|
|
73
|
+
break :blk self.ring.get_sqe() catch unreachable;
|
|
74
|
+
};
|
|
75
|
+
// Submit an absolute timeout that will be canceled if any other SQE completes first:
|
|
76
|
+
linux.io_uring_prep_timeout(timeout_sqe, &timeout_ts, 1, os.linux.IORING_TIMEOUT_ABS);
|
|
77
|
+
timeout_sqe.user_data = 0;
|
|
78
|
+
timeouts += 1;
|
|
79
|
+
// The amount of time this call will block is bounded by the timeout we just submitted:
|
|
80
|
+
try self.flush(1, &timeouts, &etime);
|
|
81
|
+
}
|
|
82
|
+
// Reap any remaining timeouts, which reference the timespec in the current stack frame.
|
|
83
|
+
// The busy loop here is required to avoid a potential deadlock, as the kernel determines
|
|
84
|
+
// when the timeouts are pushed to the completion queue, not us.
|
|
85
|
+
while (timeouts > 0) _ = try self.flush_completions(0, &timeouts, &etime);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
fn flush(self: *IO, wait_nr: u32, timeouts: *usize, etime: *bool) !void {
|
|
89
|
+
// Flush any queued SQEs and reuse the same syscall to wait for completions if required:
|
|
90
|
+
try self.flush_submissions(wait_nr, timeouts, etime);
|
|
91
|
+
// We can now just peek for any CQEs without waiting and without another syscall:
|
|
92
|
+
try self.flush_completions(0, timeouts, etime);
|
|
93
|
+
// Run completions only after all completions have been flushed:
|
|
94
|
+
// Loop on a copy of the linked list, having reset the list first, so that any synchronous
|
|
95
|
+
// append on running a completion is executed only the next time round the event loop,
|
|
96
|
+
// without creating an infinite loop.
|
|
97
|
+
{
|
|
98
|
+
var copy = self.completed;
|
|
99
|
+
self.completed = .{};
|
|
100
|
+
while (copy.pop()) |completion| completion.complete();
|
|
101
|
+
}
|
|
102
|
+
// Again, loop on a copy of the list to avoid an infinite loop:
|
|
103
|
+
{
|
|
104
|
+
var copy = self.unqueued;
|
|
105
|
+
self.unqueued = .{};
|
|
106
|
+
while (copy.pop()) |completion| self.enqueue(completion);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
fn flush_completions(self: *IO, wait_nr: u32, timeouts: *usize, etime: *bool) !void {
|
|
111
|
+
var cqes: [256]io_uring_cqe = undefined;
|
|
112
|
+
var wait_remaining = wait_nr;
|
|
113
|
+
while (true) {
|
|
114
|
+
// Guard against waiting indefinitely (if there are too few requests inflight),
|
|
115
|
+
// especially if this is not the first time round the loop:
|
|
116
|
+
const completed = self.ring.copy_cqes(&cqes, wait_remaining) catch |err| switch (err) {
|
|
117
|
+
error.SignalInterrupt => continue,
|
|
118
|
+
else => return err,
|
|
119
|
+
};
|
|
120
|
+
if (completed > wait_remaining) wait_remaining = 0 else wait_remaining -= completed;
|
|
121
|
+
for (cqes[0..completed]) |cqe| {
|
|
122
|
+
if (cqe.user_data == 0) {
|
|
123
|
+
timeouts.* -= 1;
|
|
124
|
+
// We are only done if the timeout submitted was completed due to time, not if
|
|
125
|
+
// it was completed due to the completion of an event, in which case `cqe.res`
|
|
126
|
+
// would be 0. It is possible for multiple timeout operations to complete at the
|
|
127
|
+
// same time if the nanoseconds value passed to `run_for_ns()` is very short.
|
|
128
|
+
if (-cqe.res == @enumToInt(os.E.TIME)) etime.* = true;
|
|
129
|
+
continue;
|
|
130
|
+
}
|
|
131
|
+
const completion = @intToPtr(*Completion, @intCast(usize, cqe.user_data));
|
|
132
|
+
completion.result = cqe.res;
|
|
133
|
+
// We do not run the completion here (instead appending to a linked list) to avoid:
|
|
134
|
+
// * recursion through `flush_submissions()` and `flush_completions()`,
|
|
135
|
+
// * unbounded stack usage, and
|
|
136
|
+
// * confusing stack traces.
|
|
137
|
+
self.completed.push(completion);
|
|
138
|
+
}
|
|
139
|
+
if (completed < cqes.len) break;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
fn flush_submissions(self: *IO, wait_nr: u32, timeouts: *usize, etime: *bool) !void {
|
|
144
|
+
while (true) {
|
|
145
|
+
_ = self.ring.submit_and_wait(wait_nr) catch |err| switch (err) {
|
|
146
|
+
error.SignalInterrupt => continue,
|
|
147
|
+
// Wait for some completions and then try again:
|
|
148
|
+
// See https://github.com/axboe/liburing/issues/281 re: error.SystemResources.
|
|
149
|
+
// Be careful also that copy_cqes() will flush before entering to wait (it does):
|
|
150
|
+
// https://github.com/axboe/liburing/commit/35c199c48dfd54ad46b96e386882e7ac341314c5
|
|
151
|
+
error.CompletionQueueOvercommitted, error.SystemResources => {
|
|
152
|
+
try self.flush_completions(1, timeouts, etime);
|
|
153
|
+
continue;
|
|
154
|
+
},
|
|
155
|
+
else => return err,
|
|
156
|
+
};
|
|
157
|
+
break;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
fn enqueue(self: *IO, completion: *Completion) void {
|
|
162
|
+
const sqe = self.ring.get_sqe() catch |err| switch (err) {
|
|
163
|
+
error.SubmissionQueueFull => {
|
|
164
|
+
self.unqueued.push(completion);
|
|
165
|
+
return;
|
|
166
|
+
},
|
|
167
|
+
};
|
|
168
|
+
completion.prep(sqe);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/// This struct holds the data needed for a single io_uring operation
|
|
172
|
+
pub const Completion = struct {
|
|
173
|
+
io: *IO,
|
|
174
|
+
result: i32 = undefined,
|
|
175
|
+
next: ?*Completion = null,
|
|
176
|
+
operation: Operation,
|
|
177
|
+
context: ?*anyopaque,
|
|
178
|
+
callback: fn (context: ?*anyopaque, completion: *Completion, result: *const anyopaque) void,
|
|
179
|
+
|
|
180
|
+
fn prep(completion: *Completion, sqe: *io_uring_sqe) void {
|
|
181
|
+
switch (completion.operation) {
|
|
182
|
+
.accept => |*op| {
|
|
183
|
+
linux.io_uring_prep_accept(
|
|
184
|
+
sqe,
|
|
185
|
+
op.socket,
|
|
186
|
+
&op.address,
|
|
187
|
+
&op.address_size,
|
|
188
|
+
os.SOCK.CLOEXEC,
|
|
189
|
+
);
|
|
190
|
+
},
|
|
191
|
+
.close => |op| {
|
|
192
|
+
linux.io_uring_prep_close(sqe, op.fd);
|
|
193
|
+
},
|
|
194
|
+
.connect => |*op| {
|
|
195
|
+
linux.io_uring_prep_connect(
|
|
196
|
+
sqe,
|
|
197
|
+
op.socket,
|
|
198
|
+
&op.address.any,
|
|
199
|
+
op.address.getOsSockLen(),
|
|
200
|
+
);
|
|
201
|
+
},
|
|
202
|
+
.fsync => |op| {
|
|
203
|
+
linux.io_uring_prep_fsync(sqe, op.fd, 0);
|
|
204
|
+
},
|
|
205
|
+
.read => |op| {
|
|
206
|
+
linux.io_uring_prep_read(
|
|
207
|
+
sqe,
|
|
208
|
+
op.fd,
|
|
209
|
+
op.buffer[0..buffer_limit(op.buffer.len)],
|
|
210
|
+
op.offset,
|
|
211
|
+
);
|
|
212
|
+
},
|
|
213
|
+
.recv => |op| {
|
|
214
|
+
linux.io_uring_prep_recv(sqe, op.socket, op.buffer, os.MSG.NOSIGNAL);
|
|
215
|
+
},
|
|
216
|
+
.send => |op| {
|
|
217
|
+
linux.io_uring_prep_send(sqe, op.socket, op.buffer, os.MSG.NOSIGNAL);
|
|
218
|
+
},
|
|
219
|
+
.timeout => |*op| {
|
|
220
|
+
linux.io_uring_prep_timeout(sqe, &op.timespec, 0, 0);
|
|
221
|
+
},
|
|
222
|
+
.write => |op| {
|
|
223
|
+
linux.io_uring_prep_write(
|
|
224
|
+
sqe,
|
|
225
|
+
op.fd,
|
|
226
|
+
op.buffer[0..buffer_limit(op.buffer.len)],
|
|
227
|
+
op.offset,
|
|
228
|
+
);
|
|
229
|
+
},
|
|
230
|
+
}
|
|
231
|
+
sqe.user_data = @ptrToInt(completion);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
fn complete(completion: *Completion) void {
|
|
235
|
+
switch (completion.operation) {
|
|
236
|
+
.accept => {
|
|
237
|
+
const result = blk: {
|
|
238
|
+
if (completion.result < 0) {
|
|
239
|
+
const err = switch (@intToEnum(os.E, -completion.result)) {
|
|
240
|
+
.INTR => {
|
|
241
|
+
completion.io.enqueue(completion);
|
|
242
|
+
return;
|
|
243
|
+
},
|
|
244
|
+
.AGAIN => error.WouldBlock,
|
|
245
|
+
.BADF => error.FileDescriptorInvalid,
|
|
246
|
+
.CONNABORTED => error.ConnectionAborted,
|
|
247
|
+
.FAULT => unreachable,
|
|
248
|
+
.INVAL => error.SocketNotListening,
|
|
249
|
+
.MFILE => error.ProcessFdQuotaExceeded,
|
|
250
|
+
.NFILE => error.SystemFdQuotaExceeded,
|
|
251
|
+
.NOBUFS => error.SystemResources,
|
|
252
|
+
.NOMEM => error.SystemResources,
|
|
253
|
+
.NOTSOCK => error.FileDescriptorNotASocket,
|
|
254
|
+
.OPNOTSUPP => error.OperationNotSupported,
|
|
255
|
+
.PERM => error.PermissionDenied,
|
|
256
|
+
.PROTO => error.ProtocolFailure,
|
|
257
|
+
else => |errno| os.unexpectedErrno(errno),
|
|
258
|
+
};
|
|
259
|
+
break :blk err;
|
|
260
|
+
} else {
|
|
261
|
+
break :blk @intCast(os.socket_t, completion.result);
|
|
262
|
+
}
|
|
263
|
+
};
|
|
264
|
+
completion.callback(completion.context, completion, &result);
|
|
265
|
+
},
|
|
266
|
+
.close => {
|
|
267
|
+
const result = blk: {
|
|
268
|
+
if (completion.result < 0) {
|
|
269
|
+
const err = switch (@intToEnum(os.E, -completion.result)) {
|
|
270
|
+
.INTR => {}, // A success, see https://github.com/ziglang/zig/issues/2425
|
|
271
|
+
.BADF => error.FileDescriptorInvalid,
|
|
272
|
+
.DQUOT => error.DiskQuota,
|
|
273
|
+
.IO => error.InputOutput,
|
|
274
|
+
.NOSPC => error.NoSpaceLeft,
|
|
275
|
+
else => |errno| os.unexpectedErrno(errno),
|
|
276
|
+
};
|
|
277
|
+
break :blk err;
|
|
278
|
+
} else {
|
|
279
|
+
assert(completion.result == 0);
|
|
280
|
+
}
|
|
281
|
+
};
|
|
282
|
+
completion.callback(completion.context, completion, &result);
|
|
283
|
+
},
|
|
284
|
+
.connect => {
|
|
285
|
+
const result = blk: {
|
|
286
|
+
if (completion.result < 0) {
|
|
287
|
+
const err = switch (@intToEnum(os.E, -completion.result)) {
|
|
288
|
+
.INTR => {
|
|
289
|
+
completion.io.enqueue(completion);
|
|
290
|
+
return;
|
|
291
|
+
},
|
|
292
|
+
.ACCES => error.AccessDenied,
|
|
293
|
+
.ADDRINUSE => error.AddressInUse,
|
|
294
|
+
.ADDRNOTAVAIL => error.AddressNotAvailable,
|
|
295
|
+
.AFNOSUPPORT => error.AddressFamilyNotSupported,
|
|
296
|
+
.AGAIN, .INPROGRESS => error.WouldBlock,
|
|
297
|
+
.ALREADY => error.OpenAlreadyInProgress,
|
|
298
|
+
.BADF => error.FileDescriptorInvalid,
|
|
299
|
+
.CONNREFUSED => error.ConnectionRefused,
|
|
300
|
+
.CONNRESET => error.ConnectionResetByPeer,
|
|
301
|
+
.FAULT => unreachable,
|
|
302
|
+
.ISCONN => error.AlreadyConnected,
|
|
303
|
+
.NETUNREACH => error.NetworkUnreachable,
|
|
304
|
+
.NOENT => error.FileNotFound,
|
|
305
|
+
.NOTSOCK => error.FileDescriptorNotASocket,
|
|
306
|
+
.PERM => error.PermissionDenied,
|
|
307
|
+
.PROTOTYPE => error.ProtocolNotSupported,
|
|
308
|
+
.TIMEDOUT => error.ConnectionTimedOut,
|
|
309
|
+
else => |errno| os.unexpectedErrno(errno),
|
|
310
|
+
};
|
|
311
|
+
break :blk err;
|
|
312
|
+
} else {
|
|
313
|
+
assert(completion.result == 0);
|
|
314
|
+
}
|
|
315
|
+
};
|
|
316
|
+
completion.callback(completion.context, completion, &result);
|
|
317
|
+
},
|
|
318
|
+
.fsync => {
|
|
319
|
+
const result = blk: {
|
|
320
|
+
if (completion.result < 0) {
|
|
321
|
+
const err = switch (@intToEnum(os.E, -completion.result)) {
|
|
322
|
+
.INTR => {
|
|
323
|
+
completion.io.enqueue(completion);
|
|
324
|
+
return;
|
|
325
|
+
},
|
|
326
|
+
.BADF => error.FileDescriptorInvalid,
|
|
327
|
+
.DQUOT => error.DiskQuota,
|
|
328
|
+
.INVAL => error.ArgumentsInvalid,
|
|
329
|
+
.IO => error.InputOutput,
|
|
330
|
+
.NOSPC => error.NoSpaceLeft,
|
|
331
|
+
.ROFS => error.ReadOnlyFileSystem,
|
|
332
|
+
else => |errno| os.unexpectedErrno(errno),
|
|
333
|
+
};
|
|
334
|
+
break :blk err;
|
|
335
|
+
} else {
|
|
336
|
+
assert(completion.result == 0);
|
|
337
|
+
}
|
|
338
|
+
};
|
|
339
|
+
completion.callback(completion.context, completion, &result);
|
|
340
|
+
},
|
|
341
|
+
.read => {
|
|
342
|
+
const result = blk: {
|
|
343
|
+
if (completion.result < 0) {
|
|
344
|
+
const err = switch (@intToEnum(os.E, -completion.result)) {
|
|
345
|
+
.INTR => {
|
|
346
|
+
completion.io.enqueue(completion);
|
|
347
|
+
return;
|
|
348
|
+
},
|
|
349
|
+
.AGAIN => error.WouldBlock,
|
|
350
|
+
.BADF => error.NotOpenForReading,
|
|
351
|
+
.CONNRESET => error.ConnectionResetByPeer,
|
|
352
|
+
.FAULT => unreachable,
|
|
353
|
+
.INVAL => error.Alignment,
|
|
354
|
+
.IO => error.InputOutput,
|
|
355
|
+
.ISDIR => error.IsDir,
|
|
356
|
+
.NOBUFS => error.SystemResources,
|
|
357
|
+
.NOMEM => error.SystemResources,
|
|
358
|
+
.NXIO => error.Unseekable,
|
|
359
|
+
.OVERFLOW => error.Unseekable,
|
|
360
|
+
.SPIPE => error.Unseekable,
|
|
361
|
+
else => |errno| os.unexpectedErrno(errno),
|
|
362
|
+
};
|
|
363
|
+
break :blk err;
|
|
364
|
+
} else {
|
|
365
|
+
break :blk @intCast(usize, completion.result);
|
|
366
|
+
}
|
|
367
|
+
};
|
|
368
|
+
completion.callback(completion.context, completion, &result);
|
|
369
|
+
},
|
|
370
|
+
.recv => {
|
|
371
|
+
const result = blk: {
|
|
372
|
+
if (completion.result < 0) {
|
|
373
|
+
const err = switch (@intToEnum(os.E, -completion.result)) {
|
|
374
|
+
.INTR => {
|
|
375
|
+
completion.io.enqueue(completion);
|
|
376
|
+
return;
|
|
377
|
+
},
|
|
378
|
+
.AGAIN => error.WouldBlock,
|
|
379
|
+
.BADF => error.FileDescriptorInvalid,
|
|
380
|
+
.CONNREFUSED => error.ConnectionRefused,
|
|
381
|
+
.FAULT => unreachable,
|
|
382
|
+
.INVAL => unreachable,
|
|
383
|
+
.NOMEM => error.SystemResources,
|
|
384
|
+
.NOTCONN => error.SocketNotConnected,
|
|
385
|
+
.NOTSOCK => error.FileDescriptorNotASocket,
|
|
386
|
+
.CONNRESET => error.ConnectionResetByPeer,
|
|
387
|
+
else => |errno| os.unexpectedErrno(errno),
|
|
388
|
+
};
|
|
389
|
+
break :blk err;
|
|
390
|
+
} else {
|
|
391
|
+
break :blk @intCast(usize, completion.result);
|
|
392
|
+
}
|
|
393
|
+
};
|
|
394
|
+
completion.callback(completion.context, completion, &result);
|
|
395
|
+
},
|
|
396
|
+
.send => {
|
|
397
|
+
const result = blk: {
|
|
398
|
+
if (completion.result < 0) {
|
|
399
|
+
const err = switch (@intToEnum(os.E, -completion.result)) {
|
|
400
|
+
.INTR => {
|
|
401
|
+
completion.io.enqueue(completion);
|
|
402
|
+
return;
|
|
403
|
+
},
|
|
404
|
+
.ACCES => error.AccessDenied,
|
|
405
|
+
.AGAIN => error.WouldBlock,
|
|
406
|
+
.ALREADY => error.FastOpenAlreadyInProgress,
|
|
407
|
+
.AFNOSUPPORT => error.AddressFamilyNotSupported,
|
|
408
|
+
.BADF => error.FileDescriptorInvalid,
|
|
409
|
+
.CONNRESET => error.ConnectionResetByPeer,
|
|
410
|
+
.DESTADDRREQ => unreachable,
|
|
411
|
+
.FAULT => unreachable,
|
|
412
|
+
.INVAL => unreachable,
|
|
413
|
+
.ISCONN => unreachable,
|
|
414
|
+
.MSGSIZE => error.MessageTooBig,
|
|
415
|
+
.NOBUFS => error.SystemResources,
|
|
416
|
+
.NOMEM => error.SystemResources,
|
|
417
|
+
.NOTCONN => error.SocketNotConnected,
|
|
418
|
+
.NOTSOCK => error.FileDescriptorNotASocket,
|
|
419
|
+
.OPNOTSUPP => error.OperationNotSupported,
|
|
420
|
+
.PIPE => error.BrokenPipe,
|
|
421
|
+
else => |errno| os.unexpectedErrno(errno),
|
|
422
|
+
};
|
|
423
|
+
break :blk err;
|
|
424
|
+
} else {
|
|
425
|
+
break :blk @intCast(usize, completion.result);
|
|
426
|
+
}
|
|
427
|
+
};
|
|
428
|
+
completion.callback(completion.context, completion, &result);
|
|
429
|
+
},
|
|
430
|
+
.timeout => {
|
|
431
|
+
assert(completion.result < 0);
|
|
432
|
+
const result = switch (@intToEnum(os.E, -completion.result)) {
|
|
433
|
+
.INTR => {
|
|
434
|
+
completion.io.enqueue(completion);
|
|
435
|
+
return;
|
|
436
|
+
},
|
|
437
|
+
.CANCELED => error.Canceled,
|
|
438
|
+
.TIME => {}, // A success.
|
|
439
|
+
else => |errno| os.unexpectedErrno(errno),
|
|
440
|
+
};
|
|
441
|
+
completion.callback(completion.context, completion, &result);
|
|
442
|
+
},
|
|
443
|
+
.write => {
|
|
444
|
+
const result = blk: {
|
|
445
|
+
if (completion.result < 0) {
|
|
446
|
+
const err = switch (@intToEnum(os.E, -completion.result)) {
|
|
447
|
+
.INTR => {
|
|
448
|
+
completion.io.enqueue(completion);
|
|
449
|
+
return;
|
|
450
|
+
},
|
|
451
|
+
.AGAIN => error.WouldBlock,
|
|
452
|
+
.BADF => error.NotOpenForWriting,
|
|
453
|
+
.DESTADDRREQ => error.NotConnected,
|
|
454
|
+
.DQUOT => error.DiskQuota,
|
|
455
|
+
.FAULT => unreachable,
|
|
456
|
+
.FBIG => error.FileTooBig,
|
|
457
|
+
.INVAL => error.Alignment,
|
|
458
|
+
.IO => error.InputOutput,
|
|
459
|
+
.NOSPC => error.NoSpaceLeft,
|
|
460
|
+
.NXIO => error.Unseekable,
|
|
461
|
+
.OVERFLOW => error.Unseekable,
|
|
462
|
+
.PERM => error.AccessDenied,
|
|
463
|
+
.PIPE => error.BrokenPipe,
|
|
464
|
+
.SPIPE => error.Unseekable,
|
|
465
|
+
else => |errno| os.unexpectedErrno(errno),
|
|
466
|
+
};
|
|
467
|
+
break :blk err;
|
|
468
|
+
} else {
|
|
469
|
+
break :blk @intCast(usize, completion.result);
|
|
470
|
+
}
|
|
471
|
+
};
|
|
472
|
+
completion.callback(completion.context, completion, &result);
|
|
473
|
+
},
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
};
|
|
477
|
+
|
|
478
|
+
/// This union encodes the set of operations supported as well as their arguments.
|
|
479
|
+
const Operation = union(enum) {
|
|
480
|
+
accept: struct {
|
|
481
|
+
socket: os.socket_t,
|
|
482
|
+
address: os.sockaddr = undefined,
|
|
483
|
+
address_size: os.socklen_t = @sizeOf(os.sockaddr),
|
|
484
|
+
},
|
|
485
|
+
close: struct {
|
|
486
|
+
fd: os.fd_t,
|
|
487
|
+
},
|
|
488
|
+
connect: struct {
|
|
489
|
+
socket: os.socket_t,
|
|
490
|
+
address: std.net.Address,
|
|
491
|
+
},
|
|
492
|
+
fsync: struct {
|
|
493
|
+
fd: os.fd_t,
|
|
494
|
+
},
|
|
495
|
+
read: struct {
|
|
496
|
+
fd: os.fd_t,
|
|
497
|
+
buffer: []u8,
|
|
498
|
+
offset: u64,
|
|
499
|
+
},
|
|
500
|
+
recv: struct {
|
|
501
|
+
socket: os.socket_t,
|
|
502
|
+
buffer: []u8,
|
|
503
|
+
},
|
|
504
|
+
send: struct {
|
|
505
|
+
socket: os.socket_t,
|
|
506
|
+
buffer: []const u8,
|
|
507
|
+
},
|
|
508
|
+
timeout: struct {
|
|
509
|
+
timespec: os.linux.kernel_timespec,
|
|
510
|
+
},
|
|
511
|
+
write: struct {
|
|
512
|
+
fd: os.fd_t,
|
|
513
|
+
buffer: []const u8,
|
|
514
|
+
offset: u64,
|
|
515
|
+
},
|
|
516
|
+
};
|
|
517
|
+
|
|
518
|
+
pub const AcceptError = error{
|
|
519
|
+
WouldBlock,
|
|
520
|
+
FileDescriptorInvalid,
|
|
521
|
+
ConnectionAborted,
|
|
522
|
+
SocketNotListening,
|
|
523
|
+
ProcessFdQuotaExceeded,
|
|
524
|
+
SystemFdQuotaExceeded,
|
|
525
|
+
SystemResources,
|
|
526
|
+
FileDescriptorNotASocket,
|
|
527
|
+
OperationNotSupported,
|
|
528
|
+
PermissionDenied,
|
|
529
|
+
ProtocolFailure,
|
|
530
|
+
} || os.UnexpectedError;
|
|
531
|
+
|
|
532
|
+
pub fn accept(
|
|
533
|
+
self: *IO,
|
|
534
|
+
comptime Context: type,
|
|
535
|
+
context: Context,
|
|
536
|
+
comptime callback: fn (
|
|
537
|
+
context: Context,
|
|
538
|
+
completion: *Completion,
|
|
539
|
+
result: AcceptError!os.socket_t,
|
|
540
|
+
) void,
|
|
541
|
+
completion: *Completion,
|
|
542
|
+
socket: os.socket_t,
|
|
543
|
+
) void {
|
|
544
|
+
completion.* = .{
|
|
545
|
+
.io = self,
|
|
546
|
+
.context = context,
|
|
547
|
+
.callback = struct {
|
|
548
|
+
fn wrapper(ctx: ?*anyopaque, comp: *Completion, res: *const anyopaque) void {
|
|
549
|
+
callback(
|
|
550
|
+
@intToPtr(Context, @ptrToInt(ctx)),
|
|
551
|
+
comp,
|
|
552
|
+
@intToPtr(*const AcceptError!os.socket_t, @ptrToInt(res)).*,
|
|
553
|
+
);
|
|
554
|
+
}
|
|
555
|
+
}.wrapper,
|
|
556
|
+
.operation = .{
|
|
557
|
+
.accept = .{
|
|
558
|
+
.socket = socket,
|
|
559
|
+
.address = undefined,
|
|
560
|
+
.address_size = @sizeOf(os.sockaddr),
|
|
561
|
+
},
|
|
562
|
+
},
|
|
563
|
+
};
|
|
564
|
+
self.enqueue(completion);
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
pub const CloseError = error{
|
|
568
|
+
FileDescriptorInvalid,
|
|
569
|
+
DiskQuota,
|
|
570
|
+
InputOutput,
|
|
571
|
+
NoSpaceLeft,
|
|
572
|
+
} || os.UnexpectedError;
|
|
573
|
+
|
|
574
|
+
pub fn close(
|
|
575
|
+
self: *IO,
|
|
576
|
+
comptime Context: type,
|
|
577
|
+
context: Context,
|
|
578
|
+
comptime callback: fn (
|
|
579
|
+
context: Context,
|
|
580
|
+
completion: *Completion,
|
|
581
|
+
result: CloseError!void,
|
|
582
|
+
) void,
|
|
583
|
+
completion: *Completion,
|
|
584
|
+
fd: os.fd_t,
|
|
585
|
+
) void {
|
|
586
|
+
completion.* = .{
|
|
587
|
+
.io = self,
|
|
588
|
+
.context = context,
|
|
589
|
+
.callback = struct {
|
|
590
|
+
fn wrapper(ctx: ?*anyopaque, comp: *Completion, res: *const anyopaque) void {
|
|
591
|
+
callback(
|
|
592
|
+
@intToPtr(Context, @ptrToInt(ctx)),
|
|
593
|
+
comp,
|
|
594
|
+
@intToPtr(*const CloseError!void, @ptrToInt(res)).*,
|
|
595
|
+
);
|
|
596
|
+
}
|
|
597
|
+
}.wrapper,
|
|
598
|
+
.operation = .{
|
|
599
|
+
.close = .{ .fd = fd },
|
|
600
|
+
},
|
|
601
|
+
};
|
|
602
|
+
self.enqueue(completion);
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
pub const ConnectError = error{
|
|
606
|
+
AccessDenied,
|
|
607
|
+
AddressInUse,
|
|
608
|
+
AddressNotAvailable,
|
|
609
|
+
AddressFamilyNotSupported,
|
|
610
|
+
WouldBlock,
|
|
611
|
+
OpenAlreadyInProgress,
|
|
612
|
+
FileDescriptorInvalid,
|
|
613
|
+
ConnectionRefused,
|
|
614
|
+
AlreadyConnected,
|
|
615
|
+
NetworkUnreachable,
|
|
616
|
+
FileNotFound,
|
|
617
|
+
FileDescriptorNotASocket,
|
|
618
|
+
PermissionDenied,
|
|
619
|
+
ProtocolNotSupported,
|
|
620
|
+
ConnectionTimedOut,
|
|
621
|
+
} || os.UnexpectedError;
|
|
622
|
+
|
|
623
|
+
pub fn connect(
|
|
624
|
+
self: *IO,
|
|
625
|
+
comptime Context: type,
|
|
626
|
+
context: Context,
|
|
627
|
+
comptime callback: fn (
|
|
628
|
+
context: Context,
|
|
629
|
+
completion: *Completion,
|
|
630
|
+
result: ConnectError!void,
|
|
631
|
+
) void,
|
|
632
|
+
completion: *Completion,
|
|
633
|
+
socket: os.socket_t,
|
|
634
|
+
address: std.net.Address,
|
|
635
|
+
) void {
|
|
636
|
+
completion.* = .{
|
|
637
|
+
.io = self,
|
|
638
|
+
.context = context,
|
|
639
|
+
.callback = struct {
|
|
640
|
+
fn wrapper(ctx: ?*anyopaque, comp: *Completion, res: *const anyopaque) void {
|
|
641
|
+
callback(
|
|
642
|
+
@intToPtr(Context, @ptrToInt(ctx)),
|
|
643
|
+
comp,
|
|
644
|
+
@intToPtr(*const ConnectError!void, @ptrToInt(res)).*,
|
|
645
|
+
);
|
|
646
|
+
}
|
|
647
|
+
}.wrapper,
|
|
648
|
+
.operation = .{
|
|
649
|
+
.connect = .{
|
|
650
|
+
.socket = socket,
|
|
651
|
+
.address = address,
|
|
652
|
+
},
|
|
653
|
+
},
|
|
654
|
+
};
|
|
655
|
+
self.enqueue(completion);
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
pub const FsyncError = error{
|
|
659
|
+
FileDescriptorInvalid,
|
|
660
|
+
DiskQuota,
|
|
661
|
+
ArgumentsInvalid,
|
|
662
|
+
InputOutput,
|
|
663
|
+
NoSpaceLeft,
|
|
664
|
+
ReadOnlyFileSystem,
|
|
665
|
+
} || os.UnexpectedError;
|
|
666
|
+
|
|
667
|
+
pub fn fsync(
|
|
668
|
+
self: *IO,
|
|
669
|
+
comptime Context: type,
|
|
670
|
+
context: Context,
|
|
671
|
+
comptime callback: fn (
|
|
672
|
+
context: Context,
|
|
673
|
+
completion: *Completion,
|
|
674
|
+
result: FsyncError!void,
|
|
675
|
+
) void,
|
|
676
|
+
completion: *Completion,
|
|
677
|
+
fd: os.fd_t,
|
|
678
|
+
) void {
|
|
679
|
+
completion.* = .{
|
|
680
|
+
.io = self,
|
|
681
|
+
.context = context,
|
|
682
|
+
.callback = struct {
|
|
683
|
+
fn wrapper(ctx: ?*anyopaque, comp: *Completion, res: *const anyopaque) void {
|
|
684
|
+
callback(
|
|
685
|
+
@intToPtr(Context, @ptrToInt(ctx)),
|
|
686
|
+
comp,
|
|
687
|
+
@intToPtr(*const FsyncError!void, @ptrToInt(res)).*,
|
|
688
|
+
);
|
|
689
|
+
}
|
|
690
|
+
}.wrapper,
|
|
691
|
+
.operation = .{
|
|
692
|
+
.fsync = .{
|
|
693
|
+
.fd = fd,
|
|
694
|
+
},
|
|
695
|
+
},
|
|
696
|
+
};
|
|
697
|
+
self.enqueue(completion);
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
pub const ReadError = error{
|
|
701
|
+
WouldBlock,
|
|
702
|
+
NotOpenForReading,
|
|
703
|
+
ConnectionResetByPeer,
|
|
704
|
+
Alignment,
|
|
705
|
+
InputOutput,
|
|
706
|
+
IsDir,
|
|
707
|
+
SystemResources,
|
|
708
|
+
Unseekable,
|
|
709
|
+
} || os.UnexpectedError;
|
|
710
|
+
|
|
711
|
+
pub fn read(
|
|
712
|
+
self: *IO,
|
|
713
|
+
comptime Context: type,
|
|
714
|
+
context: Context,
|
|
715
|
+
comptime callback: fn (
|
|
716
|
+
context: Context,
|
|
717
|
+
completion: *Completion,
|
|
718
|
+
result: ReadError!usize,
|
|
719
|
+
) void,
|
|
720
|
+
completion: *Completion,
|
|
721
|
+
fd: os.fd_t,
|
|
722
|
+
buffer: []u8,
|
|
723
|
+
offset: u64,
|
|
724
|
+
) void {
|
|
725
|
+
completion.* = .{
|
|
726
|
+
.io = self,
|
|
727
|
+
.context = context,
|
|
728
|
+
.callback = struct {
|
|
729
|
+
fn wrapper(ctx: ?*anyopaque, comp: *Completion, res: *const anyopaque) void {
|
|
730
|
+
callback(
|
|
731
|
+
@intToPtr(Context, @ptrToInt(ctx)),
|
|
732
|
+
comp,
|
|
733
|
+
@intToPtr(*const ReadError!usize, @ptrToInt(res)).*,
|
|
734
|
+
);
|
|
735
|
+
}
|
|
736
|
+
}.wrapper,
|
|
737
|
+
.operation = .{
|
|
738
|
+
.read = .{
|
|
739
|
+
.fd = fd,
|
|
740
|
+
.buffer = buffer,
|
|
741
|
+
.offset = offset,
|
|
742
|
+
},
|
|
743
|
+
},
|
|
744
|
+
};
|
|
745
|
+
self.enqueue(completion);
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
pub const RecvError = error{
|
|
749
|
+
WouldBlock,
|
|
750
|
+
FileDescriptorInvalid,
|
|
751
|
+
ConnectionRefused,
|
|
752
|
+
SystemResources,
|
|
753
|
+
SocketNotConnected,
|
|
754
|
+
FileDescriptorNotASocket,
|
|
755
|
+
} || os.UnexpectedError;
|
|
756
|
+
|
|
757
|
+
pub fn recv(
|
|
758
|
+
self: *IO,
|
|
759
|
+
comptime Context: type,
|
|
760
|
+
context: Context,
|
|
761
|
+
comptime callback: fn (
|
|
762
|
+
context: Context,
|
|
763
|
+
completion: *Completion,
|
|
764
|
+
result: RecvError!usize,
|
|
765
|
+
) void,
|
|
766
|
+
completion: *Completion,
|
|
767
|
+
socket: os.socket_t,
|
|
768
|
+
buffer: []u8,
|
|
769
|
+
) void {
|
|
770
|
+
completion.* = .{
|
|
771
|
+
.io = self,
|
|
772
|
+
.context = context,
|
|
773
|
+
.callback = struct {
|
|
774
|
+
fn wrapper(ctx: ?*anyopaque, comp: *Completion, res: *const anyopaque) void {
|
|
775
|
+
callback(
|
|
776
|
+
@intToPtr(Context, @ptrToInt(ctx)),
|
|
777
|
+
comp,
|
|
778
|
+
@intToPtr(*const RecvError!usize, @ptrToInt(res)).*,
|
|
779
|
+
);
|
|
780
|
+
}
|
|
781
|
+
}.wrapper,
|
|
782
|
+
.operation = .{
|
|
783
|
+
.recv = .{
|
|
784
|
+
.socket = socket,
|
|
785
|
+
.buffer = buffer,
|
|
786
|
+
},
|
|
787
|
+
},
|
|
788
|
+
};
|
|
789
|
+
self.enqueue(completion);
|
|
790
|
+
}
|
|
791
|
+
|
|
792
|
+
pub const SendError = error{
|
|
793
|
+
AccessDenied,
|
|
794
|
+
WouldBlock,
|
|
795
|
+
FastOpenAlreadyInProgress,
|
|
796
|
+
AddressFamilyNotSupported,
|
|
797
|
+
FileDescriptorInvalid,
|
|
798
|
+
ConnectionResetByPeer,
|
|
799
|
+
MessageTooBig,
|
|
800
|
+
SystemResources,
|
|
801
|
+
SocketNotConnected,
|
|
802
|
+
FileDescriptorNotASocket,
|
|
803
|
+
OperationNotSupported,
|
|
804
|
+
BrokenPipe,
|
|
805
|
+
} || os.UnexpectedError;
|
|
806
|
+
|
|
807
|
+
pub fn send(
|
|
808
|
+
self: *IO,
|
|
809
|
+
comptime Context: type,
|
|
810
|
+
context: Context,
|
|
811
|
+
comptime callback: fn (
|
|
812
|
+
context: Context,
|
|
813
|
+
completion: *Completion,
|
|
814
|
+
result: SendError!usize,
|
|
815
|
+
) void,
|
|
816
|
+
completion: *Completion,
|
|
817
|
+
socket: os.socket_t,
|
|
818
|
+
buffer: []const u8,
|
|
819
|
+
) void {
|
|
820
|
+
completion.* = .{
|
|
821
|
+
.io = self,
|
|
822
|
+
.context = context,
|
|
823
|
+
.callback = struct {
|
|
824
|
+
fn wrapper(ctx: ?*anyopaque, comp: *Completion, res: *const anyopaque) void {
|
|
825
|
+
callback(
|
|
826
|
+
@intToPtr(Context, @ptrToInt(ctx)),
|
|
827
|
+
comp,
|
|
828
|
+
@intToPtr(*const SendError!usize, @ptrToInt(res)).*,
|
|
829
|
+
);
|
|
830
|
+
}
|
|
831
|
+
}.wrapper,
|
|
832
|
+
.operation = .{
|
|
833
|
+
.send = .{
|
|
834
|
+
.socket = socket,
|
|
835
|
+
.buffer = buffer,
|
|
836
|
+
},
|
|
837
|
+
},
|
|
838
|
+
};
|
|
839
|
+
self.enqueue(completion);
|
|
840
|
+
}
|
|
841
|
+
|
|
842
|
+
pub const TimeoutError = error{Canceled} || os.UnexpectedError;
|
|
843
|
+
|
|
844
|
+
pub fn timeout(
|
|
845
|
+
self: *IO,
|
|
846
|
+
comptime Context: type,
|
|
847
|
+
context: Context,
|
|
848
|
+
comptime callback: fn (
|
|
849
|
+
context: Context,
|
|
850
|
+
completion: *Completion,
|
|
851
|
+
result: TimeoutError!void,
|
|
852
|
+
) void,
|
|
853
|
+
completion: *Completion,
|
|
854
|
+
nanoseconds: u63,
|
|
855
|
+
) void {
|
|
856
|
+
completion.* = .{
|
|
857
|
+
.io = self,
|
|
858
|
+
.context = context,
|
|
859
|
+
.callback = struct {
|
|
860
|
+
fn wrapper(ctx: ?*anyopaque, comp: *Completion, res: *const anyopaque) void {
|
|
861
|
+
callback(
|
|
862
|
+
@intToPtr(Context, @ptrToInt(ctx)),
|
|
863
|
+
comp,
|
|
864
|
+
@intToPtr(*const TimeoutError!void, @ptrToInt(res)).*,
|
|
865
|
+
);
|
|
866
|
+
}
|
|
867
|
+
}.wrapper,
|
|
868
|
+
.operation = .{
|
|
869
|
+
.timeout = .{
|
|
870
|
+
.timespec = .{ .tv_sec = 0, .tv_nsec = nanoseconds },
|
|
871
|
+
},
|
|
872
|
+
},
|
|
873
|
+
};
|
|
874
|
+
self.enqueue(completion);
|
|
875
|
+
}
|
|
876
|
+
|
|
877
|
+
pub const WriteError = error{
|
|
878
|
+
WouldBlock,
|
|
879
|
+
NotOpenForWriting,
|
|
880
|
+
NotConnected,
|
|
881
|
+
DiskQuota,
|
|
882
|
+
FileTooBig,
|
|
883
|
+
Alignment,
|
|
884
|
+
InputOutput,
|
|
885
|
+
NoSpaceLeft,
|
|
886
|
+
Unseekable,
|
|
887
|
+
AccessDenied,
|
|
888
|
+
BrokenPipe,
|
|
889
|
+
} || os.UnexpectedError;
|
|
890
|
+
|
|
891
|
+
pub fn write(
|
|
892
|
+
self: *IO,
|
|
893
|
+
comptime Context: type,
|
|
894
|
+
context: Context,
|
|
895
|
+
comptime callback: fn (
|
|
896
|
+
context: Context,
|
|
897
|
+
completion: *Completion,
|
|
898
|
+
result: WriteError!usize,
|
|
899
|
+
) void,
|
|
900
|
+
completion: *Completion,
|
|
901
|
+
fd: os.fd_t,
|
|
902
|
+
buffer: []const u8,
|
|
903
|
+
offset: u64,
|
|
904
|
+
) void {
|
|
905
|
+
_ = callback;
|
|
906
|
+
|
|
907
|
+
completion.* = .{
|
|
908
|
+
.io = self,
|
|
909
|
+
.context = context,
|
|
910
|
+
.callback = struct {
|
|
911
|
+
fn wrapper(ctx: ?*anyopaque, comp: *Completion, res: *const anyopaque) void {
|
|
912
|
+
callback(
|
|
913
|
+
@intToPtr(Context, @ptrToInt(ctx)),
|
|
914
|
+
comp,
|
|
915
|
+
@intToPtr(*const WriteError!usize, @ptrToInt(res)).*,
|
|
916
|
+
);
|
|
917
|
+
}
|
|
918
|
+
}.wrapper,
|
|
919
|
+
.operation = .{
|
|
920
|
+
.write = .{
|
|
921
|
+
.fd = fd,
|
|
922
|
+
.buffer = buffer,
|
|
923
|
+
.offset = offset,
|
|
924
|
+
},
|
|
925
|
+
},
|
|
926
|
+
};
|
|
927
|
+
self.enqueue(completion);
|
|
928
|
+
}
|
|
929
|
+
|
|
930
|
+
pub fn openSocket(family: u32, sock_type: u32, protocol: u32) !os.socket_t {
|
|
931
|
+
return os.socket(family, sock_type, protocol);
|
|
932
|
+
}
|
|
933
|
+
};
|