farce 0.0.1.alpha2-java
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/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/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/vault.rb +56 -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/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 +420 -0
|
@@ -0,0 +1,413 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# shareable_constant_value: literal
|
|
3
|
+
# warn_indent: true
|
|
4
|
+
|
|
5
|
+
require "farce/engine/ruby/shared/vault_weak_map"
|
|
6
|
+
|
|
7
|
+
module Farce
|
|
8
|
+
# @!visibility private
|
|
9
|
+
module Internal # :nodoc: all
|
|
10
|
+
class VaultWeakMapBase < Abstract::ConcurrentMap
|
|
11
|
+
OWNER = Atom.new
|
|
12
|
+
CLAIM_OWNERS = Object.new.freeze
|
|
13
|
+
TIMED_OUT = Object.new.freeze
|
|
14
|
+
private_constant :OWNER, :CLAIM_OWNERS, :TIMED_OUT
|
|
15
|
+
|
|
16
|
+
def initialize(
|
|
17
|
+
initial_mapping = nil,
|
|
18
|
+
compare_by_identity: false,
|
|
19
|
+
compare_keys_by_identity: compare_by_identity,
|
|
20
|
+
compare_values_by_identity: compare_by_identity
|
|
21
|
+
)
|
|
22
|
+
validate_boolean(compare_by_identity, "compare_by_identity")
|
|
23
|
+
validate_boolean(compare_keys_by_identity, "compare_keys_by_identity")
|
|
24
|
+
validate_boolean(compare_values_by_identity, "compare_values_by_identity")
|
|
25
|
+
raise TypeError, "initial mapping must be a Hash" unless initial_mapping.nil? || initial_mapping.is_a?(Hash)
|
|
26
|
+
|
|
27
|
+
super()
|
|
28
|
+
|
|
29
|
+
@compare_keys_by_identity = compare_keys_by_identity
|
|
30
|
+
@compare_values_by_identity = compare_values_by_identity
|
|
31
|
+
@token = Object.new.freeze
|
|
32
|
+
@vault = OWNER.store_if_absent { Vault.new }
|
|
33
|
+
@freeze_state = Flag.new(false)
|
|
34
|
+
request(:create, {
|
|
35
|
+
weak_keys: weak_keys?,
|
|
36
|
+
weak_values: weak_values?,
|
|
37
|
+
compare_keys_by_identity: compare_keys_by_identity?,
|
|
38
|
+
}.freeze)
|
|
39
|
+
initial_mapping&.each { self[it.first] = it.last }
|
|
40
|
+
Object.instance_method(:freeze).bind_call(self)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def freeze
|
|
44
|
+
@freeze_state.set
|
|
45
|
+
self
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def frozen? = @freeze_state.value
|
|
49
|
+
|
|
50
|
+
def check_mutation = check_frozen!
|
|
51
|
+
|
|
52
|
+
def prepare_mutation_key(key)
|
|
53
|
+
check_frozen!
|
|
54
|
+
canonical_key(key)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def [](key) = read(key, nil)[1]
|
|
58
|
+
|
|
59
|
+
def []=(key, value)
|
|
60
|
+
store(key, value)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def fetch(*arguments)
|
|
64
|
+
unless arguments.length.between?(1, 2)
|
|
65
|
+
raise ArgumentError, "wrong number of arguments (given #{arguments.length}, expected 1..2)"
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
key, default = arguments
|
|
69
|
+
default_given = arguments.length == 2
|
|
70
|
+
warn "block supersedes default value argument", uplevel: 1 if block_given? && default_given
|
|
71
|
+
present, value = read(key, nil)
|
|
72
|
+
return value if present
|
|
73
|
+
return yield(key) if block_given?
|
|
74
|
+
return default if default_given
|
|
75
|
+
|
|
76
|
+
raise KeyError.new("key not found: #{key.inspect}", receiver: self, key: key)
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def get(key, timeout: nil, &fallback)
|
|
80
|
+
result = read(key, timeout_deadline(timeout))
|
|
81
|
+
result.equal?(TIMED_OUT) ? fallback&.call : result[1]
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def store(key, value, timeout: nil, &fallback)
|
|
85
|
+
check_frozen!
|
|
86
|
+
key = canonical_key(key)
|
|
87
|
+
check_value(value, "value")
|
|
88
|
+
result = await_response(deadline: timeout_deadline(timeout)) { request(:store, key, value, false) }
|
|
89
|
+
result.equal?(TIMED_OUT) ? fallback&.call : value
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def swap(key, replacement, timeout: nil, &fallback)
|
|
93
|
+
check_frozen!
|
|
94
|
+
key = canonical_key(key)
|
|
95
|
+
check_value(replacement, "value")
|
|
96
|
+
result = await_response(deadline: timeout_deadline(timeout)) { request(:store, key, replacement, true) }
|
|
97
|
+
result.equal?(TIMED_OUT) ? fallback&.call : result[1]
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def store_if_absent(key, timeout: nil, &update)
|
|
101
|
+
raise LocalJumpError, "no block given" unless update
|
|
102
|
+
|
|
103
|
+
claimed_update(key, timeout_deadline(timeout), claim: :absent) { update.call }
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def compare_and_set(key, expected, replacement, timeout: nil)
|
|
107
|
+
check_frozen!
|
|
108
|
+
check_value(expected, "value")
|
|
109
|
+
check_value(replacement, "value")
|
|
110
|
+
matched = false
|
|
111
|
+
claimed_update(key, timeout_deadline(timeout), claim: :present) do |current|
|
|
112
|
+
next TIMED_OUT unless values_equal?(current, expected)
|
|
113
|
+
|
|
114
|
+
check_frozen!
|
|
115
|
+
matched = true
|
|
116
|
+
replacement
|
|
117
|
+
end
|
|
118
|
+
matched
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def update(key, timeout: nil, &update)
|
|
122
|
+
raise LocalJumpError, "no block given" unless update
|
|
123
|
+
|
|
124
|
+
claimed_update(key, timeout_deadline(timeout), claim: :always, &update)
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def upsert(key, initial_value, timeout: nil, &update)
|
|
128
|
+
raise LocalJumpError, "no block given" unless update
|
|
129
|
+
check_value(initial_value, "value")
|
|
130
|
+
|
|
131
|
+
claimed_update(key, timeout_deadline(timeout), claim: :present, initial: initial_value, &update)
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
# Yield presence and value under the entry claim.
|
|
135
|
+
# MAP_KEEP and MAP_DELETE are control results. Other results replace the value.
|
|
136
|
+
# Return whether a change committed.
|
|
137
|
+
def modify(key)
|
|
138
|
+
raise LocalJumpError, "no block given" unless block_given?
|
|
139
|
+
check_frozen!
|
|
140
|
+
key = canonical_key(key)
|
|
141
|
+
ticket = Object.new.freeze
|
|
142
|
+
sent = finished = false
|
|
143
|
+
begin
|
|
144
|
+
response = await_response(deadline: nil) do
|
|
145
|
+
sent = true
|
|
146
|
+
request(:claim, key, ticket, true)
|
|
147
|
+
end
|
|
148
|
+
present, current, signal = response[1], response[2], response[3]
|
|
149
|
+
owners = claim_owners
|
|
150
|
+
owners[signal] = Fiber.current
|
|
151
|
+
value = yield(present, current)
|
|
152
|
+
check_frozen!
|
|
153
|
+
action = if MAP_KEEP.equal?(value)
|
|
154
|
+
:abort
|
|
155
|
+
elsif MAP_DELETE.equal?(value)
|
|
156
|
+
:delete
|
|
157
|
+
else
|
|
158
|
+
:store
|
|
159
|
+
end
|
|
160
|
+
check_value(value, "value") if action == :store
|
|
161
|
+
result = request(:finish, key, ticket, action, value)
|
|
162
|
+
finished = true
|
|
163
|
+
result.first != :stale && (action == :store || (action == :delete && present))
|
|
164
|
+
ensure
|
|
165
|
+
owners&.delete(signal)
|
|
166
|
+
request(:finish, key, ticket, :abort) if sent && !finished
|
|
167
|
+
end
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
def wait_until_changed(key, expected, timeout: nil, &fallback)
|
|
171
|
+
wait_for_value(key, expected, timeout_deadline(timeout), fallback, non_nil: false)
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
def wait_until_non_nil(key, timeout: nil, &fallback)
|
|
175
|
+
wait_for_value(key, nil, timeout_deadline(timeout), fallback, non_nil: true)
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
def key?(key) = read(key, nil).first
|
|
179
|
+
def compare_keys_by_identity? = @compare_keys_by_identity
|
|
180
|
+
def compare_values_by_identity? = @compare_values_by_identity
|
|
181
|
+
def shareable_keys? = true
|
|
182
|
+
def shareable_values? = true
|
|
183
|
+
def size = await_response(deadline: nil) { request(:size) }[1]
|
|
184
|
+
def keys = entries_snapshot.map(&:first)
|
|
185
|
+
|
|
186
|
+
def each(&block)
|
|
187
|
+
return enum_for(__callee__) { size } unless block
|
|
188
|
+
entries_snapshot.each { block.call(it) }
|
|
189
|
+
self
|
|
190
|
+
end
|
|
191
|
+
alias each_pair each
|
|
192
|
+
|
|
193
|
+
def each_live
|
|
194
|
+
return enum_for(__method__) { size } unless block_given?
|
|
195
|
+
token = request(:open_cursor)[1]
|
|
196
|
+
begin
|
|
197
|
+
while true
|
|
198
|
+
result = await_response(deadline: nil) { request(:next_live, token) }
|
|
199
|
+
break if result.first == :done
|
|
200
|
+
yield [result[1], result[2]]
|
|
201
|
+
end
|
|
202
|
+
ensure
|
|
203
|
+
request(:close_cursor, token)
|
|
204
|
+
end
|
|
205
|
+
self
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
def each_key(&block)
|
|
209
|
+
return enum_for(__callee__) { size } unless block
|
|
210
|
+
entries_snapshot.each { block.call(it.first) }
|
|
211
|
+
self
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
def each_value(&block)
|
|
215
|
+
return enum_for(__callee__) { size } unless block
|
|
216
|
+
entries_snapshot.each { block.call(it.last) }
|
|
217
|
+
self
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
def delete(key)
|
|
221
|
+
check_frozen!
|
|
222
|
+
key = canonical_key(key)
|
|
223
|
+
result = await_response(deadline: nil) { request(:delete, key) }
|
|
224
|
+
result.first == :ok ? result[1] : nil
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
def getkey(key)
|
|
228
|
+
key = canonical_key(key)
|
|
229
|
+
result = await_response(deadline: nil) { request(:getkey, key) }
|
|
230
|
+
result.first == :ok ? result[1] : nil
|
|
231
|
+
end
|
|
232
|
+
|
|
233
|
+
def clear
|
|
234
|
+
check_frozen!
|
|
235
|
+
request(:clear)
|
|
236
|
+
self
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
private
|
|
240
|
+
|
|
241
|
+
def check_frozen! = Internal::Freeze.check(self)
|
|
242
|
+
|
|
243
|
+
def request(action, *) = @vault.weak_map(@token, action, *)
|
|
244
|
+
|
|
245
|
+
def read(key, deadline)
|
|
246
|
+
key = canonical_key(key)
|
|
247
|
+
result = await_response(deadline:) { request(:read, key) }
|
|
248
|
+
return TIMED_OUT if result.equal?(TIMED_OUT)
|
|
249
|
+
return [false, nil] if result.first == :missing
|
|
250
|
+
|
|
251
|
+
[result[1], result[2]]
|
|
252
|
+
end
|
|
253
|
+
|
|
254
|
+
def claimed_update(key, deadline, claim:, initial: TIMED_OUT)
|
|
255
|
+
check_frozen!
|
|
256
|
+
key = canonical_key(key)
|
|
257
|
+
ticket = Object.new.freeze
|
|
258
|
+
sent = finished = false
|
|
259
|
+
|
|
260
|
+
begin
|
|
261
|
+
response = await_response(deadline:) do
|
|
262
|
+
sent = true
|
|
263
|
+
request(:claim, key, ticket, claim != :present || !initial.equal?(TIMED_OUT))
|
|
264
|
+
end
|
|
265
|
+
return if response.equal?(TIMED_OUT)
|
|
266
|
+
return false if response.first == :missing
|
|
267
|
+
|
|
268
|
+
present, current, signal = response[1], response[2], response[3]
|
|
269
|
+
if claim == :absent && present
|
|
270
|
+
request(:finish, key, ticket, :abort)
|
|
271
|
+
finished = true
|
|
272
|
+
return current
|
|
273
|
+
end
|
|
274
|
+
|
|
275
|
+
owners = claim_owners
|
|
276
|
+
owners[signal] = Fiber.current
|
|
277
|
+
replacement = present || initial.equal?(TIMED_OUT) ? yield(current) : initial
|
|
278
|
+
check_frozen!
|
|
279
|
+
|
|
280
|
+
if replacement.equal?(TIMED_OUT)
|
|
281
|
+
request(:finish, key, ticket, :abort)
|
|
282
|
+
finished = true
|
|
283
|
+
return false
|
|
284
|
+
end
|
|
285
|
+
|
|
286
|
+
check_value(replacement, "value")
|
|
287
|
+
result = request(:finish, key, ticket, :store, replacement)
|
|
288
|
+
finished = true
|
|
289
|
+
result.first == :stale ? nil : replacement
|
|
290
|
+
ensure
|
|
291
|
+
owners&.delete(signal)
|
|
292
|
+
request(:finish, key, ticket, :abort) if sent && !finished
|
|
293
|
+
end
|
|
294
|
+
end
|
|
295
|
+
|
|
296
|
+
def entries_snapshot
|
|
297
|
+
result = await_response(deadline: nil) { request(:snapshot) }
|
|
298
|
+
result[1]
|
|
299
|
+
end
|
|
300
|
+
|
|
301
|
+
def await_response(deadline:)
|
|
302
|
+
checked_after_timeout = false
|
|
303
|
+
loop do
|
|
304
|
+
response = yield
|
|
305
|
+
return response unless response.first == :busy
|
|
306
|
+
|
|
307
|
+
signal, generation = response[1], response[2]
|
|
308
|
+
reject_recursive_wait!(signal)
|
|
309
|
+
timeout = remaining_timeout(deadline)
|
|
310
|
+
if timeout&.zero?
|
|
311
|
+
return TIMED_OUT if checked_after_timeout
|
|
312
|
+
checked_after_timeout = true
|
|
313
|
+
next
|
|
314
|
+
end
|
|
315
|
+
changed = signal.wait_until_changed(generation, timeout:) { TIMED_OUT }
|
|
316
|
+
if changed.equal?(TIMED_OUT)
|
|
317
|
+
return TIMED_OUT if checked_after_timeout
|
|
318
|
+
checked_after_timeout = true
|
|
319
|
+
end
|
|
320
|
+
end
|
|
321
|
+
end
|
|
322
|
+
|
|
323
|
+
def wait_for_value(key, expected, deadline, fallback, non_nil:)
|
|
324
|
+
key = canonical_key(key)
|
|
325
|
+
check_value(expected, "value")
|
|
326
|
+
checked_after_timeout = false
|
|
327
|
+
loop do
|
|
328
|
+
response = await_response(deadline:) { request(:wait_read, key) }
|
|
329
|
+
return fallback&.call if response.equal?(TIMED_OUT)
|
|
330
|
+
# Keep the canonical stored key alive, even when the lookup used a
|
|
331
|
+
# different equal key. It arrives atomically with the entry's signal.
|
|
332
|
+
key = response[5] if response.first == :ok
|
|
333
|
+
present = response.first == :ok && response[1]
|
|
334
|
+
current = present ? response[2] : nil
|
|
335
|
+
return current if non_nil ? !current.nil? : !values_equal?(current, expected)
|
|
336
|
+
|
|
337
|
+
signal = response.first == :ok ? response[3] : response[1]
|
|
338
|
+
generation = response.first == :ok ? response[4] : response[2]
|
|
339
|
+
changed = signal.wait_until_changed(generation, timeout: remaining_timeout(deadline)) { TIMED_OUT }
|
|
340
|
+
if changed.equal?(TIMED_OUT)
|
|
341
|
+
return fallback&.call if checked_after_timeout
|
|
342
|
+
checked_after_timeout = true
|
|
343
|
+
end
|
|
344
|
+
end
|
|
345
|
+
end
|
|
346
|
+
|
|
347
|
+
def claim_owners
|
|
348
|
+
Storage.thread.store_if_absent(CLAIM_OWNERS) { {}.compare_by_identity }
|
|
349
|
+
end
|
|
350
|
+
|
|
351
|
+
def reject_recursive_wait!(signal)
|
|
352
|
+
owner = claim_owners[signal]
|
|
353
|
+
return unless owner
|
|
354
|
+
raise ThreadError, "deadlock; recursive weak-map access during an update" if owner.equal?(Fiber.current)
|
|
355
|
+
return if Fiber.respond_to?(:scheduler) && Fiber.scheduler
|
|
356
|
+
|
|
357
|
+
raise ThreadError, "deadlock; weak-map update is owned by another unscheduled fiber"
|
|
358
|
+
end
|
|
359
|
+
|
|
360
|
+
def values_equal?(left, right)
|
|
361
|
+
compare_values_by_identity? ? left.equal?(right) : left == right
|
|
362
|
+
end
|
|
363
|
+
|
|
364
|
+
def canonical_key(key)
|
|
365
|
+
key = String.instance_method(:-@).bind_call(key) if String === key && !key.frozen? && !compare_keys_by_identity?
|
|
366
|
+
check_value(key, "key")
|
|
367
|
+
end
|
|
368
|
+
|
|
369
|
+
def check_value(value, name)
|
|
370
|
+
return value if Ractor.shareable?(value)
|
|
371
|
+
raise Ractor::IsolationError, "#{name} must be Ractor-shareable"
|
|
372
|
+
end
|
|
373
|
+
|
|
374
|
+
def timeout_deadline(timeout)
|
|
375
|
+
return if timeout.nil?
|
|
376
|
+
|
|
377
|
+
timeout = Float(timeout)
|
|
378
|
+
unless timeout.finite? && !timeout.negative?
|
|
379
|
+
raise ArgumentError, "timeout must be a finite, non-negative number or nil"
|
|
380
|
+
end
|
|
381
|
+
Clock.now + timeout
|
|
382
|
+
end
|
|
383
|
+
|
|
384
|
+
def remaining_timeout(deadline)
|
|
385
|
+
return unless deadline
|
|
386
|
+
|
|
387
|
+
remaining = deadline - Clock.now
|
|
388
|
+
remaining.positive? ? remaining : 0
|
|
389
|
+
end
|
|
390
|
+
|
|
391
|
+
def validate_boolean(value, name)
|
|
392
|
+
return if value.equal?(true) || value.equal?(false)
|
|
393
|
+
raise ArgumentError, "#{name} must be true or false"
|
|
394
|
+
end
|
|
395
|
+
end
|
|
396
|
+
private_constant :VaultWeakMapBase
|
|
397
|
+
|
|
398
|
+
class WeakKeyMap < VaultWeakMapBase
|
|
399
|
+
def weak_keys? = true
|
|
400
|
+
def weak_values? = false
|
|
401
|
+
end
|
|
402
|
+
|
|
403
|
+
class WeakValueMap < VaultWeakMapBase
|
|
404
|
+
def weak_keys? = false
|
|
405
|
+
def weak_values? = true
|
|
406
|
+
end
|
|
407
|
+
|
|
408
|
+
class WeakMap < VaultWeakMapBase
|
|
409
|
+
def weak_keys? = true
|
|
410
|
+
def weak_values? = true
|
|
411
|
+
end
|
|
412
|
+
end
|
|
413
|
+
end
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# shareable_constant_value: literal
|
|
3
|
+
# warn_indent: true
|
|
4
|
+
|
|
5
|
+
# Load the scheduler dependency before native initialization.
|
|
6
|
+
# Late Resolv loading can crash RubyGems for some reason
|
|
7
|
+
require "resolv"
|
|
8
|
+
require "farce/strict"
|
|
9
|
+
|
|
10
|
+
module Farce
|
|
11
|
+
# @!visibility private
|
|
12
|
+
module Internal # :nodoc: all
|
|
13
|
+
# Compiled extensions and Ruby helpers share one directory for each CRuby ABI.
|
|
14
|
+
ENGINE_PATH = "farce/engine/ruby/#{RUBY_VERSION[/^\d+\.\d+/]}".freeze
|
|
15
|
+
private_constant :ENGINE_PATH
|
|
16
|
+
|
|
17
|
+
begin
|
|
18
|
+
require "#{ENGINE_PATH}/rebind"
|
|
19
|
+
require "#{ENGINE_PATH}/farce"
|
|
20
|
+
rescue LoadError => e
|
|
21
|
+
# simplecov:disable
|
|
22
|
+
warn <<~WARNING
|
|
23
|
+
Farce: Failed to load native extension for Ruby #{RUBY_VERSION}.
|
|
24
|
+
Please run `rake compile` to build the extension.
|
|
25
|
+
WARNING
|
|
26
|
+
raise e
|
|
27
|
+
# simplecov:enable
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
unless const_defined?(:NATIVE_WEAK_MAPS, false) && NATIVE_WEAK_MAPS
|
|
31
|
+
autoload :WeakMap, "farce/engine/ruby/shared/weak_map"
|
|
32
|
+
autoload :WeakKeyMap, "farce/engine/ruby/shared/weak_map"
|
|
33
|
+
autoload :WeakValueMap, "farce/engine/ruby/shared/weak_map"
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
StrictAtom = Atom
|
|
37
|
+
StrictLFUMap = ShareableLFUMap
|
|
38
|
+
StrictLRUMap = ShareableLRUMap
|
|
39
|
+
StrictTreeMap = ShareableTreeMap
|
|
40
|
+
|
|
41
|
+
autoload :ProxyOwner, "farce/engine/ruby/shared/proxy_owner"
|
|
42
|
+
autoload :ParallelScheduler, "farce/engine/ruby/shared/parallel_scheduler"
|
|
43
|
+
autoload :MainScheduler, "farce/engine/ruby/shared/main_scheduler"
|
|
44
|
+
autoload :Lease, "farce/engine/ruby/shared/lease"
|
|
45
|
+
autoload :LeasePool, "farce/engine/ruby/shared/lease_pool"
|
|
46
|
+
autoload :StrictMap, "farce/engine/ruby/shared/strict_map"
|
|
47
|
+
autoload :StrictWeakKeyMap, "farce/engine/ruby/shared/strict_map"
|
|
48
|
+
autoload :StrictWeakMap, "farce/engine/ruby/shared/strict_map"
|
|
49
|
+
autoload :StrictWeakValueMap, "farce/engine/ruby/shared/strict_map"
|
|
50
|
+
autoload :WeakAtom, "farce/engine/ruby/shared/weak_atom" unless const_defined?(:WeakAtom)
|
|
51
|
+
autoload :UnsharedWeakAtom, "farce/engine/shared/unshared_weak_atom" unless const_defined?(:UnsharedWeakAtom)
|
|
52
|
+
autoload :UnsharedAtom, "farce/engine/shared/unshared_atom"
|
|
53
|
+
autoload :UnsharedVector, "farce/engine/ruby/shared/unshared_vector"
|
|
54
|
+
autoload :UnsharedMap, "farce/engine/shared/unshared_weak_map"
|
|
55
|
+
autoload :UnsharedWeakMap, "farce/engine/shared/unshared_weak_map"
|
|
56
|
+
autoload :UnsharedWeakKeyMap, "farce/engine/shared/unshared_weak_map"
|
|
57
|
+
autoload :UnsharedWeakValueMap, "farce/engine/shared/unshared_weak_map"
|
|
58
|
+
autoload :FiberScheduler, "farce/engine/ruby/fiber_scheduler"
|
|
59
|
+
|
|
60
|
+
autoload :BasePort, "#{ENGINE_PATH}/port"
|
|
61
|
+
autoload :Port, "#{ENGINE_PATH}/port"
|
|
62
|
+
autoload :RactorMethods, "#{ENGINE_PATH}/ractor_methods"
|
|
63
|
+
autoload :Vault, "#{ENGINE_PATH}/vault"
|
|
64
|
+
|
|
65
|
+
def native_ractors? = true
|
|
66
|
+
|
|
67
|
+
class << self
|
|
68
|
+
alias commit_native_transaction commit_transaction
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def self.commit_transaction(entries, guards = [], transaction = nil)
|
|
72
|
+
external, entries = entries.partition { it.respond_to?(:commit_group) }
|
|
73
|
+
return ExternalTransaction.commit(entries, external, guards, transaction) unless external.empty?
|
|
74
|
+
guards = guards.map { it.respond_to?(:native_flag) ? it.native_flag : it }
|
|
75
|
+
native, portable = entries.partition { NativeTransactionEntry === it }
|
|
76
|
+
return commit_native_transaction(native, guards) if portable.empty?
|
|
77
|
+
PortableTransaction.commit(portable, guards) { commit_native_transaction(native, guards) }
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def self.notify_transaction(entries)
|
|
81
|
+
entries.each { it.notify unless NativeTransactionEntry === it }
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def prepare_method_definition(&) = Ractor.shareable_proc(&)
|
|
85
|
+
|
|
86
|
+
if ::Ractor.const_defined?(:Port, false)
|
|
87
|
+
def native_ports? = true
|
|
88
|
+
else
|
|
89
|
+
def native_ports? = false
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
# Native waits opt in through the scheduler's ractor_selector method.
|
|
93
|
+
# Blocking fibers use Ruby directly, including the helper's result retrieval.
|
|
94
|
+
# Version-specific hooks retain native timeout support and method visibility.
|
|
95
|
+
patch = lambda do |namespace, *methods, signature: "...", schedule: "self, ...", before: nil|
|
|
96
|
+
methods.each do |method|
|
|
97
|
+
visibility = namespace.private_method_defined?(method) ? "private" : "public"
|
|
98
|
+
namespace.class_eval <<~RUBY, __FILE__, __LINE__ + 1
|
|
99
|
+
alias #{method}! #{method}
|
|
100
|
+
private :#{method}!
|
|
101
|
+
|
|
102
|
+
def #{method}(#{signature})
|
|
103
|
+
#{before}
|
|
104
|
+
scheduler = Fiber.scheduler unless Fiber.blocking?
|
|
105
|
+
selector = scheduler.ractor_selector if scheduler.respond_to?(:ractor_selector)
|
|
106
|
+
return #{method}!(#{signature}) unless selector
|
|
107
|
+
selector.ractor_#{method}(#{schedule})
|
|
108
|
+
end
|
|
109
|
+
#{visibility} :#{method}
|
|
110
|
+
RUBY
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
require "#{ENGINE_PATH}/ractor_selector"
|
|
115
|
+
RactorSelector.install_hooks(patch)
|
|
116
|
+
::Ractor.singleton_class.alias_method :recv, :receive
|
|
117
|
+
::Ractor.alias_method :recv, :receive
|
|
118
|
+
|
|
119
|
+
if defined?(Darwin.main_qos_class) && defined?(Darwin.get_qos_class)
|
|
120
|
+
def prepare_thread
|
|
121
|
+
main_qos_class = Darwin.main_qos_class
|
|
122
|
+
qos_class, priority = Darwin.get_qos_class
|
|
123
|
+
Darwin.set_qos_class(main_qos_class, priority) if main_qos_class > qos_class
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
require "farce/engine/ruby/key_lock_map"
|
|
130
|
+
require "farce/engine/shared/trie_builder"
|
|
@@ -0,0 +1,130 @@
|
|
|
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
|
+
class Exchanger
|
|
9
|
+
include Freeze::Unfreezable
|
|
10
|
+
|
|
11
|
+
TIMED_OUT = Object.new.freeze
|
|
12
|
+
WAITING = Object.new.freeze
|
|
13
|
+
Waiter = Struct.new(:condition, :matched, :offered, :reader, :received, :scheduler, :writer)
|
|
14
|
+
private_constant :TIMED_OUT, :WAITING, :Waiter
|
|
15
|
+
|
|
16
|
+
def initialize
|
|
17
|
+
@mutex = Mutex.new
|
|
18
|
+
@waiting = [nil]
|
|
19
|
+
Freeze.publish(self)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def exchange(offered, timeout: nil, &fallback)
|
|
23
|
+
deadline = timeout_deadline(timeout)
|
|
24
|
+
waiter = build_waiter(offered)
|
|
25
|
+
|
|
26
|
+
partner = @mutex.synchronize do
|
|
27
|
+
if partner = @waiting.first
|
|
28
|
+
@waiting[0] = nil
|
|
29
|
+
partner.received = offered
|
|
30
|
+
partner.matched = true
|
|
31
|
+
notify(partner)
|
|
32
|
+
partner
|
|
33
|
+
elsif deadline && deadline <= Clock.now
|
|
34
|
+
TIMED_OUT
|
|
35
|
+
else
|
|
36
|
+
@waiting[0] = waiter
|
|
37
|
+
nil
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
return partner.offered if partner && !TIMED_OUT.equal?(partner)
|
|
42
|
+
return timed_out(fallback) if TIMED_OUT.equal?(partner)
|
|
43
|
+
|
|
44
|
+
result = waiter.scheduler ? wait_with_scheduler(waiter, deadline) : wait_with_condition(waiter, deadline)
|
|
45
|
+
TIMED_OUT.equal?(result) ? timed_out(fallback) : result
|
|
46
|
+
ensure
|
|
47
|
+
remove(waiter) if waiter
|
|
48
|
+
waiter&.reader&.close
|
|
49
|
+
waiter&.writer&.close
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
private
|
|
53
|
+
|
|
54
|
+
def build_waiter(offered)
|
|
55
|
+
scheduler = Fiber.current_scheduler if Fiber.respond_to?(:current_scheduler)
|
|
56
|
+
# JRuby may run a later scheduled fiber on a backing thread where this is nil.
|
|
57
|
+
scheduler ||= Fiber.scheduler if Fiber.respond_to?(:scheduler) && !Fiber.current.blocking?
|
|
58
|
+
reader, writer = IO.pipe if scheduler
|
|
59
|
+
Waiter.new(
|
|
60
|
+
condition: ConditionVariable.new,
|
|
61
|
+
matched: false,
|
|
62
|
+
offered: offered,
|
|
63
|
+
reader: reader,
|
|
64
|
+
scheduler: scheduler,
|
|
65
|
+
writer: writer,
|
|
66
|
+
)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def notify(waiter)
|
|
70
|
+
if waiter.scheduler
|
|
71
|
+
waiter.writer.write_nonblock(".", exception: false)
|
|
72
|
+
else
|
|
73
|
+
waiter.condition.broadcast
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def wait_with_condition(waiter, deadline)
|
|
78
|
+
@mutex.synchronize do
|
|
79
|
+
until waiter.matched
|
|
80
|
+
remaining = deadline - Clock.now if deadline
|
|
81
|
+
return cancel(waiter) if remaining && !remaining.positive?
|
|
82
|
+
waiter.condition.wait(@mutex, remaining)
|
|
83
|
+
end
|
|
84
|
+
waiter.received
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def wait_with_scheduler(waiter, deadline)
|
|
89
|
+
loop do
|
|
90
|
+
state, result = @mutex.synchronize do
|
|
91
|
+
if waiter.matched
|
|
92
|
+
[nil, waiter.received]
|
|
93
|
+
else
|
|
94
|
+
remaining = deadline - Clock.now if deadline
|
|
95
|
+
remaining && !remaining.positive? ? [nil, cancel(waiter)] : [WAITING, remaining]
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
return result unless WAITING.equal?(state)
|
|
99
|
+
|
|
100
|
+
waiter.scheduler.io_wait(waiter.reader, IO::READABLE, result)
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def cancel(waiter)
|
|
105
|
+
remove(waiter)
|
|
106
|
+
waiter.matched ? waiter.received : TIMED_OUT
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def remove(waiter)
|
|
110
|
+
@mutex.owned? ? remove_locked(waiter) : @mutex.synchronize { remove_locked(waiter) }
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def remove_locked(waiter)
|
|
114
|
+
@waiting[0] = nil if @waiting.first.equal?(waiter)
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def timeout_deadline(timeout)
|
|
118
|
+
return unless timeout
|
|
119
|
+
|
|
120
|
+
timeout = Float(timeout)
|
|
121
|
+
if !timeout.finite? || timeout.negative?
|
|
122
|
+
raise ArgumentError, "timeout must be a finite, non-negative number or nil"
|
|
123
|
+
end
|
|
124
|
+
Clock.now + timeout
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def timed_out(fallback) = fallback&.call
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
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
|
+
class IdentityKey
|
|
9
|
+
BASIC_OBJECT_EQUAL_METHOD = BasicObject.instance_method(:equal?)
|
|
10
|
+
BASIC_OBJECT_ID_METHOD = BasicObject.instance_method(:__id__)
|
|
11
|
+
private_constant :BASIC_OBJECT_EQUAL_METHOD, :BASIC_OBJECT_ID_METHOD
|
|
12
|
+
|
|
13
|
+
attr_reader :value
|
|
14
|
+
|
|
15
|
+
def initialize(value) = @value = value
|
|
16
|
+
def hash = BASIC_OBJECT_ID_METHOD.bind_call(value)
|
|
17
|
+
def eql?(other) = other.is_a?(IdentityKey) && BASIC_OBJECT_EQUAL_METHOD.bind_call(value, other.value)
|
|
18
|
+
|
|
19
|
+
alias == eql?
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|