tigerbeetle-node 0.5.0 → 0.5.1
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/package.json +2 -2
- package/scripts/postinstall.sh +2 -2
- package/src/node.zig +17 -15
- package/src/tigerbeetle/scripts/install.sh +1 -1
- package/src/tigerbeetle/scripts/install_zig.bat +3 -3
- package/src/tigerbeetle/scripts/install_zig.sh +4 -2
- package/src/tigerbeetle/scripts/lint.zig +8 -2
- package/src/tigerbeetle/src/benchmark.zig +8 -6
- package/src/tigerbeetle/src/cli.zig +6 -4
- package/src/tigerbeetle/src/config.zig +2 -2
- package/src/tigerbeetle/src/demo.zig +119 -97
- 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 +2 -3
- package/src/tigerbeetle/src/io/benchmark.zig +238 -0
- package/src/tigerbeetle/src/{io_darwin.zig → io/darwin.zig} +88 -127
- 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 -54
- package/src/tigerbeetle/src/message_pool.zig +3 -2
- package/src/tigerbeetle/src/simulator.zig +41 -37
- package/src/tigerbeetle/src/state_machine.zig +58 -27
- 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 +1 -1
- package/src/tigerbeetle/src/test/state_machine.zig +4 -0
- package/src/tigerbeetle/src/test/storage.zig +23 -19
- package/src/tigerbeetle/src/test/time.zig +2 -2
- package/src/tigerbeetle/src/tigerbeetle.zig +6 -128
- package/src/tigerbeetle/src/time.zig +6 -5
- package/src/tigerbeetle/src/vsr/client.zig +7 -7
- 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 +49 -46
- package/src/tigerbeetle/src/vsr.zig +24 -20
- package/src/translate.zig +55 -55
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
const std = @import("std");
|
|
2
|
+
const os = std.os;
|
|
3
|
+
const assert = std.debug.assert;
|
|
4
|
+
|
|
5
|
+
const Time = @import("../time.zig").Time;
|
|
6
|
+
const IO = @import("../io.zig").IO;
|
|
7
|
+
|
|
8
|
+
// 1 MB: larger than socket buffer so forces io_pending on darwin
|
|
9
|
+
// Configure this value to smaller amounts to test IO scheduling overhead
|
|
10
|
+
const buffer_size = 1 * 1024 * 1024;
|
|
11
|
+
|
|
12
|
+
// max time for the benchmark to run
|
|
13
|
+
const run_duration = 1 * std.time.ns_per_s;
|
|
14
|
+
|
|
15
|
+
pub fn main() !void {
|
|
16
|
+
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
|
17
|
+
const allocator = &gpa.allocator;
|
|
18
|
+
defer {
|
|
19
|
+
const leaks = gpa.deinit();
|
|
20
|
+
assert(!leaks);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const buffer = try allocator.alloc(u8, buffer_size * 2);
|
|
24
|
+
defer allocator.free(buffer);
|
|
25
|
+
std.mem.set(u8, buffer, 0);
|
|
26
|
+
|
|
27
|
+
var timer = Time{};
|
|
28
|
+
const started = timer.monotonic();
|
|
29
|
+
var self = Context{
|
|
30
|
+
.io = try IO.init(32, 0),
|
|
31
|
+
.timer = &timer,
|
|
32
|
+
.started = started,
|
|
33
|
+
.current = started,
|
|
34
|
+
.tx = .{ .buffer = buffer[0 * buffer_size ..][0..buffer_size] },
|
|
35
|
+
.rx = .{ .buffer = buffer[1 * buffer_size ..][0..buffer_size] },
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
defer {
|
|
39
|
+
self.io.deinit();
|
|
40
|
+
const elapsed_ns = self.current - started;
|
|
41
|
+
const transferred_mb = @intToFloat(f64, self.transferred) / 1024 / 1024;
|
|
42
|
+
|
|
43
|
+
std.debug.print("IO throughput test: took {}ms @ {d:.2} MB/s\n", .{
|
|
44
|
+
elapsed_ns / std.time.ns_per_ms,
|
|
45
|
+
transferred_mb / (@intToFloat(f64, elapsed_ns) / std.time.ns_per_s),
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Setup the server socket
|
|
50
|
+
self.server.fd = try IO.openSocket(os.AF_INET, os.SOCK_STREAM, os.IPPROTO_TCP);
|
|
51
|
+
defer os.closeSocket(self.server.fd);
|
|
52
|
+
|
|
53
|
+
const address = try std.net.Address.parseIp4("127.0.0.1", 3131);
|
|
54
|
+
try os.setsockopt(
|
|
55
|
+
self.server.fd,
|
|
56
|
+
os.SOL_SOCKET,
|
|
57
|
+
os.SO_REUSEADDR,
|
|
58
|
+
&std.mem.toBytes(@as(c_int, 1)),
|
|
59
|
+
);
|
|
60
|
+
try os.bind(self.server.fd, &address.any, address.getOsSockLen());
|
|
61
|
+
try os.listen(self.server.fd, 1);
|
|
62
|
+
|
|
63
|
+
// Start accepting the client
|
|
64
|
+
self.io.accept(
|
|
65
|
+
*Context,
|
|
66
|
+
&self,
|
|
67
|
+
Context.on_accept,
|
|
68
|
+
&self.server.completion,
|
|
69
|
+
self.server.fd,
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
// Setup the client connection
|
|
73
|
+
self.tx.socket.fd = try IO.openSocket(os.AF_INET, os.SOCK_STREAM, os.IPPROTO_TCP);
|
|
74
|
+
defer os.closeSocket(self.tx.socket.fd);
|
|
75
|
+
|
|
76
|
+
self.io.connect(
|
|
77
|
+
*Context,
|
|
78
|
+
&self,
|
|
79
|
+
Context.on_connect,
|
|
80
|
+
&self.tx.socket.completion,
|
|
81
|
+
self.tx.socket.fd,
|
|
82
|
+
address,
|
|
83
|
+
);
|
|
84
|
+
|
|
85
|
+
// Run the IO loop, calling either tick() or run_for_ns() at "pseudo-random"
|
|
86
|
+
// to benchmark each io-driving execution path
|
|
87
|
+
var tick: usize = 0xdeadbeef;
|
|
88
|
+
while (self.is_running()) : (tick +%= 1) {
|
|
89
|
+
if (tick % 61 == 0) {
|
|
90
|
+
const timeout_ns = tick % (10 * std.time.ns_per_ms);
|
|
91
|
+
try self.io.run_for_ns(@intCast(u63, timeout_ns));
|
|
92
|
+
} else {
|
|
93
|
+
try self.io.tick();
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Assert that everything is connected
|
|
98
|
+
assert(self.server.fd != -1);
|
|
99
|
+
assert(self.tx.socket.fd != -1);
|
|
100
|
+
assert(self.rx.socket.fd != -1);
|
|
101
|
+
|
|
102
|
+
// Close the accepted client socket.
|
|
103
|
+
// The actual client socket + server socket are closed by defer
|
|
104
|
+
os.closeSocket(self.rx.socket.fd);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const Context = struct {
|
|
108
|
+
io: IO,
|
|
109
|
+
tx: Pipe,
|
|
110
|
+
rx: Pipe,
|
|
111
|
+
timer: *Time,
|
|
112
|
+
started: u64,
|
|
113
|
+
current: u64,
|
|
114
|
+
server: Socket = .{},
|
|
115
|
+
transferred: u64 = 0,
|
|
116
|
+
|
|
117
|
+
const Socket = struct {
|
|
118
|
+
fd: os.socket_t = -1,
|
|
119
|
+
completion: IO.Completion = undefined,
|
|
120
|
+
};
|
|
121
|
+
const Pipe = struct {
|
|
122
|
+
socket: Socket = .{},
|
|
123
|
+
buffer: []u8,
|
|
124
|
+
transferred: usize = 0,
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
fn is_running(self: Context) bool {
|
|
128
|
+
// Make sure that we're connected
|
|
129
|
+
if (self.rx.socket.fd == -1) return true;
|
|
130
|
+
|
|
131
|
+
// Make sure that we haven't run too long as configured
|
|
132
|
+
const elapsed = self.current - self.started;
|
|
133
|
+
return elapsed < run_duration;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
fn on_accept(
|
|
137
|
+
self: *Context,
|
|
138
|
+
completion: *IO.Completion,
|
|
139
|
+
result: IO.AcceptError!os.socket_t,
|
|
140
|
+
) void {
|
|
141
|
+
assert(self.rx.socket.fd == -1);
|
|
142
|
+
assert(&self.server.completion == completion);
|
|
143
|
+
self.rx.socket.fd = result catch |err| std.debug.panic("accept error {}", .{err});
|
|
144
|
+
|
|
145
|
+
// Start reading data from the accepted client socket
|
|
146
|
+
assert(self.rx.transferred == 0);
|
|
147
|
+
self.do_transfer("rx", .read, 0);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
fn on_connect(
|
|
151
|
+
self: *Context,
|
|
152
|
+
completion: *IO.Completion,
|
|
153
|
+
result: IO.ConnectError!void,
|
|
154
|
+
) void {
|
|
155
|
+
assert(self.tx.socket.fd != -1);
|
|
156
|
+
assert(&self.tx.socket.completion == completion);
|
|
157
|
+
|
|
158
|
+
// Start sending data to the server's accepted client
|
|
159
|
+
assert(self.tx.transferred == 0);
|
|
160
|
+
self.do_transfer("tx", .write, 0);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const TransferType = enum {
|
|
164
|
+
read = 0,
|
|
165
|
+
write = 1,
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
fn do_transfer(
|
|
169
|
+
self: *Context,
|
|
170
|
+
comptime pipe_name: []const u8,
|
|
171
|
+
comptime transfer_type: TransferType,
|
|
172
|
+
bytes: usize,
|
|
173
|
+
) void {
|
|
174
|
+
// The type of IO to perform and what type of IO to perform next (after the current one completes).
|
|
175
|
+
const transfer_info = switch (transfer_type) {
|
|
176
|
+
.read => .{
|
|
177
|
+
.IoError = IO.RecvError,
|
|
178
|
+
.io_func = "recv",
|
|
179
|
+
.next = TransferType.write,
|
|
180
|
+
},
|
|
181
|
+
.write => .{
|
|
182
|
+
.IoError = IO.SendError,
|
|
183
|
+
.io_func = "send",
|
|
184
|
+
.next = TransferType.read,
|
|
185
|
+
},
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
assert(bytes <= buffer_size);
|
|
189
|
+
self.transferred += bytes;
|
|
190
|
+
|
|
191
|
+
// Check in with the benchmark timer to stop sending/receiving data
|
|
192
|
+
self.current = self.timer.monotonic();
|
|
193
|
+
if (!self.is_running())
|
|
194
|
+
return;
|
|
195
|
+
|
|
196
|
+
// Select which connection (tx or rx) depending on the type of transfer
|
|
197
|
+
const pipe = &@field(self, pipe_name);
|
|
198
|
+
pipe.transferred += bytes;
|
|
199
|
+
assert(pipe.transferred <= pipe.buffer.len);
|
|
200
|
+
|
|
201
|
+
// There's still more data to transfer on the connection
|
|
202
|
+
if (pipe.transferred < pipe.buffer.len) {
|
|
203
|
+
// Callback which calls this function again when data is transferred.
|
|
204
|
+
// Effectively loops back above.
|
|
205
|
+
const on_transfer = struct {
|
|
206
|
+
fn on_transfer(
|
|
207
|
+
_self: *Context,
|
|
208
|
+
completion: *IO.Completion,
|
|
209
|
+
result: transfer_info.IoError!usize,
|
|
210
|
+
) void {
|
|
211
|
+
const _bytes = result catch |err| {
|
|
212
|
+
std.debug.panic("{s} error: {}", .{ transfer_info.io_func, err });
|
|
213
|
+
};
|
|
214
|
+
assert(&@field(_self, pipe_name).socket.completion == completion);
|
|
215
|
+
_self.do_transfer(pipe_name, transfer_type, _bytes);
|
|
216
|
+
}
|
|
217
|
+
}.on_transfer;
|
|
218
|
+
|
|
219
|
+
// Perform the IO with the callback for the completion
|
|
220
|
+
return @field(self.io, transfer_info.io_func)(
|
|
221
|
+
*Context,
|
|
222
|
+
self,
|
|
223
|
+
on_transfer,
|
|
224
|
+
&pipe.socket.completion,
|
|
225
|
+
pipe.socket.fd,
|
|
226
|
+
pipe.buffer[pipe.transferred..],
|
|
227
|
+
);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// This transfer type completed transferring all the bytes.
|
|
231
|
+
// Now, switch the transfer type (transfer_info.next).
|
|
232
|
+
// This means if we read to the buffer, now we write it out.
|
|
233
|
+
// Inversely, if we wrote the buffer, now we read it back.
|
|
234
|
+
// This is basically a modified echo benchmark.
|
|
235
|
+
pipe.transferred = 0;
|
|
236
|
+
self.do_transfer(pipe_name, transfer_info.next, 0);
|
|
237
|
+
}
|
|
238
|
+
};
|
|
@@ -1,19 +1,24 @@
|
|
|
1
1
|
const std = @import("std");
|
|
2
2
|
const os = std.os;
|
|
3
|
+
const mem = std.mem;
|
|
3
4
|
const assert = std.debug.assert;
|
|
4
5
|
|
|
5
|
-
const FIFO = @import("fifo.zig").FIFO;
|
|
6
|
-
const Time = @import("time.zig").Time;
|
|
7
|
-
const buffer_limit = @import("io.zig").buffer_limit;
|
|
6
|
+
const FIFO = @import("../fifo.zig").FIFO;
|
|
7
|
+
const Time = @import("../time.zig").Time;
|
|
8
|
+
const buffer_limit = @import("../io.zig").buffer_limit;
|
|
8
9
|
|
|
9
10
|
pub const IO = struct {
|
|
10
11
|
kq: os.fd_t,
|
|
11
12
|
time: Time = .{},
|
|
13
|
+
io_inflight: usize = 0,
|
|
12
14
|
timeouts: FIFO(Completion) = .{},
|
|
13
15
|
completed: FIFO(Completion) = .{},
|
|
14
16
|
io_pending: FIFO(Completion) = .{},
|
|
15
17
|
|
|
16
18
|
pub fn init(entries: u12, flags: u32) !IO {
|
|
19
|
+
_ = entries;
|
|
20
|
+
_ = flags;
|
|
21
|
+
|
|
17
22
|
const kq = try os.kqueue();
|
|
18
23
|
assert(kq > -1);
|
|
19
24
|
return IO{ .kq = kq };
|
|
@@ -27,9 +32,7 @@ pub const IO = struct {
|
|
|
27
32
|
|
|
28
33
|
/// Pass all queued submissions to the kernel and peek for completions.
|
|
29
34
|
pub fn tick(self: *IO) !void {
|
|
30
|
-
|
|
31
|
-
// What we're seeing for the tigerbeetle-node client is that recv() syscalls never complete.
|
|
32
|
-
return self.run_for_ns(std.time.ns_per_ms);
|
|
35
|
+
return self.flush(false);
|
|
33
36
|
}
|
|
34
37
|
|
|
35
38
|
/// Pass all queued submissions to the kernel and run for `nanoseconds`.
|
|
@@ -42,8 +45,11 @@ pub const IO = struct {
|
|
|
42
45
|
fn callback(
|
|
43
46
|
timed_out_ptr: *bool,
|
|
44
47
|
_completion: *Completion,
|
|
45
|
-
|
|
48
|
+
result: TimeoutError!void,
|
|
46
49
|
) void {
|
|
50
|
+
_ = _completion;
|
|
51
|
+
_ = result catch unreachable;
|
|
52
|
+
|
|
47
53
|
timed_out_ptr.* = true;
|
|
48
54
|
}
|
|
49
55
|
}.callback;
|
|
@@ -84,10 +90,13 @@ pub const IO = struct {
|
|
|
84
90
|
// - tick() is non-blocking (wait_for_completions = false)
|
|
85
91
|
// - run_for_ns() always submits a timeout
|
|
86
92
|
if (change_events == 0 and self.completed.peek() == null) {
|
|
87
|
-
if (
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
93
|
+
if (wait_for_completions) {
|
|
94
|
+
const timeout_ns = next_timeout orelse @panic("kevent() blocking forever");
|
|
95
|
+
ts.tv_nsec = @intCast(@TypeOf(ts.tv_nsec), timeout_ns % std.time.ns_per_s);
|
|
96
|
+
ts.tv_sec = @intCast(@TypeOf(ts.tv_sec), timeout_ns / std.time.ns_per_s);
|
|
97
|
+
} else if (self.io_inflight == 0) {
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
91
100
|
}
|
|
92
101
|
|
|
93
102
|
const new_events = try os.kevent(
|
|
@@ -103,6 +112,9 @@ pub const IO = struct {
|
|
|
103
112
|
self.io_pending.in = null;
|
|
104
113
|
}
|
|
105
114
|
|
|
115
|
+
self.io_inflight += change_events;
|
|
116
|
+
self.io_inflight -= new_events;
|
|
117
|
+
|
|
106
118
|
for (events[0..new_events]) |event| {
|
|
107
119
|
const completion = @intToPtr(*Completion, event.udata);
|
|
108
120
|
completion.next = null;
|
|
@@ -117,25 +129,25 @@ pub const IO = struct {
|
|
|
117
129
|
}
|
|
118
130
|
}
|
|
119
131
|
|
|
120
|
-
fn flush_io(
|
|
132
|
+
fn flush_io(_: *IO, events: []os.Kevent, io_pending_top: *?*Completion) usize {
|
|
121
133
|
for (events) |*event, flushed| {
|
|
122
134
|
const completion = io_pending_top.* orelse return flushed;
|
|
123
135
|
io_pending_top.* = completion.next;
|
|
124
136
|
|
|
125
137
|
const event_info = switch (completion.operation) {
|
|
126
|
-
.accept => |op| [2]c_int{ op.socket, os.EVFILT_READ },
|
|
127
|
-
.connect => |op| [2]c_int{ op.socket, os.EVFILT_WRITE },
|
|
128
|
-
.read => |op| [2]c_int{ op.fd, os.EVFILT_READ },
|
|
129
|
-
.write => |op| [2]c_int{ op.fd, os.EVFILT_WRITE },
|
|
130
|
-
.recv => |op| [2]c_int{ op.socket, os.EVFILT_READ },
|
|
131
|
-
.send => |op| [2]c_int{ op.socket, os.EVFILT_WRITE },
|
|
138
|
+
.accept => |op| [2]c_int{ op.socket, os.system.EVFILT_READ },
|
|
139
|
+
.connect => |op| [2]c_int{ op.socket, os.system.EVFILT_WRITE },
|
|
140
|
+
.read => |op| [2]c_int{ op.fd, os.system.EVFILT_READ },
|
|
141
|
+
.write => |op| [2]c_int{ op.fd, os.system.EVFILT_WRITE },
|
|
142
|
+
.recv => |op| [2]c_int{ op.socket, os.system.EVFILT_READ },
|
|
143
|
+
.send => |op| [2]c_int{ op.socket, os.system.EVFILT_WRITE },
|
|
132
144
|
else => @panic("invalid completion operation queued for io"),
|
|
133
145
|
};
|
|
134
146
|
|
|
135
147
|
event.* = .{
|
|
136
148
|
.ident = @intCast(u32, event_info[0]),
|
|
137
149
|
.filter = @intCast(i16, event_info[1]),
|
|
138
|
-
.flags = os.EV_ADD | os.EV_ENABLE | os.EV_ONESHOT,
|
|
150
|
+
.flags = os.system.EV_ADD | os.system.EV_ENABLE | os.system.EV_ONESHOT,
|
|
139
151
|
.fflags = 0,
|
|
140
152
|
.data = 0,
|
|
141
153
|
.udata = @ptrToInt(completion),
|
|
@@ -175,7 +187,7 @@ pub const IO = struct {
|
|
|
175
187
|
/// This struct holds the data needed for a single IO operation
|
|
176
188
|
pub const Completion = struct {
|
|
177
189
|
next: ?*Completion,
|
|
178
|
-
context: ?*
|
|
190
|
+
context: ?*anyopaque,
|
|
179
191
|
callback: fn (*IO, *Completion) void,
|
|
180
192
|
operation: Operation,
|
|
181
193
|
};
|
|
@@ -183,7 +195,6 @@ pub const IO = struct {
|
|
|
183
195
|
const Operation = union(enum) {
|
|
184
196
|
accept: struct {
|
|
185
197
|
socket: os.socket_t,
|
|
186
|
-
flags: u32,
|
|
187
198
|
},
|
|
188
199
|
close: struct {
|
|
189
200
|
fd: os.fd_t,
|
|
@@ -195,13 +206,6 @@ pub const IO = struct {
|
|
|
195
206
|
},
|
|
196
207
|
fsync: struct {
|
|
197
208
|
fd: os.fd_t,
|
|
198
|
-
flags: u32,
|
|
199
|
-
},
|
|
200
|
-
openat: struct {
|
|
201
|
-
fd: os.fd_t,
|
|
202
|
-
path: [*:0]const u8,
|
|
203
|
-
flags: u32,
|
|
204
|
-
mode: os.mode_t,
|
|
205
209
|
},
|
|
206
210
|
read: struct {
|
|
207
211
|
fd: os.fd_t,
|
|
@@ -213,13 +217,11 @@ pub const IO = struct {
|
|
|
213
217
|
socket: os.socket_t,
|
|
214
218
|
buf: [*]u8,
|
|
215
219
|
len: u32,
|
|
216
|
-
flags: u32,
|
|
217
220
|
},
|
|
218
221
|
send: struct {
|
|
219
222
|
socket: os.socket_t,
|
|
220
223
|
buf: [*]const u8,
|
|
221
224
|
len: u32,
|
|
222
|
-
flags: u32,
|
|
223
225
|
},
|
|
224
226
|
timeout: struct {
|
|
225
227
|
expires: u64,
|
|
@@ -285,7 +287,7 @@ pub const IO = struct {
|
|
|
285
287
|
}
|
|
286
288
|
}
|
|
287
289
|
|
|
288
|
-
pub const AcceptError = os.AcceptError;
|
|
290
|
+
pub const AcceptError = os.AcceptError || os.SetSockOptError;
|
|
289
291
|
|
|
290
292
|
pub fn accept(
|
|
291
293
|
self: *IO,
|
|
@@ -298,7 +300,6 @@ pub const IO = struct {
|
|
|
298
300
|
) void,
|
|
299
301
|
completion: *Completion,
|
|
300
302
|
socket: os.socket_t,
|
|
301
|
-
flags: u32,
|
|
302
303
|
) void {
|
|
303
304
|
self.submit(
|
|
304
305
|
context,
|
|
@@ -307,11 +308,33 @@ pub const IO = struct {
|
|
|
307
308
|
.accept,
|
|
308
309
|
.{
|
|
309
310
|
.socket = socket,
|
|
310
|
-
.flags = flags,
|
|
311
311
|
},
|
|
312
312
|
struct {
|
|
313
313
|
fn doOperation(op: anytype) AcceptError!os.socket_t {
|
|
314
|
-
|
|
314
|
+
const fd = try os.accept(
|
|
315
|
+
op.socket,
|
|
316
|
+
null,
|
|
317
|
+
null,
|
|
318
|
+
os.SOCK.NONBLOCK | os.SOCK.CLOEXEC,
|
|
319
|
+
);
|
|
320
|
+
errdefer os.close(fd);
|
|
321
|
+
|
|
322
|
+
// Darwin doesn't support os.MSG_NOSIGNAL to avoid getting SIGPIPE on socket send().
|
|
323
|
+
// Instead, it uses the SO_NOSIGPIPE socket option which does the same for all send()s.
|
|
324
|
+
os.setsockopt(
|
|
325
|
+
fd,
|
|
326
|
+
os.SOL.SOCKET,
|
|
327
|
+
os.SO.NOSIGPIPE,
|
|
328
|
+
&mem.toBytes(@as(c_int, 1)),
|
|
329
|
+
) catch |err| return switch (err) {
|
|
330
|
+
error.TimeoutTooBig => unreachable,
|
|
331
|
+
error.PermissionDenied => error.NetworkSubsystemFailed,
|
|
332
|
+
error.AlreadyConnected => error.NetworkSubsystemFailed,
|
|
333
|
+
error.InvalidProtocolOption => error.ProtocolFailure,
|
|
334
|
+
else => |e| e,
|
|
335
|
+
};
|
|
336
|
+
|
|
337
|
+
return fd;
|
|
315
338
|
}
|
|
316
339
|
},
|
|
317
340
|
);
|
|
@@ -347,10 +370,10 @@ pub const IO = struct {
|
|
|
347
370
|
struct {
|
|
348
371
|
fn doOperation(op: anytype) CloseError!void {
|
|
349
372
|
return switch (os.errno(os.system.close(op.fd))) {
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
373
|
+
.SUCCESS => {},
|
|
374
|
+
.BADF => error.FileDescriptorInvalid,
|
|
375
|
+
.INTR => {}, // A success, see https://github.com/ziglang/zig/issues/2425
|
|
376
|
+
.IO => error.InputOutput,
|
|
354
377
|
else => |errno| os.unexpectedErrno(errno),
|
|
355
378
|
};
|
|
356
379
|
}
|
|
@@ -399,15 +422,7 @@ pub const IO = struct {
|
|
|
399
422
|
);
|
|
400
423
|
}
|
|
401
424
|
|
|
402
|
-
pub const FsyncError =
|
|
403
|
-
FileDescriptorInvalid,
|
|
404
|
-
DiskQuota,
|
|
405
|
-
ArgumentsInvalid,
|
|
406
|
-
InputOutput,
|
|
407
|
-
NoSpaceLeft,
|
|
408
|
-
ReadOnlyFileSystem,
|
|
409
|
-
AccessDenied,
|
|
410
|
-
} || os.UnexpectedError;
|
|
425
|
+
pub const FsyncError = os.SyncError;
|
|
411
426
|
|
|
412
427
|
pub fn fsync(
|
|
413
428
|
self: *IO,
|
|
@@ -420,7 +435,6 @@ pub const IO = struct {
|
|
|
420
435
|
) void,
|
|
421
436
|
completion: *Completion,
|
|
422
437
|
fd: os.fd_t,
|
|
423
|
-
flags: u32,
|
|
424
438
|
) void {
|
|
425
439
|
self.submit(
|
|
426
440
|
context,
|
|
@@ -429,68 +443,10 @@ pub const IO = struct {
|
|
|
429
443
|
.fsync,
|
|
430
444
|
.{
|
|
431
445
|
.fd = fd,
|
|
432
|
-
.flags = flags,
|
|
433
446
|
},
|
|
434
447
|
struct {
|
|
435
448
|
fn doOperation(op: anytype) FsyncError!void {
|
|
436
|
-
_ = os.fcntl(op.fd, os.
|
|
437
|
-
}
|
|
438
|
-
},
|
|
439
|
-
);
|
|
440
|
-
}
|
|
441
|
-
|
|
442
|
-
pub const OpenatError = error{
|
|
443
|
-
AccessDenied,
|
|
444
|
-
FileDescriptorInvalid,
|
|
445
|
-
DeviceBusy,
|
|
446
|
-
PathAlreadyExists,
|
|
447
|
-
FileTooBig,
|
|
448
|
-
ArgumentsInvalid,
|
|
449
|
-
IsDir,
|
|
450
|
-
SymLinkLoop,
|
|
451
|
-
ProcessFdQuotaExceeded,
|
|
452
|
-
NameTooLong,
|
|
453
|
-
SystemFdQuotaExceeded,
|
|
454
|
-
NoDevice,
|
|
455
|
-
FileNotFound,
|
|
456
|
-
SystemResources,
|
|
457
|
-
NoSpaceLeft,
|
|
458
|
-
NotDir,
|
|
459
|
-
FileLocksNotSupported,
|
|
460
|
-
BadPathName,
|
|
461
|
-
InvalidUtf8,
|
|
462
|
-
WouldBlock,
|
|
463
|
-
} || os.UnexpectedError;
|
|
464
|
-
|
|
465
|
-
pub fn openat(
|
|
466
|
-
self: *IO,
|
|
467
|
-
comptime Context: type,
|
|
468
|
-
context: Context,
|
|
469
|
-
comptime callback: fn (
|
|
470
|
-
context: Context,
|
|
471
|
-
completion: *Completion,
|
|
472
|
-
result: OpenatError!os.fd_t,
|
|
473
|
-
) void,
|
|
474
|
-
completion: *Completion,
|
|
475
|
-
fd: os.fd_t,
|
|
476
|
-
path: [*:0]const u8,
|
|
477
|
-
flags: u32,
|
|
478
|
-
mode: os.mode_t,
|
|
479
|
-
) void {
|
|
480
|
-
self.submit(
|
|
481
|
-
context,
|
|
482
|
-
callback,
|
|
483
|
-
completion,
|
|
484
|
-
.openat,
|
|
485
|
-
.{
|
|
486
|
-
.fd = fd,
|
|
487
|
-
.path = path,
|
|
488
|
-
.mode = mode,
|
|
489
|
-
.flags = flags,
|
|
490
|
-
},
|
|
491
|
-
struct {
|
|
492
|
-
fn doOperation(op: anytype) OpenatError!os.fd_t {
|
|
493
|
-
return os.openatZ(op.fd, op.path, op.flags, op.mode);
|
|
449
|
+
_ = os.fcntl(op.fd, os.F.FULLFSYNC, 1) catch return os.fsync(op.fd);
|
|
494
450
|
}
|
|
495
451
|
},
|
|
496
452
|
);
|
|
@@ -542,20 +498,20 @@ pub const IO = struct {
|
|
|
542
498
|
@bitCast(isize, op.offset),
|
|
543
499
|
);
|
|
544
500
|
return switch (os.errno(rc)) {
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
501
|
+
.SUCCESS => @intCast(usize, rc),
|
|
502
|
+
.INTR => continue,
|
|
503
|
+
.AGAIN => error.WouldBlock,
|
|
504
|
+
.BADF => error.NotOpenForReading,
|
|
505
|
+
.CONNRESET => error.ConnectionResetByPeer,
|
|
506
|
+
.FAULT => unreachable,
|
|
507
|
+
.INVAL => error.Alignment,
|
|
508
|
+
.IO => error.InputOutput,
|
|
509
|
+
.ISDIR => error.IsDir,
|
|
510
|
+
.NOBUFS => error.SystemResources,
|
|
511
|
+
.NOMEM => error.SystemResources,
|
|
512
|
+
.NXIO => error.Unseekable,
|
|
513
|
+
.OVERFLOW => error.Unseekable,
|
|
514
|
+
.SPIPE => error.Unseekable,
|
|
559
515
|
else => |err| os.unexpectedErrno(err),
|
|
560
516
|
};
|
|
561
517
|
}
|
|
@@ -578,7 +534,6 @@ pub const IO = struct {
|
|
|
578
534
|
completion: *Completion,
|
|
579
535
|
socket: os.socket_t,
|
|
580
536
|
buffer: []u8,
|
|
581
|
-
flags: u32,
|
|
582
537
|
) void {
|
|
583
538
|
self.submit(
|
|
584
539
|
context,
|
|
@@ -589,11 +544,10 @@ pub const IO = struct {
|
|
|
589
544
|
.socket = socket,
|
|
590
545
|
.buf = buffer.ptr,
|
|
591
546
|
.len = @intCast(u32, buffer_limit(buffer.len)),
|
|
592
|
-
.flags = flags,
|
|
593
547
|
},
|
|
594
548
|
struct {
|
|
595
549
|
fn doOperation(op: anytype) RecvError!usize {
|
|
596
|
-
return os.recv(op.socket, op.buf[0..op.len],
|
|
550
|
+
return os.recv(op.socket, op.buf[0..op.len], 0);
|
|
597
551
|
}
|
|
598
552
|
},
|
|
599
553
|
);
|
|
@@ -613,7 +567,6 @@ pub const IO = struct {
|
|
|
613
567
|
completion: *Completion,
|
|
614
568
|
socket: os.socket_t,
|
|
615
569
|
buffer: []const u8,
|
|
616
|
-
flags: u32,
|
|
617
570
|
) void {
|
|
618
571
|
self.submit(
|
|
619
572
|
context,
|
|
@@ -624,11 +577,10 @@ pub const IO = struct {
|
|
|
624
577
|
.socket = socket,
|
|
625
578
|
.buf = buffer.ptr,
|
|
626
579
|
.len = @intCast(u32, buffer_limit(buffer.len)),
|
|
627
|
-
.flags = flags,
|
|
628
580
|
},
|
|
629
581
|
struct {
|
|
630
582
|
fn doOperation(op: anytype) SendError!usize {
|
|
631
|
-
return os.send(op.socket, op.buf[0..op.len],
|
|
583
|
+
return os.send(op.socket, op.buf[0..op.len], 0);
|
|
632
584
|
}
|
|
633
585
|
},
|
|
634
586
|
);
|
|
@@ -698,4 +650,13 @@ pub const IO = struct {
|
|
|
698
650
|
},
|
|
699
651
|
);
|
|
700
652
|
}
|
|
653
|
+
|
|
654
|
+
pub fn openSocket(family: u32, sock_type: u32, protocol: u32) !os.socket_t {
|
|
655
|
+
const fd = try os.socket(family, sock_type | os.SOCK.NONBLOCK, protocol);
|
|
656
|
+
errdefer os.close(fd);
|
|
657
|
+
|
|
658
|
+
// darwin doesn't support os.MSG_NOSIGNAL, but instead a socket option to avoid SIGPIPE.
|
|
659
|
+
try os.setsockopt(fd, os.SOL.SOCKET, os.SO.NOSIGPIPE, &mem.toBytes(@as(c_int, 1)));
|
|
660
|
+
return fd;
|
|
661
|
+
}
|
|
701
662
|
};
|