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
|
@@ -20,12 +20,20 @@ const vsr = @import("vsr.zig");
|
|
|
20
20
|
const Replica = vsr.Replica(StateMachine, MessageBus, Storage, Time);
|
|
21
21
|
|
|
22
22
|
pub fn main() !void {
|
|
23
|
-
var
|
|
24
|
-
|
|
23
|
+
var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
|
|
24
|
+
defer arena.deinit();
|
|
25
25
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
26
|
+
const allocator = arena.allocator();
|
|
27
|
+
|
|
28
|
+
switch (cli.parse_args(allocator)) {
|
|
29
|
+
.init => |args| try init(args.cluster, args.replica, args.dir_fd),
|
|
30
|
+
.start => |args| try start(
|
|
31
|
+
allocator,
|
|
32
|
+
args.cluster,
|
|
33
|
+
args.replica,
|
|
34
|
+
args.addresses,
|
|
35
|
+
args.dir_fd,
|
|
36
|
+
),
|
|
29
37
|
}
|
|
30
38
|
}
|
|
31
39
|
|
|
@@ -34,7 +42,7 @@ const filename_fmt = "cluster_{d:0>10}_replica_{d:0>3}.tigerbeetle";
|
|
|
34
42
|
const filename_len = fmt.count(filename_fmt, .{ 0, 0 });
|
|
35
43
|
|
|
36
44
|
/// Create a .tigerbeetle data file for the given args and exit
|
|
37
|
-
fn init(
|
|
45
|
+
fn init(cluster: u32, replica: u8, dir_fd: os.fd_t) !void {
|
|
38
46
|
// Add 1 for the terminating null byte
|
|
39
47
|
var buffer: [filename_len + 1]u8 = undefined;
|
|
40
48
|
const filename = fmt.bufPrintZ(&buffer, filename_fmt, .{ cluster, replica }) catch unreachable;
|
|
@@ -53,7 +61,7 @@ fn init(arena: *mem.Allocator, cluster: u32, replica: u8, dir_fd: os.fd_t) !void
|
|
|
53
61
|
|
|
54
62
|
/// Run as a replica server defined by the given args
|
|
55
63
|
fn start(
|
|
56
|
-
|
|
64
|
+
allocator: mem.Allocator,
|
|
57
65
|
cluster: u32,
|
|
58
66
|
replica_index: u8,
|
|
59
67
|
addresses: []std.net.Address,
|
|
@@ -75,14 +83,14 @@ fn start(
|
|
|
75
83
|
);
|
|
76
84
|
var io = try IO.init(128, 0);
|
|
77
85
|
var state_machine = try StateMachine.init(
|
|
78
|
-
|
|
86
|
+
allocator,
|
|
79
87
|
config.accounts_max,
|
|
80
88
|
config.transfers_max,
|
|
81
89
|
config.commits_max,
|
|
82
90
|
);
|
|
83
91
|
var storage = try Storage.init(config.journal_size_max, storage_fd, &io);
|
|
84
92
|
var message_bus = try MessageBus.init(
|
|
85
|
-
|
|
93
|
+
allocator,
|
|
86
94
|
cluster,
|
|
87
95
|
addresses,
|
|
88
96
|
replica_index,
|
|
@@ -90,7 +98,7 @@ fn start(
|
|
|
90
98
|
);
|
|
91
99
|
var time: Time = .{};
|
|
92
100
|
var replica = try Replica.init(
|
|
93
|
-
|
|
101
|
+
allocator,
|
|
94
102
|
cluster,
|
|
95
103
|
@intCast(u8, addresses.len),
|
|
96
104
|
replica_index,
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
const std = @import("std");
|
|
2
|
+
const builtin = @import("builtin");
|
|
2
3
|
const assert = std.debug.assert;
|
|
3
4
|
const mem = std.mem;
|
|
4
5
|
const os = std.os;
|
|
5
6
|
|
|
6
|
-
const
|
|
7
|
-
const sock_flags = os.SOCK_CLOEXEC | (if (is_darwin) os.SOCK_NONBLOCK else 0);
|
|
7
|
+
const is_linux = builtin.target.os.tag == .linux;
|
|
8
8
|
|
|
9
9
|
const config = @import("config.zig");
|
|
10
10
|
const log = std.log.scoped(.message_bus);
|
|
@@ -54,8 +54,8 @@ fn MessageBusImpl(comptime process_type: ProcessType) type {
|
|
|
54
54
|
|
|
55
55
|
/// The callback to be called when a message is received. Use set_on_message() to set
|
|
56
56
|
/// with type safety for the context pointer.
|
|
57
|
-
on_message_callback: ?fn (context: ?*
|
|
58
|
-
on_message_context: ?*
|
|
57
|
+
on_message_callback: ?fn (context: ?*anyopaque, message: *Message) void = null,
|
|
58
|
+
on_message_context: ?*anyopaque = null,
|
|
59
59
|
|
|
60
60
|
/// This slice is allocated with a fixed size in the init function and never reallocated.
|
|
61
61
|
connections: []Connection,
|
|
@@ -75,7 +75,7 @@ fn MessageBusImpl(comptime process_type: ProcessType) type {
|
|
|
75
75
|
|
|
76
76
|
/// Initialize the MessageBus for the given cluster, configuration and replica/client process.
|
|
77
77
|
pub fn init(
|
|
78
|
-
allocator:
|
|
78
|
+
allocator: mem.Allocator,
|
|
79
79
|
cluster: u32,
|
|
80
80
|
configuration: []std.net.Address,
|
|
81
81
|
process: switch (process_type) {
|
|
@@ -124,7 +124,7 @@ fn MessageBusImpl(comptime process_type: ProcessType) type {
|
|
|
124
124
|
|
|
125
125
|
// Pre-allocate enough memory to hold all possible connections in the client map.
|
|
126
126
|
if (process_type == .replica) {
|
|
127
|
-
try bus.process.clients.
|
|
127
|
+
try bus.process.clients.ensureTotalCapacity(allocator, config.connections_max);
|
|
128
128
|
}
|
|
129
129
|
|
|
130
130
|
return bus;
|
|
@@ -140,7 +140,7 @@ fn MessageBusImpl(comptime process_type: ProcessType) type {
|
|
|
140
140
|
assert(bus.on_message_context == null);
|
|
141
141
|
|
|
142
142
|
bus.on_message_callback = struct {
|
|
143
|
-
fn wrapper(_context: ?*
|
|
143
|
+
fn wrapper(_context: ?*anyopaque, message: *Message) void {
|
|
144
144
|
on_message(@intToPtr(Context, @ptrToInt(_context)), message);
|
|
145
145
|
}
|
|
146
146
|
}.wrapper;
|
|
@@ -148,13 +148,13 @@ fn MessageBusImpl(comptime process_type: ProcessType) type {
|
|
|
148
148
|
}
|
|
149
149
|
|
|
150
150
|
/// TODO This is required by the Client.
|
|
151
|
-
pub fn deinit(
|
|
151
|
+
pub fn deinit(_: *Self) void {}
|
|
152
152
|
|
|
153
153
|
fn init_tcp(address: std.net.Address) !os.socket_t {
|
|
154
|
-
const fd = try
|
|
154
|
+
const fd = try IO.openSocket(
|
|
155
155
|
address.any.family,
|
|
156
|
-
os.
|
|
157
|
-
os.
|
|
156
|
+
os.SOCK.STREAM,
|
|
157
|
+
os.IPPROTO.TCP,
|
|
158
158
|
);
|
|
159
159
|
errdefer os.close(fd);
|
|
160
160
|
|
|
@@ -164,71 +164,55 @@ fn MessageBusImpl(comptime process_type: ProcessType) type {
|
|
|
164
164
|
}
|
|
165
165
|
}.set;
|
|
166
166
|
|
|
167
|
-
// darwin doesn't support os.MSG_NOSIGNAL, but instead a socket option to avoid SIGPIPE.
|
|
168
|
-
if (is_darwin) {
|
|
169
|
-
try set(fd, os.SOL_SOCKET, os.SO_NOSIGPIPE, 1);
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
// Set tcp recv buffer size
|
|
173
167
|
if (config.tcp_rcvbuf > 0) rcvbuf: {
|
|
174
|
-
if (
|
|
168
|
+
if (is_linux) {
|
|
175
169
|
// Requires CAP_NET_ADMIN privilege (settle for SO_RCVBUF in case of an EPERM):
|
|
176
|
-
if (set(fd, os.
|
|
170
|
+
if (set(fd, os.SOL.SOCKET, os.SO.RCVBUFFORCE, config.tcp_rcvbuf)) |_| {
|
|
177
171
|
break :rcvbuf;
|
|
178
172
|
} else |err| switch (err) {
|
|
179
173
|
error.PermissionDenied => {},
|
|
180
174
|
else => |e| return e,
|
|
181
175
|
}
|
|
182
176
|
}
|
|
183
|
-
try set(fd, os.
|
|
177
|
+
try set(fd, os.SOL.SOCKET, os.SO.RCVBUF, config.tcp_rcvbuf);
|
|
184
178
|
}
|
|
185
179
|
|
|
186
|
-
// Set tcp send buffer size
|
|
187
180
|
if (config.tcp_sndbuf > 0) sndbuf: {
|
|
188
|
-
if (
|
|
181
|
+
if (is_linux) {
|
|
189
182
|
// Requires CAP_NET_ADMIN privilege (settle for SO_SNDBUF in case of an EPERM):
|
|
190
|
-
if (set(fd, os.
|
|
183
|
+
if (set(fd, os.SOL.SOCKET, os.SO.SNDBUFFORCE, config.tcp_sndbuf)) |_| {
|
|
191
184
|
break :sndbuf;
|
|
192
185
|
} else |err| switch (err) {
|
|
193
186
|
error.PermissionDenied => {},
|
|
194
187
|
else => |e| return e,
|
|
195
188
|
}
|
|
196
189
|
}
|
|
197
|
-
try set(fd, os.
|
|
190
|
+
try set(fd, os.SOL.SOCKET, os.SO.SNDBUF, config.tcp_sndbuf);
|
|
198
191
|
}
|
|
199
192
|
|
|
200
|
-
// Set tcp keep alive
|
|
201
193
|
if (config.tcp_keepalive) {
|
|
202
|
-
try set(fd, os.
|
|
203
|
-
if (
|
|
204
|
-
try set(fd, os.
|
|
205
|
-
try set(fd, os.
|
|
206
|
-
try set(fd, os.
|
|
194
|
+
try set(fd, os.SOL.SOCKET, os.SO.KEEPALIVE, 1);
|
|
195
|
+
if (is_linux) {
|
|
196
|
+
try set(fd, os.IPPROTO.TCP, os.TCP.KEEPIDLE, config.tcp_keepidle);
|
|
197
|
+
try set(fd, os.IPPROTO.TCP, os.TCP.KEEPINTVL, config.tcp_keepintvl);
|
|
198
|
+
try set(fd, os.IPPROTO.TCP, os.TCP.KEEPCNT, config.tcp_keepcnt);
|
|
207
199
|
}
|
|
208
200
|
}
|
|
209
201
|
|
|
210
|
-
// Set tcp user timeout
|
|
211
202
|
if (config.tcp_user_timeout > 0) {
|
|
212
|
-
if (
|
|
213
|
-
try set(fd, os.
|
|
203
|
+
if (is_linux) {
|
|
204
|
+
try set(fd, os.IPPROTO.TCP, os.TCP.USER_TIMEOUT, config.tcp_user_timeout);
|
|
214
205
|
}
|
|
215
206
|
}
|
|
216
207
|
|
|
217
208
|
// Set tcp no-delay
|
|
218
209
|
if (config.tcp_nodelay) {
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
else if (is_darwin)
|
|
222
|
-
@as(u32, 1)
|
|
223
|
-
else
|
|
224
|
-
null;
|
|
225
|
-
|
|
226
|
-
if (TCP_NODELAY) |tcp_nodelay| {
|
|
227
|
-
try set(fd, os.IPPROTO_TCP, tcp_nodelay, 1);
|
|
210
|
+
if (is_linux) {
|
|
211
|
+
try set(fd, os.IPPROTO.TCP, os.TCP.NODELAY, 1);
|
|
228
212
|
}
|
|
229
213
|
}
|
|
230
214
|
|
|
231
|
-
try set(fd, os.
|
|
215
|
+
try set(fd, os.SOL.SOCKET, os.SO.REUSEADDR, 1);
|
|
232
216
|
try os.bind(fd, &address.any, address.getOsSockLen());
|
|
233
217
|
try os.listen(fd, config.tcp_backlog);
|
|
234
218
|
|
|
@@ -289,7 +273,7 @@ fn MessageBusImpl(comptime process_type: ProcessType) type {
|
|
|
289
273
|
if (connection.state == .terminating) return;
|
|
290
274
|
}
|
|
291
275
|
|
|
292
|
-
log.
|
|
276
|
+
log.info("all connections in use but not all replicas are connected, " ++
|
|
293
277
|
"attempting to disconnect a client", .{});
|
|
294
278
|
for (bus.connections) |*connection| {
|
|
295
279
|
if (connection.peer == .client) {
|
|
@@ -298,7 +282,7 @@ fn MessageBusImpl(comptime process_type: ProcessType) type {
|
|
|
298
282
|
}
|
|
299
283
|
}
|
|
300
284
|
|
|
301
|
-
log.
|
|
285
|
+
log.info("failed to disconnect a client as no peer was a known client, " ++
|
|
302
286
|
"attempting to disconnect an unknown peer.", .{});
|
|
303
287
|
for (bus.connections) |*connection| {
|
|
304
288
|
if (connection.peer == .unknown) {
|
|
@@ -332,7 +316,6 @@ fn MessageBusImpl(comptime process_type: ProcessType) type {
|
|
|
332
316
|
on_accept,
|
|
333
317
|
&bus.process.accept_completion,
|
|
334
318
|
bus.process.accept_fd,
|
|
335
|
-
sock_flags,
|
|
336
319
|
);
|
|
337
320
|
}
|
|
338
321
|
|
|
@@ -341,6 +324,8 @@ fn MessageBusImpl(comptime process_type: ProcessType) type {
|
|
|
341
324
|
completion: *IO.Completion,
|
|
342
325
|
result: IO.AcceptError!os.socket_t,
|
|
343
326
|
) void {
|
|
327
|
+
_ = completion;
|
|
328
|
+
|
|
344
329
|
comptime assert(process_type == .replica);
|
|
345
330
|
assert(bus.process.accept_connection != null);
|
|
346
331
|
defer bus.process.accept_connection = null;
|
|
@@ -459,7 +444,7 @@ fn MessageBusImpl(comptime process_type: ProcessType) type {
|
|
|
459
444
|
// The first replica's network address family determines the
|
|
460
445
|
// family for all other replicas:
|
|
461
446
|
const family = bus.configuration[0].any.family;
|
|
462
|
-
connection.fd =
|
|
447
|
+
connection.fd = IO.openSocket(family, os.SOCK.STREAM, os.IPPROTO.TCP) catch return;
|
|
463
448
|
connection.peer = .{ .replica = replica };
|
|
464
449
|
connection.state = .connecting;
|
|
465
450
|
bus.connections_used += 1;
|
|
@@ -469,7 +454,7 @@ fn MessageBusImpl(comptime process_type: ProcessType) type {
|
|
|
469
454
|
|
|
470
455
|
var attempts = &bus.replicas_connect_attempts[replica];
|
|
471
456
|
const ms = vsr.exponential_backoff_with_jitter(
|
|
472
|
-
|
|
457
|
+
bus.prng.random(),
|
|
473
458
|
config.connection_delay_min_ms,
|
|
474
459
|
config.connection_delay_max_ms,
|
|
475
460
|
attempts.*,
|
|
@@ -600,7 +585,7 @@ fn MessageBusImpl(comptime process_type: ProcessType) type {
|
|
|
600
585
|
connection.send_queue.push(message.ref()) catch |err| switch (err) {
|
|
601
586
|
error.NoSpaceLeft => {
|
|
602
587
|
bus.unref(message);
|
|
603
|
-
log.
|
|
588
|
+
log.info("message queue for peer {} full, dropping message", .{
|
|
604
589
|
connection.peer,
|
|
605
590
|
});
|
|
606
591
|
return;
|
|
@@ -633,12 +618,12 @@ fn MessageBusImpl(comptime process_type: ProcessType) type {
|
|
|
633
618
|
//
|
|
634
619
|
// TODO: Investigate differences between shutdown() on Linux vs Darwin.
|
|
635
620
|
// Especially how this interacts with our assumptions around pending I/O.
|
|
636
|
-
const rc = os.system.shutdown(connection.fd, os.
|
|
621
|
+
const rc = os.system.shutdown(connection.fd, os.SHUT.RDWR);
|
|
637
622
|
switch (os.errno(rc)) {
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
623
|
+
.SUCCESS => {},
|
|
624
|
+
.BADF => unreachable,
|
|
625
|
+
.INVAL => unreachable,
|
|
626
|
+
.NOTCONN => {
|
|
642
627
|
// This should only happen if we for some reason decide to terminate
|
|
643
628
|
// a connection while a connect operation is in progress.
|
|
644
629
|
// This is fine though, we simply continue with the logic below and
|
|
@@ -653,7 +638,7 @@ fn MessageBusImpl(comptime process_type: ProcessType) type {
|
|
|
653
638
|
//assert(connection.recv_submitted);
|
|
654
639
|
//assert(!connection.send_submitted);
|
|
655
640
|
},
|
|
656
|
-
|
|
641
|
+
.NOTSOCK => unreachable,
|
|
657
642
|
else => |err| os.unexpectedErrno(err) catch {},
|
|
658
643
|
}
|
|
659
644
|
},
|
|
@@ -886,7 +871,6 @@ fn MessageBusImpl(comptime process_type: ProcessType) type {
|
|
|
886
871
|
&connection.recv_completion,
|
|
887
872
|
connection.fd,
|
|
888
873
|
connection.recv_message.?.buffer[connection.recv_progress..config.message_size_max],
|
|
889
|
-
if (is_darwin) 0 else os.MSG_NOSIGNAL,
|
|
890
874
|
);
|
|
891
875
|
}
|
|
892
876
|
|
|
@@ -919,7 +903,7 @@ fn MessageBusImpl(comptime process_type: ProcessType) type {
|
|
|
919
903
|
assert(connection.peer == .client or connection.peer == .replica);
|
|
920
904
|
assert(connection.state == .connected);
|
|
921
905
|
assert(connection.fd != -1);
|
|
922
|
-
const message = connection.send_queue.
|
|
906
|
+
const message = connection.send_queue.head() orelse return;
|
|
923
907
|
assert(!connection.send_submitted);
|
|
924
908
|
connection.send_submitted = true;
|
|
925
909
|
bus.io.send(
|
|
@@ -929,7 +913,6 @@ fn MessageBusImpl(comptime process_type: ProcessType) type {
|
|
|
929
913
|
&connection.send_completion,
|
|
930
914
|
connection.fd,
|
|
931
915
|
message.buffer[connection.send_progress..message.header.size],
|
|
932
|
-
if (is_darwin) 0 else os.MSG_NOSIGNAL,
|
|
933
916
|
);
|
|
934
917
|
}
|
|
935
918
|
|
|
@@ -949,9 +932,9 @@ fn MessageBusImpl(comptime process_type: ProcessType) type {
|
|
|
949
932
|
connection.terminate(bus, .shutdown);
|
|
950
933
|
return;
|
|
951
934
|
};
|
|
952
|
-
assert(connection.send_progress <= connection.send_queue.
|
|
935
|
+
assert(connection.send_progress <= connection.send_queue.head().?.header.size);
|
|
953
936
|
// If the message has been fully sent, move on to the next one.
|
|
954
|
-
if (connection.send_progress == connection.send_queue.
|
|
937
|
+
if (connection.send_progress == connection.send_queue.head().?.header.size) {
|
|
955
938
|
connection.send_progress = 0;
|
|
956
939
|
const message = connection.send_queue.pop().?;
|
|
957
940
|
bus.unref(message);
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
const std = @import("std");
|
|
2
|
+
const builtin = @import("builtin");
|
|
2
3
|
const assert = std.debug.assert;
|
|
3
4
|
const mem = std.mem;
|
|
4
5
|
|
|
@@ -51,7 +52,7 @@ pub const MessagePool = struct {
|
|
|
51
52
|
/// List of currently usused header-sized messages
|
|
52
53
|
header_only_free_list: ?*Message,
|
|
53
54
|
|
|
54
|
-
pub fn init(allocator:
|
|
55
|
+
pub fn init(allocator: mem.Allocator) error{OutOfMemory}!MessagePool {
|
|
55
56
|
var ret: MessagePool = .{
|
|
56
57
|
.free_list = null,
|
|
57
58
|
.header_only_free_list = null,
|
|
@@ -119,7 +120,7 @@ pub const MessagePool = struct {
|
|
|
119
120
|
pub fn unref(pool: *MessagePool, message: *Message) void {
|
|
120
121
|
message.references -= 1;
|
|
121
122
|
if (message.references == 0) {
|
|
122
|
-
if (
|
|
123
|
+
if (builtin.mode == .Debug) mem.set(u8, message.buffer, undefined);
|
|
123
124
|
if (message.header_only()) {
|
|
124
125
|
message.next = pool.header_only_free_list;
|
|
125
126
|
pool.header_only_free_list = message;
|
|
@@ -14,51 +14,84 @@ pub fn RingBuffer(comptime T: type, comptime size: usize) type {
|
|
|
14
14
|
/// The number of items in the buffer.
|
|
15
15
|
count: usize = 0,
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
self.buffer[(self.index + self.count) % self.buffer.len] = item;
|
|
22
|
-
self.count += 1;
|
|
17
|
+
// TODO add doc comments to these functions:
|
|
18
|
+
pub inline fn head(self: Self) ?T {
|
|
19
|
+
if (self.empty()) return null;
|
|
20
|
+
return self.buffer[self.index];
|
|
23
21
|
}
|
|
24
22
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
return
|
|
23
|
+
pub inline fn head_ptr(self: *Self) ?*T {
|
|
24
|
+
if (self.empty()) return null;
|
|
25
|
+
return &self.buffer[self.index];
|
|
28
26
|
}
|
|
29
27
|
|
|
30
|
-
|
|
31
|
-
pub fn peek_ptr(self: *Self) ?*T {
|
|
28
|
+
pub inline fn tail(self: Self) ?T {
|
|
32
29
|
if (self.empty()) return null;
|
|
33
|
-
return
|
|
30
|
+
return self.buffer[(self.index + self.count - 1) % self.buffer.len];
|
|
34
31
|
}
|
|
35
32
|
|
|
36
|
-
|
|
37
|
-
pub fn pop(self: *Self) ?T {
|
|
33
|
+
pub inline fn tail_ptr(self: *Self) ?*T {
|
|
38
34
|
if (self.empty()) return null;
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
35
|
+
return &self.buffer[(self.index + self.count - 1) % self.buffer.len];
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
pub inline fn next_tail(self: Self) ?T {
|
|
39
|
+
if (self.full()) return null;
|
|
40
|
+
return self.buffer[(self.index + self.count) % self.buffer.len];
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
pub inline fn next_tail_ptr(self: *Self) ?*T {
|
|
44
|
+
if (self.full()) return null;
|
|
45
|
+
return &self.buffer[(self.index + self.count) % self.buffer.len];
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
pub inline fn advance_head(self: *Self) void {
|
|
49
|
+
self.index += 1;
|
|
50
|
+
self.index %= self.buffer.len;
|
|
51
|
+
self.count -= 1;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
pub inline fn advance_tail(self: *Self) void {
|
|
55
|
+
assert(self.count < self.buffer.len);
|
|
56
|
+
self.count += 1;
|
|
44
57
|
}
|
|
45
58
|
|
|
46
59
|
/// Returns whether the ring buffer is completely full.
|
|
47
|
-
pub fn full(self:
|
|
60
|
+
pub inline fn full(self: Self) bool {
|
|
48
61
|
return self.count == self.buffer.len;
|
|
49
62
|
}
|
|
50
63
|
|
|
51
64
|
/// Returns whether the ring buffer is completely empty.
|
|
52
|
-
pub fn empty(self:
|
|
65
|
+
pub inline fn empty(self: Self) bool {
|
|
53
66
|
return self.count == 0;
|
|
54
67
|
}
|
|
55
68
|
|
|
69
|
+
// Higher level, less error-prone wrappers:
|
|
70
|
+
|
|
71
|
+
/// Add an element to the RingBuffer. Returns an error if the buffer
|
|
72
|
+
/// is already full and the element could not be added.
|
|
73
|
+
pub fn push(self: *Self, item: T) error{NoSpaceLeft}!void {
|
|
74
|
+
const ptr = self.next_tail_ptr() orelse return error.NoSpaceLeft;
|
|
75
|
+
ptr.* = item;
|
|
76
|
+
self.advance_tail();
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/// Remove and return the next item, if any.
|
|
80
|
+
pub fn pop(self: *Self) ?T {
|
|
81
|
+
const result = self.head() orelse return null;
|
|
82
|
+
self.advance_head();
|
|
83
|
+
return result;
|
|
84
|
+
}
|
|
85
|
+
|
|
56
86
|
pub const Iterator = struct {
|
|
57
87
|
ring: *Self,
|
|
58
88
|
count: usize = 0,
|
|
59
89
|
|
|
60
90
|
pub fn next(it: *Iterator) ?T {
|
|
61
|
-
|
|
91
|
+
assert(it.count <= it.ring.count);
|
|
92
|
+
if (it.count == it.ring.count) return null;
|
|
93
|
+
defer it.count += 1;
|
|
94
|
+
return it.ring.buffer[(it.ring.index + it.count) % it.ring.buffer.len];
|
|
62
95
|
}
|
|
63
96
|
|
|
64
97
|
pub fn next_ptr(it: *Iterator) ?*T {
|
|
@@ -79,43 +112,6 @@ pub fn RingBuffer(comptime T: type, comptime size: usize) type {
|
|
|
79
112
|
|
|
80
113
|
const testing = std.testing;
|
|
81
114
|
|
|
82
|
-
test "push/peek/pop/full/empty" {
|
|
83
|
-
var fifo = RingBuffer(u32, 3){};
|
|
84
|
-
|
|
85
|
-
try testing.expect(!fifo.full());
|
|
86
|
-
try testing.expect(fifo.empty());
|
|
87
|
-
|
|
88
|
-
try fifo.push(1);
|
|
89
|
-
try testing.expectEqual(@as(?u32, 1), fifo.peek());
|
|
90
|
-
|
|
91
|
-
try testing.expect(!fifo.full());
|
|
92
|
-
try testing.expect(!fifo.empty());
|
|
93
|
-
|
|
94
|
-
try fifo.push(2);
|
|
95
|
-
try testing.expectEqual(@as(?u32, 1), fifo.peek());
|
|
96
|
-
|
|
97
|
-
try fifo.push(3);
|
|
98
|
-
try testing.expectError(error.NoSpaceLeft, fifo.push(4));
|
|
99
|
-
|
|
100
|
-
try testing.expect(fifo.full());
|
|
101
|
-
try testing.expect(!fifo.empty());
|
|
102
|
-
|
|
103
|
-
try testing.expectEqual(@as(?u32, 1), fifo.peek());
|
|
104
|
-
try testing.expectEqual(@as(?u32, 1), fifo.pop());
|
|
105
|
-
|
|
106
|
-
try testing.expect(!fifo.full());
|
|
107
|
-
try testing.expect(!fifo.empty());
|
|
108
|
-
|
|
109
|
-
fifo.peek_ptr().?.* += 1000;
|
|
110
|
-
|
|
111
|
-
try testing.expectEqual(@as(?u32, 1002), fifo.pop());
|
|
112
|
-
try testing.expectEqual(@as(?u32, 3), fifo.pop());
|
|
113
|
-
try testing.expectEqual(@as(?u32, null), fifo.pop());
|
|
114
|
-
|
|
115
|
-
try testing.expect(!fifo.full());
|
|
116
|
-
try testing.expect(fifo.empty());
|
|
117
|
-
}
|
|
118
|
-
|
|
119
115
|
fn test_iterator(comptime T: type, ring: *T, values: []const u32) !void {
|
|
120
116
|
const ring_index = ring.index;
|
|
121
117
|
|
|
@@ -133,22 +129,41 @@ fn test_iterator(comptime T: type, ring: *T, values: []const u32) !void {
|
|
|
133
129
|
try testing.expectEqual(ring_index, ring.index);
|
|
134
130
|
}
|
|
135
131
|
|
|
136
|
-
test "
|
|
132
|
+
test "RingBuffer: low level interface" {
|
|
137
133
|
const Ring = RingBuffer(u32, 2);
|
|
138
134
|
|
|
139
135
|
var ring = Ring{};
|
|
140
136
|
try test_iterator(Ring, &ring, &[_]u32{});
|
|
141
137
|
|
|
142
|
-
try ring.
|
|
138
|
+
try testing.expectEqual(@as(?u32, null), ring.head());
|
|
139
|
+
try testing.expectEqual(@as(?*u32, null), ring.head_ptr());
|
|
140
|
+
try testing.expectEqual(@as(?u32, null), ring.tail());
|
|
141
|
+
try testing.expectEqual(@as(?*u32, null), ring.tail_ptr());
|
|
142
|
+
|
|
143
|
+
ring.next_tail_ptr().?.* = 0;
|
|
144
|
+
ring.advance_tail();
|
|
145
|
+
try testing.expectEqual(@as(?u32, 0), ring.tail());
|
|
146
|
+
try testing.expectEqual(@as(u32, 0), ring.tail_ptr().?.*);
|
|
143
147
|
try test_iterator(Ring, &ring, &[_]u32{0});
|
|
144
148
|
|
|
145
|
-
|
|
149
|
+
ring.next_tail_ptr().?.* = 1;
|
|
150
|
+
ring.advance_tail();
|
|
151
|
+
try testing.expectEqual(@as(?u32, 1), ring.tail());
|
|
152
|
+
try testing.expectEqual(@as(u32, 1), ring.tail_ptr().?.*);
|
|
146
153
|
try test_iterator(Ring, &ring, &[_]u32{ 0, 1 });
|
|
147
154
|
|
|
148
|
-
try testing.expectEqual(@as(?u32,
|
|
155
|
+
try testing.expectEqual(@as(?u32, null), ring.next_tail());
|
|
156
|
+
try testing.expectEqual(@as(?*u32, null), ring.next_tail_ptr());
|
|
157
|
+
|
|
158
|
+
try testing.expectEqual(@as(?u32, 0), ring.head());
|
|
159
|
+
try testing.expectEqual(@as(u32, 0), ring.head_ptr().?.*);
|
|
160
|
+
ring.advance_head();
|
|
149
161
|
try test_iterator(Ring, &ring, &[_]u32{1});
|
|
150
162
|
|
|
151
|
-
|
|
163
|
+
ring.next_tail_ptr().?.* = 2;
|
|
164
|
+
ring.advance_tail();
|
|
165
|
+
try testing.expectEqual(@as(?u32, 2), ring.tail());
|
|
166
|
+
try testing.expectEqual(@as(u32, 2), ring.tail_ptr().?.*);
|
|
152
167
|
try test_iterator(Ring, &ring, &[_]u32{ 1, 2 });
|
|
153
168
|
|
|
154
169
|
var iterator = ring.iterator();
|
|
@@ -156,15 +171,67 @@ test "iterator" {
|
|
|
156
171
|
item_ptr.* += 1000;
|
|
157
172
|
}
|
|
158
173
|
|
|
159
|
-
try testing.expectEqual(@as(?u32, 1001), ring.
|
|
174
|
+
try testing.expectEqual(@as(?u32, 1001), ring.head());
|
|
175
|
+
try testing.expectEqual(@as(u32, 1001), ring.head_ptr().?.*);
|
|
176
|
+
ring.advance_head();
|
|
160
177
|
try test_iterator(Ring, &ring, &[_]u32{1002});
|
|
161
178
|
|
|
162
|
-
|
|
179
|
+
ring.next_tail_ptr().?.* = 3;
|
|
180
|
+
ring.advance_tail();
|
|
181
|
+
try testing.expectEqual(@as(?u32, 3), ring.tail());
|
|
182
|
+
try testing.expectEqual(@as(u32, 3), ring.tail_ptr().?.*);
|
|
163
183
|
try test_iterator(Ring, &ring, &[_]u32{ 1002, 3 });
|
|
164
184
|
|
|
165
|
-
try testing.expectEqual(@as(?u32, 1002), ring.
|
|
185
|
+
try testing.expectEqual(@as(?u32, 1002), ring.head());
|
|
186
|
+
try testing.expectEqual(@as(u32, 1002), ring.head_ptr().?.*);
|
|
187
|
+
ring.advance_head();
|
|
166
188
|
try test_iterator(Ring, &ring, &[_]u32{3});
|
|
167
189
|
|
|
168
|
-
try testing.expectEqual(@as(?u32, 3), ring.
|
|
190
|
+
try testing.expectEqual(@as(?u32, 3), ring.head());
|
|
191
|
+
try testing.expectEqual(@as(u32, 3), ring.head_ptr().?.*);
|
|
192
|
+
ring.advance_head();
|
|
169
193
|
try test_iterator(Ring, &ring, &[_]u32{});
|
|
194
|
+
|
|
195
|
+
try testing.expectEqual(@as(?u32, null), ring.head());
|
|
196
|
+
try testing.expectEqual(@as(?*u32, null), ring.head_ptr());
|
|
197
|
+
try testing.expectEqual(@as(?u32, null), ring.tail());
|
|
198
|
+
try testing.expectEqual(@as(?*u32, null), ring.tail_ptr());
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
test "RingBuffer: push/pop high level interface" {
|
|
202
|
+
var fifo = RingBuffer(u32, 3){};
|
|
203
|
+
|
|
204
|
+
try testing.expect(!fifo.full());
|
|
205
|
+
try testing.expect(fifo.empty());
|
|
206
|
+
|
|
207
|
+
try fifo.push(1);
|
|
208
|
+
try testing.expectEqual(@as(?u32, 1), fifo.head());
|
|
209
|
+
|
|
210
|
+
try testing.expect(!fifo.full());
|
|
211
|
+
try testing.expect(!fifo.empty());
|
|
212
|
+
|
|
213
|
+
try fifo.push(2);
|
|
214
|
+
try testing.expectEqual(@as(?u32, 1), fifo.head());
|
|
215
|
+
|
|
216
|
+
try fifo.push(3);
|
|
217
|
+
try testing.expectError(error.NoSpaceLeft, fifo.push(4));
|
|
218
|
+
|
|
219
|
+
try testing.expect(fifo.full());
|
|
220
|
+
try testing.expect(!fifo.empty());
|
|
221
|
+
|
|
222
|
+
try testing.expectEqual(@as(?u32, 1), fifo.head());
|
|
223
|
+
try testing.expectEqual(@as(?u32, 1), fifo.pop());
|
|
224
|
+
|
|
225
|
+
try testing.expect(!fifo.full());
|
|
226
|
+
try testing.expect(!fifo.empty());
|
|
227
|
+
|
|
228
|
+
try fifo.push(4);
|
|
229
|
+
|
|
230
|
+
try testing.expectEqual(@as(?u32, 2), fifo.pop());
|
|
231
|
+
try testing.expectEqual(@as(?u32, 3), fifo.pop());
|
|
232
|
+
try testing.expectEqual(@as(?u32, 4), fifo.pop());
|
|
233
|
+
try testing.expectEqual(@as(?u32, null), fifo.pop());
|
|
234
|
+
|
|
235
|
+
try testing.expect(!fifo.full());
|
|
236
|
+
try testing.expect(fifo.empty());
|
|
170
237
|
}
|