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,350 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# shareable_constant_value: literal
|
|
3
|
+
# warn_indent: true
|
|
4
|
+
|
|
5
|
+
require "farce/engine/jvm/types"
|
|
6
|
+
|
|
7
|
+
module Farce
|
|
8
|
+
# @!visibility private
|
|
9
|
+
module Internal # :nodoc: all
|
|
10
|
+
module JVMTreeMapBackend
|
|
11
|
+
Key = Struct.new(:key, :mutation_owner)
|
|
12
|
+
Value = Data.define(:value)
|
|
13
|
+
KEY_COMPARATOR = JVMContainers.comparator do |left, right|
|
|
14
|
+
next 0 if left.equal?(right)
|
|
15
|
+
|
|
16
|
+
comparison = JVMContainers.compare(left.key, right.key)
|
|
17
|
+
owner = left.mutation_owner || right.mutation_owner
|
|
18
|
+
raise FrozenError, "can't modify frozen #{owner.class}" if owner&.__send__(:mutation_frozen?)
|
|
19
|
+
comparison
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
INITIALIZATION_LOCK = Mutex.new
|
|
23
|
+
State = Struct.new(:tree, :revision)
|
|
24
|
+
private_constant :Key, :Value, :KEY_COMPARATOR, :INITIALIZATION_LOCK, :State
|
|
25
|
+
|
|
26
|
+
def initialize(entries = nil)
|
|
27
|
+
check_uninitialized
|
|
28
|
+
|
|
29
|
+
parsed_entries = coerce_entries(entries)
|
|
30
|
+
check_uninitialized
|
|
31
|
+
guard = JVMOperationGuard.new(synchronized: synchronized?, label: "tree map")
|
|
32
|
+
state = build_state(parsed_entries)
|
|
33
|
+
|
|
34
|
+
if synchronized?
|
|
35
|
+
guard.synchronize { publish_initialization(guard, state) }
|
|
36
|
+
else
|
|
37
|
+
publish_initialization(guard, state)
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Adapt the existing Java lock only for transaction participants.
|
|
42
|
+
TransactionLock = Struct.new(:lock) do
|
|
43
|
+
def try_lock = lock.tryLock
|
|
44
|
+
def unlock = lock.unlock
|
|
45
|
+
end
|
|
46
|
+
private_constant :TransactionLock
|
|
47
|
+
|
|
48
|
+
def transaction_snapshot
|
|
49
|
+
raise TypeError, "unsafe tree maps do not support transactions" unless synchronized?
|
|
50
|
+
tree, revision = access { [JVMContainers::TreeMap.new(@state.tree), @state.revision] }
|
|
51
|
+
working = self.class.new
|
|
52
|
+
working.instance_variable_get(:@state).tree = tree
|
|
53
|
+
lock = TransactionLock.new(@guard.instance_variable_get(:@lock))
|
|
54
|
+
PortableTransaction::TreeEntry.new(self, working, revision, lock, :tree)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def prepare_key(key) = canonical_key(key)
|
|
58
|
+
|
|
59
|
+
def freeze
|
|
60
|
+
state = @freeze_state
|
|
61
|
+
return super unless synchronized? && state
|
|
62
|
+
|
|
63
|
+
state.set
|
|
64
|
+
self
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def frozen?
|
|
68
|
+
state = @freeze_state
|
|
69
|
+
synchronized? && state ? state.value : super
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def [](key)
|
|
73
|
+
key = canonical_key(key)
|
|
74
|
+
access do
|
|
75
|
+
wrapped = JVMContainers.nullable(@state.tree.get(Key.new(key)))
|
|
76
|
+
wrapped&.value
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def fetch(*arguments)
|
|
81
|
+
unless arguments.length.between?(1, 2)
|
|
82
|
+
raise ArgumentError, "wrong number of arguments (given #{arguments.length}, expected 1..2)"
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
original_key, default = arguments
|
|
86
|
+
key = canonical_key(original_key)
|
|
87
|
+
default_given = arguments.length == 2
|
|
88
|
+
warn "block supersedes default value argument", uplevel: 1 if block_given? && default_given
|
|
89
|
+
wrapped = access { JVMContainers.nullable(@state.tree.get(Key.new(key))) }
|
|
90
|
+
return wrapped.value if wrapped
|
|
91
|
+
return yield(original_key) if block_given?
|
|
92
|
+
return default if default_given
|
|
93
|
+
|
|
94
|
+
raise KeyError.new(
|
|
95
|
+
"key not found: #{original_key.inspect}",
|
|
96
|
+
receiver: self,
|
|
97
|
+
key: original_key,
|
|
98
|
+
)
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def key?(key)
|
|
102
|
+
key = canonical_key(key)
|
|
103
|
+
access { !JVMContainers.nullable(@state.tree.get(Key.new(key))).nil? }
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def getkey(key)
|
|
107
|
+
key = canonical_key(key)
|
|
108
|
+
access do
|
|
109
|
+
probe = Key.new(key)
|
|
110
|
+
entry = JVMContainers.nullable(@state.tree.ceilingEntry(probe))
|
|
111
|
+
next unless entry && KEY_COMPARATOR.compare(probe, entry.getKey).zero?
|
|
112
|
+
|
|
113
|
+
entry.getKey.key
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def each
|
|
118
|
+
return enum_for(__method__) { size } unless block_given?
|
|
119
|
+
|
|
120
|
+
entries = access do
|
|
121
|
+
snapshot = []
|
|
122
|
+
iterator = @state.tree.entrySet.iterator
|
|
123
|
+
while iterator.hasNext
|
|
124
|
+
entry = iterator.next
|
|
125
|
+
snapshot << [entry.getKey.key, entry.getValue.value]
|
|
126
|
+
end
|
|
127
|
+
snapshot
|
|
128
|
+
end
|
|
129
|
+
entries.each { yield it }
|
|
130
|
+
self
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
def each_live
|
|
134
|
+
return enum_for(__method__) { size } unless block_given?
|
|
135
|
+
|
|
136
|
+
iterator = nil
|
|
137
|
+
last_key = nil
|
|
138
|
+
revision = nil
|
|
139
|
+
started = false
|
|
140
|
+
loop do
|
|
141
|
+
pair = access do
|
|
142
|
+
current = @state.revision
|
|
143
|
+
if !started || revision != current
|
|
144
|
+
entries = started ? @state.tree.tailMap(Key.new(last_key), false).entrySet : @state.tree.entrySet
|
|
145
|
+
iterator = entries.iterator
|
|
146
|
+
end
|
|
147
|
+
next unless iterator.hasNext
|
|
148
|
+
|
|
149
|
+
entry = iterator.next
|
|
150
|
+
last_key = entry.getKey.key
|
|
151
|
+
revision = current
|
|
152
|
+
[last_key, entry.getValue.value]
|
|
153
|
+
end
|
|
154
|
+
break unless pair
|
|
155
|
+
started = true
|
|
156
|
+
yield pair
|
|
157
|
+
end
|
|
158
|
+
self
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
def []=(key, value)
|
|
162
|
+
key = canonical_key(key)
|
|
163
|
+
access do
|
|
164
|
+
check_local_frozen
|
|
165
|
+
wrapped_key = mutation_key(key)
|
|
166
|
+
begin
|
|
167
|
+
@state.tree.put(wrapped_key, Value.new(value))
|
|
168
|
+
@state.revision += 1
|
|
169
|
+
ensure
|
|
170
|
+
Thread.handle_interrupt(INTERRUPT_MASK) do
|
|
171
|
+
wrapped_key.mutation_owner = nil
|
|
172
|
+
end
|
|
173
|
+
end
|
|
174
|
+
value
|
|
175
|
+
end
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
def delete(key)
|
|
179
|
+
key = canonical_key(key)
|
|
180
|
+
access do
|
|
181
|
+
check_local_frozen
|
|
182
|
+
wrapped = JVMContainers.nullable(@state.tree.remove(mutation_key(key)))
|
|
183
|
+
@state.revision += 1 if wrapped
|
|
184
|
+
wrapped&.value
|
|
185
|
+
end
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
def first_key
|
|
189
|
+
access do
|
|
190
|
+
entry = JVMContainers.nullable(@state.tree.firstEntry)
|
|
191
|
+
entry&.getKey&.key
|
|
192
|
+
end
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
def last_key
|
|
196
|
+
access do
|
|
197
|
+
entry = JVMContainers.nullable(@state.tree.lastEntry)
|
|
198
|
+
entry&.getKey&.key
|
|
199
|
+
end
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
def shift
|
|
203
|
+
access do
|
|
204
|
+
check_local_frozen
|
|
205
|
+
iterator = @state.tree.entrySet.iterator
|
|
206
|
+
next nil unless iterator.hasNext
|
|
207
|
+
|
|
208
|
+
entry = iterator.next
|
|
209
|
+
result = [entry.getKey.key, entry.getValue.value]
|
|
210
|
+
iterator.remove
|
|
211
|
+
@state.revision += 1
|
|
212
|
+
result
|
|
213
|
+
end
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
def pop
|
|
217
|
+
access do
|
|
218
|
+
check_local_frozen
|
|
219
|
+
iterator = @state.tree.descendingMap.entrySet.iterator
|
|
220
|
+
next nil unless iterator.hasNext
|
|
221
|
+
|
|
222
|
+
entry = iterator.next
|
|
223
|
+
result = [entry.getKey.key, entry.getValue.value]
|
|
224
|
+
iterator.remove
|
|
225
|
+
@state.revision += 1
|
|
226
|
+
result
|
|
227
|
+
end
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
def size = access { @state.tree.size }
|
|
231
|
+
|
|
232
|
+
alias length size
|
|
233
|
+
|
|
234
|
+
def empty? = access { @state.tree.isEmpty }
|
|
235
|
+
|
|
236
|
+
def clear
|
|
237
|
+
access do
|
|
238
|
+
check_local_frozen
|
|
239
|
+
unless @state.tree.isEmpty
|
|
240
|
+
@state.tree.clear
|
|
241
|
+
@state.revision += 1
|
|
242
|
+
end
|
|
243
|
+
end
|
|
244
|
+
self
|
|
245
|
+
end
|
|
246
|
+
|
|
247
|
+
def initialize_copy(_other)
|
|
248
|
+
raise TypeError, "tree maps cannot be copied"
|
|
249
|
+
end
|
|
250
|
+
|
|
251
|
+
private
|
|
252
|
+
|
|
253
|
+
def publish_initialization(guard, state)
|
|
254
|
+
INITIALIZATION_LOCK.synchronize do
|
|
255
|
+
Thread.handle_interrupt(INTERRUPT_MASK) do
|
|
256
|
+
raise "tree map is already initialized" if defined?(@state)
|
|
257
|
+
raise FrozenError, "can't modify frozen #{self.class}" if
|
|
258
|
+
JVMContainers.frozen_object?(self)
|
|
259
|
+
|
|
260
|
+
@state = state
|
|
261
|
+
@guard = guard
|
|
262
|
+
@freeze_state = Flag.new(false) if synchronized?
|
|
263
|
+
JVMContainers.freeze_object(self) if synchronized?
|
|
264
|
+
end
|
|
265
|
+
end
|
|
266
|
+
end
|
|
267
|
+
|
|
268
|
+
def access(&)
|
|
269
|
+
raise "tree map is not initialized" unless defined?(@guard) && @guard
|
|
270
|
+
|
|
271
|
+
@guard.synchronize(&)
|
|
272
|
+
end
|
|
273
|
+
|
|
274
|
+
def synchronized? = false
|
|
275
|
+
|
|
276
|
+
def check_local_frozen
|
|
277
|
+
raise FrozenError, "can't modify frozen #{self.class}" if mutation_frozen?
|
|
278
|
+
end
|
|
279
|
+
|
|
280
|
+
def mutation_frozen? = synchronized? ? frozen? : JVMContainers.frozen_object?(self)
|
|
281
|
+
|
|
282
|
+
def mutation_key(key)
|
|
283
|
+
Key.new(key, self)
|
|
284
|
+
end
|
|
285
|
+
|
|
286
|
+
def canonical_key(key)
|
|
287
|
+
JVMContainers.string?(key) ? JVMContainers.canonical_string(key) : key
|
|
288
|
+
end
|
|
289
|
+
|
|
290
|
+
def build_state(entries)
|
|
291
|
+
tree = JVMContainers::TreeMap.new(KEY_COMPARATOR)
|
|
292
|
+
entries&.each do |key, value|
|
|
293
|
+
check_uninitialized
|
|
294
|
+
|
|
295
|
+
stored_key = canonical_key(key)
|
|
296
|
+
check_uninitialized
|
|
297
|
+
|
|
298
|
+
wrapped_key = Key.new(stored_key, self)
|
|
299
|
+
begin
|
|
300
|
+
tree.put(wrapped_key, Value.new(value))
|
|
301
|
+
ensure
|
|
302
|
+
Thread.handle_interrupt(INTERRUPT_MASK) do
|
|
303
|
+
wrapped_key.mutation_owner = nil
|
|
304
|
+
end
|
|
305
|
+
end
|
|
306
|
+
check_uninitialized
|
|
307
|
+
end
|
|
308
|
+
State.new(tree, 0)
|
|
309
|
+
end
|
|
310
|
+
|
|
311
|
+
def check_uninitialized
|
|
312
|
+
raise "tree map is already initialized" if defined?(@state)
|
|
313
|
+
raise FrozenError, "can't modify frozen #{self.class}" if JVMContainers.frozen_object?(self)
|
|
314
|
+
end
|
|
315
|
+
|
|
316
|
+
def coerce_entries(entries)
|
|
317
|
+
return if entries.nil?
|
|
318
|
+
return entries if JVMContainers.is_a?(entries, Hash)
|
|
319
|
+
|
|
320
|
+
hash = entries.to_hash if entries.respond_to?(:to_hash)
|
|
321
|
+
return hash if JVMContainers.is_a?(hash, Hash)
|
|
322
|
+
|
|
323
|
+
raise TypeError, "entries must be a Hash or respond to #to_hash"
|
|
324
|
+
end
|
|
325
|
+
end
|
|
326
|
+
|
|
327
|
+
# Explicit unsynchronized JVM implementation.
|
|
328
|
+
class UnsafeTreeMap
|
|
329
|
+
include JVMTreeMapBackend
|
|
330
|
+
end
|
|
331
|
+
|
|
332
|
+
# Default JVM implementation: ordinary TreeMap under a coarse lock. The
|
|
333
|
+
# object itself is frozen, while its guarded Java backing map remains the
|
|
334
|
+
# controlled mutation boundary.
|
|
335
|
+
class TreeMap
|
|
336
|
+
include JVMTreeMapBackend
|
|
337
|
+
|
|
338
|
+
private
|
|
339
|
+
|
|
340
|
+
def synchronized? = true
|
|
341
|
+
end
|
|
342
|
+
|
|
343
|
+
MutableTreeMap = TreeMap
|
|
344
|
+
|
|
345
|
+
# No Ractors, no problems :)
|
|
346
|
+
ShareableTreeMap = TreeMap
|
|
347
|
+
|
|
348
|
+
private_constant :JVMTreeMapBackend
|
|
349
|
+
end
|
|
350
|
+
end
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# shareable_constant_value: literal
|
|
3
|
+
# warn_indent: true
|
|
4
|
+
|
|
5
|
+
# Loaded only by JRuby and TruffleRuby in JVM mode. The two runtimes expose
|
|
6
|
+
# different Java interop namespaces, so the rest of the container code goes
|
|
7
|
+
# through this deliberately small bridge.
|
|
8
|
+
|
|
9
|
+
module Farce
|
|
10
|
+
# @!visibility private
|
|
11
|
+
module Internal # :nodoc: all
|
|
12
|
+
module JVMContainers
|
|
13
|
+
CLASS_ALLOCATE_METHOD = Class.instance_method(:allocate)
|
|
14
|
+
BASIC_OBJECT_EQUAL_METHOD = BasicObject.instance_method(:equal?)
|
|
15
|
+
OBJECT_CLASS_METHOD = Object.instance_method(:class)
|
|
16
|
+
OBJECT_FREEZE_METHOD = Object.instance_method(:freeze)
|
|
17
|
+
OBJECT_FROZEN_METHOD = Object.instance_method(:frozen?)
|
|
18
|
+
OBJECT_ID_METHOD = BasicObject.instance_method(:__id__)
|
|
19
|
+
OBJECT_INSTANCE_VARIABLES_METHOD = Object.instance_method(:instance_variables)
|
|
20
|
+
OBJECT_INSTANCE_VARIABLE_GET_METHOD = Object.instance_method(:instance_variable_get)
|
|
21
|
+
OBJECT_INSTANCE_VARIABLE_SET_METHOD = Object.instance_method(:instance_variable_set)
|
|
22
|
+
OBJECT_IS_A_METHOD = Object.instance_method(:is_a?)
|
|
23
|
+
STRING_INITIALIZE_COPY_METHOD = String.instance_method(:initialize_copy)
|
|
24
|
+
|
|
25
|
+
if RUBY_ENGINE == "jruby"
|
|
26
|
+
ConcurrentHashMap = Java::JavaUtilConcurrent::ConcurrentHashMap
|
|
27
|
+
TreeMap = Java::JavaUtil::TreeMap
|
|
28
|
+
ArrayDeque = Java::JavaUtil::ArrayDeque
|
|
29
|
+
AtomicBoolean = Java::JavaUtilConcurrentAtomic::AtomicBoolean
|
|
30
|
+
AtomicLong = Java::JavaUtilConcurrentAtomic::AtomicLong
|
|
31
|
+
Comparator = Java::JavaUtil::Comparator
|
|
32
|
+
ReentrantLock = Java::JavaUtilConcurrentLocks::ReentrantLock
|
|
33
|
+
else
|
|
34
|
+
ConcurrentHashMap = Java.type("java.util.concurrent.ConcurrentHashMap")
|
|
35
|
+
TreeMap = Java.type("java.util.TreeMap")
|
|
36
|
+
ArrayDeque = Java.type("java.util.ArrayDeque")
|
|
37
|
+
AtomicBoolean = Java.type("java.util.concurrent.atomic.AtomicBoolean")
|
|
38
|
+
AtomicLong = Java.type("java.util.concurrent.atomic.AtomicLong")
|
|
39
|
+
Comparator = Java.type("java.util.Comparator")
|
|
40
|
+
ReentrantLock = Java.type("java.util.concurrent.locks.ReentrantLock")
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
module_function
|
|
44
|
+
|
|
45
|
+
def compare(left, right)
|
|
46
|
+
comparison = left <=> right
|
|
47
|
+
raise ArgumentError, "comparison failed" if comparison.nil?
|
|
48
|
+
|
|
49
|
+
# Match rb_cmpint: comparator results only need ordering against zero,
|
|
50
|
+
# they need not implement Numeric's #positive?/#negative?.
|
|
51
|
+
return 1 if comparison > 0 # rubocop:disable Style/NumericPredicate
|
|
52
|
+
return -1 if comparison < 0 # rubocop:disable Style/NumericPredicate
|
|
53
|
+
|
|
54
|
+
0
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# Passing a Ruby lambda through this Java method produces a concrete SAM
|
|
58
|
+
# proxy. This avoids TruffleRuby choosing TreeMap's Map constructor when
|
|
59
|
+
# a bare Ruby lambda could also look map-like to polyglot interop.
|
|
60
|
+
def comparator(&block) = Comparator.nullsFirst(block)
|
|
61
|
+
|
|
62
|
+
# TruffleRuby represents a Java null as Polyglot::ForeignNull. It reports
|
|
63
|
+
# nil? but is not the VM's Qnil, so safe navigation still invokes methods
|
|
64
|
+
# on it. Always normalize Java-returned nullable references explicitly.
|
|
65
|
+
def nullable(value) = value.nil? ? nil : value
|
|
66
|
+
|
|
67
|
+
# Call BasicObject's primitive directly so a payload cannot destabilize
|
|
68
|
+
# the cancellation index by overriding #__id__.
|
|
69
|
+
def identity_token(value) = OBJECT_ID_METHOD.bind_call(value)
|
|
70
|
+
|
|
71
|
+
# Identity deletion is part of the container contract, so it must not
|
|
72
|
+
# dispatch an overridable payload #equal?.
|
|
73
|
+
def identical?(left, right) = BASIC_OBJECT_EQUAL_METHOD.bind_call(left, right)
|
|
74
|
+
|
|
75
|
+
# Match rb_obj_freeze rather than dispatching to an overridable Ruby
|
|
76
|
+
# method while an initialized shared container is being published.
|
|
77
|
+
def freeze_object(value) = OBJECT_FREEZE_METHOD.bind_call(value)
|
|
78
|
+
|
|
79
|
+
# Frozen local containers must remain immutable even when a subclass
|
|
80
|
+
# overrides #frozen? and lies about the VM's object flag.
|
|
81
|
+
def frozen_object?(value) = OBJECT_FROZEN_METHOD.bind_call(value)
|
|
82
|
+
|
|
83
|
+
def is_a?(value, klass) = OBJECT_IS_A_METHOD.bind_call(value, klass)
|
|
84
|
+
|
|
85
|
+
def string?(value) = is_a?(value, String)
|
|
86
|
+
|
|
87
|
+
# Match String#-@ for ordinary Strings without dispatching an override.
|
|
88
|
+
# String subclasses retain the explicit primitive snapshot path because
|
|
89
|
+
# JRuby and TruffleRuby differ from CRuby around subclass #freeze hooks.
|
|
90
|
+
def canonical_string(value)
|
|
91
|
+
klass = OBJECT_CLASS_METHOD.bind_call(value)
|
|
92
|
+
return -value if identical?(klass, String)
|
|
93
|
+
|
|
94
|
+
copy_frozen_string(value)
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# Preserve String subclasses and their comparison methods while
|
|
98
|
+
# bypassing overridable #dup, #initialize_copy, #class, and #freeze.
|
|
99
|
+
# This mirrors the primitive String snapshot used by native containers.
|
|
100
|
+
def copy_frozen_string(value)
|
|
101
|
+
klass = OBJECT_CLASS_METHOD.bind_call(value)
|
|
102
|
+
copy = CLASS_ALLOCATE_METHOD.bind_call(klass)
|
|
103
|
+
STRING_INITIALIZE_COPY_METHOD.bind_call(copy, value)
|
|
104
|
+
OBJECT_INSTANCE_VARIABLES_METHOD.bind_call(value).each do |name|
|
|
105
|
+
instance_value = OBJECT_INSTANCE_VARIABLE_GET_METHOD.bind_call(value, name)
|
|
106
|
+
OBJECT_INSTANCE_VARIABLE_SET_METHOD.bind_call(copy, name, instance_value)
|
|
107
|
+
end
|
|
108
|
+
freeze_object(copy)
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
class JVMOperationGuard
|
|
113
|
+
def initialize(synchronized:, label:, recursive_error: nil)
|
|
114
|
+
@lock = JVMContainers::ReentrantLock.new if synchronized
|
|
115
|
+
@label = label
|
|
116
|
+
@owner = nil
|
|
117
|
+
@recursive_error = recursive_error || (synchronized ? ThreadError : RuntimeError)
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def synchronize(&)
|
|
121
|
+
# JRuby exposes a distinct Ruby Thread wrapper to sibling Fibers on the
|
|
122
|
+
# same Java thread. Normalize it before checking logical ownership so a
|
|
123
|
+
# reentrant Java lock cannot admit a sibling while callbacks hold state.
|
|
124
|
+
current = Internal.storage_thread(Thread.current)
|
|
125
|
+
if JVMContainers.identical?(@owner, current)
|
|
126
|
+
message = @recursive_error == ThreadError ? "recursive #{@label} access" :
|
|
127
|
+
"container cannot be modified during comparison"
|
|
128
|
+
raise @recursive_error, message
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
if @lock
|
|
132
|
+
synchronize_locked(current, &)
|
|
133
|
+
else
|
|
134
|
+
begin
|
|
135
|
+
@owner = current
|
|
136
|
+
yield
|
|
137
|
+
ensure
|
|
138
|
+
Thread.handle_interrupt(INTERRUPT_MASK) { @owner = nil }
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
private
|
|
144
|
+
|
|
145
|
+
# Querying the Java lock in ensure closes the tiny async-exception gap
|
|
146
|
+
# between ReentrantLock#lock returning and Ruby recording ownership.
|
|
147
|
+
# A cancellation while waiting sees false; one after acquisition always
|
|
148
|
+
# releases the lock, even if it arrived before @owner was assigned.
|
|
149
|
+
def synchronize_locked(current)
|
|
150
|
+
acquired = @lock.tryLock
|
|
151
|
+
unless acquired
|
|
152
|
+
# A blocking Java lock can strand the backing thread that must run the
|
|
153
|
+
# lock owner. CRuby uses a scheduler-aware native gate; JVM engines do
|
|
154
|
+
# not promise that extension, so fail safely on the contended path.
|
|
155
|
+
raise ThreadError, "shared JVM container contention cannot block a scheduled Fiber" if scheduled_fiber?
|
|
156
|
+
|
|
157
|
+
@lock.lock
|
|
158
|
+
end
|
|
159
|
+
@owner = current
|
|
160
|
+
yield
|
|
161
|
+
ensure
|
|
162
|
+
Thread.handle_interrupt(INTERRUPT_MASK) do
|
|
163
|
+
if @lock.isHeldByCurrentThread
|
|
164
|
+
@owner = nil
|
|
165
|
+
@lock.unlock
|
|
166
|
+
end
|
|
167
|
+
end
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
def scheduled_fiber?
|
|
171
|
+
return false unless Fiber.respond_to?(:scheduler)
|
|
172
|
+
return false if Fiber.current.respond_to?(:blocking?) && Fiber.current.blocking?
|
|
173
|
+
|
|
174
|
+
scheduler = Fiber.current_scheduler if Fiber.respond_to?(:current_scheduler)
|
|
175
|
+
scheduler ||= Fiber.scheduler
|
|
176
|
+
!scheduler.nil?
|
|
177
|
+
end
|
|
178
|
+
end
|
|
179
|
+
end
|
|
180
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# shareable_constant_value: literal
|
|
3
|
+
# warn_indent: true
|
|
4
|
+
|
|
5
|
+
# Common atomic and ordered-container loader for JRuby and TruffleRuby in JVM mode.
|
|
6
|
+
|
|
7
|
+
module Farce
|
|
8
|
+
# @!visibility private
|
|
9
|
+
module Internal # :nodoc: all
|
|
10
|
+
autoload :ConcurrentWeakRegistry, "farce/engine/jvm/concurrent_weak_registry"
|
|
11
|
+
autoload :Counter, "farce/engine/jvm/counter"
|
|
12
|
+
autoload :Flag, "farce/engine/jvm/flag"
|
|
13
|
+
autoload :UnsafeTreeMap, "farce/engine/jvm/tree_map"
|
|
14
|
+
autoload :PriorityQueue, "farce/engine/jvm/priority_queue"
|
|
15
|
+
autoload :ShareableTreeMap, "farce/engine/jvm/tree_map"
|
|
16
|
+
autoload :TreeMap, "farce/engine/jvm/tree_map"
|
|
17
|
+
autoload :MutableTreeMap, "farce/engine/jvm/tree_map"
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# shareable_constant_value: literal
|
|
3
|
+
# warn_indent: true
|
|
4
|
+
|
|
5
|
+
module Farce
|
|
6
|
+
module Internal
|
|
7
|
+
class FiberScheduler
|
|
8
|
+
# Ruby 3.4 can strand a control Ractor started alongside scheduled workers.
|
|
9
|
+
# Start it before admitting fibers. Ruby 4.x creates the selector lazily.
|
|
10
|
+
module RactorSelectorSetup
|
|
11
|
+
def initialize(...)
|
|
12
|
+
RactorSelector.current
|
|
13
|
+
super
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
private_constant :RactorSelectorSetup
|
|
17
|
+
prepend RactorSelectorSetup
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|