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,621 @@
|
|
|
1
|
+
const std = @import("std");
|
|
2
|
+
const os = std.os;
|
|
3
|
+
const testing = std.testing;
|
|
4
|
+
const assert = std.debug.assert;
|
|
5
|
+
|
|
6
|
+
const Time = @import("../time.zig").Time;
|
|
7
|
+
const IO = @import("../io.zig").IO;
|
|
8
|
+
|
|
9
|
+
test "write/fsync/read/close" {
|
|
10
|
+
try struct {
|
|
11
|
+
const Context = @This();
|
|
12
|
+
|
|
13
|
+
io: IO,
|
|
14
|
+
done: bool = false,
|
|
15
|
+
fd: os.fd_t,
|
|
16
|
+
|
|
17
|
+
write_buf: [20]u8 = [_]u8{97} ** 20,
|
|
18
|
+
read_buf: [20]u8 = [_]u8{98} ** 20,
|
|
19
|
+
|
|
20
|
+
written: usize = 0,
|
|
21
|
+
fsynced: bool = false,
|
|
22
|
+
read: usize = 0,
|
|
23
|
+
|
|
24
|
+
fn run_test() !void {
|
|
25
|
+
const path = "test_io_write_fsync_read";
|
|
26
|
+
const file = try std.fs.cwd().createFile(path, .{ .read = true, .truncate = true });
|
|
27
|
+
defer std.fs.cwd().deleteFile(path) catch {};
|
|
28
|
+
|
|
29
|
+
var self: Context = .{
|
|
30
|
+
.io = try IO.init(32, 0),
|
|
31
|
+
.fd = file.handle,
|
|
32
|
+
};
|
|
33
|
+
defer self.io.deinit();
|
|
34
|
+
|
|
35
|
+
var completion: IO.Completion = undefined;
|
|
36
|
+
|
|
37
|
+
self.io.write(
|
|
38
|
+
*Context,
|
|
39
|
+
&self,
|
|
40
|
+
write_callback,
|
|
41
|
+
&completion,
|
|
42
|
+
self.fd,
|
|
43
|
+
&self.write_buf,
|
|
44
|
+
10,
|
|
45
|
+
);
|
|
46
|
+
while (!self.done) try self.io.tick();
|
|
47
|
+
|
|
48
|
+
try testing.expectEqual(self.write_buf.len, self.written);
|
|
49
|
+
try testing.expect(self.fsynced);
|
|
50
|
+
try testing.expectEqual(self.read_buf.len, self.read);
|
|
51
|
+
try testing.expectEqualSlices(u8, &self.write_buf, &self.read_buf);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
fn write_callback(
|
|
55
|
+
self: *Context,
|
|
56
|
+
completion: *IO.Completion,
|
|
57
|
+
result: IO.WriteError!usize,
|
|
58
|
+
) void {
|
|
59
|
+
self.written = result catch @panic("write error");
|
|
60
|
+
self.io.fsync(*Context, self, fsync_callback, completion, self.fd);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
fn fsync_callback(
|
|
64
|
+
self: *Context,
|
|
65
|
+
completion: *IO.Completion,
|
|
66
|
+
result: IO.FsyncError!void,
|
|
67
|
+
) void {
|
|
68
|
+
_ = result catch @panic("fsync error");
|
|
69
|
+
|
|
70
|
+
self.fsynced = true;
|
|
71
|
+
self.io.read(*Context, self, read_callback, completion, self.fd, &self.read_buf, 10);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
fn read_callback(
|
|
75
|
+
self: *Context,
|
|
76
|
+
completion: *IO.Completion,
|
|
77
|
+
result: IO.ReadError!usize,
|
|
78
|
+
) void {
|
|
79
|
+
self.read = result catch @panic("read error");
|
|
80
|
+
self.io.close(*Context, self, close_callback, completion, self.fd);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
fn close_callback(
|
|
84
|
+
self: *Context,
|
|
85
|
+
completion: *IO.Completion,
|
|
86
|
+
result: IO.CloseError!void,
|
|
87
|
+
) void {
|
|
88
|
+
_ = completion;
|
|
89
|
+
_ = result catch @panic("close error");
|
|
90
|
+
|
|
91
|
+
self.done = true;
|
|
92
|
+
}
|
|
93
|
+
}.run_test();
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
test "accept/connect/send/receive" {
|
|
97
|
+
try struct {
|
|
98
|
+
const Context = @This();
|
|
99
|
+
|
|
100
|
+
io: IO,
|
|
101
|
+
done: bool = false,
|
|
102
|
+
server: os.socket_t,
|
|
103
|
+
client: os.socket_t,
|
|
104
|
+
|
|
105
|
+
accepted_sock: os.socket_t = undefined,
|
|
106
|
+
|
|
107
|
+
send_buf: [10]u8 = [_]u8{ 1, 0, 1, 0, 1, 0, 1, 0, 1, 0 },
|
|
108
|
+
recv_buf: [5]u8 = [_]u8{ 0, 1, 0, 1, 0 },
|
|
109
|
+
|
|
110
|
+
sent: usize = 0,
|
|
111
|
+
received: usize = 0,
|
|
112
|
+
|
|
113
|
+
fn run_test() !void {
|
|
114
|
+
const address = try std.net.Address.parseIp4("127.0.0.1", 3131);
|
|
115
|
+
const kernel_backlog = 1;
|
|
116
|
+
const server = try IO.openSocket(address.any.family, os.SOCK.STREAM, os.IPPROTO.TCP);
|
|
117
|
+
defer os.closeSocket(server);
|
|
118
|
+
|
|
119
|
+
const client = try IO.openSocket(address.any.family, os.SOCK.STREAM, os.IPPROTO.TCP);
|
|
120
|
+
defer os.closeSocket(client);
|
|
121
|
+
|
|
122
|
+
try os.setsockopt(
|
|
123
|
+
server,
|
|
124
|
+
os.SOL.SOCKET,
|
|
125
|
+
os.SO.REUSEADDR,
|
|
126
|
+
&std.mem.toBytes(@as(c_int, 1)),
|
|
127
|
+
);
|
|
128
|
+
try os.bind(server, &address.any, address.getOsSockLen());
|
|
129
|
+
try os.listen(server, kernel_backlog);
|
|
130
|
+
|
|
131
|
+
var self: Context = .{
|
|
132
|
+
.io = try IO.init(32, 0),
|
|
133
|
+
.server = server,
|
|
134
|
+
.client = client,
|
|
135
|
+
};
|
|
136
|
+
defer self.io.deinit();
|
|
137
|
+
|
|
138
|
+
var client_completion: IO.Completion = undefined;
|
|
139
|
+
self.io.connect(
|
|
140
|
+
*Context,
|
|
141
|
+
&self,
|
|
142
|
+
connect_callback,
|
|
143
|
+
&client_completion,
|
|
144
|
+
client,
|
|
145
|
+
address,
|
|
146
|
+
);
|
|
147
|
+
|
|
148
|
+
var server_completion: IO.Completion = undefined;
|
|
149
|
+
self.io.accept(*Context, &self, accept_callback, &server_completion, server);
|
|
150
|
+
|
|
151
|
+
while (!self.done) try self.io.tick();
|
|
152
|
+
|
|
153
|
+
try testing.expectEqual(self.send_buf.len, self.sent);
|
|
154
|
+
try testing.expectEqual(self.recv_buf.len, self.received);
|
|
155
|
+
|
|
156
|
+
try testing.expectEqualSlices(u8, self.send_buf[0..self.received], &self.recv_buf);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
fn connect_callback(
|
|
160
|
+
self: *Context,
|
|
161
|
+
completion: *IO.Completion,
|
|
162
|
+
result: IO.ConnectError!void,
|
|
163
|
+
) void {
|
|
164
|
+
_ = result catch @panic("connect error");
|
|
165
|
+
|
|
166
|
+
self.io.send(
|
|
167
|
+
*Context,
|
|
168
|
+
self,
|
|
169
|
+
send_callback,
|
|
170
|
+
completion,
|
|
171
|
+
self.client,
|
|
172
|
+
&self.send_buf,
|
|
173
|
+
);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
fn send_callback(
|
|
177
|
+
self: *Context,
|
|
178
|
+
completion: *IO.Completion,
|
|
179
|
+
result: IO.SendError!usize,
|
|
180
|
+
) void {
|
|
181
|
+
_ = completion;
|
|
182
|
+
|
|
183
|
+
self.sent = result catch @panic("send error");
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
fn accept_callback(
|
|
187
|
+
self: *Context,
|
|
188
|
+
completion: *IO.Completion,
|
|
189
|
+
result: IO.AcceptError!os.socket_t,
|
|
190
|
+
) void {
|
|
191
|
+
self.accepted_sock = result catch @panic("accept error");
|
|
192
|
+
self.io.recv(
|
|
193
|
+
*Context,
|
|
194
|
+
self,
|
|
195
|
+
recv_callback,
|
|
196
|
+
completion,
|
|
197
|
+
self.accepted_sock,
|
|
198
|
+
&self.recv_buf,
|
|
199
|
+
);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
fn recv_callback(
|
|
203
|
+
self: *Context,
|
|
204
|
+
completion: *IO.Completion,
|
|
205
|
+
result: IO.RecvError!usize,
|
|
206
|
+
) void {
|
|
207
|
+
_ = completion;
|
|
208
|
+
|
|
209
|
+
self.received = result catch @panic("recv error");
|
|
210
|
+
self.done = true;
|
|
211
|
+
}
|
|
212
|
+
}.run_test();
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
test "timeout" {
|
|
216
|
+
const ms = 20;
|
|
217
|
+
const margin = 5;
|
|
218
|
+
const count = 10;
|
|
219
|
+
|
|
220
|
+
try struct {
|
|
221
|
+
const Context = @This();
|
|
222
|
+
|
|
223
|
+
io: IO,
|
|
224
|
+
timer: *Time,
|
|
225
|
+
count: u32 = 0,
|
|
226
|
+
stop_time: u64 = 0,
|
|
227
|
+
|
|
228
|
+
fn run_test() !void {
|
|
229
|
+
var timer = Time{};
|
|
230
|
+
const start_time = timer.monotonic();
|
|
231
|
+
|
|
232
|
+
var self: Context = .{
|
|
233
|
+
.timer = &timer,
|
|
234
|
+
.io = try IO.init(32, 0),
|
|
235
|
+
};
|
|
236
|
+
defer self.io.deinit();
|
|
237
|
+
|
|
238
|
+
var completions: [count]IO.Completion = undefined;
|
|
239
|
+
for (completions) |*completion| {
|
|
240
|
+
self.io.timeout(
|
|
241
|
+
*Context,
|
|
242
|
+
&self,
|
|
243
|
+
timeout_callback,
|
|
244
|
+
completion,
|
|
245
|
+
ms * std.time.ns_per_ms,
|
|
246
|
+
);
|
|
247
|
+
}
|
|
248
|
+
while (self.count < count) try self.io.tick();
|
|
249
|
+
|
|
250
|
+
try self.io.tick();
|
|
251
|
+
try testing.expectEqual(@as(u32, count), self.count);
|
|
252
|
+
|
|
253
|
+
try testing.expectApproxEqAbs(
|
|
254
|
+
@as(f64, ms),
|
|
255
|
+
@intToFloat(f64, (self.stop_time - start_time) / std.time.ns_per_ms),
|
|
256
|
+
margin,
|
|
257
|
+
);
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
fn timeout_callback(
|
|
261
|
+
self: *Context,
|
|
262
|
+
completion: *IO.Completion,
|
|
263
|
+
result: IO.TimeoutError!void,
|
|
264
|
+
) void {
|
|
265
|
+
_ = completion;
|
|
266
|
+
_ = result catch @panic("timeout error");
|
|
267
|
+
|
|
268
|
+
if (self.stop_time == 0) self.stop_time = self.timer.monotonic();
|
|
269
|
+
self.count += 1;
|
|
270
|
+
}
|
|
271
|
+
}.run_test();
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
test "submission queue full" {
|
|
275
|
+
const ms = 20;
|
|
276
|
+
const count = 10;
|
|
277
|
+
|
|
278
|
+
try struct {
|
|
279
|
+
const Context = @This();
|
|
280
|
+
|
|
281
|
+
io: IO,
|
|
282
|
+
count: u32 = 0,
|
|
283
|
+
|
|
284
|
+
fn run_test() !void {
|
|
285
|
+
var self: Context = .{ .io = try IO.init(1, 0) };
|
|
286
|
+
defer self.io.deinit();
|
|
287
|
+
|
|
288
|
+
var completions: [count]IO.Completion = undefined;
|
|
289
|
+
for (completions) |*completion| {
|
|
290
|
+
self.io.timeout(
|
|
291
|
+
*Context,
|
|
292
|
+
&self,
|
|
293
|
+
timeout_callback,
|
|
294
|
+
completion,
|
|
295
|
+
ms * std.time.ns_per_ms,
|
|
296
|
+
);
|
|
297
|
+
}
|
|
298
|
+
while (self.count < count) try self.io.tick();
|
|
299
|
+
|
|
300
|
+
try self.io.tick();
|
|
301
|
+
try testing.expectEqual(@as(u32, count), self.count);
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
fn timeout_callback(
|
|
305
|
+
self: *Context,
|
|
306
|
+
completion: *IO.Completion,
|
|
307
|
+
result: IO.TimeoutError!void,
|
|
308
|
+
) void {
|
|
309
|
+
_ = completion;
|
|
310
|
+
_ = result catch @panic("timeout error");
|
|
311
|
+
|
|
312
|
+
self.count += 1;
|
|
313
|
+
}
|
|
314
|
+
}.run_test();
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
test "tick to wait" {
|
|
318
|
+
// Use only IO.tick() to see if pending IO is actually processsed
|
|
319
|
+
|
|
320
|
+
try struct {
|
|
321
|
+
const Context = @This();
|
|
322
|
+
|
|
323
|
+
io: IO,
|
|
324
|
+
accepted: os.socket_t = -1,
|
|
325
|
+
connected: bool = false,
|
|
326
|
+
received: bool = false,
|
|
327
|
+
|
|
328
|
+
fn run_test() !void {
|
|
329
|
+
var self: Context = .{ .io = try IO.init(1, 0) };
|
|
330
|
+
defer self.io.deinit();
|
|
331
|
+
|
|
332
|
+
const address = try std.net.Address.parseIp4("127.0.0.1", 3131);
|
|
333
|
+
const kernel_backlog = 1;
|
|
334
|
+
|
|
335
|
+
const server = try IO.openSocket(address.any.family, os.SOCK.STREAM, os.IPPROTO.TCP);
|
|
336
|
+
defer os.closeSocket(server);
|
|
337
|
+
|
|
338
|
+
try os.setsockopt(
|
|
339
|
+
server,
|
|
340
|
+
os.SOL.SOCKET,
|
|
341
|
+
os.SO.REUSEADDR,
|
|
342
|
+
&std.mem.toBytes(@as(c_int, 1)),
|
|
343
|
+
);
|
|
344
|
+
try os.bind(server, &address.any, address.getOsSockLen());
|
|
345
|
+
try os.listen(server, kernel_backlog);
|
|
346
|
+
|
|
347
|
+
const client = try IO.openSocket(address.any.family, os.SOCK.STREAM, os.IPPROTO.TCP);
|
|
348
|
+
defer os.closeSocket(client);
|
|
349
|
+
|
|
350
|
+
// Start the accept
|
|
351
|
+
var server_completion: IO.Completion = undefined;
|
|
352
|
+
self.io.accept(*Context, &self, accept_callback, &server_completion, server);
|
|
353
|
+
|
|
354
|
+
// Start the connect
|
|
355
|
+
var client_completion: IO.Completion = undefined;
|
|
356
|
+
self.io.connect(
|
|
357
|
+
*Context,
|
|
358
|
+
&self,
|
|
359
|
+
connect_callback,
|
|
360
|
+
&client_completion,
|
|
361
|
+
client,
|
|
362
|
+
address,
|
|
363
|
+
);
|
|
364
|
+
|
|
365
|
+
// Tick the IO to drain the accept & connect completions
|
|
366
|
+
assert(!self.connected);
|
|
367
|
+
assert(self.accepted == -1);
|
|
368
|
+
|
|
369
|
+
while (self.accepted == -1 or !self.connected)
|
|
370
|
+
try self.io.tick();
|
|
371
|
+
|
|
372
|
+
assert(self.connected);
|
|
373
|
+
assert(self.accepted != -1);
|
|
374
|
+
defer os.closeSocket(self.accepted);
|
|
375
|
+
|
|
376
|
+
// Start receiving on the client
|
|
377
|
+
var recv_completion: IO.Completion = undefined;
|
|
378
|
+
var recv_buffer: [64]u8 = undefined;
|
|
379
|
+
std.mem.set(u8, &recv_buffer, 0xaa);
|
|
380
|
+
self.io.recv(
|
|
381
|
+
*Context,
|
|
382
|
+
&self,
|
|
383
|
+
recv_callback,
|
|
384
|
+
&recv_completion,
|
|
385
|
+
client,
|
|
386
|
+
&recv_buffer,
|
|
387
|
+
);
|
|
388
|
+
|
|
389
|
+
// Drain out the recv completion from any internal IO queues
|
|
390
|
+
try self.io.tick();
|
|
391
|
+
try self.io.tick();
|
|
392
|
+
try self.io.tick();
|
|
393
|
+
|
|
394
|
+
// Complete the recv() *outside* of the IO instance.
|
|
395
|
+
// Other tests already check .tick() with IO based completions.
|
|
396
|
+
// This simulates IO being completed by an external system
|
|
397
|
+
var send_buf = std.mem.zeroes([64]u8);
|
|
398
|
+
const wrote = try os.write(self.accepted, &send_buf);
|
|
399
|
+
try testing.expectEqual(wrote, send_buf.len);
|
|
400
|
+
|
|
401
|
+
// Wait for the recv() to complete using only IO.tick().
|
|
402
|
+
// If tick is broken, then this will deadlock
|
|
403
|
+
assert(!self.received);
|
|
404
|
+
while (!self.received) {
|
|
405
|
+
try self.io.tick();
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
// Make sure the receive actually happened
|
|
409
|
+
assert(self.received);
|
|
410
|
+
try testing.expect(std.mem.eql(u8, &recv_buffer, &send_buf));
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
fn accept_callback(
|
|
414
|
+
self: *Context,
|
|
415
|
+
completion: *IO.Completion,
|
|
416
|
+
result: IO.AcceptError!os.socket_t,
|
|
417
|
+
) void {
|
|
418
|
+
_ = completion;
|
|
419
|
+
|
|
420
|
+
assert(self.accepted == -1);
|
|
421
|
+
self.accepted = result catch @panic("accept error");
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
fn connect_callback(
|
|
425
|
+
self: *Context,
|
|
426
|
+
completion: *IO.Completion,
|
|
427
|
+
result: IO.ConnectError!void,
|
|
428
|
+
) void {
|
|
429
|
+
_ = completion;
|
|
430
|
+
_ = result catch @panic("connect error");
|
|
431
|
+
|
|
432
|
+
assert(!self.connected);
|
|
433
|
+
self.connected = true;
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
fn recv_callback(
|
|
437
|
+
self: *Context,
|
|
438
|
+
completion: *IO.Completion,
|
|
439
|
+
result: IO.RecvError!usize,
|
|
440
|
+
) void {
|
|
441
|
+
_ = completion;
|
|
442
|
+
_ = result catch |err| std.debug.panic("recv error: {}", .{err});
|
|
443
|
+
|
|
444
|
+
assert(!self.received);
|
|
445
|
+
self.received = true;
|
|
446
|
+
}
|
|
447
|
+
}.run_test();
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
test "pipe data over socket" {
|
|
451
|
+
try struct {
|
|
452
|
+
io: IO,
|
|
453
|
+
tx: Pipe,
|
|
454
|
+
rx: Pipe,
|
|
455
|
+
server: Socket = .{},
|
|
456
|
+
|
|
457
|
+
const buffer_size = 1 * 1024 * 1024;
|
|
458
|
+
|
|
459
|
+
const Context = @This();
|
|
460
|
+
const Socket = struct {
|
|
461
|
+
fd: os.socket_t = -1,
|
|
462
|
+
completion: IO.Completion = undefined,
|
|
463
|
+
};
|
|
464
|
+
const Pipe = struct {
|
|
465
|
+
socket: Socket = .{},
|
|
466
|
+
buffer: []u8,
|
|
467
|
+
transferred: usize = 0,
|
|
468
|
+
};
|
|
469
|
+
|
|
470
|
+
fn run() !void {
|
|
471
|
+
const tx_buf = try testing.allocator.alloc(u8, buffer_size);
|
|
472
|
+
defer testing.allocator.free(tx_buf);
|
|
473
|
+
|
|
474
|
+
const rx_buf = try testing.allocator.alloc(u8, buffer_size);
|
|
475
|
+
defer testing.allocator.free(rx_buf);
|
|
476
|
+
|
|
477
|
+
std.mem.set(u8, tx_buf, 1);
|
|
478
|
+
std.mem.set(u8, rx_buf, 0);
|
|
479
|
+
|
|
480
|
+
var self = Context{
|
|
481
|
+
.io = try IO.init(32, 0),
|
|
482
|
+
.tx = .{ .buffer = tx_buf },
|
|
483
|
+
.rx = .{ .buffer = rx_buf },
|
|
484
|
+
};
|
|
485
|
+
defer self.io.deinit();
|
|
486
|
+
|
|
487
|
+
self.server.fd = try IO.openSocket(os.AF.INET, os.SOCK.STREAM, os.IPPROTO.TCP);
|
|
488
|
+
defer os.closeSocket(self.server.fd);
|
|
489
|
+
|
|
490
|
+
const address = try std.net.Address.parseIp4("127.0.0.1", 3131);
|
|
491
|
+
try os.setsockopt(
|
|
492
|
+
self.server.fd,
|
|
493
|
+
os.SOL.SOCKET,
|
|
494
|
+
os.SO.REUSEADDR,
|
|
495
|
+
&std.mem.toBytes(@as(c_int, 1)),
|
|
496
|
+
);
|
|
497
|
+
|
|
498
|
+
try os.bind(self.server.fd, &address.any, address.getOsSockLen());
|
|
499
|
+
try os.listen(self.server.fd, 1);
|
|
500
|
+
|
|
501
|
+
self.io.accept(
|
|
502
|
+
*Context,
|
|
503
|
+
&self,
|
|
504
|
+
on_accept,
|
|
505
|
+
&self.server.completion,
|
|
506
|
+
self.server.fd,
|
|
507
|
+
);
|
|
508
|
+
|
|
509
|
+
self.tx.socket.fd = try IO.openSocket(os.AF.INET, os.SOCK.STREAM, os.IPPROTO.TCP);
|
|
510
|
+
defer os.closeSocket(self.tx.socket.fd);
|
|
511
|
+
|
|
512
|
+
self.io.connect(
|
|
513
|
+
*Context,
|
|
514
|
+
&self,
|
|
515
|
+
on_connect,
|
|
516
|
+
&self.tx.socket.completion,
|
|
517
|
+
self.tx.socket.fd,
|
|
518
|
+
address,
|
|
519
|
+
);
|
|
520
|
+
|
|
521
|
+
var tick: usize = 0xdeadbeef;
|
|
522
|
+
while (self.rx.transferred != self.rx.buffer.len) : (tick +%= 1) {
|
|
523
|
+
if (tick % 61 == 0) {
|
|
524
|
+
const timeout_ns = tick % (10 * std.time.ns_per_ms);
|
|
525
|
+
try self.io.run_for_ns(@intCast(u63, timeout_ns));
|
|
526
|
+
} else {
|
|
527
|
+
try self.io.tick();
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
try testing.expect(self.server.fd != -1);
|
|
532
|
+
try testing.expect(self.tx.socket.fd != -1);
|
|
533
|
+
try testing.expect(self.rx.socket.fd != -1);
|
|
534
|
+
os.closeSocket(self.rx.socket.fd);
|
|
535
|
+
|
|
536
|
+
try testing.expectEqual(self.tx.transferred, buffer_size);
|
|
537
|
+
try testing.expectEqual(self.rx.transferred, buffer_size);
|
|
538
|
+
try testing.expect(std.mem.eql(u8, self.tx.buffer, self.rx.buffer));
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
fn on_accept(
|
|
542
|
+
self: *Context,
|
|
543
|
+
completion: *IO.Completion,
|
|
544
|
+
result: IO.AcceptError!os.socket_t,
|
|
545
|
+
) void {
|
|
546
|
+
assert(self.rx.socket.fd == -1);
|
|
547
|
+
assert(&self.server.completion == completion);
|
|
548
|
+
self.rx.socket.fd = result catch |err| std.debug.panic("accept error {}", .{err});
|
|
549
|
+
|
|
550
|
+
assert(self.rx.transferred == 0);
|
|
551
|
+
self.do_receiver(0);
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
fn on_connect(
|
|
555
|
+
self: *Context,
|
|
556
|
+
completion: *IO.Completion,
|
|
557
|
+
result: IO.ConnectError!void,
|
|
558
|
+
) void {
|
|
559
|
+
_ = completion;
|
|
560
|
+
_ = result catch unreachable;
|
|
561
|
+
|
|
562
|
+
assert(self.tx.socket.fd != -1);
|
|
563
|
+
assert(&self.tx.socket.completion == completion);
|
|
564
|
+
|
|
565
|
+
assert(self.tx.transferred == 0);
|
|
566
|
+
self.do_sender(0);
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
fn do_sender(self: *Context, bytes: usize) void {
|
|
570
|
+
self.tx.transferred += bytes;
|
|
571
|
+
assert(self.tx.transferred <= self.tx.buffer.len);
|
|
572
|
+
|
|
573
|
+
if (self.tx.transferred < self.tx.buffer.len) {
|
|
574
|
+
self.io.send(
|
|
575
|
+
*Context,
|
|
576
|
+
self,
|
|
577
|
+
on_send,
|
|
578
|
+
&self.tx.socket.completion,
|
|
579
|
+
self.tx.socket.fd,
|
|
580
|
+
self.tx.buffer[self.tx.transferred..],
|
|
581
|
+
);
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
fn on_send(
|
|
586
|
+
self: *Context,
|
|
587
|
+
completion: *IO.Completion,
|
|
588
|
+
result: IO.SendError!usize,
|
|
589
|
+
) void {
|
|
590
|
+
const bytes = result catch |err| std.debug.panic("send error: {}", .{err});
|
|
591
|
+
assert(&self.tx.socket.completion == completion);
|
|
592
|
+
self.do_sender(bytes);
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
fn do_receiver(self: *Context, bytes: usize) void {
|
|
596
|
+
self.rx.transferred += bytes;
|
|
597
|
+
assert(self.rx.transferred <= self.rx.buffer.len);
|
|
598
|
+
|
|
599
|
+
if (self.rx.transferred < self.rx.buffer.len) {
|
|
600
|
+
self.io.recv(
|
|
601
|
+
*Context,
|
|
602
|
+
self,
|
|
603
|
+
on_recv,
|
|
604
|
+
&self.rx.socket.completion,
|
|
605
|
+
self.rx.socket.fd,
|
|
606
|
+
self.rx.buffer[self.rx.transferred..],
|
|
607
|
+
);
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
fn on_recv(
|
|
612
|
+
self: *Context,
|
|
613
|
+
completion: *IO.Completion,
|
|
614
|
+
result: IO.RecvError!usize,
|
|
615
|
+
) void {
|
|
616
|
+
const bytes = result catch |err| std.debug.panic("recv error: {}", .{err});
|
|
617
|
+
assert(&self.rx.socket.completion == completion);
|
|
618
|
+
self.do_receiver(bytes);
|
|
619
|
+
}
|
|
620
|
+
}.run();
|
|
621
|
+
}
|