farce 0.0.1.alpha2-x86_64-linux-gnu
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,189 @@
|
|
|
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
|
+
# Atom coordinates updates. The backend owns the weak slot and publishes
|
|
9
|
+
# changes, including writes whose requesting thread was interrupted.
|
|
10
|
+
class WeakAtomBase
|
|
11
|
+
TIMED_OUT = Object.new.freeze
|
|
12
|
+
private_constant :TIMED_OUT
|
|
13
|
+
|
|
14
|
+
def initialize(value = nil, compare_by_identity: false)
|
|
15
|
+
identity = BasicObject.instance_method(:equal?)
|
|
16
|
+
unless identity.bind_call(compare_by_identity, true) || identity.bind_call(compare_by_identity, false)
|
|
17
|
+
raise ArgumentError, "compare_by_identity must be a boolean"
|
|
18
|
+
end
|
|
19
|
+
validate_value(value)
|
|
20
|
+
@compare_by_identity = compare_by_identity
|
|
21
|
+
@lock = Atom.new
|
|
22
|
+
@changes = Atom.new(0)
|
|
23
|
+
@freeze_state = Flag.new(false)
|
|
24
|
+
initialize_storage(value)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def value = read_value
|
|
28
|
+
def compare_by_identity? = @compare_by_identity
|
|
29
|
+
|
|
30
|
+
def freeze
|
|
31
|
+
@freeze_state.set
|
|
32
|
+
self
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def frozen? = @freeze_state.value
|
|
36
|
+
|
|
37
|
+
def value=(new_value)
|
|
38
|
+
store(new_value)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def get(timeout: nil, &fallback)
|
|
42
|
+
result = with_value(timeout) { read_value }
|
|
43
|
+
result.equal?(TIMED_OUT) ? fallback&.call : result
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def store(new_value, timeout: nil, &fallback)
|
|
47
|
+
check_frozen!
|
|
48
|
+
validate_value(new_value)
|
|
49
|
+
result = with_value(timeout) do
|
|
50
|
+
check_frozen!
|
|
51
|
+
write_value(new_value)
|
|
52
|
+
new_value
|
|
53
|
+
end
|
|
54
|
+
result.equal?(TIMED_OUT) ? fallback&.call : result
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def swap(new_value, timeout: nil, &fallback)
|
|
58
|
+
check_frozen!
|
|
59
|
+
validate_value(new_value)
|
|
60
|
+
result = with_value(timeout) do
|
|
61
|
+
check_frozen!
|
|
62
|
+
previous = read_value
|
|
63
|
+
write_value(new_value)
|
|
64
|
+
previous
|
|
65
|
+
end
|
|
66
|
+
result.equal?(TIMED_OUT) ? fallback&.call : result
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def store_if_absent(timeout: nil)
|
|
70
|
+
raise LocalJumpError, "no block given" unless block_given?
|
|
71
|
+
check_frozen!
|
|
72
|
+
result = with_value(timeout) do
|
|
73
|
+
check_frozen!
|
|
74
|
+
current = read_value
|
|
75
|
+
next current unless current.nil?
|
|
76
|
+
replacement = yield
|
|
77
|
+
check_frozen!
|
|
78
|
+
validate_value(replacement)
|
|
79
|
+
write_value(replacement)
|
|
80
|
+
replacement
|
|
81
|
+
end
|
|
82
|
+
result.equal?(TIMED_OUT) ? nil : result
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def compare_and_set(expected, replacement, timeout: nil)
|
|
86
|
+
check_frozen!
|
|
87
|
+
validate_value(expected)
|
|
88
|
+
validate_value(replacement)
|
|
89
|
+
result = with_value(timeout) do
|
|
90
|
+
matches = values_equal?(read_value, expected)
|
|
91
|
+
check_frozen!
|
|
92
|
+
next false unless matches
|
|
93
|
+
write_value(replacement)
|
|
94
|
+
true
|
|
95
|
+
end
|
|
96
|
+
!result.equal?(TIMED_OUT) && result
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def update(timeout: nil)
|
|
100
|
+
raise LocalJumpError, "no block given" unless block_given?
|
|
101
|
+
check_frozen!
|
|
102
|
+
result = with_value(timeout) do
|
|
103
|
+
replacement = yield(read_value)
|
|
104
|
+
check_frozen!
|
|
105
|
+
validate_value(replacement)
|
|
106
|
+
write_value(replacement)
|
|
107
|
+
replacement
|
|
108
|
+
end
|
|
109
|
+
result.equal?(TIMED_OUT) ? nil : result
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def upsert(initial, timeout: nil)
|
|
113
|
+
raise LocalJumpError, "no block given" unless block_given?
|
|
114
|
+
check_frozen!
|
|
115
|
+
validate_value(initial)
|
|
116
|
+
result = with_value(timeout) do
|
|
117
|
+
current = read_value
|
|
118
|
+
replacement = current.nil? ? initial : yield(current)
|
|
119
|
+
check_frozen!
|
|
120
|
+
validate_value(replacement)
|
|
121
|
+
write_value(replacement)
|
|
122
|
+
replacement
|
|
123
|
+
end
|
|
124
|
+
result.equal?(TIMED_OUT) ? nil : result
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def wait_until_changed(expected, timeout: nil, &fallback)
|
|
128
|
+
validate_value(expected)
|
|
129
|
+
wait_for_value(expected, timeout, fallback, non_nil: false)
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
def wait_until_non_nil(timeout: nil, &fallback)
|
|
133
|
+
wait_for_value(nil, timeout, fallback, non_nil: true)
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
private
|
|
137
|
+
|
|
138
|
+
def check_frozen!
|
|
139
|
+
return unless frozen?
|
|
140
|
+
|
|
141
|
+
raise FrozenError.new("can't modify frozen #{self.class}", receiver: self)
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
def validate_value(_value); end
|
|
145
|
+
|
|
146
|
+
def with_value(timeout)
|
|
147
|
+
deadline = timeout_deadline(timeout)
|
|
148
|
+
result = TIMED_OUT
|
|
149
|
+
@lock.update(timeout: remaining_timeout(deadline)) do
|
|
150
|
+
result = yield
|
|
151
|
+
nil
|
|
152
|
+
end
|
|
153
|
+
result
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
def wait_for_value(expected, timeout, fallback, non_nil:)
|
|
157
|
+
deadline = timeout_deadline(timeout)
|
|
158
|
+
loop do
|
|
159
|
+
generation = @changes.value
|
|
160
|
+
current = read_value
|
|
161
|
+
return current if non_nil ? !current.nil? : !values_equal?(current, expected)
|
|
162
|
+
# Detect recursive waits without waiting for another owner's update.
|
|
163
|
+
@lock.get(timeout: 0)
|
|
164
|
+
return fallback&.call if remaining_timeout(deadline)&.zero?
|
|
165
|
+
@changes.wait_until_changed(generation, timeout: remaining_timeout(deadline))
|
|
166
|
+
end
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
def values_equal?(left, right)
|
|
170
|
+
return left == right unless compare_by_identity?
|
|
171
|
+
BasicObject.instance_method(:equal?).bind_call(left, right)
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
def timeout_deadline(timeout)
|
|
175
|
+
return if timeout.nil?
|
|
176
|
+
timeout = Float(timeout)
|
|
177
|
+
raise ArgumentError, "timeout must be finite and non-negative" unless timeout.finite? && !timeout.negative?
|
|
178
|
+
Clock.now + timeout
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
def remaining_timeout(deadline)
|
|
182
|
+
return unless deadline
|
|
183
|
+
remaining = deadline - Clock.now
|
|
184
|
+
remaining.negative? ? 0 : remaining
|
|
185
|
+
end
|
|
186
|
+
end
|
|
187
|
+
private_constant :WeakAtomBase
|
|
188
|
+
end
|
|
189
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# shareable_constant_value: literal
|
|
3
|
+
# warn_indent: true
|
|
4
|
+
|
|
5
|
+
require "farce/engine/shared/unshared_weak_atom"
|
|
6
|
+
|
|
7
|
+
module Farce
|
|
8
|
+
# @!visibility private
|
|
9
|
+
module Internal # :nodoc: all
|
|
10
|
+
class WeakAtom < UnsharedWeakAtom
|
|
11
|
+
private
|
|
12
|
+
|
|
13
|
+
def validate_value(value)
|
|
14
|
+
return if Farce::Ractor.shareable?(value)
|
|
15
|
+
|
|
16
|
+
raise Ractor::IsolationError, "value must be Ractor-shareable"
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# shareable_constant_value: literal
|
|
3
|
+
# warn_indent: true
|
|
4
|
+
|
|
5
|
+
require "farce/engine/shared/weak_map/reference"
|
|
6
|
+
|
|
7
|
+
module Farce
|
|
8
|
+
# @!visibility private
|
|
9
|
+
module Internal # :nodoc: all
|
|
10
|
+
# The common cell holds its value directly. The index supplies key ownership.
|
|
11
|
+
# A WeakKeyMap index owns this cell and controls its collection lifetime.
|
|
12
|
+
class UnsharedWeakMapCell < UnsharedWeakMapLock
|
|
13
|
+
attr_reader :token
|
|
14
|
+
|
|
15
|
+
def initialize(key_reference)
|
|
16
|
+
super()
|
|
17
|
+
@token = Object.new
|
|
18
|
+
@key = key_reference
|
|
19
|
+
@value = nil
|
|
20
|
+
@present = @retired = false
|
|
21
|
+
@changes = nil
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def lookup_key
|
|
25
|
+
@mutex.synchronize do
|
|
26
|
+
return [false, nil] if @retired
|
|
27
|
+
alive, key = @key.read
|
|
28
|
+
return [false, nil] unless alive
|
|
29
|
+
[@reserved || !@present || read_value.first, key]
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def state
|
|
34
|
+
@mutex.synchronize do
|
|
35
|
+
return [:retired, false, nil] if @retired
|
|
36
|
+
return [:dead, false, nil] unless @key.read.first
|
|
37
|
+
return [:ok, false, nil] unless @present
|
|
38
|
+
alive, value = read_value
|
|
39
|
+
[alive ? :ok : :dead, alive, value]
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def present? = @mutex.synchronize { @present }
|
|
44
|
+
def retired? = @mutex.synchronize { @retired }
|
|
45
|
+
|
|
46
|
+
def store(value)
|
|
47
|
+
@mutex.synchronize do
|
|
48
|
+
return false if @retired || !@key.read.first
|
|
49
|
+
write_value(value)
|
|
50
|
+
@present = true
|
|
51
|
+
@changes&.broadcast
|
|
52
|
+
true
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def retire = retire_state(force: true).first
|
|
57
|
+
def retire_if_dead = retire_state(force: false)
|
|
58
|
+
|
|
59
|
+
def change_generation
|
|
60
|
+
@mutex.synchronize { (@changes ||= Signal.new).generation }
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def wait_for_change?(observed, deadline)
|
|
64
|
+
timeout = deadline - Clock.now if deadline
|
|
65
|
+
return false if timeout && !timeout.positive?
|
|
66
|
+
!!@changes.wait(observed, timeout:) { false }
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
private
|
|
70
|
+
|
|
71
|
+
def unavailable? = @retired
|
|
72
|
+
def read_value = [true, @value]
|
|
73
|
+
def write_value(value) = @value = value
|
|
74
|
+
|
|
75
|
+
def retire_state(force:)
|
|
76
|
+
@mutex.synchronize do
|
|
77
|
+
return [false, false, nil] if @retired || (!force && @reserved)
|
|
78
|
+
alive, key = @key.read
|
|
79
|
+
return [false, false, nil] if !force && alive && (!@present || read_value.first)
|
|
80
|
+
@retired = true
|
|
81
|
+
@present = false
|
|
82
|
+
@key = @value = nil
|
|
83
|
+
@changes&.broadcast
|
|
84
|
+
@signal&.broadcast
|
|
85
|
+
[true, alive, key]
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
private_constant :UnsharedWeakMapCell
|
|
90
|
+
|
|
91
|
+
class UnsharedWeakValueMapCell < UnsharedWeakMapCell
|
|
92
|
+
private
|
|
93
|
+
|
|
94
|
+
def read_value = @value.read
|
|
95
|
+
def write_value(value) = @value = UnsharedWeakMapWeakReference.for(value)
|
|
96
|
+
end
|
|
97
|
+
private_constant :UnsharedWeakValueMapCell
|
|
98
|
+
end
|
|
99
|
+
end
|
|
@@ -0,0 +1,249 @@
|
|
|
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
|
+
# Shared coordination for indexes. Reference ownership belongs to the
|
|
9
|
+
# concrete index and cell classes, never to flags in this class.
|
|
10
|
+
class UnsharedWeakMapIndex
|
|
11
|
+
attr_reader :change_signal
|
|
12
|
+
|
|
13
|
+
def initialize
|
|
14
|
+
@lock = UnsharedWeakMapLock.new
|
|
15
|
+
@registry_mutex = Mutex.new
|
|
16
|
+
@change_signal = Signal.new
|
|
17
|
+
@sweep_cursor = 0
|
|
18
|
+
@iteration_epoch = 0
|
|
19
|
+
@registry_holes = 0
|
|
20
|
+
reset
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def resolve(key, deadline: nil, create: false)
|
|
24
|
+
completed, result = @lock.synchronize(deadline) do
|
|
25
|
+
data = data_for(key)
|
|
26
|
+
indexed_key = index_key(key)
|
|
27
|
+
entry = data[indexed_key]
|
|
28
|
+
if entry
|
|
29
|
+
retired, = entry.retire_if_dead
|
|
30
|
+
if retired || entry.retired?
|
|
31
|
+
data.delete(indexed_key)
|
|
32
|
+
forget(entry)
|
|
33
|
+
entry = nil
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
created = false
|
|
37
|
+
if !entry && create
|
|
38
|
+
begin
|
|
39
|
+
entry = yield(key_reference(key))
|
|
40
|
+
data[indexed_key] = entry
|
|
41
|
+
register(entry)
|
|
42
|
+
created = true
|
|
43
|
+
@change_signal.broadcast
|
|
44
|
+
ensure
|
|
45
|
+
unless created
|
|
46
|
+
entry&.retire
|
|
47
|
+
entry&.release
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
[entry, created]
|
|
52
|
+
end
|
|
53
|
+
completed ? result : [nil, :timed_out]
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def remove(key, entry)
|
|
57
|
+
@lock.synchronize do
|
|
58
|
+
unlink(entry, key)
|
|
59
|
+
forget(entry)
|
|
60
|
+
end
|
|
61
|
+
@change_signal.broadcast
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def clear
|
|
65
|
+
_, entries = @lock.synchronize do
|
|
66
|
+
entries = snapshot
|
|
67
|
+
reset
|
|
68
|
+
entries
|
|
69
|
+
end
|
|
70
|
+
entries.each(&:retire)
|
|
71
|
+
@change_signal.broadcast
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def sweep(entries)
|
|
75
|
+
stale = entries.reject { it.lookup_key.first }
|
|
76
|
+
return if stale.empty?
|
|
77
|
+
@lock.synchronize { stale.each { sweep_entry(it) } }
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def sweep_one
|
|
81
|
+
entry = @registry_mutex.synchronize do
|
|
82
|
+
next if @entries.empty?
|
|
83
|
+
@sweep_cursor %= @entries.size
|
|
84
|
+
@entries[@sweep_cursor].tap { @sweep_cursor += 1 }
|
|
85
|
+
end
|
|
86
|
+
return unless entry && !entry.lookup_key.first
|
|
87
|
+
@lock.try_synchronize { sweep_entry(entry) }
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def snapshot = @registry_mutex.synchronize { @entries.compact }
|
|
91
|
+
|
|
92
|
+
def live_cursor
|
|
93
|
+
@registry_mutex.synchronize do
|
|
94
|
+
[registry_enumerator, @iteration_epoch]
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def next_live(cursor)
|
|
99
|
+
@registry_mutex.synchronize do
|
|
100
|
+
raise "map structurally changed during live iteration" if cursor[1] != @iteration_epoch
|
|
101
|
+
while (entry = cursor[0].next).nil?
|
|
102
|
+
# Deletion leaves a hole while an array cursor is active.
|
|
103
|
+
end
|
|
104
|
+
entry
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def close_cursor(cursor)
|
|
109
|
+
cursor[0] = nil
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
private
|
|
113
|
+
|
|
114
|
+
def data_for(_key) = @data
|
|
115
|
+
def index_key(key) = key
|
|
116
|
+
def key_reference(key) = UnsharedWeakMapReference.new(key)
|
|
117
|
+
def registry_enumerator = @entries.to_enum(:each)
|
|
118
|
+
|
|
119
|
+
def register(entry)
|
|
120
|
+
@registry_mutex.synchronize do
|
|
121
|
+
@iteration_epoch += 1
|
|
122
|
+
@entries << entry
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def forget(entry)
|
|
127
|
+
@registry_mutex.synchronize do
|
|
128
|
+
if (index = @entries.index(entry))
|
|
129
|
+
@entries[index] = nil
|
|
130
|
+
@registry_holes += 1
|
|
131
|
+
if @registry_holes > @entries.size / 4
|
|
132
|
+
# Existing cursors keep their old array. Retired cells release
|
|
133
|
+
# their keys and values, so abandoning a cursor retains no
|
|
134
|
+
# registration in the map and requires no finalizer.
|
|
135
|
+
@entries = @entries.compact
|
|
136
|
+
@registry_holes = 0
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
def sweep_entry(entry)
|
|
143
|
+
retired, alive, key = entry.retire_if_dead
|
|
144
|
+
return unless retired
|
|
145
|
+
unlink(entry, key) if alive
|
|
146
|
+
forget(entry)
|
|
147
|
+
@change_signal.broadcast
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
def unlink(entry, key)
|
|
151
|
+
data = data_for(key)
|
|
152
|
+
indexed_key = index_key(key)
|
|
153
|
+
data.delete(indexed_key) if data[indexed_key].equal?(entry)
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
private_constant :UnsharedWeakMapIndex
|
|
157
|
+
|
|
158
|
+
# WeakKeyMap is the only owner of heap-keyed cells. The enumerable registry
|
|
159
|
+
# is weak and cannot prolong a cell's lifetime. Immediate keys need a Hash.
|
|
160
|
+
# Comparison and cleanup follow the primitive. JRuby currently uses ==
|
|
161
|
+
# instead of eql?, and JVM runtimes defer stale value cleanup until access.
|
|
162
|
+
class UnsharedWeakKeyMapIndex < UnsharedWeakMapIndex
|
|
163
|
+
def snapshot = @registry_mutex.synchronize { @entries.values }
|
|
164
|
+
|
|
165
|
+
def sweep_one
|
|
166
|
+
return unless @entries.respond_to?(:sweep_one)
|
|
167
|
+
@registry_mutex.synchronize { @entries.sweep_one }
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
private
|
|
171
|
+
|
|
172
|
+
def reset
|
|
173
|
+
@data = ObjectSpace::WeakKeyMap.new
|
|
174
|
+
@immediate = {}
|
|
175
|
+
@registry_mutex.synchronize do
|
|
176
|
+
@entries = if Internal.const_defined?(:ConcurrentWeakRegistry, false)
|
|
177
|
+
ConcurrentWeakRegistry.new
|
|
178
|
+
else
|
|
179
|
+
ObjectSpace::WeakMap.new
|
|
180
|
+
end
|
|
181
|
+
@iteration_epoch += 1
|
|
182
|
+
end
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
def data_for(key) = Internal.garbage_collectable?(key) ? @data : @immediate
|
|
186
|
+
def key_reference(key) = UnsharedWeakMapWeakReference.for(key)
|
|
187
|
+
def registry_enumerator = @entries.to_enum(:each_value)
|
|
188
|
+
|
|
189
|
+
def register(entry)
|
|
190
|
+
@registry_mutex.synchronize do
|
|
191
|
+
@iteration_epoch += 1
|
|
192
|
+
@entries[entry.token] = entry
|
|
193
|
+
end
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
def forget(entry) = @registry_mutex.synchronize { @entries.delete(entry.token) }
|
|
197
|
+
end
|
|
198
|
+
private_constant :UnsharedWeakKeyMapIndex
|
|
199
|
+
|
|
200
|
+
# WeakMap has weak values too, so this index must retain its cells. Its
|
|
201
|
+
# strong side is reclaimed by housekeeping after an identity key disappears.
|
|
202
|
+
class UnsharedWeakIdentityMapIndex < UnsharedWeakMapIndex
|
|
203
|
+
private
|
|
204
|
+
|
|
205
|
+
def reset
|
|
206
|
+
@data = ObjectSpace::WeakMap.new
|
|
207
|
+
@registry_mutex.synchronize do
|
|
208
|
+
@entries = []
|
|
209
|
+
@registry_holes = 0
|
|
210
|
+
@iteration_epoch += 1
|
|
211
|
+
end
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
def key_reference(key) = UnsharedWeakMapWeakReference.for(key)
|
|
215
|
+
end
|
|
216
|
+
private_constant :UnsharedWeakIdentityMapIndex
|
|
217
|
+
|
|
218
|
+
class UnsharedStrongKeyMapIndex < UnsharedWeakMapIndex
|
|
219
|
+
private
|
|
220
|
+
|
|
221
|
+
def reset
|
|
222
|
+
@data = {}
|
|
223
|
+
@registry_mutex.synchronize do
|
|
224
|
+
@entries = []
|
|
225
|
+
@registry_holes = 0
|
|
226
|
+
@iteration_epoch += 1
|
|
227
|
+
end
|
|
228
|
+
end
|
|
229
|
+
end
|
|
230
|
+
private_constant :UnsharedStrongKeyMapIndex
|
|
231
|
+
|
|
232
|
+
class UnsharedStrongIdentityMapIndex < UnsharedStrongKeyMapIndex
|
|
233
|
+
# Ruby numeric identity can differ from a JVM object's identity.
|
|
234
|
+
class Key
|
|
235
|
+
attr_reader :value
|
|
236
|
+
|
|
237
|
+
def initialize(value) = @value = value
|
|
238
|
+
def hash = BasicObject.instance_method(:__id__).bind_call(value)
|
|
239
|
+
def eql?(other) = other.is_a?(Key) && BasicObject.instance_method(:equal?).bind_call(value, other.value)
|
|
240
|
+
end
|
|
241
|
+
private_constant :Key
|
|
242
|
+
|
|
243
|
+
private
|
|
244
|
+
|
|
245
|
+
def index_key(key) = Key.new(key)
|
|
246
|
+
end
|
|
247
|
+
private_constant :UnsharedStrongIdentityMapIndex
|
|
248
|
+
end
|
|
249
|
+
end
|
|
@@ -0,0 +1,80 @@
|
|
|
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
|
+
# A per-key reservation. User code runs without holding the state mutex.
|
|
9
|
+
# The same lock protects creation in a key index, as in Storage::ThreadSafe.
|
|
10
|
+
class UnsharedWeakMapLock
|
|
11
|
+
def initialize
|
|
12
|
+
@mutex = Mutex.new
|
|
13
|
+
@reserved = false
|
|
14
|
+
@owner_fiber = nil
|
|
15
|
+
@owner_thread = nil
|
|
16
|
+
@signal = nil
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def reserve(deadline)
|
|
20
|
+
while true
|
|
21
|
+
signal, observed = @mutex.synchronize do
|
|
22
|
+
return :retired if unavailable?
|
|
23
|
+
unless @reserved
|
|
24
|
+
@reserved = true
|
|
25
|
+
@owner_fiber = Fiber.current
|
|
26
|
+
@owner_thread = Internal.storage_thread(Thread.current)
|
|
27
|
+
return :acquired
|
|
28
|
+
end
|
|
29
|
+
reject_recursive_wait!
|
|
30
|
+
@signal ||= Signal.new
|
|
31
|
+
[@signal, @signal.generation]
|
|
32
|
+
end
|
|
33
|
+
return :timed_out unless ReservationWaiting.wait(signal, observed, deadline)
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def release
|
|
38
|
+
signal = @mutex.synchronize do
|
|
39
|
+
@reserved = false
|
|
40
|
+
@owner_fiber = @owner_thread = nil
|
|
41
|
+
@signal
|
|
42
|
+
end
|
|
43
|
+
signal&.broadcast
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def synchronize(deadline = nil)
|
|
47
|
+
acquired = reserve(deadline) == :acquired
|
|
48
|
+
acquired ? [true, yield] : [false, nil]
|
|
49
|
+
ensure
|
|
50
|
+
release if acquired
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def try_synchronize
|
|
54
|
+
acquired = @mutex.synchronize do
|
|
55
|
+
next false if @reserved
|
|
56
|
+
@reserved = true
|
|
57
|
+
@owner_fiber = Fiber.current
|
|
58
|
+
@owner_thread = Internal.storage_thread(Thread.current)
|
|
59
|
+
true
|
|
60
|
+
end
|
|
61
|
+
acquired ? [true, yield] : [false, nil]
|
|
62
|
+
ensure
|
|
63
|
+
release if acquired
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
private
|
|
67
|
+
|
|
68
|
+
def unavailable? = false
|
|
69
|
+
|
|
70
|
+
def reject_recursive_wait!
|
|
71
|
+
raise ThreadError, "deadlock; recursive weak-map access during an update" if @owner_fiber.equal?(Fiber.current)
|
|
72
|
+
scheduler = Fiber.scheduler if Fiber.respond_to?(:scheduler)
|
|
73
|
+
return unless @owner_thread.equal?(Internal.storage_thread(Thread.current)) && !scheduler
|
|
74
|
+
|
|
75
|
+
raise ThreadError, "deadlock; weak-map update is owned by another unscheduled fiber"
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
private_constant :UnsharedWeakMapLock
|
|
79
|
+
end
|
|
80
|
+
end
|
|
@@ -0,0 +1,33 @@
|
|
|
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 UnsharedWeakMapReference
|
|
9
|
+
def initialize(value) = @value = value
|
|
10
|
+
def read = [true, @value]
|
|
11
|
+
end
|
|
12
|
+
private_constant :UnsharedWeakMapReference
|
|
13
|
+
|
|
14
|
+
# A weak value slot also supplies reverse key lookup for enumeration.
|
|
15
|
+
class UnsharedWeakMapWeakReference
|
|
16
|
+
def self.for(value)
|
|
17
|
+
Internal.garbage_collectable?(value) ? new(value) : UnsharedWeakMapReference.new(value)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def initialize(value)
|
|
21
|
+
@token = Object.new
|
|
22
|
+
@data = ObjectSpace::WeakMap.new
|
|
23
|
+
@data[@token] = value
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def read
|
|
27
|
+
value = @data[@token]
|
|
28
|
+
[!value.nil?, value]
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
private_constant :UnsharedWeakMapWeakReference
|
|
32
|
+
end
|
|
33
|
+
end
|