tigerbeetle 0.0.34 → 0.0.37
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +10 -0
- data/ext/tb_client/extconf.rb +13 -13
- data/ext/tb_client/tigerbeetle/LICENSE +177 -0
- data/ext/tb_client/tigerbeetle/build.zig +2327 -0
- data/ext/tb_client/tigerbeetle/src/aof.zig +1000 -0
- data/ext/tb_client/tigerbeetle/src/build_multiversion.zig +808 -0
- data/ext/tb_client/tigerbeetle/src/cdc/amqp/protocol.zig +1283 -0
- data/ext/tb_client/tigerbeetle/src/cdc/amqp/spec.zig +1704 -0
- data/ext/tb_client/tigerbeetle/src/cdc/amqp/types.zig +341 -0
- data/ext/tb_client/tigerbeetle/src/cdc/amqp.zig +1450 -0
- data/ext/tb_client/tigerbeetle/src/cdc/runner.zig +1659 -0
- data/ext/tb_client/tigerbeetle/src/clients/c/samples/main.c +406 -0
- data/ext/tb_client/tigerbeetle/src/clients/c/tb_client/context.zig +1084 -0
- data/ext/tb_client/tigerbeetle/src/clients/c/tb_client/echo_client.zig +286 -0
- data/ext/tb_client/tigerbeetle/src/clients/c/tb_client/packet.zig +158 -0
- data/ext/tb_client/tigerbeetle/src/clients/c/tb_client/signal.zig +229 -0
- data/ext/tb_client/tigerbeetle/src/clients/c/tb_client/signal_fuzz.zig +110 -0
- data/ext/tb_client/tigerbeetle/src/clients/c/tb_client.h +386 -0
- data/ext/tb_client/tigerbeetle/src/clients/c/tb_client.zig +34 -0
- data/ext/tb_client/tigerbeetle/src/clients/c/tb_client_exports.zig +281 -0
- data/ext/tb_client/tigerbeetle/src/clients/c/tb_client_header.zig +312 -0
- data/ext/tb_client/tigerbeetle/src/clients/c/tb_client_header_test.zig +138 -0
- data/ext/tb_client/tigerbeetle/src/clients/c/test.zig +466 -0
- data/ext/tb_client/tigerbeetle/src/clients/docs_samples.zig +157 -0
- data/ext/tb_client/tigerbeetle/src/clients/docs_types.zig +90 -0
- data/ext/tb_client/tigerbeetle/src/clients/dotnet/ci.zig +203 -0
- data/ext/tb_client/tigerbeetle/src/clients/dotnet/docs.zig +79 -0
- data/ext/tb_client/tigerbeetle/src/clients/dotnet/dotnet_bindings.zig +542 -0
- data/ext/tb_client/tigerbeetle/src/clients/go/ci.zig +109 -0
- data/ext/tb_client/tigerbeetle/src/clients/go/docs.zig +86 -0
- data/ext/tb_client/tigerbeetle/src/clients/go/go_bindings.zig +370 -0
- data/ext/tb_client/tigerbeetle/src/clients/go/pkg/native/tb_client.h +386 -0
- data/ext/tb_client/tigerbeetle/src/clients/java/ci.zig +167 -0
- data/ext/tb_client/tigerbeetle/src/clients/java/docs.zig +126 -0
- data/ext/tb_client/tigerbeetle/src/clients/java/java_bindings.zig +996 -0
- data/ext/tb_client/tigerbeetle/src/clients/java/src/client.zig +748 -0
- data/ext/tb_client/tigerbeetle/src/clients/java/src/jni.zig +3238 -0
- data/ext/tb_client/tigerbeetle/src/clients/java/src/jni_tests.zig +1718 -0
- data/ext/tb_client/tigerbeetle/src/clients/java/src/jni_thread_cleaner.zig +190 -0
- data/ext/tb_client/tigerbeetle/src/clients/node/ci.zig +104 -0
- data/ext/tb_client/tigerbeetle/src/clients/node/docs.zig +75 -0
- data/ext/tb_client/tigerbeetle/src/clients/node/node.zig +522 -0
- data/ext/tb_client/tigerbeetle/src/clients/node/node_bindings.zig +267 -0
- data/ext/tb_client/tigerbeetle/src/clients/node/src/c.zig +3 -0
- data/ext/tb_client/tigerbeetle/src/clients/node/src/translate.zig +379 -0
- data/ext/tb_client/tigerbeetle/src/clients/python/ci.zig +131 -0
- data/ext/tb_client/tigerbeetle/src/clients/python/docs.zig +63 -0
- data/ext/tb_client/tigerbeetle/src/clients/python/python_bindings.zig +588 -0
- data/ext/tb_client/tigerbeetle/src/clients/rust/assets/tb_client.h +386 -0
- data/ext/tb_client/tigerbeetle/src/clients/rust/ci.zig +73 -0
- data/ext/tb_client/tigerbeetle/src/clients/rust/docs.zig +106 -0
- data/ext/tb_client/tigerbeetle/src/clients/rust/rust_bindings.zig +305 -0
- data/ext/tb_client/tigerbeetle/src/config.zig +296 -0
- data/ext/tb_client/tigerbeetle/src/constants.zig +790 -0
- data/ext/tb_client/tigerbeetle/src/copyhound.zig +202 -0
- data/ext/tb_client/tigerbeetle/src/counting_allocator.zig +72 -0
- data/ext/tb_client/tigerbeetle/src/direction.zig +11 -0
- data/ext/tb_client/tigerbeetle/src/docs_website/build.zig +158 -0
- data/ext/tb_client/tigerbeetle/src/docs_website/src/content.zig +156 -0
- data/ext/tb_client/tigerbeetle/src/docs_website/src/docs.zig +252 -0
- data/ext/tb_client/tigerbeetle/src/docs_website/src/file_checker.zig +313 -0
- data/ext/tb_client/tigerbeetle/src/docs_website/src/html.zig +87 -0
- data/ext/tb_client/tigerbeetle/src/docs_website/src/page_writer.zig +63 -0
- data/ext/tb_client/tigerbeetle/src/docs_website/src/redirects.zig +47 -0
- data/ext/tb_client/tigerbeetle/src/docs_website/src/search_index_writer.zig +28 -0
- data/ext/tb_client/tigerbeetle/src/docs_website/src/service_worker_writer.zig +61 -0
- data/ext/tb_client/tigerbeetle/src/docs_website/src/single_page_writer.zig +169 -0
- data/ext/tb_client/tigerbeetle/src/docs_website/src/website.zig +46 -0
- data/ext/tb_client/tigerbeetle/src/ewah.zig +445 -0
- data/ext/tb_client/tigerbeetle/src/ewah_benchmark.zig +128 -0
- data/ext/tb_client/tigerbeetle/src/ewah_fuzz.zig +171 -0
- data/ext/tb_client/tigerbeetle/src/fuzz_tests.zig +179 -0
- data/ext/tb_client/tigerbeetle/src/integration_tests.zig +662 -0
- data/ext/tb_client/tigerbeetle/src/io/common.zig +155 -0
- data/ext/tb_client/tigerbeetle/src/io/darwin.zig +1093 -0
- data/ext/tb_client/tigerbeetle/src/io/linux.zig +1880 -0
- data/ext/tb_client/tigerbeetle/src/io/test.zig +1005 -0
- data/ext/tb_client/tigerbeetle/src/io/windows.zig +1598 -0
- data/ext/tb_client/tigerbeetle/src/io.zig +34 -0
- data/ext/tb_client/tigerbeetle/src/iops.zig +134 -0
- data/ext/tb_client/tigerbeetle/src/list.zig +236 -0
- data/ext/tb_client/tigerbeetle/src/lsm/binary_search.zig +848 -0
- data/ext/tb_client/tigerbeetle/src/lsm/binary_search_benchmark.zig +179 -0
- data/ext/tb_client/tigerbeetle/src/lsm/cache_map.zig +424 -0
- data/ext/tb_client/tigerbeetle/src/lsm/cache_map_fuzz.zig +420 -0
- data/ext/tb_client/tigerbeetle/src/lsm/compaction.zig +2117 -0
- data/ext/tb_client/tigerbeetle/src/lsm/composite_key.zig +182 -0
- data/ext/tb_client/tigerbeetle/src/lsm/forest.zig +1119 -0
- data/ext/tb_client/tigerbeetle/src/lsm/forest_fuzz.zig +1102 -0
- data/ext/tb_client/tigerbeetle/src/lsm/forest_table_iterator.zig +200 -0
- data/ext/tb_client/tigerbeetle/src/lsm/groove.zig +1495 -0
- data/ext/tb_client/tigerbeetle/src/lsm/k_way_merge.zig +739 -0
- data/ext/tb_client/tigerbeetle/src/lsm/k_way_merge_benchmark.zig +166 -0
- data/ext/tb_client/tigerbeetle/src/lsm/manifest.zig +754 -0
- data/ext/tb_client/tigerbeetle/src/lsm/manifest_level.zig +1294 -0
- data/ext/tb_client/tigerbeetle/src/lsm/manifest_level_fuzz.zig +510 -0
- data/ext/tb_client/tigerbeetle/src/lsm/manifest_log.zig +1263 -0
- data/ext/tb_client/tigerbeetle/src/lsm/manifest_log_fuzz.zig +628 -0
- data/ext/tb_client/tigerbeetle/src/lsm/node_pool.zig +247 -0
- data/ext/tb_client/tigerbeetle/src/lsm/scan_buffer.zig +116 -0
- data/ext/tb_client/tigerbeetle/src/lsm/scan_builder.zig +543 -0
- data/ext/tb_client/tigerbeetle/src/lsm/scan_fuzz.zig +938 -0
- data/ext/tb_client/tigerbeetle/src/lsm/scan_lookup.zig +293 -0
- data/ext/tb_client/tigerbeetle/src/lsm/scan_merge.zig +362 -0
- data/ext/tb_client/tigerbeetle/src/lsm/scan_range.zig +99 -0
- data/ext/tb_client/tigerbeetle/src/lsm/scan_state.zig +17 -0
- data/ext/tb_client/tigerbeetle/src/lsm/scan_tree.zig +1036 -0
- data/ext/tb_client/tigerbeetle/src/lsm/schema.zig +617 -0
- data/ext/tb_client/tigerbeetle/src/lsm/scratch_memory.zig +84 -0
- data/ext/tb_client/tigerbeetle/src/lsm/segmented_array.zig +1500 -0
- data/ext/tb_client/tigerbeetle/src/lsm/segmented_array_benchmark.zig +149 -0
- data/ext/tb_client/tigerbeetle/src/lsm/segmented_array_fuzz.zig +7 -0
- data/ext/tb_client/tigerbeetle/src/lsm/set_associative_cache.zig +865 -0
- data/ext/tb_client/tigerbeetle/src/lsm/table.zig +607 -0
- data/ext/tb_client/tigerbeetle/src/lsm/table_memory.zig +843 -0
- data/ext/tb_client/tigerbeetle/src/lsm/table_value_iterator.zig +105 -0
- data/ext/tb_client/tigerbeetle/src/lsm/timestamp_range.zig +40 -0
- data/ext/tb_client/tigerbeetle/src/lsm/tree.zig +630 -0
- data/ext/tb_client/tigerbeetle/src/lsm/tree_fuzz.zig +933 -0
- data/ext/tb_client/tigerbeetle/src/lsm/zig_zag_merge.zig +557 -0
- data/ext/tb_client/tigerbeetle/src/message_buffer.zig +469 -0
- data/ext/tb_client/tigerbeetle/src/message_bus.zig +1214 -0
- data/ext/tb_client/tigerbeetle/src/message_bus_fuzz.zig +936 -0
- data/ext/tb_client/tigerbeetle/src/message_pool.zig +343 -0
- data/ext/tb_client/tigerbeetle/src/multiversion.zig +2195 -0
- data/ext/tb_client/tigerbeetle/src/queue.zig +390 -0
- data/ext/tb_client/tigerbeetle/src/repl/completion.zig +201 -0
- data/ext/tb_client/tigerbeetle/src/repl/parser.zig +1356 -0
- data/ext/tb_client/tigerbeetle/src/repl/terminal.zig +496 -0
- data/ext/tb_client/tigerbeetle/src/repl.zig +1034 -0
- data/ext/tb_client/tigerbeetle/src/scripts/amqp.zig +973 -0
- data/ext/tb_client/tigerbeetle/src/scripts/cfo.zig +1866 -0
- data/ext/tb_client/tigerbeetle/src/scripts/changelog.zig +304 -0
- data/ext/tb_client/tigerbeetle/src/scripts/ci.zig +227 -0
- data/ext/tb_client/tigerbeetle/src/scripts/client_readmes.zig +658 -0
- data/ext/tb_client/tigerbeetle/src/scripts/devhub.zig +466 -0
- data/ext/tb_client/tigerbeetle/src/scripts/release.zig +1058 -0
- data/ext/tb_client/tigerbeetle/src/scripts.zig +105 -0
- data/ext/tb_client/tigerbeetle/src/shell.zig +1195 -0
- data/ext/tb_client/tigerbeetle/src/stack.zig +260 -0
- data/ext/tb_client/tigerbeetle/src/state_machine/auditor.zig +911 -0
- data/ext/tb_client/tigerbeetle/src/state_machine/workload.zig +2079 -0
- data/ext/tb_client/tigerbeetle/src/state_machine.zig +4872 -0
- data/ext/tb_client/tigerbeetle/src/state_machine_fuzz.zig +288 -0
- data/ext/tb_client/tigerbeetle/src/state_machine_tests.zig +3128 -0
- data/ext/tb_client/tigerbeetle/src/static_allocator.zig +82 -0
- data/ext/tb_client/tigerbeetle/src/stdx/bit_set.zig +157 -0
- data/ext/tb_client/tigerbeetle/src/stdx/bounded_array.zig +292 -0
- data/ext/tb_client/tigerbeetle/src/stdx/debug.zig +65 -0
- data/ext/tb_client/tigerbeetle/src/stdx/flags.zig +1414 -0
- data/ext/tb_client/tigerbeetle/src/stdx/mlock.zig +92 -0
- data/ext/tb_client/tigerbeetle/src/stdx/prng.zig +677 -0
- data/ext/tb_client/tigerbeetle/src/stdx/radix.zig +336 -0
- data/ext/tb_client/tigerbeetle/src/stdx/ring_buffer.zig +511 -0
- data/ext/tb_client/tigerbeetle/src/stdx/sort_test.zig +112 -0
- data/ext/tb_client/tigerbeetle/src/stdx/stdx.zig +1160 -0
- data/ext/tb_client/tigerbeetle/src/stdx/testing/low_level_hash_vectors.zig +142 -0
- data/ext/tb_client/tigerbeetle/src/stdx/testing/snaptest.zig +361 -0
- data/ext/tb_client/tigerbeetle/src/stdx/time_units.zig +275 -0
- data/ext/tb_client/tigerbeetle/src/stdx/unshare.zig +295 -0
- data/ext/tb_client/tigerbeetle/src/stdx/vendored/aegis.zig +436 -0
- data/ext/tb_client/tigerbeetle/src/stdx/windows.zig +48 -0
- data/ext/tb_client/tigerbeetle/src/stdx/zipfian.zig +402 -0
- data/ext/tb_client/tigerbeetle/src/storage.zig +489 -0
- data/ext/tb_client/tigerbeetle/src/storage_fuzz.zig +180 -0
- data/ext/tb_client/tigerbeetle/src/testing/bench.zig +146 -0
- data/ext/tb_client/tigerbeetle/src/testing/cluster/grid_checker.zig +53 -0
- data/ext/tb_client/tigerbeetle/src/testing/cluster/journal_checker.zig +61 -0
- data/ext/tb_client/tigerbeetle/src/testing/cluster/manifest_checker.zig +76 -0
- data/ext/tb_client/tigerbeetle/src/testing/cluster/message_bus.zig +110 -0
- data/ext/tb_client/tigerbeetle/src/testing/cluster/network.zig +412 -0
- data/ext/tb_client/tigerbeetle/src/testing/cluster/state_checker.zig +331 -0
- data/ext/tb_client/tigerbeetle/src/testing/cluster/storage_checker.zig +458 -0
- data/ext/tb_client/tigerbeetle/src/testing/cluster.zig +1198 -0
- data/ext/tb_client/tigerbeetle/src/testing/exhaustigen.zig +128 -0
- data/ext/tb_client/tigerbeetle/src/testing/fixtures.zig +181 -0
- data/ext/tb_client/tigerbeetle/src/testing/fuzz.zig +144 -0
- data/ext/tb_client/tigerbeetle/src/testing/id.zig +97 -0
- data/ext/tb_client/tigerbeetle/src/testing/io.zig +317 -0
- data/ext/tb_client/tigerbeetle/src/testing/marks.zig +126 -0
- data/ext/tb_client/tigerbeetle/src/testing/packet_simulator.zig +533 -0
- data/ext/tb_client/tigerbeetle/src/testing/reply_sequence.zig +154 -0
- data/ext/tb_client/tigerbeetle/src/testing/state_machine.zig +389 -0
- data/ext/tb_client/tigerbeetle/src/testing/storage.zig +1247 -0
- data/ext/tb_client/tigerbeetle/src/testing/table.zig +249 -0
- data/ext/tb_client/tigerbeetle/src/testing/time.zig +98 -0
- data/ext/tb_client/tigerbeetle/src/testing/tmp_tigerbeetle.zig +212 -0
- data/ext/tb_client/tigerbeetle/src/testing/vortex/constants.zig +26 -0
- data/ext/tb_client/tigerbeetle/src/testing/vortex/faulty_network.zig +580 -0
- data/ext/tb_client/tigerbeetle/src/testing/vortex/java_driver/ci.zig +39 -0
- data/ext/tb_client/tigerbeetle/src/testing/vortex/logged_process.zig +214 -0
- data/ext/tb_client/tigerbeetle/src/testing/vortex/rust_driver/ci.zig +34 -0
- data/ext/tb_client/tigerbeetle/src/testing/vortex/supervisor.zig +766 -0
- data/ext/tb_client/tigerbeetle/src/testing/vortex/workload.zig +543 -0
- data/ext/tb_client/tigerbeetle/src/testing/vortex/zig_driver.zig +181 -0
- data/ext/tb_client/tigerbeetle/src/tidy.zig +1448 -0
- data/ext/tb_client/tigerbeetle/src/tigerbeetle/benchmark_driver.zig +227 -0
- data/ext/tb_client/tigerbeetle/src/tigerbeetle/benchmark_load.zig +1069 -0
- data/ext/tb_client/tigerbeetle/src/tigerbeetle/cli.zig +1422 -0
- data/ext/tb_client/tigerbeetle/src/tigerbeetle/inspect.zig +1658 -0
- data/ext/tb_client/tigerbeetle/src/tigerbeetle/inspect_integrity.zig +518 -0
- data/ext/tb_client/tigerbeetle/src/tigerbeetle/libtb_client.zig +36 -0
- data/ext/tb_client/tigerbeetle/src/tigerbeetle/main.zig +646 -0
- data/ext/tb_client/tigerbeetle/src/tigerbeetle.zig +958 -0
- data/ext/tb_client/tigerbeetle/src/time.zig +236 -0
- data/ext/tb_client/tigerbeetle/src/trace/event.zig +745 -0
- data/ext/tb_client/tigerbeetle/src/trace/statsd.zig +462 -0
- data/ext/tb_client/tigerbeetle/src/trace.zig +556 -0
- data/ext/tb_client/tigerbeetle/src/unit_tests.zig +321 -0
- data/ext/tb_client/tigerbeetle/src/vopr.zig +1785 -0
- data/ext/tb_client/tigerbeetle/src/vortex.zig +101 -0
- data/ext/tb_client/tigerbeetle/src/vsr/checkpoint_trailer.zig +473 -0
- data/ext/tb_client/tigerbeetle/src/vsr/checksum.zig +208 -0
- data/ext/tb_client/tigerbeetle/src/vsr/checksum_benchmark.zig +43 -0
- data/ext/tb_client/tigerbeetle/src/vsr/client.zig +768 -0
- data/ext/tb_client/tigerbeetle/src/vsr/client_replies.zig +532 -0
- data/ext/tb_client/tigerbeetle/src/vsr/client_sessions.zig +338 -0
- data/ext/tb_client/tigerbeetle/src/vsr/clock.zig +1019 -0
- data/ext/tb_client/tigerbeetle/src/vsr/fault_detector.zig +279 -0
- data/ext/tb_client/tigerbeetle/src/vsr/free_set.zig +1381 -0
- data/ext/tb_client/tigerbeetle/src/vsr/free_set_fuzz.zig +315 -0
- data/ext/tb_client/tigerbeetle/src/vsr/grid.zig +1460 -0
- data/ext/tb_client/tigerbeetle/src/vsr/grid_blocks_missing.zig +757 -0
- data/ext/tb_client/tigerbeetle/src/vsr/grid_scrubber.zig +797 -0
- data/ext/tb_client/tigerbeetle/src/vsr/journal.zig +2586 -0
- data/ext/tb_client/tigerbeetle/src/vsr/marzullo.zig +308 -0
- data/ext/tb_client/tigerbeetle/src/vsr/message_header.zig +1777 -0
- data/ext/tb_client/tigerbeetle/src/vsr/multi_batch.zig +715 -0
- data/ext/tb_client/tigerbeetle/src/vsr/multi_batch_fuzz.zig +185 -0
- data/ext/tb_client/tigerbeetle/src/vsr/repair_budget.zig +333 -0
- data/ext/tb_client/tigerbeetle/src/vsr/replica.zig +12355 -0
- data/ext/tb_client/tigerbeetle/src/vsr/replica_format.zig +416 -0
- data/ext/tb_client/tigerbeetle/src/vsr/replica_reformat.zig +165 -0
- data/ext/tb_client/tigerbeetle/src/vsr/replica_test.zig +2910 -0
- data/ext/tb_client/tigerbeetle/src/vsr/routing.zig +1075 -0
- data/ext/tb_client/tigerbeetle/src/vsr/superblock.zig +1603 -0
- data/ext/tb_client/tigerbeetle/src/vsr/superblock_fuzz.zig +484 -0
- data/ext/tb_client/tigerbeetle/src/vsr/superblock_quorums.zig +405 -0
- data/ext/tb_client/tigerbeetle/src/vsr/superblock_quorums_fuzz.zig +355 -0
- data/ext/tb_client/tigerbeetle/src/vsr/sync.zig +29 -0
- data/ext/tb_client/tigerbeetle/src/vsr.zig +1727 -0
- data/lib/tb_client/shared_lib.rb +12 -5
- data/lib/tigerbeetle/client.rb +1 -1
- data/lib/tigerbeetle/platforms.rb +9 -0
- data/lib/tigerbeetle/version.rb +2 -2
- data/tigerbeetle.gemspec +22 -5
- metadata +242 -3
- data/ext/tb_client/pkg.tar.gz +0 -0
|
@@ -0,0 +1,1005 @@
|
|
|
1
|
+
const std = @import("std");
|
|
2
|
+
const builtin = @import("builtin");
|
|
3
|
+
const os = std.os;
|
|
4
|
+
const posix = std.posix;
|
|
5
|
+
const testing = std.testing;
|
|
6
|
+
const assert = std.debug.assert;
|
|
7
|
+
const stdx = @import("stdx");
|
|
8
|
+
const KiB = stdx.KiB;
|
|
9
|
+
const MiB = stdx.MiB;
|
|
10
|
+
|
|
11
|
+
const TimeOS = @import("../time.zig").TimeOS;
|
|
12
|
+
const Time = @import("../time.zig").Time;
|
|
13
|
+
const IO = @import("../io.zig").IO;
|
|
14
|
+
|
|
15
|
+
pub const tcp_options: IO.TCPOptions = .{
|
|
16
|
+
.rcvbuf = 0,
|
|
17
|
+
.sndbuf = 0,
|
|
18
|
+
.keepalive = null,
|
|
19
|
+
.user_timeout_ms = 0,
|
|
20
|
+
.nodelay = false,
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
test "open/write/read/close/statx" {
|
|
24
|
+
try struct {
|
|
25
|
+
const Context = @This();
|
|
26
|
+
const StatxType = if (builtin.target.os.tag == .linux) std.os.linux.Statx else void;
|
|
27
|
+
|
|
28
|
+
path: [:0]const u8 = "test_io_write_read_close",
|
|
29
|
+
io: IO,
|
|
30
|
+
done: bool = false,
|
|
31
|
+
|
|
32
|
+
fd: ?posix.fd_t = null,
|
|
33
|
+
write_buf: [20]u8 = @splat(97),
|
|
34
|
+
read_buf: [20]u8 = @splat(98),
|
|
35
|
+
|
|
36
|
+
written: usize = 0,
|
|
37
|
+
read: usize = 0,
|
|
38
|
+
|
|
39
|
+
statx: StatxType = undefined,
|
|
40
|
+
|
|
41
|
+
fn run_test() !void {
|
|
42
|
+
var self: Context = .{
|
|
43
|
+
.io = try IO.init(32, 0),
|
|
44
|
+
};
|
|
45
|
+
defer self.io.deinit();
|
|
46
|
+
|
|
47
|
+
// The file gets created below, either by createFile or openat.
|
|
48
|
+
defer std.fs.cwd().deleteFile(self.path) catch {};
|
|
49
|
+
|
|
50
|
+
var completion: IO.Completion = undefined;
|
|
51
|
+
|
|
52
|
+
if (builtin.target.os.tag == .linux) {
|
|
53
|
+
self.io.openat(
|
|
54
|
+
*Context,
|
|
55
|
+
&self,
|
|
56
|
+
openat_callback,
|
|
57
|
+
&completion,
|
|
58
|
+
posix.AT.FDCWD,
|
|
59
|
+
self.path,
|
|
60
|
+
.{ .ACCMODE = .RDWR, .TRUNC = true, .CREAT = true },
|
|
61
|
+
std.fs.File.default_mode,
|
|
62
|
+
);
|
|
63
|
+
} else {
|
|
64
|
+
const file = try std.fs.cwd().createFile(self.path, .{
|
|
65
|
+
.read = true,
|
|
66
|
+
.truncate = true,
|
|
67
|
+
});
|
|
68
|
+
self.openat_callback(&completion, file.handle);
|
|
69
|
+
}
|
|
70
|
+
while (!self.done) try self.io.run();
|
|
71
|
+
|
|
72
|
+
try testing.expectEqual(self.write_buf.len, self.written);
|
|
73
|
+
try testing.expectEqual(self.read_buf.len, self.read);
|
|
74
|
+
try testing.expectEqualSlices(u8, &self.write_buf, &self.read_buf);
|
|
75
|
+
|
|
76
|
+
if (builtin.target.os.tag == .linux) {
|
|
77
|
+
// Offset of 10 specified to read / write below.
|
|
78
|
+
try testing.expectEqual(self.statx.size - 10, self.written);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
fn openat_callback(
|
|
83
|
+
self: *Context,
|
|
84
|
+
completion: *IO.Completion,
|
|
85
|
+
result: anyerror!posix.fd_t,
|
|
86
|
+
) void {
|
|
87
|
+
self.fd = result catch @panic("openat error");
|
|
88
|
+
self.io.write(
|
|
89
|
+
*Context,
|
|
90
|
+
self,
|
|
91
|
+
write_callback,
|
|
92
|
+
completion,
|
|
93
|
+
self.fd.?,
|
|
94
|
+
&self.write_buf,
|
|
95
|
+
10,
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
fn write_callback(
|
|
100
|
+
self: *Context,
|
|
101
|
+
completion: *IO.Completion,
|
|
102
|
+
result: IO.WriteError!usize,
|
|
103
|
+
) void {
|
|
104
|
+
self.written = result catch @panic("write error");
|
|
105
|
+
self.io.read(*Context, self, read_callback, completion, self.fd.?, &self.read_buf, 10);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
fn read_callback(
|
|
109
|
+
self: *Context,
|
|
110
|
+
completion: *IO.Completion,
|
|
111
|
+
result: IO.ReadError!usize,
|
|
112
|
+
) void {
|
|
113
|
+
self.read = result catch @panic("read error");
|
|
114
|
+
self.io.close(*Context, self, close_callback, completion, self.fd.?);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
fn close_callback(
|
|
118
|
+
self: *Context,
|
|
119
|
+
completion: *IO.Completion,
|
|
120
|
+
result: IO.CloseError!void,
|
|
121
|
+
) void {
|
|
122
|
+
_ = result catch @panic("close error");
|
|
123
|
+
|
|
124
|
+
if (builtin.target.os.tag == .linux) {
|
|
125
|
+
self.io.statx(
|
|
126
|
+
*Context,
|
|
127
|
+
self,
|
|
128
|
+
statx_callback,
|
|
129
|
+
completion,
|
|
130
|
+
posix.AT.FDCWD,
|
|
131
|
+
self.path,
|
|
132
|
+
0,
|
|
133
|
+
os.linux.STATX_BASIC_STATS,
|
|
134
|
+
&self.statx,
|
|
135
|
+
);
|
|
136
|
+
} else {
|
|
137
|
+
self.done = true;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
fn statx_callback(
|
|
142
|
+
self: *Context,
|
|
143
|
+
completion: *IO.Completion,
|
|
144
|
+
result: IO.StatxError!void,
|
|
145
|
+
) void {
|
|
146
|
+
_ = completion;
|
|
147
|
+
_ = result catch @panic("statx error");
|
|
148
|
+
|
|
149
|
+
assert(!self.done);
|
|
150
|
+
self.done = true;
|
|
151
|
+
}
|
|
152
|
+
}.run_test();
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
test "accept/connect/send/receive" {
|
|
156
|
+
try struct {
|
|
157
|
+
const Context = @This();
|
|
158
|
+
|
|
159
|
+
io: *IO,
|
|
160
|
+
done: bool = false,
|
|
161
|
+
server: posix.socket_t,
|
|
162
|
+
client: posix.socket_t,
|
|
163
|
+
|
|
164
|
+
accepted_sock: posix.socket_t = undefined,
|
|
165
|
+
|
|
166
|
+
send_buf: [10]u8 = [_]u8{ 1, 0, 1, 0, 1, 0, 1, 0, 1, 0 },
|
|
167
|
+
recv_buf: [5]u8 = [_]u8{ 0, 1, 0, 1, 0 },
|
|
168
|
+
|
|
169
|
+
sent: usize = 0,
|
|
170
|
+
received: usize = 0,
|
|
171
|
+
|
|
172
|
+
fn run_test() !void {
|
|
173
|
+
var io = try IO.init(32, 0);
|
|
174
|
+
defer io.deinit();
|
|
175
|
+
|
|
176
|
+
const address = try std.net.Address.parseIp4("127.0.0.1", 0);
|
|
177
|
+
const kernel_backlog = 1;
|
|
178
|
+
|
|
179
|
+
const server = try io.open_socket_tcp(address.any.family, tcp_options);
|
|
180
|
+
defer io.close_socket(server);
|
|
181
|
+
|
|
182
|
+
const client = try io.open_socket_tcp(address.any.family, tcp_options);
|
|
183
|
+
defer io.close_socket(client);
|
|
184
|
+
|
|
185
|
+
try posix.setsockopt(
|
|
186
|
+
server,
|
|
187
|
+
posix.SOL.SOCKET,
|
|
188
|
+
posix.SO.REUSEADDR,
|
|
189
|
+
&std.mem.toBytes(@as(c_int, 1)),
|
|
190
|
+
);
|
|
191
|
+
try posix.bind(server, &address.any, address.getOsSockLen());
|
|
192
|
+
try posix.listen(server, kernel_backlog);
|
|
193
|
+
|
|
194
|
+
var client_address = std.net.Address.initIp4(undefined, undefined);
|
|
195
|
+
var client_address_len = client_address.getOsSockLen();
|
|
196
|
+
try posix.getsockname(server, &client_address.any, &client_address_len);
|
|
197
|
+
|
|
198
|
+
var self: Context = .{
|
|
199
|
+
.io = &io,
|
|
200
|
+
.server = server,
|
|
201
|
+
.client = client,
|
|
202
|
+
};
|
|
203
|
+
|
|
204
|
+
var client_completion: IO.Completion = undefined;
|
|
205
|
+
self.io.connect(
|
|
206
|
+
*Context,
|
|
207
|
+
&self,
|
|
208
|
+
connect_callback,
|
|
209
|
+
&client_completion,
|
|
210
|
+
client,
|
|
211
|
+
client_address,
|
|
212
|
+
);
|
|
213
|
+
|
|
214
|
+
var server_completion: IO.Completion = undefined;
|
|
215
|
+
self.io.accept(*Context, &self, accept_callback, &server_completion, server);
|
|
216
|
+
|
|
217
|
+
while (!self.done) try self.io.run();
|
|
218
|
+
|
|
219
|
+
try testing.expectEqual(self.send_buf.len, self.sent);
|
|
220
|
+
try testing.expectEqual(self.recv_buf.len, self.received);
|
|
221
|
+
|
|
222
|
+
try testing.expectEqualSlices(u8, self.send_buf[0..self.received], &self.recv_buf);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
fn connect_callback(
|
|
226
|
+
self: *Context,
|
|
227
|
+
completion: *IO.Completion,
|
|
228
|
+
result: IO.ConnectError!void,
|
|
229
|
+
) void {
|
|
230
|
+
_ = result catch @panic("connect error");
|
|
231
|
+
|
|
232
|
+
self.io.send(
|
|
233
|
+
*Context,
|
|
234
|
+
self,
|
|
235
|
+
send_callback,
|
|
236
|
+
completion,
|
|
237
|
+
self.client,
|
|
238
|
+
&self.send_buf,
|
|
239
|
+
);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
fn send_callback(
|
|
243
|
+
self: *Context,
|
|
244
|
+
completion: *IO.Completion,
|
|
245
|
+
result: IO.SendError!usize,
|
|
246
|
+
) void {
|
|
247
|
+
_ = completion;
|
|
248
|
+
|
|
249
|
+
self.sent = result catch @panic("send error");
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
fn accept_callback(
|
|
253
|
+
self: *Context,
|
|
254
|
+
completion: *IO.Completion,
|
|
255
|
+
result: IO.AcceptError!posix.socket_t,
|
|
256
|
+
) void {
|
|
257
|
+
self.accepted_sock = result catch @panic("accept error");
|
|
258
|
+
self.io.recv(
|
|
259
|
+
*Context,
|
|
260
|
+
self,
|
|
261
|
+
recv_callback,
|
|
262
|
+
completion,
|
|
263
|
+
self.accepted_sock,
|
|
264
|
+
&self.recv_buf,
|
|
265
|
+
);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
fn recv_callback(
|
|
269
|
+
self: *Context,
|
|
270
|
+
completion: *IO.Completion,
|
|
271
|
+
result: IO.RecvError!usize,
|
|
272
|
+
) void {
|
|
273
|
+
_ = completion;
|
|
274
|
+
|
|
275
|
+
self.received = result catch @panic("recv error");
|
|
276
|
+
self.done = true;
|
|
277
|
+
}
|
|
278
|
+
}.run_test();
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
test "timeout" {
|
|
282
|
+
const ms = 20;
|
|
283
|
+
const margin = 100;
|
|
284
|
+
const count = 10;
|
|
285
|
+
|
|
286
|
+
try struct {
|
|
287
|
+
const Context = @This();
|
|
288
|
+
|
|
289
|
+
io: IO,
|
|
290
|
+
timer: Time,
|
|
291
|
+
count: u32 = 0,
|
|
292
|
+
stop_time: u64 = 0,
|
|
293
|
+
|
|
294
|
+
fn run_test() !void {
|
|
295
|
+
var time_os: TimeOS = .{};
|
|
296
|
+
const timer = time_os.time();
|
|
297
|
+
const start_time = timer.monotonic().ns;
|
|
298
|
+
var self: Context = .{
|
|
299
|
+
.timer = timer,
|
|
300
|
+
.io = try IO.init(32, 0),
|
|
301
|
+
};
|
|
302
|
+
defer self.io.deinit();
|
|
303
|
+
|
|
304
|
+
var completions: [count]IO.Completion = undefined;
|
|
305
|
+
for (&completions) |*completion| {
|
|
306
|
+
self.io.timeout(
|
|
307
|
+
*Context,
|
|
308
|
+
&self,
|
|
309
|
+
timeout_callback,
|
|
310
|
+
completion,
|
|
311
|
+
ms * std.time.ns_per_ms,
|
|
312
|
+
);
|
|
313
|
+
}
|
|
314
|
+
while (self.count < count) try self.io.run();
|
|
315
|
+
|
|
316
|
+
try self.io.run();
|
|
317
|
+
try testing.expectEqual(@as(u32, count), self.count);
|
|
318
|
+
|
|
319
|
+
try testing.expectApproxEqAbs(
|
|
320
|
+
@as(f64, ms),
|
|
321
|
+
@as(f64, @floatFromInt((self.stop_time - start_time) / std.time.ns_per_ms)),
|
|
322
|
+
margin,
|
|
323
|
+
);
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
fn timeout_callback(
|
|
327
|
+
self: *Context,
|
|
328
|
+
completion: *IO.Completion,
|
|
329
|
+
result: IO.TimeoutError!void,
|
|
330
|
+
) void {
|
|
331
|
+
_ = completion;
|
|
332
|
+
_ = result catch @panic("timeout error");
|
|
333
|
+
|
|
334
|
+
if (self.stop_time == 0) self.stop_time = self.timer.monotonic().ns;
|
|
335
|
+
self.count += 1;
|
|
336
|
+
}
|
|
337
|
+
}.run_test();
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
test "event" {
|
|
341
|
+
try struct {
|
|
342
|
+
const Context = @This();
|
|
343
|
+
|
|
344
|
+
io: IO,
|
|
345
|
+
count: u32 = 0,
|
|
346
|
+
main_thread_id: std.Thread.Id,
|
|
347
|
+
event: IO.Event = IO.INVALID_EVENT,
|
|
348
|
+
event_completion: IO.Completion = undefined,
|
|
349
|
+
|
|
350
|
+
const delay = 5 * std.time.ns_per_ms;
|
|
351
|
+
const events_count = 5;
|
|
352
|
+
|
|
353
|
+
fn run_test() !void {
|
|
354
|
+
var self: Context = .{
|
|
355
|
+
.io = try IO.init(32, 0),
|
|
356
|
+
.main_thread_id = std.Thread.getCurrentId(),
|
|
357
|
+
};
|
|
358
|
+
defer self.io.deinit();
|
|
359
|
+
|
|
360
|
+
self.event = try self.io.open_event();
|
|
361
|
+
defer self.io.close_event(self.event);
|
|
362
|
+
|
|
363
|
+
var time_os: TimeOS = .{};
|
|
364
|
+
const timer = time_os.time();
|
|
365
|
+
const start = timer.monotonic();
|
|
366
|
+
|
|
367
|
+
// Listen to the event and spawn a thread that triggers the completion after some time.
|
|
368
|
+
self.io.event_listen(self.event, &self.event_completion, on_event);
|
|
369
|
+
const thread = try std.Thread.spawn(.{}, Context.trigger_event, .{&self});
|
|
370
|
+
|
|
371
|
+
// Wait for the number of events to complete.
|
|
372
|
+
while (self.count < events_count) try self.io.run();
|
|
373
|
+
thread.join();
|
|
374
|
+
|
|
375
|
+
// Make sure the event was triggered multiple times.
|
|
376
|
+
assert(self.count == events_count);
|
|
377
|
+
|
|
378
|
+
// Make sure at least some time has passed.
|
|
379
|
+
const elapsed = timer.monotonic().duration_since(start);
|
|
380
|
+
assert(elapsed.ns >= delay);
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
fn trigger_event(self: *Context) void {
|
|
384
|
+
assert(std.Thread.getCurrentId() != self.main_thread_id);
|
|
385
|
+
while (self.count < events_count) {
|
|
386
|
+
std.time.sleep(delay + 1);
|
|
387
|
+
|
|
388
|
+
// Triggering the event:
|
|
389
|
+
self.io.event_trigger(self.event, &self.event_completion);
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
fn on_event(completion: *IO.Completion) void {
|
|
394
|
+
const self: *Context = @fieldParentPtr("event_completion", completion);
|
|
395
|
+
assert(std.Thread.getCurrentId() == self.main_thread_id);
|
|
396
|
+
|
|
397
|
+
self.count += 1;
|
|
398
|
+
if (self.count == events_count) return;
|
|
399
|
+
|
|
400
|
+
// Reattaching the event.
|
|
401
|
+
self.io.event_listen(self.event, &self.event_completion, on_event);
|
|
402
|
+
}
|
|
403
|
+
}.run_test();
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
test "submission queue full" {
|
|
407
|
+
const ms = 20;
|
|
408
|
+
const count = 10;
|
|
409
|
+
|
|
410
|
+
try struct {
|
|
411
|
+
const Context = @This();
|
|
412
|
+
|
|
413
|
+
io: IO,
|
|
414
|
+
count: u32 = 0,
|
|
415
|
+
|
|
416
|
+
fn run_test() !void {
|
|
417
|
+
var self: Context = .{ .io = try IO.init(1, 0) };
|
|
418
|
+
defer self.io.deinit();
|
|
419
|
+
|
|
420
|
+
var completions: [count]IO.Completion = undefined;
|
|
421
|
+
for (&completions) |*completion| {
|
|
422
|
+
self.io.timeout(
|
|
423
|
+
*Context,
|
|
424
|
+
&self,
|
|
425
|
+
timeout_callback,
|
|
426
|
+
completion,
|
|
427
|
+
ms * std.time.ns_per_ms,
|
|
428
|
+
);
|
|
429
|
+
}
|
|
430
|
+
while (self.count < count) try self.io.run();
|
|
431
|
+
|
|
432
|
+
try self.io.run();
|
|
433
|
+
try testing.expectEqual(@as(u32, count), self.count);
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
fn timeout_callback(
|
|
437
|
+
self: *Context,
|
|
438
|
+
completion: *IO.Completion,
|
|
439
|
+
result: IO.TimeoutError!void,
|
|
440
|
+
) void {
|
|
441
|
+
_ = completion;
|
|
442
|
+
_ = result catch @panic("timeout error");
|
|
443
|
+
|
|
444
|
+
self.count += 1;
|
|
445
|
+
}
|
|
446
|
+
}.run_test();
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
test "tick to wait" {
|
|
450
|
+
// Use only IO.run() to see if pending IO is actually processed.
|
|
451
|
+
|
|
452
|
+
try struct {
|
|
453
|
+
const Context = @This();
|
|
454
|
+
|
|
455
|
+
io: IO,
|
|
456
|
+
accepted: ?posix.socket_t = null,
|
|
457
|
+
connected: bool = false,
|
|
458
|
+
received: bool = false,
|
|
459
|
+
|
|
460
|
+
fn run_test() !void {
|
|
461
|
+
var self: Context = .{ .io = try IO.init(1, 0) };
|
|
462
|
+
defer self.io.deinit();
|
|
463
|
+
|
|
464
|
+
const address = try std.net.Address.parseIp4("127.0.0.1", 0);
|
|
465
|
+
const kernel_backlog = 1;
|
|
466
|
+
|
|
467
|
+
const server = try self.io.open_socket_tcp(address.any.family, tcp_options);
|
|
468
|
+
defer self.io.close_socket(server);
|
|
469
|
+
|
|
470
|
+
try posix.setsockopt(
|
|
471
|
+
server,
|
|
472
|
+
posix.SOL.SOCKET,
|
|
473
|
+
posix.SO.REUSEADDR,
|
|
474
|
+
&std.mem.toBytes(@as(c_int, 1)),
|
|
475
|
+
);
|
|
476
|
+
try posix.bind(server, &address.any, address.getOsSockLen());
|
|
477
|
+
try posix.listen(server, kernel_backlog);
|
|
478
|
+
|
|
479
|
+
var client_address = std.net.Address.initIp4(undefined, undefined);
|
|
480
|
+
var client_address_len = client_address.getOsSockLen();
|
|
481
|
+
try posix.getsockname(server, &client_address.any, &client_address_len);
|
|
482
|
+
|
|
483
|
+
const client = try self.io.open_socket_tcp(client_address.any.family, tcp_options);
|
|
484
|
+
defer self.io.close_socket(client);
|
|
485
|
+
|
|
486
|
+
// Start the accept.
|
|
487
|
+
var server_completion: IO.Completion = undefined;
|
|
488
|
+
self.io.accept(*Context, &self, accept_callback, &server_completion, server);
|
|
489
|
+
|
|
490
|
+
// Start the connect.
|
|
491
|
+
var client_completion: IO.Completion = undefined;
|
|
492
|
+
self.io.connect(
|
|
493
|
+
*Context,
|
|
494
|
+
&self,
|
|
495
|
+
connect_callback,
|
|
496
|
+
&client_completion,
|
|
497
|
+
client,
|
|
498
|
+
client_address,
|
|
499
|
+
);
|
|
500
|
+
|
|
501
|
+
// Tick the IO to drain the accept & connect completions.
|
|
502
|
+
assert(!self.connected);
|
|
503
|
+
assert(self.accepted == null);
|
|
504
|
+
|
|
505
|
+
while (self.accepted == null or !self.connected)
|
|
506
|
+
try self.io.run();
|
|
507
|
+
|
|
508
|
+
assert(self.connected);
|
|
509
|
+
assert(self.accepted != null);
|
|
510
|
+
defer self.io.close_socket(self.accepted.?);
|
|
511
|
+
|
|
512
|
+
// Start receiving on the client.
|
|
513
|
+
var recv_completion: IO.Completion = undefined;
|
|
514
|
+
var recv_buffer: [64]u8 = undefined;
|
|
515
|
+
@memset(&recv_buffer, 0xaa);
|
|
516
|
+
self.io.recv(
|
|
517
|
+
*Context,
|
|
518
|
+
&self,
|
|
519
|
+
recv_callback,
|
|
520
|
+
&recv_completion,
|
|
521
|
+
client,
|
|
522
|
+
&recv_buffer,
|
|
523
|
+
);
|
|
524
|
+
|
|
525
|
+
// Drain out the recv completion from any internal IO queues.
|
|
526
|
+
try self.io.run();
|
|
527
|
+
try self.io.run();
|
|
528
|
+
try self.io.run();
|
|
529
|
+
|
|
530
|
+
// Complete the recv() *outside* of the IO instance.
|
|
531
|
+
// Other tests already check .tick() with IO based completions.
|
|
532
|
+
// This simulates IO being completed by an external system.
|
|
533
|
+
var send_buf: [64]u8 = @splat(0);
|
|
534
|
+
const wrote = try os_send(self.accepted.?, &send_buf, 0);
|
|
535
|
+
try testing.expectEqual(wrote, send_buf.len);
|
|
536
|
+
|
|
537
|
+
// Wait for the recv() to complete using only IO.run().
|
|
538
|
+
// If tick is broken, then this will deadlock
|
|
539
|
+
assert(!self.received);
|
|
540
|
+
while (!self.received) {
|
|
541
|
+
try self.io.run();
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
// Make sure the receive actually happened.
|
|
545
|
+
assert(self.received);
|
|
546
|
+
try testing.expect(std.mem.eql(u8, &recv_buffer, &send_buf));
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
fn accept_callback(
|
|
550
|
+
self: *Context,
|
|
551
|
+
completion: *IO.Completion,
|
|
552
|
+
result: IO.AcceptError!posix.socket_t,
|
|
553
|
+
) void {
|
|
554
|
+
_ = completion;
|
|
555
|
+
|
|
556
|
+
assert(self.accepted == null);
|
|
557
|
+
self.accepted = result catch @panic("accept error");
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
fn connect_callback(
|
|
561
|
+
self: *Context,
|
|
562
|
+
completion: *IO.Completion,
|
|
563
|
+
result: IO.ConnectError!void,
|
|
564
|
+
) void {
|
|
565
|
+
_ = completion;
|
|
566
|
+
_ = result catch @panic("connect error");
|
|
567
|
+
|
|
568
|
+
assert(!self.connected);
|
|
569
|
+
self.connected = true;
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
fn recv_callback(
|
|
573
|
+
self: *Context,
|
|
574
|
+
completion: *IO.Completion,
|
|
575
|
+
result: IO.RecvError!usize,
|
|
576
|
+
) void {
|
|
577
|
+
_ = completion;
|
|
578
|
+
_ = result catch |err| std.debug.panic("recv error: {}", .{err});
|
|
579
|
+
|
|
580
|
+
assert(!self.received);
|
|
581
|
+
self.received = true;
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
fn os_send(sock: posix.socket_t, buf: []const u8, flags: u32) !usize {
|
|
585
|
+
return posix.sendto(sock, buf, flags, null, 0);
|
|
586
|
+
}
|
|
587
|
+
}.run_test();
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
test "pipe data over socket" {
|
|
591
|
+
try struct {
|
|
592
|
+
io: IO,
|
|
593
|
+
tx: Pipe,
|
|
594
|
+
rx: Pipe,
|
|
595
|
+
server: Socket = .{},
|
|
596
|
+
|
|
597
|
+
const buffer_size = 1 * MiB;
|
|
598
|
+
|
|
599
|
+
const Context = @This();
|
|
600
|
+
const Socket = struct {
|
|
601
|
+
fd: ?posix.socket_t = null,
|
|
602
|
+
completion: IO.Completion = undefined,
|
|
603
|
+
};
|
|
604
|
+
const Pipe = struct {
|
|
605
|
+
socket: Socket = .{},
|
|
606
|
+
buffer: []u8,
|
|
607
|
+
transferred: usize = 0,
|
|
608
|
+
};
|
|
609
|
+
|
|
610
|
+
fn run() !void {
|
|
611
|
+
const tx_buf = try testing.allocator.alloc(u8, buffer_size);
|
|
612
|
+
defer testing.allocator.free(tx_buf);
|
|
613
|
+
|
|
614
|
+
const rx_buf = try testing.allocator.alloc(u8, buffer_size);
|
|
615
|
+
defer testing.allocator.free(rx_buf);
|
|
616
|
+
|
|
617
|
+
@memset(tx_buf, 1);
|
|
618
|
+
@memset(rx_buf, 0);
|
|
619
|
+
var self = Context{
|
|
620
|
+
.io = try IO.init(32, 0),
|
|
621
|
+
.tx = .{ .buffer = tx_buf },
|
|
622
|
+
.rx = .{ .buffer = rx_buf },
|
|
623
|
+
};
|
|
624
|
+
defer self.io.deinit();
|
|
625
|
+
|
|
626
|
+
self.server.fd = try self.io.open_socket_tcp(posix.AF.INET, tcp_options);
|
|
627
|
+
defer self.io.close_socket(self.server.fd.?);
|
|
628
|
+
|
|
629
|
+
const address = try std.net.Address.parseIp4("127.0.0.1", 0);
|
|
630
|
+
try posix.setsockopt(
|
|
631
|
+
self.server.fd.?,
|
|
632
|
+
posix.SOL.SOCKET,
|
|
633
|
+
posix.SO.REUSEADDR,
|
|
634
|
+
&std.mem.toBytes(@as(c_int, 1)),
|
|
635
|
+
);
|
|
636
|
+
|
|
637
|
+
try posix.bind(self.server.fd.?, &address.any, address.getOsSockLen());
|
|
638
|
+
try posix.listen(self.server.fd.?, 1);
|
|
639
|
+
|
|
640
|
+
var client_address = std.net.Address.initIp4(undefined, undefined);
|
|
641
|
+
var client_address_len = client_address.getOsSockLen();
|
|
642
|
+
try posix.getsockname(self.server.fd.?, &client_address.any, &client_address_len);
|
|
643
|
+
|
|
644
|
+
self.io.accept(
|
|
645
|
+
*Context,
|
|
646
|
+
&self,
|
|
647
|
+
on_accept,
|
|
648
|
+
&self.server.completion,
|
|
649
|
+
self.server.fd.?,
|
|
650
|
+
);
|
|
651
|
+
|
|
652
|
+
self.tx.socket.fd = try self.io.open_socket_tcp(posix.AF.INET, tcp_options);
|
|
653
|
+
defer self.io.close_socket(self.tx.socket.fd.?);
|
|
654
|
+
|
|
655
|
+
self.io.connect(
|
|
656
|
+
*Context,
|
|
657
|
+
&self,
|
|
658
|
+
on_connect,
|
|
659
|
+
&self.tx.socket.completion,
|
|
660
|
+
self.tx.socket.fd.?,
|
|
661
|
+
client_address,
|
|
662
|
+
);
|
|
663
|
+
|
|
664
|
+
var tick: usize = 0xdeadbeef;
|
|
665
|
+
while (self.rx.transferred != self.rx.buffer.len) : (tick +%= 1) {
|
|
666
|
+
if (tick % 61 == 0) {
|
|
667
|
+
const timeout_ns = tick % (10 * std.time.ns_per_ms);
|
|
668
|
+
try self.io.run_for_ns(@as(u63, @intCast(timeout_ns)));
|
|
669
|
+
} else {
|
|
670
|
+
try self.io.run();
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
try testing.expect(self.server.fd != null);
|
|
675
|
+
try testing.expect(self.tx.socket.fd != null);
|
|
676
|
+
try testing.expect(self.rx.socket.fd != null);
|
|
677
|
+
self.io.close_socket(self.rx.socket.fd.?);
|
|
678
|
+
|
|
679
|
+
try testing.expectEqual(self.tx.transferred, buffer_size);
|
|
680
|
+
try testing.expectEqual(self.rx.transferred, buffer_size);
|
|
681
|
+
try testing.expect(std.mem.eql(u8, self.tx.buffer, self.rx.buffer));
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
fn on_accept(
|
|
685
|
+
self: *Context,
|
|
686
|
+
completion: *IO.Completion,
|
|
687
|
+
result: IO.AcceptError!posix.socket_t,
|
|
688
|
+
) void {
|
|
689
|
+
assert(self.rx.socket.fd == null);
|
|
690
|
+
assert(&self.server.completion == completion);
|
|
691
|
+
self.rx.socket.fd = result catch |err| std.debug.panic("accept error {}", .{err});
|
|
692
|
+
|
|
693
|
+
assert(self.rx.transferred == 0);
|
|
694
|
+
self.do_receiver(0);
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
fn on_connect(
|
|
698
|
+
self: *Context,
|
|
699
|
+
completion: *IO.Completion,
|
|
700
|
+
result: IO.ConnectError!void,
|
|
701
|
+
) void {
|
|
702
|
+
_ = result catch unreachable;
|
|
703
|
+
|
|
704
|
+
assert(self.tx.socket.fd != null);
|
|
705
|
+
assert(&self.tx.socket.completion == completion);
|
|
706
|
+
|
|
707
|
+
assert(self.tx.transferred == 0);
|
|
708
|
+
self.do_sender(0);
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
fn do_sender(self: *Context, bytes: usize) void {
|
|
712
|
+
self.tx.transferred += bytes;
|
|
713
|
+
assert(self.tx.transferred <= self.tx.buffer.len);
|
|
714
|
+
|
|
715
|
+
if (self.tx.transferred < self.tx.buffer.len) {
|
|
716
|
+
self.io.send(
|
|
717
|
+
*Context,
|
|
718
|
+
self,
|
|
719
|
+
on_send,
|
|
720
|
+
&self.tx.socket.completion,
|
|
721
|
+
self.tx.socket.fd.?,
|
|
722
|
+
self.tx.buffer[self.tx.transferred..],
|
|
723
|
+
);
|
|
724
|
+
}
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
fn on_send(
|
|
728
|
+
self: *Context,
|
|
729
|
+
completion: *IO.Completion,
|
|
730
|
+
result: IO.SendError!usize,
|
|
731
|
+
) void {
|
|
732
|
+
const bytes = result catch |err| std.debug.panic("send error: {}", .{err});
|
|
733
|
+
assert(&self.tx.socket.completion == completion);
|
|
734
|
+
self.do_sender(bytes);
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
fn do_receiver(self: *Context, bytes: usize) void {
|
|
738
|
+
self.rx.transferred += bytes;
|
|
739
|
+
assert(self.rx.transferred <= self.rx.buffer.len);
|
|
740
|
+
|
|
741
|
+
if (self.rx.transferred < self.rx.buffer.len) {
|
|
742
|
+
self.io.recv(
|
|
743
|
+
*Context,
|
|
744
|
+
self,
|
|
745
|
+
on_recv,
|
|
746
|
+
&self.rx.socket.completion,
|
|
747
|
+
self.rx.socket.fd.?,
|
|
748
|
+
self.rx.buffer[self.rx.transferred..],
|
|
749
|
+
);
|
|
750
|
+
}
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
fn on_recv(
|
|
754
|
+
self: *Context,
|
|
755
|
+
completion: *IO.Completion,
|
|
756
|
+
result: IO.RecvError!usize,
|
|
757
|
+
) void {
|
|
758
|
+
const bytes = result catch |err| std.debug.panic("recv error: {}", .{err});
|
|
759
|
+
assert(&self.rx.socket.completion == completion);
|
|
760
|
+
self.do_receiver(bytes);
|
|
761
|
+
}
|
|
762
|
+
}.run();
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
test "cancel_all" {
|
|
766
|
+
const checksum = @import("../vsr/checksum.zig").checksum;
|
|
767
|
+
const allocator = std.testing.allocator;
|
|
768
|
+
const file_path = "test_cancel_all";
|
|
769
|
+
const read_count = 8;
|
|
770
|
+
const read_size = 16 * KiB;
|
|
771
|
+
|
|
772
|
+
// For this test to be useful, we rely on open(DIRECT).
|
|
773
|
+
// (See below).
|
|
774
|
+
if (builtin.target.os.tag != .linux) return;
|
|
775
|
+
|
|
776
|
+
try struct {
|
|
777
|
+
const Context = @This();
|
|
778
|
+
|
|
779
|
+
io: IO,
|
|
780
|
+
canceled: bool = false,
|
|
781
|
+
|
|
782
|
+
fn run_test() !void {
|
|
783
|
+
defer std.fs.cwd().deleteFile(file_path) catch {};
|
|
784
|
+
|
|
785
|
+
var context: Context = .{ .io = try IO.init(32, 0) };
|
|
786
|
+
defer context.io.deinit();
|
|
787
|
+
|
|
788
|
+
{
|
|
789
|
+
// Initialize a file filled with test data.
|
|
790
|
+
const file_buffer = try allocator.alloc(u8, read_size);
|
|
791
|
+
defer allocator.free(file_buffer);
|
|
792
|
+
|
|
793
|
+
for (file_buffer, 0..) |*b, i| b.* = @intCast(i % 256);
|
|
794
|
+
|
|
795
|
+
try std.fs.cwd().writeFile(.{ .sub_path = file_path, .data = file_buffer });
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
var read_completions: [read_count]IO.Completion = undefined;
|
|
799
|
+
var read_buffers: [read_count][]u8 = undefined;
|
|
800
|
+
var read_buffer_checksums: [read_count]u128 = undefined;
|
|
801
|
+
var read_buffers_allocated: u32 = 0;
|
|
802
|
+
defer for (read_buffers[0..read_buffers_allocated]) |b| allocator.free(b);
|
|
803
|
+
|
|
804
|
+
for (&read_buffers) |*read_buffer| {
|
|
805
|
+
read_buffer.* = try allocator.alloc(u8, read_size);
|
|
806
|
+
read_buffers_allocated += 1;
|
|
807
|
+
}
|
|
808
|
+
|
|
809
|
+
// Test cancellation:
|
|
810
|
+
// 1. Re-open the file.
|
|
811
|
+
// 2. Kick off multiple (async) reads.
|
|
812
|
+
// 3. Abort the reads (ideally before they can complete, since that is more interesting
|
|
813
|
+
// to test).
|
|
814
|
+
//
|
|
815
|
+
// The reason to re-open the file with DIRECT is that it slows down the reads enough to
|
|
816
|
+
// actually test the interesting case -- cancelling an in-flight read and verifying that
|
|
817
|
+
// the buffer is not written to after `cancel_all()` completes.
|
|
818
|
+
//
|
|
819
|
+
// (Without DIRECT the reads all finish their callbacks even before io.run() returns.)
|
|
820
|
+
const file = try std.posix.open(file_path, .{ .DIRECT = true }, 0);
|
|
821
|
+
defer std.posix.close(file);
|
|
822
|
+
|
|
823
|
+
for (&read_completions, read_buffers) |*completion, buffer| {
|
|
824
|
+
context.io.read(*Context, &context, read_callback, completion, file, buffer, 0);
|
|
825
|
+
}
|
|
826
|
+
try context.io.run();
|
|
827
|
+
|
|
828
|
+
// Set to true *before* calling cancel_all() to ensure that any farther callbacks from
|
|
829
|
+
// IO completion will panic.
|
|
830
|
+
context.canceled = true;
|
|
831
|
+
|
|
832
|
+
context.io.cancel_all();
|
|
833
|
+
|
|
834
|
+
// All of the in-flight reads are canceled at this point.
|
|
835
|
+
// To verify, checksum all of the read buffer memory, then wait and make sure that there
|
|
836
|
+
// are no farther modifications to the buffers.
|
|
837
|
+
for (read_buffers, &read_buffer_checksums) |buffer, *buffer_checksum| {
|
|
838
|
+
buffer_checksum.* = checksum(buffer);
|
|
839
|
+
}
|
|
840
|
+
|
|
841
|
+
const sleep_ms = 50;
|
|
842
|
+
std.time.sleep(sleep_ms * std.time.ns_per_ms);
|
|
843
|
+
|
|
844
|
+
for (read_buffers, read_buffer_checksums) |buffer, buffer_checksum| {
|
|
845
|
+
try testing.expectEqual(checksum(buffer), buffer_checksum);
|
|
846
|
+
}
|
|
847
|
+
}
|
|
848
|
+
|
|
849
|
+
fn read_callback(
|
|
850
|
+
context: *Context,
|
|
851
|
+
completion: *IO.Completion,
|
|
852
|
+
result: IO.ReadError!usize,
|
|
853
|
+
) void {
|
|
854
|
+
_ = completion;
|
|
855
|
+
_ = result catch @panic("read error");
|
|
856
|
+
|
|
857
|
+
assert(!context.canceled);
|
|
858
|
+
}
|
|
859
|
+
}.run_test();
|
|
860
|
+
}
|
|
861
|
+
|
|
862
|
+
test "cancel" {
|
|
863
|
+
if (builtin.target.os.tag != .linux) return;
|
|
864
|
+
try struct {
|
|
865
|
+
const Context = @This();
|
|
866
|
+
|
|
867
|
+
io: *IO,
|
|
868
|
+
server: posix.socket_t,
|
|
869
|
+
client: posix.socket_t,
|
|
870
|
+
accepted_sock: posix.socket_t = undefined,
|
|
871
|
+
|
|
872
|
+
accepted: bool = false,
|
|
873
|
+
connected: bool = false,
|
|
874
|
+
canceled: bool = false,
|
|
875
|
+
|
|
876
|
+
recv_result: ?IO.RecvError!usize = null,
|
|
877
|
+
|
|
878
|
+
fn run_test() !void {
|
|
879
|
+
const allocator = std.testing.allocator;
|
|
880
|
+
var io = try IO.init(32, 0);
|
|
881
|
+
defer io.deinit();
|
|
882
|
+
|
|
883
|
+
const buffer_size = 512 * KiB;
|
|
884
|
+
|
|
885
|
+
const buffer: []u8 = try allocator.alloc(u8, buffer_size);
|
|
886
|
+
defer allocator.free(buffer);
|
|
887
|
+
|
|
888
|
+
const address = try std.net.Address.parseIp4("127.0.0.1", 0);
|
|
889
|
+
|
|
890
|
+
const server = try io.open_socket_tcp(address.any.family, tcp_options);
|
|
891
|
+
defer io.close_socket(server);
|
|
892
|
+
|
|
893
|
+
const client = try io.open_socket_tcp(address.any.family, tcp_options);
|
|
894
|
+
defer io.close_socket(client);
|
|
895
|
+
|
|
896
|
+
try posix.setsockopt(
|
|
897
|
+
server,
|
|
898
|
+
posix.SOL.SOCKET,
|
|
899
|
+
posix.SO.REUSEADDR,
|
|
900
|
+
&std.mem.toBytes(@as(c_int, 1)),
|
|
901
|
+
);
|
|
902
|
+
try posix.bind(server, &address.any, address.getOsSockLen());
|
|
903
|
+
try posix.listen(server, 1);
|
|
904
|
+
|
|
905
|
+
var client_address = std.net.Address.initIp4(undefined, undefined);
|
|
906
|
+
var client_address_len = client_address.getOsSockLen();
|
|
907
|
+
try posix.getsockname(
|
|
908
|
+
server,
|
|
909
|
+
&client_address.any,
|
|
910
|
+
&client_address_len,
|
|
911
|
+
);
|
|
912
|
+
|
|
913
|
+
var context: Context = .{
|
|
914
|
+
.io = &io,
|
|
915
|
+
.server = server,
|
|
916
|
+
.client = client,
|
|
917
|
+
};
|
|
918
|
+
|
|
919
|
+
var client_completion: IO.Completion = undefined;
|
|
920
|
+
context.io.connect(
|
|
921
|
+
*Context,
|
|
922
|
+
&context,
|
|
923
|
+
connect_callback,
|
|
924
|
+
&client_completion,
|
|
925
|
+
client,
|
|
926
|
+
client_address,
|
|
927
|
+
);
|
|
928
|
+
|
|
929
|
+
var server_completion: IO.Completion = undefined;
|
|
930
|
+
context.io.accept(
|
|
931
|
+
*Context,
|
|
932
|
+
&context,
|
|
933
|
+
accept_callback,
|
|
934
|
+
&server_completion,
|
|
935
|
+
server,
|
|
936
|
+
);
|
|
937
|
+
|
|
938
|
+
while (!(context.connected and context.accepted)) try context.io.run();
|
|
939
|
+
|
|
940
|
+
var recv_completion: IO.Completion = undefined;
|
|
941
|
+
context.io.recv(
|
|
942
|
+
*Context,
|
|
943
|
+
&context,
|
|
944
|
+
recv_callback,
|
|
945
|
+
&recv_completion,
|
|
946
|
+
context.accepted_sock,
|
|
947
|
+
buffer,
|
|
948
|
+
);
|
|
949
|
+
try context.io.run();
|
|
950
|
+
|
|
951
|
+
var cancel_completion: IO.Completion = undefined;
|
|
952
|
+
context.io.cancel(
|
|
953
|
+
*Context,
|
|
954
|
+
&context,
|
|
955
|
+
cancel_callback,
|
|
956
|
+
.{
|
|
957
|
+
.completion = &cancel_completion,
|
|
958
|
+
.target = &recv_completion,
|
|
959
|
+
},
|
|
960
|
+
);
|
|
961
|
+
|
|
962
|
+
while (!context.canceled or context.recv_result == null) try context.io.run();
|
|
963
|
+
|
|
964
|
+
try std.testing.expectError(
|
|
965
|
+
IO.RecvError.Canceled,
|
|
966
|
+
context.recv_result.?,
|
|
967
|
+
);
|
|
968
|
+
}
|
|
969
|
+
|
|
970
|
+
fn cancel_callback(
|
|
971
|
+
self: *Context,
|
|
972
|
+
_: *IO.Completion,
|
|
973
|
+
result: IO.CancelError!void,
|
|
974
|
+
) void {
|
|
975
|
+
_ = result catch @panic("cancel error");
|
|
976
|
+
self.canceled = true;
|
|
977
|
+
}
|
|
978
|
+
|
|
979
|
+
fn connect_callback(
|
|
980
|
+
self: *Context,
|
|
981
|
+
_: *IO.Completion,
|
|
982
|
+
result: IO.ConnectError!void,
|
|
983
|
+
) void {
|
|
984
|
+
_ = result catch @panic("connect error");
|
|
985
|
+
self.connected = true;
|
|
986
|
+
}
|
|
987
|
+
|
|
988
|
+
fn accept_callback(
|
|
989
|
+
self: *Context,
|
|
990
|
+
_: *IO.Completion,
|
|
991
|
+
result: IO.AcceptError!posix.socket_t,
|
|
992
|
+
) void {
|
|
993
|
+
self.accepted_sock = result catch @panic("accept error");
|
|
994
|
+
self.accepted = true;
|
|
995
|
+
}
|
|
996
|
+
|
|
997
|
+
fn recv_callback(
|
|
998
|
+
self: *Context,
|
|
999
|
+
_: *IO.Completion,
|
|
1000
|
+
result: IO.RecvError!usize,
|
|
1001
|
+
) void {
|
|
1002
|
+
self.recv_result = result;
|
|
1003
|
+
}
|
|
1004
|
+
}.run_test();
|
|
1005
|
+
}
|