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,441 @@
|
|
|
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 per-key ownership and lifecycle implementation for LeaseMap.
|
|
9
|
+
class LeaseMap
|
|
10
|
+
include Freeze::Unfreezable
|
|
11
|
+
|
|
12
|
+
AUTO_LEASE_CONTEXTS = Object.new.freeze
|
|
13
|
+
private_constant :AUTO_LEASE_CONTEXTS
|
|
14
|
+
|
|
15
|
+
def initialize(initial_mapping, lease_class:, registry_class:)
|
|
16
|
+
raise TypeError, "lease map initializer must return a Hash" unless initial_mapping.is_a?(Hash)
|
|
17
|
+
|
|
18
|
+
@lease_class = lease_class
|
|
19
|
+
@registry_lock = Lock.new
|
|
20
|
+
@creation_locks = KeyLockMap.new(registry_class:)
|
|
21
|
+
|
|
22
|
+
initial_mapping.each_key { validate_key!(it) }
|
|
23
|
+
initial_mapping.each_value { validate_resource!(it) }
|
|
24
|
+
|
|
25
|
+
entries = initial_mapping.to_h { |key, resource| [key, new_lease(resource)] }
|
|
26
|
+
@registry = registry_class.new(entries, compare_values_by_identity: true)
|
|
27
|
+
|
|
28
|
+
return unless @registry.ractor_shareable?
|
|
29
|
+
|
|
30
|
+
Freeze.publish(self)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def checkout(key, receiver:, missing_key: key, timeout: nil, &block)
|
|
34
|
+
lease = fetch_lease(key, receiver:, missing_key:)
|
|
35
|
+
if block
|
|
36
|
+
entered = false
|
|
37
|
+
lease.checkout(timeout:) do |resource|
|
|
38
|
+
entered = true
|
|
39
|
+
block.call(resource)
|
|
40
|
+
end
|
|
41
|
+
else
|
|
42
|
+
context = auto_context
|
|
43
|
+
context ? acquire_for_context(lease, context, timeout:) : lease.checkout(timeout:)
|
|
44
|
+
end
|
|
45
|
+
rescue RetiredLeaseError
|
|
46
|
+
raise if entered
|
|
47
|
+
prune_binding(key, lease)
|
|
48
|
+
raise_missing_key(missing_key, receiver)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def try_checkout(key, receiver:, missing_key: key, &block)
|
|
52
|
+
lease = fetch_lease(key, receiver:, missing_key:)
|
|
53
|
+
if block
|
|
54
|
+
entered = false
|
|
55
|
+
lease.try_checkout do |resource|
|
|
56
|
+
entered = true
|
|
57
|
+
block.call(resource)
|
|
58
|
+
end
|
|
59
|
+
else
|
|
60
|
+
context = auto_context
|
|
61
|
+
context ? acquire_for_context(lease, context, try: true) : lease.try_checkout
|
|
62
|
+
end
|
|
63
|
+
rescue RetiredLeaseError
|
|
64
|
+
raise if entered
|
|
65
|
+
prune_binding(key, lease)
|
|
66
|
+
raise_missing_key(missing_key, receiver)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def checkin(key, resource, receiver:, missing_key: key)
|
|
70
|
+
lease = fetch_lease(key, receiver:, missing_key:)
|
|
71
|
+
|
|
72
|
+
if nil.equal?(resource)
|
|
73
|
+
unless explicitly_owned?(lease) && !scope_managed?(lease)
|
|
74
|
+
raise OwnershipError, "the current Fiber does not own an explicit checkout"
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
delete_entry(key, lease)
|
|
78
|
+
return self
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
lease.checkin(resource)
|
|
82
|
+
self
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def read_entry(key)
|
|
86
|
+
lease = current_lease(key)
|
|
87
|
+
return [false, nil] unless lease
|
|
88
|
+
|
|
89
|
+
resource = if lease.owned?
|
|
90
|
+
owned_resource(lease)
|
|
91
|
+
elsif (context = auto_context)
|
|
92
|
+
acquire_for_context(lease, context)
|
|
93
|
+
else
|
|
94
|
+
raise RetiredLeaseError, "lease has been retired" if lease.retired?
|
|
95
|
+
raise OwnershipError, "the current Fiber does not own the key checkout"
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
[true, resource]
|
|
99
|
+
rescue RetiredLeaseError
|
|
100
|
+
prune_binding(key, lease)
|
|
101
|
+
[false, nil]
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def lease_for(key, receiver:, missing_key: key) = fetch_lease(key, receiver:, missing_key:)
|
|
105
|
+
|
|
106
|
+
def store(key, resource)
|
|
107
|
+
key = validate_key!(key)
|
|
108
|
+
return delete(key) if nil.equal?(resource)
|
|
109
|
+
validate_resource!(resource)
|
|
110
|
+
|
|
111
|
+
lease = nil
|
|
112
|
+
inserted = false
|
|
113
|
+
context = auto_context
|
|
114
|
+
@creation_locks.synchronize(key) do
|
|
115
|
+
Thread.handle_interrupt(INTERRUPT_MASK) do
|
|
116
|
+
@registry_lock.synchronize do
|
|
117
|
+
lease = @registry[key]
|
|
118
|
+
if lease&.retired?
|
|
119
|
+
@registry.delete(key)
|
|
120
|
+
lease = nil
|
|
121
|
+
end
|
|
122
|
+
unless lease
|
|
123
|
+
lease = new_lease(resource)
|
|
124
|
+
@registry[key] = lease
|
|
125
|
+
acquire_for_context(lease, context) if context
|
|
126
|
+
inserted = true
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
return resource if inserted
|
|
132
|
+
|
|
133
|
+
replace(key, lease, resource)
|
|
134
|
+
rescue RetiredLeaseError
|
|
135
|
+
prune_binding(key, lease)
|
|
136
|
+
retry
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
def store_if_absent(key, &)
|
|
140
|
+
raise LocalJumpError, "no block given" unless block_given?
|
|
141
|
+
key = validate_key!(key)
|
|
142
|
+
|
|
143
|
+
while true
|
|
144
|
+
present, resource = read_entry(key)
|
|
145
|
+
return resource if present
|
|
146
|
+
|
|
147
|
+
context = auto_context
|
|
148
|
+
raise OwnershipError, "resource construction requires an automatic lease scope" unless context
|
|
149
|
+
|
|
150
|
+
inserted = false
|
|
151
|
+
@creation_locks.synchronize(key) do
|
|
152
|
+
next if current_lease(key)
|
|
153
|
+
|
|
154
|
+
Thread.handle_interrupt(INTERRUPT_MASK) do
|
|
155
|
+
resource = Thread.handle_interrupt(Exception => :immediate, &)
|
|
156
|
+
validate_resource!(resource)
|
|
157
|
+
@registry_lock.synchronize do
|
|
158
|
+
lease = new_lease(resource)
|
|
159
|
+
@registry[key] = lease
|
|
160
|
+
resource = acquire_for_context(lease, context)
|
|
161
|
+
inserted = true
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
return resource if inserted
|
|
166
|
+
end
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
def delete(key)
|
|
170
|
+
lease = current_lease(key)
|
|
171
|
+
return unless lease
|
|
172
|
+
|
|
173
|
+
delete_entry(key, lease)
|
|
174
|
+
rescue RetiredLeaseError
|
|
175
|
+
prune_binding(key, lease)
|
|
176
|
+
nil
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
def clear
|
|
180
|
+
entries_snapshot.each do |key, lease|
|
|
181
|
+
delete_entry(key, lease)
|
|
182
|
+
rescue RetiredLeaseError
|
|
183
|
+
prune_binding(key, lease)
|
|
184
|
+
end
|
|
185
|
+
self
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
def auto_lease
|
|
189
|
+
raise LocalJumpError, "no block given" unless block_given?
|
|
190
|
+
|
|
191
|
+
contexts = context = nil
|
|
192
|
+
pushed = false
|
|
193
|
+
Thread.handle_interrupt(INTERRUPT_MASK) do
|
|
194
|
+
contexts = auto_contexts
|
|
195
|
+
context = {}.compare_by_identity
|
|
196
|
+
(contexts[self] ||= []) << context
|
|
197
|
+
pushed = true
|
|
198
|
+
end
|
|
199
|
+
yield
|
|
200
|
+
ensure
|
|
201
|
+
if pushed
|
|
202
|
+
Thread.handle_interrupt(INTERRUPT_MASK) do
|
|
203
|
+
cleanup_context(context)
|
|
204
|
+
ensure
|
|
205
|
+
stack = contexts[self]
|
|
206
|
+
stack.pop
|
|
207
|
+
contexts.delete(self) if stack.empty?
|
|
208
|
+
end
|
|
209
|
+
end
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
def available?(key) = current_lease(key)&.available? || false
|
|
213
|
+
def checked_out?(key) = current_lease(key)&.checked_out? || false
|
|
214
|
+
def owned?(key) = current_lease(key)&.owned? || false
|
|
215
|
+
def key?(key) = !current_lease(key).nil?
|
|
216
|
+
def size = entries_snapshot.size
|
|
217
|
+
def keys = entries_snapshot.map(&:first)
|
|
218
|
+
def compare_keys_by_identity? = @registry.compare_keys_by_identity?
|
|
219
|
+
def compare_values_by_identity? = true
|
|
220
|
+
def getkey(key) = current_lease(key) && @registry.getkey(key)
|
|
221
|
+
|
|
222
|
+
def each
|
|
223
|
+
return enum_for(__method__) { size } unless block_given?
|
|
224
|
+
|
|
225
|
+
entries_snapshot.each do |key, lease|
|
|
226
|
+
next unless current_binding?(key, lease)
|
|
227
|
+
|
|
228
|
+
if lease.owned?
|
|
229
|
+
yield key, owned_resource(lease)
|
|
230
|
+
else
|
|
231
|
+
entered = false
|
|
232
|
+
begin
|
|
233
|
+
lease.checkout do |resource|
|
|
234
|
+
entered = true
|
|
235
|
+
yield key, resource
|
|
236
|
+
end
|
|
237
|
+
rescue RetiredLeaseError
|
|
238
|
+
raise if entered
|
|
239
|
+
|
|
240
|
+
prune_binding(key, lease)
|
|
241
|
+
end
|
|
242
|
+
end
|
|
243
|
+
end
|
|
244
|
+
self
|
|
245
|
+
end
|
|
246
|
+
|
|
247
|
+
def each_key
|
|
248
|
+
return enum_for(__method__) { size } unless block_given?
|
|
249
|
+
|
|
250
|
+
keys.each { yield it }
|
|
251
|
+
self
|
|
252
|
+
end
|
|
253
|
+
|
|
254
|
+
def each_value
|
|
255
|
+
return enum_for(__method__) { size } unless block_given?
|
|
256
|
+
|
|
257
|
+
each { |_key, value| yield value }
|
|
258
|
+
self
|
|
259
|
+
end
|
|
260
|
+
|
|
261
|
+
def handles = entries_snapshot
|
|
262
|
+
|
|
263
|
+
private
|
|
264
|
+
|
|
265
|
+
def new_lease(resource) = @lease_class.new { resource }
|
|
266
|
+
|
|
267
|
+
def current_lease(key)
|
|
268
|
+
@registry_lock.synchronize do
|
|
269
|
+
lease = @registry[key]
|
|
270
|
+
if lease&.retired?
|
|
271
|
+
@registry.delete(key) if @registry[key].equal?(lease)
|
|
272
|
+
lease = nil
|
|
273
|
+
end
|
|
274
|
+
lease
|
|
275
|
+
end
|
|
276
|
+
end
|
|
277
|
+
|
|
278
|
+
def fetch_lease(key, receiver:, missing_key: key)
|
|
279
|
+
current_lease(key) || raise_missing_key(missing_key, receiver)
|
|
280
|
+
end
|
|
281
|
+
|
|
282
|
+
def raise_missing_key(key, receiver)
|
|
283
|
+
raise KeyError.new("key not found: #{key.inspect}", receiver:, key:)
|
|
284
|
+
end
|
|
285
|
+
|
|
286
|
+
def replace(key, lease, resource)
|
|
287
|
+
if lease.owned?
|
|
288
|
+
replace_owned_resource(lease, resource)
|
|
289
|
+
return resource
|
|
290
|
+
end
|
|
291
|
+
|
|
292
|
+
original = nil
|
|
293
|
+
completed = false
|
|
294
|
+
begin
|
|
295
|
+
if (context = auto_context)
|
|
296
|
+
original = acquire_for_context(lease, context)
|
|
297
|
+
replace_owned_resource(lease, resource)
|
|
298
|
+
else
|
|
299
|
+
lease.__send__(:checkout_with_handoff) { |value| original = value }
|
|
300
|
+
lease.checkin(resource)
|
|
301
|
+
end
|
|
302
|
+
completed = true
|
|
303
|
+
ensure
|
|
304
|
+
if !completed && original && lease.owned? && !lease.retired?
|
|
305
|
+
if scope_managed?(lease)
|
|
306
|
+
lease.__send__(:checkin_scope, owned_resource(lease))
|
|
307
|
+
else
|
|
308
|
+
lease.checkin(original)
|
|
309
|
+
end
|
|
310
|
+
end
|
|
311
|
+
end
|
|
312
|
+
resource
|
|
313
|
+
rescue RetiredLeaseError
|
|
314
|
+
prune_binding(key, lease)
|
|
315
|
+
raise
|
|
316
|
+
end
|
|
317
|
+
|
|
318
|
+
def delete_entry(key, lease)
|
|
319
|
+
return unless current_binding?(key, lease)
|
|
320
|
+
|
|
321
|
+
acquired = false
|
|
322
|
+
resource = nil
|
|
323
|
+
begin
|
|
324
|
+
if lease.owned?
|
|
325
|
+
resource = owned_resource(lease)
|
|
326
|
+
else
|
|
327
|
+
lease.__send__(:checkout_with_handoff) do |value|
|
|
328
|
+
resource = value
|
|
329
|
+
acquired = true
|
|
330
|
+
end
|
|
331
|
+
end
|
|
332
|
+
|
|
333
|
+
removed = Thread.handle_interrupt(INTERRUPT_MASK) do
|
|
334
|
+
@registry_lock.synchronize do
|
|
335
|
+
next false unless @registry[key].equal?(lease)
|
|
336
|
+
|
|
337
|
+
@registry.delete(key)
|
|
338
|
+
remove_auto_resource(lease)
|
|
339
|
+
lease.retire
|
|
340
|
+
true
|
|
341
|
+
end
|
|
342
|
+
end
|
|
343
|
+
return resource if removed
|
|
344
|
+
ensure
|
|
345
|
+
lease.checkin(resource) if acquired && lease.owned? && !lease.retired?
|
|
346
|
+
end
|
|
347
|
+
nil
|
|
348
|
+
end
|
|
349
|
+
|
|
350
|
+
def entries_snapshot
|
|
351
|
+
@registry_lock.synchronize do
|
|
352
|
+
entries = @registry.each_pair.to_a
|
|
353
|
+
entries.reject! do |key, lease|
|
|
354
|
+
next false unless lease.retired?
|
|
355
|
+
|
|
356
|
+
@registry.delete(key) if @registry[key].equal?(lease)
|
|
357
|
+
true
|
|
358
|
+
end
|
|
359
|
+
entries
|
|
360
|
+
end
|
|
361
|
+
end
|
|
362
|
+
|
|
363
|
+
def current_binding?(key, lease)
|
|
364
|
+
@registry_lock.synchronize { @registry[key].equal?(lease) && !lease.retired? }
|
|
365
|
+
end
|
|
366
|
+
|
|
367
|
+
def prune_binding(key, lease)
|
|
368
|
+
return unless lease
|
|
369
|
+
|
|
370
|
+
@registry_lock.synchronize { @registry.delete(key) if @registry[key].equal?(lease) && lease.retired? }
|
|
371
|
+
end
|
|
372
|
+
|
|
373
|
+
def owned_resource(lease) = lease.__send__(:owned_resource)
|
|
374
|
+
|
|
375
|
+
def replace_owned_resource(lease, resource)
|
|
376
|
+
lease.__send__(:replace_owned_resource, resource)
|
|
377
|
+
end
|
|
378
|
+
|
|
379
|
+
def auto_contexts
|
|
380
|
+
Storage.fiber.store_if_absent(AUTO_LEASE_CONTEXTS, mode: :strong) { {}.compare_by_identity }
|
|
381
|
+
end
|
|
382
|
+
|
|
383
|
+
def auto_context
|
|
384
|
+
auto_contexts[self]&.last
|
|
385
|
+
end
|
|
386
|
+
|
|
387
|
+
def acquire_for_context(lease, context, timeout: nil, try: false)
|
|
388
|
+
method = try ? :try_checkout_with_handoff : :checkout_with_handoff
|
|
389
|
+
options = try ? {} : { timeout: }
|
|
390
|
+
already_registered = context.key?(lease)
|
|
391
|
+
lease.__send__(method, **options) do
|
|
392
|
+
lease.__send__(:mark_scope_managed)
|
|
393
|
+
register_context_resource(context, lease)
|
|
394
|
+
end
|
|
395
|
+
rescue Exception # rubocop:disable Lint/RescueException
|
|
396
|
+
context.delete(lease) if !already_registered && context.key?(lease)
|
|
397
|
+
raise
|
|
398
|
+
end
|
|
399
|
+
|
|
400
|
+
def register_context_resource(context, lease)
|
|
401
|
+
context[lease] = true
|
|
402
|
+
end
|
|
403
|
+
|
|
404
|
+
def remove_auto_resource(lease)
|
|
405
|
+
auto_contexts[self]&.each { it.delete(lease) }
|
|
406
|
+
end
|
|
407
|
+
|
|
408
|
+
def cleanup_context(context)
|
|
409
|
+
Thread.handle_interrupt(INTERRUPT_MASK) do
|
|
410
|
+
failure = nil
|
|
411
|
+
context.each_key.to_a.reverse_each do |lease|
|
|
412
|
+
next if lease.retired? || !lease.owned? || !scope_managed?(lease)
|
|
413
|
+
|
|
414
|
+
begin
|
|
415
|
+
lease.__send__(:checkin_scope, owned_resource(lease))
|
|
416
|
+
rescue Exception => e # rubocop:disable Lint/RescueException
|
|
417
|
+
failure ||= e
|
|
418
|
+
end
|
|
419
|
+
end
|
|
420
|
+
raise failure if failure
|
|
421
|
+
end
|
|
422
|
+
end
|
|
423
|
+
|
|
424
|
+
def validate_resource!(resource)
|
|
425
|
+
return unless nil.equal?(resource) || true.equal?(resource) || false.equal?(resource)
|
|
426
|
+
|
|
427
|
+
raise ArgumentError, "lease resource cannot be nil or boolean"
|
|
428
|
+
end
|
|
429
|
+
|
|
430
|
+
def validate_key!(key)
|
|
431
|
+
key = String.instance_method(:-@).bind_call(key) if String === key && !key.frozen?
|
|
432
|
+
return key if Ractor.shareable?(key)
|
|
433
|
+
|
|
434
|
+
raise Ractor::IsolationError, "key must be Ractor-shareable"
|
|
435
|
+
end
|
|
436
|
+
|
|
437
|
+
def explicitly_owned?(lease) = lease.__send__(:explicitly_owned?)
|
|
438
|
+
def scope_managed?(lease) = lease.__send__(:scope_managed?)
|
|
439
|
+
end
|
|
440
|
+
end
|
|
441
|
+
end
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# shareable_constant_value: literal
|
|
3
|
+
# warn_indent: true
|
|
4
|
+
|
|
5
|
+
module Farce
|
|
6
|
+
# @!visibility private
|
|
7
|
+
module Internal # :nodoc: all
|
|
8
|
+
# Coordinates capacity, ownership, waiting, and cleanup for a resource pool.
|
|
9
|
+
class LeasePoolState
|
|
10
|
+
OWNERSHIP = Object.new.freeze
|
|
11
|
+
private_constant :OWNERSHIP
|
|
12
|
+
|
|
13
|
+
attr_reader :max_size
|
|
14
|
+
|
|
15
|
+
def initialize(max_size)
|
|
16
|
+
@max_size = max_size
|
|
17
|
+
@slots = ::Farce::Counter.new
|
|
18
|
+
@managed = Counter.new
|
|
19
|
+
@creating = Counter.new
|
|
20
|
+
@checked_out = Counter.new
|
|
21
|
+
@available = Queue.new(capacity: max_size)
|
|
22
|
+
@signal = Signal.new
|
|
23
|
+
initialize_storage
|
|
24
|
+
finalize_initialization
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def size = @managed.value
|
|
28
|
+
def available_count = @available.size
|
|
29
|
+
def checked_out_count = @checked_out.value
|
|
30
|
+
def creating_count = @creating.value
|
|
31
|
+
|
|
32
|
+
def checkout(factory, timeout: nil, &block)
|
|
33
|
+
return checkout_block(factory, timeout, block) if block
|
|
34
|
+
|
|
35
|
+
checkout_explicit(factory, timeout:, wait: true)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def try_checkout(factory, &block)
|
|
39
|
+
return checkout_block(factory, nil, block, wait: false) if block
|
|
40
|
+
|
|
41
|
+
checkout_explicit(factory, wait: false)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def checkin(resource)
|
|
45
|
+
tokens = explicit_tokens(create: false)
|
|
46
|
+
raise OwnershipError, "the current Fiber does not own an explicit pool checkout" unless tokens&.any?
|
|
47
|
+
validate_resource!(resource) unless nil.equal?(resource)
|
|
48
|
+
|
|
49
|
+
Thread.handle_interrupt(INTERRUPT_MASK) do
|
|
50
|
+
token = tokens.last
|
|
51
|
+
if nil.equal?(resource)
|
|
52
|
+
@checked_out.decrement
|
|
53
|
+
@managed.decrement
|
|
54
|
+
@slots.decrement
|
|
55
|
+
else
|
|
56
|
+
return_resource(token, resource)
|
|
57
|
+
@available.push(token)
|
|
58
|
+
@checked_out.decrement
|
|
59
|
+
end
|
|
60
|
+
tokens.pop
|
|
61
|
+
clear_explicit_tokens if tokens.empty?
|
|
62
|
+
@signal.broadcast
|
|
63
|
+
end
|
|
64
|
+
self
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
private
|
|
68
|
+
|
|
69
|
+
def checkout_explicit(factory, wait:, timeout: nil)
|
|
70
|
+
acquisition = nil
|
|
71
|
+
owned = false
|
|
72
|
+
begin
|
|
73
|
+
Thread.handle_interrupt(INTERRUPT_MASK) do
|
|
74
|
+
acquisition = acquire(factory, timeout:, wait:)
|
|
75
|
+
return unless acquisition
|
|
76
|
+
|
|
77
|
+
explicit_tokens << acquisition.first
|
|
78
|
+
owned = true
|
|
79
|
+
end
|
|
80
|
+
acquisition.last
|
|
81
|
+
rescue Exception # rubocop:disable Lint/RescueException
|
|
82
|
+
rollback_explicit(acquisition) if owned
|
|
83
|
+
raise
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def checkout_block(factory, timeout, block, wait: true)
|
|
88
|
+
acquisition = nil
|
|
89
|
+
Thread.handle_interrupt(INTERRUPT_MASK) do
|
|
90
|
+
acquisition = acquire(factory, timeout:, wait:)
|
|
91
|
+
return unless acquisition
|
|
92
|
+
|
|
93
|
+
begin
|
|
94
|
+
Thread.handle_interrupt(Exception => :immediate) { return block.call(acquisition.last) }
|
|
95
|
+
ensure
|
|
96
|
+
checkin_block(acquisition)
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def acquire(factory, timeout:, wait:)
|
|
102
|
+
deadline = timeout_deadline(timeout)
|
|
103
|
+
|
|
104
|
+
loop do
|
|
105
|
+
observed = @signal.generation
|
|
106
|
+
token = @available.try_pop
|
|
107
|
+
return take_available(token) if token
|
|
108
|
+
return create_resource(factory) if reserve_creation
|
|
109
|
+
return unless wait
|
|
110
|
+
|
|
111
|
+
remaining = deadline - Clock.now if deadline
|
|
112
|
+
raise TimeoutError, "lease pool checkout timed out" if remaining && !remaining.positive?
|
|
113
|
+
interval = LeaseWaiting.wait_interval(remaining)
|
|
114
|
+
changed = Thread.handle_interrupt(Exception => :immediate) do
|
|
115
|
+
@signal.wait(observed, timeout: interval)
|
|
116
|
+
end
|
|
117
|
+
next if changed || interval != remaining
|
|
118
|
+
|
|
119
|
+
raise TimeoutError, "lease pool checkout timed out"
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def take_available(token)
|
|
124
|
+
resource = take_resource(token)
|
|
125
|
+
@checked_out.increment
|
|
126
|
+
[token, resource]
|
|
127
|
+
rescue Exception # rubocop:disable Lint/RescueException
|
|
128
|
+
@available.push(token)
|
|
129
|
+
@signal.broadcast
|
|
130
|
+
raise
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
def reserve_creation # rubocop:disable Naming/PredicateMethod
|
|
134
|
+
return false unless @slots.increment_if_below(max_size)
|
|
135
|
+
|
|
136
|
+
@creating.increment
|
|
137
|
+
true
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
def create_resource(factory)
|
|
141
|
+
committed = false
|
|
142
|
+
stored = false
|
|
143
|
+
token = nil
|
|
144
|
+
begin
|
|
145
|
+
resource = Thread.handle_interrupt(Exception => :immediate) { factory.call }
|
|
146
|
+
validate_resource!(resource)
|
|
147
|
+
token = next_token
|
|
148
|
+
store_resource(token, resource)
|
|
149
|
+
stored = true
|
|
150
|
+
resource = take_resource(token)
|
|
151
|
+
stored = false
|
|
152
|
+
@managed.increment
|
|
153
|
+
@creating.decrement
|
|
154
|
+
@checked_out.increment
|
|
155
|
+
committed = true
|
|
156
|
+
[token, resource]
|
|
157
|
+
ensure
|
|
158
|
+
unless committed
|
|
159
|
+
discard_resource(token) if stored
|
|
160
|
+
@creating.decrement
|
|
161
|
+
@slots.decrement
|
|
162
|
+
@signal.broadcast
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
def checkin_block(acquisition)
|
|
168
|
+
token, resource = acquisition
|
|
169
|
+
return_resource(token, resource)
|
|
170
|
+
@available.push(token)
|
|
171
|
+
@checked_out.decrement
|
|
172
|
+
@signal.broadcast
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
def rollback_explicit(acquisition)
|
|
176
|
+
Thread.handle_interrupt(INTERRUPT_MASK) do
|
|
177
|
+
token, resource = acquisition
|
|
178
|
+
return_resource(token, resource)
|
|
179
|
+
@available.push(token)
|
|
180
|
+
@checked_out.decrement
|
|
181
|
+
tokens = explicit_tokens
|
|
182
|
+
tokens.pop
|
|
183
|
+
clear_explicit_tokens if tokens.empty?
|
|
184
|
+
@signal.broadcast
|
|
185
|
+
end
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
def explicit_tokens(create: true)
|
|
189
|
+
ownership = Storage.fiber[OWNERSHIP, :strong]
|
|
190
|
+
if create
|
|
191
|
+
ownership ||= Storage.fiber.store_if_absent(OWNERSHIP, mode: :strong) { {}.compare_by_identity }
|
|
192
|
+
ownership[self] ||= []
|
|
193
|
+
else
|
|
194
|
+
ownership&.[](self)
|
|
195
|
+
end
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
def clear_explicit_tokens
|
|
199
|
+
Storage.fiber[OWNERSHIP, :strong]&.delete(self)
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
def timeout_deadline(timeout)
|
|
203
|
+
return if timeout.nil?
|
|
204
|
+
|
|
205
|
+
timeout = Float(timeout)
|
|
206
|
+
unless timeout.finite? && !timeout.negative?
|
|
207
|
+
raise ArgumentError, "timeout must be a finite, non-negative number or nil"
|
|
208
|
+
end
|
|
209
|
+
Clock.now + timeout
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
def next_token = Object.new.freeze
|
|
213
|
+
|
|
214
|
+
def validate_resource!(resource)
|
|
215
|
+
return unless nil.equal?(resource) || true.equal?(resource) || false.equal?(resource)
|
|
216
|
+
|
|
217
|
+
raise ArgumentError, "lease pool resource cannot be nil or boolean"
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
def initialize_storage = nil
|
|
221
|
+
def finalize_initialization = nil
|
|
222
|
+
def store_resource(*) = raise(NoMethodError, "abstract lease pool storage")
|
|
223
|
+
def take_resource(*) = raise(NoMethodError, "abstract lease pool storage")
|
|
224
|
+
def return_resource(*) = raise(NoMethodError, "abstract lease pool storage")
|
|
225
|
+
def discard_resource(*) = raise(NoMethodError, "abstract lease pool storage")
|
|
226
|
+
end
|
|
227
|
+
end
|
|
228
|
+
end
|