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,536 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# shareable_constant_value: literal
|
|
3
|
+
# warn_indent: true
|
|
4
|
+
|
|
5
|
+
module Farce
|
|
6
|
+
# A Ractor-shareable task scheduler.
|
|
7
|
+
#
|
|
8
|
+
# Tasks run as fibers using a fiber scheduler, or as threads when the Ruby implementation
|
|
9
|
+
# doesn't support Fiber schedulers (ie TruffleRuby).
|
|
10
|
+
#
|
|
11
|
+
# If supports any existing Fiber scheduler, including Async and CarbonFiber, but
|
|
12
|
+
# can also be used as a Fiber scheduler itself.
|
|
13
|
+
#
|
|
14
|
+
# Use {.create} to start a worker, or construct an instance and install it with
|
|
15
|
+
# `Fiber.set_scheduler` on the current thread. A scheduler can only be launched
|
|
16
|
+
# or installed once.
|
|
17
|
+
#
|
|
18
|
+
# It will keep the fiber scheduler alive until it is explicitly closed.
|
|
19
|
+
# Closing a scheduler stops new submissions through {#schedule}. The underlying
|
|
20
|
+
# fiber scheduler has its own lifecycle: draining fibers may still spawn child
|
|
21
|
+
# fibers with `Fiber.schedule` while it is closing.
|
|
22
|
+
#
|
|
23
|
+
# @example Submit arguments to a worker
|
|
24
|
+
# # Runs a scheduler in a background Ractor/Thread
|
|
25
|
+
# scheduler = Farce::Scheduler.create
|
|
26
|
+
# scheduler.schedule("hello") { |message| puts message }
|
|
27
|
+
#
|
|
28
|
+
# @example Using Farce::Scheduler as a Fiber scheduler
|
|
29
|
+
# scheduler = Farce::Scheduler.new
|
|
30
|
+
# Fiber.set_scheduler(scheduler)
|
|
31
|
+
# Fiber.schedule { puts "Hello from the scheduler!" }
|
|
32
|
+
class Scheduler < Farce::Abstract::Scheduler
|
|
33
|
+
include Shareable::Unfreezable
|
|
34
|
+
include Internal::Inspect
|
|
35
|
+
|
|
36
|
+
CLOSED_STATES = ::Set[:closed, :closing, :error].freeze
|
|
37
|
+
private_constant :CLOSED_STATES
|
|
38
|
+
|
|
39
|
+
# Returns a Farce scheduler for the current thread's installed fiber scheduler.
|
|
40
|
+
# Returns `nil` when no fiber scheduler is installed.
|
|
41
|
+
#
|
|
42
|
+
# Creates and installs a wrapper when necessary, starting its dispatcher on
|
|
43
|
+
# that scheduler. Returns `nil` when no fiber scheduler is installed.
|
|
44
|
+
#
|
|
45
|
+
# Additional keyword arguments are forwarded to {#initialize} when wrapping.
|
|
46
|
+
#
|
|
47
|
+
# @example Create a scheduler for Async, and send it to a different Ractor
|
|
48
|
+
# require "async"
|
|
49
|
+
# require "farce"
|
|
50
|
+
#
|
|
51
|
+
# # No fiber scheduler set here
|
|
52
|
+
# Farce::Scheduler.current # => nil
|
|
53
|
+
#
|
|
54
|
+
# Async do
|
|
55
|
+
# # A scheduler that will hand tasks to Async::Scheduler
|
|
56
|
+
# scheduler = Farce::Scheduler.current # => #<Farce::Scheduler>
|
|
57
|
+
#
|
|
58
|
+
# # This looks like doing the same as nested Async { ... } calls or
|
|
59
|
+
# # Fiber.schedule { ... }, …
|
|
60
|
+
# scheduler.schedule { puts "Scheduled from the main Ractor" }
|
|
61
|
+
#
|
|
62
|
+
# # … but it can be called from other threads and ractors
|
|
63
|
+
# Ractor.new(scheduler) do |scheduler|
|
|
64
|
+
# scheduler.schedule { puts "Scheduled from a background Ractor" }
|
|
65
|
+
# end
|
|
66
|
+
# end
|
|
67
|
+
#
|
|
68
|
+
# @example Full circle
|
|
69
|
+
# scheduler = Farce::Scheduler.new
|
|
70
|
+
# Farce::Scheduler.current # => nil
|
|
71
|
+
# Fiber.set_scheduler(scheduler)
|
|
72
|
+
# Farce::Scheduler.current == scheduler # => true
|
|
73
|
+
# @return [Scheduler, nil]
|
|
74
|
+
def self.current(**)
|
|
75
|
+
return unless scheduler = Fiber.scheduler
|
|
76
|
+
register = Internal::Storage.store_if_absent(self) { Internal::Storage.new }
|
|
77
|
+
register.store_if_absent(scheduler) do
|
|
78
|
+
wrapper = new(**, external: true)
|
|
79
|
+
wrapper.__send__(:set_scheduler!, scheduler:)
|
|
80
|
+
wrapper
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# Creates a new scheduler and starts it in a background ractor, thread, or custom executor.
|
|
85
|
+
#
|
|
86
|
+
# @overload create(executor = nil, name: nil, priority: nil, capacity: 1024, backend: Farce.config.io_backend)
|
|
87
|
+
# Runs tasks on the configured fiber scheduler. The backend option selects
|
|
88
|
+
# the IO driver when using Farce's built-in scheduler.
|
|
89
|
+
#
|
|
90
|
+
# @example
|
|
91
|
+
# scheduler = Farce::Scheduler.create
|
|
92
|
+
# scheduler.schedule { puts "Running on the built-in scheduler" }
|
|
93
|
+
#
|
|
94
|
+
# @param executor [Class, nil] Ractor or Thread, nil selects the runtime default.
|
|
95
|
+
# @param name [String, nil] Optional worker name.
|
|
96
|
+
# @param priority [Integer, nil] Optional worker thread priority.
|
|
97
|
+
# @param capacity [Integer, nil] Pending-task capacity, nil creates an unbounded queue.
|
|
98
|
+
# @param backend [Symbol] IO backend for the built-in scheduler, defaults to Farce configuration.
|
|
99
|
+
# @return [Scheduler]
|
|
100
|
+
#
|
|
101
|
+
# @overload create(executor = nil, name: nil, priority: nil, capacity: 1024, &block)
|
|
102
|
+
# Runs tasks on a fiber scheduler supplied by your block. Use this to send
|
|
103
|
+
# work through Farce to another library's event loop, such as Async.
|
|
104
|
+
#
|
|
105
|
+
# Construct and return the scheduler inside the block: Farce calls it once
|
|
106
|
+
# in the worker, where that event loop will run, and installs the result.
|
|
107
|
+
# The block configures the scheduler. Submit actual tasks with {#schedule}.
|
|
108
|
+
# Configure any IO backend on the returned scheduler itself. Farce's backend
|
|
109
|
+
# option is ignored when a block is supplied.
|
|
110
|
+
#
|
|
111
|
+
# The block must be Ractor-shareable. Require the library before calling
|
|
112
|
+
# create and avoid capturing a scheduler created on another thread or Ractor.
|
|
113
|
+
#
|
|
114
|
+
# @example Run tasks on Async in a worker thread
|
|
115
|
+
# require "async"
|
|
116
|
+
# require "farce"
|
|
117
|
+
#
|
|
118
|
+
# # Using Thread as an executor as at the moment Async cannot run on a non-main Ractor
|
|
119
|
+
# scheduler = Farce::Scheduler.create(Thread) { Async::Scheduler.new }
|
|
120
|
+
# scheduler.schedule { puts "Running on Async" }
|
|
121
|
+
#
|
|
122
|
+
# @example Run tasks on CarbonFiber in a worker ractor
|
|
123
|
+
# require "carbon_fiber"
|
|
124
|
+
# require "farce"
|
|
125
|
+
#
|
|
126
|
+
# scheduler = Farce::Scheduler.create(Ractor) { CarbonFiber::Scheduler.new }
|
|
127
|
+
# scheduler.schedule { puts "Running on CarbonFiber" }
|
|
128
|
+
#
|
|
129
|
+
# @param executor [Class, nil]
|
|
130
|
+
# Ractor or Thread. `nil` selects the runtime default.
|
|
131
|
+
# Choose an executor supported by the supplied scheduler and its dependencies.
|
|
132
|
+
# @param name [String, nil] Optional worker name.
|
|
133
|
+
# @param priority [Integer, nil] Optional worker thread priority.
|
|
134
|
+
# @param capacity [Integer, nil] Pending-task capacity, nil creates an unbounded queue.
|
|
135
|
+
# @yieldreturn [Object] The fiber scheduler to install in the worker.
|
|
136
|
+
# @return [Scheduler]
|
|
137
|
+
# @see #launch
|
|
138
|
+
def self.create(executor = nil, name: nil, priority: nil, **, &)
|
|
139
|
+
new(**, &).tap { it.launch(executor, name:, priority:) }
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
# Creates a scheduler without starting a worker. Use this instead of {.create}
|
|
143
|
+
# when you want to launch it later with {#launch}, or install it on the current
|
|
144
|
+
# thread with `Fiber.set_scheduler(scheduler)`.
|
|
145
|
+
#
|
|
146
|
+
# @overload initialize(capacity: 1024, backend: Farce.config.io_backend)
|
|
147
|
+
# Uses the configured fiber scheduler when launched or installed.
|
|
148
|
+
#
|
|
149
|
+
# @param capacity [Integer, nil] Pending-task capacity, nil creates an unbounded queue.
|
|
150
|
+
# @param backend [Symbol] IO backend for the built-in scheduler, defaults to Farce configuration.
|
|
151
|
+
#
|
|
152
|
+
# @overload initialize(capacity: 1024, &block)
|
|
153
|
+
# Uses another library's fiber scheduler while retaining Farce's ability to
|
|
154
|
+
# accept tasks from other threads and Ractors.
|
|
155
|
+
#
|
|
156
|
+
# Supply a block that constructs and returns that scheduler. The block runs
|
|
157
|
+
# once when this handle is launched or installed, in the thread that will
|
|
158
|
+
# run the tasks, rather than during new. It must be Ractor-shareable.
|
|
159
|
+
# Farce installs its result. Submit tasks separately with {#schedule}.
|
|
160
|
+
# Configure the supplied scheduler in the block. Farce's backend option is
|
|
161
|
+
# ignored for this overload.
|
|
162
|
+
#
|
|
163
|
+
# @example Choose Async, then launch the worker later
|
|
164
|
+
# require "async"
|
|
165
|
+
# require "farce"
|
|
166
|
+
#
|
|
167
|
+
# scheduler = Farce::Scheduler.new { Async::Scheduler.new }
|
|
168
|
+
# worker = scheduler.launch_thread
|
|
169
|
+
# scheduler.schedule { puts "Running on Async" }
|
|
170
|
+
#
|
|
171
|
+
# @param capacity [Integer, nil] Pending-task capacity, nil creates an unbounded queue.
|
|
172
|
+
# @yieldreturn [Object] The fiber scheduler to install when launched or installed.
|
|
173
|
+
def initialize(capacity: 1024, backend: Internal::FROZEN_CONFIG.io_backend, queue: nil, external: false,
|
|
174
|
+
pool_worker: nil, queue_owner: true, &constructor)
|
|
175
|
+
constructor ||= Internal::FROZEN_CONFIG.fiber_scheduler_constructor
|
|
176
|
+
@capacity = capacity ? Integer(capacity) : nil
|
|
177
|
+
@backend = backend
|
|
178
|
+
@constructor = Ractor.shareable?(constructor) ? constructor : Ractor.shareable_proc(&constructor) if constructor
|
|
179
|
+
@external = external || !constructor.nil?
|
|
180
|
+
@owner = Internal::Atom.new(nil)
|
|
181
|
+
@state = Internal::Atom.new(:initialized)
|
|
182
|
+
@error = Internal::Atom.new
|
|
183
|
+
@queue = queue || Internal::Queue.new(capacity: @capacity)
|
|
184
|
+
@pool_worker = pool_worker
|
|
185
|
+
@queue_owner = queue_owner
|
|
186
|
+
super()
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
# @return [Exception, nil] The error encountered by the scheduler, if any.
|
|
190
|
+
def error
|
|
191
|
+
value = @error.value
|
|
192
|
+
value.is_a?(Envelope) ? value.value : value
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
# @overload launch_ractor(name: nil, priority: nil)
|
|
196
|
+
# Launches a Ractor worker. Accepts the keyword arguments of {#launch}.
|
|
197
|
+
# @param name [String, nil] Optional worker name.
|
|
198
|
+
# @param priority [Integer, nil] Optional worker thread priority.
|
|
199
|
+
# @return [Ractor] The worker.
|
|
200
|
+
def launch_ractor(...) = launch(Ractor, ...)
|
|
201
|
+
|
|
202
|
+
# @overload launch_thread(name: nil, priority: nil)
|
|
203
|
+
# Launches a Thread worker. Accepts the keyword arguments of {#launch}.
|
|
204
|
+
# @param name [String, nil] Optional worker name.
|
|
205
|
+
# @param priority [Integer, nil] Optional worker thread priority.
|
|
206
|
+
# @return [Thread] The worker.
|
|
207
|
+
def launch_thread(...) = launch(Thread, ...)
|
|
208
|
+
|
|
209
|
+
# Starts dispatching tasks in a new worker and waits for setup to leave its
|
|
210
|
+
# launching/setup states before returning. Returns the created worker instance.
|
|
211
|
+
#
|
|
212
|
+
# @example Using the return value to assign a ThreadGroup
|
|
213
|
+
# scheduler = Farce::Scheduler.new
|
|
214
|
+
# worker = scheduler.launch_thread
|
|
215
|
+
# group = ThreadGroup.new
|
|
216
|
+
#
|
|
217
|
+
# group.add(worker)
|
|
218
|
+
# group.enclose
|
|
219
|
+
#
|
|
220
|
+
# @example Using a custom worker
|
|
221
|
+
# class MyWorker
|
|
222
|
+
# def initialize(...)
|
|
223
|
+
# # flip a coin whether we're using Ractor or Thread
|
|
224
|
+
# @worker = [Ractor, Thread].sample.new(...)
|
|
225
|
+
# end
|
|
226
|
+
#
|
|
227
|
+
# def join = @worker.join
|
|
228
|
+
# end
|
|
229
|
+
#
|
|
230
|
+
# scheduler = Farce::Scheduler.new
|
|
231
|
+
# scheduler.launch(MyWorker)
|
|
232
|
+
#
|
|
233
|
+
# @param executor [Class, nil]
|
|
234
|
+
# Ractor or Thread. Defaults to Ractor when native Ractors are available, otherwise Thread.
|
|
235
|
+
# @param name [String, nil] Optional worker name.
|
|
236
|
+
# @param priority [Integer, nil] Optional worker thread priority.
|
|
237
|
+
# @return [Ractor, Thread, Object] The worker.
|
|
238
|
+
# @raise [RuntimeError] If this handle has already been launched, installed, or closed.
|
|
239
|
+
def launch(executor = nil, name: nil, priority: nil)
|
|
240
|
+
raise "Scheduler may not be reused" unless @state.compare_and_set(:initialized, :launching)
|
|
241
|
+
|
|
242
|
+
executor ||= Internal.native_ractors? ? Ractor : Thread
|
|
243
|
+
priority &&= Integer(priority)
|
|
244
|
+
name &&= -String(name)
|
|
245
|
+
callback = Ractor.shareable_proc(self: self) { _1.__send__(:launch!, _3, _2) }
|
|
246
|
+
|
|
247
|
+
return executor.new(self, priority, name, &callback) unless executor <= Ractor
|
|
248
|
+
# Requiring an autoloaded implementation from a new Ractor can deadlock
|
|
249
|
+
# while its launcher waits for setup to finish.
|
|
250
|
+
Internal.const_get(:FiberScheduler, false)
|
|
251
|
+
Internal.const_get(:Storage, false)
|
|
252
|
+
Internal.const_get(:ThreadPool, false)
|
|
253
|
+
executor.new(self, priority, name, name:, &callback)
|
|
254
|
+
rescue Exception => e # rubocop:disable Lint/RescueException
|
|
255
|
+
@state.value = :error
|
|
256
|
+
@error.value = Envelope.new(e)
|
|
257
|
+
raise
|
|
258
|
+
ensure
|
|
259
|
+
# if we return the thread too early, someone could kill it while another threads blocks on a schedule call
|
|
260
|
+
if System.windows? && executor <= Ractor
|
|
261
|
+
sleep 0.001 while @state.value == :launching
|
|
262
|
+
sleep 0.001 while @state.value == :setup
|
|
263
|
+
else
|
|
264
|
+
@state.wait_until_changed(:launching)
|
|
265
|
+
@state.wait_until_changed(:setup)
|
|
266
|
+
end
|
|
267
|
+
end
|
|
268
|
+
|
|
269
|
+
# @return [Ractor, nil] The owning Ractor, or nil before ownership is assigned.
|
|
270
|
+
def owner = @owner.value
|
|
271
|
+
|
|
272
|
+
# Assigns ownership during setup. An existing owner cannot be changed.
|
|
273
|
+
# This is used by schedule to determine if a task is scheduled locally or remotely.
|
|
274
|
+
#
|
|
275
|
+
# If {#schedule} is called without setting `auto_local` to false, it will block until the owner can be determined.
|
|
276
|
+
# Setting the owner explicitly avoids blocking on ownership determination.
|
|
277
|
+
#
|
|
278
|
+
# @example
|
|
279
|
+
# scheduler = Farce::Scheduler.new
|
|
280
|
+
#
|
|
281
|
+
# # Scheduler hasn't been launched, so it could block.
|
|
282
|
+
# scheduler.owner = Ractor.current
|
|
283
|
+
#
|
|
284
|
+
# # Now this doesn't block
|
|
285
|
+
# scheduler.schedule { some_task }
|
|
286
|
+
#
|
|
287
|
+
# # Can use #launch_thread, but #launch_ractor would fail now that the owner is set.
|
|
288
|
+
# scheduler.launch_thread
|
|
289
|
+
#
|
|
290
|
+
# @param value [Ractor, nil]
|
|
291
|
+
# @raise [ArgumentError] If value is not a Ractor/nil or changes an assigned owner.
|
|
292
|
+
def owner=(value)
|
|
293
|
+
raise ArgumentError, "owner must be a Ractor" unless value.nil? || value.is_a?(Ractor)
|
|
294
|
+
return if @owner.compare_and_set(nil, value)
|
|
295
|
+
return if owner == value
|
|
296
|
+
raise ArgumentError, "owner has already been set"
|
|
297
|
+
end
|
|
298
|
+
|
|
299
|
+
# @return [Boolean] Whether a fiber scheduler constructor was supplied.
|
|
300
|
+
def wraps_external? = @external
|
|
301
|
+
|
|
302
|
+
# Retrieves the constructed external scheduler from its owning Ractor.
|
|
303
|
+
# @return [Object, nil]
|
|
304
|
+
# The external scheduler, or nil before setup or when using the built-in scheduler.
|
|
305
|
+
# @raise [Ractor::IsolationError]
|
|
306
|
+
# If called outside the owning Ractor for an external scheduler.
|
|
307
|
+
def external_scheduler
|
|
308
|
+
return unless wraps_external?
|
|
309
|
+
return Internal::Storage[self] if owner.nil? || owner == Ractor.current
|
|
310
|
+
raise Ractor::IsolationError, "external_scheduler is owned by a different Ractor"
|
|
311
|
+
end
|
|
312
|
+
|
|
313
|
+
# Enqueues a task, waiting for queue space if necessary. Returns after
|
|
314
|
+
# submission. It does not wait for task completion or return the task's value.
|
|
315
|
+
#
|
|
316
|
+
# With `auto_local` enabled, waits for ownership to be assigned and uses `:local`
|
|
317
|
+
# mode whenever the caller and owner are in the same Ractor, including different
|
|
318
|
+
# threads in that Ractor.
|
|
319
|
+
#
|
|
320
|
+
# Local tasks retain their block and arguments.
|
|
321
|
+
#
|
|
322
|
+
# Otherwise, the block must be convertible to a Ractor-shareable proc.
|
|
323
|
+
# Pass task data as arguments so the selected transfer mode can be applied to it.
|
|
324
|
+
#
|
|
325
|
+
# @!macro modes
|
|
326
|
+
#
|
|
327
|
+
# @param args [Array<Object>] Positional arguments passed to the task block.
|
|
328
|
+
# @param mode [Symbol]
|
|
329
|
+
# Argument transfer mode: `:copy`, `:move`, `:local`, `:make_shareable`, `:shareable_copy`, or `:raise`.
|
|
330
|
+
# See {ModeManager#wrap}.
|
|
331
|
+
# @param auto_local [Boolean] Whether to override mode with `:local` in the owning Ractor.
|
|
332
|
+
# @yield [*args] The task to execute.
|
|
333
|
+
# @return [Scheduler] self.
|
|
334
|
+
# @raise [SchedulerClosedError] If the handle is closing, closed, or in an error state.
|
|
335
|
+
# @see Ractor.shareable_proc
|
|
336
|
+
def schedule(*args, mode: :copy, auto_local: true, &block)
|
|
337
|
+
raise SchedulerClosedError, "cannot schedule task on a closed scheduler" if closed?
|
|
338
|
+
|
|
339
|
+
if auto_local
|
|
340
|
+
owner = @owner.wait_until_non_nil
|
|
341
|
+
raise SchedulerClosedError, "cannot schedule task on a closed scheduler" if closed?
|
|
342
|
+
mode = :local if owner == Ractor.current
|
|
343
|
+
elsif mode == :local && (owner = @owner.value) && owner != Ractor.current
|
|
344
|
+
raise Ractor::IsolationError, "cannot schedule local task outside the owning Ractor"
|
|
345
|
+
end
|
|
346
|
+
|
|
347
|
+
task = mode == :local ?
|
|
348
|
+
Envelope::Local.new(args.any? ? -> { block.call(*args) } : block) :
|
|
349
|
+
Internal::ScheduledTask.new(args, block, mode)
|
|
350
|
+
|
|
351
|
+
@queue.push(task)
|
|
352
|
+
self
|
|
353
|
+
rescue ClosedQueueError
|
|
354
|
+
raise unless closed?
|
|
355
|
+
raise SchedulerClosedError, "cannot schedule task on a closed scheduler"
|
|
356
|
+
end
|
|
357
|
+
|
|
358
|
+
# (see Farce::Abstract::Scheduler#local?)
|
|
359
|
+
def local?(wait: true)
|
|
360
|
+
owner = wait ? @owner.wait_until_non_nil : @owner.value
|
|
361
|
+
owner == Ractor.current
|
|
362
|
+
end
|
|
363
|
+
|
|
364
|
+
# Requests shutdown and rejects further submissions. Repeated calls are harmless.
|
|
365
|
+
# The dispatcher drains queued tasks before closing the queue. This method
|
|
366
|
+
# does not wait for dispatch or running tasks to finish.
|
|
367
|
+
#
|
|
368
|
+
# This wakes a dispatcher blocked on an empty queue. It does not directly
|
|
369
|
+
# close the underlying fiber scheduler.
|
|
370
|
+
# @return [Scheduler] self.
|
|
371
|
+
def close
|
|
372
|
+
until closed?
|
|
373
|
+
state = @state.value
|
|
374
|
+
@state.compare_and_set(state, :closing) unless CLOSED_STATES.include?(state)
|
|
375
|
+
end
|
|
376
|
+
# if the scheduler never acquired an owner, #schedule might be blocked
|
|
377
|
+
@owner.compare_and_set(nil, Ractor.current)
|
|
378
|
+
wake_dispatcher
|
|
379
|
+
self
|
|
380
|
+
end
|
|
381
|
+
|
|
382
|
+
# @return [Symbol]
|
|
383
|
+
# The current state of the scheduler. One of:
|
|
384
|
+
# * `:initialized`: The scheduler has been created but not yet launched.
|
|
385
|
+
# * `:launching`: The scheduler is in the process of being {#launch launched} on a background worker.
|
|
386
|
+
# * `:setup`: The scheduler is in the process of being setting up a fiber scheduler.
|
|
387
|
+
# * `:running`: The scheduler is actively dispatching tasks.
|
|
388
|
+
# * `:closing`: The scheduler has been requested to shut down and is draining its queue.
|
|
389
|
+
# * `:closed`: The scheduler has finished shutting down.
|
|
390
|
+
# * `:error`: The scheduler encountered an error during dispatch.
|
|
391
|
+
def state = @state.value
|
|
392
|
+
|
|
393
|
+
# @return [Boolean]
|
|
394
|
+
# Whether new submissions are rejected, including while
|
|
395
|
+
# closing or after a dispatch error.
|
|
396
|
+
# Does not imply all tasks have finished.
|
|
397
|
+
# @see #state
|
|
398
|
+
def closed? = CLOSED_STATES.include?(@state.value)
|
|
399
|
+
|
|
400
|
+
# @return [Boolean] Whether the dispatcher is in its `:running` state.
|
|
401
|
+
# @see #state
|
|
402
|
+
def alive? = @state.value == :running
|
|
403
|
+
|
|
404
|
+
# @api private
|
|
405
|
+
def inspect_with(inspector)
|
|
406
|
+
super do
|
|
407
|
+
external = Internal::Storage[self]
|
|
408
|
+
inspector.attribute(:state, state)
|
|
409
|
+
inspector.attribute(:backend, @backend)
|
|
410
|
+
inspector.attribute(:external_scheduler, external) if external && !external.is_a?(Internal::FiberScheduler)
|
|
411
|
+
inspector.attribute(:local, local?) if @owner.value
|
|
412
|
+
yield if block_given?
|
|
413
|
+
end
|
|
414
|
+
end
|
|
415
|
+
|
|
416
|
+
private
|
|
417
|
+
|
|
418
|
+
def set_scheduler!(expected_state = :initialized, scheduler: nil)
|
|
419
|
+
setup_owner(expected_state)
|
|
420
|
+
scheduler ||= @constructor&.call || Internal::FiberScheduler.new(backend: @backend)
|
|
421
|
+
register = Internal::Storage.store_if_absent(self.class) { Internal::Storage.new }
|
|
422
|
+
register[scheduler] = self
|
|
423
|
+
Internal::Storage[self] = scheduler
|
|
424
|
+
Fiber.set_scheduler(scheduler) unless Fiber.scheduler.equal?(scheduler)
|
|
425
|
+
runnable_count = :farce_runnable_count if @pool_worker && scheduler.respond_to?(:farce_runnable_count)
|
|
426
|
+
scheduler.fiber do
|
|
427
|
+
dispatch(scheduler, runnable_count) do |task|
|
|
428
|
+
if @pool_worker
|
|
429
|
+
scheduler.fiber(&task.to_proc(@pool_worker))
|
|
430
|
+
else
|
|
431
|
+
scheduler.fiber(&task)
|
|
432
|
+
end
|
|
433
|
+
end
|
|
434
|
+
end
|
|
435
|
+
end
|
|
436
|
+
|
|
437
|
+
def launch!(name, priority)
|
|
438
|
+
Thread.current.name = name if name
|
|
439
|
+
Thread.current.priority = priority if priority
|
|
440
|
+
|
|
441
|
+
if Fiber.respond_to?(:set_scheduler)
|
|
442
|
+
begin
|
|
443
|
+
set_scheduler!(:launching)
|
|
444
|
+
ensure
|
|
445
|
+
Fiber.set_scheduler(nil) if Fiber.scheduler
|
|
446
|
+
end
|
|
447
|
+
else
|
|
448
|
+
setup_owner(:launching)
|
|
449
|
+
group = ThreadGroup.new
|
|
450
|
+
group.add(Thread.current)
|
|
451
|
+
|
|
452
|
+
dispatch do |task|
|
|
453
|
+
callback = @pool_worker ? task.to_proc(@pool_worker) : task.to_proc
|
|
454
|
+
group.add(Thread.new(&callback))
|
|
455
|
+
end
|
|
456
|
+
group.list.each { it.join unless it.equal?(Thread.current) }
|
|
457
|
+
end
|
|
458
|
+
end
|
|
459
|
+
|
|
460
|
+
def setup_owner(expected_state)
|
|
461
|
+
raise "Scheduler may not be reused" unless @state.compare_and_set(expected_state, :setup)
|
|
462
|
+
self.owner = Ractor.current
|
|
463
|
+
end
|
|
464
|
+
|
|
465
|
+
def wake_dispatcher
|
|
466
|
+
# A full queue already guarantees that the dispatcher will wake and observe the closing state.
|
|
467
|
+
@queue.try_push(nil)
|
|
468
|
+
rescue ClosedQueueError
|
|
469
|
+
nil
|
|
470
|
+
end
|
|
471
|
+
|
|
472
|
+
def dispatch(scheduler = nil, runnable_count = nil)
|
|
473
|
+
failure = nil
|
|
474
|
+
@state.compare_and_set(:setup, :running)
|
|
475
|
+
while keep_processing?
|
|
476
|
+
break if @pool_worker && !@pool_worker.wait_for_admission(scheduler, runnable_count)
|
|
477
|
+
timed_out = false
|
|
478
|
+
timeout = @pool_worker&.idle_timeout
|
|
479
|
+
task = @queue.pop(timeout:) { timed_out = true }
|
|
480
|
+
if timed_out
|
|
481
|
+
break if @pool_worker&.retire?
|
|
482
|
+
next
|
|
483
|
+
end
|
|
484
|
+
next unless task
|
|
485
|
+
task = task.value if task.is_a?(Envelope)
|
|
486
|
+
if task
|
|
487
|
+
@pool_worker&.task_started
|
|
488
|
+
yield task
|
|
489
|
+
end
|
|
490
|
+
end
|
|
491
|
+
@queue.close if @queue_owner
|
|
492
|
+
rescue Internal::SchedulerLifecycle::Cancelled => e
|
|
493
|
+
# Shutdown cancellation must not create envelope ports in a terminating Ractor.
|
|
494
|
+
failure = e
|
|
495
|
+
raise
|
|
496
|
+
rescue ClosedQueueError
|
|
497
|
+
raise unless @pool_worker
|
|
498
|
+
rescue Exception => e # rubocop:disable Lint/RescueException
|
|
499
|
+
failure = e
|
|
500
|
+
@state.value = :error
|
|
501
|
+
@error.value = Envelope.new(e)
|
|
502
|
+
raise
|
|
503
|
+
ensure
|
|
504
|
+
@pool_worker&.stopped(failure)
|
|
505
|
+
# make sure we're in a closed state
|
|
506
|
+
unless @state.compare_and_set(:closing, :closed)
|
|
507
|
+
state = @state.value
|
|
508
|
+
@state.compare_and_set(state, :closed) unless CLOSED_STATES.include?(state)
|
|
509
|
+
end
|
|
510
|
+
end
|
|
511
|
+
|
|
512
|
+
def next_task
|
|
513
|
+
task = @queue.pop
|
|
514
|
+
return task unless task.is_a?(Envelope)
|
|
515
|
+
task.value
|
|
516
|
+
end
|
|
517
|
+
|
|
518
|
+
def keep_processing?
|
|
519
|
+
case @state.value
|
|
520
|
+
when :running, :setup then true
|
|
521
|
+
when :closing then @queue.size.positive?
|
|
522
|
+
else false
|
|
523
|
+
end
|
|
524
|
+
end
|
|
525
|
+
end
|
|
526
|
+
|
|
527
|
+
module FiberExtension
|
|
528
|
+
Fiber.singleton_class.prepend(self) if Fiber.respond_to?(:set_scheduler)
|
|
529
|
+
def set_scheduler(scheduler) # rubocop:disable Naming/AccessorMethodName
|
|
530
|
+
return super unless scheduler.is_a?(Farce::Scheduler)
|
|
531
|
+
scheduler.__send__(:set_scheduler!)
|
|
532
|
+
end
|
|
533
|
+
end
|
|
534
|
+
|
|
535
|
+
private_constant :FiberExtension
|
|
536
|
+
end
|
data/lib/farce/set.rb
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# shareable_constant_value: literal
|
|
3
|
+
# warn_indent: true
|
|
4
|
+
|
|
5
|
+
module Farce
|
|
6
|
+
# A Ractor-shareable concurrent set whose elements support transfer modes.
|
|
7
|
+
# Membership uses a stable insertion-time hash and eql? snapshot. Later
|
|
8
|
+
# mutation of a local element does not change its membership key. Elements
|
|
9
|
+
# whose equality cannot be preserved by a shareable snapshot are rejected.
|
|
10
|
+
# Identity comparison supports shareable elements and the `:local`, `:move`,
|
|
11
|
+
# `:make_shareable`, and `:shareable_copy` modes. Copying a non-shareable
|
|
12
|
+
# identity element is unsupported. On runtimes without native Ractors,
|
|
13
|
+
# elements follow ordinary hash-key mutation rules.
|
|
14
|
+
#
|
|
15
|
+
# @example Track unique job identifiers
|
|
16
|
+
# jobs = Farce::Set[:compile, :test]
|
|
17
|
+
# jobs.add?(:publish) # => jobs
|
|
18
|
+
# jobs.add?(:test) # => nil
|
|
19
|
+
class Set < Farce::Abstract::Set
|
|
20
|
+
include Shareable::Delegated
|
|
21
|
+
|
|
22
|
+
# The default transfer mode for elements.
|
|
23
|
+
# @return [Symbol] The configured transfer mode.
|
|
24
|
+
def mode = @manager.mode
|
|
25
|
+
|
|
26
|
+
protected
|
|
27
|
+
|
|
28
|
+
def value_modes? = true
|
|
29
|
+
|
|
30
|
+
private
|
|
31
|
+
|
|
32
|
+
def initialize_value_mode(mode)
|
|
33
|
+
mode = :copy if UNDEFINED.equal?(mode)
|
|
34
|
+
@manager = ModeManager.new(mode:)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def new_map(...) = Farce::Strict::Map.new(...)
|
|
38
|
+
end
|
|
39
|
+
end
|