tigerbeetle-node 0.5.2 → 0.6.0

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.
Files changed (34) hide show
  1. package/README.md +3 -4
  2. package/package.json +1 -1
  3. package/src/node.zig +2 -12
  4. package/src/tigerbeetle/scripts/benchmark.bat +46 -0
  5. package/src/tigerbeetle/scripts/install_zig.bat +2 -2
  6. package/src/tigerbeetle/scripts/install_zig.sh +1 -1
  7. package/src/tigerbeetle/scripts/vopr.sh +2 -2
  8. package/src/tigerbeetle/src/benchmark.zig +2 -6
  9. package/src/tigerbeetle/src/cli.zig +39 -18
  10. package/src/tigerbeetle/src/config.zig +24 -9
  11. package/src/tigerbeetle/src/demo.zig +1 -1
  12. package/src/tigerbeetle/src/io/benchmark.zig +24 -49
  13. package/src/tigerbeetle/src/io/darwin.zig +175 -44
  14. package/src/tigerbeetle/src/io/linux.zig +177 -72
  15. package/src/tigerbeetle/src/io/test.zig +61 -39
  16. package/src/tigerbeetle/src/io/windows.zig +1161 -0
  17. package/src/tigerbeetle/src/io.zig +2 -0
  18. package/src/tigerbeetle/src/main.zig +13 -8
  19. package/src/tigerbeetle/src/message_bus.zig +49 -61
  20. package/src/tigerbeetle/src/message_pool.zig +63 -57
  21. package/src/tigerbeetle/src/ring_buffer.zig +7 -0
  22. package/src/tigerbeetle/src/simulator.zig +4 -4
  23. package/src/tigerbeetle/src/storage.zig +0 -230
  24. package/src/tigerbeetle/src/test/cluster.zig +3 -6
  25. package/src/tigerbeetle/src/test/message_bus.zig +4 -3
  26. package/src/tigerbeetle/src/test/network.zig +13 -16
  27. package/src/tigerbeetle/src/test/state_checker.zig +3 -2
  28. package/src/tigerbeetle/src/tigerbeetle.zig +5 -3
  29. package/src/tigerbeetle/src/time.zig +58 -11
  30. package/src/tigerbeetle/src/vsr/client.zig +18 -32
  31. package/src/tigerbeetle/src/vsr/clock.zig +1 -1
  32. package/src/tigerbeetle/src/vsr/journal.zig +2 -6
  33. package/src/tigerbeetle/src/vsr/replica.zig +146 -169
  34. package/src/tigerbeetle/src/vsr.zig +263 -5
package/README.md CHANGED
@@ -9,13 +9,12 @@ The following steps will install the `tigerbeetle-node` module to your current w
9
9
  * NodeJS >= `14.0.0`. _(If the correct version is not installed, an installation error will occur)_
10
10
 
11
11
  > Your operating system should be Linux (kernel >= v5.6) or macOS. Windows support is not yet available but is in the works.
12
-
13
- > Ensure you are not currently in the project (tigerbeetle-node) directory, but the `parent` directory instead;
14
12
 
15
13
  ### YARN Package Manager
16
14
 
17
15
  ```sh
18
- yarn add tigerbeetle-node
16
+ # Run the following from this directory
17
+ yarn add
19
18
  ```
20
19
  or
21
20
 
@@ -146,7 +145,7 @@ const errors = await client.createTransfers([transfer])
146
145
  Two-phase transfers are supported natively by toggling the appropriate flag. TigerBeetle will then adjust the `credits_reserved` and `debits_reserved` fields of the appropriate accounts. A corresponding commit transfer then needs to be sent to accept or reject the transfer.
147
146
  | bit 0 | bit 1 | bit 2 |
148
147
  |----------|--------------------|------------------|
149
- | `linked` | `two_phase_commit` | `condition` |
148
+ | `linked` | `posting` | `condition` |
150
149
 
151
150
  The `condition` flag signals to TigerBeetle that a 256-bit cryptographic condition will be supplied in the `reserved` field. This will be validated against a supplied pre-image when the transfer is committed. Transfers within a batch may also be linked (see [linked events](#linked-events)).
152
151
  ```js
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tigerbeetle-node",
3
- "version": "0.5.2",
3
+ "version": "0.6.0",
4
4
  "description": "TigerBeetle Node.js client",
5
5
  "main": "dist/index.js",
6
6
  "typings": "dist/index.d.ts",
package/src/node.zig CHANGED
@@ -579,12 +579,7 @@ fn request(env: c.napi_env, info: c.napi_callback_info) callconv(.C) c.napi_valu
579
579
  translate.throw(env, "Unknown operation.") catch return null;
580
580
  }
581
581
 
582
- const message = context.client.get_message() orelse {
583
- translate.throw(
584
- env,
585
- "Too many requests outstanding.",
586
- ) catch return null;
587
- };
582
+ const message = context.client.get_message();
588
583
  defer context.client.unref(message);
589
584
 
590
585
  const operation = @intToEnum(Operation, @intCast(u8, operation_int));
@@ -634,12 +629,7 @@ fn raw_request(env: c.napi_env, info: c.napi_callback_info) callconv(.C) c.napi_
634
629
  }
635
630
  const operation = @intToEnum(Operation, @intCast(u8, operation_int));
636
631
 
637
- const message = context.client.get_message() orelse {
638
- translate.throw(
639
- env,
640
- "Too many requests outstanding.",
641
- ) catch return null;
642
- };
632
+ const message = context.client.get_message();
643
633
  defer context.client.unref(message);
644
634
 
645
635
  const body_length = translate.bytes_from_buffer(
@@ -0,0 +1,46 @@
1
+ @echo off
2
+ setlocal enabledelayedexpansion
3
+
4
+ if "%~1" equ ":main" (
5
+ shift /1
6
+ goto main
7
+ )
8
+
9
+ cmd /d /c "%~f0" :main %*
10
+ set ZIG_RESULT=%ERRORLEVEL%
11
+ taskkill /F /IM tigerbeetle.exe >nul
12
+
13
+ if !ZIG_RESULT! equ 0 (
14
+ del /f benchmark.log
15
+ ) else (
16
+ echo.
17
+ echo Error running benchmark, here are more details
18
+ type benchmark.log
19
+ )
20
+
21
+ echo.
22
+ exit /b
23
+
24
+ :main
25
+ zig\zig.exe build -Drelease-safe
26
+ move zig-out\bin\tigerbeetle.exe . >nul
27
+
28
+ for /l %%i in (0, 1, 0) do (
29
+ echo Initializing replica %%i
30
+ set ZIG_FILE=.\cluster_0000000000_replica_00%%i.tigerbeetle
31
+ if exist "!ZIG_FILE!" DEL /F "!ZIG_FILE!"
32
+ .\tigerbeetle.exe init --directory=. --cluster=0 --replica=%%i > benchmark.log 2>&1
33
+ )
34
+
35
+ for /l %%i in (0, 1, 0) do (
36
+ echo Starting replica %%i
37
+ start /B "tigerbeetle_%%i" .\tigerbeetle.exe start --directory=. --cluster=0 --addresses=3001 --replica=%%i > benchmark.log 2>&1
38
+ )
39
+
40
+ rem Wait for replicas to start, listen and connect:
41
+ timeout /t 2
42
+
43
+ echo.
44
+ echo Benchmarking...
45
+ zig\zig.exe run -OReleaseSafe src\benchmark.zig
46
+ exit /b %errorlevel%
@@ -1,6 +1,6 @@
1
1
  @echo off
2
2
 
3
- set ZIG_RELEASE_DEFAULT=0.9.0
3
+ set ZIG_RELEASE_DEFAULT=0.9.1
4
4
 
5
5
  :: Determine the Zig build:
6
6
  if "%~1"=="" (
@@ -39,7 +39,7 @@ if "%ZIG_RELEASE%"=="builds" (
39
39
  )
40
40
 
41
41
  :: Using variable modifiers to determine the directory and filename from the URL:
42
- :: %~ni Expands %i to a file name only and %~xi Expands %i to a file name extension only.
42
+ :: %%~ni Expands %%i to a file name only and %%~xi Expands %%i to a file name extension only.
43
43
  for /f %%i in ("%ZIG_URL%") do (
44
44
  set ZIG_DIRECTORY=%%~ni
45
45
  set ZIG_TARBALL=%%~nxi
@@ -1,7 +1,7 @@
1
1
  #!/bin/bash
2
2
  set -e
3
3
 
4
- ZIG_RELEASE_DEFAULT="0.9.0"
4
+ ZIG_RELEASE_DEFAULT="0.9.1"
5
5
 
6
6
  # Default to the release build, or allow the latest dev build, or an explicit release version:
7
7
  if [ -z "$1" ]; then
@@ -1,9 +1,9 @@
1
1
  #!/usr/bin/env bash
2
2
  set -e
3
3
 
4
- # Install Zig 0.8.0 if it does not already exist:
4
+ # Install Zig if it does not already exist:
5
5
  if [ ! -d "zig" ]; then
6
- scripts/install_zig.sh 0.8.0
6
+ scripts/install_zig.sh
7
7
  echo ""
8
8
  echo "Running the TigerBeetle VOPR for the first time..."
9
9
  echo "Visit https://www.tigerbeetle.com"
@@ -228,9 +228,7 @@ const TimedQueue = struct {
228
228
  if (self.batches.head_ptr()) |starting_batch| {
229
229
  log.debug("sending first batch...", .{});
230
230
  self.batch_start = now;
231
- var message = self.client.get_message() orelse {
232
- @panic("Client message pool has been exhausted. Cannot execute batch.");
233
- };
231
+ const message = self.client.get_message();
234
232
  defer self.client.unref(message);
235
233
 
236
234
  std.mem.copy(
@@ -288,9 +286,7 @@ const TimedQueue = struct {
288
286
  }
289
287
 
290
288
  if (self.batches.head_ptr()) |next_batch| {
291
- var message = self.client.get_message() orelse {
292
- @panic("Client message pool has been exhausted.");
293
- };
289
+ const message = self.client.get_message();
294
290
  defer self.client.unref(message);
295
291
 
296
292
  std.mem.copy(
@@ -8,6 +8,7 @@ const os = std.os;
8
8
 
9
9
  const config = @import("config.zig");
10
10
  const vsr = @import("vsr.zig");
11
+ const IO = @import("io.zig").IO;
11
12
 
12
13
  const usage = fmt.comptimePrint(
13
14
  \\Usage:
@@ -87,18 +88,31 @@ pub const Command = union(enum) {
87
88
 
88
89
  /// Parse the command line arguments passed to the tigerbeetle binary.
89
90
  /// Exits the program with a non-zero exit code if an error is found.
90
- pub fn parse_args(allocator: std.mem.Allocator) Command {
91
+ pub fn parse_args(allocator: std.mem.Allocator) !Command {
91
92
  var maybe_cluster: ?[]const u8 = null;
92
93
  var maybe_replica: ?[]const u8 = null;
93
94
  var maybe_addresses: ?[]const u8 = null;
94
95
  var maybe_directory: ?[:0]const u8 = null;
95
96
 
96
- var args = std.process.args();
97
+ var args = try std.process.argsWithAllocator(allocator);
98
+ defer args.deinit();
99
+
100
+ // Keep track of the args from the ArgIterator above that were allocated
101
+ // then free them all at the end of the scope.
102
+ var args_allocated = std.ArrayList([:0]const u8).init(allocator);
103
+ defer {
104
+ for (args_allocated.items) |arg| allocator.free(arg);
105
+ args_allocated.deinit();
106
+ }
107
+
97
108
  // Skip argv[0] which is the name of this executable
98
- _ = args.nextPosix();
109
+ const did_skip = args.skip();
110
+ assert(did_skip);
111
+
112
+ const raw_command = try (args.next(allocator) orelse
113
+ fatal("no command provided, expected 'start' or 'init'", .{}));
114
+ defer allocator.free(raw_command);
99
115
 
100
- const raw_command = args.nextPosix() orelse
101
- fatal("no command provided, expected 'start' or 'init'", .{});
102
116
  if (mem.eql(u8, raw_command, "-h") or mem.eql(u8, raw_command, "--help")) {
103
117
  std.io.getStdOut().writeAll(usage) catch os.exit(1);
104
118
  os.exit(0);
@@ -106,7 +120,10 @@ pub fn parse_args(allocator: std.mem.Allocator) Command {
106
120
  const command = meta.stringToEnum(meta.Tag(Command), raw_command) orelse
107
121
  fatal("unknown command '{s}', expected 'start' or 'init'", .{raw_command});
108
122
 
109
- while (args.nextPosix()) |arg| {
123
+ while (args.next(allocator)) |parsed_arg| {
124
+ const arg = try parsed_arg;
125
+ try args_allocated.append(arg);
126
+
110
127
  if (mem.startsWith(u8, arg, "--cluster")) {
111
128
  maybe_cluster = parse_flag("--cluster", arg);
112
129
  } else if (mem.startsWith(u8, arg, "--replica")) {
@@ -132,7 +149,7 @@ pub fn parse_args(allocator: std.mem.Allocator) Command {
132
149
  const replica = parse_replica(raw_replica);
133
150
 
134
151
  const dir_path = maybe_directory orelse config.directory;
135
- const dir_fd = os.openZ(dir_path, os.O.CLOEXEC | os.O.RDONLY, 0) catch |err|
152
+ const dir_fd = IO.open_dir(dir_path) catch |err|
136
153
  fatal("failed to open directory '{s}': {}", .{ dir_path, err });
137
154
 
138
155
  switch (command) {
@@ -141,11 +158,13 @@ pub fn parse_args(allocator: std.mem.Allocator) Command {
141
158
  fatal("--addresses: supported only by 'start' command", .{});
142
159
  }
143
160
 
144
- return .{ .init = .{
145
- .cluster = cluster,
146
- .replica = replica,
147
- .dir_fd = dir_fd,
148
- } };
161
+ return Command{
162
+ .init = .{
163
+ .cluster = cluster,
164
+ .replica = replica,
165
+ .dir_fd = dir_fd,
166
+ },
167
+ };
149
168
  },
150
169
  .start => {
151
170
  const raw_addresses = maybe_addresses orelse
@@ -156,12 +175,14 @@ pub fn parse_args(allocator: std.mem.Allocator) Command {
156
175
  fatal("--replica: value greater than length of --addresses array", .{});
157
176
  }
158
177
 
159
- return .{ .start = .{
160
- .cluster = cluster,
161
- .replica = replica,
162
- .addresses = addresses,
163
- .dir_fd = dir_fd,
164
- } };
178
+ return Command{
179
+ .start = .{
180
+ .cluster = cluster,
181
+ .replica = replica,
182
+ .addresses = addresses,
183
+ .dir_fd = dir_fd,
184
+ },
185
+ };
165
186
  },
166
187
  }
167
188
  }
@@ -1,3 +1,6 @@
1
+ const std = @import("std");
2
+ const assert = std.debug.assert;
3
+
1
4
  /// Whether development or production:
2
5
  pub const deployment_environment = .development;
3
6
 
@@ -86,12 +89,6 @@ pub const connections_max = replicas_max + clients_max;
86
89
  /// This impacts the amount of memory allocated at initialization by the server.
87
90
  pub const message_size_max = 1 * 1024 * 1024;
88
91
 
89
- /// The number of full-sized messages allocated at initialization by the message bus.
90
- pub const message_bus_messages_max = connections_max * 4;
91
- /// The number of header-sized messages allocated at initialization by the message bus.
92
- /// These are much smaller/cheaper and we can therefore have many of them.
93
- pub const message_bus_headers_max = connections_max * connection_send_queue_max * 2;
94
-
95
92
  /// The maximum number of Viewstamped Replication prepare messages that can be inflight at a time.
96
93
  /// This is immutable once assigned per cluster, as replicas need to know how many operations might
97
94
  /// possibly be uncommitted during a view change, and this must be constant for all replicas.
@@ -102,8 +99,15 @@ pub const pipelining_max = clients_max;
102
99
  pub const connection_delay_min_ms = 50;
103
100
  pub const connection_delay_max_ms = 1000;
104
101
 
105
- /// The maximum number of outgoing messages that may be queued on a connection.
106
- pub const connection_send_queue_max = pipelining_max;
102
+ /// The maximum number of outgoing messages that may be queued on a replica connection.
103
+ pub const connection_send_queue_max_replica = std.math.max(std.math.min(clients_max, 4), 2);
104
+
105
+ /// The maximum number of outgoing messages that may be queued on a client connection.
106
+ /// The client has one in-flight request, and occasionally a ping.
107
+ pub const connection_send_queue_max_client = 2;
108
+
109
+ /// The maximum number of outgoing requests that may be queued on a client (including the in-flight request).
110
+ pub const client_request_queue_max = 32;
107
111
 
108
112
  /// The maximum number of connections in the kernel's complete connection queue pending an accept():
109
113
  /// If the backlog argument is greater than the value in `/proc/sys/net/core/somaxconn`, then it is
@@ -124,7 +128,8 @@ pub const tcp_rcvbuf = 4 * 1024 * 1024;
124
128
  /// This sets SO_SNDBUF as an alternative to the auto-tuning range in /proc/sys/net/ipv4/tcp_wmem.
125
129
  /// The value is limited by /proc/sys/net/core/wmem_max, unless the CAP_NET_ADMIN privilege exists.
126
130
  /// The kernel doubles this value to allow space for packet bookkeeping overhead.
127
- pub const tcp_sndbuf = 4 * 1024 * 1024;
131
+ pub const tcp_sndbuf_replica = connection_send_queue_max_replica * message_size_max;
132
+ pub const tcp_sndbuf_client = connection_send_queue_max_client * message_size_max;
128
133
 
129
134
  /// Whether to enable TCP keepalive:
130
135
  pub const tcp_keepalive = true;
@@ -218,3 +223,13 @@ pub const clock_synchronization_window_min_ms = 2000;
218
223
  /// This eliminates the impact of gradual clock drift on our clock offset (clock skew) measurements.
219
224
  /// If a window expires because of this then it is likely that the clock epoch will also be expired.
220
225
  pub const clock_synchronization_window_max_ms = 20000;
226
+
227
+ comptime {
228
+ // vsr.parse_address assumes that config.address/config.port are valid.
229
+ _ = std.net.Address.parseIp4(address, 0) catch unreachable;
230
+ _ = @as(u16, port);
231
+
232
+ // Avoid latency issues from a too-large sndbuf.
233
+ assert(tcp_sndbuf_replica <= 4 * 1024 * 1024);
234
+ assert(tcp_sndbuf_client <= 4 * 1024 * 1024);
235
+ }
@@ -53,7 +53,7 @@ pub fn request(
53
53
 
54
54
  message_bus.set_on_message(*Client, &client, Client.on_message);
55
55
 
56
- var message = client.get_message() orelse unreachable;
56
+ var message = client.get_message();
57
57
  defer client.unref(message);
58
58
 
59
59
  const body = std.mem.asBytes(&batch);
@@ -1,6 +1,7 @@
1
1
  const std = @import("std");
2
2
  const os = std.os;
3
3
  const assert = std.debug.assert;
4
+ const log = std.log.scoped(.io_benchmark);
4
5
 
5
6
  const Time = @import("../time.zig").Time;
6
7
  const IO = @import("../io.zig").IO;
@@ -14,7 +15,7 @@ const run_duration = 1 * std.time.ns_per_s;
14
15
 
15
16
  pub fn main() !void {
16
17
  var gpa = std.heap.GeneralPurposeAllocator(.{}){};
17
- const allocator = &gpa.allocator;
18
+ const allocator = gpa.allocator();
18
19
  defer {
19
20
  const leaks = gpa.deinit();
20
21
  assert(!leaks);
@@ -24,37 +25,34 @@ pub fn main() !void {
24
25
  defer allocator.free(buffer);
25
26
  std.mem.set(u8, buffer, 0);
26
27
 
27
- var timer = Time{};
28
- const started = timer.monotonic();
29
28
  var self = Context{
30
29
  .io = try IO.init(32, 0),
31
- .timer = &timer,
32
- .started = started,
33
- .current = started,
34
30
  .tx = .{ .buffer = buffer[0 * buffer_size ..][0..buffer_size] },
35
31
  .rx = .{ .buffer = buffer[1 * buffer_size ..][0..buffer_size] },
36
32
  };
33
+ defer self.io.deinit();
37
34
 
35
+ var timer = Time{};
36
+ const started = timer.monotonic();
38
37
  defer {
39
- self.io.deinit();
40
- const elapsed_ns = self.current - started;
38
+ const elapsed_ns = timer.monotonic() - started;
41
39
  const transferred_mb = @intToFloat(f64, self.transferred) / 1024 / 1024;
42
40
 
43
- std.debug.print("IO throughput test: took {}ms @ {d:.2} MB/s\n", .{
41
+ log.info("took {}ms @ {d:.2} MB/s\n", .{
44
42
  elapsed_ns / std.time.ns_per_ms,
45
43
  transferred_mb / (@intToFloat(f64, elapsed_ns) / std.time.ns_per_s),
46
44
  });
47
45
  }
48
46
 
49
47
  // Setup the server socket
50
- self.server.fd = try IO.openSocket(os.AF_INET, os.SOCK_STREAM, os.IPPROTO_TCP);
48
+ self.server.fd = try self.io.open_socket(os.AF.INET, os.SOCK.STREAM, os.IPPROTO.TCP);
51
49
  defer os.closeSocket(self.server.fd);
52
50
 
53
51
  const address = try std.net.Address.parseIp4("127.0.0.1", 3131);
54
52
  try os.setsockopt(
55
53
  self.server.fd,
56
- os.SOL_SOCKET,
57
- os.SO_REUSEADDR,
54
+ os.SOL.SOCKET,
55
+ os.SO.REUSEADDR,
58
56
  &std.mem.toBytes(@as(c_int, 1)),
59
57
  );
60
58
  try os.bind(self.server.fd, &address.any, address.getOsSockLen());
@@ -70,7 +68,7 @@ pub fn main() !void {
70
68
  );
71
69
 
72
70
  // Setup the client connection
73
- self.tx.socket.fd = try IO.openSocket(os.AF_INET, os.SOCK_STREAM, os.IPPROTO_TCP);
71
+ self.tx.socket.fd = try self.io.open_socket(os.AF.INET, os.SOCK.STREAM, os.IPPROTO.TCP);
74
72
  defer os.closeSocket(self.tx.socket.fd);
75
73
 
76
74
  self.io.connect(
@@ -82,22 +80,14 @@ pub fn main() !void {
82
80
  address,
83
81
  );
84
82
 
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
- }
83
+ // Run the IO loop for the duration of the benchmark
84
+ log.info("running for {}", .{std.fmt.fmtDuration(run_duration)});
85
+ try self.io.run_for_ns(run_duration);
96
86
 
97
87
  // 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);
88
+ assert(self.server.fd != IO.INVALID_SOCKET);
89
+ assert(self.tx.socket.fd != IO.INVALID_SOCKET);
90
+ assert(self.rx.socket.fd != IO.INVALID_SOCKET);
101
91
 
102
92
  // Close the accepted client socket.
103
93
  // The actual client socket + server socket are closed by defer
@@ -108,14 +98,11 @@ const Context = struct {
108
98
  io: IO,
109
99
  tx: Pipe,
110
100
  rx: Pipe,
111
- timer: *Time,
112
- started: u64,
113
- current: u64,
114
101
  server: Socket = .{},
115
102
  transferred: u64 = 0,
116
103
 
117
104
  const Socket = struct {
118
- fd: os.socket_t = -1,
105
+ fd: os.socket_t = IO.INVALID_SOCKET,
119
106
  completion: IO.Completion = undefined,
120
107
  };
121
108
  const Pipe = struct {
@@ -124,21 +111,12 @@ const Context = struct {
124
111
  transferred: usize = 0,
125
112
  };
126
113
 
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
114
  fn on_accept(
137
115
  self: *Context,
138
116
  completion: *IO.Completion,
139
117
  result: IO.AcceptError!os.socket_t,
140
118
  ) void {
141
- assert(self.rx.socket.fd == -1);
119
+ assert(self.rx.socket.fd == IO.INVALID_SOCKET);
142
120
  assert(&self.server.completion == completion);
143
121
  self.rx.socket.fd = result catch |err| std.debug.panic("accept error {}", .{err});
144
122
 
@@ -152,7 +130,9 @@ const Context = struct {
152
130
  completion: *IO.Completion,
153
131
  result: IO.ConnectError!void,
154
132
  ) void {
155
- assert(self.tx.socket.fd != -1);
133
+ _ = result catch unreachable;
134
+
135
+ assert(self.tx.socket.fd != IO.INVALID_SOCKET);
156
136
  assert(&self.tx.socket.completion == completion);
157
137
 
158
138
  // Start sending data to the server's accepted client
@@ -161,8 +141,8 @@ const Context = struct {
161
141
  }
162
142
 
163
143
  const TransferType = enum {
164
- read = 0,
165
- write = 1,
144
+ read,
145
+ write,
166
146
  };
167
147
 
168
148
  fn do_transfer(
@@ -188,11 +168,6 @@ const Context = struct {
188
168
  assert(bytes <= buffer_size);
189
169
  self.transferred += bytes;
190
170
 
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
171
  // Select which connection (tx or rx) depending on the type of transfer
197
172
  const pipe = &@field(self, pipe_name);
198
173
  pipe.transferred += bytes;