farce 0.0.1.alpha2-java
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/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 +420 -0
|
@@ -0,0 +1,505 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# shareable_constant_value: literal
|
|
3
|
+
# warn_indent: true
|
|
4
|
+
|
|
5
|
+
require "farce/engine/shared/map_key_coordination"
|
|
6
|
+
|
|
7
|
+
module Farce
|
|
8
|
+
# @!visibility private
|
|
9
|
+
module Internal # :nodoc: all
|
|
10
|
+
class Map
|
|
11
|
+
include MapKeyCoordination
|
|
12
|
+
include TransactionMapBackend
|
|
13
|
+
|
|
14
|
+
BASIC_OBJECT_EQUAL_METHOD = BasicObject.instance_method(:equal?)
|
|
15
|
+
private_constant :BASIC_OBJECT_EQUAL_METHOD
|
|
16
|
+
|
|
17
|
+
class Key
|
|
18
|
+
attr_accessor :mutation_owner
|
|
19
|
+
attr_reader :value
|
|
20
|
+
|
|
21
|
+
def initialize(value) = @value = value
|
|
22
|
+
|
|
23
|
+
def hash
|
|
24
|
+
result = value.hash
|
|
25
|
+
owner = mutation_owner
|
|
26
|
+
Internal::Freeze.check(owner) if owner
|
|
27
|
+
result
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def eql?(other)
|
|
31
|
+
result = other.is_a?(Key) && value.eql?(other.value)
|
|
32
|
+
owner = mutation_owner || (other.mutation_owner if other.is_a?(Key))
|
|
33
|
+
Internal::Freeze.check(owner) if owner
|
|
34
|
+
result
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
alias == eql?
|
|
38
|
+
end
|
|
39
|
+
private_constant :Key
|
|
40
|
+
|
|
41
|
+
# The Java bridge converts objects such as Ruby strings unless they are boxed.
|
|
42
|
+
class Value
|
|
43
|
+
attr_reader :value
|
|
44
|
+
|
|
45
|
+
def initialize(value) = @value = value
|
|
46
|
+
end
|
|
47
|
+
private_constant :Value
|
|
48
|
+
|
|
49
|
+
def initialize(
|
|
50
|
+
initial_mapping = nil,
|
|
51
|
+
compare_by_identity: false,
|
|
52
|
+
compare_keys_by_identity: compare_by_identity,
|
|
53
|
+
compare_values_by_identity: compare_by_identity
|
|
54
|
+
)
|
|
55
|
+
validate_boolean(compare_by_identity, "compare_by_identity")
|
|
56
|
+
validate_boolean(compare_keys_by_identity, "compare_keys_by_identity")
|
|
57
|
+
validate_boolean(compare_values_by_identity, "compare_values_by_identity")
|
|
58
|
+
raise TypeError, "initial mapping must be a Hash" unless initial_mapping.nil? || initial_mapping.is_a?(Hash)
|
|
59
|
+
|
|
60
|
+
@compare_keys_by_identity = compare_keys_by_identity
|
|
61
|
+
@compare_values_by_identity = compare_values_by_identity
|
|
62
|
+
@freeze_state = Flag.new(false)
|
|
63
|
+
@map = java.util.concurrent.ConcurrentHashMap.new
|
|
64
|
+
# Native operations may overlap. Block updates reserve one logical key.
|
|
65
|
+
@state_mutex = Mutex.new
|
|
66
|
+
@change_signal = Signal.new
|
|
67
|
+
@active_owner_fiber = nil
|
|
68
|
+
@active_owner_thread = nil
|
|
69
|
+
@active_owners = nil
|
|
70
|
+
initialize_key_coordination
|
|
71
|
+
initial_mapping&.each do |key, value|
|
|
72
|
+
check_frozen!
|
|
73
|
+
wrapped = wrap_key(key)
|
|
74
|
+
with_mutation_callback_guard(wrapped) { concurrent_store(wrapped, value) }
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def freeze
|
|
79
|
+
state = @freeze_state
|
|
80
|
+
return super unless state
|
|
81
|
+
|
|
82
|
+
state.set
|
|
83
|
+
self
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def frozen?
|
|
87
|
+
state = @freeze_state
|
|
88
|
+
state ? state.value : super
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def check_mutation = check_frozen!
|
|
92
|
+
|
|
93
|
+
def prepare_mutation_key(key)
|
|
94
|
+
check_frozen!
|
|
95
|
+
normalize_external_key(key)
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def [](key) = concurrent_get(wrap_key(key))
|
|
99
|
+
|
|
100
|
+
def fetch(*arguments)
|
|
101
|
+
unless arguments.length.between?(1, 2)
|
|
102
|
+
raise ArgumentError, "wrong number of arguments (given #{arguments.length}, expected 1..2)"
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
key, default = arguments
|
|
106
|
+
default_given = arguments.length == 2
|
|
107
|
+
warn "block supersedes default value argument", uplevel: 1 if block_given? && default_given
|
|
108
|
+
stored = @map.get(wrap_key(key))
|
|
109
|
+
return stored.value if stored
|
|
110
|
+
return yield(key) if block_given?
|
|
111
|
+
return default if default_given
|
|
112
|
+
|
|
113
|
+
raise KeyError.new("key not found: #{key.inspect}", receiver: self, key: key)
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def []=(key, value)
|
|
117
|
+
check_frozen!
|
|
118
|
+
wrapped = wrap_key(key)
|
|
119
|
+
_, result = with_mutating_key_operation(wrapped, nil) do |reservation|
|
|
120
|
+
commit_mutation(reservation) do
|
|
121
|
+
native_operation { concurrent_store(wrapped, value) }
|
|
122
|
+
changed!
|
|
123
|
+
end
|
|
124
|
+
value
|
|
125
|
+
end
|
|
126
|
+
result
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def get(key, timeout: nil, &fallback)
|
|
130
|
+
wrapped = wrap_key(key)
|
|
131
|
+
completed, result = with_key_operation(wrapped, timeout_deadline(timeout)) do
|
|
132
|
+
native_operation { concurrent_get(wrapped) }
|
|
133
|
+
end
|
|
134
|
+
completed ? result : fallback&.call
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
def store(key, value, timeout: nil, &fallback)
|
|
138
|
+
check_frozen!
|
|
139
|
+
wrapped = wrap_key(key)
|
|
140
|
+
completed, result = with_mutating_key_operation(wrapped, timeout_deadline(timeout)) do |reservation|
|
|
141
|
+
commit_mutation(reservation) do
|
|
142
|
+
native_operation { concurrent_store(wrapped, value) }
|
|
143
|
+
changed!
|
|
144
|
+
end
|
|
145
|
+
value
|
|
146
|
+
end
|
|
147
|
+
completed ? result : fallback&.call
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
def swap(key, replacement, timeout: nil, &fallback)
|
|
151
|
+
check_frozen!
|
|
152
|
+
wrapped = wrap_key(key)
|
|
153
|
+
completed, result = with_mutating_key_operation(wrapped, timeout_deadline(timeout)) do |reservation|
|
|
154
|
+
previous = nil
|
|
155
|
+
commit_mutation(reservation) do
|
|
156
|
+
previous = native_operation { concurrent_swap(wrapped, replacement) }
|
|
157
|
+
changed!
|
|
158
|
+
end
|
|
159
|
+
previous
|
|
160
|
+
end
|
|
161
|
+
completed ? result : fallback&.call
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
def store_if_absent(key, timeout: nil)
|
|
165
|
+
raise LocalJumpError, "no block given" unless block_given?
|
|
166
|
+
check_frozen!
|
|
167
|
+
|
|
168
|
+
wrapped = wrap_key(key)
|
|
169
|
+
completed, result = with_mutating_key_operation(wrapped, timeout_deadline(timeout)) do |reservation|
|
|
170
|
+
if native_operation { concurrent_key?(wrapped) }
|
|
171
|
+
native_operation { concurrent_get(wrapped) }
|
|
172
|
+
else
|
|
173
|
+
value = yield
|
|
174
|
+
stored = commit_mutation(reservation) do
|
|
175
|
+
native_operation { concurrent_store(wrapped, value) }
|
|
176
|
+
changed!
|
|
177
|
+
end
|
|
178
|
+
value if stored
|
|
179
|
+
end
|
|
180
|
+
end
|
|
181
|
+
completed ? result : nil
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
def compare_and_set(key, expected, replacement, timeout: nil)
|
|
185
|
+
check_frozen!
|
|
186
|
+
wrapped = wrap_key(key)
|
|
187
|
+
completed, result = with_mutating_key_operation(wrapped, timeout_deadline(timeout)) do |reservation|
|
|
188
|
+
next false unless native_operation { concurrent_key?(wrapped) }
|
|
189
|
+
|
|
190
|
+
current = native_operation { concurrent_get(wrapped) }
|
|
191
|
+
next false unless values_equal?(current, expected)
|
|
192
|
+
|
|
193
|
+
replaced = commit_mutation(reservation) do
|
|
194
|
+
native_operation { concurrent_store(wrapped, replacement) }
|
|
195
|
+
changed!
|
|
196
|
+
end
|
|
197
|
+
replaced
|
|
198
|
+
end
|
|
199
|
+
completed && result
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
def update(key, timeout: nil)
|
|
203
|
+
raise LocalJumpError, "no block given" unless block_given?
|
|
204
|
+
check_frozen!
|
|
205
|
+
|
|
206
|
+
wrapped = wrap_key(key)
|
|
207
|
+
completed, result = with_mutating_key_operation(wrapped, timeout_deadline(timeout)) do |reservation|
|
|
208
|
+
value = yield(native_operation { concurrent_get(wrapped) })
|
|
209
|
+
stored = commit_mutation(reservation) do
|
|
210
|
+
native_operation { concurrent_store(wrapped, value) }
|
|
211
|
+
changed!
|
|
212
|
+
end
|
|
213
|
+
value if stored
|
|
214
|
+
end
|
|
215
|
+
completed ? result : nil
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
# Yield presence and value under the key reservation.
|
|
219
|
+
# MAP_KEEP and MAP_DELETE are control results. Other results replace the value.
|
|
220
|
+
# Return whether a change committed.
|
|
221
|
+
def modify(key) # rubocop:disable Naming/PredicateMethod
|
|
222
|
+
raise LocalJumpError, "no block given" unless block_given?
|
|
223
|
+
check_frozen!
|
|
224
|
+
wrapped = wrap_key(key)
|
|
225
|
+
_, result = with_mutating_key_operation(wrapped, nil) do |reservation|
|
|
226
|
+
present, current = native_operation { [concurrent_key?(wrapped), concurrent_get(wrapped)] }
|
|
227
|
+
value = yield(present, current)
|
|
228
|
+
next false if MAP_KEEP.equal?(value)
|
|
229
|
+
remove = MAP_DELETE.equal?(value)
|
|
230
|
+
next false if remove && !present
|
|
231
|
+
commit_mutation(reservation) do
|
|
232
|
+
native_operation do
|
|
233
|
+
remove ? concurrent_delete(wrapped) : concurrent_store(wrapped, value)
|
|
234
|
+
end
|
|
235
|
+
changed!
|
|
236
|
+
end
|
|
237
|
+
end
|
|
238
|
+
!!result
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
def upsert(key, initial_value, timeout: nil)
|
|
242
|
+
raise LocalJumpError, "no block given" unless block_given?
|
|
243
|
+
check_frozen!
|
|
244
|
+
|
|
245
|
+
wrapped = wrap_key(key)
|
|
246
|
+
completed, result = with_mutating_key_operation(wrapped, timeout_deadline(timeout)) do |reservation|
|
|
247
|
+
present = native_operation { concurrent_key?(wrapped) }
|
|
248
|
+
value = present ? yield(native_operation { concurrent_get(wrapped) }) : initial_value
|
|
249
|
+
stored = commit_mutation(reservation) do
|
|
250
|
+
native_operation { concurrent_store(wrapped, value) }
|
|
251
|
+
changed!
|
|
252
|
+
end
|
|
253
|
+
value if stored
|
|
254
|
+
end
|
|
255
|
+
completed ? result : nil
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
def wait_until_changed(key, expected, timeout: nil, &fallback)
|
|
259
|
+
wait_for_value(key, expected, timeout_deadline(timeout), fallback, non_nil: false)
|
|
260
|
+
end
|
|
261
|
+
|
|
262
|
+
def wait_until_non_nil(key, timeout: nil, &fallback)
|
|
263
|
+
wait_for_value(key, nil, timeout_deadline(timeout), fallback, non_nil: true)
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
def key?(key) = concurrent_key?(wrap_key(key))
|
|
267
|
+
def size = @map.size
|
|
268
|
+
def compare_keys_by_identity? = @compare_keys_by_identity
|
|
269
|
+
def compare_values_by_identity? = @compare_values_by_identity
|
|
270
|
+
|
|
271
|
+
def keys = entries_snapshot.map(&:first)
|
|
272
|
+
|
|
273
|
+
def each(&block)
|
|
274
|
+
return enum_for(__callee__) { size } unless block
|
|
275
|
+
|
|
276
|
+
entries_snapshot.each { |pair| block.call(pair) }
|
|
277
|
+
self
|
|
278
|
+
end
|
|
279
|
+
alias each_pair each
|
|
280
|
+
|
|
281
|
+
def each_live
|
|
282
|
+
return enum_for(__method__) { size } unless block_given?
|
|
283
|
+
iterator = @map.entry_set.iterator
|
|
284
|
+
while iterator.has_next
|
|
285
|
+
entry = iterator.next
|
|
286
|
+
yield [entry.key.value, entry.value.value]
|
|
287
|
+
end
|
|
288
|
+
self
|
|
289
|
+
end
|
|
290
|
+
|
|
291
|
+
def each_key(&block)
|
|
292
|
+
return enum_for(__callee__) { size } unless block
|
|
293
|
+
|
|
294
|
+
entries_snapshot.each { |pair| block.call(pair.first) }
|
|
295
|
+
self
|
|
296
|
+
end
|
|
297
|
+
|
|
298
|
+
def each_value(&block)
|
|
299
|
+
return enum_for(__callee__) { size } unless block
|
|
300
|
+
|
|
301
|
+
entries_snapshot.each { |pair| block.call(pair.last) }
|
|
302
|
+
self
|
|
303
|
+
end
|
|
304
|
+
|
|
305
|
+
def clear
|
|
306
|
+
check_frozen!
|
|
307
|
+
clear_key_operations do
|
|
308
|
+
native_operation { @map.clear }
|
|
309
|
+
changed!
|
|
310
|
+
end
|
|
311
|
+
self
|
|
312
|
+
end
|
|
313
|
+
|
|
314
|
+
def delete(key)
|
|
315
|
+
check_frozen!
|
|
316
|
+
wrapped = wrap_key(key)
|
|
317
|
+
_, result = with_mutating_key_operation(wrapped, nil) do |reservation|
|
|
318
|
+
value = nil
|
|
319
|
+
commit_mutation(reservation) do
|
|
320
|
+
value = native_operation { concurrent_delete(wrapped) }
|
|
321
|
+
changed!
|
|
322
|
+
end
|
|
323
|
+
value
|
|
324
|
+
end
|
|
325
|
+
result
|
|
326
|
+
end
|
|
327
|
+
|
|
328
|
+
def getkey(key)
|
|
329
|
+
wrapped = wrap_key(key)
|
|
330
|
+
return unless concurrent_key?(wrapped)
|
|
331
|
+
|
|
332
|
+
@map.each_pair do |stored, _value|
|
|
333
|
+
return stored.value if stored.eql?(wrapped)
|
|
334
|
+
end
|
|
335
|
+
nil
|
|
336
|
+
end
|
|
337
|
+
|
|
338
|
+
private
|
|
339
|
+
|
|
340
|
+
def check_frozen! = Internal::Freeze.check(self)
|
|
341
|
+
|
|
342
|
+
def commit_mutation(reservation, &)
|
|
343
|
+
check_frozen!
|
|
344
|
+
reservation.commit(&)
|
|
345
|
+
end
|
|
346
|
+
|
|
347
|
+
def with_mutating_key_operation(key, deadline, &)
|
|
348
|
+
with_key_operation(key, deadline) do |reservation|
|
|
349
|
+
with_mutation_callback_guard(key) do
|
|
350
|
+
check_frozen!
|
|
351
|
+
yield reservation
|
|
352
|
+
end
|
|
353
|
+
end
|
|
354
|
+
end
|
|
355
|
+
|
|
356
|
+
def with_mutation_callback_guard(key)
|
|
357
|
+
return yield unless key.is_a?(Key)
|
|
358
|
+
|
|
359
|
+
key.mutation_owner = self
|
|
360
|
+
yield
|
|
361
|
+
ensure
|
|
362
|
+
key.mutation_owner = nil if key.is_a?(Key)
|
|
363
|
+
end
|
|
364
|
+
|
|
365
|
+
def wrap_key(key)
|
|
366
|
+
return IdentityKey.new(key) if compare_keys_by_identity?
|
|
367
|
+
key = String.instance_method(:-@).bind_call(key) if String === key && !key.frozen?
|
|
368
|
+
Key.new(key)
|
|
369
|
+
end
|
|
370
|
+
|
|
371
|
+
def entries_snapshot
|
|
372
|
+
entries = []
|
|
373
|
+
@map.each_pair { |key, value| entries << [key.value, value.value] }
|
|
374
|
+
entries
|
|
375
|
+
end
|
|
376
|
+
|
|
377
|
+
def concurrent_get(key)
|
|
378
|
+
stored = @map.get(key)
|
|
379
|
+
stored&.value
|
|
380
|
+
end
|
|
381
|
+
|
|
382
|
+
def concurrent_store(key, value)
|
|
383
|
+
@map.put(key, Value.new(value))
|
|
384
|
+
value
|
|
385
|
+
end
|
|
386
|
+
|
|
387
|
+
def concurrent_swap(key, value)
|
|
388
|
+
previous = @map.put(key, Value.new(value))
|
|
389
|
+
previous&.value
|
|
390
|
+
end
|
|
391
|
+
|
|
392
|
+
def concurrent_key?(key) = @map.contains_key(key)
|
|
393
|
+
|
|
394
|
+
def concurrent_delete(key)
|
|
395
|
+
previous = @map.remove(key)
|
|
396
|
+
previous&.value
|
|
397
|
+
end
|
|
398
|
+
|
|
399
|
+
def native_operation(&)
|
|
400
|
+
entered = false
|
|
401
|
+
@state_mutex.synchronize do
|
|
402
|
+
reject_active_operation_reentry!
|
|
403
|
+
Thread.handle_interrupt(INTERRUPT_MASK) do
|
|
404
|
+
register_active_operation
|
|
405
|
+
entered = true
|
|
406
|
+
end
|
|
407
|
+
end
|
|
408
|
+
yield
|
|
409
|
+
ensure
|
|
410
|
+
Thread.handle_interrupt(INTERRUPT_MASK) { leave_operation } if entered
|
|
411
|
+
end
|
|
412
|
+
|
|
413
|
+
def validate_boolean(value, name)
|
|
414
|
+
return if identical?(value, true) || identical?(value, false)
|
|
415
|
+
raise ArgumentError, "#{name} must be true or false"
|
|
416
|
+
end
|
|
417
|
+
|
|
418
|
+
def leave_operation
|
|
419
|
+
@state_mutex.synchronize { unregister_active_operation }
|
|
420
|
+
end
|
|
421
|
+
|
|
422
|
+
def changed! = @change_signal.broadcast
|
|
423
|
+
|
|
424
|
+
def wait_for_value(key, expected, deadline, fallback, non_nil:)
|
|
425
|
+
wrapped = wrap_key(key)
|
|
426
|
+
while true
|
|
427
|
+
generation = @change_signal.generation
|
|
428
|
+
completed, current = with_key_operation(wrapped, deadline) do
|
|
429
|
+
native_operation { concurrent_get(wrapped) }
|
|
430
|
+
end
|
|
431
|
+
return fallback&.call unless completed
|
|
432
|
+
ready = non_nil ? !current.nil? : !values_equal?(current, expected)
|
|
433
|
+
return current if ready
|
|
434
|
+
return fallback&.call unless wait_for_signal(@change_signal, generation, deadline)
|
|
435
|
+
end
|
|
436
|
+
end
|
|
437
|
+
|
|
438
|
+
def wait_for_signal(signal, generation, deadline)
|
|
439
|
+
return signal.wait(generation) unless deadline
|
|
440
|
+
|
|
441
|
+
remaining = deadline - Clock.now
|
|
442
|
+
return false unless remaining.positive?
|
|
443
|
+
!signal.wait(generation, timeout: remaining).nil?
|
|
444
|
+
end
|
|
445
|
+
|
|
446
|
+
def timeout_deadline(timeout)
|
|
447
|
+
return if timeout.nil?
|
|
448
|
+
|
|
449
|
+
timeout = Float(timeout)
|
|
450
|
+
if !timeout.finite? || timeout.negative?
|
|
451
|
+
raise ArgumentError, "timeout must be a finite, non-negative number or nil"
|
|
452
|
+
end
|
|
453
|
+
Clock.now + timeout
|
|
454
|
+
end
|
|
455
|
+
|
|
456
|
+
def reject_active_operation_reentry!
|
|
457
|
+
return unless active_operation_owned_by?(Fiber.current)
|
|
458
|
+
|
|
459
|
+
raise ThreadError, "deadlock; recursive map access during an operation"
|
|
460
|
+
end
|
|
461
|
+
|
|
462
|
+
def register_active_operation
|
|
463
|
+
fiber = Fiber.current
|
|
464
|
+
thread = Thread.current
|
|
465
|
+
if @active_owners
|
|
466
|
+
@active_owners[fiber] = thread
|
|
467
|
+
elsif @active_owner_fiber
|
|
468
|
+
owners = {}.compare_by_identity
|
|
469
|
+
owners[@active_owner_fiber] = @active_owner_thread
|
|
470
|
+
owners[fiber] = thread
|
|
471
|
+
@active_owners = owners
|
|
472
|
+
@active_owner_fiber = @active_owner_thread = nil
|
|
473
|
+
else
|
|
474
|
+
@active_owner_thread = thread
|
|
475
|
+
@active_owner_fiber = fiber
|
|
476
|
+
end
|
|
477
|
+
end
|
|
478
|
+
|
|
479
|
+
def unregister_active_operation
|
|
480
|
+
if @active_owners
|
|
481
|
+
removed = @active_owners.delete(Fiber.current)
|
|
482
|
+
if @active_owners.one?
|
|
483
|
+
@active_owner_fiber, @active_owner_thread = @active_owners.first
|
|
484
|
+
@active_owners = nil
|
|
485
|
+
end
|
|
486
|
+
removed
|
|
487
|
+
elsif identical?(@active_owner_fiber, Fiber.current)
|
|
488
|
+
removed = @active_owner_thread
|
|
489
|
+
@active_owner_fiber = @active_owner_thread = nil
|
|
490
|
+
removed
|
|
491
|
+
end
|
|
492
|
+
end
|
|
493
|
+
|
|
494
|
+
def active_operation_owned_by?(fiber)
|
|
495
|
+
@active_owners ? @active_owners.key?(fiber) : identical?(@active_owner_fiber, fiber)
|
|
496
|
+
end
|
|
497
|
+
|
|
498
|
+
def values_equal?(left, right)
|
|
499
|
+
compare_values_by_identity? ? identical?(left, right) : left == right
|
|
500
|
+
end
|
|
501
|
+
|
|
502
|
+
def identical?(left, right) = BASIC_OBJECT_EQUAL_METHOD.bind_call(left, right)
|
|
503
|
+
end
|
|
504
|
+
end
|
|
505
|
+
end
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# shareable_constant_value: literal
|
|
3
|
+
# warn_indent: true
|
|
4
|
+
|
|
5
|
+
module Farce
|
|
6
|
+
module Internal
|
|
7
|
+
# RubyNumeric's final Java clone methods return self. Invoke Object's copy
|
|
8
|
+
# setup on a fresh Numeric to preserve Ruby's hooks and singleton methods.
|
|
9
|
+
module MutableNumericCopy
|
|
10
|
+
DUP_SETUP = org.jruby.RubyBasicObject.java_class.declared_method(
|
|
11
|
+
"dupSetup", org.jruby.runtime.ThreadContext.java_class, org.jruby.RubyBasicObject.java_class,
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
CLONE_SETUP = org.jruby.RubyBasicObject.java_class.declared_method(
|
|
15
|
+
"cloneSetup", org.jruby.runtime.ThreadContext.java_class, org.jruby.RubyBasicObject.java_class,
|
|
16
|
+
org.jruby.runtime.builtin.IRubyObject.java_class,
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
DUP_SETUP.accessible = CLONE_SETUP.accessible = true
|
|
20
|
+
|
|
21
|
+
private_constant :DUP_SETUP, :CLONE_SETUP
|
|
22
|
+
|
|
23
|
+
def dup = copy_numeric(DUP_SETUP)
|
|
24
|
+
|
|
25
|
+
def clone(freeze: nil)
|
|
26
|
+
unless freeze.nil? || freeze.equal?(true) || freeze.equal?(false)
|
|
27
|
+
raise ArgumentError, "unexpected value for freeze: #{freeze.class}"
|
|
28
|
+
end
|
|
29
|
+
copy_numeric(CLONE_SETUP, JRuby.reference(freeze))
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
private
|
|
33
|
+
|
|
34
|
+
def copy_numeric(method, *)
|
|
35
|
+
copy = JRuby.reference(self.class.allocate)
|
|
36
|
+
method.invoke(JRuby.reference(self), JRuby.runtime.current_context, copy, *)
|
|
37
|
+
rescue Java::JavaLangReflect::InvocationTargetException => e
|
|
38
|
+
raise e.cause
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# shareable_constant_value: literal
|
|
3
|
+
# warn_indent: true
|
|
4
|
+
|
|
5
|
+
require "java"
|
|
6
|
+
require "farce/engine/jvm/extension"
|
|
7
|
+
|
|
8
|
+
module Farce
|
|
9
|
+
# @!visibility private
|
|
10
|
+
module Internal # :nodoc: all
|
|
11
|
+
class Signal < JVMExtension::QueueSignal
|
|
12
|
+
NANOSECONDS = java.util.concurrent.TimeUnit::NANOSECONDS
|
|
13
|
+
READABLE = IO::READABLE
|
|
14
|
+
private_constant :NANOSECONDS, :READABLE
|
|
15
|
+
|
|
16
|
+
def initialize
|
|
17
|
+
super
|
|
18
|
+
@fiber_waiters = java.util.concurrent.ConcurrentHashMap.new
|
|
19
|
+
@thread_waiters = java.util.concurrent.atomic.AtomicInteger.new
|
|
20
|
+
Freeze.publish(self)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# JRuby-dev 10.1.2 cannot construct a Java subclass after a module is included.
|
|
24
|
+
# Keep these methods local until its reifier is fixed.
|
|
25
|
+
def freeze = raise(TypeError, "#{self.class} cannot be frozen")
|
|
26
|
+
def frozen? = false
|
|
27
|
+
|
|
28
|
+
def generation = phase
|
|
29
|
+
def num_waiting = @thread_waiters.get + @fiber_waiters.size
|
|
30
|
+
|
|
31
|
+
def broadcast
|
|
32
|
+
generation = broadcastThreads
|
|
33
|
+
|
|
34
|
+
# Publish the generation even with no waiters, so a waiter registering
|
|
35
|
+
# concurrently can detect the broadcast before it parks.
|
|
36
|
+
return generation if @fiber_waiters.empty?
|
|
37
|
+
|
|
38
|
+
@fiber_waiters.key_set.each do |writer|
|
|
39
|
+
writer.write_nonblock(".", exception: false)
|
|
40
|
+
rescue IOError, SystemCallError
|
|
41
|
+
nil
|
|
42
|
+
end
|
|
43
|
+
generation
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def wait(observed = nil, timeout: nil)
|
|
47
|
+
validate_generation(observed) unless observed.nil?
|
|
48
|
+
timeout = parse_timeout(timeout)
|
|
49
|
+
observed ||= generation
|
|
50
|
+
|
|
51
|
+
scheduler = Fiber.current_scheduler if Fiber.respond_to?(:current_scheduler)
|
|
52
|
+
# JRuby may run a later scheduled fiber on a backing thread where this is nil.
|
|
53
|
+
scheduler ||= Fiber.scheduler if !Fiber.current.blocking? && Fiber.respond_to?(:scheduler)
|
|
54
|
+
return wait_with_scheduler(scheduler, observed, timeout) { yield if block_given? } if scheduler
|
|
55
|
+
|
|
56
|
+
wait_with_phaser(observed, timeout) { yield if block_given? }
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
private
|
|
60
|
+
|
|
61
|
+
def validate_generation(generation)
|
|
62
|
+
raise TypeError, "generation must be an Integer" unless generation.is_a?(::Integer)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def parse_timeout(timeout)
|
|
66
|
+
return unless timeout
|
|
67
|
+
|
|
68
|
+
timeout = Float(timeout)
|
|
69
|
+
if !timeout.finite? || timeout.negative?
|
|
70
|
+
raise ArgumentError, "timeout must be a finite, non-negative number or nil"
|
|
71
|
+
end
|
|
72
|
+
timeout
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def wait_with_phaser(observed, timeout)
|
|
76
|
+
current = generation
|
|
77
|
+
return current unless current == observed
|
|
78
|
+
|
|
79
|
+
deadline = Clock.now + timeout if timeout
|
|
80
|
+
registered = false
|
|
81
|
+
begin
|
|
82
|
+
Thread.handle_interrupt(INTERRUPT_MASK) do
|
|
83
|
+
@thread_waiters.increment_and_get
|
|
84
|
+
registered = true
|
|
85
|
+
end
|
|
86
|
+
loop do
|
|
87
|
+
current = generation
|
|
88
|
+
return current unless current == observed
|
|
89
|
+
|
|
90
|
+
remaining = deadline - Clock.now if deadline
|
|
91
|
+
return yield if remaining && !remaining.positive?
|
|
92
|
+
|
|
93
|
+
# Java Phaser waits do not deliver Ruby cancellation until they return.
|
|
94
|
+
# Retry bounded slices without shortening the caller's deadline.
|
|
95
|
+
interval = LeaseWaiting.wait_interval(remaining)
|
|
96
|
+
begin
|
|
97
|
+
return await_advance_interruptibly(observed, (interval * 1_000_000_000).ceil, NANOSECONDS)
|
|
98
|
+
rescue Java::JavaUtilConcurrent::TimeoutException
|
|
99
|
+
# A positive Ruby sleep delivers :on_blocking interrupts too.
|
|
100
|
+
# JRuby optimizes sleep(0) without entering a blocking region.
|
|
101
|
+
sleep 0.000001
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
ensure
|
|
105
|
+
Thread.handle_interrupt(INTERRUPT_MASK) do
|
|
106
|
+
@thread_waiters.decrement_and_get if registered
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def wait_with_scheduler(scheduler, observed, timeout)
|
|
112
|
+
current = generation
|
|
113
|
+
return current unless current == observed
|
|
114
|
+
|
|
115
|
+
# The Java monitor closes the registration/commit race. Publish the
|
|
116
|
+
# registration flag under an interrupt mask so cancellation cannot
|
|
117
|
+
# leak a registration between the Java return and Ruby assignment.
|
|
118
|
+
registered = false
|
|
119
|
+
Thread.handle_interrupt(INTERRUPT_MASK) do
|
|
120
|
+
beginSchedulerWait
|
|
121
|
+
registered = true
|
|
122
|
+
end
|
|
123
|
+
deadline = Clock.now + timeout if timeout
|
|
124
|
+
reader, writer = IO.pipe
|
|
125
|
+
@fiber_waiters[writer] = true
|
|
126
|
+
|
|
127
|
+
loop do
|
|
128
|
+
current = generation
|
|
129
|
+
return current unless current == observed
|
|
130
|
+
|
|
131
|
+
remaining = deadline - Clock.now if deadline
|
|
132
|
+
return yield if remaining && !remaining.positive?
|
|
133
|
+
|
|
134
|
+
scheduler.io_wait(reader, READABLE, remaining)
|
|
135
|
+
end
|
|
136
|
+
ensure
|
|
137
|
+
Thread.handle_interrupt(INTERRUPT_MASK) do
|
|
138
|
+
@fiber_waiters.delete(writer) if writer
|
|
139
|
+
reader&.close
|
|
140
|
+
writer&.close
|
|
141
|
+
ensure
|
|
142
|
+
endSchedulerWait if registered
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
end
|