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,227 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# shareable_constant_value: literal
|
|
3
|
+
# warn_indent: true
|
|
4
|
+
|
|
5
|
+
module Farce
|
|
6
|
+
module Internal # :nodoc: all
|
|
7
|
+
# Portable snapshots of public values. Backing stores never enter the stream.
|
|
8
|
+
module MarshalSupport
|
|
9
|
+
extend self
|
|
10
|
+
|
|
11
|
+
def protocol_method?(name) = %i[marshal_dump marshal_load _dump _load _dump_data _load_data].include?(name)
|
|
12
|
+
|
|
13
|
+
# A version belongs to the public snapshot, not to a backend layout.
|
|
14
|
+
def payload(data, length)
|
|
15
|
+
unless data.is_a?(Array) && data.length == length + 1 && data.first == 1
|
|
16
|
+
raise TypeError, "invalid Farce Marshal snapshot"
|
|
17
|
+
end
|
|
18
|
+
data.drop(1)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def value(value) = [Internal.marshal_shareable?(value), Kernel.instance_method(:frozen?).bind_call(value), value]
|
|
22
|
+
|
|
23
|
+
def restore_value(data)
|
|
24
|
+
shareable, frozen, value = data
|
|
25
|
+
if shareable && Shareable === value && !(Shareable::Native === value) &&
|
|
26
|
+
!Kernel.instance_method(:frozen?).bind_call(value) &&
|
|
27
|
+
value.respond_to?(:marshal_load, true) && value.method(:marshal_load).owner.name&.start_with?("Farce::")
|
|
28
|
+
raise TypeError, "recursive shareable snapshots require an initialized value"
|
|
29
|
+
end
|
|
30
|
+
value = Ractor.make_shareable(value) if shareable
|
|
31
|
+
Kernel.instance_method(:freeze).bind_call(value) if frozen
|
|
32
|
+
value
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def freeze(object, frozen)
|
|
36
|
+
return object unless frozen
|
|
37
|
+
object.freeze
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
module Initialize
|
|
41
|
+
private
|
|
42
|
+
|
|
43
|
+
# @api private
|
|
44
|
+
def marshal_initialize(arguments, options, _configuration = nil)
|
|
45
|
+
initialize(*arguments, **options)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# @api private
|
|
49
|
+
def marshal_configuration = nil
|
|
50
|
+
|
|
51
|
+
# A restored manager must retain its links to already-loaded envelopes.
|
|
52
|
+
# @api private
|
|
53
|
+
def marshal_mode_manager(mode) = @manager || ModeManager.new(mode:)
|
|
54
|
+
|
|
55
|
+
def publish_marshaled
|
|
56
|
+
if is_a?(Shareable)
|
|
57
|
+
publish_shareable
|
|
58
|
+
elsif is_a?(Unshareable)
|
|
59
|
+
pin_unshareable
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
module Counter
|
|
65
|
+
include Initialize
|
|
66
|
+
|
|
67
|
+
# @api private
|
|
68
|
+
def marshal_dump
|
|
69
|
+
options = respond_to?(:scope) ? { scope: } : {}
|
|
70
|
+
[1, initial, value, options, marshal_configuration, frozen?]
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# @api private
|
|
74
|
+
def marshal_load(data)
|
|
75
|
+
initial, current, options, configuration, frozen = MarshalSupport.payload(data, 5)
|
|
76
|
+
marshal_initialize([initial], options, configuration)
|
|
77
|
+
store(current)
|
|
78
|
+
MarshalSupport.freeze(self, frozen)
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
module Flag
|
|
83
|
+
include Initialize
|
|
84
|
+
|
|
85
|
+
# @api private
|
|
86
|
+
def marshal_dump
|
|
87
|
+
options = respond_to?(:scope) ? { scope: } : {}
|
|
88
|
+
[1, value, options, marshal_configuration, frozen?]
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
# @api private
|
|
92
|
+
def marshal_load(data)
|
|
93
|
+
current, options, configuration, frozen = MarshalSupport.payload(data, 4)
|
|
94
|
+
marshal_initialize([current], options, configuration)
|
|
95
|
+
MarshalSupport.freeze(self, frozen)
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
module Atom
|
|
100
|
+
include Initialize
|
|
101
|
+
|
|
102
|
+
# @api private
|
|
103
|
+
def marshal_dump
|
|
104
|
+
options = { compare_by_identity: compare_by_identity? }
|
|
105
|
+
options[:mode] = mode if respond_to?(:mode)
|
|
106
|
+
options[:scope] = scope if respond_to?(:scope)
|
|
107
|
+
[1, MarshalSupport.value(value), options, marshal_configuration, frozen?]
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
# @api private
|
|
111
|
+
def marshal_load(data)
|
|
112
|
+
current, options, configuration, frozen = MarshalSupport.payload(data, 4)
|
|
113
|
+
marshal_initialize([nil], options, configuration)
|
|
114
|
+
store(MarshalSupport.restore_value(current))
|
|
115
|
+
MarshalSupport.freeze(self, frozen)
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
module Vector
|
|
120
|
+
include Initialize
|
|
121
|
+
|
|
122
|
+
# @api private
|
|
123
|
+
def marshal_dump
|
|
124
|
+
options = { compare_by_identity: compare_by_identity? }
|
|
125
|
+
options[:scope] = scope if respond_to?(:scope)
|
|
126
|
+
[1, internal_vector.snapshot.map { MarshalSupport.value(it) }, options, marshal_configuration, frozen?]
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
# @api private
|
|
130
|
+
def marshal_load(data)
|
|
131
|
+
entries, options, configuration, frozen = MarshalSupport.payload(data, 4)
|
|
132
|
+
marshal_initialize([[]], options, configuration)
|
|
133
|
+
entries.each { internal_vector.push(MarshalSupport.restore_value(it)) }
|
|
134
|
+
MarshalSupport.freeze(self, frozen)
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
module Map
|
|
139
|
+
include Initialize
|
|
140
|
+
|
|
141
|
+
# @api private
|
|
142
|
+
def marshal_dump
|
|
143
|
+
options = {}
|
|
144
|
+
unless is_a?(Abstract::TreeMap)
|
|
145
|
+
options[:compare_keys_by_identity] = compare_keys_by_identity?
|
|
146
|
+
options[:compare_values_by_identity] = compare_values_by_identity?
|
|
147
|
+
end
|
|
148
|
+
options[:mode] = mode if respond_to?(:mode)
|
|
149
|
+
options[:manager] = @manager if defined?(@manager) && !@manager.is_a?(WeakModeManager)
|
|
150
|
+
options[:scope] = scope if respond_to?(:scope)
|
|
151
|
+
options[:max_size] = max_size if respond_to?(:max_size)
|
|
152
|
+
options[:normalize_keys] = KeyNormalizer.dump(@key_normalizer) if @key_normalizer
|
|
153
|
+
entries = marshal_entries.map do |key, stored|
|
|
154
|
+
[MarshalSupport.value(key), marshal_map_value(stored)]
|
|
155
|
+
end
|
|
156
|
+
[1, entries, options, marshal_configuration, frozen?]
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
# @api private
|
|
160
|
+
def marshal_load(data)
|
|
161
|
+
entries, options, configuration, frozen = MarshalSupport.payload(data, 4)
|
|
162
|
+
@manager = options.delete(:manager) if options.key?(:manager)
|
|
163
|
+
options[:normalize_keys] = KeyNormalizer.restore(options[:normalize_keys]) if options.key?(:normalize_keys)
|
|
164
|
+
marshal_initialize([nil], options, configuration)
|
|
165
|
+
map = marshal_map
|
|
166
|
+
entries.each do |key, stored|
|
|
167
|
+
map[MarshalSupport.restore_value(key)] = marshal_restore_map_value(stored)
|
|
168
|
+
end
|
|
169
|
+
MarshalSupport.freeze(self, frozen)
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
private
|
|
173
|
+
|
|
174
|
+
# @api private
|
|
175
|
+
def marshal_entries = marshal_map.each
|
|
176
|
+
|
|
177
|
+
# @api private
|
|
178
|
+
def marshal_map
|
|
179
|
+
map = internal_map
|
|
180
|
+
KeyNormalizer::ConcurrentMap === map ? map.transaction_source : map
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
# @api private
|
|
184
|
+
def marshal_map_value(value)
|
|
185
|
+
MarshalSupport.value(value)
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
# @api private
|
|
189
|
+
def marshal_restore_map_value(data)
|
|
190
|
+
MarshalSupport.restore_value(data)
|
|
191
|
+
end
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
module Reject
|
|
195
|
+
# @api private
|
|
196
|
+
def marshal_dump = raise(TypeError, "#{self.class} cannot be marshaled")
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
module WeakValue
|
|
200
|
+
# @api private
|
|
201
|
+
def marshal_dump
|
|
202
|
+
state, current = state_and_value
|
|
203
|
+
raise TypeError, "moved weak values cannot be marshaled" if state == :moved
|
|
204
|
+
[1, MarshalSupport.value(current), frozen?]
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
# @api private
|
|
208
|
+
def marshal_load(data)
|
|
209
|
+
current, frozen = MarshalSupport.payload(data, 2)
|
|
210
|
+
initialize(MarshalSupport.restore_value(current))
|
|
211
|
+
freeze if frozen
|
|
212
|
+
end
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
module WeakRef
|
|
216
|
+
# @api private
|
|
217
|
+
def marshal_dump = [1, @value]
|
|
218
|
+
|
|
219
|
+
# @api private
|
|
220
|
+
def marshal_load(data)
|
|
221
|
+
@value, = MarshalSupport.payload(data, 1)
|
|
222
|
+
Kernel.instance_method(:freeze).bind_call(self)
|
|
223
|
+
end
|
|
224
|
+
end
|
|
225
|
+
end
|
|
226
|
+
end
|
|
227
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
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
|
+
# When including Farce in a class or module, this module is included instead
|
|
9
|
+
# This avoids pulling in private constants, like {Farce::Internal} or `UNDEFINED`.
|
|
10
|
+
module Mixin
|
|
11
|
+
# This will trigger all autoloads, but any other approach would be complex and fragile.
|
|
12
|
+
Farce.constants(false).each { const_set(it, Farce.const_get(it)) unless it =~ /\A[A-Z_]+\Z|\ATest/ }
|
|
13
|
+
|
|
14
|
+
def self.included(base)
|
|
15
|
+
return unless base.const_defined?(:Ractor, false) && ractor = base.const_get(:Ractor, false)
|
|
16
|
+
ractor.const_set(:Port, Farce::Port) unless ractor.const_defined?(:Port, false)
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,63 @@
|
|
|
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
|
+
# Coordinate mutable keys by ordering within one Ractor.
|
|
9
|
+
# SortedSet uses this for backing trees whose keys cannot be published.
|
|
10
|
+
class MutableOrderedKeyLockMap
|
|
11
|
+
Gate = Struct.new(:lock, :participants)
|
|
12
|
+
private_constant :Gate
|
|
13
|
+
|
|
14
|
+
def initialize
|
|
15
|
+
@guard = Farce::Lock.new
|
|
16
|
+
@entries = MutableTreeMap.new
|
|
17
|
+
@participants = 0
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def synchronize(key, &)
|
|
21
|
+
raise LocalJumpError, "no block given" unless block_given?
|
|
22
|
+
|
|
23
|
+
gate = nil
|
|
24
|
+
registered = false
|
|
25
|
+
begin
|
|
26
|
+
Thread.handle_interrupt(INTERRUPT_MASK) do
|
|
27
|
+
@guard.synchronize do
|
|
28
|
+
gate = @entries[key]
|
|
29
|
+
unless gate
|
|
30
|
+
gate = Gate.new(KeyLockMap.new(registry_class: Farce::Strict::Map), 0)
|
|
31
|
+
@entries[key] = gate
|
|
32
|
+
end
|
|
33
|
+
gate.participants += 1
|
|
34
|
+
@participants += 1
|
|
35
|
+
registered = true
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
gate.lock.synchronize(:operation, &)
|
|
39
|
+
ensure
|
|
40
|
+
release(key, gate) if registered
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def transaction_snapshot = PortableTransaction::ReservationEntry.new(self)
|
|
45
|
+
|
|
46
|
+
private
|
|
47
|
+
|
|
48
|
+
def release(key, gate)
|
|
49
|
+
Thread.handle_interrupt(INTERRUPT_MASK) do
|
|
50
|
+
@guard.synchronize do
|
|
51
|
+
gate.participants -= 1
|
|
52
|
+
@participants -= 1
|
|
53
|
+
if @participants.zero?
|
|
54
|
+
@entries.clear
|
|
55
|
+
elsif gate.participants.zero?
|
|
56
|
+
@entries.delete(key)
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# shareable_constant_value: literal
|
|
3
|
+
# warn_indent: true
|
|
4
|
+
|
|
5
|
+
module Farce
|
|
6
|
+
module Internal
|
|
7
|
+
# Coordination objects own live state that cannot be duplicated.
|
|
8
|
+
module Noncopyable
|
|
9
|
+
include MarshalSupport::Reject
|
|
10
|
+
|
|
11
|
+
def dup = initialize_copy(self)
|
|
12
|
+
def clone(...) = initialize_copy(self)
|
|
13
|
+
|
|
14
|
+
private def initialize_copy(_other) = raise(TypeError, "#{self.class} cannot be copied")
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,69 @@
|
|
|
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
|
+
# Coordinate keys using their ordering, independently of their hash values.
|
|
9
|
+
class OrderedKeyLockMap
|
|
10
|
+
include Shareable::Unfreezable
|
|
11
|
+
|
|
12
|
+
Gate = Data.define(:lock, :participants)
|
|
13
|
+
private_constant :Gate
|
|
14
|
+
|
|
15
|
+
def initialize
|
|
16
|
+
@guard = Farce::Lock.new
|
|
17
|
+
@entries = ShareableTreeMap.new
|
|
18
|
+
@participants = Counter.new(0)
|
|
19
|
+
super
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def synchronize(key, &)
|
|
23
|
+
raise LocalJumpError, "no block given" unless block_given?
|
|
24
|
+
|
|
25
|
+
gate = nil
|
|
26
|
+
registered = false
|
|
27
|
+
|
|
28
|
+
begin
|
|
29
|
+
Thread.handle_interrupt(INTERRUPT_MASK) do
|
|
30
|
+
@guard.synchronize do
|
|
31
|
+
gate = @entries[key]
|
|
32
|
+
unless gate
|
|
33
|
+
gate = Gate.new(KeyLockMap.new(registry_class: Farce::Strict::Map), Counter.new(0))
|
|
34
|
+
Ractor.make_shareable(gate) if Internal.native_ractors?
|
|
35
|
+
@entries[key] = gate
|
|
36
|
+
end
|
|
37
|
+
gate.participants.add(1)
|
|
38
|
+
@participants.add(1)
|
|
39
|
+
registered = true
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
gate.lock.synchronize(:operation, &)
|
|
43
|
+
ensure
|
|
44
|
+
release(key, gate) if registered
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def transaction_snapshot = PortableTransaction::ReservationEntry.new(self)
|
|
49
|
+
|
|
50
|
+
private
|
|
51
|
+
|
|
52
|
+
def release(key, gate)
|
|
53
|
+
Thread.handle_interrupt(INTERRUPT_MASK) do
|
|
54
|
+
@guard.synchronize do
|
|
55
|
+
remaining = gate.participants.add(-1)
|
|
56
|
+
total = @participants.add(-1)
|
|
57
|
+
# Clearing needs no comparisons. It also reclaims idle records left
|
|
58
|
+
# behind when a comparator raised during an earlier cleanup.
|
|
59
|
+
if total.zero?
|
|
60
|
+
@entries.clear
|
|
61
|
+
elsif remaining.zero?
|
|
62
|
+
@entries.delete(key)
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# shareable_constant_value: literal
|
|
3
|
+
# warn_indent: true
|
|
4
|
+
|
|
5
|
+
module Farce
|
|
6
|
+
module Internal # :nodoc: all
|
|
7
|
+
# Runs delayed pool control work outside application Ractors.
|
|
8
|
+
class PoolSupervisorService
|
|
9
|
+
include Shareable::Unfreezable
|
|
10
|
+
|
|
11
|
+
STOP = :stop
|
|
12
|
+
private_constant :STOP
|
|
13
|
+
|
|
14
|
+
def initialize
|
|
15
|
+
@queue = TimerQueue.new(mode: :raise)
|
|
16
|
+
@closed = Atom.new(false)
|
|
17
|
+
@worker = Ractor.new(@queue, name: "farce-pool-supervisor") do |queue|
|
|
18
|
+
while (entry = queue.pop) != STOP
|
|
19
|
+
pool, token = entry
|
|
20
|
+
pool.scale_if_needed(token)
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
super
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def schedule(pool, token, delay)
|
|
27
|
+
raise ::Farce::ClosedQueueError, "pool supervisor is closed" if @closed.value
|
|
28
|
+
@queue.push([pool, token].freeze, delay:)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def close
|
|
32
|
+
return unless @closed.compare_and_set(false, true)
|
|
33
|
+
@queue.push(STOP, clock: 0)
|
|
34
|
+
@worker.respond_to?(:join) ? @worker.join : @worker.take
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
PoolSupervisor = PoolSupervisorService.new
|
|
39
|
+
at_exit { PoolSupervisor.close }
|
|
40
|
+
end
|
|
41
|
+
end
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# shareable_constant_value: literal
|
|
3
|
+
# warn_indent: true
|
|
4
|
+
|
|
5
|
+
module Farce
|
|
6
|
+
module Internal # :nodoc: all
|
|
7
|
+
# Coordinates one scheduler with its pool.
|
|
8
|
+
class PoolWorker
|
|
9
|
+
include Shareable::Unfreezable
|
|
10
|
+
|
|
11
|
+
def initialize(pool)
|
|
12
|
+
@pool = pool
|
|
13
|
+
@inflight = Counter.new if pool.max_inflight || pool.shrink_after
|
|
14
|
+
@pressure = pool.min_size < pool.max_size
|
|
15
|
+
@registered = Atom.new(true)
|
|
16
|
+
super()
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def idle_timeout = @pool.shrink_after
|
|
20
|
+
|
|
21
|
+
def wait_for_admission(scheduler, runnable_count)
|
|
22
|
+
return false if @pool.drained?
|
|
23
|
+
limit = @pool.max_inflight
|
|
24
|
+
return true unless limit
|
|
25
|
+
|
|
26
|
+
loop do
|
|
27
|
+
return false if @pool.drained?
|
|
28
|
+
return true if @pool.closing? || @pool.at_max_size? || @inflight.value < limit
|
|
29
|
+
return true if runnable_count && scheduler.public_send(runnable_count).zero?
|
|
30
|
+
|
|
31
|
+
generation = @pool.admission_generation
|
|
32
|
+
next if @pool.closing? || @pool.at_max_size? || @inflight.value < limit
|
|
33
|
+
@pool.wait_for_admission(generation)
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def task_started
|
|
38
|
+
@inflight&.increment
|
|
39
|
+
@pool.task_taken if @pressure
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def task_finished
|
|
43
|
+
return unless @inflight
|
|
44
|
+
@inflight.decrement
|
|
45
|
+
@pool.admission_changed if @pool.max_inflight && !@pool.at_max_size?
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def retire?
|
|
49
|
+
return false unless @inflight&.value&.zero?
|
|
50
|
+
return false unless @pool.retire_worker
|
|
51
|
+
@registered.compare_and_set(true, false)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def stopped(error, replace: true) # rubocop:disable Naming/PredicateMethod
|
|
55
|
+
return false unless @registered.compare_and_set(true, false)
|
|
56
|
+
@pool.worker_stopped(error, replace:)
|
|
57
|
+
true
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# shareable_constant_value: literal
|
|
3
|
+
# warn_indent: true
|
|
4
|
+
|
|
5
|
+
module Farce
|
|
6
|
+
module Internal # :nodoc: all
|
|
7
|
+
module PortableTransaction
|
|
8
|
+
# Stop new ordered-key reservations and reject initializers already active.
|
|
9
|
+
# Ordinary TreeMap operations already use this guard and participant count.
|
|
10
|
+
class ReservationEntry < Entry
|
|
11
|
+
def initialize(source) # rubocop:disable Lint/MissingSuper
|
|
12
|
+
@source = source
|
|
13
|
+
@locks = [source.instance_variable_get(:@guard)]
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def prepare = nil
|
|
17
|
+
def apply = nil
|
|
18
|
+
def restore = nil
|
|
19
|
+
def notify = nil
|
|
20
|
+
|
|
21
|
+
def valid?
|
|
22
|
+
participants = @source.instance_variable_get(:@participants)
|
|
23
|
+
participants = participants.value unless Integer === participants
|
|
24
|
+
participants.zero?
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# shareable_constant_value: literal
|
|
3
|
+
# warn_indent: true
|
|
4
|
+
|
|
5
|
+
module Farce
|
|
6
|
+
module Internal # :nodoc: all
|
|
7
|
+
module PortableTransaction
|
|
8
|
+
# Keep the index identity so operations already resolving a key retry
|
|
9
|
+
# against the installed entries. Retiring old cells invalidates queued
|
|
10
|
+
# operations without adding checks to ordinary map methods.
|
|
11
|
+
class StrongMapEntry < Entry
|
|
12
|
+
FIELDS = %i[@data @entries @registry_holes @sweep_cursor @iteration_epoch].freeze
|
|
13
|
+
CELL_FIELDS = %i[@retired @present @key @value].freeze
|
|
14
|
+
|
|
15
|
+
def self.pairs(index)
|
|
16
|
+
with_cells(index) do |cells|
|
|
17
|
+
cells.filter_map do |cell|
|
|
18
|
+
next if cell.instance_variable_get(:@retired) || !cell.instance_variable_get(:@present)
|
|
19
|
+
alive, key = cell.instance_variable_get(:@key).read
|
|
20
|
+
[key, cell.instance_variable_get(:@value)] if alive
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def self.size(index)
|
|
26
|
+
with_cells(index) do |cells|
|
|
27
|
+
cells.count { !it.instance_variable_get(:@retired) && it.instance_variable_get(:@present) }
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Read the current registry and cells together, so a concurrent commit
|
|
32
|
+
# cannot retire cells between enumeration and reading their contents.
|
|
33
|
+
def self.with_cells(index)
|
|
34
|
+
loop do
|
|
35
|
+
cells = index.snapshot
|
|
36
|
+
registry = index.instance_variable_get(:@registry_mutex)
|
|
37
|
+
locks = [registry, *cells.map { it.instance_variable_get(:@mutex) }].sort_by(&:object_id)
|
|
38
|
+
acquired = []
|
|
39
|
+
Thread.handle_interrupt(INTERRUPT_MASK) do
|
|
40
|
+
locked = locks.all? do |lock|
|
|
41
|
+
lock.try_lock && acquired.push(lock)
|
|
42
|
+
end
|
|
43
|
+
if locked
|
|
44
|
+
current = index.instance_variable_get(:@entries).compact
|
|
45
|
+
if current.size == cells.size &&
|
|
46
|
+
current.each_index.all? { PortableTransaction.same?(current[it], cells[it]) }
|
|
47
|
+
return yield(cells)
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
ensure
|
|
51
|
+
acquired.reverse_each(&:unlock)
|
|
52
|
+
end
|
|
53
|
+
if Fiber.respond_to?(:scheduler) && Fiber.scheduler
|
|
54
|
+
Fiber.scheduler.kernel_sleep(0)
|
|
55
|
+
else
|
|
56
|
+
Thread.pass
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
private_class_method :with_cells
|
|
61
|
+
|
|
62
|
+
# This entry coordinates index and cell locks rather than a single storage field.
|
|
63
|
+
def initialize(source, working, _kind, baseline) # rubocop:disable Lint/MissingSuper
|
|
64
|
+
@source, @working, @baseline = source, working, baseline
|
|
65
|
+
@dirty = @applied = false
|
|
66
|
+
@index = source.instance_variable_get(:@index)
|
|
67
|
+
@index_lock = @index.instance_variable_get(:@lock)
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def prepare
|
|
71
|
+
capture_cells
|
|
72
|
+
replacement = @working.instance_variable_get(:@index)
|
|
73
|
+
@replacement = FIELDS.map { replacement.instance_variable_get(it) }
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def valid?
|
|
77
|
+
return false if @dirty && @source.frozen?
|
|
78
|
+
return false if @index_lock.instance_variable_get(:@reserved)
|
|
79
|
+
current = @index.instance_variable_get(:@entries).compact
|
|
80
|
+
return false unless current.size == @cells.size &&
|
|
81
|
+
current.each_index.all? { PortableTransaction.same?(current[it], @cells[it]) }
|
|
82
|
+
return false unless @cells.size == @baseline.size
|
|
83
|
+
return false unless @cells.all? do |cell|
|
|
84
|
+
next false if cell.instance_variable_get(:@reserved) || cell.instance_variable_get(:@retired)
|
|
85
|
+
next false unless cell.instance_variable_get(:@present)
|
|
86
|
+
alive, key = cell.instance_variable_get(:@key).read
|
|
87
|
+
alive && @baseline.key?(key) &&
|
|
88
|
+
PortableTransaction.same?(@baseline[key], cell.instance_variable_get(:@value))
|
|
89
|
+
end
|
|
90
|
+
@original = FIELDS.map { @index.instance_variable_get(it) }
|
|
91
|
+
@original_cells = @cells.map { |cell| CELL_FIELDS.map { cell.instance_variable_get(it) } }
|
|
92
|
+
@replacement[-1] = @original[-1] + 1
|
|
93
|
+
true
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def apply
|
|
97
|
+
return unless @dirty
|
|
98
|
+
@applied = true
|
|
99
|
+
FIELDS.each_with_index { |field, index| @index.instance_variable_set(field, @replacement[index]) }
|
|
100
|
+
@cells.each do |cell|
|
|
101
|
+
cell.instance_variable_set(:@retired, true)
|
|
102
|
+
cell.instance_variable_set(:@present, false)
|
|
103
|
+
cell.instance_variable_set(:@key, nil)
|
|
104
|
+
cell.instance_variable_set(:@value, nil)
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def restore
|
|
109
|
+
return unless @applied
|
|
110
|
+
FIELDS.each_with_index { |field, index| @index.instance_variable_set(field, @original[index]) }
|
|
111
|
+
@cells.each_with_index do |cell, index|
|
|
112
|
+
CELL_FIELDS.each_with_index do |field, offset|
|
|
113
|
+
cell.instance_variable_set(field, @original_cells[index][offset])
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def notify
|
|
119
|
+
return unless @dirty
|
|
120
|
+
@index.change_signal.broadcast
|
|
121
|
+
@cells.each do |cell|
|
|
122
|
+
cell.instance_variable_get(:@changes)&.broadcast
|
|
123
|
+
cell.instance_variable_get(:@signal)&.broadcast
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
private
|
|
128
|
+
|
|
129
|
+
def capture_cells
|
|
130
|
+
@cells = @index.snapshot
|
|
131
|
+
@locks = [@index_lock.instance_variable_get(:@mutex), @index.instance_variable_get(:@registry_mutex)]
|
|
132
|
+
@locks.concat(@cells.map { it.instance_variable_get(:@mutex) })
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
end
|