farce 0.0.1.alpha2-x86-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,619 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# shareable_constant_value: literal
|
|
3
|
+
# warn_indent: true
|
|
4
|
+
|
|
5
|
+
module Farce
|
|
6
|
+
# @!visibility private
|
|
7
|
+
module Internal # :nodoc: all
|
|
8
|
+
# Portable correctness backend for bounded maps confined to one Ractor.
|
|
9
|
+
# Production engines may replace it with specialized storage.
|
|
10
|
+
class PortableBoundedMap
|
|
11
|
+
INITIALIZATION_LOCK = Lock.new
|
|
12
|
+
private_constant :INITIALIZATION_LOCK
|
|
13
|
+
|
|
14
|
+
class HashCode
|
|
15
|
+
def initialize(value) = @value = value
|
|
16
|
+
def hash = @value
|
|
17
|
+
|
|
18
|
+
def eql?(other)
|
|
19
|
+
HashCode === other && Integer.instance_method(:==).bind_call(@value, other.value)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
protected attr_reader :value
|
|
23
|
+
end
|
|
24
|
+
private_constant :HashCode
|
|
25
|
+
|
|
26
|
+
class Node
|
|
27
|
+
attr_accessor :key, :hash_code, :collision_index, :value, :previous, :following, :bucket
|
|
28
|
+
|
|
29
|
+
def initialize(key, hash_code, value)
|
|
30
|
+
@key = key
|
|
31
|
+
@hash_code = hash_code
|
|
32
|
+
@collision_index = nil
|
|
33
|
+
@value = value
|
|
34
|
+
@previous = nil
|
|
35
|
+
@following = nil
|
|
36
|
+
@bucket = nil
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
private_constant :Node
|
|
40
|
+
|
|
41
|
+
def initialize(
|
|
42
|
+
max_size:,
|
|
43
|
+
compare_by_identity: false,
|
|
44
|
+
compare_keys_by_identity: compare_by_identity,
|
|
45
|
+
compare_values_by_identity: compare_by_identity
|
|
46
|
+
)
|
|
47
|
+
check_initializable!
|
|
48
|
+
validate_boolean(compare_by_identity, "compare_by_identity")
|
|
49
|
+
validate_boolean(compare_keys_by_identity, "compare_keys_by_identity")
|
|
50
|
+
validate_boolean(compare_values_by_identity, "compare_values_by_identity")
|
|
51
|
+
validate_limit(max_size, "max_size")
|
|
52
|
+
|
|
53
|
+
mutex = Mutex.new
|
|
54
|
+
INITIALIZATION_LOCK.synchronize do
|
|
55
|
+
commit do
|
|
56
|
+
check_initializable!
|
|
57
|
+
@initializing = true
|
|
58
|
+
begin
|
|
59
|
+
@max_size = max_size
|
|
60
|
+
@compare_keys_by_identity = compare_keys_by_identity
|
|
61
|
+
@compare_values_by_identity = compare_values_by_identity
|
|
62
|
+
@buckets = {}
|
|
63
|
+
@size = 0
|
|
64
|
+
@mutex = mutex
|
|
65
|
+
@freeze_state = Flag.new(false)
|
|
66
|
+
initialize_policy
|
|
67
|
+
@initialized = true
|
|
68
|
+
ensure
|
|
69
|
+
remove_instance_variable(:@initializing) if instance_variable_defined?(:@initializing)
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def initialize_copy(other)
|
|
76
|
+
super
|
|
77
|
+
other.send(:synchronize) do
|
|
78
|
+
@max_size = other.instance_variable_get(:@max_size)
|
|
79
|
+
@compare_keys_by_identity = other.instance_variable_get(:@compare_keys_by_identity)
|
|
80
|
+
@compare_values_by_identity = other.instance_variable_get(:@compare_values_by_identity)
|
|
81
|
+
@mutex = Mutex.new
|
|
82
|
+
@freeze_state = Flag.new(false)
|
|
83
|
+
@buckets = {}
|
|
84
|
+
@size = 0
|
|
85
|
+
initialize_policy
|
|
86
|
+
copy_policy_nodes(other)
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def [](key)
|
|
91
|
+
synchronize do
|
|
92
|
+
node = find_node(key)
|
|
93
|
+
return nil unless node
|
|
94
|
+
unless frozen?
|
|
95
|
+
prepared = policy_prepare_access(node)
|
|
96
|
+
commit { policy_commit_access(node, prepared) }
|
|
97
|
+
end
|
|
98
|
+
node.value
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def freeze
|
|
103
|
+
state = @freeze_state
|
|
104
|
+
return super unless state
|
|
105
|
+
|
|
106
|
+
state.set
|
|
107
|
+
self
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def frozen?
|
|
111
|
+
state = @freeze_state
|
|
112
|
+
state ? state.value : super
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def []=(key, value)
|
|
116
|
+
synchronize do
|
|
117
|
+
check_mutable!
|
|
118
|
+
if @max_size.zero?
|
|
119
|
+
key_hash_code(canonical_key(key))
|
|
120
|
+
check_mutable!
|
|
121
|
+
next value
|
|
122
|
+
end
|
|
123
|
+
node = find_node(key)
|
|
124
|
+
if node
|
|
125
|
+
prepared = policy_prepare_access(node)
|
|
126
|
+
check_mutable!
|
|
127
|
+
commit do
|
|
128
|
+
node.value = value
|
|
129
|
+
policy_commit_access(node, prepared)
|
|
130
|
+
end
|
|
131
|
+
else
|
|
132
|
+
victim = policy_victim if @size >= @max_size
|
|
133
|
+
stored_key = canonical_key(key)
|
|
134
|
+
hash_code = key_hash_code(stored_key)
|
|
135
|
+
node = Node.new(stored_key, hash_code, value)
|
|
136
|
+
prepared = policy_prepare_insert(node)
|
|
137
|
+
bucket = @buckets[hash_code]
|
|
138
|
+
replacement_bucket = bucket ? bucket.dup : []
|
|
139
|
+
node.collision_index = replacement_bucket.length
|
|
140
|
+
replacement_bucket << node
|
|
141
|
+
check_mutable!
|
|
142
|
+
|
|
143
|
+
commit do
|
|
144
|
+
# The internal hash-code keys cannot invoke user callbacks. Replacing a
|
|
145
|
+
# prepared collision chain happens before callback-free eviction.
|
|
146
|
+
@buckets[hash_code] = replacement_bucket
|
|
147
|
+
@size += 1
|
|
148
|
+
policy_commit_insert(node, prepared)
|
|
149
|
+
remove_node(victim) if victim
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
value
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
def fetch(*arguments)
|
|
157
|
+
unless arguments.length.between?(1, 2)
|
|
158
|
+
raise ArgumentError, "wrong number of arguments (given #{arguments.length}, expected 1..2)"
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
key, default = arguments
|
|
162
|
+
default_given = arguments.length == 2
|
|
163
|
+
|
|
164
|
+
warn "block supersedes default value argument", uplevel: 1 if block_given? && default_given
|
|
165
|
+
|
|
166
|
+
found, value = synchronize do
|
|
167
|
+
node = find_node(key)
|
|
168
|
+
if node
|
|
169
|
+
unless frozen?
|
|
170
|
+
prepared = policy_prepare_access(node)
|
|
171
|
+
commit { policy_commit_access(node, prepared) }
|
|
172
|
+
end
|
|
173
|
+
[true, node.value]
|
|
174
|
+
else
|
|
175
|
+
[false, nil]
|
|
176
|
+
end
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
return value if found
|
|
180
|
+
return yield(key) if block_given?
|
|
181
|
+
return default if default_given
|
|
182
|
+
|
|
183
|
+
raise KeyError.new("key not found: #{key.inspect}", receiver: self, key: key)
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
def prepare_key(key)
|
|
187
|
+
synchronize do
|
|
188
|
+
stored_key = canonical_key(key)
|
|
189
|
+
find_node(stored_key)
|
|
190
|
+
stored_key
|
|
191
|
+
end
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
def key?(key) = synchronize { !find_node(key).nil? }
|
|
195
|
+
|
|
196
|
+
def getkey(key)
|
|
197
|
+
synchronize { find_node(key)&.key }
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
def delete(key)
|
|
201
|
+
synchronize do
|
|
202
|
+
check_mutable!
|
|
203
|
+
node = find_node(key)
|
|
204
|
+
if node
|
|
205
|
+
check_mutable!
|
|
206
|
+
commit { remove_node(node).value }
|
|
207
|
+
end
|
|
208
|
+
end
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
def clear
|
|
212
|
+
synchronize do
|
|
213
|
+
check_mutable!
|
|
214
|
+
commit do
|
|
215
|
+
@buckets.clear
|
|
216
|
+
@size = 0
|
|
217
|
+
policy_clear
|
|
218
|
+
end
|
|
219
|
+
end
|
|
220
|
+
self
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
def max_size = synchronize { @max_size }
|
|
224
|
+
|
|
225
|
+
def max_size=(limit)
|
|
226
|
+
validate_limit(limit, "max_size")
|
|
227
|
+
synchronize do
|
|
228
|
+
check_mutable!
|
|
229
|
+
commit do
|
|
230
|
+
remove_victims_until(limit)
|
|
231
|
+
@max_size = limit
|
|
232
|
+
end
|
|
233
|
+
end
|
|
234
|
+
limit
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
def prune(to:)
|
|
238
|
+
validate_limit(to, "to")
|
|
239
|
+
synchronize do
|
|
240
|
+
check_mutable!
|
|
241
|
+
original_size = @size
|
|
242
|
+
commit { remove_victims_until(to) }
|
|
243
|
+
original_size - @size
|
|
244
|
+
end
|
|
245
|
+
end
|
|
246
|
+
|
|
247
|
+
def shift
|
|
248
|
+
synchronize do
|
|
249
|
+
check_mutable!
|
|
250
|
+
node = policy_victim
|
|
251
|
+
node ? commit { remove_node(node).then { [it.key, it.value] } } : nil
|
|
252
|
+
end
|
|
253
|
+
end
|
|
254
|
+
|
|
255
|
+
def empty? = synchronize { @size.zero? }
|
|
256
|
+
def size = synchronize { @size }
|
|
257
|
+
alias length size
|
|
258
|
+
|
|
259
|
+
def compare_keys_by_identity? = synchronize { @compare_keys_by_identity }
|
|
260
|
+
def compare_values_by_identity? = synchronize { @compare_values_by_identity }
|
|
261
|
+
|
|
262
|
+
def each(&block)
|
|
263
|
+
return enum_for(__method__) unless block_given?
|
|
264
|
+
snapshot.each { block.call(*it) }
|
|
265
|
+
self
|
|
266
|
+
end
|
|
267
|
+
alias each_pair each
|
|
268
|
+
|
|
269
|
+
def each_key(&block)
|
|
270
|
+
return enum_for(__method__) unless block_given?
|
|
271
|
+
snapshot.each { block.call(it.first) }
|
|
272
|
+
self
|
|
273
|
+
end
|
|
274
|
+
|
|
275
|
+
def each_value(&block)
|
|
276
|
+
return enum_for(__method__) unless block_given?
|
|
277
|
+
snapshot.each { block.call(it.last) }
|
|
278
|
+
self
|
|
279
|
+
end
|
|
280
|
+
|
|
281
|
+
def keys = snapshot.map!(&:first).freeze
|
|
282
|
+
|
|
283
|
+
private
|
|
284
|
+
|
|
285
|
+
def check_initializable!
|
|
286
|
+
if instance_variable_defined?(:@initializing) || instance_variable_defined?(:@initialized)
|
|
287
|
+
raise "bounded map is already initialized"
|
|
288
|
+
end
|
|
289
|
+
raise FrozenError, "can't modify frozen #{self.class}" if frozen?
|
|
290
|
+
end
|
|
291
|
+
|
|
292
|
+
def synchronize(&)
|
|
293
|
+
raise "bounded map is not initialized" unless instance_variable_defined?(:@initialized)
|
|
294
|
+
|
|
295
|
+
@mutex.synchronize(&)
|
|
296
|
+
end
|
|
297
|
+
|
|
298
|
+
def check_mutable! = Internal::Freeze.check(self)
|
|
299
|
+
|
|
300
|
+
def find_node(key)
|
|
301
|
+
hash_code = key_hash_code(key)
|
|
302
|
+
@buckets[hash_code]&.find { keys_equal?(it.key, key) }
|
|
303
|
+
end
|
|
304
|
+
|
|
305
|
+
def primitive_id(value) = BasicObject.instance_method(:__id__).bind_call(value)
|
|
306
|
+
|
|
307
|
+
def key_hash_code(key)
|
|
308
|
+
hash = @compare_keys_by_identity ? primitive_id(key) : key.hash
|
|
309
|
+
hash = Integer.try_convert(hash) unless Integer === hash
|
|
310
|
+
raise TypeError, "hash value must be an Integer" unless hash
|
|
311
|
+
HashCode.new(hash)
|
|
312
|
+
end
|
|
313
|
+
|
|
314
|
+
def keys_equal?(stored, probe)
|
|
315
|
+
identical = BasicObject.instance_method(:equal?).bind_call(stored, probe)
|
|
316
|
+
return true if identical
|
|
317
|
+
!@compare_keys_by_identity && stored.eql?(probe)
|
|
318
|
+
end
|
|
319
|
+
|
|
320
|
+
def canonical_key(key) = !@compare_keys_by_identity && String === key ? canonical_string(key) : key
|
|
321
|
+
|
|
322
|
+
def canonical_string(string)
|
|
323
|
+
klass = Object.instance_method(:class).bind_call(string)
|
|
324
|
+
if BasicObject.instance_method(:equal?).bind_call(klass, String)
|
|
325
|
+
return String.instance_method(:-@).bind_call(string)
|
|
326
|
+
end
|
|
327
|
+
|
|
328
|
+
copy = Class.instance_method(:allocate).bind_call(klass)
|
|
329
|
+
String.instance_method(:replace).bind_call(copy, string)
|
|
330
|
+
Object.instance_method(:instance_variables).bind_call(string).each do |name|
|
|
331
|
+
value = Object.instance_method(:instance_variable_get).bind_call(string, name)
|
|
332
|
+
Object.instance_method(:instance_variable_set).bind_call(copy, name, value)
|
|
333
|
+
end
|
|
334
|
+
Object.instance_method(:freeze).bind_call(copy)
|
|
335
|
+
end
|
|
336
|
+
|
|
337
|
+
def remove_node(node)
|
|
338
|
+
policy_remove(node)
|
|
339
|
+
bucket = @buckets.fetch(node.hash_code)
|
|
340
|
+
index = node.collision_index
|
|
341
|
+
last = bucket.pop
|
|
342
|
+
unless BasicObject.instance_method(:equal?).bind_call(last, node)
|
|
343
|
+
bucket[index] = last
|
|
344
|
+
last.collision_index = index
|
|
345
|
+
end
|
|
346
|
+
node.collision_index = nil
|
|
347
|
+
@buckets.delete(node.hash_code) if bucket.empty?
|
|
348
|
+
@size -= 1
|
|
349
|
+
node
|
|
350
|
+
end
|
|
351
|
+
|
|
352
|
+
def copy_node(node)
|
|
353
|
+
copy = Node.new(node.key, node.hash_code, node.value)
|
|
354
|
+
bucket = (@buckets[node.hash_code] ||= [])
|
|
355
|
+
copy.collision_index = bucket.length
|
|
356
|
+
bucket << copy
|
|
357
|
+
@size += 1
|
|
358
|
+
copy
|
|
359
|
+
end
|
|
360
|
+
|
|
361
|
+
def remove_victims_until(target)
|
|
362
|
+
remove_node(policy_victim) while @size > target
|
|
363
|
+
end
|
|
364
|
+
|
|
365
|
+
def snapshot
|
|
366
|
+
synchronize do
|
|
367
|
+
@buckets.each_value.flat_map { |bucket| bucket.map { [it.key, it.value] } }
|
|
368
|
+
end
|
|
369
|
+
end
|
|
370
|
+
|
|
371
|
+
def validate_boolean(value, name)
|
|
372
|
+
equal = BasicObject.instance_method(:equal?)
|
|
373
|
+
return if equal.bind_call(value, true) || equal.bind_call(value, false)
|
|
374
|
+
raise ArgumentError, "#{name} must be true or false"
|
|
375
|
+
end
|
|
376
|
+
|
|
377
|
+
def validate_limit(value, name)
|
|
378
|
+
raise TypeError, "#{name} must be an Integer" unless Integer === value
|
|
379
|
+
raise ArgumentError, "#{name} must be non-negative" if value.negative?
|
|
380
|
+
end
|
|
381
|
+
|
|
382
|
+
def commit(&) = Thread.handle_interrupt(INTERRUPT_MASK, &)
|
|
383
|
+
|
|
384
|
+
# Policy hooks are deliberately separate from the public storage protocol.
|
|
385
|
+
def initialize_policy = raise NotImplementedError
|
|
386
|
+
def policy_clear = raise NotImplementedError
|
|
387
|
+
def policy_commit_access(node, prepared) = raise NotImplementedError
|
|
388
|
+
def policy_commit_insert(node, prepared) = raise NotImplementedError
|
|
389
|
+
def policy_prepare_access(node) = raise NotImplementedError
|
|
390
|
+
def policy_prepare_insert(node) = raise NotImplementedError
|
|
391
|
+
def policy_remove(node) = raise NotImplementedError
|
|
392
|
+
def policy_victim = raise NotImplementedError
|
|
393
|
+
def copy_policy_nodes(_other) = raise NotImplementedError
|
|
394
|
+
end
|
|
395
|
+
|
|
396
|
+
class PortableLRUMap < PortableBoundedMap
|
|
397
|
+
private
|
|
398
|
+
|
|
399
|
+
def initialize_policy
|
|
400
|
+
@least_recent = nil
|
|
401
|
+
@most_recent = nil
|
|
402
|
+
end
|
|
403
|
+
|
|
404
|
+
def copy_policy_nodes(other)
|
|
405
|
+
node = other.instance_variable_get(:@least_recent)
|
|
406
|
+
while node
|
|
407
|
+
append(copy_node(node))
|
|
408
|
+
node = node.following
|
|
409
|
+
end
|
|
410
|
+
end
|
|
411
|
+
|
|
412
|
+
def policy_prepare_access(_node) = nil
|
|
413
|
+
def policy_prepare_insert(_node) = nil
|
|
414
|
+
|
|
415
|
+
def policy_commit_access(node, _prepared)
|
|
416
|
+
return if node.equal?(@most_recent)
|
|
417
|
+
unlink(node)
|
|
418
|
+
append(node)
|
|
419
|
+
end
|
|
420
|
+
|
|
421
|
+
def policy_commit_insert(node, _prepared) = append(node)
|
|
422
|
+
|
|
423
|
+
def policy_remove(node) = unlink(node)
|
|
424
|
+
def policy_victim = @least_recent
|
|
425
|
+
|
|
426
|
+
def policy_clear
|
|
427
|
+
@least_recent = nil
|
|
428
|
+
@most_recent = nil
|
|
429
|
+
end
|
|
430
|
+
|
|
431
|
+
def append(node)
|
|
432
|
+
node.previous = @most_recent
|
|
433
|
+
node.following = nil
|
|
434
|
+
@most_recent.following = node if @most_recent
|
|
435
|
+
@least_recent ||= node
|
|
436
|
+
@most_recent = node
|
|
437
|
+
end
|
|
438
|
+
|
|
439
|
+
def unlink(node)
|
|
440
|
+
if node.previous
|
|
441
|
+
node.previous.following = node.following
|
|
442
|
+
else
|
|
443
|
+
@least_recent = node.following
|
|
444
|
+
end
|
|
445
|
+
if node.following
|
|
446
|
+
node.following.previous = node.previous
|
|
447
|
+
else
|
|
448
|
+
@most_recent = node.previous
|
|
449
|
+
end
|
|
450
|
+
node.previous = nil
|
|
451
|
+
node.following = nil
|
|
452
|
+
end
|
|
453
|
+
end
|
|
454
|
+
|
|
455
|
+
# Adds the public strict-map key and value contract to portable storage.
|
|
456
|
+
module PortableStrictBoundedMap
|
|
457
|
+
def [](key) = super(strict_key(key))
|
|
458
|
+
|
|
459
|
+
def []=(key, value)
|
|
460
|
+
super(strict_key(key), strict_value(value))
|
|
461
|
+
end
|
|
462
|
+
|
|
463
|
+
def fetch(*arguments, &)
|
|
464
|
+
strict_key(arguments.first) unless arguments.empty?
|
|
465
|
+
super
|
|
466
|
+
end
|
|
467
|
+
|
|
468
|
+
def prepare_key(key) = super(strict_key(key))
|
|
469
|
+
def key?(key) = super(strict_key(key))
|
|
470
|
+
def getkey(key) = super(strict_key(key))
|
|
471
|
+
def delete(key) = super(strict_key(key))
|
|
472
|
+
|
|
473
|
+
private
|
|
474
|
+
|
|
475
|
+
def strict_key(key)
|
|
476
|
+
key = canonical_key(key)
|
|
477
|
+
return key if Ractor.shareable?(key)
|
|
478
|
+
raise Ractor::IsolationError, "key must be Ractor-shareable"
|
|
479
|
+
end
|
|
480
|
+
|
|
481
|
+
def strict_value(value)
|
|
482
|
+
return value unless Internal.native_ractors?
|
|
483
|
+
return value if Ractor.shareable?(value)
|
|
484
|
+
raise Ractor::IsolationError, "value must be Ractor-shareable"
|
|
485
|
+
end
|
|
486
|
+
end
|
|
487
|
+
private_constant :PortableStrictBoundedMap
|
|
488
|
+
|
|
489
|
+
class PortableStrictLRUMap < PortableLRUMap
|
|
490
|
+
include PortableStrictBoundedMap
|
|
491
|
+
end
|
|
492
|
+
|
|
493
|
+
class PortableLFUMap < PortableBoundedMap
|
|
494
|
+
class Bucket
|
|
495
|
+
attr_accessor :frequency, :previous, :following, :least_recent, :most_recent
|
|
496
|
+
|
|
497
|
+
def initialize(frequency)
|
|
498
|
+
@frequency = frequency
|
|
499
|
+
@previous = nil
|
|
500
|
+
@following = nil
|
|
501
|
+
@least_recent = nil
|
|
502
|
+
@most_recent = nil
|
|
503
|
+
end
|
|
504
|
+
|
|
505
|
+
def empty? = least_recent.nil?
|
|
506
|
+
end
|
|
507
|
+
private_constant :Bucket
|
|
508
|
+
|
|
509
|
+
private
|
|
510
|
+
|
|
511
|
+
def initialize_policy
|
|
512
|
+
@least_frequent = nil
|
|
513
|
+
end
|
|
514
|
+
|
|
515
|
+
def copy_policy_nodes(other)
|
|
516
|
+
source_bucket = other.instance_variable_get(:@least_frequent)
|
|
517
|
+
previous = nil
|
|
518
|
+
while source_bucket
|
|
519
|
+
bucket = Bucket.new(source_bucket.frequency)
|
|
520
|
+
bucket.previous = previous
|
|
521
|
+
previous.following = bucket if previous
|
|
522
|
+
@least_frequent ||= bucket
|
|
523
|
+
node = source_bucket.least_recent
|
|
524
|
+
while node
|
|
525
|
+
append_to_bucket(copy_node(node), bucket)
|
|
526
|
+
node = node.following
|
|
527
|
+
end
|
|
528
|
+
previous = bucket
|
|
529
|
+
source_bucket = source_bucket.following
|
|
530
|
+
end
|
|
531
|
+
end
|
|
532
|
+
|
|
533
|
+
def policy_prepare_insert(_node)
|
|
534
|
+
return @least_frequent if @least_frequent&.frequency == 1
|
|
535
|
+
Bucket.new(1)
|
|
536
|
+
end
|
|
537
|
+
|
|
538
|
+
def policy_commit_insert(node, bucket)
|
|
539
|
+
unless bucket.equal?(@least_frequent)
|
|
540
|
+
bucket.following = @least_frequent
|
|
541
|
+
@least_frequent.previous = bucket if @least_frequent
|
|
542
|
+
@least_frequent = bucket
|
|
543
|
+
end
|
|
544
|
+
append_to_bucket(node, bucket)
|
|
545
|
+
end
|
|
546
|
+
|
|
547
|
+
def policy_prepare_access(node)
|
|
548
|
+
source = node.bucket
|
|
549
|
+
frequency = source.frequency + 1
|
|
550
|
+
following = source.following
|
|
551
|
+
return following if following&.frequency == frequency
|
|
552
|
+
Bucket.new(frequency)
|
|
553
|
+
end
|
|
554
|
+
|
|
555
|
+
def policy_commit_access(node, destination)
|
|
556
|
+
source = node.bucket
|
|
557
|
+
unless destination.equal?(source.following)
|
|
558
|
+
destination.previous = source
|
|
559
|
+
destination.following = source.following
|
|
560
|
+
source.following.previous = destination if source.following
|
|
561
|
+
source.following = destination
|
|
562
|
+
end
|
|
563
|
+
unlink_from_bucket(node)
|
|
564
|
+
remove_bucket(source) if source.empty?
|
|
565
|
+
append_to_bucket(node, destination)
|
|
566
|
+
end
|
|
567
|
+
|
|
568
|
+
def policy_remove(node)
|
|
569
|
+
bucket = node.bucket
|
|
570
|
+
unlink_from_bucket(node)
|
|
571
|
+
remove_bucket(bucket) if bucket.empty?
|
|
572
|
+
end
|
|
573
|
+
|
|
574
|
+
def policy_victim = @least_frequent&.least_recent
|
|
575
|
+
def policy_clear = @least_frequent = nil
|
|
576
|
+
|
|
577
|
+
def append_to_bucket(node, bucket)
|
|
578
|
+
node.bucket = bucket
|
|
579
|
+
node.previous = bucket.most_recent
|
|
580
|
+
node.following = nil
|
|
581
|
+
bucket.most_recent.following = node if bucket.most_recent
|
|
582
|
+
bucket.least_recent ||= node
|
|
583
|
+
bucket.most_recent = node
|
|
584
|
+
end
|
|
585
|
+
|
|
586
|
+
def unlink_from_bucket(node)
|
|
587
|
+
bucket = node.bucket
|
|
588
|
+
if node.previous
|
|
589
|
+
node.previous.following = node.following
|
|
590
|
+
else
|
|
591
|
+
bucket.least_recent = node.following
|
|
592
|
+
end
|
|
593
|
+
if node.following
|
|
594
|
+
node.following.previous = node.previous
|
|
595
|
+
else
|
|
596
|
+
bucket.most_recent = node.previous
|
|
597
|
+
end
|
|
598
|
+
node.previous = nil
|
|
599
|
+
node.following = nil
|
|
600
|
+
node.bucket = nil
|
|
601
|
+
end
|
|
602
|
+
|
|
603
|
+
def remove_bucket(bucket)
|
|
604
|
+
if bucket.previous
|
|
605
|
+
bucket.previous.following = bucket.following
|
|
606
|
+
else
|
|
607
|
+
@least_frequent = bucket.following
|
|
608
|
+
end
|
|
609
|
+
bucket.following.previous = bucket.previous if bucket.following
|
|
610
|
+
bucket.previous = nil
|
|
611
|
+
bucket.following = nil
|
|
612
|
+
end
|
|
613
|
+
end
|
|
614
|
+
|
|
615
|
+
class PortableStrictLFUMap < PortableLFUMap
|
|
616
|
+
include PortableStrictBoundedMap
|
|
617
|
+
end
|
|
618
|
+
end
|
|
619
|
+
end
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# shareable_constant_value: literal
|
|
3
|
+
# warn_indent: true
|
|
4
|
+
|
|
5
|
+
module Farce
|
|
6
|
+
module Internal
|
|
7
|
+
class ProxyOwner
|
|
8
|
+
include ProxyOwnerNotifications
|
|
9
|
+
|
|
10
|
+
class OwnerScheduler < Farce::ThreadScheduler
|
|
11
|
+
def initialize
|
|
12
|
+
@threads = Thread::Queue.new
|
|
13
|
+
super
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def schedule(*, **, &)
|
|
17
|
+
@threads << Thread.new(*, &)
|
|
18
|
+
self
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def join
|
|
22
|
+
@threads.pop.join until @threads.empty?
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
private_constant :OwnerScheduler
|
|
26
|
+
|
|
27
|
+
def self.create_scheduler
|
|
28
|
+
Storage[:local_scheduler] = OwnerScheduler.new
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def self.drain_scheduler(scheduler) = scheduler.join
|
|
32
|
+
|
|
33
|
+
def self.current = Storage.store_if_absent(self) { new }
|
|
34
|
+
|
|
35
|
+
def initialize
|
|
36
|
+
super
|
|
37
|
+
Ractor.current.monitor(self)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def <<(_) = stop
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|