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,172 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# shareable_constant_value: literal
|
|
3
|
+
# warn_indent: true
|
|
4
|
+
|
|
5
|
+
module Farce
|
|
6
|
+
module Abstract
|
|
7
|
+
# @abstract A set maintained in ascending comparator order.
|
|
8
|
+
# Membership, insertion, and deletion use `<=>`. Two elements occupy the
|
|
9
|
+
# same slot when their comparison returns zero. Elements must be mutually
|
|
10
|
+
# comparable and must not change their ordering while stored. Incompatible
|
|
11
|
+
# elements raise during insertion or lookup.
|
|
12
|
+
class SortedSet < Set
|
|
13
|
+
# Construct a set in ascending comparator order.
|
|
14
|
+
# @overload initialize(enumerable = nil, normalize: nil, compare_by_identity: false, mode: :copy, **options)
|
|
15
|
+
# @param enumerable [#each, nil] The initial elements, or nil for an empty set.
|
|
16
|
+
# @param normalize [Symbol, Proc, Hash, Farce::Abstract::Map, nil] The element normalizer.
|
|
17
|
+
# @param compare_by_identity [Boolean] Must be false. Membership uses <=>.
|
|
18
|
+
# @param mode [Symbol] The default transfer mode. Only Farce::SortedSet accepts
|
|
19
|
+
# this option, which defaults to :copy.
|
|
20
|
+
# @param options [Hash] Additional options for the selected variant.
|
|
21
|
+
# @option options [Symbol] scope (:ractor) The scope used by Farce::Local::SortedSet.
|
|
22
|
+
# @yield [element] Optionally transform each initial element before normalization.
|
|
23
|
+
# @yieldparam element [BasicObject] An element from enumerable.
|
|
24
|
+
# @yieldreturn [BasicObject] The comparable element to normalize and store.
|
|
25
|
+
# @return [Farce::Abstract::SortedSet] The new sorted set.
|
|
26
|
+
# @raise [ArgumentError] If compare_by_identity is true.
|
|
27
|
+
# @see Farce::Abstract::Set#initialize Additional options for Local variants.
|
|
28
|
+
def initialize(
|
|
29
|
+
enumerable = nil, normalize: nil, compare_by_identity: false, mode: UNDEFINED, **, &transform
|
|
30
|
+
)
|
|
31
|
+
raise ArgumentError, "sorted sets do not support identity comparison" if compare_by_identity
|
|
32
|
+
super(enumerable, normalize:, compare_by_identity: false, mode:, **, &transform)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# @api private
|
|
36
|
+
def transaction_wrapper(transaction) = Transaction::SortedSet.new(transaction, self, @map)
|
|
37
|
+
|
|
38
|
+
# Return whether this set has comparator-equivalent members in the same order.
|
|
39
|
+
# @param other [BasicObject] The object to compare against.
|
|
40
|
+
# @return [Boolean] Whether other is a Farce sorted set with comparator-equivalent members.
|
|
41
|
+
def ==(other)
|
|
42
|
+
return true if equal?(other)
|
|
43
|
+
return false unless other.is_a?(Abstract::SortedSet) && size == other.size
|
|
44
|
+
ordered_keys.zip(other.ordered_keys).all? { comparator_equal?(_1, _2) }
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# Compare canonical ordered members using eql?.
|
|
48
|
+
# @param other [BasicObject] The object to compare against.
|
|
49
|
+
# @return [Boolean] Whether other is a Farce sorted set with eql? ordered members.
|
|
50
|
+
def eql?(other)
|
|
51
|
+
return true if equal?(other)
|
|
52
|
+
other.is_a?(Abstract::SortedSet) && ordered_keys.eql?(other.ordered_keys)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# Return a hash derived from the canonical ordered members.
|
|
56
|
+
# @return [Integer] The hash code.
|
|
57
|
+
def hash = ordered_keys.hash
|
|
58
|
+
|
|
59
|
+
# Remove every comparator-equivalent member yielded by enumerable.
|
|
60
|
+
# @param enumerable [#each] The elements to normalize and remove by comparison.
|
|
61
|
+
# @return [self] The set.
|
|
62
|
+
def subtract(enumerable)
|
|
63
|
+
check_frozen!
|
|
64
|
+
if ordered_compatible?(enumerable)
|
|
65
|
+
enumerable.each_stored { |key, _| @map.delete(key) }
|
|
66
|
+
else
|
|
67
|
+
each_input(enumerable) do |element|
|
|
68
|
+
key = lookup_key(normalize_element(element))
|
|
69
|
+
@map.delete(key) unless MISSING_KEY.equal?(key)
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
self
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# Return a same-kind set containing members present by comparator in every input.
|
|
76
|
+
# @param enumerables [Array<#each>] The collections whose comparator-equivalent members must be present.
|
|
77
|
+
# @return [Farce::Abstract::SortedSet] A new sorted set of the same class with the same settings.
|
|
78
|
+
def intersection(*enumerables)
|
|
79
|
+
indexes = enumerables.map { ordered_index(it) }
|
|
80
|
+
dup.filter_stored! { |key, _| indexes.all? { it.key?(key) } }
|
|
81
|
+
end
|
|
82
|
+
alias & intersection
|
|
83
|
+
|
|
84
|
+
# Return whether every member has a comparator-equivalent member in other.
|
|
85
|
+
# @param other [Farce::Abstract::Set, ::Set] The set to compare against.
|
|
86
|
+
# @return [Boolean] Whether the comparator-based relation holds.
|
|
87
|
+
# @raise [ArgumentError] If other is not a Farce or Ruby set.
|
|
88
|
+
def subset?(other)
|
|
89
|
+
validate_set_like(other)
|
|
90
|
+
index = ordered_index(other)
|
|
91
|
+
size <= index.size && ordered_keys.all? { index.key?(it) }
|
|
92
|
+
end
|
|
93
|
+
alias <= subset?
|
|
94
|
+
|
|
95
|
+
# Return whether this is a proper comparator subset of other.
|
|
96
|
+
# @param other [Farce::Abstract::Set, ::Set] The set to compare against.
|
|
97
|
+
# @return [Boolean] Whether the comparator-based relation holds.
|
|
98
|
+
# @raise [ArgumentError] If other is not a Farce or Ruby set.
|
|
99
|
+
def proper_subset?(other)
|
|
100
|
+
validate_set_like(other)
|
|
101
|
+
index = ordered_index(other)
|
|
102
|
+
size < index.size && ordered_keys.all? { index.key?(it) }
|
|
103
|
+
end
|
|
104
|
+
alias < proper_subset?
|
|
105
|
+
|
|
106
|
+
# Return whether every member of other has a comparator-equivalent member here.
|
|
107
|
+
# @param other [Farce::Abstract::Set, ::Set] The set to compare against.
|
|
108
|
+
# @return [Boolean] Whether the comparator-based relation holds.
|
|
109
|
+
# @raise [ArgumentError] If other is not a Farce or Ruby set.
|
|
110
|
+
def superset?(other)
|
|
111
|
+
validate_set_like(other)
|
|
112
|
+
index = ordered_index(other)
|
|
113
|
+
size >= index.size && index.keys.all? { @map.key?(it) }
|
|
114
|
+
end
|
|
115
|
+
alias >= superset?
|
|
116
|
+
|
|
117
|
+
# Return whether this is a proper comparator superset of other.
|
|
118
|
+
# @param other [Farce::Abstract::Set, ::Set] The set to compare against.
|
|
119
|
+
# @return [Boolean] Whether the comparator-based relation holds.
|
|
120
|
+
# @raise [ArgumentError] If other is not a Farce or Ruby set.
|
|
121
|
+
def proper_superset?(other)
|
|
122
|
+
validate_set_like(other)
|
|
123
|
+
index = ordered_index(other)
|
|
124
|
+
size > index.size && index.keys.all? { @map.key?(it) }
|
|
125
|
+
end
|
|
126
|
+
alias > proper_superset?
|
|
127
|
+
|
|
128
|
+
# Return whether another set has a comparator-equivalent member.
|
|
129
|
+
# @param other [Farce::Abstract::Set, ::Set] The set to compare against.
|
|
130
|
+
# @return [Boolean] Whether the comparator-based relation holds.
|
|
131
|
+
# @raise [ArgumentError] If other is not a Farce or Ruby set.
|
|
132
|
+
def intersect?(other)
|
|
133
|
+
validate_set_like(other)
|
|
134
|
+
index = ordered_index(other)
|
|
135
|
+
left, right = size <= index.size ? [ordered_keys, index] : [index.keys, @map]
|
|
136
|
+
left.any? { right.key?(it) }
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
protected
|
|
140
|
+
|
|
141
|
+
def ordered_keys = @map.keys
|
|
142
|
+
def ordered? = true
|
|
143
|
+
|
|
144
|
+
private
|
|
145
|
+
|
|
146
|
+
def ordered_compatible?(other)
|
|
147
|
+
canonical_compatible?(other) && other.is_a?(Abstract::SortedSet)
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
def ordered_index(enumerable)
|
|
151
|
+
index = new_map(nil, compare_keys_by_identity: false, **copy_map_options(self))
|
|
152
|
+
if ordered_compatible?(enumerable)
|
|
153
|
+
enumerable.each_stored { |key, _| index[key] = true }
|
|
154
|
+
else
|
|
155
|
+
each_input(enumerable) do |element|
|
|
156
|
+
key = lookup_key(normalize_element(element))
|
|
157
|
+
index[key] = true unless MISSING_KEY.equal?(key)
|
|
158
|
+
end
|
|
159
|
+
end
|
|
160
|
+
index
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
def comparator_equal?(left, right)
|
|
164
|
+
comparison = left <=> right
|
|
165
|
+
return comparison.zero? if comparison
|
|
166
|
+
raise ArgumentError, "comparison of #{left.class} with #{right.class} failed"
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
def identity_membership?(_element) = false
|
|
170
|
+
end
|
|
171
|
+
end
|
|
172
|
+
end
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# shareable_constant_value: literal
|
|
3
|
+
# warn_indent: true
|
|
4
|
+
|
|
5
|
+
module Farce
|
|
6
|
+
module Abstract
|
|
7
|
+
# @abstract Shared timer scheduling and blocking behavior.
|
|
8
|
+
class TimerQueue < Queue
|
|
9
|
+
include Internal::BlockingPriorityQueue
|
|
10
|
+
|
|
11
|
+
# @param capacity [Integer, nil] the maximum number of values, or nil for an unbounded queue
|
|
12
|
+
def initialize(capacity: nil, track_age: false)
|
|
13
|
+
super
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# Add a value to become available at the given time.
|
|
17
|
+
# @param value [BasicObject] the value to add
|
|
18
|
+
# @param non_block [Boolean] whether to raise an exception when the queue is at capacity
|
|
19
|
+
# @param timeout [Numeric, nil] maximum number of seconds to wait for capacity
|
|
20
|
+
# @param time_options [Hash{Symbol => Object}] a scheduling option accepted by {Clock.parse}:
|
|
21
|
+
# `at:`, `time:`, `timeout_at:`, `delay:`, `in:`, `offset:`, `wait:`, or `clock:`.
|
|
22
|
+
# With no scheduling option, the value is available immediately. Since `timeout:` controls the capacity wait,
|
|
23
|
+
# use `delay:` or `wait:` to schedule a relative offset.
|
|
24
|
+
# @raise [ThreadError] when the queue is at capacity and non_block is true
|
|
25
|
+
# @return [Boolean] whether the value was added
|
|
26
|
+
def push(value, non_block = false, timeout: nil, **time_options) # rubocop:disable Style/OptionalBooleanParameter
|
|
27
|
+
at = Clock.parse(time_options)
|
|
28
|
+
push_to_storage(at, non_block, value, timeout:)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Remove the earliest value, waiting until its timestamp is reached.
|
|
32
|
+
# @param timeout [Numeric, nil] maximum number of seconds to wait
|
|
33
|
+
# @yield called when the timeout expires first
|
|
34
|
+
# @return [BasicObject, nil] the value or the fallback result
|
|
35
|
+
def pop(non_block = false, timeout: nil) # rubocop:disable Style/OptionalBooleanParameter
|
|
36
|
+
return try_pop { raise ThreadError, "queue empty" } if non_block
|
|
37
|
+
deadline = timeout_at(timeout) unless timeout.nil?
|
|
38
|
+
|
|
39
|
+
while true
|
|
40
|
+
empty = false
|
|
41
|
+
value = internal_queue.pop_before(Clock.now) { empty = true }
|
|
42
|
+
return value unless empty
|
|
43
|
+
return block_given? ? yield : nil if UNDEFINED.equal?(wait_for_timestamp(deadline))
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# Try to add a value without waiting for capacity.
|
|
48
|
+
# @param value [BasicObject] the value to add
|
|
49
|
+
# @param time_options [Hash{Symbol => Object}] a scheduling option accepted by {Clock.parse}:
|
|
50
|
+
# `at:`, `time:`, `timeout_at:`, `delay:`, `in:`, `offset:`, `timeout:`, `wait:`, or `clock:`.
|
|
51
|
+
# With no scheduling option, the value is available immediately.
|
|
52
|
+
# @yield called when the queue is at capacity
|
|
53
|
+
# @return [Boolean, BasicObject] true, or the fallback result when full
|
|
54
|
+
def try_push(value, **time_options)
|
|
55
|
+
at = Clock.parse(time_options)
|
|
56
|
+
return true if internal_queue.push(at, value)
|
|
57
|
+
block_given? ? yield : false
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Try to remove the earliest value only if its timestamp has been reached.
|
|
61
|
+
# @yield called when no value is ready
|
|
62
|
+
# @return [BasicObject, nil] the value or the fallback result
|
|
63
|
+
def try_pop(&) = internal_queue.pop_before(Clock.now, &)
|
|
64
|
+
|
|
65
|
+
# Return the earliest value without removing it, whether or not it is due.
|
|
66
|
+
# @yield called when the queue is empty
|
|
67
|
+
# @return [BasicObject, nil] the value or the fallback result
|
|
68
|
+
def peek(&) = internal_queue.peek(&)
|
|
69
|
+
|
|
70
|
+
# Return the earliest timestamp without removing it.
|
|
71
|
+
# @yield called when the queue is empty
|
|
72
|
+
# @return [Float, nil] the timestamp or the fallback result
|
|
73
|
+
def first_timestamp(&) = internal_queue.peek_priority(&)
|
|
74
|
+
|
|
75
|
+
# Return the latest timestamp without removing it.
|
|
76
|
+
# @yield called when the queue is empty
|
|
77
|
+
# @return [Float, nil] the timestamp or the fallback result
|
|
78
|
+
def last_timestamp(&) = internal_queue.peek_last_priority(&)
|
|
79
|
+
|
|
80
|
+
# Checks whether the earliest timestamp has been reached.
|
|
81
|
+
# @param leeway [Numeric] how much leeway to allow for clock drift and scheduling delays
|
|
82
|
+
def overdue?(leeway: 0)
|
|
83
|
+
return false unless timestamp = first_timestamp
|
|
84
|
+
timestamp <= Clock.in(leeway)
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# Returns how long the earliest timestamp has been overdue, or nil if it is not yet due.
|
|
88
|
+
# @param leeway [Numeric] how much leeway to allow for clock drift and scheduling delays
|
|
89
|
+
# @return [Float, nil] the number of seconds overdue, or nil if the earliest timestamp is not yet due
|
|
90
|
+
def overdue_by(leeway: 0)
|
|
91
|
+
return unless timestamp = first_timestamp
|
|
92
|
+
delay = Clock.now - timestamp
|
|
93
|
+
delay > leeway ? delay : nil
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
# Wait until the earliest timestamp is reached without removing its value.
|
|
97
|
+
# @param timeout [Numeric, nil] maximum number of seconds to wait
|
|
98
|
+
# @return [Boolean] whether a value is ready
|
|
99
|
+
def wait_pop(timeout: nil) # rubocop:disable Naming/PredicateMethod
|
|
100
|
+
!UNDEFINED.equal?(wait_for_timestamp(timeout_at(timeout)))
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
# Delete the oldest matching value at the exact time.
|
|
104
|
+
# @param value [BasicObject] the value to delete
|
|
105
|
+
# @param at [Numeric, Time] the exact time to search
|
|
106
|
+
# @param compare_by_identity [Boolean] compare values by identity instead of equality
|
|
107
|
+
# @return [Boolean] whether a value was deleted
|
|
108
|
+
def delete(value, at:, compare_by_identity: false)
|
|
109
|
+
delete_from_storage(Clock.at(at), value, compare_by_identity:)
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
private
|
|
113
|
+
|
|
114
|
+
def wait_for_timestamp(deadline)
|
|
115
|
+
while true
|
|
116
|
+
generation = internal_signal.generation
|
|
117
|
+
timestamp = internal_queue.peek_priority
|
|
118
|
+
wake_after = nil
|
|
119
|
+
if timestamp
|
|
120
|
+
delay = timestamp - Clock.now
|
|
121
|
+
return timestamp unless delay.positive?
|
|
122
|
+
wake_after = delay if delay.finite?
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
remaining = remaining_timeout(deadline)
|
|
126
|
+
|
|
127
|
+
wait_for = wake_after && remaining ?
|
|
128
|
+
(wake_after < remaining ? wake_after : remaining) :
|
|
129
|
+
wake_after || remaining
|
|
130
|
+
changed = internal_signal.wait(generation, timeout: wait_for)
|
|
131
|
+
return UNDEFINED if !changed && deadline && remaining_timeout(deadline).zero?
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
end
|
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# shareable_constant_value: literal
|
|
3
|
+
# warn_indent: true
|
|
4
|
+
|
|
5
|
+
module Farce
|
|
6
|
+
module Abstract
|
|
7
|
+
# @abstract Superclass for all tree map implementations.
|
|
8
|
+
#
|
|
9
|
+
# You can think of a tree map as a sorted hash (sorted by key).
|
|
10
|
+
# Under the hood, tree maps are implemented as a balanced binary search tree.
|
|
11
|
+
#
|
|
12
|
+
# This means lookups, insertions, and deletions are O(log n) operations (vs O(1) for a hash map).
|
|
13
|
+
# This is much slower than a hash map, but much faster than ad hoc sorting of the map.
|
|
14
|
+
class TreeMap < Map
|
|
15
|
+
include DuplicableMap
|
|
16
|
+
|
|
17
|
+
# @note Subclasses may accept additional, optional arguments (usually keyword arguments) to configure the map.
|
|
18
|
+
# @param entries [Hash, Array<Array(BasicObject, BasicObject)>, Map, #each, nil]
|
|
19
|
+
# Optional initial entries for the map. Needs to implement #each and yield key-value pairs.
|
|
20
|
+
# If nil, the map will be empty.
|
|
21
|
+
# @!macro key_normalization
|
|
22
|
+
def initialize(entries = nil, normalize_keys: nil, **keyword_entries)
|
|
23
|
+
unless keyword_entries.empty?
|
|
24
|
+
raise ArgumentError, "entries given as both positional and keyword arguments" unless entries.nil?
|
|
25
|
+
entries = keyword_entries
|
|
26
|
+
end
|
|
27
|
+
entries = convert_entries(entries)
|
|
28
|
+
@map = new_tree_map
|
|
29
|
+
@key_locks = new_key_locks
|
|
30
|
+
restoring = Internal::KeyNormalizer.restoration?(normalize_keys)
|
|
31
|
+
normalizer = Internal::KeyNormalizer.build(
|
|
32
|
+
normalize_keys,
|
|
33
|
+
shareable: normalize_keys && Internal::KeyNormalizer.shareable_target?(self),
|
|
34
|
+
)
|
|
35
|
+
Internal::KeyNormalizer.install(self, normalizer, Internal::KeyNormalizer::TreeOperations) unless restoring
|
|
36
|
+
entries&.each { self[_1] = _2 }
|
|
37
|
+
Internal::KeyNormalizer.install(self, normalizer, Internal::KeyNormalizer::TreeOperations) if restoring
|
|
38
|
+
super()
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# @api private
|
|
42
|
+
def transaction_wrapper(transaction)
|
|
43
|
+
transaction.enlist(transaction_key_locks)
|
|
44
|
+
manager = @manager if defined?(@manager)
|
|
45
|
+
Transaction::TreeMap.new(transaction, self, internal_map, manager:)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# (see Map#[])
|
|
49
|
+
def [](key) = unwrap_value(internal_map[prepare_key(key)])
|
|
50
|
+
|
|
51
|
+
# (see Map#[]=)
|
|
52
|
+
def []=(key, value)
|
|
53
|
+
key = internal_map.prepare_key(prepare_key(key))
|
|
54
|
+
with_key_lock(key) { internal_map[key] = wrap_value(value) }
|
|
55
|
+
value
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# Return an existing value, or store the block result for an absent key.
|
|
59
|
+
# Concurrent callers for equally ordered keys share one initialization.
|
|
60
|
+
# The block runs without holding the map's structural lock. Other keys
|
|
61
|
+
# remain accessible. Assigning the same key waits for initialization.
|
|
62
|
+
# Deletion or clearing can precede a pending initialization's insertion.
|
|
63
|
+
# Unsafe maps require callers to provide their own synchronization.
|
|
64
|
+
# @param key [BasicObject] The key to retrieve or initialize.
|
|
65
|
+
# @yieldreturn [BasicObject] The value to store.
|
|
66
|
+
# @return [BasicObject] The existing or newly stored value.
|
|
67
|
+
# @raise [LocalJumpError] If no block is given, even when the key exists.
|
|
68
|
+
# @raise [ThreadError] If initialization recursively accesses its own gate.
|
|
69
|
+
def store_if_absent(key)
|
|
70
|
+
raise LocalJumpError, "no block given" unless block_given?
|
|
71
|
+
|
|
72
|
+
map = internal_map
|
|
73
|
+
key = map.prepare_key(prepare_key(key))
|
|
74
|
+
found = true
|
|
75
|
+
existing = map.fetch(key) { found = false }
|
|
76
|
+
return unwrap_value(existing) if found
|
|
77
|
+
|
|
78
|
+
with_key_lock(key) do
|
|
79
|
+
stored = map.fetch(key) do
|
|
80
|
+
value = yield
|
|
81
|
+
wrapped = wrap_value(value)
|
|
82
|
+
map[key] = wrapped
|
|
83
|
+
return unwrap_value(wrapped)
|
|
84
|
+
end
|
|
85
|
+
unwrap_value(stored)
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
# (see Map#delete)
|
|
90
|
+
def delete(key) = unwrap_value(internal_map.delete(prepare_key(key)))
|
|
91
|
+
|
|
92
|
+
# (see Map#empty?)
|
|
93
|
+
def empty? = internal_map.empty?
|
|
94
|
+
|
|
95
|
+
# (see Map#fetch)
|
|
96
|
+
def fetch(*arguments)
|
|
97
|
+
unless arguments.length.between?(1, 2)
|
|
98
|
+
raise ArgumentError, "wrong number of arguments (given #{arguments.length}, expected 1..2)"
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
key, default = arguments
|
|
102
|
+
key = prepare_key(key)
|
|
103
|
+
warn "block supersedes default value argument", uplevel: 1 if block_given? && arguments.length == 2
|
|
104
|
+
value = internal_map.fetch(key) do
|
|
105
|
+
return yield(key) if block_given?
|
|
106
|
+
return default if arguments.length == 2
|
|
107
|
+
raise KeyError.new("key not found: #{key.inspect}", receiver: self, key: key)
|
|
108
|
+
end
|
|
109
|
+
unwrap_value(value)
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
# Return the smallest key according to the map's ordering.
|
|
113
|
+
# @return [BasicObject, nil] The first key, or nil if the map is empty.
|
|
114
|
+
def first_key = internal_map.first_key
|
|
115
|
+
|
|
116
|
+
# (see Map#getkey)
|
|
117
|
+
def getkey(key) = internal_map.getkey(prepare_key(key))
|
|
118
|
+
|
|
119
|
+
# (see Map#key?)
|
|
120
|
+
def key?(key) = internal_map.key?(prepare_key(key))
|
|
121
|
+
|
|
122
|
+
# Return the largest key according to the map's ordering.
|
|
123
|
+
# @return [BasicObject, nil] The last key, or nil if the map is empty.
|
|
124
|
+
def last_key = internal_map.last_key
|
|
125
|
+
|
|
126
|
+
# (see Map#size)
|
|
127
|
+
def length = internal_map.length
|
|
128
|
+
|
|
129
|
+
# Remove and return the entry with the largest key according to the map's ordering.
|
|
130
|
+
# @return [Array(BasicObject, BasicObject), nil] The last key-value pair, or nil if the map is empty.
|
|
131
|
+
def pop
|
|
132
|
+
pair = internal_map.pop
|
|
133
|
+
[pair.first, unwrap_value(pair.last)] if pair
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
# Remove and return the entry with the smallest key according to the map's ordering.
|
|
137
|
+
# @return [Array(BasicObject, BasicObject), nil] The first key-value pair, or nil if the map is empty.
|
|
138
|
+
def shift
|
|
139
|
+
pair = internal_map.shift
|
|
140
|
+
[pair.first, unwrap_value(pair.last)] if pair
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
# (see Map#size)
|
|
144
|
+
def size = internal_map.size
|
|
145
|
+
|
|
146
|
+
# Remove all entries from the map.
|
|
147
|
+
# @return [self]
|
|
148
|
+
def clear
|
|
149
|
+
internal_map.clear
|
|
150
|
+
self
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
# Iterate over the map's key-value pairs.
|
|
154
|
+
# Order is guaranteed to be from smallest to largest key according.
|
|
155
|
+
#
|
|
156
|
+
# @overload each
|
|
157
|
+
# @yield [pair] Called once for each entry.
|
|
158
|
+
# @yieldparam pair [Array<BasicObject>] A two-element `[key, value]` pair.
|
|
159
|
+
# @return [self]
|
|
160
|
+
# @overload each
|
|
161
|
+
# @return [Enumerator] An enumerator over two-element `[key, value]` pairs.
|
|
162
|
+
def each
|
|
163
|
+
return enum_for(__method__) unless block_given?
|
|
164
|
+
internal_map.each { |key, value| yield [key, unwrap_value(value)] }
|
|
165
|
+
self
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
# Iterate incrementally in key order without allocating a full snapshot.
|
|
169
|
+
# Deletion and value replacement between yields are permitted. Insertion
|
|
170
|
+
# or clearing can invalidate traversal and raise RuntimeError. No tree
|
|
171
|
+
# lock is held while the block runs.
|
|
172
|
+
# @api private
|
|
173
|
+
def each_live
|
|
174
|
+
return enum_for(__method__) { size } unless block_given?
|
|
175
|
+
internal_map.each_live { |key, value| yield [key, unwrap_value(value)] }
|
|
176
|
+
self
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
alias each_pair each
|
|
180
|
+
|
|
181
|
+
# Iterate over the keys currently stored in the map.
|
|
182
|
+
# Keys are yielded from smallest to largest.
|
|
183
|
+
#
|
|
184
|
+
# @overload each_key
|
|
185
|
+
# @yield [key] Called once for each stored key.
|
|
186
|
+
# @yieldparam key [BasicObject] A stored key.
|
|
187
|
+
# @return [self]
|
|
188
|
+
# @overload each_key
|
|
189
|
+
# @return [Enumerator] An enumerator over the stored keys.
|
|
190
|
+
def each_key
|
|
191
|
+
return enum_for(__method__) unless block_given?
|
|
192
|
+
each { |key, _| yield key }
|
|
193
|
+
self
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
# Iterate over the values currently stored in the map.
|
|
197
|
+
# Values are yielded in the order of their corresponding keys (from smallest to largest).
|
|
198
|
+
#
|
|
199
|
+
# @overload each_value
|
|
200
|
+
# @yield [value] Called once for each stored value.
|
|
201
|
+
# @yieldparam value [BasicObject] A stored value.
|
|
202
|
+
# @return [self]
|
|
203
|
+
# @overload each_value
|
|
204
|
+
# @return [Enumerator] An enumerator over the stored values.
|
|
205
|
+
def each_value
|
|
206
|
+
return enum_for(__method__) unless block_given?
|
|
207
|
+
each { |_, value| yield value }
|
|
208
|
+
self
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
# Return the keys currently stored in the map.
|
|
212
|
+
# The result is frozen and ordered from smallest to largest key.
|
|
213
|
+
# @return [Array<BasicObject>] The keys currently stored in the map, in order from smallest to largest.
|
|
214
|
+
def keys = each_key.to_a.freeze
|
|
215
|
+
|
|
216
|
+
# Return the values currently stored in the map.
|
|
217
|
+
# The result is frozen and ordered according to the order of their corresponding keys (from smallest to largest).
|
|
218
|
+
# @return [Array<BasicObject>] The values currently stored in the map.
|
|
219
|
+
def values = each_value.to_a.freeze
|
|
220
|
+
|
|
221
|
+
# TreeMap keys always have to be Ractor-shareable.
|
|
222
|
+
# Mutable strings are accepted however and will be converted to an immutable string.
|
|
223
|
+
# @return [true]
|
|
224
|
+
def shareable_keys? = true
|
|
225
|
+
|
|
226
|
+
# TreeMap keys are compared by their ordering, never by identity.
|
|
227
|
+
# @return [false]
|
|
228
|
+
def compare_keys_by_identity? = false
|
|
229
|
+
|
|
230
|
+
# TreeMap values are compared by equality, never by identity.
|
|
231
|
+
# @return [false]
|
|
232
|
+
def compare_values_by_identity? = false
|
|
233
|
+
|
|
234
|
+
protected
|
|
235
|
+
|
|
236
|
+
def transaction_key_locks = @key_locks
|
|
237
|
+
|
|
238
|
+
private
|
|
239
|
+
|
|
240
|
+
def copy_map_backend(source, empty: false)
|
|
241
|
+
copied = source.class.new
|
|
242
|
+
source.each { |key, value| copied[key] = value } unless empty
|
|
243
|
+
copied
|
|
244
|
+
end
|
|
245
|
+
|
|
246
|
+
def install_copied_map(map)
|
|
247
|
+
super
|
|
248
|
+
@key_locks = new_key_locks
|
|
249
|
+
end
|
|
250
|
+
|
|
251
|
+
def each_for_inspect(&) = each(&)
|
|
252
|
+
|
|
253
|
+
def prepare_key(key)
|
|
254
|
+
return key if String === key || Ractor.shareable?(key)
|
|
255
|
+
raise Ractor::IsolationError, "key must be Ractor-shareable"
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
def new_key_locks = Internal::OrderedKeyLockMap.new
|
|
259
|
+
def with_key_lock(key, &) = @key_locks.synchronize(key, &)
|
|
260
|
+
|
|
261
|
+
# simplecov:disable
|
|
262
|
+
def new_tree_map(...)
|
|
263
|
+
raise "subclass failed to implement #new_tree_map" unless instance_of?(TreeMap)
|
|
264
|
+
raise NoMethodError, "Farce::Abstract::TreeMap should not be instantiated directly. Use a subclass instead."
|
|
265
|
+
end
|
|
266
|
+
# simplecov:enable
|
|
267
|
+
end
|
|
268
|
+
end
|
|
269
|
+
end
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# shareable_constant_value: literal
|
|
3
|
+
# warn_indent: true
|
|
4
|
+
|
|
5
|
+
module Farce
|
|
6
|
+
module Abstract
|
|
7
|
+
# Mixin to indicate {#value} can safely be called on an object.
|
|
8
|
+
#
|
|
9
|
+
# @!method value
|
|
10
|
+
# @abstract
|
|
11
|
+
# Can be called multiple times without argument.
|
|
12
|
+
# Some subclasses may allow optional arguments, especially a `timeout` keyword argument.
|
|
13
|
+
# This method may block.
|
|
14
|
+
# @return [BasicObject] The underlying value of the object.
|
|
15
|
+
#
|
|
16
|
+
# @abstract
|
|
17
|
+
module Value
|
|
18
|
+
Internal::Atom.include(self)
|
|
19
|
+
|
|
20
|
+
# Unwraps nested {Value} objects to get the underlying value.
|
|
21
|
+
#
|
|
22
|
+
# @example Nested unwrapping
|
|
23
|
+
# MyValue = Data.define(:value) { include Farce::Abstract::Value }
|
|
24
|
+
# inner = MyValue.new(42)
|
|
25
|
+
# outer = MyValue.new(inner)
|
|
26
|
+
# outer.unwrap # => 42
|
|
27
|
+
#
|
|
28
|
+
# @example Cycle detection
|
|
29
|
+
# MyValue = Struct.new(:name, :value) { include Farce::Abstract::Value }
|
|
30
|
+
#
|
|
31
|
+
# a = MyValue.new("A")
|
|
32
|
+
# b = MyValue.new("B", a)
|
|
33
|
+
# c = MyValue.new("C", a)
|
|
34
|
+
# a.value = b
|
|
35
|
+
#
|
|
36
|
+
# c.unwrap # => nil
|
|
37
|
+
# c.unwrap("default") # => "default"
|
|
38
|
+
#
|
|
39
|
+
# # RuntimeError: cycle detected starting at A
|
|
40
|
+
# c.unwrap { raise "cycle detected starting at #{value.name}" }
|
|
41
|
+
#
|
|
42
|
+
# @overload unwrap(default = nil)
|
|
43
|
+
# @param [BasicObject] default The value to return if a cycle is detected.
|
|
44
|
+
#
|
|
45
|
+
# @overload unwrap
|
|
46
|
+
# @yield [value] Block to call if a cycle is detected.
|
|
47
|
+
# @yieldparam [Value] value The value that has been determined to be the start of the cycle.
|
|
48
|
+
# @yieldreturn [BasicObject] The value to return if a cycle is detected
|
|
49
|
+
#
|
|
50
|
+
# @return [BasicObject] The underlying value of the object, or a default value based on the argument or block.
|
|
51
|
+
def unwrap(default = nil)
|
|
52
|
+
value = value()
|
|
53
|
+
return value unless value.is_a?(Value)
|
|
54
|
+
|
|
55
|
+
seen = ::Set.new.compare_by_identity
|
|
56
|
+
seen << self
|
|
57
|
+
|
|
58
|
+
while value.is_a?(Value)
|
|
59
|
+
return block_given? ? yield(value) : default if seen.include?(value)
|
|
60
|
+
seen << value
|
|
61
|
+
value = value.value
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
value
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|