farce 0.0.1.alpha2-x86-linux-gnu
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/CODE_OF_CONDUCT.md +26 -0
- data/CONTRIBUTING.md +71 -0
- data/MIT-LICENSE +20 -0
- data/README.md +1524 -0
- data/SECURITY.md +10 -0
- data/docs/benchmarks.md +185 -0
- data/docs/gems/dry-types.md +290 -0
- data/docs/gems/msgpack.md +70 -0
- data/docs/gems/ractor-shim.md +63 -0
- data/docs/modes.md +628 -0
- data/docs/scopes.md +649 -0
- data/docs/variants.md +346 -0
- data/ext/ext_helper.rb +20 -0
- data/ext/farce/README.md +21 -0
- data/ext/farce/atom.c +1023 -0
- data/ext/farce/bounded_map.c +1683 -0
- data/ext/farce/containers.h +77 -0
- data/ext/farce/counter.c +399 -0
- data/ext/farce/darwin.c +100 -0
- data/ext/farce/depend +12 -0
- data/ext/farce/dict.c +1523 -0
- data/ext/farce/dict.h +152 -0
- data/ext/farce/drivers.c +237 -0
- data/ext/farce/exchanger.c +299 -0
- data/ext/farce/extconf.rb +99 -0
- data/ext/farce/farce.c +359 -0
- data/ext/farce/flag.c +288 -0
- data/ext/farce/io.c +338 -0
- data/ext/farce/lock.c +510 -0
- data/ext/farce/map.c +2240 -0
- data/ext/farce/priority_queue.c +2056 -0
- data/ext/farce/queue.c +1059 -0
- data/ext/farce/reactor.c +820 -0
- data/ext/farce/reactor.h +103 -0
- data/ext/farce/shareable.h +31 -0
- data/ext/farce/signal.c +350 -0
- data/ext/farce/transaction.c +354 -0
- data/ext/farce/transaction.h +40 -0
- data/ext/farce/tree_map.c +1953 -0
- data/ext/farce/trie.c +2020 -0
- data/ext/farce/unshareable.c +155 -0
- data/ext/farce/unshared_io_pool.h +234 -0
- data/ext/farce/unshared_signal.c +263 -0
- data/ext/farce/unshared_wait.h +193 -0
- data/ext/farce/unsupported.c +6 -0
- data/ext/farce/vector.c +1195 -0
- data/ext/farce/weak_map.c +1714 -0
- data/ext/java/org/farce/BoundedMap.java +394 -0
- data/ext/java/org/farce/FiberScheduler.java +141 -0
- data/ext/java/org/farce/PriorityKey.java +45 -0
- data/ext/java/org/farce/PriorityQueue.java +373 -0
- data/ext/java/org/farce/QueueSignal.java +28 -0
- data/ext/rebind/README.md +14 -0
- data/ext/rebind/extconf.rb +10 -0
- data/ext/rebind/rebind.c +186 -0
- data/lib/farce/_yard/internal.rb +13 -0
- data/lib/farce/_yard/macros.rb +61 -0
- data/lib/farce/_yard/ractor.rb +46 -0
- data/lib/farce/abstract/atom.rb +186 -0
- data/lib/farce/abstract/bounded_map.rb +232 -0
- data/lib/farce/abstract/collection.rb +151 -0
- data/lib/farce/abstract/concurrent_map.rb +381 -0
- data/lib/farce/abstract/counter.rb +193 -0
- data/lib/farce/abstract/duplicable_map.rb +229 -0
- data/lib/farce/abstract/exchanger.rb +26 -0
- data/lib/farce/abstract/flag.rb +71 -0
- data/lib/farce/abstract/lazy.rb +115 -0
- data/lib/farce/abstract/lease.rb +104 -0
- data/lib/farce/abstract/lease_map.rb +261 -0
- data/lib/farce/abstract/lease_pool.rb +91 -0
- data/lib/farce/abstract/lfu_map.rb +20 -0
- data/lib/farce/abstract/lru_map.rb +25 -0
- data/lib/farce/abstract/map.rb +345 -0
- data/lib/farce/abstract/molecule.rb +245 -0
- data/lib/farce/abstract/port.rb +74 -0
- data/lib/farce/abstract/priority_queue.rb +117 -0
- data/lib/farce/abstract/queue.rb +269 -0
- data/lib/farce/abstract/scheduler.rb +111 -0
- data/lib/farce/abstract/set.rb +910 -0
- data/lib/farce/abstract/sorted_set.rb +172 -0
- data/lib/farce/abstract/timer_queue.rb +136 -0
- data/lib/farce/abstract/tree_map.rb +269 -0
- data/lib/farce/abstract/value.rb +68 -0
- data/lib/farce/abstract/vector.rb +1126 -0
- data/lib/farce/abstract/weak_atom.rb +32 -0
- data/lib/farce/abstract/weak_key_map.rb +12 -0
- data/lib/farce/abstract/weak_map.rb +13 -0
- data/lib/farce/abstract/weak_set.rb +12 -0
- data/lib/farce/abstract/weak_value_map.rb +12 -0
- data/lib/farce/abstract.rb +17 -0
- data/lib/farce/atom.rb +293 -0
- data/lib/farce/class_mirror.rb +87 -0
- data/lib/farce/clock.rb +121 -0
- data/lib/farce/config.rb +229 -0
- data/lib/farce/counter.rb +71 -0
- data/lib/farce/deduper.rb +122 -0
- data/lib/farce/engine/jruby/bounded_map.rb +314 -0
- data/lib/farce/engine/jruby/fiber_scheduler.jar +0 -0
- data/lib/farce/engine/jruby/fiber_scheduler.rb +119 -0
- data/lib/farce/engine/jruby/lease_waiting.rb +19 -0
- data/lib/farce/engine/jruby/map.rb +505 -0
- data/lib/farce/engine/jruby/mutable_numeric_copy.rb +42 -0
- data/lib/farce/engine/jruby/signal.rb +147 -0
- data/lib/farce/engine/jruby.rb +63 -0
- data/lib/farce/engine/jvm/concurrent_weak_registry.rb +55 -0
- data/lib/farce/engine/jvm/counter.rb +102 -0
- data/lib/farce/engine/jvm/extension.rb +32 -0
- data/lib/farce/engine/jvm/farce.jar +0 -0
- data/lib/farce/engine/jvm/flag.rb +79 -0
- data/lib/farce/engine/jvm/priority_queue.rb +217 -0
- data/lib/farce/engine/jvm/tree_map.rb +350 -0
- data/lib/farce/engine/jvm/types.rb +180 -0
- data/lib/farce/engine/jvm.rb +19 -0
- data/lib/farce/engine/ruby/3.4/farce.so +0 -0
- data/lib/farce/engine/ruby/3.4/fiber_scheduler.rb +20 -0
- data/lib/farce/engine/ruby/3.4/port.rb +186 -0
- data/lib/farce/engine/ruby/3.4/ractor_methods.rb +26 -0
- data/lib/farce/engine/ruby/3.4/ractor_selector.rb +92 -0
- data/lib/farce/engine/ruby/3.4/rebind.so +0 -0
- data/lib/farce/engine/ruby/3.4/vault.rb +56 -0
- data/lib/farce/engine/ruby/4.0/farce.so +0 -0
- data/lib/farce/engine/ruby/4.0/port.rb +19 -0
- data/lib/farce/engine/ruby/4.0/ractor_methods.rb +20 -0
- data/lib/farce/engine/ruby/4.0/ractor_selector.rb +108 -0
- data/lib/farce/engine/ruby/4.0/rebind.so +0 -0
- data/lib/farce/engine/ruby/4.0/vault.rb +74 -0
- data/lib/farce/engine/ruby/4.1/port.rb +21 -0
- data/lib/farce/engine/ruby/4.1/ractor_methods.rb +22 -0
- data/lib/farce/engine/ruby/4.1/ractor_selector.rb +25 -0
- data/lib/farce/engine/ruby/4.1/vault.rb +5 -0
- data/lib/farce/engine/ruby/fiber_scheduler.rb +32 -0
- data/lib/farce/engine/ruby/key_lock_map.rb +28 -0
- data/lib/farce/engine/ruby/shared/lease.rb +26 -0
- data/lib/farce/engine/ruby/shared/lease_pool.rb +24 -0
- data/lib/farce/engine/ruby/shared/main_scheduler.rb +9 -0
- data/lib/farce/engine/ruby/shared/parallel_scheduler.rb +9 -0
- data/lib/farce/engine/ruby/shared/proxy_owner.rb +31 -0
- data/lib/farce/engine/ruby/shared/ractor_methods.rb +34 -0
- data/lib/farce/engine/ruby/shared/ractor_selector.rb +328 -0
- data/lib/farce/engine/ruby/shared/strict_map.rb +13 -0
- data/lib/farce/engine/ruby/shared/unshared_vector.rb +14 -0
- data/lib/farce/engine/ruby/shared/vault.rb +144 -0
- data/lib/farce/engine/ruby/shared/vault_weak_map.rb +336 -0
- data/lib/farce/engine/ruby/shared/weak_atom.rb +54 -0
- data/lib/farce/engine/ruby/shared/weak_map.rb +413 -0
- data/lib/farce/engine/ruby.rb +130 -0
- data/lib/farce/engine/shared/atom.rb +10 -0
- data/lib/farce/engine/shared/exchanger.rb +130 -0
- data/lib/farce/engine/shared/identity_key.rb +22 -0
- data/lib/farce/engine/shared/lease.rb +10 -0
- data/lib/farce/engine/shared/lease_pool.rb +10 -0
- data/lib/farce/engine/shared/main_scheduler.rb +20 -0
- data/lib/farce/engine/shared/map_key_coordination.rb +226 -0
- data/lib/farce/engine/shared/parallel_scheduler.rb +9 -0
- data/lib/farce/engine/shared/port.rb +44 -0
- data/lib/farce/engine/shared/portable_bounded_map.rb +619 -0
- data/lib/farce/engine/shared/proxy_owner.rb +43 -0
- data/lib/farce/engine/shared/queue.rb +233 -0
- data/lib/farce/engine/shared/ractor_methods.rb +73 -0
- data/lib/farce/engine/shared/rebindable.rb +15 -0
- data/lib/farce/engine/shared/strict_atom.rb +73 -0
- data/lib/farce/engine/shared/strict_map.rb +117 -0
- data/lib/farce/engine/shared/strict_queue_values.rb +27 -0
- data/lib/farce/engine/shared/strict_tree_map.rb +56 -0
- data/lib/farce/engine/shared/transaction_map_backend.rb +20 -0
- data/lib/farce/engine/shared/trie.rb +317 -0
- data/lib/farce/engine/shared/trie_builder.rb +199 -0
- data/lib/farce/engine/shared/unshareable.rb +14 -0
- data/lib/farce/engine/shared/unshared_atom.rb +256 -0
- data/lib/farce/engine/shared/unshared_priority_queue.rb +9 -0
- data/lib/farce/engine/shared/unshared_queue.rb +18 -0
- data/lib/farce/engine/shared/unshared_signal.rb +21 -0
- data/lib/farce/engine/shared/unshared_vector.rb +377 -0
- data/lib/farce/engine/shared/unshared_weak_atom.rb +30 -0
- data/lib/farce/engine/shared/unshared_weak_map.rb +445 -0
- data/lib/farce/engine/shared/vault.rb +31 -0
- data/lib/farce/engine/shared/vector.rb +83 -0
- data/lib/farce/engine/shared/weak_atom/base.rb +189 -0
- data/lib/farce/engine/shared/weak_atom.rb +20 -0
- data/lib/farce/engine/shared/weak_map/cell.rb +99 -0
- data/lib/farce/engine/shared/weak_map/index.rb +249 -0
- data/lib/farce/engine/shared/weak_map/lock.rb +80 -0
- data/lib/farce/engine/shared/weak_map/reference.rb +33 -0
- data/lib/farce/engine/shared.rb +63 -0
- data/lib/farce/engine/truffleruby/fiber_scheduler.rb +13 -0
- data/lib/farce/engine/truffleruby/lock.rb +121 -0
- data/lib/farce/engine/truffleruby/map.rb +656 -0
- data/lib/farce/engine/truffleruby/native/counter.rb +105 -0
- data/lib/farce/engine/truffleruby/native/flag.rb +77 -0
- data/lib/farce/engine/truffleruby/native/ordered_array_support.rb +67 -0
- data/lib/farce/engine/truffleruby/native/priority_queue.rb +388 -0
- data/lib/farce/engine/truffleruby/native/tree_map.rb +51 -0
- data/lib/farce/engine/truffleruby/native/unsafe_tree_map.rb +350 -0
- data/lib/farce/engine/truffleruby/signal.rb +77 -0
- data/lib/farce/engine/truffleruby.rb +148 -0
- data/lib/farce/envelope.rb +335 -0
- data/lib/farce/error.rb +35 -0
- data/lib/farce/exchanger.rb +41 -0
- data/lib/farce/flag.rb +27 -0
- data/lib/farce/integrations/active_support/blank.rb +68 -0
- data/lib/farce/integrations/active_support/clock.rb +17 -0
- data/lib/farce/integrations/active_support/duplicable.rb +78 -0
- data/lib/farce/integrations/active_support/map.rb +175 -0
- data/lib/farce/integrations/active_support/set.rb +60 -0
- data/lib/farce/integrations/active_support/value_serialization.rb +17 -0
- data/lib/farce/integrations/active_support/vector.rb +231 -0
- data/lib/farce/integrations/active_support.rb +9 -0
- data/lib/farce/integrations/activesupport.rb +5 -0
- data/lib/farce/integrations/bson.rb +108 -0
- data/lib/farce/integrations/cbor.rb +51 -0
- data/lib/farce/integrations/concurrent.rb +112 -0
- data/lib/farce/integrations/dry-types.rb +5 -0
- data/lib/farce/integrations/dry_types.rb +694 -0
- data/lib/farce/integrations/json.rb +7 -0
- data/lib/farce/integrations/msgpack.rb +112 -0
- data/lib/farce/integrations/oj.rb +69 -0
- data/lib/farce/integrations/psych.rb +191 -0
- data/lib/farce/integrations/ractor-sharing.rb +5 -0
- data/lib/farce/integrations/ractor-tmvar.rb +5 -0
- data/lib/farce/integrations/ractor_sharing.rb +172 -0
- data/lib/farce/integrations/ractor_tmvar.rb +64 -0
- data/lib/farce/integrations/shared/to_json.rb +42 -0
- data/lib/farce/integrations/sorted_set.rb +11 -0
- data/lib/farce/integrations/weakref.rb +38 -0
- data/lib/farce/integrations/yajl.rb +10 -0
- data/lib/farce/integrations.rb +156 -0
- data/lib/farce/internal/_frozen_config.rb +11 -0
- data/lib/farce/internal/autoloads.rb +47 -0
- data/lib/farce/internal/blocking_priority_queue.rb +122 -0
- data/lib/farce/internal/converter.rb +106 -0
- data/lib/farce/internal/copyable.rb +31 -0
- data/lib/farce/internal/delegation.rb +20 -0
- data/lib/farce/internal/external_transaction.rb +59 -0
- data/lib/farce/internal/fake_ractor.rb +179 -0
- data/lib/farce/internal/freeze.rb +118 -0
- data/lib/farce/internal/inspect.rb +157 -0
- data/lib/farce/internal/key_lock_map.rb +29 -0
- data/lib/farce/internal/key_normalizer.rb +289 -0
- data/lib/farce/internal/lease_initialization.rb +115 -0
- data/lib/farce/internal/lease_map.rb +441 -0
- data/lib/farce/internal/lease_pool_state.rb +228 -0
- data/lib/farce/internal/lease_state.rb +342 -0
- data/lib/farce/internal/lease_waiting.rb +13 -0
- data/lib/farce/internal/managed_queue.rb +26 -0
- data/lib/farce/internal/map_value_modes.rb +227 -0
- data/lib/farce/internal/marshal_support.rb +227 -0
- data/lib/farce/internal/mixin.rb +20 -0
- data/lib/farce/internal/mutable_ordered_key_lock_map.rb +63 -0
- data/lib/farce/internal/noncopyable.rb +17 -0
- data/lib/farce/internal/ordered_key_lock_map.rb +69 -0
- data/lib/farce/internal/pool_supervisor.rb +41 -0
- data/lib/farce/internal/pool_worker.rb +61 -0
- data/lib/farce/internal/portable_transaction/reservation_entry.rb +29 -0
- data/lib/farce/internal/portable_transaction/strong_map_entry.rb +137 -0
- data/lib/farce/internal/portable_transaction/strong_map_size_entry.rb +44 -0
- data/lib/farce/internal/portable_transaction/tree_entry.rb +64 -0
- data/lib/farce/internal/portable_transaction.rb +164 -0
- data/lib/farce/internal/proxy_owner_notifications.rb +29 -0
- data/lib/farce/internal/reservation_waiting.rb +24 -0
- data/lib/farce/internal/scheduled_task.rb +48 -0
- data/lib/farce/internal/scheduler_io.rb +171 -0
- data/lib/farce/internal/scheduler_lifecycle.rb +314 -0
- data/lib/farce/internal/select_scheduler.rb +235 -0
- data/lib/farce/internal/storage.rb +162 -0
- data/lib/farce/internal/strict_lease.rb +30 -0
- data/lib/farce/internal/strict_lease_map.rb +20 -0
- data/lib/farce/internal/strict_lease_pool.rb +29 -0
- data/lib/farce/internal/thread_pool.rb +166 -0
- data/lib/farce/internal/transaction_conflict.rb +9 -0
- data/lib/farce/internal/transaction_freeze_guard.rb +30 -0
- data/lib/farce/internal/transaction_map_snapshot.rb +123 -0
- data/lib/farce/internal/undefined.rb +22 -0
- data/lib/farce/internal/unshared_lease.rb +29 -0
- data/lib/farce/internal/unshared_lease_pool.rb +34 -0
- data/lib/farce/internal/unshared_queue_waiting.rb +28 -0
- data/lib/farce/internal/value_serialization.rb +13 -0
- data/lib/farce/internal/weak_map_value_modes.rb +58 -0
- data/lib/farce/internal/weak_mode_manager.rb +26 -0
- data/lib/farce/internal.rb +129 -0
- data/lib/farce/lazy.rb +100 -0
- data/lib/farce/lazy_ref.rb +40 -0
- data/lib/farce/lease.rb +28 -0
- data/lib/farce/lease_map.rb +34 -0
- data/lib/farce/lease_pool.rb +29 -0
- data/lib/farce/lfu_map.rb +53 -0
- data/lib/farce/local/atom.rb +23 -0
- data/lib/farce/local/counter.rb +88 -0
- data/lib/farce/local/flag.rb +65 -0
- data/lib/farce/local/lazy.rb +38 -0
- data/lib/farce/local/lazy_ref.rb +36 -0
- data/lib/farce/local/lease.rb +77 -0
- data/lib/farce/local/lease_map.rb +80 -0
- data/lib/farce/local/lease_pool.rb +38 -0
- data/lib/farce/local/lfu_map.rb +55 -0
- data/lib/farce/local/lru_map.rb +62 -0
- data/lib/farce/local/map.rb +47 -0
- data/lib/farce/local/molecule.rb +45 -0
- data/lib/farce/local/priority_queue.rb +53 -0
- data/lib/farce/local/queue.rb +32 -0
- data/lib/farce/local/scoped.rb +166 -0
- data/lib/farce/local/set.rb +18 -0
- data/lib/farce/local/sorted_set.rb +50 -0
- data/lib/farce/local/timer_queue.rb +41 -0
- data/lib/farce/local/tree_map.rb +47 -0
- data/lib/farce/local/vector.rb +30 -0
- data/lib/farce/local/weak_atom.rb +23 -0
- data/lib/farce/local/weak_key_map.rb +39 -0
- data/lib/farce/local/weak_map.rb +39 -0
- data/lib/farce/local/weak_set.rb +18 -0
- data/lib/farce/local/weak_value_map.rb +39 -0
- data/lib/farce/local.rb +35 -0
- data/lib/farce/lock.rb +54 -0
- data/lib/farce/lru_map.rb +63 -0
- data/lib/farce/map.rb +58 -0
- data/lib/farce/mode_manager.rb +142 -0
- data/lib/farce/molecule.rb +60 -0
- data/lib/farce/mutable.rb +171 -0
- data/lib/farce/pool.rb +332 -0
- data/lib/farce/port.rb +134 -0
- data/lib/farce/priority_queue.rb +70 -0
- data/lib/farce/proxy/register.rb +101 -0
- data/lib/farce/proxy/supervisor.rb +137 -0
- data/lib/farce/proxy/wrapper.rb +49 -0
- data/lib/farce/proxy.rb +291 -0
- data/lib/farce/queue.rb +60 -0
- data/lib/farce/ractor.rb +269 -0
- data/lib/farce/read_write_lock.rb +256 -0
- data/lib/farce/reference.rb +158 -0
- data/lib/farce/resolv/dns.rb +42 -0
- data/lib/farce/resolv.rb +85 -0
- data/lib/farce/scheduler.rb +536 -0
- data/lib/farce/set.rb +39 -0
- data/lib/farce/shareable.rb +150 -0
- data/lib/farce/signal.rb +97 -0
- data/lib/farce/sorted_set.rb +36 -0
- data/lib/farce/strict/atom.rb +34 -0
- data/lib/farce/strict/counter.rb +10 -0
- data/lib/farce/strict/exchanger.rb +25 -0
- data/lib/farce/strict/flag.rb +10 -0
- data/lib/farce/strict/lazy.rb +26 -0
- data/lib/farce/strict/lazy_ref.rb +26 -0
- data/lib/farce/strict/lease.rb +27 -0
- data/lib/farce/strict/lease_map.rb +33 -0
- data/lib/farce/strict/lease_pool.rb +28 -0
- data/lib/farce/strict/lfu_map.rb +27 -0
- data/lib/farce/strict/lru_map.rb +28 -0
- data/lib/farce/strict/map.rb +54 -0
- data/lib/farce/strict/molecule.rb +16 -0
- data/lib/farce/strict/port.rb +40 -0
- data/lib/farce/strict/priority_queue.rb +20 -0
- data/lib/farce/strict/queue.rb +17 -0
- data/lib/farce/strict/set.rb +14 -0
- data/lib/farce/strict/sorted_set.rb +19 -0
- data/lib/farce/strict/timer_queue.rb +13 -0
- data/lib/farce/strict/tree_map.rb +27 -0
- data/lib/farce/strict/vector.rb +24 -0
- data/lib/farce/strict/weak_atom.rb +31 -0
- data/lib/farce/strict/weak_key_map.rb +49 -0
- data/lib/farce/strict/weak_map.rb +49 -0
- data/lib/farce/strict/weak_set.rb +14 -0
- data/lib/farce/strict/weak_value_map.rb +48 -0
- data/lib/farce/strict.rb +28 -0
- data/lib/farce/system.rb +40 -0
- data/lib/farce/thread_scheduler.rb +70 -0
- data/lib/farce/timer_queue.rb +66 -0
- data/lib/farce/transaction/atom.rb +73 -0
- data/lib/farce/transaction/map.rb +25 -0
- data/lib/farce/transaction/map_operations.rb +143 -0
- data/lib/farce/transaction/molecule.rb +90 -0
- data/lib/farce/transaction/mutable.rb +61 -0
- data/lib/farce/transaction/set.rb +17 -0
- data/lib/farce/transaction/set_operations.rb +48 -0
- data/lib/farce/transaction/sorted_set.rb +18 -0
- data/lib/farce/transaction/tree_map.rb +78 -0
- data/lib/farce/transaction/vector.rb +139 -0
- data/lib/farce/transaction/wrapper.rb +202 -0
- data/lib/farce/transaction.rb +343 -0
- data/lib/farce/tree_map.rb +56 -0
- data/lib/farce/unsafe/lfu_map.rb +36 -0
- data/lib/farce/unsafe/lru_map.rb +36 -0
- data/lib/farce/unsafe/tree_map.rb +21 -0
- data/lib/farce/unsafe.rb +29 -0
- data/lib/farce/unshareable.rb +67 -0
- data/lib/farce/unshared/atom.rb +24 -0
- data/lib/farce/unshared/counter.rb +10 -0
- data/lib/farce/unshared/flag.rb +10 -0
- data/lib/farce/unshared/lazy.rb +38 -0
- data/lib/farce/unshared/lazy_ref.rb +26 -0
- data/lib/farce/unshared/lease.rb +27 -0
- data/lib/farce/unshared/lease_map.rb +26 -0
- data/lib/farce/unshared/lease_pool.rb +25 -0
- data/lib/farce/unshared/lfu_map.rb +20 -0
- data/lib/farce/unshared/lru_map.rb +20 -0
- data/lib/farce/unshared/map.rb +49 -0
- data/lib/farce/unshared/molecule.rb +16 -0
- data/lib/farce/unshared/priority_queue.rb +16 -0
- data/lib/farce/unshared/queue.rb +27 -0
- data/lib/farce/unshared/set.rb +14 -0
- data/lib/farce/unshared/sorted_set.rb +28 -0
- data/lib/farce/unshared/timer_queue.rb +16 -0
- data/lib/farce/unshared/tree_map.rb +21 -0
- data/lib/farce/unshared/vector.rb +19 -0
- data/lib/farce/unshared/weak_atom.rb +31 -0
- data/lib/farce/unshared/weak_key_map.rb +44 -0
- data/lib/farce/unshared/weak_map.rb +44 -0
- data/lib/farce/unshared/weak_set.rb +14 -0
- data/lib/farce/unshared/weak_value_map.rb +43 -0
- data/lib/farce/unshared.rb +28 -0
- data/lib/farce/vector.rb +228 -0
- data/lib/farce/version.rb +8 -0
- data/lib/farce/walker/definitions.rb +186 -0
- data/lib/farce/walker/modification.rb +355 -0
- data/lib/farce/walker.rb +319 -0
- data/lib/farce/weak_atom.rb +121 -0
- data/lib/farce/weak_key_map.rb +31 -0
- data/lib/farce/weak_map.rb +33 -0
- data/lib/farce/weak_ref.rb +67 -0
- data/lib/farce/weak_set.rb +82 -0
- data/lib/farce/weak_value.rb +195 -0
- data/lib/farce/weak_value_map.rb +32 -0
- data/lib/farce.rb +519 -0
- metadata +468 -0
|
@@ -0,0 +1,373 @@
|
|
|
1
|
+
package org.farce;
|
|
2
|
+
|
|
3
|
+
import java.util.Comparator;
|
|
4
|
+
import java.util.HashMap;
|
|
5
|
+
import java.util.Iterator;
|
|
6
|
+
import java.util.Map;
|
|
7
|
+
import java.util.TreeMap;
|
|
8
|
+
import java.util.concurrent.locks.ReentrantLock;
|
|
9
|
+
import java.util.function.BiPredicate;
|
|
10
|
+
import java.util.function.BooleanSupplier;
|
|
11
|
+
import java.util.function.Consumer;
|
|
12
|
+
import java.util.function.Function;
|
|
13
|
+
|
|
14
|
+
/** Ordered FIFO buckets with opaque Ruby values and transactional callbacks. */
|
|
15
|
+
public final class PriorityQueue {
|
|
16
|
+
public static final class Failure extends RuntimeException {
|
|
17
|
+
private final int code;
|
|
18
|
+
Failure(int code, String message) { super(message); this.code = code; }
|
|
19
|
+
public int getCode() { return code; }
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
private static final class Bucket {
|
|
23
|
+
Entry head;
|
|
24
|
+
Entry tail;
|
|
25
|
+
int size;
|
|
26
|
+
HashMap<Object, Entry> identities;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
private static class Entry {
|
|
30
|
+
final Object value;
|
|
31
|
+
Entry previous;
|
|
32
|
+
Entry next;
|
|
33
|
+
Object token;
|
|
34
|
+
Entry identityPrevious;
|
|
35
|
+
Entry identityNext;
|
|
36
|
+
boolean committed;
|
|
37
|
+
Entry(Object value) { this.value = value; }
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
private static final class TrackedEntry extends Entry {
|
|
41
|
+
final long enqueuedAt;
|
|
42
|
+
TrackedEntry agePrevious;
|
|
43
|
+
TrackedEntry ageNext;
|
|
44
|
+
TrackedEntry(Object value) { super(value); enqueuedAt = System.nanoTime(); }
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
private final ReentrantLock lock = new ReentrantLock();
|
|
48
|
+
private final TreeMap<PriorityKey, Bucket> tree;
|
|
49
|
+
private final Function<PriorityKey, PriorityKey> snapshot;
|
|
50
|
+
private final Function<Object, Object> identity;
|
|
51
|
+
private final BiPredicate<Object, Object> equal;
|
|
52
|
+
private final BiPredicate<Object, Object> identical;
|
|
53
|
+
private final BiPredicate<Object, Object> match;
|
|
54
|
+
private final Consumer<Runnable> committer;
|
|
55
|
+
private final QueueSignal signal;
|
|
56
|
+
private final BooleanSupplier scheduled;
|
|
57
|
+
private final long capacity;
|
|
58
|
+
private final boolean trackAge;
|
|
59
|
+
private long size;
|
|
60
|
+
private long generation;
|
|
61
|
+
private TrackedEntry ageHead;
|
|
62
|
+
private TrackedEntry ageTail;
|
|
63
|
+
private boolean sealed;
|
|
64
|
+
private boolean closed;
|
|
65
|
+
|
|
66
|
+
public PriorityQueue(Comparator<PriorityKey> comparator,
|
|
67
|
+
Function<PriorityKey, PriorityKey> snapshot,
|
|
68
|
+
Function<Object, Object> identity,
|
|
69
|
+
BiPredicate<Object, Object> equal,
|
|
70
|
+
BiPredicate<Object, Object> identical,
|
|
71
|
+
BiPredicate<Object, Object> match,
|
|
72
|
+
Consumer<Runnable> committer,
|
|
73
|
+
BooleanSupplier scheduled, long capacity, QueueSignal signal,
|
|
74
|
+
boolean trackAge) {
|
|
75
|
+
this.tree = new TreeMap<>(comparator);
|
|
76
|
+
this.snapshot = snapshot;
|
|
77
|
+
this.identity = identity;
|
|
78
|
+
this.equal = equal;
|
|
79
|
+
this.identical = identical;
|
|
80
|
+
this.match = match;
|
|
81
|
+
this.committer = committer;
|
|
82
|
+
this.signal = signal;
|
|
83
|
+
this.scheduled = scheduled;
|
|
84
|
+
this.capacity = capacity;
|
|
85
|
+
this.trackAge = trackAge;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
private void enter() {
|
|
89
|
+
if (lock.isHeldByCurrentThread())
|
|
90
|
+
throw new Failure(1, "recursive priority queue access");
|
|
91
|
+
if (!lock.tryLock()) {
|
|
92
|
+
if (scheduled.getAsBoolean())
|
|
93
|
+
throw new Failure(3, "shared JVM container contention cannot block a scheduled Fiber");
|
|
94
|
+
lock.lock();
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
private void checkPushOpen() {
|
|
99
|
+
if (closed) throw new Failure(2, "queue is closed");
|
|
100
|
+
if (sealed) throw new Failure(4, "queue is sealed");
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
private void checkReadOpen() {
|
|
104
|
+
if (closed) throw new Failure(2, "queue is closed");
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
private void commit(Runnable operation) {
|
|
108
|
+
if (signal != null && signal.commitWithoutFiberWaiters(operation)) return;
|
|
109
|
+
if (committer == null) operation.run();
|
|
110
|
+
else committer.accept(operation);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Construct keys inside Java to avoid allocating a Ruby Java-proxy per key.
|
|
114
|
+
public boolean pushFloat(double priority, Object rubyPriority, Object value) {
|
|
115
|
+
return push(new PriorityKey(rubyPriority, true, priority), value);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
public Object readBefore(double cutoff, Object rubyCutoff) {
|
|
119
|
+
return read(0, false, new PriorityKey(rubyCutoff, true, cutoff));
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
public boolean push(PriorityKey query, Object value) {
|
|
123
|
+
enter();
|
|
124
|
+
Bucket candidate = null;
|
|
125
|
+
Bucket bucket = null;
|
|
126
|
+
Entry entry = null;
|
|
127
|
+
try {
|
|
128
|
+
checkPushOpen();
|
|
129
|
+
if (capacity > 0 && size >= capacity) return false;
|
|
130
|
+
bucket = tree.get(query);
|
|
131
|
+
if (bucket == null) {
|
|
132
|
+
PriorityKey key = query.requiresSnapshot() ? snapshot.apply(query) : query;
|
|
133
|
+
candidate = new Bucket();
|
|
134
|
+
Bucket existing = tree.putIfAbsent(key, candidate);
|
|
135
|
+
bucket = existing == null ? candidate : existing;
|
|
136
|
+
}
|
|
137
|
+
entry = trackAge ? new TrackedEntry(value) : new Entry(value);
|
|
138
|
+
if (bucket.identities != null) addIdentity(bucket.identities, entry);
|
|
139
|
+
Bucket target = bucket;
|
|
140
|
+
Entry added = entry;
|
|
141
|
+
commit(() -> {
|
|
142
|
+
added.previous = target.tail;
|
|
143
|
+
if (target.tail == null) target.head = added;
|
|
144
|
+
else target.tail.next = added;
|
|
145
|
+
target.tail = added;
|
|
146
|
+
target.size++;
|
|
147
|
+
size++;
|
|
148
|
+
trackPush(added);
|
|
149
|
+
added.committed = true;
|
|
150
|
+
});
|
|
151
|
+
return true;
|
|
152
|
+
} finally {
|
|
153
|
+
if (entry == null || !entry.committed) {
|
|
154
|
+
if (entry != null && entry.identityNext != null) discardIdentity(bucket.identities, entry);
|
|
155
|
+
if (candidate != null && candidate.size == 0) removeEmpty(candidate);
|
|
156
|
+
}
|
|
157
|
+
lock.unlock();
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
private void removeEmpty(Bucket bucket) {
|
|
162
|
+
Iterator<Bucket> iterator = tree.values().iterator();
|
|
163
|
+
while (iterator.hasNext()) {
|
|
164
|
+
if (iterator.next() == bucket) { iterator.remove(); return; }
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
private Iterator<Map.Entry<PriorityKey, Bucket>> iterator(boolean last) {
|
|
169
|
+
return (last ? tree.descendingMap() : tree).entrySet().iterator();
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// kind: 0 = pop, 1 = peek, 2 = priority. Null cutoff means unbounded.
|
|
173
|
+
public Object read(int kind, boolean last, PriorityKey cutoff) {
|
|
174
|
+
enter();
|
|
175
|
+
try {
|
|
176
|
+
checkReadOpen();
|
|
177
|
+
Iterator<Map.Entry<PriorityKey, Bucket>> iterator = iterator(last);
|
|
178
|
+
if (!iterator.hasNext()) return null;
|
|
179
|
+
Map.Entry<PriorityKey, Bucket> item = iterator.next();
|
|
180
|
+
if (kind == 2) return item.getKey().getRubyKey();
|
|
181
|
+
if (cutoff != null && tree.comparator().compare(item.getKey(), cutoff) > 0) return null;
|
|
182
|
+
Bucket bucket = item.getValue();
|
|
183
|
+
Entry entry = bucket.head;
|
|
184
|
+
if (kind == 1) return entry.value;
|
|
185
|
+
commit(() -> {
|
|
186
|
+
unlink(bucket, entry);
|
|
187
|
+
if (bucket.size == 0) iterator.remove();
|
|
188
|
+
});
|
|
189
|
+
return entry.value;
|
|
190
|
+
} finally { lock.unlock(); }
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// kind: 0 = stored == requested, 1 = identity, 2 = requested === stored.
|
|
194
|
+
public boolean removeValue(PriorityKey key, Object value, int kind) {
|
|
195
|
+
enter();
|
|
196
|
+
try {
|
|
197
|
+
checkReadOpen();
|
|
198
|
+
Bucket bucket = tree.get(key);
|
|
199
|
+
if (bucket == null) return false;
|
|
200
|
+
Entry found = null;
|
|
201
|
+
if (kind == 1 && bucket.size >= 32 && bucket.identities == null) buildIdentities(bucket);
|
|
202
|
+
if (kind == 1 && bucket.identities != null) {
|
|
203
|
+
Entry head = bucket.identities.get(identity.apply(value));
|
|
204
|
+
if (head != null && identical.test(head.value, value)) found = head;
|
|
205
|
+
} else {
|
|
206
|
+
BiPredicate<Object, Object> predicate = kind == 1 ? identical : kind == 2 ? match : equal;
|
|
207
|
+
for (Entry entry = bucket.head; entry != null; entry = entry.next) {
|
|
208
|
+
if (predicate.test(entry.value, value)) { found = entry; break; }
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
if (found == null) return false;
|
|
212
|
+
Iterator<Map.Entry<PriorityKey, Bucket>> removal = null;
|
|
213
|
+
if (bucket.size == 1) {
|
|
214
|
+
removal = tree.tailMap(key, true).entrySet().iterator();
|
|
215
|
+
if (!removal.hasNext() || removal.next().getValue() != bucket)
|
|
216
|
+
throw new IllegalStateException("priority map lost its bucket");
|
|
217
|
+
}
|
|
218
|
+
Entry selected = found;
|
|
219
|
+
Iterator<Map.Entry<PriorityKey, Bucket>> positioned = removal;
|
|
220
|
+
commit(() -> {
|
|
221
|
+
unlink(bucket, selected);
|
|
222
|
+
if (positioned != null) positioned.remove();
|
|
223
|
+
});
|
|
224
|
+
return true;
|
|
225
|
+
} finally { lock.unlock(); }
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
private void unlink(Bucket bucket, Entry entry) {
|
|
229
|
+
if (entry.previous == null) bucket.head = entry.next;
|
|
230
|
+
else entry.previous.next = entry.next;
|
|
231
|
+
if (entry.next == null) bucket.tail = entry.previous;
|
|
232
|
+
else entry.next.previous = entry.previous;
|
|
233
|
+
entry.previous = entry.next = null;
|
|
234
|
+
if (bucket.identities != null) discardIdentity(bucket.identities, entry);
|
|
235
|
+
bucket.size--;
|
|
236
|
+
size--;
|
|
237
|
+
trackRemove(entry);
|
|
238
|
+
if (sealed && size == 0) closed = true;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
private void trackPush(Entry entry) {
|
|
242
|
+
if (!trackAge) return;
|
|
243
|
+
TrackedEntry tracked = (TrackedEntry)entry;
|
|
244
|
+
tracked.agePrevious = ageTail;
|
|
245
|
+
if (ageTail == null) ageHead = tracked;
|
|
246
|
+
else ageTail.ageNext = tracked;
|
|
247
|
+
ageTail = tracked;
|
|
248
|
+
generation++;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
private void trackRemove(Entry entry) {
|
|
252
|
+
if (!trackAge) return;
|
|
253
|
+
TrackedEntry tracked = (TrackedEntry)entry;
|
|
254
|
+
if (tracked.agePrevious == null) ageHead = tracked.ageNext;
|
|
255
|
+
else tracked.agePrevious.ageNext = tracked.ageNext;
|
|
256
|
+
if (tracked.ageNext == null) ageTail = tracked.agePrevious;
|
|
257
|
+
else tracked.ageNext.agePrevious = tracked.agePrevious;
|
|
258
|
+
generation++;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
// Circular identity FIFOs need no separate slot allocation and retain no dead entries.
|
|
262
|
+
private void addIdentity(HashMap<Object, Entry> index, Entry entry) {
|
|
263
|
+
Object token = identity.apply(entry.value);
|
|
264
|
+
Entry head = index.get(token);
|
|
265
|
+
entry.token = token;
|
|
266
|
+
if (head == null) {
|
|
267
|
+
index.put(token, entry);
|
|
268
|
+
entry.identityNext = entry.identityPrevious = entry;
|
|
269
|
+
} else {
|
|
270
|
+
Entry tail = head.identityPrevious;
|
|
271
|
+
entry.identityNext = head;
|
|
272
|
+
entry.identityPrevious = tail;
|
|
273
|
+
tail.identityNext = head.identityPrevious = entry;
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
private void discardIdentity(HashMap<Object, Entry> index, Entry entry) {
|
|
278
|
+
if (entry.identityNext == entry) index.remove(entry.token);
|
|
279
|
+
else {
|
|
280
|
+
entry.identityPrevious.identityNext = entry.identityNext;
|
|
281
|
+
entry.identityNext.identityPrevious = entry.identityPrevious;
|
|
282
|
+
if (index.get(entry.token) == entry) index.put(entry.token, entry.identityNext);
|
|
283
|
+
}
|
|
284
|
+
entry.token = null;
|
|
285
|
+
entry.identityNext = entry.identityPrevious = null;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
private void buildIdentities(Bucket bucket) {
|
|
289
|
+
HashMap<Object, Entry> index = new HashMap<>();
|
|
290
|
+
boolean completed = false;
|
|
291
|
+
try {
|
|
292
|
+
for (Entry entry = bucket.head; entry != null; entry = entry.next) addIdentity(index, entry);
|
|
293
|
+
bucket.identities = index;
|
|
294
|
+
completed = true;
|
|
295
|
+
} finally {
|
|
296
|
+
if (!completed) {
|
|
297
|
+
for (Entry entry = bucket.head; entry != null; entry = entry.next) {
|
|
298
|
+
entry.token = null;
|
|
299
|
+
entry.identityNext = entry.identityPrevious = null;
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
public long size() {
|
|
306
|
+
enter();
|
|
307
|
+
try { return size; } finally { lock.unlock(); }
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
public boolean isClosed() {
|
|
311
|
+
enter();
|
|
312
|
+
try { return closed; } finally { lock.unlock(); }
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
public boolean isSealed() {
|
|
316
|
+
enter();
|
|
317
|
+
try { return sealed; } finally { lock.unlock(); }
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
public boolean isAgeTracking() { return trackAge; }
|
|
321
|
+
|
|
322
|
+
public Long generation() {
|
|
323
|
+
if (!trackAge) return null;
|
|
324
|
+
enter();
|
|
325
|
+
try { return generation; } finally { lock.unlock(); }
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
public Double oldestEnqueuedAt() {
|
|
329
|
+
if (!trackAge) return null;
|
|
330
|
+
enter();
|
|
331
|
+
try { return ageHead == null ? null : ageHead.enqueuedAt / 1_000_000_000.0; }
|
|
332
|
+
finally { lock.unlock(); }
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
public Double oldestAge() {
|
|
336
|
+
if (!trackAge) return null;
|
|
337
|
+
enter();
|
|
338
|
+
try { return ageHead == null ? null : (System.nanoTime() - ageHead.enqueuedAt) / 1_000_000_000.0; }
|
|
339
|
+
finally { lock.unlock(); }
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
public void clear() {
|
|
343
|
+
enter();
|
|
344
|
+
try { commit(() -> {
|
|
345
|
+
tree.clear();
|
|
346
|
+
if (trackAge && size > 0) generation++;
|
|
347
|
+
size = 0;
|
|
348
|
+
ageHead = ageTail = null;
|
|
349
|
+
if (sealed) closed = true;
|
|
350
|
+
}); }
|
|
351
|
+
finally { lock.unlock(); }
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
public void seal() {
|
|
355
|
+
enter();
|
|
356
|
+
try { commit(() -> {
|
|
357
|
+
if (!sealed && trackAge) generation++;
|
|
358
|
+
sealed = true;
|
|
359
|
+
if (size == 0) closed = true;
|
|
360
|
+
}); }
|
|
361
|
+
finally { lock.unlock(); }
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
public void close() {
|
|
365
|
+
enter();
|
|
366
|
+
try { commit(() -> {
|
|
367
|
+
if (!closed && trackAge) generation++;
|
|
368
|
+
sealed = true;
|
|
369
|
+
closed = true;
|
|
370
|
+
}); }
|
|
371
|
+
finally { lock.unlock(); }
|
|
372
|
+
}
|
|
373
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
package org.farce;
|
|
2
|
+
|
|
3
|
+
import java.util.concurrent.Phaser;
|
|
4
|
+
|
|
5
|
+
/** Thread-only notification and queue commit can execute without Ruby callbacks. */
|
|
6
|
+
public class QueueSignal extends Phaser {
|
|
7
|
+
private int schedulerWaiters;
|
|
8
|
+
|
|
9
|
+
public QueueSignal() { super(1); }
|
|
10
|
+
|
|
11
|
+
public synchronized int broadcastThreads() {
|
|
12
|
+
int previous = arrive();
|
|
13
|
+
return previous == Integer.MAX_VALUE ? 0 : previous + 1;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// Registration and commits use the SAME monitor. A new waiter can never
|
|
17
|
+
// register between the zero-waiter check and the phase advance/commit.
|
|
18
|
+
public synchronized void beginSchedulerWait() { schedulerWaiters++; }
|
|
19
|
+
|
|
20
|
+
public synchronized void endSchedulerWait() { schedulerWaiters--; }
|
|
21
|
+
|
|
22
|
+
public synchronized boolean commitWithoutFiberWaiters(Runnable operation) {
|
|
23
|
+
if (schedulerWaiters != 0) return false;
|
|
24
|
+
broadcastThreads();
|
|
25
|
+
operation.run();
|
|
26
|
+
return true;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# Block rebinding
|
|
2
|
+
|
|
3
|
+
A small extension for block rebinding on CRuby.
|
|
4
|
+
|
|
5
|
+
Rebinding rather than wrapping procs is necessary for some use cases, such as retaining Ractor-shareability.
|
|
6
|
+
It also produces significantly faster and smaller procs than wrapping or casting to an unbound method and rebinding.
|
|
7
|
+
|
|
8
|
+
It does however rely on MRI internals, as there are no public APIs for this, so it needs to be verified for each new Ruby version. This is in part why it is kept as a separate extension.
|
|
9
|
+
|
|
10
|
+
On Ruby implementations other than MRI it generates a dummy Makefile, so it still "compiles".
|
|
11
|
+
|
|
12
|
+
## AI Usage
|
|
13
|
+
|
|
14
|
+
Anthropic's Claude AI has been used to explore the underlying data structures, identify edge cases, and helped write the patch. All generated code has been reviewed extensively if it ended up being used in the final implementation.
|
data/ext/rebind/rebind.c
ADDED
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
#include <ruby.h>
|
|
2
|
+
#include <ruby/version.h>
|
|
3
|
+
|
|
4
|
+
#include <stddef.h>
|
|
5
|
+
|
|
6
|
+
/*
|
|
7
|
+
* Private CRuby layout mirrored from vm_core.h.
|
|
8
|
+
*
|
|
9
|
+
* Ruby 4.1 split Proc's storage into separate representations sharing a
|
|
10
|
+
* header. Older supported CRuby versions store the captured block first.
|
|
11
|
+
* We deliberately mirror only the prefix we need.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
union farce_block_code
|
|
15
|
+
{
|
|
16
|
+
const void *iseq;
|
|
17
|
+
const void *ifunc;
|
|
18
|
+
VALUE val;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
struct farce_captured_block
|
|
22
|
+
{
|
|
23
|
+
VALUE self;
|
|
24
|
+
const VALUE *ep;
|
|
25
|
+
union farce_block_code code;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
enum farce_block_type
|
|
29
|
+
{
|
|
30
|
+
FARCE_BLOCK_ISEQ = 0,
|
|
31
|
+
FARCE_BLOCK_IFUNC = 1,
|
|
32
|
+
FARCE_BLOCK_SYMBOL = 2,
|
|
33
|
+
FARCE_BLOCK_PROC = 3
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
struct farce_block
|
|
37
|
+
{
|
|
38
|
+
union
|
|
39
|
+
{
|
|
40
|
+
struct farce_captured_block captured;
|
|
41
|
+
VALUE symbol;
|
|
42
|
+
VALUE proc;
|
|
43
|
+
} as;
|
|
44
|
+
|
|
45
|
+
enum farce_block_type type;
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
#if RUBY_API_VERSION_MAJOR == 4 && RUBY_API_VERSION_MINOR >= 1
|
|
49
|
+
struct farce_proc_header
|
|
50
|
+
{
|
|
51
|
+
enum farce_block_type type : 8;
|
|
52
|
+
unsigned int is_from_method : 1;
|
|
53
|
+
unsigned int is_lambda : 1;
|
|
54
|
+
unsigned int is_isolated : 1;
|
|
55
|
+
unsigned int is_refined : 1;
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
struct farce_proc_captured
|
|
59
|
+
{
|
|
60
|
+
struct farce_proc_header header;
|
|
61
|
+
struct farce_captured_block captured;
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
union farce_proc
|
|
65
|
+
{
|
|
66
|
+
struct farce_block block;
|
|
67
|
+
struct farce_proc_header header;
|
|
68
|
+
struct farce_proc_captured captured;
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
typedef union farce_proc farce_proc_t;
|
|
72
|
+
|
|
73
|
+
#define FARCE_PROC_TYPE(proc) ((proc)->header.type)
|
|
74
|
+
#define FARCE_PROC_SELF(proc) (&(proc)->captured.captured.self)
|
|
75
|
+
#define FARCE_PROC_LAMBDA(proc) ((proc)->header.is_lambda)
|
|
76
|
+
|
|
77
|
+
#else
|
|
78
|
+
struct farce_proc
|
|
79
|
+
{
|
|
80
|
+
struct farce_block block;
|
|
81
|
+
|
|
82
|
+
unsigned int is_from_method : 1;
|
|
83
|
+
unsigned int is_lambda : 1;
|
|
84
|
+
unsigned int is_isolated : 1;
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
typedef struct farce_proc farce_proc_t;
|
|
88
|
+
|
|
89
|
+
#define FARCE_PROC_TYPE(proc) ((proc)->block.type)
|
|
90
|
+
#define FARCE_PROC_SELF(proc) (&(proc)->block.as.captured.self)
|
|
91
|
+
#define FARCE_PROC_LAMBDA(proc) ((proc)->is_lambda)
|
|
92
|
+
#endif
|
|
93
|
+
|
|
94
|
+
#define FARCE_STATIC_ASSERT(name, expr) \
|
|
95
|
+
typedef char farce_static_assert_##name[(expr) ? 1 : -1]
|
|
96
|
+
|
|
97
|
+
FARCE_STATIC_ASSERT(
|
|
98
|
+
captured_block_is_three_words,
|
|
99
|
+
sizeof(struct farce_captured_block) == 3 * sizeof(VALUE));
|
|
100
|
+
|
|
101
|
+
FARCE_STATIC_ASSERT(
|
|
102
|
+
captured_self_is_first,
|
|
103
|
+
offsetof(struct farce_captured_block, self) == 0);
|
|
104
|
+
|
|
105
|
+
FARCE_STATIC_ASSERT(
|
|
106
|
+
block_is_four_words,
|
|
107
|
+
sizeof(struct farce_block) == 4 * sizeof(VALUE));
|
|
108
|
+
|
|
109
|
+
#if RUBY_API_VERSION_MAJOR == 4 && RUBY_API_VERSION_MINOR >= 1
|
|
110
|
+
FARCE_STATIC_ASSERT(
|
|
111
|
+
captured_self_offset,
|
|
112
|
+
offsetof(union farce_proc, captured.captured.self) == sizeof(VALUE));
|
|
113
|
+
#else
|
|
114
|
+
FARCE_STATIC_ASSERT(
|
|
115
|
+
proc_starts_with_block,
|
|
116
|
+
offsetof(struct farce_proc, block) == 0);
|
|
117
|
+
#endif
|
|
118
|
+
|
|
119
|
+
#if !((RUBY_API_VERSION_MAJOR == 3 && RUBY_API_VERSION_MINOR == 4) || \
|
|
120
|
+
(RUBY_API_VERSION_MAJOR == 4 && RUBY_API_VERSION_MINOR <= 1))
|
|
121
|
+
#error "Farce's Proc layout needs verification for this CRuby version"
|
|
122
|
+
#endif
|
|
123
|
+
|
|
124
|
+
static farce_proc_t *
|
|
125
|
+
farce_proc_ptr(VALUE proc)
|
|
126
|
+
{
|
|
127
|
+
return (farce_proc_t *)RTYPEDDATA_GET_DATA(proc);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
static int
|
|
131
|
+
farce_proc_rebindable_p(farce_proc_t *proc)
|
|
132
|
+
{
|
|
133
|
+
return (FARCE_PROC_TYPE(proc) == FARCE_BLOCK_ISEQ || FARCE_PROC_TYPE(proc) == FARCE_BLOCK_IFUNC);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
static VALUE
|
|
137
|
+
proc_rebindable_p(VALUE module, VALUE proc_val)
|
|
138
|
+
{
|
|
139
|
+
farce_proc_t *proc;
|
|
140
|
+
|
|
141
|
+
(void)module;
|
|
142
|
+
|
|
143
|
+
if (!rb_obj_is_proc(proc_val)) return Qfalse;
|
|
144
|
+
proc = farce_proc_ptr(proc_val);
|
|
145
|
+
return farce_proc_rebindable_p(proc) ? Qtrue : Qfalse;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
static VALUE
|
|
149
|
+
rebind_proc_self(VALUE module, VALUE proc_val, VALUE new_self, VALUE lambda_mode)
|
|
150
|
+
{
|
|
151
|
+
VALUE new_proc;
|
|
152
|
+
farce_proc_t *proc;
|
|
153
|
+
|
|
154
|
+
(void)module;
|
|
155
|
+
|
|
156
|
+
if (!rb_obj_is_proc(proc_val))
|
|
157
|
+
rb_raise(rb_eTypeError, "expected a Proc");
|
|
158
|
+
|
|
159
|
+
if (lambda_mode != Qnil && lambda_mode != Qtrue && lambda_mode != Qfalse)
|
|
160
|
+
rb_raise(rb_eArgError, "lambda mode must be true, false, or nil");
|
|
161
|
+
|
|
162
|
+
new_proc = rb_funcall(proc_val, rb_intern("dup"), 0);
|
|
163
|
+
proc = farce_proc_ptr(new_proc);
|
|
164
|
+
|
|
165
|
+
if (!farce_proc_rebindable_p(proc))
|
|
166
|
+
rb_raise(rb_eTypeError, "cannot rebind this Proc representation");
|
|
167
|
+
|
|
168
|
+
RB_OBJ_WRITE(new_proc, FARCE_PROC_SELF(proc), new_self);
|
|
169
|
+
|
|
170
|
+
if (lambda_mode != Qnil)
|
|
171
|
+
FARCE_PROC_LAMBDA(proc) = lambda_mode == Qtrue;
|
|
172
|
+
|
|
173
|
+
return new_proc;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
void Init_rebind(void)
|
|
177
|
+
{
|
|
178
|
+
#ifdef HAVE_RB_EXT_RACTOR_SAFE
|
|
179
|
+
rb_ext_ractor_safe(true);
|
|
180
|
+
#endif
|
|
181
|
+
|
|
182
|
+
VALUE mFarce = rb_const_get(rb_cObject, rb_intern("Farce"));
|
|
183
|
+
VALUE mInternal = rb_const_get(mFarce, rb_intern("Internal"));
|
|
184
|
+
rb_define_module_function(mInternal, "rebind", rebind_proc_self, 3);
|
|
185
|
+
rb_define_module_function(mInternal, "rebindable?", proc_rebindable_p, 1);
|
|
186
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# @!macro unsafe
|
|
2
|
+
# @note
|
|
3
|
+
# Instances of this class are **not thread-safe** and should not be shared concurrently.
|
|
4
|
+
# This does not just apply to Ractors, but also Threads and possibly Fibers.
|
|
5
|
+
#
|
|
6
|
+
# Only use them if you are absolutely certain of this and they do not leave a tightly controlled scope.
|
|
7
|
+
# They can provide a memory or performance advantage over their thread-safe counterparts, so they
|
|
8
|
+
# might be useful for hot path optimizations.
|
|
9
|
+
#
|
|
10
|
+
# Concurrent access, especially modifications, may corrupt the internal state of the object.
|
|
11
|
+
|
|
12
|
+
# @!macro modes
|
|
13
|
+
# Valid modes are:
|
|
14
|
+
# * `:copy` - The value will be copied between Ractors. This is the default mode.
|
|
15
|
+
# * `:make_shareable` - The value will be made Ractor-shareable using {Ractor.make_shareable}.
|
|
16
|
+
# * `:move` - The value will be moved between Ractors. This saves memory compared to copying, and supports values
|
|
17
|
+
# that can't be copied but moved (like IO objects). However, the value will no longer be accessible on the Ractor
|
|
18
|
+
# that pushed it.
|
|
19
|
+
# * `:mutable` - A {Farce::Mutable} instance will be created for the value. This isn't done recursively and thus will fail for nested unshareable values.
|
|
20
|
+
# * `:local` - The value will be kept local to the Ractor that pushed it. Another ractor trying to receive it will
|
|
21
|
+
# get an error. Useful for usage contained within a single Ractor.
|
|
22
|
+
# * `:proxy` - The value will be wrapped in a `Farce::Proxy` that executes calls in the original Ractor.
|
|
23
|
+
# * `:raise` - An error will be raised if the value is not Ractor-shareable. Useful for enforcing shareability.
|
|
24
|
+
# * `:dedup` - The value will be deduplicated using {Farce.dedup}, then made Ractor-shareable.
|
|
25
|
+
# This may update and freeze the original. Already-shareable values pass through unchanged.
|
|
26
|
+
# * `:shareable_copy` - The value will be copied and the copy will be made Ractor-shareable.
|
|
27
|
+
|
|
28
|
+
# @!macro scopes
|
|
29
|
+
# Valid scopes are:
|
|
30
|
+
# * `:ractor` - The value is shared by all threads within the same Ractor.
|
|
31
|
+
# * `:thread_group` - The value is shared by all threads within the same thread group.
|
|
32
|
+
# * `:thread` - The value is shared by all fibers within the same thread.
|
|
33
|
+
# * `:fiber_storage` - The value is shared by all fibers using the same storage.
|
|
34
|
+
# Unless explicitly specified, storage is inherited from the parent fiber for blocking fibers,
|
|
35
|
+
# but not for non-blocking fibers (such as those created by {Scheduler#schedule}).
|
|
36
|
+
# * `:fiber` - Each fiber has its own independent value.
|
|
37
|
+
|
|
38
|
+
# @!macro key_normalization
|
|
39
|
+
# @param normalize_keys [Symbol, Proc, Hash, Farce::Abstract::Map, nil]
|
|
40
|
+
# Converts incoming keys to their canonical stored form:
|
|
41
|
+
# * If a `Symbol` is provided, it will be used as a method name to call on each key.
|
|
42
|
+
# * If a `Proc` is provided, it will be called with each key and should return the normalized key.
|
|
43
|
+
# * If a `Hash` or {Farce::Abstract::Map Map} is provided, it will be used to look up the normalized key for each incoming key.
|
|
44
|
+
|
|
45
|
+
# @!macro map_initialization
|
|
46
|
+
# Create a map with optional initial entries.
|
|
47
|
+
# @param initial_mapping [Hash, Farce::Abstract::Map, #each, nil]
|
|
48
|
+
# The entries to store initially. Each iteration must yield a key-value pair.
|
|
49
|
+
# Nil creates an empty map.
|
|
50
|
+
# @!macro key_normalization
|
|
51
|
+
# @param compare_by_identity [Boolean]
|
|
52
|
+
# The default identity-comparison setting for keys and values. Defaults to false.
|
|
53
|
+
# @param compare_keys_by_identity [Boolean]
|
|
54
|
+
# Whether keys use object identity instead of `hash` and `eql?`.
|
|
55
|
+
# Defaults to `compare_by_identity`.
|
|
56
|
+
# @param compare_values_by_identity [Boolean]
|
|
57
|
+
# Whether values use object identity instead of `==`.
|
|
58
|
+
# Defaults to `compare_by_identity`.
|
|
59
|
+
|
|
60
|
+
# @!macro active_support
|
|
61
|
+
# @note This methods is only available if ActiveSupport has been loaded.
|