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
|
@@ -87,7 +87,7 @@ pub const Command = union(enum) {
|
|
|
87
87
|
|
|
88
88
|
/// Parse the command line arguments passed to the tigerbeetle binary.
|
|
89
89
|
/// Exits the program with a non-zero exit code if an error is found.
|
|
90
|
-
pub fn parse_args(allocator:
|
|
90
|
+
pub fn parse_args(allocator: std.mem.Allocator) Command {
|
|
91
91
|
var maybe_cluster: ?[]const u8 = null;
|
|
92
92
|
var maybe_replica: ?[]const u8 = null;
|
|
93
93
|
var maybe_addresses: ?[]const u8 = null;
|
|
@@ -118,8 +118,10 @@ pub fn parse_args(allocator: *std.mem.Allocator) Command {
|
|
|
118
118
|
} else if (mem.eql(u8, arg, "-h") or mem.eql(u8, arg, "--help")) {
|
|
119
119
|
std.io.getStdOut().writeAll(usage) catch os.exit(1);
|
|
120
120
|
os.exit(0);
|
|
121
|
-
} else {
|
|
121
|
+
} else if (mem.startsWith(u8, arg, "--")) {
|
|
122
122
|
fatal("unexpected argument: '{s}'", .{arg});
|
|
123
|
+
} else {
|
|
124
|
+
fatal("unexpected argument: '{s}' (must start with '--')", .{arg});
|
|
123
125
|
}
|
|
124
126
|
}
|
|
125
127
|
|
|
@@ -130,7 +132,7 @@ pub fn parse_args(allocator: *std.mem.Allocator) Command {
|
|
|
130
132
|
const replica = parse_replica(raw_replica);
|
|
131
133
|
|
|
132
134
|
const dir_path = maybe_directory orelse config.directory;
|
|
133
|
-
const dir_fd = os.openZ(dir_path, os.
|
|
135
|
+
const dir_fd = os.openZ(dir_path, os.O.CLOEXEC | os.O.RDONLY, 0) catch |err|
|
|
134
136
|
fatal("failed to open directory '{s}': {}", .{ dir_path, err });
|
|
135
137
|
|
|
136
138
|
switch (command) {
|
|
@@ -193,7 +195,7 @@ fn parse_cluster(raw_cluster: []const u8) u32 {
|
|
|
193
195
|
}
|
|
194
196
|
|
|
195
197
|
/// Parse and allocate the addresses returning a slice into that array.
|
|
196
|
-
fn parse_addresses(allocator:
|
|
198
|
+
fn parse_addresses(allocator: std.mem.Allocator, raw_addresses: []const u8) []net.Address {
|
|
197
199
|
return vsr.parse_addresses(allocator, raw_addresses) catch |err| switch (err) {
|
|
198
200
|
error.AddressHasTrailingComma => fatal("--addresses: invalid trailing comma", .{}),
|
|
199
201
|
error.AddressLimitExceeded => {
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/// Whether development or production:
|
|
2
2
|
pub const deployment_environment = .development;
|
|
3
3
|
|
|
4
|
-
/// The maximum log level in increasing order of verbosity (emergency=0, debug=
|
|
5
|
-
pub const log_level =
|
|
4
|
+
/// The maximum log level in increasing order of verbosity (emergency=0, debug=3):
|
|
5
|
+
pub const log_level = 2;
|
|
6
6
|
|
|
7
7
|
/// The maximum number of replicas allowed in a cluster.
|
|
8
8
|
pub const replicas_max = 6;
|
|
@@ -1,7 +1,16 @@
|
|
|
1
1
|
const std = @import("std");
|
|
2
2
|
const assert = std.debug.assert;
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
const config = @import("config.zig");
|
|
5
|
+
|
|
6
|
+
const tb = @import("tigerbeetle.zig");
|
|
7
|
+
const Account = tb.Account;
|
|
8
|
+
const Transfer = tb.Transfer;
|
|
9
|
+
const Commit = tb.Commit;
|
|
10
|
+
|
|
11
|
+
const CreateAccountsResult = tb.CreateAccountsResult;
|
|
12
|
+
const CreateTransfersResult = tb.CreateTransfersResult;
|
|
13
|
+
const CommitTransfersResult = tb.CommitTransfersResult;
|
|
5
14
|
|
|
6
15
|
const IO = @import("io.zig").IO;
|
|
7
16
|
const MessageBus = @import("message_bus.zig").MessageBusClient;
|
|
@@ -13,96 +22,117 @@ const Client = vsr.Client(StateMachine, MessageBus);
|
|
|
13
22
|
|
|
14
23
|
pub const log_level: std.log.Level = .alert;
|
|
15
24
|
|
|
16
|
-
pub
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
on_reply: fn (
|
|
21
|
-
user_data: u128,
|
|
22
|
-
operation: StateMachine.Operation,
|
|
23
|
-
results: Client.Error![]const u8,
|
|
24
|
-
) void,
|
|
25
|
-
) !void {
|
|
26
|
-
const allocator = std.heap.page_allocator;
|
|
27
|
-
const client_id = std.crypto.random.int(u128);
|
|
28
|
-
const cluster_id: u32 = 0;
|
|
29
|
-
var addresses = [_]std.net.Address{try std.net.Address.parseIp4("127.0.0.1", config.port)};
|
|
30
|
-
|
|
31
|
-
var io = try IO.init(32, 0);
|
|
32
|
-
defer io.deinit();
|
|
33
|
-
|
|
34
|
-
var message_bus = try MessageBus.init(allocator, cluster_id, &addresses, client_id, &io);
|
|
35
|
-
defer message_bus.deinit();
|
|
36
|
-
|
|
37
|
-
var client = try Client.init(
|
|
38
|
-
allocator,
|
|
39
|
-
client_id,
|
|
40
|
-
cluster_id,
|
|
41
|
-
@intCast(u8, addresses.len),
|
|
42
|
-
&message_bus,
|
|
43
|
-
);
|
|
44
|
-
defer client.deinit();
|
|
45
|
-
|
|
46
|
-
message_bus.set_on_message(*Client, &client, Client.on_message);
|
|
47
|
-
|
|
48
|
-
var message = client.get_message() orelse unreachable;
|
|
49
|
-
defer client.unref(message);
|
|
50
|
-
|
|
51
|
-
const body = std.mem.asBytes(&batch);
|
|
52
|
-
std.mem.copy(u8, message.buffer[@sizeOf(Header)..], body);
|
|
53
|
-
|
|
54
|
-
client.request(
|
|
55
|
-
0,
|
|
56
|
-
on_reply,
|
|
57
|
-
operation,
|
|
58
|
-
message,
|
|
59
|
-
body.len,
|
|
60
|
-
);
|
|
61
|
-
|
|
62
|
-
while (client.request_queue.count > 0) {
|
|
63
|
-
client.tick();
|
|
64
|
-
try io.run_for_ns(config.tick_ms * std.time.ns_per_ms);
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
pub fn on_create_accounts(
|
|
25
|
+
pub fn request(
|
|
26
|
+
operation: StateMachine.Operation,
|
|
27
|
+
batch: anytype,
|
|
28
|
+
on_reply: fn (
|
|
69
29
|
user_data: u128,
|
|
70
30
|
operation: StateMachine.Operation,
|
|
71
31
|
results: Client.Error![]const u8,
|
|
72
|
-
) void
|
|
73
|
-
|
|
32
|
+
) void,
|
|
33
|
+
) !void {
|
|
34
|
+
const allocator = std.heap.page_allocator;
|
|
35
|
+
const client_id = std.crypto.random.int(u128);
|
|
36
|
+
const cluster_id: u32 = 0;
|
|
37
|
+
var addresses = [_]std.net.Address{try std.net.Address.parseIp4("127.0.0.1", config.port)};
|
|
38
|
+
|
|
39
|
+
var io = try IO.init(32, 0);
|
|
40
|
+
defer io.deinit();
|
|
41
|
+
|
|
42
|
+
var message_bus = try MessageBus.init(allocator, cluster_id, &addresses, client_id, &io);
|
|
43
|
+
defer message_bus.deinit();
|
|
44
|
+
|
|
45
|
+
var client = try Client.init(
|
|
46
|
+
allocator,
|
|
47
|
+
client_id,
|
|
48
|
+
cluster_id,
|
|
49
|
+
@intCast(u8, addresses.len),
|
|
50
|
+
&message_bus,
|
|
51
|
+
);
|
|
52
|
+
defer client.deinit();
|
|
53
|
+
|
|
54
|
+
message_bus.set_on_message(*Client, &client, Client.on_message);
|
|
55
|
+
|
|
56
|
+
var message = client.get_message() orelse unreachable;
|
|
57
|
+
defer client.unref(message);
|
|
58
|
+
|
|
59
|
+
const body = std.mem.asBytes(&batch);
|
|
60
|
+
std.mem.copy(u8, message.buffer[@sizeOf(Header)..], body);
|
|
61
|
+
|
|
62
|
+
client.request(
|
|
63
|
+
0,
|
|
64
|
+
on_reply,
|
|
65
|
+
operation,
|
|
66
|
+
message,
|
|
67
|
+
body.len,
|
|
68
|
+
);
|
|
69
|
+
|
|
70
|
+
while (client.request_queue.count > 0) {
|
|
71
|
+
client.tick();
|
|
72
|
+
try io.run_for_ns(config.tick_ms * std.time.ns_per_ms);
|
|
74
73
|
}
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
)
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
pub fn on_create_accounts(
|
|
77
|
+
user_data: u128,
|
|
78
|
+
operation: StateMachine.Operation,
|
|
79
|
+
results: Client.Error![]const u8,
|
|
80
|
+
) void {
|
|
81
|
+
_ = user_data;
|
|
82
|
+
_ = operation;
|
|
83
|
+
|
|
84
|
+
print_results(CreateAccountsResult, results);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
pub fn on_lookup_accounts(
|
|
88
|
+
user_data: u128,
|
|
89
|
+
operation: StateMachine.Operation,
|
|
90
|
+
results: Client.Error![]const u8,
|
|
91
|
+
) void {
|
|
92
|
+
_ = user_data;
|
|
93
|
+
_ = operation;
|
|
94
|
+
|
|
95
|
+
print_results(Account, results);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
pub fn on_lookup_transfers(
|
|
99
|
+
user_data: u128,
|
|
100
|
+
operation: StateMachine.Operation,
|
|
101
|
+
results: Client.Error![]const u8,
|
|
102
|
+
) void {
|
|
103
|
+
_ = user_data;
|
|
104
|
+
_ = operation;
|
|
105
|
+
|
|
106
|
+
print_results(Transfer, results);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
pub fn on_create_transfers(
|
|
110
|
+
user_data: u128,
|
|
111
|
+
operation: StateMachine.Operation,
|
|
112
|
+
results: Client.Error![]const u8,
|
|
113
|
+
) void {
|
|
114
|
+
_ = user_data;
|
|
115
|
+
_ = operation;
|
|
116
|
+
|
|
117
|
+
print_results(CreateTransfersResult, results);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
pub fn on_commit_transfers(
|
|
121
|
+
user_data: u128,
|
|
122
|
+
operation: StateMachine.Operation,
|
|
123
|
+
results: Client.Error![]const u8,
|
|
124
|
+
) void {
|
|
125
|
+
_ = user_data;
|
|
126
|
+
_ = operation;
|
|
127
|
+
|
|
128
|
+
print_results(CommitTransfersResult, results);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
fn print_results(comptime Results: type, results: Client.Error![]const u8) void {
|
|
132
|
+
const body = results catch unreachable;
|
|
133
|
+
const slice = std.mem.bytesAsSlice(Results, body);
|
|
134
|
+
for (slice) |result| {
|
|
135
|
+
std.debug.print("{}\n", .{result});
|
|
107
136
|
}
|
|
108
|
-
};
|
|
137
|
+
if (slice.len == 0) std.debug.print("OK\n", .{});
|
|
138
|
+
}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
const tb = @import("tigerbeetle.zig");
|
|
2
|
+
const demo = @import("demo.zig");
|
|
3
|
+
|
|
4
|
+
const Account = tb.Account;
|
|
3
5
|
|
|
4
6
|
pub fn main() !void {
|
|
5
7
|
const accounts = [_]Account{
|
|
@@ -29,5 +31,5 @@ pub fn main() !void {
|
|
|
29
31
|
},
|
|
30
32
|
};
|
|
31
33
|
|
|
32
|
-
try
|
|
34
|
+
try demo.request(.create_accounts, accounts, demo.on_create_accounts);
|
|
33
35
|
}
|
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
usingnamespace @import("demo.zig");
|
|
1
|
+
const demo = @import("demo.zig");
|
|
3
2
|
|
|
4
3
|
pub fn main() !void {
|
|
5
4
|
const ids = [_]u128{ 1, 2 };
|
|
6
5
|
|
|
7
|
-
try
|
|
6
|
+
try demo.request(.lookup_accounts, ids, demo.on_lookup_accounts);
|
|
8
7
|
}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
const tb = @import("tigerbeetle.zig");
|
|
2
|
+
const demo = @import("demo.zig");
|
|
3
|
+
|
|
4
|
+
const Transfer = tb.Transfer;
|
|
3
5
|
|
|
4
6
|
pub fn main() !void {
|
|
5
7
|
const transfers = [_]Transfer{
|
|
@@ -16,5 +18,5 @@ pub fn main() !void {
|
|
|
16
18
|
},
|
|
17
19
|
};
|
|
18
20
|
|
|
19
|
-
try
|
|
21
|
+
try demo.request(.create_transfers, transfers, demo.on_create_transfers);
|
|
20
22
|
}
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
const std = @import("std");
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
const tb = @import("tigerbeetle.zig");
|
|
4
|
+
const demo = @import("demo.zig");
|
|
5
|
+
|
|
6
|
+
const Transfer = tb.Transfer;
|
|
5
7
|
|
|
6
8
|
pub fn main() !void {
|
|
7
9
|
const transfers = [_]Transfer{
|
|
@@ -49,5 +51,5 @@ pub fn main() !void {
|
|
|
49
51
|
},
|
|
50
52
|
};
|
|
51
53
|
|
|
52
|
-
try
|
|
54
|
+
try demo.request(.create_transfers, transfers, demo.on_create_transfers);
|
|
53
55
|
}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
const tb = @import("tigerbeetle.zig");
|
|
2
|
+
const demo = @import("demo.zig");
|
|
3
|
+
|
|
4
|
+
const Commit = tb.Commit;
|
|
3
5
|
|
|
4
6
|
pub fn main() !void {
|
|
5
7
|
const commits = [_]Commit{
|
|
@@ -17,5 +19,5 @@ pub fn main() !void {
|
|
|
17
19
|
},
|
|
18
20
|
};
|
|
19
21
|
|
|
20
|
-
try
|
|
22
|
+
try demo.request(.commit_transfers, commits, demo.on_commit_transfers);
|
|
21
23
|
}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
const tb = @import("tigerbeetle.zig");
|
|
2
|
+
const demo = @import("demo.zig");
|
|
3
|
+
|
|
4
|
+
const Commit = tb.Commit;
|
|
3
5
|
|
|
4
6
|
pub fn main() !void {
|
|
5
7
|
const commits = [_]Commit{
|
|
@@ -11,5 +13,5 @@ pub fn main() !void {
|
|
|
11
13
|
},
|
|
12
14
|
};
|
|
13
15
|
|
|
14
|
-
try
|
|
16
|
+
try demo.request(.commit_transfers, commits, demo.on_commit_transfers);
|
|
15
17
|
}
|
|
@@ -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
|
+
};
|