farce 0 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +9 -0
- data/CODE_OF_CONDUCT.md +26 -0
- data/CONTRIBUTING.md +71 -0
- data/MIT-LICENSE +20 -0
- data/README.md +1525 -0
- data/SECURITY.md +10 -0
- data/docs/benchmarks.md +185 -0
- data/docs/gems/dry-types.md +290 -0
- data/docs/gems/msgpack.md +70 -0
- data/docs/gems/ractor-shim.md +63 -0
- data/docs/modes.md +628 -0
- data/docs/scopes.md +649 -0
- data/docs/variants.md +346 -0
- data/ext/ext_helper.rb +20 -0
- data/ext/farce/README.md +21 -0
- data/ext/farce/atom.c +1023 -0
- data/ext/farce/bounded_map.c +1683 -0
- data/ext/farce/containers.h +77 -0
- data/ext/farce/counter.c +399 -0
- data/ext/farce/darwin.c +100 -0
- data/ext/farce/depend +12 -0
- data/ext/farce/dict.c +1523 -0
- data/ext/farce/dict.h +152 -0
- data/ext/farce/drivers.c +237 -0
- data/ext/farce/exchanger.c +299 -0
- data/ext/farce/extconf.rb +99 -0
- data/ext/farce/farce.c +359 -0
- data/ext/farce/flag.c +288 -0
- data/ext/farce/io.c +338 -0
- data/ext/farce/lock.c +510 -0
- data/ext/farce/map.c +2240 -0
- data/ext/farce/priority_queue.c +2056 -0
- data/ext/farce/queue.c +1059 -0
- data/ext/farce/reactor.c +820 -0
- data/ext/farce/reactor.h +103 -0
- data/ext/farce/shareable.h +31 -0
- data/ext/farce/signal.c +350 -0
- data/ext/farce/transaction.c +354 -0
- data/ext/farce/transaction.h +40 -0
- data/ext/farce/tree_map.c +1953 -0
- data/ext/farce/trie.c +2020 -0
- data/ext/farce/unshareable.c +155 -0
- data/ext/farce/unshared_io_pool.h +234 -0
- data/ext/farce/unshared_signal.c +263 -0
- data/ext/farce/unshared_wait.h +193 -0
- data/ext/farce/unsupported.c +6 -0
- data/ext/farce/vector.c +1195 -0
- data/ext/farce/weak_map.c +1714 -0
- data/ext/java/org/farce/BoundedMap.java +394 -0
- data/ext/java/org/farce/FiberScheduler.java +141 -0
- data/ext/java/org/farce/PriorityKey.java +45 -0
- data/ext/java/org/farce/PriorityQueue.java +373 -0
- data/ext/java/org/farce/QueueSignal.java +28 -0
- data/ext/rebind/README.md +14 -0
- data/ext/rebind/extconf.rb +10 -0
- data/ext/rebind/rebind.c +186 -0
- data/lib/farce/_yard/internal.rb +13 -0
- data/lib/farce/_yard/macros.rb +61 -0
- data/lib/farce/_yard/ractor.rb +46 -0
- data/lib/farce/abstract/atom.rb +186 -0
- data/lib/farce/abstract/bounded_map.rb +232 -0
- data/lib/farce/abstract/collection.rb +151 -0
- data/lib/farce/abstract/concurrent_map.rb +381 -0
- data/lib/farce/abstract/counter.rb +193 -0
- data/lib/farce/abstract/duplicable_map.rb +229 -0
- data/lib/farce/abstract/exchanger.rb +26 -0
- data/lib/farce/abstract/flag.rb +71 -0
- data/lib/farce/abstract/lazy.rb +115 -0
- data/lib/farce/abstract/lease.rb +104 -0
- data/lib/farce/abstract/lease_map.rb +261 -0
- data/lib/farce/abstract/lease_pool.rb +91 -0
- data/lib/farce/abstract/lfu_map.rb +20 -0
- data/lib/farce/abstract/lru_map.rb +25 -0
- data/lib/farce/abstract/map.rb +345 -0
- data/lib/farce/abstract/molecule.rb +245 -0
- data/lib/farce/abstract/port.rb +74 -0
- data/lib/farce/abstract/priority_queue.rb +117 -0
- data/lib/farce/abstract/queue.rb +269 -0
- data/lib/farce/abstract/scheduler.rb +111 -0
- data/lib/farce/abstract/set.rb +910 -0
- data/lib/farce/abstract/sorted_set.rb +172 -0
- data/lib/farce/abstract/timer_queue.rb +136 -0
- data/lib/farce/abstract/tree_map.rb +269 -0
- data/lib/farce/abstract/value.rb +68 -0
- data/lib/farce/abstract/vector.rb +1126 -0
- data/lib/farce/abstract/weak_atom.rb +32 -0
- data/lib/farce/abstract/weak_key_map.rb +12 -0
- data/lib/farce/abstract/weak_map.rb +13 -0
- data/lib/farce/abstract/weak_set.rb +12 -0
- data/lib/farce/abstract/weak_value_map.rb +12 -0
- data/lib/farce/abstract.rb +17 -0
- data/lib/farce/atom.rb +293 -0
- data/lib/farce/class_mirror.rb +87 -0
- data/lib/farce/clock.rb +121 -0
- data/lib/farce/config.rb +229 -0
- data/lib/farce/counter.rb +71 -0
- data/lib/farce/deduper.rb +122 -0
- data/lib/farce/engine/jruby/bounded_map.rb +314 -0
- data/lib/farce/engine/jruby/fiber_scheduler.jar +0 -0
- data/lib/farce/engine/jruby/fiber_scheduler.rb +119 -0
- data/lib/farce/engine/jruby/lease_waiting.rb +19 -0
- data/lib/farce/engine/jruby/map.rb +505 -0
- data/lib/farce/engine/jruby/mutable_numeric_copy.rb +42 -0
- data/lib/farce/engine/jruby/signal.rb +147 -0
- data/lib/farce/engine/jruby.rb +63 -0
- data/lib/farce/engine/jvm/concurrent_weak_registry.rb +55 -0
- data/lib/farce/engine/jvm/counter.rb +102 -0
- data/lib/farce/engine/jvm/extension.rb +32 -0
- data/lib/farce/engine/jvm/farce.jar +0 -0
- data/lib/farce/engine/jvm/flag.rb +79 -0
- data/lib/farce/engine/jvm/priority_queue.rb +217 -0
- data/lib/farce/engine/jvm/tree_map.rb +350 -0
- data/lib/farce/engine/jvm/types.rb +180 -0
- data/lib/farce/engine/jvm.rb +19 -0
- data/lib/farce/engine/ruby/3.4/fiber_scheduler.rb +20 -0
- data/lib/farce/engine/ruby/3.4/port.rb +186 -0
- data/lib/farce/engine/ruby/3.4/ractor_methods.rb +26 -0
- data/lib/farce/engine/ruby/3.4/ractor_selector.rb +92 -0
- data/lib/farce/engine/ruby/3.4/vault.rb +56 -0
- data/lib/farce/engine/ruby/4.0/port.rb +19 -0
- data/lib/farce/engine/ruby/4.0/ractor_methods.rb +20 -0
- data/lib/farce/engine/ruby/4.0/ractor_selector.rb +108 -0
- data/lib/farce/engine/ruby/4.0/vault.rb +74 -0
- data/lib/farce/engine/ruby/4.1/port.rb +21 -0
- data/lib/farce/engine/ruby/4.1/ractor_methods.rb +22 -0
- data/lib/farce/engine/ruby/4.1/ractor_selector.rb +25 -0
- data/lib/farce/engine/ruby/4.1/vault.rb +5 -0
- data/lib/farce/engine/ruby/fiber_scheduler.rb +32 -0
- data/lib/farce/engine/ruby/key_lock_map.rb +28 -0
- data/lib/farce/engine/ruby/shared/lease.rb +26 -0
- data/lib/farce/engine/ruby/shared/lease_pool.rb +24 -0
- data/lib/farce/engine/ruby/shared/main_scheduler.rb +9 -0
- data/lib/farce/engine/ruby/shared/parallel_scheduler.rb +9 -0
- data/lib/farce/engine/ruby/shared/proxy_owner.rb +31 -0
- data/lib/farce/engine/ruby/shared/ractor_methods.rb +34 -0
- data/lib/farce/engine/ruby/shared/ractor_selector.rb +328 -0
- data/lib/farce/engine/ruby/shared/strict_map.rb +13 -0
- data/lib/farce/engine/ruby/shared/unshared_vector.rb +14 -0
- data/lib/farce/engine/ruby/shared/vault.rb +144 -0
- data/lib/farce/engine/ruby/shared/vault_weak_map.rb +336 -0
- data/lib/farce/engine/ruby/shared/weak_atom.rb +54 -0
- data/lib/farce/engine/ruby/shared/weak_map.rb +413 -0
- data/lib/farce/engine/ruby.rb +130 -0
- data/lib/farce/engine/shared/atom.rb +10 -0
- data/lib/farce/engine/shared/exchanger.rb +130 -0
- data/lib/farce/engine/shared/identity_key.rb +22 -0
- data/lib/farce/engine/shared/lease.rb +10 -0
- data/lib/farce/engine/shared/lease_pool.rb +10 -0
- data/lib/farce/engine/shared/main_scheduler.rb +20 -0
- data/lib/farce/engine/shared/map_key_coordination.rb +226 -0
- data/lib/farce/engine/shared/parallel_scheduler.rb +9 -0
- data/lib/farce/engine/shared/port.rb +44 -0
- data/lib/farce/engine/shared/portable_bounded_map.rb +619 -0
- data/lib/farce/engine/shared/proxy_owner.rb +43 -0
- data/lib/farce/engine/shared/queue.rb +233 -0
- data/lib/farce/engine/shared/ractor_methods.rb +73 -0
- data/lib/farce/engine/shared/rebindable.rb +15 -0
- data/lib/farce/engine/shared/strict_atom.rb +73 -0
- data/lib/farce/engine/shared/strict_map.rb +117 -0
- data/lib/farce/engine/shared/strict_queue_values.rb +27 -0
- data/lib/farce/engine/shared/strict_tree_map.rb +56 -0
- data/lib/farce/engine/shared/transaction_map_backend.rb +20 -0
- data/lib/farce/engine/shared/trie.rb +317 -0
- data/lib/farce/engine/shared/trie_builder.rb +199 -0
- data/lib/farce/engine/shared/unshareable.rb +14 -0
- data/lib/farce/engine/shared/unshared_atom.rb +256 -0
- data/lib/farce/engine/shared/unshared_priority_queue.rb +9 -0
- data/lib/farce/engine/shared/unshared_queue.rb +18 -0
- data/lib/farce/engine/shared/unshared_signal.rb +21 -0
- data/lib/farce/engine/shared/unshared_vector.rb +377 -0
- data/lib/farce/engine/shared/unshared_weak_atom.rb +30 -0
- data/lib/farce/engine/shared/unshared_weak_map.rb +445 -0
- data/lib/farce/engine/shared/vault.rb +31 -0
- data/lib/farce/engine/shared/vector.rb +83 -0
- data/lib/farce/engine/shared/weak_atom/base.rb +189 -0
- data/lib/farce/engine/shared/weak_atom.rb +20 -0
- data/lib/farce/engine/shared/weak_map/cell.rb +99 -0
- data/lib/farce/engine/shared/weak_map/index.rb +249 -0
- data/lib/farce/engine/shared/weak_map/lock.rb +80 -0
- data/lib/farce/engine/shared/weak_map/reference.rb +33 -0
- data/lib/farce/engine/shared.rb +63 -0
- data/lib/farce/engine/truffleruby/fiber_scheduler.rb +13 -0
- data/lib/farce/engine/truffleruby/lock.rb +121 -0
- data/lib/farce/engine/truffleruby/map.rb +656 -0
- data/lib/farce/engine/truffleruby/native/counter.rb +105 -0
- data/lib/farce/engine/truffleruby/native/flag.rb +77 -0
- data/lib/farce/engine/truffleruby/native/ordered_array_support.rb +67 -0
- data/lib/farce/engine/truffleruby/native/priority_queue.rb +388 -0
- data/lib/farce/engine/truffleruby/native/tree_map.rb +51 -0
- data/lib/farce/engine/truffleruby/native/unsafe_tree_map.rb +350 -0
- data/lib/farce/engine/truffleruby/signal.rb +77 -0
- data/lib/farce/engine/truffleruby.rb +148 -0
- data/lib/farce/envelope.rb +335 -0
- data/lib/farce/error.rb +35 -0
- data/lib/farce/exchanger.rb +41 -0
- data/lib/farce/flag.rb +27 -0
- data/lib/farce/integrations/active_support/blank.rb +68 -0
- data/lib/farce/integrations/active_support/clock.rb +17 -0
- data/lib/farce/integrations/active_support/duplicable.rb +78 -0
- data/lib/farce/integrations/active_support/map.rb +175 -0
- data/lib/farce/integrations/active_support/set.rb +60 -0
- data/lib/farce/integrations/active_support/value_serialization.rb +17 -0
- data/lib/farce/integrations/active_support/vector.rb +231 -0
- data/lib/farce/integrations/active_support.rb +9 -0
- data/lib/farce/integrations/activesupport.rb +5 -0
- data/lib/farce/integrations/bson.rb +108 -0
- data/lib/farce/integrations/cbor.rb +51 -0
- data/lib/farce/integrations/concurrent.rb +112 -0
- data/lib/farce/integrations/dry-types.rb +5 -0
- data/lib/farce/integrations/dry_types.rb +694 -0
- data/lib/farce/integrations/json.rb +7 -0
- data/lib/farce/integrations/msgpack.rb +112 -0
- data/lib/farce/integrations/oj.rb +69 -0
- data/lib/farce/integrations/psych.rb +191 -0
- data/lib/farce/integrations/ractor-sharing.rb +5 -0
- data/lib/farce/integrations/ractor-tmvar.rb +5 -0
- data/lib/farce/integrations/ractor_sharing.rb +172 -0
- data/lib/farce/integrations/ractor_tmvar.rb +64 -0
- data/lib/farce/integrations/shared/to_json.rb +42 -0
- data/lib/farce/integrations/sorted_set.rb +11 -0
- data/lib/farce/integrations/weakref.rb +38 -0
- data/lib/farce/integrations/yajl.rb +10 -0
- data/lib/farce/integrations.rb +156 -0
- data/lib/farce/internal/_frozen_config.rb +11 -0
- data/lib/farce/internal/autoloads.rb +47 -0
- data/lib/farce/internal/blocking_priority_queue.rb +122 -0
- data/lib/farce/internal/converter.rb +106 -0
- data/lib/farce/internal/copyable.rb +31 -0
- data/lib/farce/internal/delegation.rb +20 -0
- data/lib/farce/internal/external_transaction.rb +59 -0
- data/lib/farce/internal/fake_ractor.rb +179 -0
- data/lib/farce/internal/freeze.rb +118 -0
- data/lib/farce/internal/inspect.rb +157 -0
- data/lib/farce/internal/key_lock_map.rb +29 -0
- data/lib/farce/internal/key_normalizer.rb +289 -0
- data/lib/farce/internal/lease_initialization.rb +115 -0
- data/lib/farce/internal/lease_map.rb +441 -0
- data/lib/farce/internal/lease_pool_state.rb +228 -0
- data/lib/farce/internal/lease_state.rb +342 -0
- data/lib/farce/internal/lease_waiting.rb +13 -0
- data/lib/farce/internal/managed_queue.rb +26 -0
- data/lib/farce/internal/map_value_modes.rb +227 -0
- data/lib/farce/internal/marshal_support.rb +227 -0
- data/lib/farce/internal/mixin.rb +20 -0
- data/lib/farce/internal/mutable_ordered_key_lock_map.rb +63 -0
- data/lib/farce/internal/noncopyable.rb +17 -0
- data/lib/farce/internal/ordered_key_lock_map.rb +69 -0
- data/lib/farce/internal/pool_supervisor.rb +41 -0
- data/lib/farce/internal/pool_worker.rb +61 -0
- data/lib/farce/internal/portable_transaction/reservation_entry.rb +29 -0
- data/lib/farce/internal/portable_transaction/strong_map_entry.rb +137 -0
- data/lib/farce/internal/portable_transaction/strong_map_size_entry.rb +44 -0
- data/lib/farce/internal/portable_transaction/tree_entry.rb +64 -0
- data/lib/farce/internal/portable_transaction.rb +164 -0
- data/lib/farce/internal/proxy_owner_notifications.rb +29 -0
- data/lib/farce/internal/reservation_waiting.rb +24 -0
- data/lib/farce/internal/scheduled_task.rb +48 -0
- data/lib/farce/internal/scheduler_io.rb +171 -0
- data/lib/farce/internal/scheduler_lifecycle.rb +314 -0
- data/lib/farce/internal/select_scheduler.rb +235 -0
- data/lib/farce/internal/storage.rb +162 -0
- data/lib/farce/internal/strict_lease.rb +30 -0
- data/lib/farce/internal/strict_lease_map.rb +20 -0
- data/lib/farce/internal/strict_lease_pool.rb +29 -0
- data/lib/farce/internal/thread_pool.rb +166 -0
- data/lib/farce/internal/transaction_conflict.rb +9 -0
- data/lib/farce/internal/transaction_freeze_guard.rb +30 -0
- data/lib/farce/internal/transaction_map_snapshot.rb +123 -0
- data/lib/farce/internal/undefined.rb +22 -0
- data/lib/farce/internal/unshared_lease.rb +29 -0
- data/lib/farce/internal/unshared_lease_pool.rb +34 -0
- data/lib/farce/internal/unshared_queue_waiting.rb +28 -0
- data/lib/farce/internal/value_serialization.rb +13 -0
- data/lib/farce/internal/weak_map_value_modes.rb +58 -0
- data/lib/farce/internal/weak_mode_manager.rb +26 -0
- data/lib/farce/internal.rb +129 -0
- data/lib/farce/lazy.rb +100 -0
- data/lib/farce/lazy_ref.rb +40 -0
- data/lib/farce/lease.rb +28 -0
- data/lib/farce/lease_map.rb +34 -0
- data/lib/farce/lease_pool.rb +29 -0
- data/lib/farce/lfu_map.rb +53 -0
- data/lib/farce/local/atom.rb +23 -0
- data/lib/farce/local/counter.rb +88 -0
- data/lib/farce/local/flag.rb +65 -0
- data/lib/farce/local/lazy.rb +38 -0
- data/lib/farce/local/lazy_ref.rb +36 -0
- data/lib/farce/local/lease.rb +77 -0
- data/lib/farce/local/lease_map.rb +80 -0
- data/lib/farce/local/lease_pool.rb +38 -0
- data/lib/farce/local/lfu_map.rb +55 -0
- data/lib/farce/local/lru_map.rb +62 -0
- data/lib/farce/local/map.rb +47 -0
- data/lib/farce/local/molecule.rb +45 -0
- data/lib/farce/local/priority_queue.rb +53 -0
- data/lib/farce/local/queue.rb +32 -0
- data/lib/farce/local/scoped.rb +166 -0
- data/lib/farce/local/set.rb +18 -0
- data/lib/farce/local/sorted_set.rb +50 -0
- data/lib/farce/local/timer_queue.rb +41 -0
- data/lib/farce/local/tree_map.rb +47 -0
- data/lib/farce/local/vector.rb +30 -0
- data/lib/farce/local/weak_atom.rb +23 -0
- data/lib/farce/local/weak_key_map.rb +39 -0
- data/lib/farce/local/weak_map.rb +39 -0
- data/lib/farce/local/weak_set.rb +18 -0
- data/lib/farce/local/weak_value_map.rb +39 -0
- data/lib/farce/local.rb +35 -0
- data/lib/farce/lock.rb +54 -0
- data/lib/farce/lru_map.rb +63 -0
- data/lib/farce/map.rb +58 -0
- data/lib/farce/mode_manager.rb +142 -0
- data/lib/farce/molecule.rb +60 -0
- data/lib/farce/mutable.rb +171 -0
- data/lib/farce/pool.rb +332 -0
- data/lib/farce/port.rb +134 -0
- data/lib/farce/priority_queue.rb +70 -0
- data/lib/farce/proxy/register.rb +101 -0
- data/lib/farce/proxy/supervisor.rb +137 -0
- data/lib/farce/proxy/wrapper.rb +49 -0
- data/lib/farce/proxy.rb +291 -0
- data/lib/farce/queue.rb +60 -0
- data/lib/farce/ractor.rb +269 -0
- data/lib/farce/read_write_lock.rb +256 -0
- data/lib/farce/reference.rb +158 -0
- data/lib/farce/resolv/dns.rb +42 -0
- data/lib/farce/resolv.rb +85 -0
- data/lib/farce/scheduler.rb +536 -0
- data/lib/farce/set.rb +39 -0
- data/lib/farce/shareable.rb +150 -0
- data/lib/farce/signal.rb +97 -0
- data/lib/farce/sorted_set.rb +36 -0
- data/lib/farce/strict/atom.rb +34 -0
- data/lib/farce/strict/counter.rb +10 -0
- data/lib/farce/strict/exchanger.rb +25 -0
- data/lib/farce/strict/flag.rb +10 -0
- data/lib/farce/strict/lazy.rb +26 -0
- data/lib/farce/strict/lazy_ref.rb +26 -0
- data/lib/farce/strict/lease.rb +27 -0
- data/lib/farce/strict/lease_map.rb +33 -0
- data/lib/farce/strict/lease_pool.rb +28 -0
- data/lib/farce/strict/lfu_map.rb +27 -0
- data/lib/farce/strict/lru_map.rb +28 -0
- data/lib/farce/strict/map.rb +54 -0
- data/lib/farce/strict/molecule.rb +16 -0
- data/lib/farce/strict/port.rb +40 -0
- data/lib/farce/strict/priority_queue.rb +20 -0
- data/lib/farce/strict/queue.rb +17 -0
- data/lib/farce/strict/set.rb +14 -0
- data/lib/farce/strict/sorted_set.rb +19 -0
- data/lib/farce/strict/timer_queue.rb +13 -0
- data/lib/farce/strict/tree_map.rb +27 -0
- data/lib/farce/strict/vector.rb +24 -0
- data/lib/farce/strict/weak_atom.rb +31 -0
- data/lib/farce/strict/weak_key_map.rb +49 -0
- data/lib/farce/strict/weak_map.rb +49 -0
- data/lib/farce/strict/weak_set.rb +14 -0
- data/lib/farce/strict/weak_value_map.rb +48 -0
- data/lib/farce/strict.rb +28 -0
- data/lib/farce/system.rb +40 -0
- data/lib/farce/thread_scheduler.rb +70 -0
- data/lib/farce/timer_queue.rb +66 -0
- data/lib/farce/transaction/atom.rb +73 -0
- data/lib/farce/transaction/map.rb +25 -0
- data/lib/farce/transaction/map_operations.rb +143 -0
- data/lib/farce/transaction/molecule.rb +90 -0
- data/lib/farce/transaction/mutable.rb +61 -0
- data/lib/farce/transaction/set.rb +17 -0
- data/lib/farce/transaction/set_operations.rb +48 -0
- data/lib/farce/transaction/sorted_set.rb +18 -0
- data/lib/farce/transaction/tree_map.rb +78 -0
- data/lib/farce/transaction/vector.rb +139 -0
- data/lib/farce/transaction/wrapper.rb +202 -0
- data/lib/farce/transaction.rb +343 -0
- data/lib/farce/tree_map.rb +56 -0
- data/lib/farce/unsafe/lfu_map.rb +36 -0
- data/lib/farce/unsafe/lru_map.rb +36 -0
- data/lib/farce/unsafe/tree_map.rb +21 -0
- data/lib/farce/unsafe.rb +29 -0
- data/lib/farce/unshareable.rb +67 -0
- data/lib/farce/unshared/atom.rb +24 -0
- data/lib/farce/unshared/counter.rb +10 -0
- data/lib/farce/unshared/flag.rb +10 -0
- data/lib/farce/unshared/lazy.rb +38 -0
- data/lib/farce/unshared/lazy_ref.rb +26 -0
- data/lib/farce/unshared/lease.rb +27 -0
- data/lib/farce/unshared/lease_map.rb +26 -0
- data/lib/farce/unshared/lease_pool.rb +25 -0
- data/lib/farce/unshared/lfu_map.rb +20 -0
- data/lib/farce/unshared/lru_map.rb +20 -0
- data/lib/farce/unshared/map.rb +49 -0
- data/lib/farce/unshared/molecule.rb +16 -0
- data/lib/farce/unshared/priority_queue.rb +16 -0
- data/lib/farce/unshared/queue.rb +27 -0
- data/lib/farce/unshared/set.rb +14 -0
- data/lib/farce/unshared/sorted_set.rb +28 -0
- data/lib/farce/unshared/timer_queue.rb +16 -0
- data/lib/farce/unshared/tree_map.rb +21 -0
- data/lib/farce/unshared/vector.rb +19 -0
- data/lib/farce/unshared/weak_atom.rb +31 -0
- data/lib/farce/unshared/weak_key_map.rb +44 -0
- data/lib/farce/unshared/weak_map.rb +44 -0
- data/lib/farce/unshared/weak_set.rb +14 -0
- data/lib/farce/unshared/weak_value_map.rb +43 -0
- data/lib/farce/unshared.rb +28 -0
- data/lib/farce/vector.rb +228 -0
- data/lib/farce/version.rb +8 -0
- data/lib/farce/walker/definitions.rb +186 -0
- data/lib/farce/walker/modification.rb +355 -0
- data/lib/farce/walker.rb +319 -0
- data/lib/farce/weak_atom.rb +121 -0
- data/lib/farce/weak_key_map.rb +31 -0
- data/lib/farce/weak_map.rb +33 -0
- data/lib/farce/weak_ref.rb +67 -0
- data/lib/farce/weak_set.rb +82 -0
- data/lib/farce/weak_value.rb +195 -0
- data/lib/farce/weak_value_map.rb +32 -0
- data/lib/farce.rb +519 -0
- metadata +435 -8
data/lib/farce/queue.rb
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# shareable_constant_value: literal
|
|
3
|
+
# warn_indent: true
|
|
4
|
+
|
|
5
|
+
module Farce
|
|
6
|
+
# A shareable FIFO queue with transfer modes for unshareable values.
|
|
7
|
+
class Queue < Farce::Abstract::Queue
|
|
8
|
+
include Internal::ManagedQueue
|
|
9
|
+
include Shareable::Unfreezable
|
|
10
|
+
|
|
11
|
+
# (see Farce::Abstract::Queue#initialize)
|
|
12
|
+
# @!macro modes
|
|
13
|
+
# @param mode [Symbol] the default mode for sending unshareable values
|
|
14
|
+
def initialize(capacity: 1024, mode: :copy, track_age: false)
|
|
15
|
+
@manager = ModeManager.new(mode:)
|
|
16
|
+
@queue = Internal::Queue.new(capacity:, track_age:)
|
|
17
|
+
super()
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# (see Farce::Abstract::Queue#push)
|
|
21
|
+
# @!macro modes
|
|
22
|
+
# @param mode [Symbol] the default mode for sending unshareable values
|
|
23
|
+
def push(value, non_block = false, timeout: nil, mode: nil) # rubocop:disable Style/OptionalBooleanParameter
|
|
24
|
+
value = @manager.wrap(value, mode:)
|
|
25
|
+
|
|
26
|
+
if non_block
|
|
27
|
+
return true if @queue.try_push(value)
|
|
28
|
+
raise ThreadError, "queue full"
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
timeout.nil? ? @queue.push(value) : @queue.push(value, timeout:)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# (see Farce::Abstract::Queue#try_push)
|
|
35
|
+
# @!macro modes
|
|
36
|
+
# @param mode [Symbol] the default mode for sending unshareable values
|
|
37
|
+
def try_push(value, mode: nil)
|
|
38
|
+
return true if @queue.try_push(@manager.wrap(value, mode:))
|
|
39
|
+
block_given? ? yield : false
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# (see Farce::Abstract::Queue#pop)
|
|
43
|
+
def pop(non_block = false, timeout: nil) # rubocop:disable Style/OptionalBooleanParameter
|
|
44
|
+
return try_pop { raise ThreadError, "queue empty" } if non_block
|
|
45
|
+
|
|
46
|
+
empty = false
|
|
47
|
+
result = timeout.nil? ? @queue.pop { empty = true } : @queue.pop(timeout:) { empty = true }
|
|
48
|
+
return @manager.unwrap(result) unless empty
|
|
49
|
+
yield if block_given?
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# (see Farce::Abstract::Queue#try_pop)
|
|
53
|
+
def try_pop
|
|
54
|
+
empty = false
|
|
55
|
+
result = @queue.try_pop { empty = true }
|
|
56
|
+
return @manager.unwrap(result) unless empty
|
|
57
|
+
yield if block_given?
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
data/lib/farce/ractor.rb
ADDED
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# shareable_constant_value: literal
|
|
3
|
+
# warn_indent: true
|
|
4
|
+
|
|
5
|
+
module Farce
|
|
6
|
+
# Entry point for Ractor support.
|
|
7
|
+
#
|
|
8
|
+
# This is always a module, but if native Ractor support is available:
|
|
9
|
+
# * It gets mixed into Ractor. This has no effect beyond another entry in the inheritance chain,
|
|
10
|
+
# so `is_a?(Farce::Ractor)` and `Farce::Ractor === ractor` work as expected.
|
|
11
|
+
# * It delegates, wraps, or backports known Ractor class methods.
|
|
12
|
+
#
|
|
13
|
+
# If not, then this module provides a polyfill for Ractors. With the following caveats:
|
|
14
|
+
# * It uses thread groups to keep track of Thread-to-Ractor associations.
|
|
15
|
+
# * All objects are considered shareable.
|
|
16
|
+
# * It does not monkey-patch `Thread`. This means in particular:
|
|
17
|
+
# * `Thread.list` will include all threads from all Ractors.
|
|
18
|
+
# Use {.threads Farce::Ractor.threads} instead.
|
|
19
|
+
# * `Thread.main` will return the main thread of the main Ractor, even if called from a different Ractor.
|
|
20
|
+
# Use {.main_thread Farce::Ractor.main_thread} instead.
|
|
21
|
+
#
|
|
22
|
+
# @!method [](key)
|
|
23
|
+
# @!scope class
|
|
24
|
+
# Retrieves a value from the current Ractor's storage by key.
|
|
25
|
+
# @param key [Symbol] The key to retrieve.
|
|
26
|
+
# @return [BasicObject, nil] The value associated with the key, or `nil` if not found.
|
|
27
|
+
#
|
|
28
|
+
# @!method []=(key, value)
|
|
29
|
+
# @!scope class
|
|
30
|
+
# Stores a value in the current Ractor's storage by key.
|
|
31
|
+
# @param key [Symbol] The key to store the value under.
|
|
32
|
+
# @param value [BasicObject] The value to store.
|
|
33
|
+
#
|
|
34
|
+
# @!method builtin?
|
|
35
|
+
# @!scope class
|
|
36
|
+
# @note This method isn't part of the official Ractor API, but is compatible with the `ractor-shim` gem.
|
|
37
|
+
# @return [Boolean] `true` if native Ractors are available, `false` otherwise.
|
|
38
|
+
#
|
|
39
|
+
# @!method count
|
|
40
|
+
# @!scope class
|
|
41
|
+
# Returns the number of Ractors.
|
|
42
|
+
# @return [Integer] The number of Ractors.
|
|
43
|
+
#
|
|
44
|
+
# @!method current
|
|
45
|
+
# @!scope class
|
|
46
|
+
# Returns the current Ractor instance.
|
|
47
|
+
# @return [Ractor] The current Ractor instance.
|
|
48
|
+
#
|
|
49
|
+
# @!method main
|
|
50
|
+
# @!scope class
|
|
51
|
+
# Returns the main Ractor instance.
|
|
52
|
+
# @return [Ractor] The main Ractor instance.
|
|
53
|
+
#
|
|
54
|
+
# @!method make_shareable(object, copy: false)
|
|
55
|
+
# @!scope class
|
|
56
|
+
# @note If native Ractors aren't available, this method simply returns the object, as all objects are shareable.
|
|
57
|
+
# Makes an object shareable between Ractors.
|
|
58
|
+
# @param object [BasicObject] The object to make shareable.
|
|
59
|
+
# @param copy [Boolean] Whether to create a copy of the object.
|
|
60
|
+
# @return [BasicObject] The shareable object.
|
|
61
|
+
#
|
|
62
|
+
# @!method main?
|
|
63
|
+
# @!scope class
|
|
64
|
+
# Checks if the current Ractor is the main Ractor.
|
|
65
|
+
# @return [Boolean] `true` if the current Ractor is the main Ractor, `false` otherwise.
|
|
66
|
+
#
|
|
67
|
+
# @!method main_thread
|
|
68
|
+
# @!scope class
|
|
69
|
+
# @note This method is not part of the official Ractor API, but is a replacement for a Ractor-unaware `Thread.main`.
|
|
70
|
+
# Returns the main thread of the current Ractor.
|
|
71
|
+
# @return [Thread] The main thread of the current Ractor.
|
|
72
|
+
#
|
|
73
|
+
# @!method new(*args, name: nil)
|
|
74
|
+
# @!scope class
|
|
75
|
+
# Creates a new Ractor instance.
|
|
76
|
+
# @param args [Array] The arguments to pass to the Ractor's block. Will be copied if not {.shareable? shareable}.
|
|
77
|
+
# @param name [String, nil] The name of the Ractor, or `nil` for no name.
|
|
78
|
+
# @yield [*args] The block to execute in the new Ractor.
|
|
79
|
+
# @yieldparam args [Array] The arguments passed to the Ractor.
|
|
80
|
+
# @yieldreturn [BasicObject] The {#value value} returned by the Ractor's block.
|
|
81
|
+
# @yieldreceiver [Ractor] The new Ractor instance.
|
|
82
|
+
# @return [Ractor] The new Ractor instance.
|
|
83
|
+
#
|
|
84
|
+
# @!method receive(timeout: nil)
|
|
85
|
+
# @!scope class
|
|
86
|
+
# Receives a message on the current Ractor's {#default_port default port}.
|
|
87
|
+
# @param timeout [Numeric, nil] The timeout in seconds, or `nil` for no timeout.
|
|
88
|
+
# @return [BasicObject, nil] The received message, or `nil` if the timeout has been reached.
|
|
89
|
+
#
|
|
90
|
+
# @!method select(*ractors_or_ports, timeout: nil)
|
|
91
|
+
# @!scope class
|
|
92
|
+
# Blocks the current Thread until one of the given Ractors or Ports produces a value, or until the timeout
|
|
93
|
+
# has been reached.
|
|
94
|
+
#
|
|
95
|
+
# Returns an array containing the Ractor or Port that produced the value, and the value itself.
|
|
96
|
+
# Or `nil` if the timeout has been reached.
|
|
97
|
+
#
|
|
98
|
+
# @param ractors_or_ports [Array<Ractor, Farce::Abstract::Port>] The Ractors or Ports to select from.
|
|
99
|
+
# @param timeout [Numeric, nil] The timeout in seconds, or `nil` for no timeout.
|
|
100
|
+
# @return [Array(Object, BasicObject), nil]
|
|
101
|
+
# An array containing the Ractor or Port that produced the value, and the value itself,
|
|
102
|
+
# or `nil` if the timeout has been reached.
|
|
103
|
+
#
|
|
104
|
+
# @!method shareable?(object)
|
|
105
|
+
# @!scope class
|
|
106
|
+
# @note If native Ractors aren't available, this method always returns `true`, as all objects are shareable.
|
|
107
|
+
# Checks if an object is shareable between Ractors.
|
|
108
|
+
# @param object [BasicObject] The object to check.
|
|
109
|
+
# @return [Boolean] `true` if the object is shareable, `false` otherwise.
|
|
110
|
+
#
|
|
111
|
+
# @!method shareable_lambda(self: nil)
|
|
112
|
+
# @!scope class
|
|
113
|
+
# {Farce.rebind Binds} a lambda to the given `self` and {.make_shareable makes it shareable} between Ractors.
|
|
114
|
+
# @yield The block to convert into a shareable lambda.
|
|
115
|
+
# @yieldreceiver [BasicObject] The object passed as `self` (or `nil`)
|
|
116
|
+
# @param self [BasicObject] The object to bind as `self`.
|
|
117
|
+
# @return [Proc] The shareable lambda.
|
|
118
|
+
# @see .shareable_proc
|
|
119
|
+
#
|
|
120
|
+
# @!method shareable_proc(self: nil)
|
|
121
|
+
# @!scope class
|
|
122
|
+
# {Farce.rebind Binds} a proc to the given `self` and {.make_shareable makes it shareable} between Ractors.
|
|
123
|
+
# @yield The block to convert into a shareable proc.
|
|
124
|
+
# @yieldreceiver [BasicObject] The object passed as `self` (or `nil`)
|
|
125
|
+
# @param self [BasicObject] The object to bind as `self`.
|
|
126
|
+
# @return [Proc] The shareable proc.
|
|
127
|
+
# @see .shareable_lambda
|
|
128
|
+
#
|
|
129
|
+
# @!method shim?
|
|
130
|
+
# @!scope class
|
|
131
|
+
# @note This method isn't part of the official Ractor API, but is compatible with the `ractor-shim` gem.
|
|
132
|
+
# @return [Boolean] `false` if native Ractors are available, `true` otherwise.
|
|
133
|
+
#
|
|
134
|
+
# @!method store_if_absent(key)
|
|
135
|
+
# @!scope class
|
|
136
|
+
# Stores a value in the current Ractor's storage by key if it is not already present. This method is thread-safe.
|
|
137
|
+
# @param key [Symbol] The key to store the value under.
|
|
138
|
+
# @yield Block that computes the value to store if the key is absent.
|
|
139
|
+
# @yieldreturn [BasicObject] The value to store if the key is absent.
|
|
140
|
+
# @return [BasicObject] The existing or newly stored value.
|
|
141
|
+
#
|
|
142
|
+
# @!method threads
|
|
143
|
+
# @!scope class
|
|
144
|
+
# @note This method isn't part of the official Ractor API, but is a replacement for a Ractor-unaware `Thread.list`.
|
|
145
|
+
# Returns the list of threads in the current Ractor.
|
|
146
|
+
# @return [Array<Thread>] The list of threads in the current Ractor.
|
|
147
|
+
#
|
|
148
|
+
# @!method default_port
|
|
149
|
+
# @return [Ractor::Port] The default port for the Ractor.
|
|
150
|
+
#
|
|
151
|
+
# @!method join
|
|
152
|
+
# Blocks the current Thread until the Ractor has terminated.
|
|
153
|
+
# @return [self]
|
|
154
|
+
#
|
|
155
|
+
# @!method main?
|
|
156
|
+
# @return [Boolean] `true` if the Ractor is the main Ractor
|
|
157
|
+
#
|
|
158
|
+
# @!method monitor(port)
|
|
159
|
+
# Monitors the given port for termination of the Ractor. If the Ractor is already terminated, the port will receive
|
|
160
|
+
# the Ractor's status immediately. The port will receive wither `:exited` or `:aborted` depending on whether the
|
|
161
|
+
# Ractor terminated normally or with an unhandled exception.
|
|
162
|
+
# @ruby CRuby 4.0+, JRuby, TruffleRuby
|
|
163
|
+
# @param port [Ractor::Port] The port to monitor.
|
|
164
|
+
# @return [Boolean]
|
|
165
|
+
# `true` if the Ractor is running and the port was successfully monitored,
|
|
166
|
+
# `false` if the Ractor is already terminated and the port received the Ractor's status.
|
|
167
|
+
# @see #unmonitor
|
|
168
|
+
#
|
|
169
|
+
# @!method unmonitor(port)
|
|
170
|
+
# Stops monitoring the given port for termination of the Ractor. If the port was not being monitored, this method
|
|
171
|
+
# has no effect.
|
|
172
|
+
# @ruby CRuby 4.0+, JRuby, TruffleRuby
|
|
173
|
+
# @return [self]
|
|
174
|
+
# @see #monitor
|
|
175
|
+
#
|
|
176
|
+
# @!method name
|
|
177
|
+
# @return [String, nil] The name of the Ractor, or `nil` if it has no name.
|
|
178
|
+
#
|
|
179
|
+
# @!method receive(timeout: nil)
|
|
180
|
+
# Receives a message on the Ractor's {#default_port default port}.
|
|
181
|
+
# @param timeout [Numeric, nil] The timeout in seconds, or `nil` for no timeout.
|
|
182
|
+
# @return [BasicObject, nil] The received message, or `nil` if the timeout has been reached.
|
|
183
|
+
#
|
|
184
|
+
# @!method send(message, move: false)
|
|
185
|
+
# Sends a message to the Ractor's {#default_port default port}.
|
|
186
|
+
# @param message [BasicObject] The message to send.
|
|
187
|
+
# @param move [Boolean] Whether to move the message to the Ractor if it isn't sharable.
|
|
188
|
+
# @return [self]
|
|
189
|
+
#
|
|
190
|
+
# @!method name
|
|
191
|
+
# @return [String, nil] The name of the Ractor, or `nil` if it has no name.
|
|
192
|
+
#
|
|
193
|
+
# @!method receive(timeout: nil)
|
|
194
|
+
# Receives a message on the Ractor's {#default_port default port}.
|
|
195
|
+
# @param timeout [Numeric, nil] The timeout in seconds, or `nil` for no timeout.
|
|
196
|
+
# @return [BasicObject, nil] The received message, or `nil` if the timeout has been reached.
|
|
197
|
+
#
|
|
198
|
+
# @!method send(message, move: false)
|
|
199
|
+
# Sends a message to the Ractor's {#default_port default port}.
|
|
200
|
+
# @param message [BasicObject] The message to send.
|
|
201
|
+
# @param move [Boolean] Whether to move the message to the Ractor if it isn't sharable.
|
|
202
|
+
# @return [self]
|
|
203
|
+
#
|
|
204
|
+
# @!method value
|
|
205
|
+
# Blocks the current Thread until the Ractor has terminated, and returns the value returned by the
|
|
206
|
+
# Ractor's block. Will re-raise any unhandled exception raised in the Ractor's block.
|
|
207
|
+
# @ruby CRuby 4.0+, JRuby, TruffleRuby
|
|
208
|
+
# @return [BasicObject] The value returned by the Ractor's block.
|
|
209
|
+
#
|
|
210
|
+
# @!method take
|
|
211
|
+
# @deprecated
|
|
212
|
+
# This method only exists on CRuby 3.x (outdated Ractor API). Use {#value} if you don't have to support
|
|
213
|
+
# CRuby 3.x, or use {Farce::Port}.
|
|
214
|
+
# Receives a message from the Ractor. This may be the ractor's return value.
|
|
215
|
+
# @ruby CRuby 3.x
|
|
216
|
+
# @return [BasicObject] The received message.
|
|
217
|
+
module Ractor
|
|
218
|
+
include Internal::MarshalSupport::Reject
|
|
219
|
+
::Ractor.include(self) if Internal.native_ractors?
|
|
220
|
+
|
|
221
|
+
# @!parse
|
|
222
|
+
# # @!scope class
|
|
223
|
+
# alias recv receive
|
|
224
|
+
# alias recv receive
|
|
225
|
+
# alias << send
|
|
226
|
+
|
|
227
|
+
# Either `Ractor::Error` or a subclass of `RuntimeError` if native Ractors are not available.
|
|
228
|
+
# @!macro RactorError_note
|
|
229
|
+
# Will pick up an error class defined by a shim/polyfill if one is loaded,
|
|
230
|
+
# like [ractor-shim](https://github.com/eregon/ractor-shim).
|
|
231
|
+
Error = defined?(::Ractor::Error) ? ::Ractor::Error : Class.new(RuntimeError)
|
|
232
|
+
|
|
233
|
+
# Either `Ractor::ClosedError` or a subclass of `StopIteration` if native Ractors are not available.
|
|
234
|
+
# @!macro RactorError_note
|
|
235
|
+
ClosedError = defined?(::Ractor::ClosedError) ? ::Ractor::ClosedError : Class.new(StopIteration)
|
|
236
|
+
|
|
237
|
+
# Either `Ractor::IsolationError` or a subclass of {Error} if native Ractors are not available.
|
|
238
|
+
# @!macro RactorError_note
|
|
239
|
+
IsolationError = defined?(::Ractor::IsolationError) ? ::Ractor::IsolationError : Class.new(Error)
|
|
240
|
+
|
|
241
|
+
# Either `Ractor::MovedError` or a subclass of {Error} if native Ractors are not available.
|
|
242
|
+
# @!macro RactorError_note
|
|
243
|
+
MovedError = defined?(::Ractor::MovedError) ? ::Ractor::MovedError : Class.new(Error)
|
|
244
|
+
|
|
245
|
+
# Either `Ractor::RemoteError` or a subclass of {Error} if native Ractors are not available.
|
|
246
|
+
# @!macro RactorError_note
|
|
247
|
+
RemoteError = defined?(::Ractor::RemoteError) ? ::Ractor::RemoteError : Class.new(Error)
|
|
248
|
+
|
|
249
|
+
# Either `Ractor::UnsafeError` or a subclass of {Error} if native Ractors are not available.
|
|
250
|
+
# @!macro RactorError_note
|
|
251
|
+
UnsafeError = defined?(::Ractor::UnsafeError) ? ::Ractor::UnsafeError : Class.new(Error)
|
|
252
|
+
|
|
253
|
+
# Either `Ractor::MovedObject` or a subclass of `BasicObject` if native Ractors are not available.
|
|
254
|
+
# Cannot be instantiated directly, but can be used to check if an object has been moved to another Ractor.
|
|
255
|
+
MovedObject = defined?(::Ractor::MovedObject) ? ::Ractor::MovedObject :
|
|
256
|
+
Class.new(BasicObject) { def self.new(...) = raise TypeError, "allocator undefined for #{inspect}" }
|
|
257
|
+
|
|
258
|
+
extend Internal::RactorMethods
|
|
259
|
+
|
|
260
|
+
def self.const_missing(name) = name == :Port ? Internal::BasePort : super
|
|
261
|
+
private_class_method :const_missing
|
|
262
|
+
|
|
263
|
+
# @!visibility private
|
|
264
|
+
def self.included(base)
|
|
265
|
+
return if base.const_defined?(:Port, false)
|
|
266
|
+
base.const_set(:Port, Internal::BasePort)
|
|
267
|
+
end
|
|
268
|
+
end
|
|
269
|
+
end
|
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# shareable_constant_value: literal
|
|
3
|
+
# warn_indent: true
|
|
4
|
+
|
|
5
|
+
module Farce
|
|
6
|
+
# A Ractor-shareable read/write lock with fiber-scheduler-aware waiting.
|
|
7
|
+
#
|
|
8
|
+
# Multiple execution contexts may hold the read lock concurrently. The write
|
|
9
|
+
# lock is exclusive and may be acquired while the current context holds a
|
|
10
|
+
# read lock. One concurrent upgrader retains its read lock continuously.
|
|
11
|
+
# Additional upgraders yield their read slot to avoid deadlock and restore it
|
|
12
|
+
# before returning to the outer read-lock block.
|
|
13
|
+
class ReadWriteLock
|
|
14
|
+
include Internal::MarshalSupport::Reject
|
|
15
|
+
include Internal::Copyable
|
|
16
|
+
include Shareable::Unfreezable
|
|
17
|
+
|
|
18
|
+
READER_BITS = 30
|
|
19
|
+
READER_MASK = (1 << READER_BITS) - 1
|
|
20
|
+
WRITER_BIT = 1 << READER_BITS
|
|
21
|
+
UPGRADER_BIT = 1 << (READER_BITS + 1)
|
|
22
|
+
WAITER_UNIT = 1 << (READER_BITS + 2)
|
|
23
|
+
MAX_WAITERS = ((1 << 63) - 1) / WAITER_UNIT
|
|
24
|
+
READ_DEPTH = 0
|
|
25
|
+
WRITE_DEPTH = 1
|
|
26
|
+
LOCAL_STATES = :__farce_read_write_lock_states__
|
|
27
|
+
INTERRUPTS_IMMEDIATE = { Exception => :immediate }.freeze
|
|
28
|
+
private_constant :READER_BITS, :READER_MASK, :WRITER_BIT, :UPGRADER_BIT,
|
|
29
|
+
:WAITER_UNIT, :MAX_WAITERS, :READ_DEPTH, :WRITE_DEPTH, :LOCAL_STATES,
|
|
30
|
+
:INTERRUPTS_IMMEDIATE
|
|
31
|
+
|
|
32
|
+
def initialize
|
|
33
|
+
@state = Internal::Vector.new([0])
|
|
34
|
+
@signal = Internal::Signal.new
|
|
35
|
+
super
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Runs the block while holding a shared read lock.
|
|
39
|
+
#
|
|
40
|
+
# @yield the block to run while holding the read lock
|
|
41
|
+
# @return [Object] the block result
|
|
42
|
+
def with_read_lock
|
|
43
|
+
raise LocalJumpError, "no block given" unless block_given?
|
|
44
|
+
|
|
45
|
+
local = local_state
|
|
46
|
+
Thread.handle_interrupt(Internal::INTERRUPT_MASK) do
|
|
47
|
+
global = local[READ_DEPTH].zero? && local[WRITE_DEPTH].zero?
|
|
48
|
+
acquire_read_lock if global
|
|
49
|
+
local[READ_DEPTH] += 1
|
|
50
|
+
|
|
51
|
+
begin
|
|
52
|
+
# Block capture allocates on every lock operation.
|
|
53
|
+
# rubocop:disable-next Style/ExplicitBlockArgument
|
|
54
|
+
Thread.handle_interrupt(INTERRUPTS_IMMEDIATE) { yield }
|
|
55
|
+
ensure
|
|
56
|
+
local[READ_DEPTH] -= 1
|
|
57
|
+
release_read_lock if global
|
|
58
|
+
clear_local_state(local)
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# Runs the block while holding an exclusive write lock.
|
|
64
|
+
#
|
|
65
|
+
# Calling this method from inside {#with_read_lock} upgrades the read lock.
|
|
66
|
+
#
|
|
67
|
+
# @yield the block to run while holding the write lock
|
|
68
|
+
# @return [Object] the block result
|
|
69
|
+
def with_write_lock
|
|
70
|
+
raise LocalJumpError, "no block given" unless block_given?
|
|
71
|
+
|
|
72
|
+
local = local_state
|
|
73
|
+
Thread.handle_interrupt(Internal::INTERRUPT_MASK) do
|
|
74
|
+
reentrant = local[WRITE_DEPTH].positive?
|
|
75
|
+
upgraded = !reentrant && local[READ_DEPTH].positive?
|
|
76
|
+
acquire_write_lock(upgraded) unless reentrant
|
|
77
|
+
local[WRITE_DEPTH] += 1
|
|
78
|
+
|
|
79
|
+
begin
|
|
80
|
+
# Block capture allocates on every lock operation.
|
|
81
|
+
# rubocop:disable-next Style/ExplicitBlockArgument
|
|
82
|
+
Thread.handle_interrupt(INTERRUPTS_IMMEDIATE) { yield }
|
|
83
|
+
ensure
|
|
84
|
+
local[WRITE_DEPTH] -= 1
|
|
85
|
+
unless reentrant
|
|
86
|
+
upgraded ? downgrade_write_lock : release_write_lock
|
|
87
|
+
end
|
|
88
|
+
clear_local_state(local)
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
private
|
|
94
|
+
|
|
95
|
+
def initialize_copy(other)
|
|
96
|
+
super
|
|
97
|
+
@state = Internal::Vector.new([0])
|
|
98
|
+
@signal = Internal::Signal.new
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def local_state
|
|
102
|
+
states = Thread.current[LOCAL_STATES] ||= {}
|
|
103
|
+
states[self] ||= [0, 0]
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def clear_local_state(local)
|
|
107
|
+
return unless local[READ_DEPTH].zero? && local[WRITE_DEPTH].zero?
|
|
108
|
+
|
|
109
|
+
states = Thread.current[LOCAL_STATES]
|
|
110
|
+
states.delete(self)
|
|
111
|
+
Thread.current[LOCAL_STATES] = nil if states.empty?
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def acquire_read_lock
|
|
115
|
+
return if try_acquire_read_lock
|
|
116
|
+
|
|
117
|
+
# Kernel.loop allocates on every acquisition.
|
|
118
|
+
while true
|
|
119
|
+
generation = @signal.generation
|
|
120
|
+
return if try_acquire_read_lock
|
|
121
|
+
|
|
122
|
+
wait_for_change(generation)
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def try_acquire_read_lock
|
|
127
|
+
change_state do |current|
|
|
128
|
+
if readers_allowed?(current)
|
|
129
|
+
raise ThreadError, "maximum reader count exceeded" if reader_count(current) == READER_MASK
|
|
130
|
+
[current + 1, true]
|
|
131
|
+
else
|
|
132
|
+
[current, false]
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
def release_read_lock
|
|
138
|
+
change_state do |current|
|
|
139
|
+
raise ThreadError, "read lock is not held" if reader_count(current).zero?
|
|
140
|
+
[current - 1, nil]
|
|
141
|
+
end
|
|
142
|
+
@signal.broadcast
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
def acquire_write_lock(upgrade)
|
|
146
|
+
mode = upgrade ? register_upgrade : register_writer
|
|
147
|
+
acquired = false
|
|
148
|
+
|
|
149
|
+
begin
|
|
150
|
+
acquired = try_acquire_write_lock(mode)
|
|
151
|
+
return if acquired
|
|
152
|
+
|
|
153
|
+
# Kernel.loop allocates on every acquisition.
|
|
154
|
+
while true
|
|
155
|
+
generation = @signal.generation
|
|
156
|
+
acquired = try_acquire_write_lock(mode)
|
|
157
|
+
return if acquired
|
|
158
|
+
|
|
159
|
+
wait_for_change(generation)
|
|
160
|
+
end
|
|
161
|
+
ensure
|
|
162
|
+
cancel_write_request(mode) unless acquired
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
def try_acquire_write_lock(mode)
|
|
167
|
+
change_state do |current|
|
|
168
|
+
retained = mode == :retained
|
|
169
|
+
target_readers = retained ? 1 : 0
|
|
170
|
+
available = !writer_locked?(current) && reader_count(current) == target_readers
|
|
171
|
+
available &&= upgrader?(current) if retained
|
|
172
|
+
|
|
173
|
+
if available
|
|
174
|
+
replacement = current - WAITER_UNIT + WRITER_BIT
|
|
175
|
+
replacement -= UPGRADER_BIT + 1 if retained
|
|
176
|
+
[replacement, true]
|
|
177
|
+
else
|
|
178
|
+
[current, false]
|
|
179
|
+
end
|
|
180
|
+
end
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
def register_writer
|
|
184
|
+
change_state { |current| [add_waiter(current), :writer] }
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
def register_upgrade
|
|
188
|
+
mode = change_state do |current|
|
|
189
|
+
raise ThreadError, "read lock is not held" if reader_count(current).zero?
|
|
190
|
+
|
|
191
|
+
if upgrader?(current)
|
|
192
|
+
[add_waiter(current) - 1, :released]
|
|
193
|
+
else
|
|
194
|
+
[add_waiter(current) + UPGRADER_BIT, :retained]
|
|
195
|
+
end
|
|
196
|
+
end
|
|
197
|
+
@signal.broadcast if mode == :released
|
|
198
|
+
mode
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
def cancel_write_request(mode)
|
|
202
|
+
case mode
|
|
203
|
+
when :retained
|
|
204
|
+
change_state { |current| [current - WAITER_UNIT - UPGRADER_BIT, nil] }
|
|
205
|
+
when :released, :writer
|
|
206
|
+
change_state { |current| [current - WAITER_UNIT, nil] }
|
|
207
|
+
end
|
|
208
|
+
@signal.broadcast
|
|
209
|
+
acquire_read_lock if mode == :released
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
def release_write_lock
|
|
213
|
+
change_state do |current|
|
|
214
|
+
raise ThreadError, "write lock is not held" unless writer_locked?(current)
|
|
215
|
+
[current - WRITER_BIT, nil]
|
|
216
|
+
end
|
|
217
|
+
@signal.broadcast
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
def downgrade_write_lock
|
|
221
|
+
change_state do |current|
|
|
222
|
+
raise ThreadError, "write lock is not held" unless writer_locked?(current)
|
|
223
|
+
[current - WRITER_BIT + 1, nil]
|
|
224
|
+
end
|
|
225
|
+
@signal.broadcast
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
def change_state
|
|
229
|
+
result = nil
|
|
230
|
+
@state.update(0) do |current|
|
|
231
|
+
replacement, result = yield(current)
|
|
232
|
+
replacement
|
|
233
|
+
end
|
|
234
|
+
result
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
def wait_for_change(generation)
|
|
238
|
+
Thread.handle_interrupt(INTERRUPTS_IMMEDIATE) { @signal.wait(generation) }
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
def add_waiter(current)
|
|
242
|
+
raise ThreadError, "maximum writer count exceeded" if waiting_writers(current) == MAX_WAITERS
|
|
243
|
+
current + WAITER_UNIT
|
|
244
|
+
end
|
|
245
|
+
|
|
246
|
+
def readers_allowed?(current)
|
|
247
|
+
!writer_locked?(current) && waiting_writers(current).zero?
|
|
248
|
+
end
|
|
249
|
+
|
|
250
|
+
def reader_count(current) = current & READER_MASK
|
|
251
|
+
def waiting_writers(current) = current / WAITER_UNIT
|
|
252
|
+
def writer_locked?(current) = current.anybits?(WRITER_BIT)
|
|
253
|
+
def upgrader?(current) = current.anybits?(UPGRADER_BIT)
|
|
254
|
+
def instance_variables_to_inspect = Internal::EMPTY_ARRAY
|
|
255
|
+
end
|
|
256
|
+
end
|