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,381 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# shareable_constant_value: literal
|
|
3
|
+
# warn_indent: true
|
|
4
|
+
|
|
5
|
+
module Farce
|
|
6
|
+
module Abstract
|
|
7
|
+
# @abstract Super class for {Map maps} with added concurrency features.
|
|
8
|
+
class ConcurrentMap < Map
|
|
9
|
+
include DuplicableMap
|
|
10
|
+
|
|
11
|
+
# @api private
|
|
12
|
+
def transaction_wrapper(transaction) = Transaction::Map.new(transaction, self, internal_map)
|
|
13
|
+
|
|
14
|
+
# (see Map#[])
|
|
15
|
+
def [](key) = internal_map[key]
|
|
16
|
+
|
|
17
|
+
# (see Map#[]=)
|
|
18
|
+
def []=(key, value)
|
|
19
|
+
internal_map[key] = value
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# (see Map#fetch)
|
|
23
|
+
def fetch(*arguments)
|
|
24
|
+
unless arguments.length.between?(1, 2)
|
|
25
|
+
raise ArgumentError, "wrong number of arguments (given #{arguments.length}, expected 1..2)"
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
key, default = arguments
|
|
29
|
+
warn "block supersedes default value argument", uplevel: 1 if block_given? && arguments.length == 2
|
|
30
|
+
internal_map.fetch(key) do
|
|
31
|
+
return yield(key) if block_given?
|
|
32
|
+
return default if arguments.length == 2
|
|
33
|
+
raise KeyError.new("key not found: #{key.inspect}", receiver: self, key: key)
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# Read the value associated with a key, waiting for atomic-update access if necessary.
|
|
38
|
+
# @param key [BasicObject] The key to look up.
|
|
39
|
+
# @param timeout [Numeric, nil] The maximum number of seconds to wait for access.
|
|
40
|
+
# @yield Called if access cannot be acquired before the timeout.
|
|
41
|
+
# @yieldreturn [BasicObject] The fallback value to return.
|
|
42
|
+
# @return [BasicObject, nil]
|
|
43
|
+
# The associated value, nil if the key is absent, or the fallback result if the operation times out.
|
|
44
|
+
def get(key, timeout: nil, &) = internal_map.get(key, timeout:, &)
|
|
45
|
+
|
|
46
|
+
# Associate a value with a key, waiting for atomic-update access if necessary.
|
|
47
|
+
# @param key [BasicObject] The key to store.
|
|
48
|
+
# @param value [BasicObject] The value to store.
|
|
49
|
+
# @param timeout [Numeric, nil] The maximum number of seconds to wait for access.
|
|
50
|
+
# @yield Called if access cannot be acquired before the timeout.
|
|
51
|
+
# @yieldreturn [BasicObject] The fallback value to return.
|
|
52
|
+
# @return [BasicObject, nil] `value` on success, or the fallback result or nil on timeout.
|
|
53
|
+
def store(key, value, timeout: nil, &) = internal_map.store(key, value, timeout:, &)
|
|
54
|
+
|
|
55
|
+
# Replace the value associated with a key and return its previous value.
|
|
56
|
+
# @param key [BasicObject] The key to update.
|
|
57
|
+
# @param replacement [BasicObject] The value to store.
|
|
58
|
+
# @param timeout [Numeric, nil] The maximum number of seconds to wait for access.
|
|
59
|
+
# @yield Called if access cannot be acquired before the timeout.
|
|
60
|
+
# @yieldreturn [BasicObject] The fallback value to return.
|
|
61
|
+
# @return [BasicObject, nil]
|
|
62
|
+
# The previous value, nil if the key was absent, or the fallback result if the operation times out.
|
|
63
|
+
def swap(key, replacement, timeout: nil, &) = internal_map.swap(key, replacement, timeout:, &)
|
|
64
|
+
|
|
65
|
+
# Atomically fetch an existing value or compute and store a value for an absent key.
|
|
66
|
+
# @param key [BasicObject] The key to look up or store.
|
|
67
|
+
# @param timeout [Numeric, nil] The maximum number of seconds to wait for access.
|
|
68
|
+
# @yield Called without arguments when the key is absent.
|
|
69
|
+
# @yieldreturn [BasicObject] The value to store and return.
|
|
70
|
+
# @return [BasicObject, nil] The existing or newly stored value, or nil on timeout.
|
|
71
|
+
# @raise [LocalJumpError] If no block is given.
|
|
72
|
+
def store_if_absent(key, timeout: nil, &) = internal_map.store_if_absent(key, timeout:, &)
|
|
73
|
+
|
|
74
|
+
# Atomically replace a value if the key is present and its current value matches `expected`.
|
|
75
|
+
# The configured value-comparison mode determines whether matching uses equality or identity.
|
|
76
|
+
# @param key [BasicObject] The key to update.
|
|
77
|
+
# @param expected [BasicObject] The value expected to be currently associated with the key.
|
|
78
|
+
# @param replacement [BasicObject] The value to store when the current value matches.
|
|
79
|
+
# @param timeout [Numeric, nil] The maximum number of seconds to wait for access.
|
|
80
|
+
# @return [Boolean] Whether the value was replaced. Returns false on timeout.
|
|
81
|
+
def compare_and_set(key, expected, replacement, timeout: nil)
|
|
82
|
+
internal_map.compare_and_set(key, expected, replacement, timeout:)
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
# Atomically replace the value associated with a key with the block result.
|
|
86
|
+
# The block receives nil when the key is absent or its current value is nil.
|
|
87
|
+
# @param key [BasicObject] The key to update.
|
|
88
|
+
# @param timeout [Numeric, nil] The maximum number of seconds to wait for access.
|
|
89
|
+
# @yield [value] Called after access is acquired.
|
|
90
|
+
# @yieldparam value [BasicObject, nil] The current value, or nil if the key is absent.
|
|
91
|
+
# @yieldreturn [BasicObject] The value to store and return.
|
|
92
|
+
# @return [BasicObject, nil] The newly stored value, or nil on timeout.
|
|
93
|
+
# @raise [LocalJumpError] If no block is given.
|
|
94
|
+
def update(key, timeout: nil, &) = internal_map.update(key, timeout:, &)
|
|
95
|
+
|
|
96
|
+
# Atomically insert `initial` for an absent key or replace an existing value with the block result.
|
|
97
|
+
# The block is not called when the key is absent.
|
|
98
|
+
# @param key [BasicObject] The key to insert or update.
|
|
99
|
+
# @param initial [BasicObject] The value to store when the key is absent.
|
|
100
|
+
# @param timeout [Numeric, nil] The maximum number of seconds to wait for access.
|
|
101
|
+
# @yield [value] Called when the key is present.
|
|
102
|
+
# @yieldparam value [BasicObject] The current value.
|
|
103
|
+
# @yieldreturn [BasicObject] The replacement value to store and return.
|
|
104
|
+
# @return [BasicObject, nil] The initial or replacement value, or nil on timeout.
|
|
105
|
+
# @raise [LocalJumpError] If no block is given.
|
|
106
|
+
def upsert(key, initial, timeout: nil, &) = internal_map.upsert(key, initial, timeout:, &)
|
|
107
|
+
|
|
108
|
+
# Wait until a block condition matches the current value.
|
|
109
|
+
# One timeout budget covers all checks and waits. The block is not interrupted.
|
|
110
|
+
# @yieldparam value [BasicObject, nil] the current value
|
|
111
|
+
# @yieldreturn [Boolean] whether the value matches
|
|
112
|
+
# An absent key is observed as nil.
|
|
113
|
+
# @param key [BasicObject] the key to observe
|
|
114
|
+
# @param timeout [Numeric, nil] the total seconds available
|
|
115
|
+
# @return [BasicObject, nil] the matching value, or nil on timeout
|
|
116
|
+
# @raise [LocalJumpError] if no block is given
|
|
117
|
+
def wait_until(key, timeout: nil, &) = Internal.wait_until(self, key, timeout:, &)
|
|
118
|
+
|
|
119
|
+
# Wait while the block returns a truthy value.
|
|
120
|
+
# @yieldparam value [BasicObject, nil] the current value
|
|
121
|
+
# @yieldreturn [BasicObject] a truthy value to keep waiting, or nil or false to stop
|
|
122
|
+
# @param key [BasicObject] the key to observe
|
|
123
|
+
# @param timeout [Numeric, nil] the total seconds available
|
|
124
|
+
# @return [BasicObject, nil] the value when the condition becomes false, or nil on timeout
|
|
125
|
+
# @raise [LocalJumpError] if no block is given
|
|
126
|
+
def wait_while(key, timeout: nil)
|
|
127
|
+
raise LocalJumpError, "no block given" unless block_given?
|
|
128
|
+
wait_until(key, timeout:) { |value| !yield(value) }
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
# Wait while `object === value` is true.
|
|
132
|
+
# @param key [BasicObject] the key to observe
|
|
133
|
+
# @param object [#===] the pattern to stop matching
|
|
134
|
+
# @param timeout [Numeric, nil] the total seconds available
|
|
135
|
+
# @return [BasicObject, nil] the first nonmatching value, or nil on timeout
|
|
136
|
+
def wait_while_match(key, object, timeout: nil)
|
|
137
|
+
wait_while(key, timeout:) { |value| object === value } # rubocop:disable Style/CaseEquality
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
# (see #wait_until_changed)
|
|
141
|
+
def wait_while_value(...) = wait_until_changed(...)
|
|
142
|
+
|
|
143
|
+
# Wait until the current value equals an object using the configured comparison mode.
|
|
144
|
+
# @param key [BasicObject] the key to observe
|
|
145
|
+
# @param object [BasicObject, nil] the value to compare with the current value
|
|
146
|
+
# @param timeout [Numeric, nil] the total seconds available
|
|
147
|
+
# @return [BasicObject, nil] the matching value, or nil on timeout
|
|
148
|
+
def wait_until_value(key, object, timeout: nil)
|
|
149
|
+
wait_until(key, timeout:) { |value| compare_values_by_identity? ? object.equal?(value) : object == value }
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
# Wait until `object === value` is true.
|
|
153
|
+
# @param object [#===] the pattern to match
|
|
154
|
+
# @param timeout [Numeric, nil] the total seconds available
|
|
155
|
+
# @return [BasicObject, nil] the matching value, or nil on timeout
|
|
156
|
+
def wait_until_match(key, object, timeout: nil)
|
|
157
|
+
wait_until(key, timeout:) { |value| object === value } # rubocop:disable Style/CaseEquality
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
# Wait until the value associated with a key no longer matches `expected`.
|
|
161
|
+
# An absent key is observed as nil. The configured value-comparison mode determines how values are matched.
|
|
162
|
+
# @param key [BasicObject] The key to observe.
|
|
163
|
+
# @param expected [BasicObject] The value to wait to change from.
|
|
164
|
+
# @param timeout [Numeric, nil] The maximum number of seconds to wait.
|
|
165
|
+
# @yield Called if the value has not changed before the timeout.
|
|
166
|
+
# @yieldreturn [BasicObject] The fallback value to return.
|
|
167
|
+
# @return [BasicObject, nil] The changed value, or the fallback result or nil on timeout.
|
|
168
|
+
def wait_until_changed(key, expected, timeout: nil, &)
|
|
169
|
+
internal_map.wait_until_changed(key, expected, timeout:, &)
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
# Wait until a key is associated with a non-nil value.
|
|
173
|
+
# @param key [BasicObject] The key to observe.
|
|
174
|
+
# @param timeout [Numeric, nil] The maximum number of seconds to wait.
|
|
175
|
+
# @yield Called if the value is still nil when the timeout elapses.
|
|
176
|
+
# @yieldreturn [BasicObject] The fallback value to return.
|
|
177
|
+
# @return [BasicObject, nil] The non-nil value, or the fallback result or nil on timeout.
|
|
178
|
+
def wait_until_non_nil(key, timeout: nil, &) = internal_map.wait_until_non_nil(key, timeout:, &)
|
|
179
|
+
|
|
180
|
+
# (see Map#key?)
|
|
181
|
+
def key?(key) = internal_map.key?(key)
|
|
182
|
+
|
|
183
|
+
# (see Map#getkey)
|
|
184
|
+
def getkey(key) = internal_map.getkey(key)
|
|
185
|
+
|
|
186
|
+
# (see Map#size)
|
|
187
|
+
def size = internal_map.size
|
|
188
|
+
|
|
189
|
+
# (see Map#compare_keys_by_identity?)
|
|
190
|
+
def compare_keys_by_identity? = internal_map.compare_keys_by_identity?
|
|
191
|
+
|
|
192
|
+
# (see Map#compare_values_by_identity?)
|
|
193
|
+
def compare_values_by_identity? = internal_map.compare_values_by_identity?
|
|
194
|
+
|
|
195
|
+
# (see Map#keys)
|
|
196
|
+
def keys = internal_map.keys
|
|
197
|
+
|
|
198
|
+
# Return the values currently stored in the map.
|
|
199
|
+
# @return [Array<BasicObject>] A new array of values in iteration order.
|
|
200
|
+
def values = each_value.to_a
|
|
201
|
+
|
|
202
|
+
# Iterate over entries captured when iteration begins.
|
|
203
|
+
# The map can be modified safely from the block.
|
|
204
|
+
# (see Map#each)
|
|
205
|
+
def each(&block)
|
|
206
|
+
return enum_for(__callee__) { size } unless block
|
|
207
|
+
|
|
208
|
+
internal_map.each(&block)
|
|
209
|
+
self
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
# Iterate incrementally without copying every entry first.
|
|
213
|
+
# Entries can be removed or updated by the block. Insertion or clearing
|
|
214
|
+
# may invalidate traversal and raise RuntimeError. No entry lock is held
|
|
215
|
+
# while the block runs. A small batch of entries may be retained at once.
|
|
216
|
+
# @return [self, Enumerator]
|
|
217
|
+
# @api private
|
|
218
|
+
def each_live
|
|
219
|
+
return enum_for(__method__) { size } unless block_given?
|
|
220
|
+
internal_map.each_live { yield it }
|
|
221
|
+
self
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
# (see Map#each_pair)
|
|
225
|
+
alias each_pair each
|
|
226
|
+
|
|
227
|
+
# (see Map#each_key)
|
|
228
|
+
def each_key(&block)
|
|
229
|
+
return enum_for(__callee__) { size } unless block
|
|
230
|
+
|
|
231
|
+
internal_map.each_key(&block)
|
|
232
|
+
self
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
# (see Map#each_value)
|
|
236
|
+
def each_value(&block)
|
|
237
|
+
return enum_for(__callee__) { size } unless block
|
|
238
|
+
|
|
239
|
+
internal_map.each_value(&block)
|
|
240
|
+
self
|
|
241
|
+
end
|
|
242
|
+
|
|
243
|
+
# (see Map#delete)
|
|
244
|
+
def delete(key) = internal_map.delete(key)
|
|
245
|
+
|
|
246
|
+
# (see Map#clear)
|
|
247
|
+
def clear
|
|
248
|
+
internal_map.clear
|
|
249
|
+
self
|
|
250
|
+
end
|
|
251
|
+
|
|
252
|
+
# Delete entries selected by the block and return self.
|
|
253
|
+
#
|
|
254
|
+
# Keys are captured before iteration. Each block and its decision use the existing atomic-update coordination.
|
|
255
|
+
# Blocks see the current value, and keys deleted before their turn are skipped.
|
|
256
|
+
# The whole operation is not atomic. Exceptions leave earlier changes in place.
|
|
257
|
+
# The same reentrancy restrictions as {#update} apply inside each block.
|
|
258
|
+
#
|
|
259
|
+
# @yieldparam key [BasicObject] the stored key
|
|
260
|
+
# @yieldparam value [BasicObject] the current value
|
|
261
|
+
# @return [self, Enumerator]
|
|
262
|
+
def delete_if(&)
|
|
263
|
+
return enum_for(__method__) { size } unless block_given?
|
|
264
|
+
check_frozen! if respond_to?(:check_frozen!, true)
|
|
265
|
+
filter_entries(&)
|
|
266
|
+
self
|
|
267
|
+
end
|
|
268
|
+
|
|
269
|
+
# Like {#delete_if}, but return nil when this operation deletes no entries.
|
|
270
|
+
# @return [self, nil, Enumerator]
|
|
271
|
+
def reject!(&)
|
|
272
|
+
return enum_for(__method__) { size } unless block_given?
|
|
273
|
+
check_frozen! if respond_to?(:check_frozen!, true)
|
|
274
|
+
self if filter_entries(&)
|
|
275
|
+
end
|
|
276
|
+
|
|
277
|
+
# Remove nil values using per-key update coordination. False values are retained.
|
|
278
|
+
# The whole operation is not atomic, as with {#delete_if}.
|
|
279
|
+
# @return [self, nil] self if this operation deletes entries, otherwise nil.
|
|
280
|
+
def compact! = reject! { |_, value| nil.equal?(value) }
|
|
281
|
+
|
|
282
|
+
# Keep entries selected by the block. Uses the same coordination as {#delete_if}.
|
|
283
|
+
# @return [self, Enumerator]
|
|
284
|
+
def keep_if
|
|
285
|
+
return enum_for(__method__) { size } unless block_given?
|
|
286
|
+
check_frozen! if respond_to?(:check_frozen!, true)
|
|
287
|
+
filter_entries { |key, value| !yield(key, value) }
|
|
288
|
+
self
|
|
289
|
+
end
|
|
290
|
+
|
|
291
|
+
# Like {#keep_if}, but return nil when this operation deletes no entries.
|
|
292
|
+
# @return [self, nil, Enumerator]
|
|
293
|
+
def select!
|
|
294
|
+
return enum_for(__method__) { size } unless block_given?
|
|
295
|
+
check_frozen! if respond_to?(:check_frozen!, true)
|
|
296
|
+
self if filter_entries { |key, value| !yield(key, value) }
|
|
297
|
+
end
|
|
298
|
+
alias filter! select!
|
|
299
|
+
|
|
300
|
+
# Replace each existing value with the block result using atomic-update coordination.
|
|
301
|
+
#
|
|
302
|
+
# Keys are captured before iteration. Missing keys are skipped and earlier changes survive exceptions.
|
|
303
|
+
# Results use this map's transfer mode. The same reentrancy restrictions as {#update} apply.
|
|
304
|
+
#
|
|
305
|
+
# @yieldparam value [BasicObject] the current value
|
|
306
|
+
# @return [self, Enumerator]
|
|
307
|
+
def transform_values!
|
|
308
|
+
return enum_for(__method__) { size } unless block_given?
|
|
309
|
+
check_frozen! if respond_to?(:check_frozen!, true)
|
|
310
|
+
keys.each do |key|
|
|
311
|
+
modify_entry(key, canonical: true) do |present, value|
|
|
312
|
+
present ? yield(value) : Internal::MAP_KEEP
|
|
313
|
+
end
|
|
314
|
+
end
|
|
315
|
+
self
|
|
316
|
+
end
|
|
317
|
+
|
|
318
|
+
# Apply input maps in order. A block resolves each collision under atomic-update coordination.
|
|
319
|
+
#
|
|
320
|
+
# Incoming keys use normal lookup rules. Values use this map's transfer mode, including move semantics.
|
|
321
|
+
# The operation is not atomic across keys. Exceptions leave earlier changes in place.
|
|
322
|
+
#
|
|
323
|
+
# @param others [Array<Map, Hash, #to_hash>] mappings to apply in order
|
|
324
|
+
# @yieldparam key [BasicObject] the incoming key
|
|
325
|
+
# @yieldparam old_value [BasicObject] the current value
|
|
326
|
+
# @yieldparam new_value [BasicObject] the incoming value
|
|
327
|
+
# @return [self]
|
|
328
|
+
def merge!(*others)
|
|
329
|
+
check_frozen! if respond_to?(:check_frozen!, true)
|
|
330
|
+
others.each do |other|
|
|
331
|
+
unless Map === other
|
|
332
|
+
other = Hash.try_convert(other) || raise(TypeError, "input must be a Map, Hash, or respond to #to_hash")
|
|
333
|
+
end
|
|
334
|
+
other.each_pair do |key, value|
|
|
335
|
+
if block_given?
|
|
336
|
+
modify_entry(key) { |present, old| present ? yield(key, old, value) : value }
|
|
337
|
+
else
|
|
338
|
+
self[key] = value
|
|
339
|
+
end
|
|
340
|
+
end
|
|
341
|
+
end
|
|
342
|
+
self
|
|
343
|
+
end
|
|
344
|
+
|
|
345
|
+
private
|
|
346
|
+
|
|
347
|
+
def modify_entry(key, canonical: false)
|
|
348
|
+
map = internal_map
|
|
349
|
+
map = map.instance_variable_get(:@map) if canonical && map.is_a?(Internal::KeyNormalizer::ConcurrentMap)
|
|
350
|
+
map.modify(key) do |present, value|
|
|
351
|
+
replacement = yield(present, present ? unwrap_value(value) : nil)
|
|
352
|
+
next replacement if Internal::MAP_KEEP.equal?(replacement) || Internal::MAP_DELETE.equal?(replacement)
|
|
353
|
+
wrap_value(replacement)
|
|
354
|
+
end
|
|
355
|
+
end
|
|
356
|
+
|
|
357
|
+
def filter_entries
|
|
358
|
+
changed = false
|
|
359
|
+
keys.each do |key|
|
|
360
|
+
deleted = modify_entry(key, canonical: true) do |present, value|
|
|
361
|
+
present && yield(key, value) ? Internal::MAP_DELETE : Internal::MAP_KEEP
|
|
362
|
+
end
|
|
363
|
+
changed = true if deleted
|
|
364
|
+
end
|
|
365
|
+
changed
|
|
366
|
+
end
|
|
367
|
+
|
|
368
|
+
def copy_map_backend(source, empty: false)
|
|
369
|
+
raise TypeError, "copy this backend through its public map" unless source
|
|
370
|
+
source = source.instance_variable_get(:@map) if source.is_a?(Internal::KeyNormalizer::ConcurrentMap)
|
|
371
|
+
copied = source.class.new(
|
|
372
|
+
compare_keys_by_identity: source.compare_keys_by_identity?,
|
|
373
|
+
compare_values_by_identity: source.compare_values_by_identity?,
|
|
374
|
+
)
|
|
375
|
+
source.each { |key, value| copied[key] = value } unless empty
|
|
376
|
+
normalizer = instance_variable_get(:@key_normalizer)
|
|
377
|
+
Internal::KeyNormalizer.wrap_concurrent(copied, normalizer)
|
|
378
|
+
end
|
|
379
|
+
end
|
|
380
|
+
end
|
|
381
|
+
end
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# shareable_constant_value: literal
|
|
3
|
+
# warn_indent: true
|
|
4
|
+
|
|
5
|
+
module Farce
|
|
6
|
+
module Abstract
|
|
7
|
+
# @abstract Common numeric interface and atomic operations for counters.
|
|
8
|
+
# @note This is a module rather than a class so {Farce::Counter} can inherit
|
|
9
|
+
# directly from the native counter and avoid delegation overhead.
|
|
10
|
+
module Counter
|
|
11
|
+
include Internal::MarshalSupport::Counter
|
|
12
|
+
include Internal::Copyable
|
|
13
|
+
include Internal::Inspect
|
|
14
|
+
|
|
15
|
+
def self.included(base)
|
|
16
|
+
Internal.prepare_mutable_numeric(base)
|
|
17
|
+
super
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# Numeric's default copying returns self, which is unsuitable for mutable counters.
|
|
21
|
+
define_method(:dup, Kernel.instance_method(:dup))
|
|
22
|
+
define_method(:clone, Kernel.instance_method(:clone))
|
|
23
|
+
|
|
24
|
+
include Value
|
|
25
|
+
include Internal::ValueSerialization
|
|
26
|
+
|
|
27
|
+
# Wait until the current value differs from the expected value.
|
|
28
|
+
# @param expected [Integer] the value to wait to change from
|
|
29
|
+
# @param timeout [Numeric, nil] the total seconds available
|
|
30
|
+
# @yield called when the timeout expires
|
|
31
|
+
# @return [Integer, BasicObject, nil] the changed value or timeout fallback
|
|
32
|
+
def wait_until_changed(expected, timeout: nil)
|
|
33
|
+
signal = change_signal
|
|
34
|
+
Internal.with_timeout(timeout) do |_, deadline|
|
|
35
|
+
observed = signal.generation
|
|
36
|
+
current = value
|
|
37
|
+
return current unless expected == current
|
|
38
|
+
break unless signal.wait(observed, timeout: Internal.remaining_timeout(deadline))
|
|
39
|
+
end
|
|
40
|
+
yield if block_given?
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Wait until a block condition matches the current value.
|
|
44
|
+
# One timeout budget covers all checks and waits. The block is not interrupted.
|
|
45
|
+
# @yieldparam value [Integer] the current value
|
|
46
|
+
# @yieldreturn [Boolean] whether the value matches
|
|
47
|
+
# @param timeout [Numeric, nil] the total seconds available
|
|
48
|
+
# @return [Integer, nil] the matching value, or nil on timeout
|
|
49
|
+
# @raise [LocalJumpError] if no block is given
|
|
50
|
+
def wait_until(timeout: nil, &) = Internal.wait_until(self, timeout:, &)
|
|
51
|
+
|
|
52
|
+
# Wait while the block returns a truthy value.
|
|
53
|
+
# @yieldparam value [Integer] the current value
|
|
54
|
+
# @yieldreturn [BasicObject] a truthy value to keep waiting, or nil or false to stop
|
|
55
|
+
# @param timeout [Numeric, nil] the total seconds available
|
|
56
|
+
# @return [Integer, nil] the value when the condition becomes false, or nil on timeout
|
|
57
|
+
# @raise [LocalJumpError] if no block is given
|
|
58
|
+
def wait_while(timeout: nil)
|
|
59
|
+
raise LocalJumpError, "no block given" unless block_given?
|
|
60
|
+
wait_until(timeout:) { |value| !yield(value) }
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# Wait while `object === value` is true.
|
|
64
|
+
# @param object [#===] the pattern to stop matching
|
|
65
|
+
# @param timeout [Numeric, nil] the total seconds available
|
|
66
|
+
# @return [Integer, nil] the first nonmatching value, or nil on timeout
|
|
67
|
+
def wait_while_match(object, timeout: nil)
|
|
68
|
+
wait_while(timeout:) { |value| object === value } # rubocop:disable Style/CaseEquality
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# (see #wait_until_changed)
|
|
72
|
+
def wait_while_value(...) = wait_until_changed(...)
|
|
73
|
+
|
|
74
|
+
# Wait until `object == value` is true.
|
|
75
|
+
# Counters compare integer values by equality.
|
|
76
|
+
# @param object [BasicObject] the value to compare with the current value
|
|
77
|
+
# @param timeout [Numeric, nil] the total seconds available
|
|
78
|
+
# @return [Integer, nil] the matching value, or nil on timeout
|
|
79
|
+
def wait_until_value(object, timeout: nil)
|
|
80
|
+
wait_until(timeout:) { |value| object == value }
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# Wait until `object === value` is true.
|
|
84
|
+
# @param object [#===] the pattern to match
|
|
85
|
+
# @param timeout [Numeric, nil] the total seconds available
|
|
86
|
+
# @return [Integer, nil] the matching value, or nil on timeout
|
|
87
|
+
def wait_until_match(object, timeout: nil)
|
|
88
|
+
wait_until(timeout:) { |value| object === value } # rubocop:disable Style/CaseEquality
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
# Wait until the value is strictly below the limit.
|
|
92
|
+
# @param limit [Numeric, String, #to_int] the upper bound, converted to an Integer
|
|
93
|
+
# @param timeout [Numeric, nil] the total seconds available
|
|
94
|
+
# @return [Integer, nil] the matching value, or nil on timeout
|
|
95
|
+
def wait_until_below(limit, timeout: nil)
|
|
96
|
+
limit = Integer(limit)
|
|
97
|
+
wait_until(timeout:) { |value| value < limit }
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
# Wait until the value is strictly above the floor.
|
|
101
|
+
# @param floor [Numeric, String, #to_int] the lower bound, converted to an Integer
|
|
102
|
+
# @param timeout [Numeric, nil] the total seconds available
|
|
103
|
+
# @return [Integer, nil] the matching value, or nil on timeout
|
|
104
|
+
def wait_until_above(floor, timeout: nil)
|
|
105
|
+
floor = Integer(floor)
|
|
106
|
+
wait_until(timeout:) { |value| value > floor }
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
# Wait while the value is strictly above the limit.
|
|
110
|
+
# @param limit [Numeric, String, #to_int] the upper bound, converted to an Integer
|
|
111
|
+
# @param timeout [Numeric, nil] the total seconds available
|
|
112
|
+
# @return [Integer, nil] the first value at or below the limit, or nil on timeout
|
|
113
|
+
def wait_while_above(limit, timeout: nil)
|
|
114
|
+
limit = Integer(limit)
|
|
115
|
+
wait_until(timeout:) { |value| value <= limit }
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
# Wait while the value is strictly below the floor.
|
|
119
|
+
# @param floor [Numeric, String, #to_int] the lower bound, converted to an Integer
|
|
120
|
+
# @param timeout [Numeric, nil] the total seconds available
|
|
121
|
+
# @return [Integer, nil] the first value at or above the floor, or nil on timeout
|
|
122
|
+
def wait_while_below(floor, timeout: nil)
|
|
123
|
+
floor = Integer(floor)
|
|
124
|
+
wait_until(timeout:) { |value| value >= floor }
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
# Reset the counter to its initial value.
|
|
128
|
+
# @return [self] Returns self for chaining.
|
|
129
|
+
def reset
|
|
130
|
+
self.value = initial
|
|
131
|
+
self
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
# Increment the counter by one if its current value is below the limit.
|
|
135
|
+
# The check and increment happen atomically.
|
|
136
|
+
# @param [Numeric, String, #to_int] limit The upper bound, converted to an Integer.
|
|
137
|
+
# @return [Boolean] true if the counter changed, otherwise false.
|
|
138
|
+
def increment_if_below(limit) # rubocop:disable Naming/PredicateMethod
|
|
139
|
+
limit = Integer(limit)
|
|
140
|
+
current = value
|
|
141
|
+
|
|
142
|
+
Internal::Freeze.check(self)
|
|
143
|
+
|
|
144
|
+
while current < limit
|
|
145
|
+
return true if compare_and_set(current, current + 1)
|
|
146
|
+
current = value
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
false
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
# Decrement the counter by one if its current value is above the floor.
|
|
153
|
+
# The check and decrement happen atomically.
|
|
154
|
+
# @param [Numeric, String, #to_int] floor The lower bound, converted to an Integer.
|
|
155
|
+
# @return [Boolean] true if the counter changed, otherwise false.
|
|
156
|
+
def decrement_if_above(floor) # rubocop:disable Naming/PredicateMethod
|
|
157
|
+
floor = Integer(floor)
|
|
158
|
+
current = value
|
|
159
|
+
|
|
160
|
+
Internal::Freeze.check(self)
|
|
161
|
+
|
|
162
|
+
while current > floor
|
|
163
|
+
return true if compare_and_set(current, current - 1)
|
|
164
|
+
current = value
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
false
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
# @overload add(by = 1)
|
|
171
|
+
# (see #increment)
|
|
172
|
+
def add(...) = increment(...)
|
|
173
|
+
|
|
174
|
+
methods = Integer.public_instance_methods - Object.public_instance_methods - [:singleton_method_added]
|
|
175
|
+
methods += %i[+ - / * ** <=> coerce rationalize to_i to_int to_f to_c to_r to_s]
|
|
176
|
+
methods.uniq!
|
|
177
|
+
Internal.delegate(self, :value, *methods)
|
|
178
|
+
|
|
179
|
+
# @api private
|
|
180
|
+
def inspect_with(inspector)
|
|
181
|
+
super do
|
|
182
|
+
inspector.breakable
|
|
183
|
+
inspector.object(value)
|
|
184
|
+
end
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
private
|
|
188
|
+
|
|
189
|
+
def method_missing(...) = value.public_send(...)
|
|
190
|
+
def respond_to_missing?(method, ...) = value.respond_to?(method)
|
|
191
|
+
end
|
|
192
|
+
end
|
|
193
|
+
end
|