farce 0 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +9 -0
- data/CODE_OF_CONDUCT.md +26 -0
- data/CONTRIBUTING.md +71 -0
- data/MIT-LICENSE +20 -0
- data/README.md +1525 -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/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 +435 -8
|
@@ -0,0 +1,342 @@
|
|
|
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
|
+
# Coordinates ownership and lifecycle for one leased resource.
|
|
9
|
+
class LeaseState
|
|
10
|
+
OWNED_RESOURCES = Object.new.freeze
|
|
11
|
+
SCOPE_MANAGED_RESOURCES = Object.new.freeze
|
|
12
|
+
AVAILABLE = :available
|
|
13
|
+
EXPLICIT = :explicit
|
|
14
|
+
BLOCK = :block
|
|
15
|
+
RETIRED = :retired
|
|
16
|
+
private_constant :OWNED_RESOURCES, :SCOPE_MANAGED_RESOURCES,
|
|
17
|
+
:AVAILABLE, :EXPLICIT, :BLOCK, :RETIRED
|
|
18
|
+
|
|
19
|
+
def initialize(resource)
|
|
20
|
+
validate_resource!(resource)
|
|
21
|
+
initialize_state
|
|
22
|
+
initialize_resource(resource)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def available? = state == AVAILABLE && !@lock.locked?
|
|
26
|
+
def checked_out? = !retired? && @lock.locked?
|
|
27
|
+
def owned? = !retired? && @lock.owned?
|
|
28
|
+
def retired? = state == RETIRED
|
|
29
|
+
def explicitly_owned? = owned? && state == EXPLICIT
|
|
30
|
+
|
|
31
|
+
# Return the resource held by the current Fiber.
|
|
32
|
+
# @raise [Farce::OwnershipError] unless the current Fiber owns the checkout
|
|
33
|
+
def owned_resource
|
|
34
|
+
raise OwnershipError, "the current Fiber does not own the checkout" unless @lock.owned?
|
|
35
|
+
|
|
36
|
+
owned_resources.fetch(self)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Replace the resource held by an explicit checkout without ending it.
|
|
40
|
+
# @raise [Farce::OwnershipError] unless the current Fiber owns an explicit checkout
|
|
41
|
+
def replace_owned_resource(resource)
|
|
42
|
+
unless @lock.owned? && state == EXPLICIT
|
|
43
|
+
raise OwnershipError, "the current Fiber does not own an explicit checkout"
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
validate_resource!(resource)
|
|
47
|
+
owned_resources[self] = resource
|
|
48
|
+
self
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# Acquire explicitly and complete a caller-provided ownership handoff before interrupts resume.
|
|
52
|
+
def checkout_with_handoff(timeout: nil)
|
|
53
|
+
resource = nil
|
|
54
|
+
begin
|
|
55
|
+
Thread.handle_interrupt(INTERRUPT_MASK) do
|
|
56
|
+
resource = wait_for_resource(timeout, EXPLICIT)
|
|
57
|
+
yield resource
|
|
58
|
+
end
|
|
59
|
+
rescue Exception # rubocop:disable Lint/RescueException
|
|
60
|
+
rollback_acquisition(resource) if resource && @lock.owned? && state == EXPLICIT
|
|
61
|
+
raise
|
|
62
|
+
end
|
|
63
|
+
resource
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# Attempt an explicit checkout and complete an ownership handoff before interrupts resume.
|
|
67
|
+
def try_checkout_with_handoff
|
|
68
|
+
resource = nil
|
|
69
|
+
begin
|
|
70
|
+
Thread.handle_interrupt(INTERRUPT_MASK) do
|
|
71
|
+
resource = try_acquire(EXPLICIT)
|
|
72
|
+
yield resource if resource
|
|
73
|
+
end
|
|
74
|
+
rescue Exception # rubocop:disable Lint/RescueException
|
|
75
|
+
rollback_acquisition(resource) if resource && @lock.owned? && state == EXPLICIT
|
|
76
|
+
raise
|
|
77
|
+
end
|
|
78
|
+
resource
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# Mark an explicit checkout as owned by an automatic cleanup scope.
|
|
82
|
+
def mark_scope_managed
|
|
83
|
+
unless @lock.owned? && state == EXPLICIT
|
|
84
|
+
raise OwnershipError, "the current Fiber does not own an explicit checkout"
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
scope_managed_resources[self] = true
|
|
88
|
+
self
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def scope_managed? = Storage.fiber[SCOPE_MANAGED_RESOURCES, :strong]&.key?(self) || false
|
|
92
|
+
|
|
93
|
+
# Return a checkout through its automatic cleanup scope.
|
|
94
|
+
def checkin_scope(resource)
|
|
95
|
+
checkin_resource(resource, scope: true)
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def checkout(timeout: nil, &block)
|
|
99
|
+
return wait_for_block(timeout, block) if block
|
|
100
|
+
|
|
101
|
+
explicit_checkout { wait_for_resource(timeout, EXPLICIT) }
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def try_checkout(&block)
|
|
105
|
+
if block
|
|
106
|
+
outcome = try_acquire_block(block)
|
|
107
|
+
return outcome.first if outcome
|
|
108
|
+
return
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
explicit_checkout { try_acquire(EXPLICIT) }
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def checkin(resource)
|
|
115
|
+
checkin_resource(resource, scope: false)
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def checkin_resource(resource, scope:)
|
|
119
|
+
raise RetiredLeaseError, "lease has been retired" if retired?
|
|
120
|
+
unless @lock.owned? && state == EXPLICIT
|
|
121
|
+
raise OwnershipError, "the current Fiber does not own an explicit checkout"
|
|
122
|
+
end
|
|
123
|
+
raise OwnershipError, "the checkout belongs to an automatic lease scope" if scope_managed? && !scope
|
|
124
|
+
|
|
125
|
+
validate_resource!(resource)
|
|
126
|
+
Thread.handle_interrupt(INTERRUPT_MASK) do
|
|
127
|
+
return_resource(resource)
|
|
128
|
+
unregister_owned_resource
|
|
129
|
+
release
|
|
130
|
+
end
|
|
131
|
+
self
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
def retire
|
|
135
|
+
return self if retired?
|
|
136
|
+
raise OwnershipError, "the current Fiber does not own the checkout" unless @lock.owned?
|
|
137
|
+
|
|
138
|
+
Thread.handle_interrupt(INTERRUPT_MASK) do
|
|
139
|
+
@state.store(RETIRED)
|
|
140
|
+
unregister_owned_resource
|
|
141
|
+
@owner_thread_id.store(0)
|
|
142
|
+
@lock.unlock
|
|
143
|
+
@signal.broadcast
|
|
144
|
+
end
|
|
145
|
+
self
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
private
|
|
149
|
+
|
|
150
|
+
def wait_for_resource(timeout, kind)
|
|
151
|
+
deadline = timeout_deadline(timeout)
|
|
152
|
+
resource = nil
|
|
153
|
+
|
|
154
|
+
loop do
|
|
155
|
+
observed = @signal.generation
|
|
156
|
+
resource = try_acquire(kind)
|
|
157
|
+
return resource if resource
|
|
158
|
+
|
|
159
|
+
remaining = deadline - Clock.now if deadline
|
|
160
|
+
raise TimeoutError, "lease checkout timed out" if remaining && !remaining.positive?
|
|
161
|
+
reject_unscheduled_fiber_wait!
|
|
162
|
+
interval = wait_interval(remaining)
|
|
163
|
+
changed = Thread.handle_interrupt(Exception => :immediate) do
|
|
164
|
+
@signal.wait(observed, timeout: interval)
|
|
165
|
+
end
|
|
166
|
+
next if changed || interval != remaining
|
|
167
|
+
|
|
168
|
+
raise TimeoutError, "lease checkout timed out"
|
|
169
|
+
end
|
|
170
|
+
rescue Exception # rubocop:disable Lint/RescueException
|
|
171
|
+
rollback_acquisition(resource) if resource && @lock.owned? && state == kind
|
|
172
|
+
raise
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
def wait_for_block(timeout, block)
|
|
176
|
+
deadline = timeout_deadline(timeout)
|
|
177
|
+
|
|
178
|
+
loop do
|
|
179
|
+
observed = @signal.generation
|
|
180
|
+
outcome = try_acquire_block(block)
|
|
181
|
+
return outcome.first if outcome
|
|
182
|
+
|
|
183
|
+
remaining = deadline - Clock.now if deadline
|
|
184
|
+
raise TimeoutError, "lease checkout timed out" if remaining && !remaining.positive?
|
|
185
|
+
reject_unscheduled_fiber_wait!
|
|
186
|
+
interval = wait_interval(remaining)
|
|
187
|
+
changed = Thread.handle_interrupt(Exception => :immediate) do
|
|
188
|
+
@signal.wait(observed, timeout: interval)
|
|
189
|
+
end
|
|
190
|
+
next if changed || interval != remaining
|
|
191
|
+
|
|
192
|
+
raise TimeoutError, "lease checkout timed out"
|
|
193
|
+
end
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
def checkin_block(resource)
|
|
197
|
+
return if retired?
|
|
198
|
+
raise OwnershipError, "the current Fiber does not own this block checkout" unless
|
|
199
|
+
@lock.owned? && state == BLOCK
|
|
200
|
+
|
|
201
|
+
Thread.handle_interrupt(INTERRUPT_MASK) do
|
|
202
|
+
return_resource(resource)
|
|
203
|
+
unregister_owned_resource
|
|
204
|
+
release
|
|
205
|
+
end
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
def initialize_state
|
|
209
|
+
@lock = Lock.new
|
|
210
|
+
@state = Atom.new(AVAILABLE)
|
|
211
|
+
@signal = Signal.new
|
|
212
|
+
@owner_thread_id = Atom.new(0)
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
def state = @state.value
|
|
216
|
+
|
|
217
|
+
def try_acquire_block(block)
|
|
218
|
+
raise RetiredLeaseError, "lease has been retired" if retired?
|
|
219
|
+
raise ThreadError, "deadlock; recursive lease checkout" if @lock.owned?
|
|
220
|
+
|
|
221
|
+
acquired = false
|
|
222
|
+
resource = nil
|
|
223
|
+
Thread.handle_interrupt(INTERRUPT_MASK) do
|
|
224
|
+
acquired = @lock.try_lock
|
|
225
|
+
return unless acquired
|
|
226
|
+
|
|
227
|
+
begin
|
|
228
|
+
raise RetiredLeaseError, "lease has been retired" if retired?
|
|
229
|
+
@owner_thread_id.store(Thread.current.object_id)
|
|
230
|
+
@state.store(BLOCK)
|
|
231
|
+
resource = take_resource
|
|
232
|
+
register_owned_resource(resource)
|
|
233
|
+
[Thread.handle_interrupt(Exception => :immediate) { block.call(resource) }]
|
|
234
|
+
ensure
|
|
235
|
+
if resource
|
|
236
|
+
checkin_block(resource) if @lock.owned? && state == BLOCK
|
|
237
|
+
elsif acquired
|
|
238
|
+
rollback_acquisition(resource)
|
|
239
|
+
end
|
|
240
|
+
end
|
|
241
|
+
end
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
def explicit_checkout
|
|
245
|
+
resource = nil
|
|
246
|
+
begin
|
|
247
|
+
Thread.handle_interrupt(INTERRUPT_MASK) { resource = yield }
|
|
248
|
+
rescue Exception # rubocop:disable Lint/RescueException
|
|
249
|
+
rollback_acquisition(resource) if resource && @lock.owned? && state == EXPLICIT
|
|
250
|
+
raise
|
|
251
|
+
end
|
|
252
|
+
resource
|
|
253
|
+
end
|
|
254
|
+
|
|
255
|
+
def try_acquire(kind)
|
|
256
|
+
raise RetiredLeaseError, "lease has been retired" if retired?
|
|
257
|
+
raise ThreadError, "deadlock; recursive lease checkout" if @lock.owned?
|
|
258
|
+
|
|
259
|
+
acquired = false
|
|
260
|
+
resource = nil
|
|
261
|
+
begin
|
|
262
|
+
Thread.handle_interrupt(INTERRUPT_MASK) do
|
|
263
|
+
acquired = @lock.try_lock
|
|
264
|
+
return unless acquired
|
|
265
|
+
|
|
266
|
+
raise RetiredLeaseError, "lease has been retired" if retired?
|
|
267
|
+
@owner_thread_id.store(Thread.current.object_id)
|
|
268
|
+
@state.store(kind)
|
|
269
|
+
resource = take_resource
|
|
270
|
+
register_owned_resource(resource)
|
|
271
|
+
end
|
|
272
|
+
rescue Exception # rubocop:disable Lint/RescueException
|
|
273
|
+
rollback_acquisition(resource) if acquired
|
|
274
|
+
raise
|
|
275
|
+
end
|
|
276
|
+
resource
|
|
277
|
+
end
|
|
278
|
+
|
|
279
|
+
def release
|
|
280
|
+
@state.store(AVAILABLE)
|
|
281
|
+
@owner_thread_id.store(0)
|
|
282
|
+
@lock.unlock
|
|
283
|
+
@signal.broadcast
|
|
284
|
+
end
|
|
285
|
+
|
|
286
|
+
def rollback_acquisition(resource)
|
|
287
|
+
Thread.handle_interrupt(INTERRUPT_MASK) do
|
|
288
|
+
return_resource(resource) if resource
|
|
289
|
+
unregister_owned_resource
|
|
290
|
+
@state.store(AVAILABLE) unless retired?
|
|
291
|
+
@owner_thread_id.store(0)
|
|
292
|
+
@lock.unlock if @lock.owned?
|
|
293
|
+
@signal.broadcast
|
|
294
|
+
end
|
|
295
|
+
end
|
|
296
|
+
|
|
297
|
+
def reject_unscheduled_fiber_wait!
|
|
298
|
+
return unless @lock.locked? && @owner_thread_id.value == Thread.current.object_id
|
|
299
|
+
return if Fiber.respond_to?(:scheduler) && Fiber.scheduler
|
|
300
|
+
|
|
301
|
+
raise ThreadError, "deadlock; lease is owned by another unscheduled Fiber on the same thread"
|
|
302
|
+
end
|
|
303
|
+
|
|
304
|
+
def wait_interval(remaining)
|
|
305
|
+
LeaseWaiting.wait_interval(remaining)
|
|
306
|
+
end
|
|
307
|
+
|
|
308
|
+
def owned_resources
|
|
309
|
+
Storage.fiber.store_if_absent(OWNED_RESOURCES, mode: :strong) { {}.compare_by_identity }
|
|
310
|
+
end
|
|
311
|
+
|
|
312
|
+
def register_owned_resource(resource)
|
|
313
|
+
owned_resources[self] = resource
|
|
314
|
+
end
|
|
315
|
+
|
|
316
|
+
def unregister_owned_resource
|
|
317
|
+
Storage.fiber[OWNED_RESOURCES, :strong]&.delete(self)
|
|
318
|
+
Storage.fiber[SCOPE_MANAGED_RESOURCES, :strong]&.delete(self)
|
|
319
|
+
end
|
|
320
|
+
|
|
321
|
+
def scope_managed_resources
|
|
322
|
+
Storage.fiber.store_if_absent(SCOPE_MANAGED_RESOURCES, mode: :strong) { {}.compare_by_identity }
|
|
323
|
+
end
|
|
324
|
+
|
|
325
|
+
def timeout_deadline(timeout)
|
|
326
|
+
return if timeout.nil?
|
|
327
|
+
|
|
328
|
+
timeout = Float(timeout)
|
|
329
|
+
unless timeout.finite? && !timeout.negative?
|
|
330
|
+
raise ArgumentError, "timeout must be a finite, non-negative number or nil"
|
|
331
|
+
end
|
|
332
|
+
Clock.now + timeout
|
|
333
|
+
end
|
|
334
|
+
|
|
335
|
+
def validate_resource!(resource)
|
|
336
|
+
return unless nil.equal?(resource) || true.equal?(resource) || false.equal?(resource)
|
|
337
|
+
|
|
338
|
+
raise ArgumentError, "lease resource cannot be nil or boolean"
|
|
339
|
+
end
|
|
340
|
+
end
|
|
341
|
+
end
|
|
342
|
+
end
|
|
@@ -0,0 +1,13 @@
|
|
|
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
|
+
# Selects how long a Lease waits for one signal notification.
|
|
9
|
+
module LeaseWaiting
|
|
10
|
+
def self.wait_interval(remaining) = remaining
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# shareable_constant_value: literal
|
|
3
|
+
# warn_indent: true
|
|
4
|
+
|
|
5
|
+
module Farce
|
|
6
|
+
module Internal # :nodoc: all
|
|
7
|
+
# Shared mode metadata and deletion probes. Reads and writes are specialized
|
|
8
|
+
# in each public class to avoid forwarding overhead on the hot path.
|
|
9
|
+
module ManagedQueue
|
|
10
|
+
DeleteProbe = Data.define(:value, :compare_by_identity, :manager) do
|
|
11
|
+
def ===(stored) = manager.same_value?(stored, value, identity: compare_by_identity)
|
|
12
|
+
end
|
|
13
|
+
private_constant :DeleteProbe
|
|
14
|
+
|
|
15
|
+
def mode = @manager.mode
|
|
16
|
+
|
|
17
|
+
private
|
|
18
|
+
|
|
19
|
+
def delete_from_storage(priority, value, compare_by_identity:)
|
|
20
|
+
comparison_mode = compare_by_identity ? :local : :copy
|
|
21
|
+
value = @manager.wrap(value, mode: comparison_mode)
|
|
22
|
+
@queue.delete_match(priority, DeleteProbe.new(value, compare_by_identity, @manager))
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -0,0 +1,227 @@
|
|
|
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
|
+
module MapValueModes
|
|
9
|
+
NIL_VALUE = Object.new.freeze
|
|
10
|
+
TIMED_OUT = Object.new.freeze
|
|
11
|
+
CANCEL_UPDATE = Object.new.freeze
|
|
12
|
+
private_constant :NIL_VALUE, :TIMED_OUT, :CANCEL_UPDATE
|
|
13
|
+
|
|
14
|
+
# @!macro modes
|
|
15
|
+
# @param initial_mapping [Hash, Farce::Abstract::Map, #each, nil] initial entries with shareable keys
|
|
16
|
+
# @param mode [Symbol] the default value transfer mode
|
|
17
|
+
# @!macro key_normalization
|
|
18
|
+
# @param compare_by_identity [Boolean] the default comparison mode for keys and values
|
|
19
|
+
# @param compare_keys_by_identity [Boolean] whether keys are compared by identity
|
|
20
|
+
# @param compare_values_by_identity [Boolean] whether values are compared by identity
|
|
21
|
+
def initialize(initial_mapping = nil, mode: :copy, normalize_keys: nil, compare_by_identity: false,
|
|
22
|
+
compare_keys_by_identity: compare_by_identity, compare_values_by_identity: compare_by_identity)
|
|
23
|
+
initial_mapping = convert_entries(initial_mapping)
|
|
24
|
+
|
|
25
|
+
identity = BasicObject.instance_method(:equal?)
|
|
26
|
+
valid_comparison = identity.bind_call(compare_values_by_identity, true) ||
|
|
27
|
+
identity.bind_call(compare_values_by_identity, false)
|
|
28
|
+
|
|
29
|
+
raise ArgumentError, "compare_values_by_identity must be a boolean" unless valid_comparison
|
|
30
|
+
@compare_values_by_identity = compare_values_by_identity
|
|
31
|
+
|
|
32
|
+
@manager = new_mode_manager(mode:)
|
|
33
|
+
@map = new_map(compare_by_identity:, compare_keys_by_identity:, compare_values_by_identity: true)
|
|
34
|
+
KeyNormalizer.prepare_concurrent(@map)
|
|
35
|
+
|
|
36
|
+
restoring = KeyNormalizer.restoration?(normalize_keys)
|
|
37
|
+
normalizer = KeyNormalizer.build(normalize_keys, shareable: true)
|
|
38
|
+
KeyNormalizer.install_concurrent(self, normalizer) unless restoring
|
|
39
|
+
|
|
40
|
+
initial_mapping&.each { |key, value| self[key] = value }
|
|
41
|
+
KeyNormalizer.install_concurrent(self, normalizer) if restoring
|
|
42
|
+
super()
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Build a wrapper that preserves this object's value modes.
|
|
46
|
+
def transaction_wrapper(transaction)
|
|
47
|
+
Transaction::Map.new(transaction, self, @map, manager: @manager, nil_value: NIL_VALUE)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# The default transfer mode for values.
|
|
51
|
+
# @return [Symbol]
|
|
52
|
+
def mode = @manager.mode
|
|
53
|
+
def compare_values_by_identity? = @compare_values_by_identity
|
|
54
|
+
def shareable_keys? = true
|
|
55
|
+
def shareable_values? = true
|
|
56
|
+
def [](key) = unwrap_value(@map[key])
|
|
57
|
+
|
|
58
|
+
def []=(key, value)
|
|
59
|
+
key = @map.prepare_mutation_key(key)
|
|
60
|
+
@map.store_prepared(key, wrap_value(value))
|
|
61
|
+
value
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def fetch(*arguments)
|
|
65
|
+
unless arguments.length.between?(1, 2)
|
|
66
|
+
raise ArgumentError, "wrong number of arguments (given #{arguments.length}, expected 1..2)"
|
|
67
|
+
end
|
|
68
|
+
key, default = arguments
|
|
69
|
+
warn "block supersedes default value argument", uplevel: 1 if block_given? && arguments.length == 2
|
|
70
|
+
value = @map.fetch(key) do
|
|
71
|
+
return yield(key) if block_given?
|
|
72
|
+
return default if arguments.length == 2
|
|
73
|
+
raise KeyError.new("key not found: #{key.inspect}", receiver: self, key: key)
|
|
74
|
+
end
|
|
75
|
+
unwrap_value(value)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def get(key, timeout: nil, &) = unwrap_result(@map.get(key, timeout:) { TIMED_OUT }, &)
|
|
79
|
+
|
|
80
|
+
# (see Abstract::ConcurrentMap#store)
|
|
81
|
+
# @param mode [Symbol, nil] the value transfer mode, or nil to use the default
|
|
82
|
+
def store(key, value, mode: nil, timeout: nil, &)
|
|
83
|
+
key = @map.prepare_mutation_key(key)
|
|
84
|
+
unwrap_result(@map.store_prepared(key, wrap_value(value, mode:), timeout:) { TIMED_OUT }, &)
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# (see Abstract::ConcurrentMap#swap)
|
|
88
|
+
# @param mode [Symbol, nil] the replacement's transfer mode, or nil to use the default
|
|
89
|
+
def swap(key, replacement, mode: nil, timeout: nil, &)
|
|
90
|
+
key = @map.prepare_mutation_key(key)
|
|
91
|
+
unwrap_result(@map.swap_prepared(key, wrap_value(replacement, mode:), timeout:) { TIMED_OUT }, &)
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
# (see Abstract::ConcurrentMap#store_if_absent)
|
|
95
|
+
# @param mode [Symbol, nil] the result's transfer mode, or nil to use the default
|
|
96
|
+
def store_if_absent(key, mode: nil, timeout: nil)
|
|
97
|
+
raise LocalJumpError, "no block given" unless block_given?
|
|
98
|
+
@map.check_mutation
|
|
99
|
+
unwrap_value(@map.store_if_absent(key, timeout:) do
|
|
100
|
+
value = yield
|
|
101
|
+
@map.check_mutation
|
|
102
|
+
wrap_value(value, mode:)
|
|
103
|
+
end)
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
# (see Abstract::ConcurrentMap#update)
|
|
107
|
+
# @param mode [Symbol, nil] the result's transfer mode, or nil to use the default
|
|
108
|
+
def update(key, mode: nil, timeout: nil)
|
|
109
|
+
raise LocalJumpError, "no block given" unless block_given?
|
|
110
|
+
@map.check_mutation
|
|
111
|
+
unwrap_value(@map.update(key, timeout:) do |current|
|
|
112
|
+
value = yield(unwrap_value(current))
|
|
113
|
+
@map.check_mutation
|
|
114
|
+
wrap_value(value, mode:)
|
|
115
|
+
end)
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
# (see Abstract::ConcurrentMap#upsert)
|
|
119
|
+
# @param mode [Symbol, nil] the value transfer mode, or nil to use the default
|
|
120
|
+
def upsert(key, initial, mode: nil, timeout: nil)
|
|
121
|
+
raise LocalJumpError, "no block given" unless block_given?
|
|
122
|
+
@map.check_mutation
|
|
123
|
+
unwrap_value(@map.update(key, timeout:) do |current|
|
|
124
|
+
value = nil.equal?(current) ? initial : yield(unwrap_value(current))
|
|
125
|
+
@map.check_mutation
|
|
126
|
+
wrap_value(value, mode:)
|
|
127
|
+
end)
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
# (see Abstract::ConcurrentMap#compare_and_set)
|
|
131
|
+
# @param mode [Symbol, nil] the replacement's transfer mode, or nil to use the default
|
|
132
|
+
def compare_and_set(key, expected, replacement, mode: nil, timeout: nil)
|
|
133
|
+
key = @map.prepare_mutation_key(key)
|
|
134
|
+
expected = wrap_comparison(expected)
|
|
135
|
+
catch(CANCEL_UPDATE) do
|
|
136
|
+
result = @map.update_prepared(key, timeout:) do |current|
|
|
137
|
+
# Abort the update instead of inserting a value for a missing key.
|
|
138
|
+
throw CANCEL_UPDATE, false if nil.equal?(current) || !values_equal?(current, expected)
|
|
139
|
+
@map.check_mutation
|
|
140
|
+
wrap_value(replacement, mode:)
|
|
141
|
+
end
|
|
142
|
+
!nil.equal?(result)
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
def wait_until_changed(key, expected, timeout: nil, &)
|
|
147
|
+
prepared_key = @map.normalize_external_key(key)
|
|
148
|
+
expected = wrap_comparison(expected)
|
|
149
|
+
deadline = Internal.timeout_deadline(timeout)
|
|
150
|
+
loop do
|
|
151
|
+
current = @map.get_prepared(prepared_key, timeout: Internal.remaining_timeout(deadline)) { TIMED_OUT }
|
|
152
|
+
return unwrap_result(current, &) if TIMED_OUT.equal?(current)
|
|
153
|
+
return unwrap_value(current) unless values_equal?(nil.equal?(current) ? NIL_VALUE : current, expected)
|
|
154
|
+
result = @map.wait_until_changed_prepared(
|
|
155
|
+
prepared_key,
|
|
156
|
+
current,
|
|
157
|
+
timeout: Internal.remaining_timeout(deadline),
|
|
158
|
+
) { TIMED_OUT }
|
|
159
|
+
return unwrap_result(result, &) if TIMED_OUT.equal?(result)
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
def wait_until_non_nil(key, timeout: nil, &) = wait_until_changed(key, nil, timeout:, &)
|
|
164
|
+
def delete(key) = unwrap_value(@map.delete(key))
|
|
165
|
+
|
|
166
|
+
def each
|
|
167
|
+
return enum_for(__method__) { size } unless block_given?
|
|
168
|
+
@map.each { |key, value| yield [key, unwrap_value(value)] }
|
|
169
|
+
self
|
|
170
|
+
end
|
|
171
|
+
alias each_pair each
|
|
172
|
+
|
|
173
|
+
def each_live
|
|
174
|
+
return enum_for(__method__) { size } unless block_given?
|
|
175
|
+
@map.each_live { |key, value| yield [key, unwrap_value(value)] }
|
|
176
|
+
self
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
def each_value
|
|
180
|
+
return enum_for(__method__) { size } unless block_given?
|
|
181
|
+
@map.each_value { |value| yield unwrap_value(value) }
|
|
182
|
+
self
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
protected
|
|
186
|
+
|
|
187
|
+
def wrap_value(value, mode: nil) = nil.equal?(value) ? NIL_VALUE : @manager.wrap(value, mode:)
|
|
188
|
+
def unwrap_value(value) = NIL_VALUE.equal?(value) ? nil : @manager.unwrap(value)
|
|
189
|
+
|
|
190
|
+
private
|
|
191
|
+
|
|
192
|
+
def marshal_map_value(value) = super(NIL_VALUE.equal?(value) ? nil : value)
|
|
193
|
+
|
|
194
|
+
def marshal_restore_map_value(data)
|
|
195
|
+
value = super
|
|
196
|
+
nil.equal?(value) ? NIL_VALUE : value
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
def each_for_inspect(&) = @map.each(&)
|
|
200
|
+
|
|
201
|
+
def inspect_pair(inspector, key, value)
|
|
202
|
+
inspector.hash_pair(key, value, @manager) { inspect_value(inspector, value) }
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
def inspect_value(inspector, value)
|
|
206
|
+
value = nil if NIL_VALUE.equal?(value)
|
|
207
|
+
super(inspector, value, @manager)
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
def new_mode_manager(mode:) = marshal_mode_manager(mode)
|
|
211
|
+
|
|
212
|
+
def check_key(key)
|
|
213
|
+
return if Ractor.shareable?(key)
|
|
214
|
+
raise Ractor::IsolationError, "key must be Ractor-shareable"
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
def wrap_comparison(value) = wrap_value(value, mode: compare_values_by_identity? ? :local : :copy)
|
|
218
|
+
|
|
219
|
+
def unwrap_result(result)
|
|
220
|
+
return unwrap_value(result) unless TIMED_OUT.equal?(result)
|
|
221
|
+
yield if block_given?
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
def values_equal?(left, right) = @manager.same_value?(left, right, identity: compare_values_by_identity?)
|
|
225
|
+
end
|
|
226
|
+
end
|
|
227
|
+
end
|