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
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
#include "containers.h"
|
|
2
|
+
|
|
3
|
+
typedef struct {
|
|
4
|
+
unsigned char reserved;
|
|
5
|
+
} containers_pin_marker_t;
|
|
6
|
+
|
|
7
|
+
static ID pin_marker_id;
|
|
8
|
+
static ID copy_marker_id;
|
|
9
|
+
static ID move_marker_id;
|
|
10
|
+
static ID pipe_id;
|
|
11
|
+
static ID close_id;
|
|
12
|
+
static VALUE cPinMarker;
|
|
13
|
+
static VALUE eIsolationError;
|
|
14
|
+
|
|
15
|
+
static size_t
|
|
16
|
+
containers_pin_marker_size(const void *pointer)
|
|
17
|
+
{
|
|
18
|
+
return pointer == NULL ? 0 : sizeof(containers_pin_marker_t);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
static const rb_data_type_t containers_pin_marker_type = {
|
|
22
|
+
.wrap_struct_name = "Ractor::Containers pin marker",
|
|
23
|
+
.function = {
|
|
24
|
+
.dmark = NULL,
|
|
25
|
+
.dfree = RUBY_TYPED_DEFAULT_FREE,
|
|
26
|
+
.dsize = containers_pin_marker_size,
|
|
27
|
+
.dcompact = NULL,
|
|
28
|
+
},
|
|
29
|
+
.parent = NULL,
|
|
30
|
+
.data = NULL,
|
|
31
|
+
.flags = RUBY_TYPED_FREE_IMMEDIATELY,
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
static bool
|
|
35
|
+
containers_pin_marker_p(VALUE value)
|
|
36
|
+
{
|
|
37
|
+
return !NIL_P(value) && rb_typeddata_is_kind_of(value, &containers_pin_marker_type);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
static void
|
|
41
|
+
containers_check_guard_target(VALUE object)
|
|
42
|
+
{
|
|
43
|
+
if (RB_SPECIAL_CONST_P(object)) {
|
|
44
|
+
rb_raise(rb_eTypeError, "cannot guard an immediate value");
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
rb_check_frozen(object);
|
|
48
|
+
if (rb_ractor_shareable_p(object)) {
|
|
49
|
+
rb_raise(eIsolationError, "cannot guard an object that is already shareable");
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
static VALUE
|
|
54
|
+
containers_new_pin_marker(void)
|
|
55
|
+
{
|
|
56
|
+
containers_pin_marker_t *marker_data;
|
|
57
|
+
VALUE marker = TypedData_Make_Struct(
|
|
58
|
+
cPinMarker,
|
|
59
|
+
containers_pin_marker_t,
|
|
60
|
+
&containers_pin_marker_type,
|
|
61
|
+
marker_data
|
|
62
|
+
);
|
|
63
|
+
marker_data->reserved = 0;
|
|
64
|
+
rb_obj_freeze(marker);
|
|
65
|
+
return marker;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
static VALUE
|
|
69
|
+
containers_new_copy_marker(void)
|
|
70
|
+
{
|
|
71
|
+
VALUE pair = rb_funcall(rb_cIO, pipe_id, 0);
|
|
72
|
+
VALUE reader = rb_ary_entry(pair, 0);
|
|
73
|
+
VALUE writer = rb_ary_entry(pair, 1);
|
|
74
|
+
|
|
75
|
+
rb_funcall(reader, close_id, 0);
|
|
76
|
+
rb_funcall(writer, close_id, 0);
|
|
77
|
+
return reader;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
static VALUE
|
|
81
|
+
containers_unshareable_pin(VALUE module, VALUE object)
|
|
82
|
+
{
|
|
83
|
+
VALUE marker;
|
|
84
|
+
(void)module;
|
|
85
|
+
|
|
86
|
+
if (RB_SPECIAL_CONST_P(object)) {
|
|
87
|
+
rb_raise(rb_eTypeError, "cannot guard an immediate value");
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
marker = rb_ivar_get(object, pin_marker_id);
|
|
91
|
+
if (containers_pin_marker_p(marker)) return object;
|
|
92
|
+
|
|
93
|
+
containers_check_guard_target(object);
|
|
94
|
+
rb_ivar_set(object, pin_marker_id, containers_new_pin_marker());
|
|
95
|
+
return object;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
static VALUE
|
|
99
|
+
containers_unshareable_prevent_copyable(VALUE module, VALUE object)
|
|
100
|
+
{
|
|
101
|
+
VALUE marker;
|
|
102
|
+
(void)module;
|
|
103
|
+
|
|
104
|
+
if (RB_SPECIAL_CONST_P(object)) {
|
|
105
|
+
rb_raise(rb_eTypeError, "cannot guard an immediate value");
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
if (containers_pin_marker_p(rb_ivar_get(object, pin_marker_id))) return object;
|
|
109
|
+
marker = rb_ivar_get(object, copy_marker_id);
|
|
110
|
+
if (rb_obj_is_kind_of(marker, rb_cIO)) return object;
|
|
111
|
+
|
|
112
|
+
containers_check_guard_target(object);
|
|
113
|
+
rb_ivar_set(object, copy_marker_id, containers_new_copy_marker());
|
|
114
|
+
return object;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
static VALUE
|
|
118
|
+
containers_unshareable_prevent_movable(VALUE module, VALUE object)
|
|
119
|
+
{
|
|
120
|
+
VALUE marker;
|
|
121
|
+
(void)module;
|
|
122
|
+
|
|
123
|
+
if (RB_SPECIAL_CONST_P(object)) {
|
|
124
|
+
rb_raise(rb_eTypeError, "cannot guard an immediate value");
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
if (containers_pin_marker_p(rb_ivar_get(object, pin_marker_id))) return object;
|
|
128
|
+
marker = rb_ivar_get(object, move_marker_id);
|
|
129
|
+
if (rb_obj_is_kind_of(marker, rb_cTime)) return object;
|
|
130
|
+
|
|
131
|
+
containers_check_guard_target(object);
|
|
132
|
+
rb_ivar_set(object, move_marker_id, rb_time_new(0, 0));
|
|
133
|
+
return object;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
void
|
|
137
|
+
containers_init_unshareable(VALUE namespace)
|
|
138
|
+
{
|
|
139
|
+
VALUE module = rb_define_module_under(namespace, "Unshareable");
|
|
140
|
+
|
|
141
|
+
pin_marker_id = rb_intern("__ractor_containers_pin__");
|
|
142
|
+
copy_marker_id = rb_intern("__ractor_containers_prevent_copy__");
|
|
143
|
+
move_marker_id = rb_intern("__ractor_containers_prevent_move__");
|
|
144
|
+
pipe_id = rb_intern("pipe");
|
|
145
|
+
close_id = rb_intern("close");
|
|
146
|
+
eIsolationError = rb_const_get(rb_cRactor, rb_intern("IsolationError"));
|
|
147
|
+
|
|
148
|
+
cPinMarker = rb_class_new(rb_cObject);
|
|
149
|
+
rb_undef_alloc_func(cPinMarker);
|
|
150
|
+
rb_global_variable(&cPinMarker);
|
|
151
|
+
|
|
152
|
+
rb_define_singleton_method(module, "pin_to_current_ractor", containers_unshareable_pin, 1);
|
|
153
|
+
rb_define_singleton_method(module, "prevent_copyable", containers_unshareable_prevent_copyable, 1);
|
|
154
|
+
rb_define_singleton_method(module, "prevent_movable", containers_unshareable_prevent_movable, 1);
|
|
155
|
+
}
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
#ifndef FARCE_UNSHARED_IO_POOL_H
|
|
2
|
+
#define FARCE_UNSHARED_IO_POOL_H
|
|
3
|
+
|
|
4
|
+
#include "unshared_wait.h"
|
|
5
|
+
#include "ruby/io.h"
|
|
6
|
+
#include <errno.h>
|
|
7
|
+
#include <fcntl.h>
|
|
8
|
+
#include <unistd.h>
|
|
9
|
+
|
|
10
|
+
/* Each pair belongs to its Ruby IO objects. Native code never closes their
|
|
11
|
+
* descriptors. A slot stays active until io_wait returns or unwinds, so no
|
|
12
|
+
* scheduler can observe a descriptor reassigned to another pending wait. */
|
|
13
|
+
typedef struct farce_io_slot farce_io_slot_t;
|
|
14
|
+
struct farce_io_slot {
|
|
15
|
+
VALUE reader;
|
|
16
|
+
VALUE writer;
|
|
17
|
+
int read_fd;
|
|
18
|
+
int write_fd;
|
|
19
|
+
bool notified;
|
|
20
|
+
bool broken;
|
|
21
|
+
farce_io_slot_t *all_previous;
|
|
22
|
+
farce_io_slot_t *all_next;
|
|
23
|
+
farce_io_slot_t *idle_next;
|
|
24
|
+
farce_io_slot_t *previous;
|
|
25
|
+
farce_io_slot_t *next;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
typedef struct {
|
|
29
|
+
farce_io_slot_t *all;
|
|
30
|
+
farce_io_slot_t *idle;
|
|
31
|
+
farce_io_slot_t *active;
|
|
32
|
+
size_t idle_count;
|
|
33
|
+
size_t active_count;
|
|
34
|
+
size_t size;
|
|
35
|
+
} farce_io_pool_t;
|
|
36
|
+
|
|
37
|
+
#define FARCE_IO_POOL_IDLE_LIMIT 2
|
|
38
|
+
|
|
39
|
+
static void
|
|
40
|
+
farce_io_pool_mark(farce_io_pool_t *pool)
|
|
41
|
+
{
|
|
42
|
+
for (farce_io_slot_t *slot = pool->all; slot; slot = slot->all_next) {
|
|
43
|
+
rb_gc_mark(slot->reader);
|
|
44
|
+
rb_gc_mark(slot->writer);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
static void
|
|
49
|
+
farce_io_pool_free(farce_io_pool_t *pool)
|
|
50
|
+
{
|
|
51
|
+
farce_io_slot_t *slot = pool->all;
|
|
52
|
+
while (slot) {
|
|
53
|
+
farce_io_slot_t *next = slot->all_next;
|
|
54
|
+
/* Ruby finalizes the IO objects and their owned descriptors. */
|
|
55
|
+
free(slot);
|
|
56
|
+
slot = next;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
static size_t
|
|
61
|
+
farce_io_pool_memsize(farce_io_pool_t *pool)
|
|
62
|
+
{
|
|
63
|
+
return pool->size * sizeof(farce_io_slot_t);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
static VALUE
|
|
67
|
+
farce_io_pool_descriptor(VALUE io)
|
|
68
|
+
{
|
|
69
|
+
return INT2NUM(rb_io_descriptor(io));
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
static void
|
|
73
|
+
farce_io_pool_discard(farce_io_pool_t *pool, farce_io_slot_t *slot)
|
|
74
|
+
{
|
|
75
|
+
if (slot->all_previous) slot->all_previous->all_next = slot->all_next;
|
|
76
|
+
else pool->all = slot->all_next;
|
|
77
|
+
if (slot->all_next) slot->all_next->all_previous = slot->all_previous;
|
|
78
|
+
pool->size--;
|
|
79
|
+
VALUE reader = slot->reader;
|
|
80
|
+
VALUE writer = slot->writer;
|
|
81
|
+
free(slot);
|
|
82
|
+
/* Cleanup must close both ends without masking the original exception. */
|
|
83
|
+
VALUE error = rb_errinfo();
|
|
84
|
+
int state;
|
|
85
|
+
rb_protect(rb_io_close, reader, &state);
|
|
86
|
+
rb_set_errinfo(error);
|
|
87
|
+
rb_protect(rb_io_close, writer, &state);
|
|
88
|
+
rb_set_errinfo(error);
|
|
89
|
+
RB_GC_GUARD(reader);
|
|
90
|
+
RB_GC_GUARD(writer);
|
|
91
|
+
RB_GC_GUARD(error);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
static farce_io_slot_t *
|
|
95
|
+
farce_io_pool_acquire(farce_io_pool_t *pool)
|
|
96
|
+
{
|
|
97
|
+
if (pool->idle) {
|
|
98
|
+
farce_io_slot_t *slot = pool->idle;
|
|
99
|
+
pool->idle = slot->idle_next;
|
|
100
|
+
pool->idle_count--;
|
|
101
|
+
slot->idle_next = NULL;
|
|
102
|
+
return slot;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/* IO.pipe supplies fully owned, exception-safe Ruby handles. If setup
|
|
106
|
+
* raises before the slot is linked, Ruby still owns both descriptors. */
|
|
107
|
+
VALUE pair = rb_funcall(rb_cIO, rb_intern("pipe"), 0);
|
|
108
|
+
VALUE reader = rb_ary_entry(pair, 0);
|
|
109
|
+
VALUE writer = rb_ary_entry(pair, 1);
|
|
110
|
+
int read_fd = rb_io_descriptor(reader);
|
|
111
|
+
int write_fd = rb_io_descriptor(writer);
|
|
112
|
+
#ifndef _WIN32
|
|
113
|
+
int read_flags = fcntl(read_fd, F_GETFL, 0);
|
|
114
|
+
int write_flags = fcntl(write_fd, F_GETFL, 0);
|
|
115
|
+
if (read_flags < 0 || write_flags < 0 ||
|
|
116
|
+
fcntl(read_fd, F_SETFL, read_flags | O_NONBLOCK) < 0 ||
|
|
117
|
+
fcntl(write_fd, F_SETFL, write_flags | O_NONBLOCK) < 0) rb_sys_fail("fcntl");
|
|
118
|
+
#endif
|
|
119
|
+
farce_io_slot_t *slot = calloc(1, sizeof(*slot));
|
|
120
|
+
if (!slot) rb_memerror();
|
|
121
|
+
slot->reader = reader;
|
|
122
|
+
slot->writer = writer;
|
|
123
|
+
slot->read_fd = read_fd;
|
|
124
|
+
slot->write_fd = write_fd;
|
|
125
|
+
slot->all_next = pool->all;
|
|
126
|
+
if (slot->all_next) slot->all_next->all_previous = slot;
|
|
127
|
+
pool->all = slot;
|
|
128
|
+
pool->size++;
|
|
129
|
+
RB_GC_GUARD(pair);
|
|
130
|
+
return slot;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
static void
|
|
134
|
+
farce_io_pool_release(farce_io_pool_t *pool, farce_io_slot_t *slot)
|
|
135
|
+
{
|
|
136
|
+
VALUE error = rb_errinfo();
|
|
137
|
+
int state;
|
|
138
|
+
VALUE descriptor = rb_protect(farce_io_pool_descriptor, slot->reader, &state);
|
|
139
|
+
rb_set_errinfo(error);
|
|
140
|
+
if (state || NUM2INT(descriptor) != slot->read_fd) slot->broken = true;
|
|
141
|
+
if (!slot->broken && slot->notified) {
|
|
142
|
+
unsigned char byte;
|
|
143
|
+
ssize_t count;
|
|
144
|
+
do { count = read(slot->read_fd, &byte, 1); } while (count < 0 && errno == EINTR);
|
|
145
|
+
if (count == 0 || (count < 0 && errno != EAGAIN && errno != EWOULDBLOCK)) slot->broken = true;
|
|
146
|
+
}
|
|
147
|
+
slot->notified = false;
|
|
148
|
+
if (slot->broken || pool->idle_count == FARCE_IO_POOL_IDLE_LIMIT) {
|
|
149
|
+
farce_io_pool_discard(pool, slot);
|
|
150
|
+
}
|
|
151
|
+
else {
|
|
152
|
+
slot->idle_next = pool->idle;
|
|
153
|
+
pool->idle = slot;
|
|
154
|
+
pool->idle_count++;
|
|
155
|
+
}
|
|
156
|
+
RB_GC_GUARD(error);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
static void
|
|
160
|
+
farce_io_pool_notify(farce_io_pool_t *pool)
|
|
161
|
+
{
|
|
162
|
+
const unsigned char byte = 1;
|
|
163
|
+
for (farce_io_slot_t *slot = pool->active; slot; slot = slot->next) {
|
|
164
|
+
if (slot->notified || slot->broken) continue;
|
|
165
|
+
ssize_t count;
|
|
166
|
+
do { count = write(slot->write_fd, &byte, 1); } while (count < 0 && errno == EINTR);
|
|
167
|
+
if (count == 1 || (count < 0 && (errno == EAGAIN || errno == EWOULDBLOCK))) slot->notified = true;
|
|
168
|
+
else slot->broken = true;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
typedef struct {
|
|
173
|
+
farce_io_pool_t *pool;
|
|
174
|
+
farce_io_slot_t *slot;
|
|
175
|
+
bool (*ready)(void *);
|
|
176
|
+
void *data;
|
|
177
|
+
bool finite;
|
|
178
|
+
double deadline;
|
|
179
|
+
bool registered;
|
|
180
|
+
bool completed;
|
|
181
|
+
} farce_io_pool_wait_t;
|
|
182
|
+
|
|
183
|
+
static VALUE
|
|
184
|
+
farce_io_pool_wait_body(VALUE opaque)
|
|
185
|
+
{
|
|
186
|
+
farce_io_pool_wait_t *wait = (farce_io_pool_wait_t *)opaque;
|
|
187
|
+
wait->slot = farce_io_pool_acquire(wait->pool);
|
|
188
|
+
/* Acquiring a new pipe may run Ruby. Recheck the caller's condition before
|
|
189
|
+
* registering under the GVL, including notifications during allocation. */
|
|
190
|
+
if (wait->ready(wait->data)) { wait->completed = true; return Qtrue; }
|
|
191
|
+
double remaining = wait->finite ? wait->deadline - farce_unshared_wait_now() : 0;
|
|
192
|
+
if (wait->finite && remaining <= 0) { wait->completed = true; return Qfalse; }
|
|
193
|
+
farce_io_slot_t *slot = wait->slot;
|
|
194
|
+
/* Older CRuby versions dispatch io_wait without checking a closed IO.
|
|
195
|
+
* Validate before handing a cached object back to the scheduler. */
|
|
196
|
+
if (rb_io_descriptor(slot->reader) != slot->read_fd) rb_raise(rb_eIOError, "queue wait descriptor changed");
|
|
197
|
+
slot->previous = NULL;
|
|
198
|
+
slot->next = wait->pool->active;
|
|
199
|
+
if (slot->next) slot->next->previous = slot;
|
|
200
|
+
wait->pool->active = slot;
|
|
201
|
+
wait->pool->active_count++;
|
|
202
|
+
wait->registered = true;
|
|
203
|
+
VALUE result = rb_io_wait(slot->reader, INT2NUM(RUBY_IO_READABLE), wait->finite ? DBL2NUM(remaining) : Qnil);
|
|
204
|
+
wait->completed = true;
|
|
205
|
+
return result;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
static VALUE
|
|
209
|
+
farce_io_pool_wait_cleanup(VALUE opaque)
|
|
210
|
+
{
|
|
211
|
+
farce_io_pool_wait_t *wait = (farce_io_pool_wait_t *)opaque;
|
|
212
|
+
if (!wait->slot) return Qnil;
|
|
213
|
+
farce_io_slot_t *slot = wait->slot;
|
|
214
|
+
if (wait->registered) {
|
|
215
|
+
if (slot->previous) slot->previous->next = slot->next;
|
|
216
|
+
else wait->pool->active = slot->next;
|
|
217
|
+
if (slot->next) slot->next->previous = slot->previous;
|
|
218
|
+
wait->pool->active_count--;
|
|
219
|
+
}
|
|
220
|
+
/* An interrupted scheduler may have started closing its IO. Finish that
|
|
221
|
+
* close after rb_io_wait unwinds, and never reuse an exceptional slot. */
|
|
222
|
+
if (!wait->completed) slot->broken = true;
|
|
223
|
+
farce_io_pool_release(wait->pool, slot);
|
|
224
|
+
return Qnil;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
static bool
|
|
228
|
+
farce_io_pool_wait(farce_io_pool_t *pool, bool finite, double deadline, bool (*ready)(void *), void *data)
|
|
229
|
+
{
|
|
230
|
+
farce_io_pool_wait_t wait = {.pool = pool, .finite = finite, .deadline = deadline, .ready = ready, .data = data};
|
|
231
|
+
return RTEST(rb_ensure(farce_io_pool_wait_body, (VALUE)&wait, farce_io_pool_wait_cleanup, (VALUE)&wait));
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
#endif
|
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
#include "unshared_io_pool.h"
|
|
2
|
+
|
|
3
|
+
static VALUE cUnsharedSignal;
|
|
4
|
+
static VALUE cUnsharedIOSignal;
|
|
5
|
+
static VALUE cUnsharedBlockSignal;
|
|
6
|
+
|
|
7
|
+
struct containers_unshared_signal {
|
|
8
|
+
farce_unshared_wait_list_t waiters;
|
|
9
|
+
uint64_t generation;
|
|
10
|
+
farce_io_pool_t fiber_pool;
|
|
11
|
+
size_t fiber_waiters;
|
|
12
|
+
bool initialized;
|
|
13
|
+
bool fiber_io;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
static void
|
|
17
|
+
unshared_signal_mark(void *pointer)
|
|
18
|
+
{
|
|
19
|
+
containers_unshared_signal_t *signal = pointer;
|
|
20
|
+
farce_unshared_wait_mark(&signal->waiters);
|
|
21
|
+
farce_io_pool_mark(&signal->fiber_pool);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
static void
|
|
25
|
+
unshared_signal_free(void *pointer)
|
|
26
|
+
{
|
|
27
|
+
containers_unshared_signal_t *signal = pointer;
|
|
28
|
+
farce_io_pool_free(&signal->fiber_pool);
|
|
29
|
+
ruby_xfree(signal);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
static size_t
|
|
33
|
+
unshared_signal_memsize(const void *pointer)
|
|
34
|
+
{
|
|
35
|
+
const containers_unshared_signal_t *signal = pointer;
|
|
36
|
+
return signal ? sizeof(*signal) + signal->fiber_pool.size * sizeof(farce_io_slot_t) : 0;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
static const rb_data_type_t unshared_signal_type = {
|
|
40
|
+
.wrap_struct_name = "Farce::Internal::UnsharedSignal",
|
|
41
|
+
.function = {
|
|
42
|
+
.dmark = unshared_signal_mark,
|
|
43
|
+
.dfree = unshared_signal_free,
|
|
44
|
+
.dsize = unshared_signal_memsize,
|
|
45
|
+
},
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
static VALUE
|
|
49
|
+
unshared_signal_allocate_mode(VALUE klass, bool fiber_io)
|
|
50
|
+
{
|
|
51
|
+
containers_unshared_signal_t *signal;
|
|
52
|
+
VALUE self = TypedData_Make_Struct(klass, containers_unshared_signal_t, &unshared_signal_type, signal);
|
|
53
|
+
signal->fiber_io = fiber_io;
|
|
54
|
+
return self;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
static VALUE unshared_signal_allocate(VALUE klass) { return unshared_signal_allocate_mode(klass, FARCE_UNSHARED_FIBER_IO); }
|
|
58
|
+
static VALUE unshared_io_signal_allocate(VALUE klass) { return unshared_signal_allocate_mode(klass, true); }
|
|
59
|
+
static VALUE unshared_block_signal_allocate(VALUE klass) { return unshared_signal_allocate_mode(klass, false); }
|
|
60
|
+
|
|
61
|
+
static containers_unshared_signal_t *
|
|
62
|
+
unshared_signal_get(VALUE self)
|
|
63
|
+
{
|
|
64
|
+
containers_unshared_signal_t *signal;
|
|
65
|
+
TypedData_Get_Struct(self, containers_unshared_signal_t, &unshared_signal_type, signal);
|
|
66
|
+
if (!signal->initialized) rb_raise(rb_eRuntimeError, "uninitialized Signal");
|
|
67
|
+
return signal;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/* Only the built-in classes bypass Ruby dispatch. Further subclasses retain
|
|
71
|
+
* ordinary protocol dispatch, including overrides. */
|
|
72
|
+
containers_unshared_signal_t *
|
|
73
|
+
containers_unshared_signal_get_if_exact(VALUE self)
|
|
74
|
+
{
|
|
75
|
+
VALUE klass = CLASS_OF(self);
|
|
76
|
+
return klass == cUnsharedSignal || klass == cUnsharedIOSignal || klass == cUnsharedBlockSignal ?
|
|
77
|
+
unshared_signal_get(self) : NULL;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
bool
|
|
81
|
+
containers_unshared_fiber_io(VALUE mode)
|
|
82
|
+
{
|
|
83
|
+
if (mode == Qundef || mode == ID2SYM(rb_intern("auto"))) return FARCE_UNSHARED_FIBER_IO;
|
|
84
|
+
if (mode == ID2SYM(rb_intern("io"))) return true;
|
|
85
|
+
if (mode == ID2SYM(rb_intern("block"))) return false;
|
|
86
|
+
rb_raise(rb_eArgError, "fiber_wait must be :auto, :io, or :block");
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/* Comparison-lock contention follows even a user subclass's native waiting
|
|
90
|
+
* policy. Inspecting it here never calls Ruby during queue publication. */
|
|
91
|
+
bool
|
|
92
|
+
containers_unshared_signal_uses_fiber_io(VALUE self)
|
|
93
|
+
{
|
|
94
|
+
if (!rb_typeddata_is_kind_of(self, &unshared_signal_type)) return FARCE_UNSHARED_FIBER_IO;
|
|
95
|
+
return unshared_signal_get(self)->fiber_io;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
static VALUE
|
|
99
|
+
unshared_signal_for(VALUE klass, VALUE mode)
|
|
100
|
+
{
|
|
101
|
+
(void)klass;
|
|
102
|
+
bool fiber_io = containers_unshared_fiber_io(mode);
|
|
103
|
+
VALUE selected = mode == ID2SYM(rb_intern("auto")) ? cUnsharedSignal :
|
|
104
|
+
(fiber_io ? cUnsharedIOSignal : cUnsharedBlockSignal);
|
|
105
|
+
return rb_class_new_instance(0, NULL, selected);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
static VALUE
|
|
109
|
+
unshared_signal_fiber_wait(VALUE self)
|
|
110
|
+
{
|
|
111
|
+
return ID2SYM(rb_intern(unshared_signal_get(self)->fiber_io ? "io" : "block"));
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
bool
|
|
115
|
+
containers_unshared_signal_has_waiters(containers_unshared_signal_t *signal)
|
|
116
|
+
{
|
|
117
|
+
return signal->waiters.count != 0 || signal->fiber_waiters != 0;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
void
|
|
121
|
+
containers_unshared_signal_notify(containers_unshared_signal_t *signal)
|
|
122
|
+
{
|
|
123
|
+
signal->generation++;
|
|
124
|
+
/* Pipe notification does not call Ruby. Notify fibers before direct
|
|
125
|
+
* scheduler callbacks, which may yield or raise. */
|
|
126
|
+
if (signal->fiber_waiters) farce_io_pool_notify(&signal->fiber_pool);
|
|
127
|
+
farce_unshared_wait_notify_all(&signal->waiters);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
static VALUE
|
|
131
|
+
unshared_signal_initialize(VALUE self)
|
|
132
|
+
{
|
|
133
|
+
containers_unshared_signal_t *signal;
|
|
134
|
+
TypedData_Get_Struct(self, containers_unshared_signal_t, &unshared_signal_type, signal);
|
|
135
|
+
if (signal->initialized) rb_raise(rb_eRuntimeError, "Signal is already initialized");
|
|
136
|
+
rb_check_frozen(self);
|
|
137
|
+
signal->initialized = true;
|
|
138
|
+
rb_obj_freeze(self);
|
|
139
|
+
return self;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
static VALUE
|
|
143
|
+
unshared_signal_initialize_copy(VALUE self, VALUE other)
|
|
144
|
+
{
|
|
145
|
+
(void)self;
|
|
146
|
+
(void)other;
|
|
147
|
+
rb_raise(rb_eTypeError, "cannot copy Signal");
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
static VALUE
|
|
151
|
+
unshared_signal_generation(VALUE self)
|
|
152
|
+
{
|
|
153
|
+
return ULL2NUM(unshared_signal_get(self)->generation);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
static VALUE
|
|
157
|
+
unshared_signal_num_waiting(VALUE self)
|
|
158
|
+
{
|
|
159
|
+
containers_unshared_signal_t *signal = unshared_signal_get(self);
|
|
160
|
+
return SIZET2NUM(signal->waiters.count + signal->fiber_waiters);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
static VALUE
|
|
164
|
+
unshared_signal_broadcast(VALUE self)
|
|
165
|
+
{
|
|
166
|
+
containers_unshared_signal_t *signal = unshared_signal_get(self);
|
|
167
|
+
uint64_t generation = signal->generation + 1;
|
|
168
|
+
containers_unshared_signal_notify(signal);
|
|
169
|
+
return ULL2NUM(generation);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
typedef struct {
|
|
173
|
+
containers_unshared_signal_t *signal;
|
|
174
|
+
uint64_t observed;
|
|
175
|
+
double deadline;
|
|
176
|
+
bool finite;
|
|
177
|
+
} unshared_signal_fiber_wait_t;
|
|
178
|
+
|
|
179
|
+
static bool
|
|
180
|
+
unshared_signal_fiber_ready(void *opaque)
|
|
181
|
+
{
|
|
182
|
+
unshared_signal_fiber_wait_t *wait = opaque;
|
|
183
|
+
return wait->signal->generation != wait->observed;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
static VALUE
|
|
187
|
+
unshared_signal_fiber_wait_body(VALUE opaque)
|
|
188
|
+
{
|
|
189
|
+
unshared_signal_fiber_wait_t *wait = (unshared_signal_fiber_wait_t *)opaque;
|
|
190
|
+
return farce_io_pool_wait(&wait->signal->fiber_pool, wait->finite, wait->deadline,
|
|
191
|
+
unshared_signal_fiber_ready, wait) ? Qtrue : Qfalse;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
static VALUE
|
|
195
|
+
unshared_signal_fiber_wait_cleanup(VALUE opaque)
|
|
196
|
+
{
|
|
197
|
+
unshared_signal_fiber_wait_t *wait = (unshared_signal_fiber_wait_t *)opaque;
|
|
198
|
+
wait->signal->fiber_waiters--;
|
|
199
|
+
return Qnil;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
static VALUE
|
|
203
|
+
unshared_signal_wait(int argc, VALUE *argv, VALUE self)
|
|
204
|
+
{
|
|
205
|
+
VALUE observed_value = Qnil;
|
|
206
|
+
VALUE keywords = Qnil;
|
|
207
|
+
VALUE keyword_values[1] = {Qundef};
|
|
208
|
+
ID keyword_ids[] = {rb_intern("timeout")};
|
|
209
|
+
rb_scan_args(argc, argv, "01:", &observed_value, &keywords);
|
|
210
|
+
if (!NIL_P(keywords)) rb_get_kwargs(keywords, keyword_ids, 0, 1, keyword_values);
|
|
211
|
+
|
|
212
|
+
bool snapshot_at_entry = NIL_P(observed_value);
|
|
213
|
+
uint64_t observed = snapshot_at_entry ? 0 : NUM2ULL(observed_value);
|
|
214
|
+
VALUE timeout = keyword_values[0] == Qundef ? Qnil : keyword_values[0];
|
|
215
|
+
bool finite = !NIL_P(timeout);
|
|
216
|
+
double deadline = 0;
|
|
217
|
+
if (finite) {
|
|
218
|
+
double seconds = NUM2DBL(timeout);
|
|
219
|
+
if (!isfinite(seconds) || seconds < 0) {
|
|
220
|
+
rb_raise(rb_eArgError, "timeout must be a finite, non-negative number or nil");
|
|
221
|
+
}
|
|
222
|
+
deadline = farce_unshared_wait_now() + seconds;
|
|
223
|
+
}
|
|
224
|
+
containers_unshared_signal_t *signal = unshared_signal_get(self);
|
|
225
|
+
if (snapshot_at_entry) observed = signal->generation;
|
|
226
|
+
|
|
227
|
+
/* Coercion above may run Ruby. The generation check and registration below
|
|
228
|
+
* run together under the GVL, so a broadcast cannot slip between them. */
|
|
229
|
+
while (signal->generation == observed) {
|
|
230
|
+
if (finite && farce_unshared_wait_now() >= deadline) {
|
|
231
|
+
return rb_block_given_p() ? rb_yield_values(0) : Qnil;
|
|
232
|
+
}
|
|
233
|
+
if (signal->fiber_io && !NIL_P(rb_fiber_scheduler_current())) {
|
|
234
|
+
/* Reuse owned pipe objects after each scheduler wait unwinds. */
|
|
235
|
+
unshared_signal_fiber_wait_t wait = {signal, observed, deadline, finite};
|
|
236
|
+
signal->fiber_waiters++;
|
|
237
|
+
rb_ensure(unshared_signal_fiber_wait_body, (VALUE)&wait,
|
|
238
|
+
unshared_signal_fiber_wait_cleanup, (VALUE)&wait);
|
|
239
|
+
}
|
|
240
|
+
else farce_unshared_wait(&signal->waiters, self, finite, deadline);
|
|
241
|
+
}
|
|
242
|
+
return ULL2NUM(signal->generation);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
void
|
|
246
|
+
containers_init_unshared_signal(VALUE namespace)
|
|
247
|
+
{
|
|
248
|
+
rb_define_const(namespace, "UNSHARED_FIBER_IO", FARCE_UNSHARED_FIBER_IO ? Qtrue : Qfalse);
|
|
249
|
+
cUnsharedSignal = rb_define_class_under(namespace, "UnsharedSignal", rb_cObject);
|
|
250
|
+
rb_define_alloc_func(cUnsharedSignal, unshared_signal_allocate);
|
|
251
|
+
cUnsharedIOSignal = rb_define_class_under(namespace, "UnsharedIOSignal", cUnsharedSignal);
|
|
252
|
+
rb_define_alloc_func(cUnsharedIOSignal, unshared_io_signal_allocate);
|
|
253
|
+
cUnsharedBlockSignal = rb_define_class_under(namespace, "UnsharedBlockSignal", cUnsharedSignal);
|
|
254
|
+
rb_define_alloc_func(cUnsharedBlockSignal, unshared_block_signal_allocate);
|
|
255
|
+
rb_define_singleton_method(cUnsharedSignal, "for", unshared_signal_for, 1);
|
|
256
|
+
rb_define_method(cUnsharedSignal, "fiber_wait", unshared_signal_fiber_wait, 0);
|
|
257
|
+
rb_define_private_method(cUnsharedSignal, "initialize", unshared_signal_initialize, 0);
|
|
258
|
+
rb_define_private_method(cUnsharedSignal, "initialize_copy", unshared_signal_initialize_copy, 1);
|
|
259
|
+
rb_define_method(cUnsharedSignal, "generation", unshared_signal_generation, 0);
|
|
260
|
+
rb_define_method(cUnsharedSignal, "num_waiting", unshared_signal_num_waiting, 0);
|
|
261
|
+
rb_define_method(cUnsharedSignal, "broadcast", unshared_signal_broadcast, 0);
|
|
262
|
+
rb_define_method(cUnsharedSignal, "wait", unshared_signal_wait, -1);
|
|
263
|
+
}
|