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,286 @@
|
|
|
1
|
+
const std = @import("std");
|
|
2
|
+
const assert = std.debug.assert;
|
|
3
|
+
const mem = std.mem;
|
|
4
|
+
|
|
5
|
+
const vsr = @import("../tb_client.zig").vsr;
|
|
6
|
+
const Header = vsr.Header;
|
|
7
|
+
const stdx = vsr.stdx;
|
|
8
|
+
const constants = vsr.constants;
|
|
9
|
+
const MessagePool = vsr.message_pool.MessagePool;
|
|
10
|
+
const Message = MessagePool.Message;
|
|
11
|
+
const Time = vsr.time.Time;
|
|
12
|
+
|
|
13
|
+
pub fn EchoClientType(comptime MessageBus: type) type {
|
|
14
|
+
return struct {
|
|
15
|
+
const EchoClient = @This();
|
|
16
|
+
|
|
17
|
+
// Exposing the same types the real client does:
|
|
18
|
+
const VSRClient = vsr.ClientType(EchoOperation, MessageBus);
|
|
19
|
+
pub const Operation = VSRClient.Operation;
|
|
20
|
+
pub const Request = VSRClient.Request;
|
|
21
|
+
|
|
22
|
+
id: u128,
|
|
23
|
+
cluster: u128,
|
|
24
|
+
release: vsr.Release = vsr.Release.minimum,
|
|
25
|
+
request_number: u32 = 0,
|
|
26
|
+
reply_timestamp: u64 = 0, // Fake timestamp, just a counter.
|
|
27
|
+
request_inflight: ?Request = null,
|
|
28
|
+
message_pool: *MessagePool,
|
|
29
|
+
time: Time,
|
|
30
|
+
|
|
31
|
+
pub fn init(
|
|
32
|
+
allocator: mem.Allocator,
|
|
33
|
+
time: Time,
|
|
34
|
+
message_pool: *MessagePool,
|
|
35
|
+
options: struct {
|
|
36
|
+
id: u128,
|
|
37
|
+
cluster: u128,
|
|
38
|
+
replica_count: u8,
|
|
39
|
+
message_bus_options: MessageBus.Options,
|
|
40
|
+
eviction_callback: ?*const fn (
|
|
41
|
+
client: *EchoClient,
|
|
42
|
+
eviction: *const Message.Eviction,
|
|
43
|
+
) void = null,
|
|
44
|
+
aof_recovery: bool,
|
|
45
|
+
},
|
|
46
|
+
) !EchoClient {
|
|
47
|
+
_ = allocator;
|
|
48
|
+
_ = options.replica_count;
|
|
49
|
+
_ = options.message_bus_options;
|
|
50
|
+
assert(!options.aof_recovery);
|
|
51
|
+
|
|
52
|
+
return EchoClient{
|
|
53
|
+
.id = options.id,
|
|
54
|
+
.cluster = options.cluster,
|
|
55
|
+
.message_pool = message_pool,
|
|
56
|
+
.time = time,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
pub fn deinit(self: *EchoClient, allocator: std.mem.Allocator) void {
|
|
61
|
+
_ = allocator;
|
|
62
|
+
if (self.request_inflight) |inflight| self.release_message(inflight.message.base());
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
pub fn tick(self: *EchoClient) void {
|
|
66
|
+
const inflight = self.request_inflight orelse return;
|
|
67
|
+
self.request_inflight = null;
|
|
68
|
+
|
|
69
|
+
self.reply_timestamp += 1;
|
|
70
|
+
const timestamp = self.reply_timestamp;
|
|
71
|
+
|
|
72
|
+
// Allocate a reply message.
|
|
73
|
+
const reply = self.get_message().build(.request);
|
|
74
|
+
defer self.release_message(reply.base());
|
|
75
|
+
|
|
76
|
+
// Copy the request message's entire content including header into the reply.
|
|
77
|
+
const operation = inflight.message.header.operation;
|
|
78
|
+
stdx.copy_disjoint(
|
|
79
|
+
.exact,
|
|
80
|
+
u8,
|
|
81
|
+
reply.buffer,
|
|
82
|
+
inflight.message.buffer,
|
|
83
|
+
);
|
|
84
|
+
|
|
85
|
+
// Similarly to the real client, release the request message before invoking the
|
|
86
|
+
// callback. This necessitates a `copy_disjoint` above.
|
|
87
|
+
self.release_message(inflight.message.base());
|
|
88
|
+
|
|
89
|
+
switch (inflight.callback) {
|
|
90
|
+
.request => |callback| {
|
|
91
|
+
callback(inflight.user_data, operation, timestamp, reply.body_used());
|
|
92
|
+
},
|
|
93
|
+
.register => |callback| {
|
|
94
|
+
const result = vsr.RegisterResult{
|
|
95
|
+
.batch_size_limit = constants.message_body_size_max,
|
|
96
|
+
};
|
|
97
|
+
callback(inflight.user_data, &result);
|
|
98
|
+
},
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
pub fn register(
|
|
103
|
+
self: *EchoClient,
|
|
104
|
+
callback: Request.RegisterCallback,
|
|
105
|
+
user_data: u128,
|
|
106
|
+
) void {
|
|
107
|
+
assert(self.request_inflight == null);
|
|
108
|
+
assert(self.request_number == 0);
|
|
109
|
+
|
|
110
|
+
const message = self.get_message().build(.request);
|
|
111
|
+
errdefer self.release_message(message.base());
|
|
112
|
+
|
|
113
|
+
// We will set parent, session, view and checksums only when sending for the first time:
|
|
114
|
+
message.header.* = .{
|
|
115
|
+
.client = self.id,
|
|
116
|
+
.request = self.request_number,
|
|
117
|
+
.cluster = self.cluster,
|
|
118
|
+
.command = .request,
|
|
119
|
+
.operation = .register,
|
|
120
|
+
.release = vsr.Release.minimum,
|
|
121
|
+
.previous_request_latency = 0,
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
assert(self.request_number == 0);
|
|
125
|
+
self.request_number += 1;
|
|
126
|
+
|
|
127
|
+
self.request_inflight = .{
|
|
128
|
+
.message = message,
|
|
129
|
+
.user_data = user_data,
|
|
130
|
+
.callback = .{ .register = callback },
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
pub fn request(
|
|
135
|
+
self: *EchoClient,
|
|
136
|
+
callback: Request.Callback,
|
|
137
|
+
user_data: u128,
|
|
138
|
+
operation: Operation,
|
|
139
|
+
events: []const u8,
|
|
140
|
+
) void {
|
|
141
|
+
const event_size = operation.event_size();
|
|
142
|
+
assert(events.len <= constants.message_body_size_max);
|
|
143
|
+
assert(events.len % event_size == 0);
|
|
144
|
+
|
|
145
|
+
const message = self.get_message().build(.request);
|
|
146
|
+
errdefer self.release_message(message.base());
|
|
147
|
+
|
|
148
|
+
message.header.* = .{
|
|
149
|
+
.client = self.id,
|
|
150
|
+
.request = 0, // Set by raw_request() below.
|
|
151
|
+
.cluster = self.cluster,
|
|
152
|
+
.command = .request,
|
|
153
|
+
.release = vsr.Release.minimum,
|
|
154
|
+
.operation = operation.to_vsr(),
|
|
155
|
+
.size = @intCast(@sizeOf(Header) + events.len),
|
|
156
|
+
.previous_request_latency = 0,
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
stdx.copy_disjoint(.exact, u8, message.body_used(), events);
|
|
160
|
+
self.raw_request(callback, user_data, message);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
pub fn raw_request(
|
|
164
|
+
self: *EchoClient,
|
|
165
|
+
callback: Request.Callback,
|
|
166
|
+
user_data: u128,
|
|
167
|
+
message: *Message.Request,
|
|
168
|
+
) void {
|
|
169
|
+
assert(message.header.client == self.id);
|
|
170
|
+
assert(message.header.cluster == self.cluster);
|
|
171
|
+
assert(message.header.release.value == self.release.value);
|
|
172
|
+
assert(!message.header.operation.vsr_reserved());
|
|
173
|
+
assert(message.header.size >= @sizeOf(Header));
|
|
174
|
+
assert(message.header.size <= constants.message_size_max);
|
|
175
|
+
|
|
176
|
+
message.header.request = self.request_number;
|
|
177
|
+
self.request_number += 1;
|
|
178
|
+
|
|
179
|
+
assert(self.request_inflight == null);
|
|
180
|
+
self.request_inflight = .{
|
|
181
|
+
.message = message,
|
|
182
|
+
.user_data = user_data,
|
|
183
|
+
.callback = .{ .request = callback },
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
pub fn get_message(self: *EchoClient) *Message {
|
|
188
|
+
return self.message_pool.get_message(null);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
pub fn release_message(self: *EchoClient, message: *Message) void {
|
|
192
|
+
self.message_pool.unref(message);
|
|
193
|
+
}
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/// Mocks the Accounting StateMachine operation, but replaces
|
|
198
|
+
/// all `Result`s with `Event`s, since the echo client replies
|
|
199
|
+
/// with the same content of the input.
|
|
200
|
+
pub const EchoOperation = enum(u8) {
|
|
201
|
+
const Operation = vsr.tigerbeetle.Operation;
|
|
202
|
+
|
|
203
|
+
pulse = @intFromEnum(Operation.pulse),
|
|
204
|
+
|
|
205
|
+
get_change_events = @intFromEnum(Operation.get_change_events),
|
|
206
|
+
|
|
207
|
+
create_accounts = @intFromEnum(Operation.create_accounts),
|
|
208
|
+
create_transfers = @intFromEnum(Operation.create_transfers),
|
|
209
|
+
lookup_accounts = @intFromEnum(Operation.lookup_accounts),
|
|
210
|
+
lookup_transfers = @intFromEnum(Operation.lookup_transfers),
|
|
211
|
+
get_account_transfers = @intFromEnum(Operation.get_account_transfers),
|
|
212
|
+
get_account_balances = @intFromEnum(Operation.get_account_balances),
|
|
213
|
+
query_accounts = @intFromEnum(Operation.query_accounts),
|
|
214
|
+
query_transfers = @intFromEnum(Operation.query_transfers),
|
|
215
|
+
|
|
216
|
+
comptime {
|
|
217
|
+
const operation_type_info = @typeInfo(Operation).@"enum";
|
|
218
|
+
const echo_type_info = @typeInfo(EchoOperation).@"enum";
|
|
219
|
+
assert(echo_type_info.tag_type == operation_type_info.tag_type);
|
|
220
|
+
assert(echo_type_info.is_exhaustive);
|
|
221
|
+
assert(echo_type_info.fields.len <= operation_type_info.fields.len);
|
|
222
|
+
for (echo_type_info.fields) |field| {
|
|
223
|
+
assert(@hasField(Operation, field.name));
|
|
224
|
+
|
|
225
|
+
const a = @field(Operation, field.name);
|
|
226
|
+
const b = @field(EchoOperation, field.name);
|
|
227
|
+
assert(@intFromEnum(a) == @intFromEnum(b));
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
inline fn cast(operation: EchoOperation) Operation {
|
|
232
|
+
return @enumFromInt(@intFromEnum(operation));
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
pub fn EventType(comptime operation: EchoOperation) type {
|
|
236
|
+
return operation.cast().EventType();
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
pub inline fn event_size(operation: EchoOperation) u32 {
|
|
240
|
+
return operation.cast().event_size();
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
pub inline fn is_batchable(operation: EchoOperation) bool {
|
|
244
|
+
return operation.cast().is_batchable();
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
pub inline fn is_multi_batch(operation: EchoOperation) bool {
|
|
248
|
+
return operation.cast().is_multi_batch();
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
pub inline fn event_max(operation: EchoOperation, batch_size_limit: u32) u32 {
|
|
252
|
+
return operation.cast().event_max(batch_size_limit);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
pub inline fn result_count_expected(
|
|
256
|
+
operation: EchoOperation,
|
|
257
|
+
batch: []const u8,
|
|
258
|
+
) u32 {
|
|
259
|
+
return operation.cast().result_count_expected(batch);
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
pub fn from_vsr(operation: vsr.Operation) ?EchoOperation {
|
|
263
|
+
if (operation == .pulse) return .pulse;
|
|
264
|
+
if (operation.vsr_reserved()) return null;
|
|
265
|
+
|
|
266
|
+
return vsr.Operation.to(EchoOperation, operation);
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
pub fn to_vsr(operation: EchoOperation) vsr.Operation {
|
|
270
|
+
return vsr.Operation.from(EchoOperation, operation);
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// Re-exporting functions where results are equal to events.
|
|
274
|
+
|
|
275
|
+
pub fn ResultType(comptime operation: EchoOperation) type {
|
|
276
|
+
return operation.EventType();
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
pub inline fn result_size(operation: EchoOperation) u32 {
|
|
280
|
+
return operation.event_size();
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
pub inline fn result_max(operation: EchoOperation, batch_size_limit: u32) u32 {
|
|
284
|
+
return operation.event_max(batch_size_limit);
|
|
285
|
+
}
|
|
286
|
+
};
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
const std = @import("std");
|
|
2
|
+
const assert = std.debug.assert;
|
|
3
|
+
|
|
4
|
+
const tb_client = @import("../tb_client.zig");
|
|
5
|
+
const stdx = tb_client.vsr.stdx;
|
|
6
|
+
const maybe = stdx.maybe;
|
|
7
|
+
|
|
8
|
+
const QueueType = tb_client.vsr.queue.QueueType;
|
|
9
|
+
|
|
10
|
+
pub const Packet = extern struct {
|
|
11
|
+
pub const Status = enum(u8) {
|
|
12
|
+
ok,
|
|
13
|
+
too_much_data,
|
|
14
|
+
client_evicted,
|
|
15
|
+
client_release_too_low,
|
|
16
|
+
client_release_too_high,
|
|
17
|
+
client_shutdown,
|
|
18
|
+
invalid_operation,
|
|
19
|
+
invalid_data_size,
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
/// External packet type exposed to the user.
|
|
23
|
+
pub const Extern = extern struct {
|
|
24
|
+
user_data: ?*anyopaque,
|
|
25
|
+
data: ?*anyopaque,
|
|
26
|
+
data_size: u32,
|
|
27
|
+
user_tag: u16,
|
|
28
|
+
operation: u8,
|
|
29
|
+
status: Status,
|
|
30
|
+
@"opaque": [64]u8 = @splat(0),
|
|
31
|
+
|
|
32
|
+
pub fn cast(self: *Extern) *Packet {
|
|
33
|
+
return @ptrCast(self);
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
const Phase = enum(u8) {
|
|
38
|
+
submitted,
|
|
39
|
+
pending,
|
|
40
|
+
batched,
|
|
41
|
+
sent,
|
|
42
|
+
complete,
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
pub const Queue = QueueType(Packet);
|
|
46
|
+
|
|
47
|
+
user_data: ?*anyopaque,
|
|
48
|
+
data: ?*anyopaque,
|
|
49
|
+
data_size: u32,
|
|
50
|
+
user_tag: u16,
|
|
51
|
+
operation: u8,
|
|
52
|
+
status: Status,
|
|
53
|
+
|
|
54
|
+
link: Queue.Link,
|
|
55
|
+
|
|
56
|
+
multi_batch_time_monotonic: u64,
|
|
57
|
+
multi_batch_next: ?*Packet,
|
|
58
|
+
multi_batch_tail: ?*Packet,
|
|
59
|
+
multi_batch_count: u16,
|
|
60
|
+
multi_batch_event_count: u16,
|
|
61
|
+
multi_batch_result_count_expected: u16,
|
|
62
|
+
phase: Phase,
|
|
63
|
+
reserved: [25]u8 = @splat(0),
|
|
64
|
+
|
|
65
|
+
pub fn cast(self: *Packet) *Extern {
|
|
66
|
+
return @ptrCast(self);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
pub fn slice(packet: *const Packet) []const u8 {
|
|
70
|
+
if (packet.data_size == 0) {
|
|
71
|
+
// It may be an empty array (null pointer)
|
|
72
|
+
// or a buffer with no elements (valid pointer and size == 0).
|
|
73
|
+
stdx.maybe(packet.data == null);
|
|
74
|
+
return &[0]u8{};
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const data: [*]const u8 = @ptrCast(packet.data.?);
|
|
78
|
+
return data[0..packet.data_size];
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/// Asserts the internal state of the packet according to its expected phase.
|
|
82
|
+
/// Inline function, so `expected` can be comptime known.
|
|
83
|
+
pub inline fn assert_phase(packet: *const Packet, expected: Phase) void {
|
|
84
|
+
assert(packet.phase == expected);
|
|
85
|
+
assert(packet.data_size == 0 or packet.data != null);
|
|
86
|
+
assert(stdx.zeroed(&packet.reserved));
|
|
87
|
+
maybe(packet.user_data == null);
|
|
88
|
+
maybe(packet.user_tag == 0);
|
|
89
|
+
|
|
90
|
+
switch (expected) {
|
|
91
|
+
.submitted => {
|
|
92
|
+
assert(packet.link.next == null);
|
|
93
|
+
assert(packet.multi_batch_next == null);
|
|
94
|
+
assert(packet.multi_batch_tail == null);
|
|
95
|
+
assert(packet.multi_batch_count == 0);
|
|
96
|
+
assert(packet.multi_batch_event_count == 0);
|
|
97
|
+
assert(packet.multi_batch_result_count_expected == 0);
|
|
98
|
+
assert(packet.multi_batch_time_monotonic == 0);
|
|
99
|
+
},
|
|
100
|
+
.pending => {
|
|
101
|
+
assert(packet.multi_batch_count >= 1);
|
|
102
|
+
assert(packet.multi_batch_next == null or packet.multi_batch_count > 1);
|
|
103
|
+
assert((packet.multi_batch_next == null) == (packet.multi_batch_tail == null));
|
|
104
|
+
maybe(packet.data_size == 0);
|
|
105
|
+
maybe(packet.multi_batch_event_count == 0);
|
|
106
|
+
maybe(packet.multi_batch_result_count_expected == 0);
|
|
107
|
+
maybe(packet.link.next == null);
|
|
108
|
+
assert(packet.multi_batch_time_monotonic != 0);
|
|
109
|
+
},
|
|
110
|
+
.batched => {
|
|
111
|
+
assert(packet.link.next == null);
|
|
112
|
+
assert(packet.multi_batch_tail == null);
|
|
113
|
+
assert(packet.multi_batch_count == 0);
|
|
114
|
+
assert(packet.multi_batch_event_count == 0);
|
|
115
|
+
assert(packet.multi_batch_result_count_expected == 0);
|
|
116
|
+
maybe(packet.multi_batch_next != null);
|
|
117
|
+
assert(packet.multi_batch_time_monotonic == 0);
|
|
118
|
+
},
|
|
119
|
+
.sent => {
|
|
120
|
+
assert(packet.link.next == null);
|
|
121
|
+
assert(packet.multi_batch_count > 0);
|
|
122
|
+
assert(packet.multi_batch_next == null or packet.multi_batch_count > 1);
|
|
123
|
+
assert((packet.multi_batch_next == null) == (packet.multi_batch_tail == null));
|
|
124
|
+
maybe(packet.multi_batch_event_count == 0);
|
|
125
|
+
maybe(packet.multi_batch_result_count_expected == 0);
|
|
126
|
+
assert(packet.multi_batch_time_monotonic != 0);
|
|
127
|
+
},
|
|
128
|
+
.complete => {
|
|
129
|
+
// The packet pointer isn't available after completed,
|
|
130
|
+
// it may be deallocated by the user;
|
|
131
|
+
unreachable;
|
|
132
|
+
},
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
comptime {
|
|
137
|
+
assert(@sizeOf(Extern) % @alignOf(Extern) == 0);
|
|
138
|
+
assert(@alignOf(Extern) == 8);
|
|
139
|
+
|
|
140
|
+
assert(@sizeOf(Packet) == @sizeOf(Extern));
|
|
141
|
+
assert(@alignOf(Packet) == @alignOf(Extern));
|
|
142
|
+
|
|
143
|
+
// Asserting the fields are identical.
|
|
144
|
+
for (std.meta.fields(Extern)) |field_extern| {
|
|
145
|
+
if (std.mem.eql(u8, field_extern.name, "opaque")) continue;
|
|
146
|
+
const field_packet = std.meta.fields(Packet)[
|
|
147
|
+
std.meta.fieldIndex(
|
|
148
|
+
Packet,
|
|
149
|
+
field_extern.name,
|
|
150
|
+
).?
|
|
151
|
+
];
|
|
152
|
+
assert(field_packet.type == field_extern.type);
|
|
153
|
+
assert(field_packet.alignment == field_extern.alignment);
|
|
154
|
+
assert(@offsetOf(Packet, field_extern.name) ==
|
|
155
|
+
@offsetOf(Extern, field_extern.name));
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
};
|
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
const std = @import("std");
|
|
2
|
+
const assert = std.debug.assert;
|
|
3
|
+
|
|
4
|
+
const vsr = @import("../tb_client.zig").vsr;
|
|
5
|
+
const TimeOS = vsr.time.TimeOS;
|
|
6
|
+
const IO = vsr.io.IO;
|
|
7
|
+
|
|
8
|
+
const Atomic = std.atomic.Value;
|
|
9
|
+
|
|
10
|
+
/// A Signal is a way to trigger a registered callback on a IO instance when notification
|
|
11
|
+
/// occurs from another thread.
|
|
12
|
+
pub const Signal = struct {
|
|
13
|
+
io: *IO,
|
|
14
|
+
completion: IO.Completion,
|
|
15
|
+
event: IO.Event,
|
|
16
|
+
event_state: Atomic(enum(u8) {
|
|
17
|
+
running,
|
|
18
|
+
waiting,
|
|
19
|
+
notified,
|
|
20
|
+
shutdown,
|
|
21
|
+
}),
|
|
22
|
+
|
|
23
|
+
listening: Atomic(bool),
|
|
24
|
+
on_signal_fn: *const fn (*Signal) void,
|
|
25
|
+
|
|
26
|
+
pub fn init(self: *Signal, io: *IO, on_signal_fn: *const fn (*Signal) void) !void {
|
|
27
|
+
const event = try io.open_event();
|
|
28
|
+
errdefer io.close_event(event);
|
|
29
|
+
|
|
30
|
+
self.* = .{
|
|
31
|
+
.io = io,
|
|
32
|
+
.completion = undefined,
|
|
33
|
+
.event = event,
|
|
34
|
+
.event_state = @TypeOf(self.event_state).init(.running),
|
|
35
|
+
.listening = Atomic(bool).init(true),
|
|
36
|
+
.on_signal_fn = on_signal_fn,
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
self.wait();
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
pub fn deinit(self: *Signal) void {
|
|
43
|
+
assert(self.event != IO.INVALID_EVENT);
|
|
44
|
+
assert(self.status() == .stopped);
|
|
45
|
+
|
|
46
|
+
self.io.close_event(self.event);
|
|
47
|
+
self.* = undefined;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/// Requests to stop listening for notifications.
|
|
51
|
+
/// The caller must continue processing `IO.run()` until `state() == .stopped`.
|
|
52
|
+
/// Safe to call from multiple threads.
|
|
53
|
+
pub fn stop(self: *Signal) void {
|
|
54
|
+
const listening = self.listening.swap(false, .release);
|
|
55
|
+
if (listening) {
|
|
56
|
+
self.notify();
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/// Returns the current state.
|
|
61
|
+
/// Safe to call from multiple threads.
|
|
62
|
+
pub fn status(self: *const Signal) enum {
|
|
63
|
+
/// Listening for event notifications.
|
|
64
|
+
/// Call `notify()` to trigger the callback.
|
|
65
|
+
running,
|
|
66
|
+
/// `stop()` was called, but the event listener is still waiting for the IO operation
|
|
67
|
+
/// to complete. Further calls to `notify()` have no effect.
|
|
68
|
+
stop_requested,
|
|
69
|
+
/// No pending listening events. It is safe to call `deinit()`.
|
|
70
|
+
stopped,
|
|
71
|
+
} {
|
|
72
|
+
return switch (self.event_state.load(.acquire)) {
|
|
73
|
+
.shutdown => .stopped,
|
|
74
|
+
.running,
|
|
75
|
+
.waiting,
|
|
76
|
+
.notified,
|
|
77
|
+
=> if (self.listening.load(.acquire))
|
|
78
|
+
.running
|
|
79
|
+
else
|
|
80
|
+
.stop_requested,
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/// Schedules the `on_signal` callback to be invoked on the IO thread.
|
|
85
|
+
/// Calling `notify()` when `state() != .running` has no effect.
|
|
86
|
+
/// Safe to call from multiple threads.
|
|
87
|
+
pub fn notify(self: *Signal) void {
|
|
88
|
+
// Try to transition from `waiting` to `notified`.
|
|
89
|
+
// If it fails, analyze the current state to determine if a notification is needed.
|
|
90
|
+
var state: @TypeOf(self.event_state.raw) = .waiting;
|
|
91
|
+
while (self.event_state.cmpxchgStrong(
|
|
92
|
+
state,
|
|
93
|
+
.notified,
|
|
94
|
+
.release,
|
|
95
|
+
.acquire,
|
|
96
|
+
)) |state_actual| {
|
|
97
|
+
switch (state_actual) {
|
|
98
|
+
.waiting, .running => state = state_actual, // Try again.
|
|
99
|
+
.notified => return, // Already notified.
|
|
100
|
+
.shutdown => return, // Ignore notifications after shutdown.
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (state == .waiting) {
|
|
105
|
+
self.io.event_trigger(self.event, &self.completion);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
fn wait(self: *Signal) void {
|
|
110
|
+
// It is not guaranteed to be `running` here, as another caller might have requested
|
|
111
|
+
// a stop during the callback.
|
|
112
|
+
assert(self.status() != .stopped);
|
|
113
|
+
|
|
114
|
+
const state = self.event_state.swap(.waiting, .acquire);
|
|
115
|
+
self.io.event_listen(self.event, &self.completion, on_event);
|
|
116
|
+
switch (state) {
|
|
117
|
+
// We should be the only ones who could've started waiting.
|
|
118
|
+
.waiting => unreachable,
|
|
119
|
+
// Wait for a `notify`.
|
|
120
|
+
.running => {},
|
|
121
|
+
// A `notify` was already called in the meantime,
|
|
122
|
+
// calling it again asynchronously.
|
|
123
|
+
.notified => self.notify(),
|
|
124
|
+
// Cannot be called after shutdown.
|
|
125
|
+
.shutdown => unreachable,
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
fn on_event(completion: *IO.Completion) void {
|
|
130
|
+
const self: *Signal = @fieldParentPtr("completion", completion);
|
|
131
|
+
const listening: bool = self.listening.load(.acquire);
|
|
132
|
+
const state = self.event_state.cmpxchgStrong(
|
|
133
|
+
.notified,
|
|
134
|
+
if (listening) .running else .shutdown,
|
|
135
|
+
.release,
|
|
136
|
+
.acquire,
|
|
137
|
+
) orelse {
|
|
138
|
+
if (listening) {
|
|
139
|
+
(self.on_signal_fn)(self);
|
|
140
|
+
self.wait();
|
|
141
|
+
}
|
|
142
|
+
return;
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
switch (state) {
|
|
146
|
+
.running => unreachable, // Multiple racing calls to on_signal().
|
|
147
|
+
.waiting => unreachable, // on_signal() called without transitioning to a waking state.
|
|
148
|
+
.notified => unreachable, // Not possible due to CAS semantics.
|
|
149
|
+
.shutdown => unreachable, // Shutdown is a final state.
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
test "signal" {
|
|
155
|
+
try struct {
|
|
156
|
+
const Context = @This();
|
|
157
|
+
|
|
158
|
+
io: IO,
|
|
159
|
+
count: u32 = 0,
|
|
160
|
+
main_thread_id: std.Thread.Id,
|
|
161
|
+
signal: Signal,
|
|
162
|
+
|
|
163
|
+
const delay = 5 * std.time.ns_per_ms;
|
|
164
|
+
const events_count = 5;
|
|
165
|
+
|
|
166
|
+
fn run_test() !void {
|
|
167
|
+
var self: Context = .{
|
|
168
|
+
.io = try IO.init(32, 0),
|
|
169
|
+
.main_thread_id = std.Thread.getCurrentId(),
|
|
170
|
+
.signal = undefined,
|
|
171
|
+
};
|
|
172
|
+
defer self.io.deinit();
|
|
173
|
+
|
|
174
|
+
try Signal.init(&self.signal, &self.io, on_signal);
|
|
175
|
+
defer self.signal.deinit();
|
|
176
|
+
|
|
177
|
+
var time_os = TimeOS{};
|
|
178
|
+
const timer = time_os.time();
|
|
179
|
+
const start = timer.monotonic();
|
|
180
|
+
|
|
181
|
+
const thread = try std.Thread.spawn(.{}, Context.notify, .{&self});
|
|
182
|
+
|
|
183
|
+
// Wait for the number of events to complete.
|
|
184
|
+
while (self.count < events_count) try self.io.run();
|
|
185
|
+
|
|
186
|
+
// Begin shutdown and keep ticking until it's completed.
|
|
187
|
+
self.signal.stop();
|
|
188
|
+
while (self.signal.status() != .stopped) try self.io.run();
|
|
189
|
+
thread.join();
|
|
190
|
+
|
|
191
|
+
// Notify after shutdown should be ignored.
|
|
192
|
+
self.signal.notify();
|
|
193
|
+
|
|
194
|
+
// Make sure the event was triggered multiple times.
|
|
195
|
+
assert(self.count == events_count);
|
|
196
|
+
|
|
197
|
+
// Make sure at least some time has passed.
|
|
198
|
+
const elapsed = timer.monotonic().duration_since(start);
|
|
199
|
+
assert(elapsed.ns >= delay);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
fn notify(self: *Context) void {
|
|
203
|
+
assert(std.Thread.getCurrentId() != self.main_thread_id);
|
|
204
|
+
while (self.signal.status() != .stopped) {
|
|
205
|
+
std.time.sleep(delay + 1);
|
|
206
|
+
|
|
207
|
+
// Triggering the event:
|
|
208
|
+
self.signal.notify();
|
|
209
|
+
|
|
210
|
+
// The same signal may be triggered multiple times,
|
|
211
|
+
// but it should only fire once.
|
|
212
|
+
self.signal.notify();
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
fn on_signal(signal: *Signal) void {
|
|
217
|
+
const self: *Context = @fieldParentPtr("signal", signal);
|
|
218
|
+
assert(std.Thread.getCurrentId() == self.main_thread_id);
|
|
219
|
+
switch (self.signal.status()) {
|
|
220
|
+
.running => {
|
|
221
|
+
assert(self.count < events_count);
|
|
222
|
+
self.count += 1;
|
|
223
|
+
},
|
|
224
|
+
.stop_requested => assert(self.count == events_count),
|
|
225
|
+
.stopped => unreachable,
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
}.run_test();
|
|
229
|
+
}
|