farce 0.0.1.alpha2-x86-linux-musl
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 +7 -0
- data/CODE_OF_CONDUCT.md +26 -0
- data/CONTRIBUTING.md +71 -0
- data/MIT-LICENSE +20 -0
- data/README.md +1524 -0
- data/SECURITY.md +10 -0
- data/docs/benchmarks.md +185 -0
- data/docs/gems/dry-types.md +290 -0
- data/docs/gems/msgpack.md +70 -0
- data/docs/gems/ractor-shim.md +63 -0
- data/docs/modes.md +628 -0
- data/docs/scopes.md +649 -0
- data/docs/variants.md +346 -0
- data/ext/ext_helper.rb +20 -0
- data/ext/farce/README.md +21 -0
- data/ext/farce/atom.c +1023 -0
- data/ext/farce/bounded_map.c +1683 -0
- data/ext/farce/containers.h +77 -0
- data/ext/farce/counter.c +399 -0
- data/ext/farce/darwin.c +100 -0
- data/ext/farce/depend +12 -0
- data/ext/farce/dict.c +1523 -0
- data/ext/farce/dict.h +152 -0
- data/ext/farce/drivers.c +237 -0
- data/ext/farce/exchanger.c +299 -0
- data/ext/farce/extconf.rb +99 -0
- data/ext/farce/farce.c +359 -0
- data/ext/farce/flag.c +288 -0
- data/ext/farce/io.c +338 -0
- data/ext/farce/lock.c +510 -0
- data/ext/farce/map.c +2240 -0
- data/ext/farce/priority_queue.c +2056 -0
- data/ext/farce/queue.c +1059 -0
- data/ext/farce/reactor.c +820 -0
- data/ext/farce/reactor.h +103 -0
- data/ext/farce/shareable.h +31 -0
- data/ext/farce/signal.c +350 -0
- data/ext/farce/transaction.c +354 -0
- data/ext/farce/transaction.h +40 -0
- data/ext/farce/tree_map.c +1953 -0
- data/ext/farce/trie.c +2020 -0
- data/ext/farce/unshareable.c +155 -0
- data/ext/farce/unshared_io_pool.h +234 -0
- data/ext/farce/unshared_signal.c +263 -0
- data/ext/farce/unshared_wait.h +193 -0
- data/ext/farce/unsupported.c +6 -0
- data/ext/farce/vector.c +1195 -0
- data/ext/farce/weak_map.c +1714 -0
- data/ext/java/org/farce/BoundedMap.java +394 -0
- data/ext/java/org/farce/FiberScheduler.java +141 -0
- data/ext/java/org/farce/PriorityKey.java +45 -0
- data/ext/java/org/farce/PriorityQueue.java +373 -0
- data/ext/java/org/farce/QueueSignal.java +28 -0
- data/ext/rebind/README.md +14 -0
- data/ext/rebind/extconf.rb +10 -0
- data/ext/rebind/rebind.c +186 -0
- data/lib/farce/_yard/internal.rb +13 -0
- data/lib/farce/_yard/macros.rb +61 -0
- data/lib/farce/_yard/ractor.rb +46 -0
- data/lib/farce/abstract/atom.rb +186 -0
- data/lib/farce/abstract/bounded_map.rb +232 -0
- data/lib/farce/abstract/collection.rb +151 -0
- data/lib/farce/abstract/concurrent_map.rb +381 -0
- data/lib/farce/abstract/counter.rb +193 -0
- data/lib/farce/abstract/duplicable_map.rb +229 -0
- data/lib/farce/abstract/exchanger.rb +26 -0
- data/lib/farce/abstract/flag.rb +71 -0
- data/lib/farce/abstract/lazy.rb +115 -0
- data/lib/farce/abstract/lease.rb +104 -0
- data/lib/farce/abstract/lease_map.rb +261 -0
- data/lib/farce/abstract/lease_pool.rb +91 -0
- data/lib/farce/abstract/lfu_map.rb +20 -0
- data/lib/farce/abstract/lru_map.rb +25 -0
- data/lib/farce/abstract/map.rb +345 -0
- data/lib/farce/abstract/molecule.rb +245 -0
- data/lib/farce/abstract/port.rb +74 -0
- data/lib/farce/abstract/priority_queue.rb +117 -0
- data/lib/farce/abstract/queue.rb +269 -0
- data/lib/farce/abstract/scheduler.rb +111 -0
- data/lib/farce/abstract/set.rb +910 -0
- data/lib/farce/abstract/sorted_set.rb +172 -0
- data/lib/farce/abstract/timer_queue.rb +136 -0
- data/lib/farce/abstract/tree_map.rb +269 -0
- data/lib/farce/abstract/value.rb +68 -0
- data/lib/farce/abstract/vector.rb +1126 -0
- data/lib/farce/abstract/weak_atom.rb +32 -0
- data/lib/farce/abstract/weak_key_map.rb +12 -0
- data/lib/farce/abstract/weak_map.rb +13 -0
- data/lib/farce/abstract/weak_set.rb +12 -0
- data/lib/farce/abstract/weak_value_map.rb +12 -0
- data/lib/farce/abstract.rb +17 -0
- data/lib/farce/atom.rb +293 -0
- data/lib/farce/class_mirror.rb +87 -0
- data/lib/farce/clock.rb +121 -0
- data/lib/farce/config.rb +229 -0
- data/lib/farce/counter.rb +71 -0
- data/lib/farce/deduper.rb +122 -0
- data/lib/farce/engine/jruby/bounded_map.rb +314 -0
- data/lib/farce/engine/jruby/fiber_scheduler.jar +0 -0
- data/lib/farce/engine/jruby/fiber_scheduler.rb +119 -0
- data/lib/farce/engine/jruby/lease_waiting.rb +19 -0
- data/lib/farce/engine/jruby/map.rb +505 -0
- data/lib/farce/engine/jruby/mutable_numeric_copy.rb +42 -0
- data/lib/farce/engine/jruby/signal.rb +147 -0
- data/lib/farce/engine/jruby.rb +63 -0
- data/lib/farce/engine/jvm/concurrent_weak_registry.rb +55 -0
- data/lib/farce/engine/jvm/counter.rb +102 -0
- data/lib/farce/engine/jvm/extension.rb +32 -0
- data/lib/farce/engine/jvm/farce.jar +0 -0
- data/lib/farce/engine/jvm/flag.rb +79 -0
- data/lib/farce/engine/jvm/priority_queue.rb +217 -0
- data/lib/farce/engine/jvm/tree_map.rb +350 -0
- data/lib/farce/engine/jvm/types.rb +180 -0
- data/lib/farce/engine/jvm.rb +19 -0
- data/lib/farce/engine/ruby/3.4/farce.so +0 -0
- data/lib/farce/engine/ruby/3.4/fiber_scheduler.rb +20 -0
- data/lib/farce/engine/ruby/3.4/port.rb +186 -0
- data/lib/farce/engine/ruby/3.4/ractor_methods.rb +26 -0
- data/lib/farce/engine/ruby/3.4/ractor_selector.rb +92 -0
- data/lib/farce/engine/ruby/3.4/rebind.so +0 -0
- data/lib/farce/engine/ruby/3.4/vault.rb +56 -0
- data/lib/farce/engine/ruby/4.0/farce.so +0 -0
- data/lib/farce/engine/ruby/4.0/port.rb +19 -0
- data/lib/farce/engine/ruby/4.0/ractor_methods.rb +20 -0
- data/lib/farce/engine/ruby/4.0/ractor_selector.rb +108 -0
- data/lib/farce/engine/ruby/4.0/rebind.so +0 -0
- data/lib/farce/engine/ruby/4.0/vault.rb +74 -0
- data/lib/farce/engine/ruby/4.1/port.rb +21 -0
- data/lib/farce/engine/ruby/4.1/ractor_methods.rb +22 -0
- data/lib/farce/engine/ruby/4.1/ractor_selector.rb +25 -0
- data/lib/farce/engine/ruby/4.1/vault.rb +5 -0
- data/lib/farce/engine/ruby/fiber_scheduler.rb +32 -0
- data/lib/farce/engine/ruby/key_lock_map.rb +28 -0
- data/lib/farce/engine/ruby/shared/lease.rb +26 -0
- data/lib/farce/engine/ruby/shared/lease_pool.rb +24 -0
- data/lib/farce/engine/ruby/shared/main_scheduler.rb +9 -0
- data/lib/farce/engine/ruby/shared/parallel_scheduler.rb +9 -0
- data/lib/farce/engine/ruby/shared/proxy_owner.rb +31 -0
- data/lib/farce/engine/ruby/shared/ractor_methods.rb +34 -0
- data/lib/farce/engine/ruby/shared/ractor_selector.rb +328 -0
- data/lib/farce/engine/ruby/shared/strict_map.rb +13 -0
- data/lib/farce/engine/ruby/shared/unshared_vector.rb +14 -0
- data/lib/farce/engine/ruby/shared/vault.rb +144 -0
- data/lib/farce/engine/ruby/shared/vault_weak_map.rb +336 -0
- data/lib/farce/engine/ruby/shared/weak_atom.rb +54 -0
- data/lib/farce/engine/ruby/shared/weak_map.rb +413 -0
- data/lib/farce/engine/ruby.rb +130 -0
- data/lib/farce/engine/shared/atom.rb +10 -0
- data/lib/farce/engine/shared/exchanger.rb +130 -0
- data/lib/farce/engine/shared/identity_key.rb +22 -0
- data/lib/farce/engine/shared/lease.rb +10 -0
- data/lib/farce/engine/shared/lease_pool.rb +10 -0
- data/lib/farce/engine/shared/main_scheduler.rb +20 -0
- data/lib/farce/engine/shared/map_key_coordination.rb +226 -0
- data/lib/farce/engine/shared/parallel_scheduler.rb +9 -0
- data/lib/farce/engine/shared/port.rb +44 -0
- data/lib/farce/engine/shared/portable_bounded_map.rb +619 -0
- data/lib/farce/engine/shared/proxy_owner.rb +43 -0
- data/lib/farce/engine/shared/queue.rb +233 -0
- data/lib/farce/engine/shared/ractor_methods.rb +73 -0
- data/lib/farce/engine/shared/rebindable.rb +15 -0
- data/lib/farce/engine/shared/strict_atom.rb +73 -0
- data/lib/farce/engine/shared/strict_map.rb +117 -0
- data/lib/farce/engine/shared/strict_queue_values.rb +27 -0
- data/lib/farce/engine/shared/strict_tree_map.rb +56 -0
- data/lib/farce/engine/shared/transaction_map_backend.rb +20 -0
- data/lib/farce/engine/shared/trie.rb +317 -0
- data/lib/farce/engine/shared/trie_builder.rb +199 -0
- data/lib/farce/engine/shared/unshareable.rb +14 -0
- data/lib/farce/engine/shared/unshared_atom.rb +256 -0
- data/lib/farce/engine/shared/unshared_priority_queue.rb +9 -0
- data/lib/farce/engine/shared/unshared_queue.rb +18 -0
- data/lib/farce/engine/shared/unshared_signal.rb +21 -0
- data/lib/farce/engine/shared/unshared_vector.rb +377 -0
- data/lib/farce/engine/shared/unshared_weak_atom.rb +30 -0
- data/lib/farce/engine/shared/unshared_weak_map.rb +445 -0
- data/lib/farce/engine/shared/vault.rb +31 -0
- data/lib/farce/engine/shared/vector.rb +83 -0
- data/lib/farce/engine/shared/weak_atom/base.rb +189 -0
- data/lib/farce/engine/shared/weak_atom.rb +20 -0
- data/lib/farce/engine/shared/weak_map/cell.rb +99 -0
- data/lib/farce/engine/shared/weak_map/index.rb +249 -0
- data/lib/farce/engine/shared/weak_map/lock.rb +80 -0
- data/lib/farce/engine/shared/weak_map/reference.rb +33 -0
- data/lib/farce/engine/shared.rb +63 -0
- data/lib/farce/engine/truffleruby/fiber_scheduler.rb +13 -0
- data/lib/farce/engine/truffleruby/lock.rb +121 -0
- data/lib/farce/engine/truffleruby/map.rb +656 -0
- data/lib/farce/engine/truffleruby/native/counter.rb +105 -0
- data/lib/farce/engine/truffleruby/native/flag.rb +77 -0
- data/lib/farce/engine/truffleruby/native/ordered_array_support.rb +67 -0
- data/lib/farce/engine/truffleruby/native/priority_queue.rb +388 -0
- data/lib/farce/engine/truffleruby/native/tree_map.rb +51 -0
- data/lib/farce/engine/truffleruby/native/unsafe_tree_map.rb +350 -0
- data/lib/farce/engine/truffleruby/signal.rb +77 -0
- data/lib/farce/engine/truffleruby.rb +148 -0
- data/lib/farce/envelope.rb +335 -0
- data/lib/farce/error.rb +35 -0
- data/lib/farce/exchanger.rb +41 -0
- data/lib/farce/flag.rb +27 -0
- data/lib/farce/integrations/active_support/blank.rb +68 -0
- data/lib/farce/integrations/active_support/clock.rb +17 -0
- data/lib/farce/integrations/active_support/duplicable.rb +78 -0
- data/lib/farce/integrations/active_support/map.rb +175 -0
- data/lib/farce/integrations/active_support/set.rb +60 -0
- data/lib/farce/integrations/active_support/value_serialization.rb +17 -0
- data/lib/farce/integrations/active_support/vector.rb +231 -0
- data/lib/farce/integrations/active_support.rb +9 -0
- data/lib/farce/integrations/activesupport.rb +5 -0
- data/lib/farce/integrations/bson.rb +108 -0
- data/lib/farce/integrations/cbor.rb +51 -0
- data/lib/farce/integrations/concurrent.rb +112 -0
- data/lib/farce/integrations/dry-types.rb +5 -0
- data/lib/farce/integrations/dry_types.rb +694 -0
- data/lib/farce/integrations/json.rb +7 -0
- data/lib/farce/integrations/msgpack.rb +112 -0
- data/lib/farce/integrations/oj.rb +69 -0
- data/lib/farce/integrations/psych.rb +191 -0
- data/lib/farce/integrations/ractor-sharing.rb +5 -0
- data/lib/farce/integrations/ractor-tmvar.rb +5 -0
- data/lib/farce/integrations/ractor_sharing.rb +172 -0
- data/lib/farce/integrations/ractor_tmvar.rb +64 -0
- data/lib/farce/integrations/shared/to_json.rb +42 -0
- data/lib/farce/integrations/sorted_set.rb +11 -0
- data/lib/farce/integrations/weakref.rb +38 -0
- data/lib/farce/integrations/yajl.rb +10 -0
- data/lib/farce/integrations.rb +156 -0
- data/lib/farce/internal/_frozen_config.rb +11 -0
- data/lib/farce/internal/autoloads.rb +47 -0
- data/lib/farce/internal/blocking_priority_queue.rb +122 -0
- data/lib/farce/internal/converter.rb +106 -0
- data/lib/farce/internal/copyable.rb +31 -0
- data/lib/farce/internal/delegation.rb +20 -0
- data/lib/farce/internal/external_transaction.rb +59 -0
- data/lib/farce/internal/fake_ractor.rb +179 -0
- data/lib/farce/internal/freeze.rb +118 -0
- data/lib/farce/internal/inspect.rb +157 -0
- data/lib/farce/internal/key_lock_map.rb +29 -0
- data/lib/farce/internal/key_normalizer.rb +289 -0
- data/lib/farce/internal/lease_initialization.rb +115 -0
- data/lib/farce/internal/lease_map.rb +441 -0
- data/lib/farce/internal/lease_pool_state.rb +228 -0
- data/lib/farce/internal/lease_state.rb +342 -0
- data/lib/farce/internal/lease_waiting.rb +13 -0
- data/lib/farce/internal/managed_queue.rb +26 -0
- data/lib/farce/internal/map_value_modes.rb +227 -0
- data/lib/farce/internal/marshal_support.rb +227 -0
- data/lib/farce/internal/mixin.rb +20 -0
- data/lib/farce/internal/mutable_ordered_key_lock_map.rb +63 -0
- data/lib/farce/internal/noncopyable.rb +17 -0
- data/lib/farce/internal/ordered_key_lock_map.rb +69 -0
- data/lib/farce/internal/pool_supervisor.rb +41 -0
- data/lib/farce/internal/pool_worker.rb +61 -0
- data/lib/farce/internal/portable_transaction/reservation_entry.rb +29 -0
- data/lib/farce/internal/portable_transaction/strong_map_entry.rb +137 -0
- data/lib/farce/internal/portable_transaction/strong_map_size_entry.rb +44 -0
- data/lib/farce/internal/portable_transaction/tree_entry.rb +64 -0
- data/lib/farce/internal/portable_transaction.rb +164 -0
- data/lib/farce/internal/proxy_owner_notifications.rb +29 -0
- data/lib/farce/internal/reservation_waiting.rb +24 -0
- data/lib/farce/internal/scheduled_task.rb +48 -0
- data/lib/farce/internal/scheduler_io.rb +171 -0
- data/lib/farce/internal/scheduler_lifecycle.rb +314 -0
- data/lib/farce/internal/select_scheduler.rb +235 -0
- data/lib/farce/internal/storage.rb +162 -0
- data/lib/farce/internal/strict_lease.rb +30 -0
- data/lib/farce/internal/strict_lease_map.rb +20 -0
- data/lib/farce/internal/strict_lease_pool.rb +29 -0
- data/lib/farce/internal/thread_pool.rb +166 -0
- data/lib/farce/internal/transaction_conflict.rb +9 -0
- data/lib/farce/internal/transaction_freeze_guard.rb +30 -0
- data/lib/farce/internal/transaction_map_snapshot.rb +123 -0
- data/lib/farce/internal/undefined.rb +22 -0
- data/lib/farce/internal/unshared_lease.rb +29 -0
- data/lib/farce/internal/unshared_lease_pool.rb +34 -0
- data/lib/farce/internal/unshared_queue_waiting.rb +28 -0
- data/lib/farce/internal/value_serialization.rb +13 -0
- data/lib/farce/internal/weak_map_value_modes.rb +58 -0
- data/lib/farce/internal/weak_mode_manager.rb +26 -0
- data/lib/farce/internal.rb +129 -0
- data/lib/farce/lazy.rb +100 -0
- data/lib/farce/lazy_ref.rb +40 -0
- data/lib/farce/lease.rb +28 -0
- data/lib/farce/lease_map.rb +34 -0
- data/lib/farce/lease_pool.rb +29 -0
- data/lib/farce/lfu_map.rb +53 -0
- data/lib/farce/local/atom.rb +23 -0
- data/lib/farce/local/counter.rb +88 -0
- data/lib/farce/local/flag.rb +65 -0
- data/lib/farce/local/lazy.rb +38 -0
- data/lib/farce/local/lazy_ref.rb +36 -0
- data/lib/farce/local/lease.rb +77 -0
- data/lib/farce/local/lease_map.rb +80 -0
- data/lib/farce/local/lease_pool.rb +38 -0
- data/lib/farce/local/lfu_map.rb +55 -0
- data/lib/farce/local/lru_map.rb +62 -0
- data/lib/farce/local/map.rb +47 -0
- data/lib/farce/local/molecule.rb +45 -0
- data/lib/farce/local/priority_queue.rb +53 -0
- data/lib/farce/local/queue.rb +32 -0
- data/lib/farce/local/scoped.rb +166 -0
- data/lib/farce/local/set.rb +18 -0
- data/lib/farce/local/sorted_set.rb +50 -0
- data/lib/farce/local/timer_queue.rb +41 -0
- data/lib/farce/local/tree_map.rb +47 -0
- data/lib/farce/local/vector.rb +30 -0
- data/lib/farce/local/weak_atom.rb +23 -0
- data/lib/farce/local/weak_key_map.rb +39 -0
- data/lib/farce/local/weak_map.rb +39 -0
- data/lib/farce/local/weak_set.rb +18 -0
- data/lib/farce/local/weak_value_map.rb +39 -0
- data/lib/farce/local.rb +35 -0
- data/lib/farce/lock.rb +54 -0
- data/lib/farce/lru_map.rb +63 -0
- data/lib/farce/map.rb +58 -0
- data/lib/farce/mode_manager.rb +142 -0
- data/lib/farce/molecule.rb +60 -0
- data/lib/farce/mutable.rb +171 -0
- data/lib/farce/pool.rb +332 -0
- data/lib/farce/port.rb +134 -0
- data/lib/farce/priority_queue.rb +70 -0
- data/lib/farce/proxy/register.rb +101 -0
- data/lib/farce/proxy/supervisor.rb +137 -0
- data/lib/farce/proxy/wrapper.rb +49 -0
- data/lib/farce/proxy.rb +291 -0
- data/lib/farce/queue.rb +60 -0
- data/lib/farce/ractor.rb +269 -0
- data/lib/farce/read_write_lock.rb +256 -0
- data/lib/farce/reference.rb +158 -0
- data/lib/farce/resolv/dns.rb +42 -0
- data/lib/farce/resolv.rb +85 -0
- data/lib/farce/scheduler.rb +536 -0
- data/lib/farce/set.rb +39 -0
- data/lib/farce/shareable.rb +150 -0
- data/lib/farce/signal.rb +97 -0
- data/lib/farce/sorted_set.rb +36 -0
- data/lib/farce/strict/atom.rb +34 -0
- data/lib/farce/strict/counter.rb +10 -0
- data/lib/farce/strict/exchanger.rb +25 -0
- data/lib/farce/strict/flag.rb +10 -0
- data/lib/farce/strict/lazy.rb +26 -0
- data/lib/farce/strict/lazy_ref.rb +26 -0
- data/lib/farce/strict/lease.rb +27 -0
- data/lib/farce/strict/lease_map.rb +33 -0
- data/lib/farce/strict/lease_pool.rb +28 -0
- data/lib/farce/strict/lfu_map.rb +27 -0
- data/lib/farce/strict/lru_map.rb +28 -0
- data/lib/farce/strict/map.rb +54 -0
- data/lib/farce/strict/molecule.rb +16 -0
- data/lib/farce/strict/port.rb +40 -0
- data/lib/farce/strict/priority_queue.rb +20 -0
- data/lib/farce/strict/queue.rb +17 -0
- data/lib/farce/strict/set.rb +14 -0
- data/lib/farce/strict/sorted_set.rb +19 -0
- data/lib/farce/strict/timer_queue.rb +13 -0
- data/lib/farce/strict/tree_map.rb +27 -0
- data/lib/farce/strict/vector.rb +24 -0
- data/lib/farce/strict/weak_atom.rb +31 -0
- data/lib/farce/strict/weak_key_map.rb +49 -0
- data/lib/farce/strict/weak_map.rb +49 -0
- data/lib/farce/strict/weak_set.rb +14 -0
- data/lib/farce/strict/weak_value_map.rb +48 -0
- data/lib/farce/strict.rb +28 -0
- data/lib/farce/system.rb +40 -0
- data/lib/farce/thread_scheduler.rb +70 -0
- data/lib/farce/timer_queue.rb +66 -0
- data/lib/farce/transaction/atom.rb +73 -0
- data/lib/farce/transaction/map.rb +25 -0
- data/lib/farce/transaction/map_operations.rb +143 -0
- data/lib/farce/transaction/molecule.rb +90 -0
- data/lib/farce/transaction/mutable.rb +61 -0
- data/lib/farce/transaction/set.rb +17 -0
- data/lib/farce/transaction/set_operations.rb +48 -0
- data/lib/farce/transaction/sorted_set.rb +18 -0
- data/lib/farce/transaction/tree_map.rb +78 -0
- data/lib/farce/transaction/vector.rb +139 -0
- data/lib/farce/transaction/wrapper.rb +202 -0
- data/lib/farce/transaction.rb +343 -0
- data/lib/farce/tree_map.rb +56 -0
- data/lib/farce/unsafe/lfu_map.rb +36 -0
- data/lib/farce/unsafe/lru_map.rb +36 -0
- data/lib/farce/unsafe/tree_map.rb +21 -0
- data/lib/farce/unsafe.rb +29 -0
- data/lib/farce/unshareable.rb +67 -0
- data/lib/farce/unshared/atom.rb +24 -0
- data/lib/farce/unshared/counter.rb +10 -0
- data/lib/farce/unshared/flag.rb +10 -0
- data/lib/farce/unshared/lazy.rb +38 -0
- data/lib/farce/unshared/lazy_ref.rb +26 -0
- data/lib/farce/unshared/lease.rb +27 -0
- data/lib/farce/unshared/lease_map.rb +26 -0
- data/lib/farce/unshared/lease_pool.rb +25 -0
- data/lib/farce/unshared/lfu_map.rb +20 -0
- data/lib/farce/unshared/lru_map.rb +20 -0
- data/lib/farce/unshared/map.rb +49 -0
- data/lib/farce/unshared/molecule.rb +16 -0
- data/lib/farce/unshared/priority_queue.rb +16 -0
- data/lib/farce/unshared/queue.rb +27 -0
- data/lib/farce/unshared/set.rb +14 -0
- data/lib/farce/unshared/sorted_set.rb +28 -0
- data/lib/farce/unshared/timer_queue.rb +16 -0
- data/lib/farce/unshared/tree_map.rb +21 -0
- data/lib/farce/unshared/vector.rb +19 -0
- data/lib/farce/unshared/weak_atom.rb +31 -0
- data/lib/farce/unshared/weak_key_map.rb +44 -0
- data/lib/farce/unshared/weak_map.rb +44 -0
- data/lib/farce/unshared/weak_set.rb +14 -0
- data/lib/farce/unshared/weak_value_map.rb +43 -0
- data/lib/farce/unshared.rb +28 -0
- data/lib/farce/vector.rb +228 -0
- data/lib/farce/version.rb +8 -0
- data/lib/farce/walker/definitions.rb +186 -0
- data/lib/farce/walker/modification.rb +355 -0
- data/lib/farce/walker.rb +319 -0
- data/lib/farce/weak_atom.rb +121 -0
- data/lib/farce/weak_key_map.rb +31 -0
- data/lib/farce/weak_map.rb +33 -0
- data/lib/farce/weak_ref.rb +67 -0
- data/lib/farce/weak_set.rb +82 -0
- data/lib/farce/weak_value.rb +195 -0
- data/lib/farce/weak_value_map.rb +32 -0
- data/lib/farce.rb +519 -0
- metadata +468 -0
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# shareable_constant_value: literal
|
|
3
|
+
# warn_indent: true
|
|
4
|
+
|
|
5
|
+
module Farce
|
|
6
|
+
# @!visibility private
|
|
7
|
+
module Internal # :nodoc: all
|
|
8
|
+
# Fallback for Ruby implementations supporting Ractor but not Ractor::Port (CRuby 3.x)
|
|
9
|
+
class Port
|
|
10
|
+
include MarshalSupport::Reject
|
|
11
|
+
include Freeze::Unfreezable
|
|
12
|
+
|
|
13
|
+
class QueueReader
|
|
14
|
+
include Freeze::Unfreezable
|
|
15
|
+
|
|
16
|
+
FROM_RACTOR = Object.new.freeze
|
|
17
|
+
|
|
18
|
+
def initialize
|
|
19
|
+
@queue = Queue.new(capacity: nil)
|
|
20
|
+
@atom = Atom.new
|
|
21
|
+
@selector = Atom.new
|
|
22
|
+
Freeze.publish(self)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def send(message, move: false)
|
|
26
|
+
return @queue.push(message) if ::Ractor.shareable?(message)
|
|
27
|
+
ractor = @atom.store_if_absent { ::Ractor.new { loop { ::Ractor.yield(::Ractor.receive) } } }
|
|
28
|
+
ractor.send(message, move:)
|
|
29
|
+
@queue.push(FROM_RACTOR)
|
|
30
|
+
rescue ClosedQueueError
|
|
31
|
+
raise ::Ractor::ClosedError, "The port was already closed"
|
|
32
|
+
ensure
|
|
33
|
+
wake_selector
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def closed? = @queue.closed?
|
|
37
|
+
|
|
38
|
+
def close
|
|
39
|
+
@queue.close
|
|
40
|
+
wake_selector
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Senders may be in another Ractor. Publish only the selectable control
|
|
44
|
+
# endpoint and shared wake flag, leaving requests and replies local.
|
|
45
|
+
def selector_watch(control, wakeup) = @selector.value = [control, wakeup].freeze
|
|
46
|
+
|
|
47
|
+
def selector_poll
|
|
48
|
+
# The empty-queue callback distinguishes no message from a nil payload.
|
|
49
|
+
result = @queue.try_pop { return }
|
|
50
|
+
result = @atom.value.take if FROM_RACTOR.equal?(result)
|
|
51
|
+
yield result
|
|
52
|
+
rescue ClosedQueueError
|
|
53
|
+
raise ::Ractor::ClosedError, "The port was already closed"
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def wake_selector
|
|
57
|
+
return unless target = @selector.value
|
|
58
|
+
control, wakeup = target
|
|
59
|
+
Thread.handle_interrupt(INTERRUPT_MASK) do
|
|
60
|
+
control.send(nil) if wakeup.compare_and_set(false, true)
|
|
61
|
+
end
|
|
62
|
+
rescue ::Ractor::ClosedError
|
|
63
|
+
nil
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def receive(timeout: nil)
|
|
67
|
+
result = @queue.pop(timeout:)
|
|
68
|
+
return result unless FROM_RACTOR.equal?(result)
|
|
69
|
+
@atom.value.take
|
|
70
|
+
rescue ClosedQueueError
|
|
71
|
+
raise ::Ractor::ClosedError, "The port was already closed"
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
class RactorReader
|
|
76
|
+
include Freeze::Unfreezable
|
|
77
|
+
|
|
78
|
+
PATTERN = /\A#<Ractor:#(?<id>\d+) (?:.+ )?(?<status>\w+)>\z/
|
|
79
|
+
|
|
80
|
+
def self.info(ractor, key)
|
|
81
|
+
raise "unexpected Ractor#inspect format: #{ractor.inspect}" unless match = PATTERN.match(ractor.inspect)
|
|
82
|
+
match[key]
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def self.status(ractor) = info(ractor, :status)
|
|
86
|
+
def self.id(ractor) = info(ractor, :id)
|
|
87
|
+
|
|
88
|
+
def initialize(ractor)
|
|
89
|
+
@closed = Atom.new(false)
|
|
90
|
+
@ractor = ractor
|
|
91
|
+
Freeze.publish(self)
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def receive(timeout: nil)
|
|
95
|
+
return ::Ractor.receive if timeout.nil?
|
|
96
|
+
RactorSelector.current.ractor_receive(::Ractor.current, timeout:)
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def selector_source = ::Ractor.current
|
|
100
|
+
|
|
101
|
+
def closed? = @closed.value
|
|
102
|
+
def close = @closed.value = true
|
|
103
|
+
def send(...) = @ractor.send(...)
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
private_constant :QueueReader, :RactorReader
|
|
107
|
+
|
|
108
|
+
def self.new(source = UNDEFINED)
|
|
109
|
+
case source
|
|
110
|
+
when UNDEFINED then super(QueueReader.new)
|
|
111
|
+
when ::Ractor then super(RactorReader.new(source))
|
|
112
|
+
when Port then source
|
|
113
|
+
else raise ArgumentError, "wrong number of arguments (given 1, expected 0)"
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def initialize(reader)
|
|
118
|
+
@owner = ::Ractor.current
|
|
119
|
+
@reader = reader
|
|
120
|
+
Freeze.publish(self)
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def ==(other) = other.is_a?(Port) && @reader == other.reader
|
|
124
|
+
|
|
125
|
+
def closed? = @reader.closed? || RactorReader.status(@owner) == "terminated"
|
|
126
|
+
|
|
127
|
+
def close
|
|
128
|
+
check_owner!
|
|
129
|
+
@reader.close
|
|
130
|
+
self
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
def send(message, move: false)
|
|
134
|
+
raise ::Ractor::ClosedError, "The port was already closed" if closed?
|
|
135
|
+
@reader.send(message, move:)
|
|
136
|
+
self
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
alias << send
|
|
140
|
+
|
|
141
|
+
def receive(timeout: nil)
|
|
142
|
+
check_owner!
|
|
143
|
+
raise ::Ractor::ClosedError, "The port was already closed" if @reader.closed?
|
|
144
|
+
if selector = RactorSelector.for_call(self)
|
|
145
|
+
selector.ractor_receive(self, timeout:)
|
|
146
|
+
else
|
|
147
|
+
@reader.receive(timeout:)
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
def selector_source
|
|
152
|
+
check_owner!
|
|
153
|
+
@reader.selector_source if @reader.is_a?(RactorReader)
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
def selector_watch(...) = @reader.selector_watch(...)
|
|
157
|
+
def selector_poll(&) = @reader.selector_poll(&)
|
|
158
|
+
|
|
159
|
+
def inspect
|
|
160
|
+
class_name = instance_of?(Port) ? "Farce::Ractor::Port" : self.class.name
|
|
161
|
+
"#<#{class_name} to:##{RactorReader.id(@owner)} id:#{object_id}>"
|
|
162
|
+
rescue StandardError
|
|
163
|
+
super
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
alias receive! receive
|
|
167
|
+
private :receive!
|
|
168
|
+
|
|
169
|
+
undef dup
|
|
170
|
+
undef clone
|
|
171
|
+
|
|
172
|
+
protected
|
|
173
|
+
|
|
174
|
+
attr_reader :reader
|
|
175
|
+
|
|
176
|
+
private
|
|
177
|
+
|
|
178
|
+
def check_owner!
|
|
179
|
+
return true if @owner.nil? || ::Ractor.current == @owner
|
|
180
|
+
raise ::Ractor::Error, "only allowed from the creator ::Ractor of this port"
|
|
181
|
+
end
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
BasePort = Port
|
|
185
|
+
end
|
|
186
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# shareable_constant_value: literal
|
|
3
|
+
# warn_indent: true
|
|
4
|
+
|
|
5
|
+
require "farce/engine/ruby/shared/ractor_methods"
|
|
6
|
+
|
|
7
|
+
module Farce
|
|
8
|
+
# @!visibility private
|
|
9
|
+
module Internal # :nodoc: all
|
|
10
|
+
module RactorMethods
|
|
11
|
+
def shareable_proc(**, &) = ::Ractor.make_shareable(Farce.rebind(**, &))
|
|
12
|
+
def shareable_lambda(**, &) = ::Ractor.make_shareable(Farce.rebind(**, lambda: true, &))
|
|
13
|
+
|
|
14
|
+
def select(*sources, timeout: nil, **)
|
|
15
|
+
selector = RactorSelector.for_call(sources)
|
|
16
|
+
selector ||= RactorSelector.current unless timeout.nil? && sources.all?(::Ractor)
|
|
17
|
+
selector ? selector.ractor_select(*sources, timeout:, **) : ::Ractor.select(*sources, **)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# TODO: Wrapper/Extension to add #default_port, etc.
|
|
21
|
+
def new(...) = ::Ractor.new(...)
|
|
22
|
+
def current = ::Ractor.current
|
|
23
|
+
def main = ::Ractor.main
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# shareable_constant_value: literal
|
|
3
|
+
# warn_indent: true
|
|
4
|
+
|
|
5
|
+
require "farce/engine/ruby/shared/ractor_selector"
|
|
6
|
+
|
|
7
|
+
module Farce
|
|
8
|
+
module Internal
|
|
9
|
+
class RactorSelector
|
|
10
|
+
# Ruby 3.4's M:N scheduler can strand the helper during Ractor teardown.
|
|
11
|
+
# Keep this helper on a dedicated native thread.
|
|
12
|
+
module DedicatedNativeThread
|
|
13
|
+
private
|
|
14
|
+
|
|
15
|
+
def run
|
|
16
|
+
Internal.lock_native_thread
|
|
17
|
+
super
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
private_constant :DedicatedNativeThread
|
|
21
|
+
prepend DedicatedNativeThread
|
|
22
|
+
|
|
23
|
+
# Native timeout: must retain Ruby's own error even with a scheduler hook.
|
|
24
|
+
# Only Farce's APIs backport that keyword.
|
|
25
|
+
def self.install_hooks(patch)
|
|
26
|
+
patch[::Ractor.singleton_class, :select, signature: "*sources, **options",
|
|
27
|
+
schedule: "*sources, **options", before: "return select!(*sources, **options) if options.key?(:timeout)"]
|
|
28
|
+
patch[::Ractor.singleton_class, :receive, signature: "",
|
|
29
|
+
schedule: "::Ractor.current"]
|
|
30
|
+
patch[::Ractor, :receive, :take, signature: "", schedule: "self"]
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def ractor_receive(source, timeout: nil) = wait([source], timeout)&.at(1)
|
|
34
|
+
alias ractor_take ractor_receive
|
|
35
|
+
|
|
36
|
+
def ractor_select(*sources, timeout: nil, yield_value: UNDEFINED, move: false)
|
|
37
|
+
options = { yield_value: yield_value, move: move } unless yield_value.equal?(UNDEFINED)
|
|
38
|
+
raise ArgumentError, "specify at least one Ractor or port" if sources.empty? && !options
|
|
39
|
+
result = wait(sources, timeout, options)
|
|
40
|
+
result[0] = :receive if result && result[0].equal?(@owner)
|
|
41
|
+
result
|
|
42
|
+
end
|
|
43
|
+
alias select ractor_select
|
|
44
|
+
|
|
45
|
+
private
|
|
46
|
+
|
|
47
|
+
def receive_source(source) = source
|
|
48
|
+
|
|
49
|
+
def fallback_port?(source)
|
|
50
|
+
!source.is_a?(::Ractor) && !source.is_a?(Request) && source.is_a?(Internal::Port)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def receive_control = @control.take
|
|
54
|
+
|
|
55
|
+
def close_control
|
|
56
|
+
@control.close_outgoing
|
|
57
|
+
@control.close_incoming
|
|
58
|
+
end
|
|
59
|
+
alias cleanup_control close_control
|
|
60
|
+
|
|
61
|
+
def check_closed(_source); end
|
|
62
|
+
|
|
63
|
+
def validate_source(source)
|
|
64
|
+
return if source.is_a?(::Ractor) || source.is_a?(Internal::Port)
|
|
65
|
+
raise ArgumentError, "expected a Ractor or port"
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# A private yielding Ractor makes wakeups selectable on Ruby 3.4.
|
|
69
|
+
# Control traffic stays out of the receiving Ractor's application inbox.
|
|
70
|
+
def build_control
|
|
71
|
+
Fiber.blocking do
|
|
72
|
+
control = ::Ractor.new do
|
|
73
|
+
::Ractor.yield(nil)
|
|
74
|
+
loop do
|
|
75
|
+
::Ractor.receive
|
|
76
|
+
::Ractor.yield(nil)
|
|
77
|
+
end
|
|
78
|
+
rescue ::Ractor::ClosedError
|
|
79
|
+
nil
|
|
80
|
+
ensure
|
|
81
|
+
# No final value is consumed. Close before Ruby publishes it so
|
|
82
|
+
# publication cannot race with the selector closing this port.
|
|
83
|
+
::Ractor.current.close_outgoing
|
|
84
|
+
end
|
|
85
|
+
# Complete startup before the first command can send a wakeup.
|
|
86
|
+
control.take
|
|
87
|
+
control
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
end
|
|
Binary file
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# shareable_constant_value: literal
|
|
3
|
+
# warn_indent: true
|
|
4
|
+
|
|
5
|
+
require "farce/engine/ruby/shared/vault"
|
|
6
|
+
|
|
7
|
+
module Farce
|
|
8
|
+
# @!visibility private
|
|
9
|
+
module Internal # :nodoc: all
|
|
10
|
+
class Vault
|
|
11
|
+
class Manager
|
|
12
|
+
private
|
|
13
|
+
|
|
14
|
+
def receive_request
|
|
15
|
+
message = ::Ractor.receive
|
|
16
|
+
return message if message.first.is_a?(Symbol)
|
|
17
|
+
|
|
18
|
+
ticket, action, arguments = message
|
|
19
|
+
[action, *arguments, ticket]
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def respond(port, message, move: false)
|
|
23
|
+
return port.store(::Ractor.make_shareable(message)) if port.is_a?(Atom)
|
|
24
|
+
::Ractor.yield([port, message].freeze, move:)
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def initialize
|
|
29
|
+
@lock = Lock.new
|
|
30
|
+
@sequence = Counter.new
|
|
31
|
+
@ractor = ::Ractor.new { Manager.new.run }
|
|
32
|
+
Freeze.publish(self)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
private
|
|
36
|
+
|
|
37
|
+
def execute(action, key, value = nil, move: false)
|
|
38
|
+
raise Ractor::IsolationError, "key must be shareable" unless ::Ractor.shareable?(key)
|
|
39
|
+
raise ThreadError, "deadlock; recursive access from the Vault Ractor" if ::Ractor.current.equal?(@ractor)
|
|
40
|
+
ticket = @sequence.add
|
|
41
|
+
@lock.synchronize do
|
|
42
|
+
# Short nested arrays avoid the 3.4 move bug affecting later array elements.
|
|
43
|
+
@ractor.send([ticket, action, [key, value].freeze].freeze, move:)
|
|
44
|
+
loop do
|
|
45
|
+
reply_ticket, response = @ractor.take
|
|
46
|
+
# A canceled request can leave an older reply in the outgoing queue.
|
|
47
|
+
next unless reply_ticket == ticket
|
|
48
|
+
|
|
49
|
+
success, payload = response
|
|
50
|
+
return success ? payload : raise(payload)
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
Binary file
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# shareable_constant_value: literal
|
|
3
|
+
# warn_indent: true
|
|
4
|
+
|
|
5
|
+
module Farce
|
|
6
|
+
# @!visibility private
|
|
7
|
+
module Internal # :nodoc: all
|
|
8
|
+
BasePort = ::Ractor::Port
|
|
9
|
+
class Port < BasePort
|
|
10
|
+
include Freeze::Unfreezable
|
|
11
|
+
|
|
12
|
+
def receive(timeout: nil)
|
|
13
|
+
selector = RactorSelector.for_call(self)
|
|
14
|
+
selector ||= RactorSelector.current unless timeout.nil?
|
|
15
|
+
selector ? selector.ractor_receive(self, timeout:) : super()
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# shareable_constant_value: literal
|
|
3
|
+
# warn_indent: true
|
|
4
|
+
|
|
5
|
+
require "farce/engine/ruby/shared/ractor_methods"
|
|
6
|
+
|
|
7
|
+
module Farce
|
|
8
|
+
# @!visibility private
|
|
9
|
+
module Internal # :nodoc: all
|
|
10
|
+
module RactorMethods
|
|
11
|
+
def select(*sources, timeout: nil)
|
|
12
|
+
selector = RactorSelector.for_call(sources)
|
|
13
|
+
selector ||= RactorSelector.current unless timeout.nil?
|
|
14
|
+
selector ? selector.ractor_select(*sources, timeout:) : ::Ractor.select(*sources)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
Internal.delegate(self, ::Ractor, :shareable_proc, :shareable_lambda, :current, :main, :new)
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# shareable_constant_value: literal
|
|
3
|
+
# warn_indent: true
|
|
4
|
+
|
|
5
|
+
require "farce/engine/ruby/shared/ractor_selector"
|
|
6
|
+
|
|
7
|
+
module Farce
|
|
8
|
+
module Internal
|
|
9
|
+
class RactorSelector
|
|
10
|
+
def self.install_hooks(patch)
|
|
11
|
+
if ::Ractor::Port.instance_method(:receive).parameters.include?(%i[key timeout])
|
|
12
|
+
patch[::Ractor.singleton_class, :select, signature: "*sources, **options", schedule: "*sources, **options"]
|
|
13
|
+
patch[::Ractor.singleton_class, :receive, schedule: "::Ractor.current, ..."]
|
|
14
|
+
patch[::Ractor, :receive]
|
|
15
|
+
patch[::Ractor::Port, :receive]
|
|
16
|
+
else
|
|
17
|
+
# Ruby 4.0 treats unsupported keywords as a positional Hash. Preserve
|
|
18
|
+
# its native argument error before consulting a cooperative scheduler.
|
|
19
|
+
patch[::Ractor.singleton_class, :select, signature: "*sources", schedule: "*sources",
|
|
20
|
+
before: "return select!(*sources) if sources.last.is_a?(Hash)"]
|
|
21
|
+
patch[::Ractor.singleton_class, :receive, signature: "", schedule: "::Ractor.current"]
|
|
22
|
+
patch[::Ractor, :receive, signature: "", schedule: "self"]
|
|
23
|
+
patch[::Ractor::Port, :receive, signature: "", schedule: "self"]
|
|
24
|
+
end
|
|
25
|
+
patch[::Ractor, :join, :value, signature: "", schedule: "self"]
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def ractor_receive(source, timeout: nil) = wait([receive_source(source)], timeout)&.at(1)
|
|
29
|
+
|
|
30
|
+
def ractor_select(*sources, timeout: nil)
|
|
31
|
+
raise ArgumentError, "specify at least one Ractor or port" if sources.empty?
|
|
32
|
+
# Monitor termination without consuming the Ractor's result. Only the
|
|
33
|
+
# selected caller retrieves that result, so a timeout leaves it available.
|
|
34
|
+
monitors = {}.compare_by_identity
|
|
35
|
+
ports = sources.map do |source|
|
|
36
|
+
if source.is_a?(::Ractor)
|
|
37
|
+
port = ::Ractor::Port.new
|
|
38
|
+
monitors[port] = source
|
|
39
|
+
source.monitor(port)
|
|
40
|
+
port
|
|
41
|
+
else
|
|
42
|
+
source
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
return unless result = wait(ports, timeout)
|
|
46
|
+
source, value = result
|
|
47
|
+
ractor = monitors[source]
|
|
48
|
+
ractor ? [ractor, native { ractor.value }] : [source, value]
|
|
49
|
+
ensure
|
|
50
|
+
monitors&.each do |port, ractor|
|
|
51
|
+
ractor.unmonitor(port)
|
|
52
|
+
port.close
|
|
53
|
+
forget(port)
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
alias select ractor_select
|
|
57
|
+
|
|
58
|
+
def ractor_join(ractor, timeout: nil)
|
|
59
|
+
terminated(ractor, timeout) { native { ractor.join } }
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def ractor_value(ractor, timeout: nil)
|
|
63
|
+
terminated(ractor, timeout) { native { ractor.value } }
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
private
|
|
67
|
+
|
|
68
|
+
def receive_source(source) = source.is_a?(::Ractor) ? source.default_port : source
|
|
69
|
+
def fallback_port?(_source) = false
|
|
70
|
+
def build_control = ::Ractor::Port.new
|
|
71
|
+
def receive_control = @control.receive
|
|
72
|
+
def close_control = @control.close
|
|
73
|
+
|
|
74
|
+
# Native ports are closed by CRuby during Ractor teardown. Closing them
|
|
75
|
+
# again from the dying helper can crash the VM.
|
|
76
|
+
def cleanup_control; end
|
|
77
|
+
|
|
78
|
+
def check_closed(source)
|
|
79
|
+
raise ::Ractor::ClosedError, "The port was already closed" if source.closed?
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def validate_source(source)
|
|
83
|
+
return if source.is_a?(::Ractor)
|
|
84
|
+
raise ArgumentError, "expected a Ractor or port" unless source.is_a?(::Ractor::Port)
|
|
85
|
+
inspect_port = ::Ractor::Port.instance_method(:inspect)
|
|
86
|
+
# Native ports expose their owner only through the built-in inspect.
|
|
87
|
+
owner = inspect_port.bind_call(@owner.default_port)[/to:#\d+/]
|
|
88
|
+
return if inspect_port.bind_call(source)[/to:#\d+/] == owner
|
|
89
|
+
raise ::Ractor::Error, "only allowed from the creator Ractor of this port"
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
# Readiness is separate from result retrieval. In particular, join must
|
|
93
|
+
# wait for termination without taking the value another caller may need.
|
|
94
|
+
def terminated(ractor, timeout)
|
|
95
|
+
port = ::Ractor::Port.new
|
|
96
|
+
ractor.monitor(port)
|
|
97
|
+
return unless wait([port], timeout)
|
|
98
|
+
yield
|
|
99
|
+
ensure
|
|
100
|
+
if port
|
|
101
|
+
ractor.unmonitor(port)
|
|
102
|
+
port.close
|
|
103
|
+
forget(port)
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
end
|
|
Binary file
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# shareable_constant_value: literal
|
|
3
|
+
# warn_indent: true
|
|
4
|
+
|
|
5
|
+
require "farce/engine/ruby/shared/vault"
|
|
6
|
+
|
|
7
|
+
module Farce
|
|
8
|
+
# @!visibility private
|
|
9
|
+
module Internal # :nodoc: all
|
|
10
|
+
class Vault
|
|
11
|
+
if System.windows?
|
|
12
|
+
# A Windows Port receiver can remain asleep after its reply was sent.
|
|
13
|
+
# Wait for a native Atom notification before consuming the queued reply.
|
|
14
|
+
class ReplyPort
|
|
15
|
+
include Freeze::Unfreezable
|
|
16
|
+
|
|
17
|
+
def initialize
|
|
18
|
+
@port = ::Ractor::Port.new
|
|
19
|
+
@ready = Atom.new
|
|
20
|
+
Freeze.publish(self)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def send(message, move: false)
|
|
24
|
+
@port.send(message, move:)
|
|
25
|
+
@ready.store(true)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def receive
|
|
29
|
+
@ready.wait_until_non_nil
|
|
30
|
+
@port.receive
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def close = @port.close
|
|
34
|
+
def closed? = @port.closed?
|
|
35
|
+
end
|
|
36
|
+
else
|
|
37
|
+
ReplyPort = ::Ractor::Port
|
|
38
|
+
end
|
|
39
|
+
private_constant :ReplyPort
|
|
40
|
+
|
|
41
|
+
class Manager
|
|
42
|
+
private
|
|
43
|
+
|
|
44
|
+
def receive_request = ::Ractor.receive
|
|
45
|
+
|
|
46
|
+
def respond(port, message, move: false)
|
|
47
|
+
return port.store(::Ractor.make_shareable(message)) if port.is_a?(Atom)
|
|
48
|
+
port.send(message, move:)
|
|
49
|
+
rescue ::Ractor::ClosedError
|
|
50
|
+
# An interrupted caller closes its private reply port.
|
|
51
|
+
raise unless port.is_a?(ReplyPort) && port.closed?
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def initialize
|
|
56
|
+
@ractor = ::Ractor.new { Manager.new.run }
|
|
57
|
+
Freeze.publish(self)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
private
|
|
61
|
+
|
|
62
|
+
def execute(action, key, value = nil, move: false)
|
|
63
|
+
raise Ractor::IsolationError, "key must be shareable" unless ::Ractor.shareable?(key)
|
|
64
|
+
raise ThreadError, "deadlock; recursive access from the Vault Ractor" if ::Ractor.current.equal?(@ractor)
|
|
65
|
+
port = ReplyPort.new
|
|
66
|
+
@ractor.send([action, key, value, port].freeze, move:)
|
|
67
|
+
success, payload = port.receive
|
|
68
|
+
success ? payload : raise(payload)
|
|
69
|
+
ensure
|
|
70
|
+
port&.close
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# shareable_constant_value: literal
|
|
3
|
+
# warn_indent: true
|
|
4
|
+
|
|
5
|
+
module Farce
|
|
6
|
+
# @!visibility private
|
|
7
|
+
module Internal # :nodoc: all
|
|
8
|
+
BasePort = ::Ractor::Port
|
|
9
|
+
class Port < BasePort
|
|
10
|
+
include Freeze::Unfreezable
|
|
11
|
+
|
|
12
|
+
def receive(timeout: nil)
|
|
13
|
+
if selector = RactorSelector.for_call(self)
|
|
14
|
+
selector.ractor_receive(self, timeout:)
|
|
15
|
+
else
|
|
16
|
+
timeout.nil? ? super() : super
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# shareable_constant_value: literal
|
|
3
|
+
# warn_indent: true
|
|
4
|
+
|
|
5
|
+
require "farce/engine/ruby/shared/ractor_methods"
|
|
6
|
+
|
|
7
|
+
module Farce
|
|
8
|
+
# @!visibility private
|
|
9
|
+
module Internal # :nodoc: all
|
|
10
|
+
module RactorMethods
|
|
11
|
+
def select(*sources, timeout: nil)
|
|
12
|
+
if selector = RactorSelector.for_call(sources)
|
|
13
|
+
selector.ractor_select(*sources, timeout:)
|
|
14
|
+
else
|
|
15
|
+
timeout.nil? ? ::Ractor.select(*sources) : ::Ractor.select(*sources, timeout:)
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
Internal.delegate(self, ::Ractor, :shareable_proc, :shareable_lambda, :current, :main, :new)
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# shareable_constant_value: literal
|
|
3
|
+
# warn_indent: true
|
|
4
|
+
|
|
5
|
+
require "farce/engine/ruby/4.0/ractor_selector"
|
|
6
|
+
|
|
7
|
+
module Farce
|
|
8
|
+
module Internal
|
|
9
|
+
class RactorSelector
|
|
10
|
+
# Ruby 4.1 can crash while formatting a dying helper's exception after
|
|
11
|
+
# Ractor teardown has freed its ports. Callers and close observe failures.
|
|
12
|
+
# See https://bugs.ruby-lang.org/issues/22386
|
|
13
|
+
module ExplicitFailureReporting
|
|
14
|
+
private
|
|
15
|
+
|
|
16
|
+
def run
|
|
17
|
+
Thread.current.report_on_exception = false
|
|
18
|
+
super
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
private_constant :ExplicitFailureReporting
|
|
22
|
+
prepend ExplicitFailureReporting
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|