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,70 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# shareable_constant_value: literal
|
|
3
|
+
# warn_indent: true
|
|
4
|
+
|
|
5
|
+
module Farce
|
|
6
|
+
# A shareable priority queue with transfer modes for unshareable values.
|
|
7
|
+
class PriorityQueue < Farce::Abstract::PriorityQueue
|
|
8
|
+
include Internal::ManagedQueue
|
|
9
|
+
include Shareable::Unfreezable
|
|
10
|
+
|
|
11
|
+
# (see Farce::Abstract::PriorityQueue#initialize)
|
|
12
|
+
# @!macro modes
|
|
13
|
+
# @param mode [Symbol] the default mode for sending unshareable values
|
|
14
|
+
def initialize(capacity: nil, default_priority: 0, order: :ascending, mode: :copy, track_age: false)
|
|
15
|
+
@manager = ModeManager.new(mode:)
|
|
16
|
+
super(capacity:, default_priority:, order:, track_age:)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# (see Farce::Abstract::PriorityQueue#push)
|
|
20
|
+
# @!macro modes
|
|
21
|
+
# @param mode [Symbol] the default mode for sending unshareable values
|
|
22
|
+
def push(value, non_block = false, priority: default_priority, timeout: nil, mode: nil) # rubocop:disable Style/OptionalBooleanParameter
|
|
23
|
+
push_to_storage(priority, non_block, @manager.wrap(value, mode:), timeout:)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# (see Farce::Abstract::PriorityQueue#try_push)
|
|
27
|
+
# @!macro modes
|
|
28
|
+
# @param mode [Symbol] the default mode for sending unshareable values
|
|
29
|
+
def try_push(value, priority: default_priority, mode: nil)
|
|
30
|
+
return true if @queue.push(priority, @manager.wrap(value, mode:))
|
|
31
|
+
|
|
32
|
+
block_given? ? yield : false
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# (see Farce::Abstract::PriorityQueue#pop)
|
|
36
|
+
def pop(non_block = false, timeout: nil) # rubocop:disable Style/OptionalBooleanParameter
|
|
37
|
+
return try_pop { raise ThreadError, "queue empty" } if non_block
|
|
38
|
+
deadline = timeout_at(timeout) unless timeout.nil?
|
|
39
|
+
|
|
40
|
+
while true
|
|
41
|
+
empty = false
|
|
42
|
+
result = @reverse_order ? @queue.pop_last { empty = true } : @queue.pop { empty = true }
|
|
43
|
+
|
|
44
|
+
return @manager.unwrap(result) unless empty
|
|
45
|
+
remaining = remaining_timeout(deadline)
|
|
46
|
+
return block_given? ? yield : nil unless wait_pop(timeout: remaining)
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# (see Farce::Abstract::PriorityQueue#try_pop)
|
|
51
|
+
def try_pop
|
|
52
|
+
empty = false
|
|
53
|
+
result = @reverse_order ? @queue.pop_last { empty = true } : @queue.pop { empty = true }
|
|
54
|
+
return @manager.unwrap(result) unless empty
|
|
55
|
+
yield if block_given?
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# (see Farce::Abstract::PriorityQueue#peek)
|
|
59
|
+
# @note Peeking at a moved value claims it for this Ractor while leaving it queued.
|
|
60
|
+
def peek
|
|
61
|
+
empty = false
|
|
62
|
+
result = if @reverse_order
|
|
63
|
+
@queue.peek_last { empty = true }
|
|
64
|
+
else
|
|
65
|
+
@queue.peek { empty = true }
|
|
66
|
+
end
|
|
67
|
+
empty ? (block_given? ? yield : nil) : @manager.unwrap(result)
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# shareable_constant_value: literal
|
|
3
|
+
# warn_indent: true
|
|
4
|
+
|
|
5
|
+
module Farce
|
|
6
|
+
class Proxy < BasicObject
|
|
7
|
+
# Creates a new register for defining wrapper methods for proxy classes.
|
|
8
|
+
# Helpful to isolate proxy definitions that shouldn't be activated globally.
|
|
9
|
+
#
|
|
10
|
+
# @example
|
|
11
|
+
# register = Farce::Proxy::Register.new
|
|
12
|
+
#
|
|
13
|
+
# register.define(Hash) do
|
|
14
|
+
# # Don't send the fetch block to the proxied object
|
|
15
|
+
# # Allows blocks that aren't ractor-safe
|
|
16
|
+
# def fetch(*args)
|
|
17
|
+
# return super unless block_given?
|
|
18
|
+
# undefined = Object.new.freeze
|
|
19
|
+
# result = super(args.first, undefined)
|
|
20
|
+
# undefined.equal?(result) ? yield(args.first) : result
|
|
21
|
+
# end
|
|
22
|
+
# end
|
|
23
|
+
#
|
|
24
|
+
# default_proxy = Farce::Proxy.new({})
|
|
25
|
+
# custom_proxy = Farce::Proxy.new({}, register:)
|
|
26
|
+
#
|
|
27
|
+
# Ractor.new(default_proxy, custom_proxy) do |default_proxy, custom_proxy|
|
|
28
|
+
# default_proxy.fetch(:key) { Ractor.main? } # => true
|
|
29
|
+
# custom_proxy.fetch(:key) { Ractor.main? } # => false
|
|
30
|
+
# end
|
|
31
|
+
class Register
|
|
32
|
+
include Internal::MarshalSupport::Reject
|
|
33
|
+
|
|
34
|
+
# Mixin extending the modules created by {Register#define}
|
|
35
|
+
module Definition
|
|
36
|
+
# Same as `Module#define_method`, but makes Proc definitions shareable across Ractors.
|
|
37
|
+
#
|
|
38
|
+
# @overload define_method(name)
|
|
39
|
+
# @param name [Symbol, String] the name of the method to define
|
|
40
|
+
# @yield block to define the method body
|
|
41
|
+
#
|
|
42
|
+
# @overload define_method(name, definition)
|
|
43
|
+
# @param name [Symbol, String] the name of the method to define
|
|
44
|
+
# @param definition [Proc, Method, UnboundMethod] the method body
|
|
45
|
+
def define_method(name, definition = UNDEFINED, &block)
|
|
46
|
+
if UNDEFINED.equal?(definition)
|
|
47
|
+
return super(name) unless block
|
|
48
|
+
definition = block
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
definition = Ractor.shareable_lambda(&definition) if definition.is_a?(Proc) && !Ractor.shareable?(definition)
|
|
52
|
+
|
|
53
|
+
super(name, definition)
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
include Farce::Shareable::Tracked
|
|
58
|
+
|
|
59
|
+
# @api private
|
|
60
|
+
attr_reader :inner_mirror, :outer_mirror
|
|
61
|
+
|
|
62
|
+
def initialize
|
|
63
|
+
@inner_mirror = ClassMirror.new(Wrapper::Inner)
|
|
64
|
+
@outer_mirror = ClassMirror.new(Wrapper::Outer)
|
|
65
|
+
super
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# Created a new module that will be included in the proxy for the specified class and layer.
|
|
69
|
+
# You can define methods using `def` within the block.
|
|
70
|
+
# These methods may call `super` (which will ultimately delegate to the proxied object).
|
|
71
|
+
#
|
|
72
|
+
# The inner layer is invoked inside the Ractor the proxy resides in, while the outer layer is
|
|
73
|
+
# invoked in the caller's Ractor.
|
|
74
|
+
#
|
|
75
|
+
# The wrapper objects do not have to be Ractor-shareable, so you may use instance variables for memoization.
|
|
76
|
+
#
|
|
77
|
+
# @param klass [Class] the class to define wrapper methods for
|
|
78
|
+
# @param layer [Symbol] the layer of the proxy to define methods for (:inner or :outer)
|
|
79
|
+
# @yield block to define wrapper methods for the proxied object
|
|
80
|
+
# @yieldreceiver [Module] the module representing the wrapper layer, extended by {Definition}
|
|
81
|
+
def define(klass, layer: :outer, &)
|
|
82
|
+
check_frozen!
|
|
83
|
+
mirror_for(layer).define(klass) do
|
|
84
|
+
extend Definition
|
|
85
|
+
|
|
86
|
+
module_eval(&)
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
private
|
|
91
|
+
|
|
92
|
+
def mirror_for(layer)
|
|
93
|
+
case layer
|
|
94
|
+
when :inner then @inner_mirror
|
|
95
|
+
when :outer then @outer_mirror
|
|
96
|
+
else raise ArgumentError, "Unknown layer: #{layer}"
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
end
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# shareable_constant_value: literal
|
|
3
|
+
# warn_indent: true
|
|
4
|
+
|
|
5
|
+
require "farce/envelope"
|
|
6
|
+
|
|
7
|
+
module Farce
|
|
8
|
+
class Proxy < BasicObject
|
|
9
|
+
# Creates a new register for defining wrapper methods for proxy classes.
|
|
10
|
+
# @api private
|
|
11
|
+
class Supervisor # :nodoc: all
|
|
12
|
+
NilValue = Object.new.freeze
|
|
13
|
+
Request = Data.define(:success, :result, :args, :kwargs, :block)
|
|
14
|
+
|
|
15
|
+
attr_reader :mode_manager, :register
|
|
16
|
+
|
|
17
|
+
def initialize(register, scheduler, reference)
|
|
18
|
+
@mode_manager = ModeManager.new(register:)
|
|
19
|
+
@queue = Strict::Queue.new(capacity: 1)
|
|
20
|
+
@pending = Strict::Map.new(compare_by_identity: true)
|
|
21
|
+
@register = register
|
|
22
|
+
@scheduler = scheduler
|
|
23
|
+
@reference = reference
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def run(object)
|
|
27
|
+
@owner = Internal::ProxyOwner.current
|
|
28
|
+
@wrapper_class = @register.outer_mirror[::Kernel.instance_method(:class).bind_call(object)]
|
|
29
|
+
Ractor.make_shareable(self)
|
|
30
|
+
@owner.register(self)
|
|
31
|
+
@scheduler.schedule(mode: :local) { run!(object) }
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Sends a method to the outer wrapper, which then will most likely trigger {#dispatch},
|
|
35
|
+
# which will send it over the queue to the inner wrapper.
|
|
36
|
+
def send(...)
|
|
37
|
+
check_owner!
|
|
38
|
+
wrapper.__send__(...)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Dispatches the method call to the inner wrapper via the queue and waits for the result.
|
|
42
|
+
# Called by Wrapper::Outer#method_missing
|
|
43
|
+
def dispatch(*args, **kwargs, &)
|
|
44
|
+
args.map! { @mode_manager.wrap(it) }
|
|
45
|
+
kwargs.transform_values! { @mode_manager.wrap(it) }
|
|
46
|
+
|
|
47
|
+
Ractor.make_shareable(kwargs)
|
|
48
|
+
Ractor.make_shareable(args)
|
|
49
|
+
|
|
50
|
+
block = Ractor.shareable_proc(&) if block_given?
|
|
51
|
+
request = Request.new(Internal::Flag.new, Strict::Queue.new(capacity: 1), args, kwargs, block)
|
|
52
|
+
|
|
53
|
+
begin
|
|
54
|
+
@pending[request.result] = true
|
|
55
|
+
# Register before checking closure so shutdown cannot miss this reply.
|
|
56
|
+
check_owner!
|
|
57
|
+
@queue.push(request)
|
|
58
|
+
result = request.result.pop
|
|
59
|
+
rescue ClosedQueueError
|
|
60
|
+
raise Ractor::RemoteError, "proxy owner has stopped"
|
|
61
|
+
ensure
|
|
62
|
+
@pending.delete(request.result)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
value = @mode_manager.unwrap(result)
|
|
66
|
+
value = nil if NilValue.equal?(value)
|
|
67
|
+
|
|
68
|
+
return value if request.success.value
|
|
69
|
+
value = Ractor::RemoteError.new(value) if value.is_a?(String)
|
|
70
|
+
raise value
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def method_defined?(...)
|
|
74
|
+
check_owner!
|
|
75
|
+
wrapper.respond_to?(...)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def stop
|
|
79
|
+
@queue.close
|
|
80
|
+
@pending.each_key(&:close)
|
|
81
|
+
@owner.unregister(self)
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
private
|
|
85
|
+
|
|
86
|
+
def check_owner!
|
|
87
|
+
return if @owner.alive? && !@queue.closed?
|
|
88
|
+
raise Ractor::RemoteError, "proxy owner has stopped"
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def wrapper
|
|
92
|
+
token = @reference.value
|
|
93
|
+
Internal::Storage.store_if_absent(token) { @wrapper_class.new(self) }
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def deliver(request, result)
|
|
97
|
+
request.result.push(result)
|
|
98
|
+
rescue ClosedQueueError
|
|
99
|
+
# The owner may have stopped while the target method was running.
|
|
100
|
+
nil
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def run!(object)
|
|
104
|
+
inner_wrapper = @register.inner_mirror[::Kernel.instance_method(:class).bind_call(object)].new(object, self)
|
|
105
|
+
until @queue.closed?
|
|
106
|
+
@queue.seal unless @reference.alive?
|
|
107
|
+
begin
|
|
108
|
+
request = @queue.pop(timeout: 0.1)
|
|
109
|
+
rescue ClosedQueueError
|
|
110
|
+
break
|
|
111
|
+
end
|
|
112
|
+
next unless request
|
|
113
|
+
begin
|
|
114
|
+
args = request.args.map { @mode_manager.unwrap(it) }
|
|
115
|
+
kwargs = request.kwargs.transform_values { @mode_manager.unwrap(it) }
|
|
116
|
+
result = inner_wrapper.__send__(*args, **kwargs, &request.block)
|
|
117
|
+
request.success.value = true
|
|
118
|
+
result = SELF if object.equal?(result)
|
|
119
|
+
result = nil.equal?(result) ? NilValue : @mode_manager.wrap(result)
|
|
120
|
+
rescue Exception => e # rubocop:disable Lint/RescueException
|
|
121
|
+
request.success.value = false
|
|
122
|
+
begin
|
|
123
|
+
result = @mode_manager.wrap(e)
|
|
124
|
+
rescue StandardError
|
|
125
|
+
result = -"#{e.class}: #{e.message}"
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
deliver(request, result)
|
|
129
|
+
end
|
|
130
|
+
ensure
|
|
131
|
+
stop
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
private_constant :Supervisor
|
|
136
|
+
end
|
|
137
|
+
end
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# shareable_constant_value: literal
|
|
3
|
+
# warn_indent: true
|
|
4
|
+
|
|
5
|
+
module Farce
|
|
6
|
+
class Proxy < BasicObject
|
|
7
|
+
# @api private
|
|
8
|
+
class Wrapper < ::BasicObject # :nodoc: all
|
|
9
|
+
include ::Farce.const_get(:Internal)::Delegation
|
|
10
|
+
|
|
11
|
+
class Inner < self
|
|
12
|
+
def initialize(object, ...)
|
|
13
|
+
@__object__ = object
|
|
14
|
+
super(...)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
private
|
|
18
|
+
|
|
19
|
+
# Dispatches method calls to @__object__
|
|
20
|
+
def method_missing(...) = @__object__.__send__(...)
|
|
21
|
+
def respond_to_missing?(...) = @__object__.respond_to?(...)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
class Outer < self
|
|
25
|
+
private
|
|
26
|
+
|
|
27
|
+
# Dispatches method calls to be processed by the Ractor the proxied object resides in.
|
|
28
|
+
def method_missing(...) = @__supervisor__.dispatch(...)
|
|
29
|
+
def respond_to_missing?(...) = @__supervisor__.dispatch(:respond_to?, ...)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
define_method(:respond_to?, ::Kernel.instance_method(:respond_to?))
|
|
33
|
+
def initialize(supervisor) = @__supervisor__ = supervisor
|
|
34
|
+
|
|
35
|
+
private
|
|
36
|
+
|
|
37
|
+
def __wrap__(...) = @__supervisor__.mode_manager.wrap(...)
|
|
38
|
+
def __unwrap__(...) = @__supervisor__.mode_manager.unwrap(...)
|
|
39
|
+
|
|
40
|
+
::Farce::MODES.each do |mode|
|
|
41
|
+
class_eval <<~RUBY, __FILE__, __LINE__ + 1
|
|
42
|
+
private def __#{mode}__(object) = __wrap__(object, mode: :#{mode})
|
|
43
|
+
RUBY
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
private_constant :Wrapper
|
|
48
|
+
end
|
|
49
|
+
end
|
data/lib/farce/proxy.rb
ADDED
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# shareable_constant_value: literal
|
|
3
|
+
# warn_indent: true
|
|
4
|
+
|
|
5
|
+
module Farce
|
|
6
|
+
# A proxy mimics the API of another object, executing method calls within the Ractor that created it.
|
|
7
|
+
# This is an easy way to have drop-in replacements for objects that cannot be shared across Ractors.
|
|
8
|
+
# Delegated calls raise Ractor::RemoteError after the owner exits.
|
|
9
|
+
#
|
|
10
|
+
# ```ruby
|
|
11
|
+
# # Mutable arrays aren't Ractor-shareable
|
|
12
|
+
# array = []
|
|
13
|
+
# proxied = []
|
|
14
|
+
# proxy = Farce::Proxy.new(proxied)
|
|
15
|
+
#
|
|
16
|
+
# Farce::Ractor.new(array, proxy) do |*list|
|
|
17
|
+
# list.each { it << 42 }
|
|
18
|
+
# end.join
|
|
19
|
+
#
|
|
20
|
+
# # The array got copied instead of being modified in place
|
|
21
|
+
# array # => []
|
|
22
|
+
#
|
|
23
|
+
# # The proxy didn't get copied
|
|
24
|
+
# proxied # => [42]
|
|
25
|
+
# ```
|
|
26
|
+
#
|
|
27
|
+
# ## Customizing Proxy Behavior
|
|
28
|
+
#
|
|
29
|
+
# Imagine we have an unshareable class:
|
|
30
|
+
#
|
|
31
|
+
# ```ruby
|
|
32
|
+
# class MyClass
|
|
33
|
+
# include Farce::Unshareable
|
|
34
|
+
# attr_reader :record
|
|
35
|
+
#
|
|
36
|
+
# def initialize = @record = []
|
|
37
|
+
# def <<(value) = @record << value
|
|
38
|
+
# end
|
|
39
|
+
# ```
|
|
40
|
+
#
|
|
41
|
+
# We can use a proxy to still allow cross-ractor access:
|
|
42
|
+
#
|
|
43
|
+
# ```ruby
|
|
44
|
+
# my_instance = MyClass.new
|
|
45
|
+
# my_proxy = Farce::Proxy.create(my_instance)
|
|
46
|
+
#
|
|
47
|
+
# Ractor.new(my_proxy) { it << 42 }.join
|
|
48
|
+
#
|
|
49
|
+
# my_instance.record # => [42]
|
|
50
|
+
# ```
|
|
51
|
+
#
|
|
52
|
+
# The above example has one issue:
|
|
53
|
+
# We cannot chain the calls, because `<<` returns an array, which actually gets copied.
|
|
54
|
+
#
|
|
55
|
+
# ```ruby
|
|
56
|
+
# my_instance = MyClass.new
|
|
57
|
+
# my_proxy = Farce::Proxy.create(my_instance)
|
|
58
|
+
#
|
|
59
|
+
# Ractor.new(my_proxy) { it << 42 << 43 }.join
|
|
60
|
+
#
|
|
61
|
+
# my_instance.record # => [42]
|
|
62
|
+
# ```
|
|
63
|
+
#
|
|
64
|
+
# We could easily fix this inside `MyClass` by returning `self` from `<<`, but maybe we don't actually control
|
|
65
|
+
# `MyClass`? In that case, we can add a proxy definition:
|
|
66
|
+
#
|
|
67
|
+
# ```ruby
|
|
68
|
+
# # The "inner" layer runs in the same ractor that the proxied object lives in.
|
|
69
|
+
# Farce::Proxy.define(MyClass, layer: :inner) do
|
|
70
|
+
# # Also proxy the value returned by `<<`
|
|
71
|
+
# def <<(...) = __proxy__(super)
|
|
72
|
+
# end
|
|
73
|
+
#
|
|
74
|
+
# my_instance = MyClass.new
|
|
75
|
+
# my_proxy = Farce::Proxy.create(my_instance)
|
|
76
|
+
#
|
|
77
|
+
# Ractor.new(my_proxy) { it << 42 << 43 }.join
|
|
78
|
+
#
|
|
79
|
+
# my_instance.record # => [42, 43]
|
|
80
|
+
# ```
|
|
81
|
+
#
|
|
82
|
+
# ### Advanced customization
|
|
83
|
+
#
|
|
84
|
+
# The proxy wrappers define methods for all supported {file:docs/modes.md modes}. Use the outer wrapper for argument
|
|
85
|
+
# handling and memoization, use the inner wrapper for handling return values:
|
|
86
|
+
#
|
|
87
|
+
# ```ruby
|
|
88
|
+
# # This class cannot easily be shared across Ractors, as it uses a Mutex and a mutable array.
|
|
89
|
+
# class MyClass
|
|
90
|
+
# attr_reader :capacity
|
|
91
|
+
#
|
|
92
|
+
# def initialize(capacity = 100)
|
|
93
|
+
# @capacity = Integer(capacity)
|
|
94
|
+
# @record = []
|
|
95
|
+
# @mutex = Mutex.new
|
|
96
|
+
# end
|
|
97
|
+
#
|
|
98
|
+
# def add(value)
|
|
99
|
+
# @mutex.synchronize do
|
|
100
|
+
# return false if @record.size >= @capacity
|
|
101
|
+
# @record << value
|
|
102
|
+
# true
|
|
103
|
+
# end
|
|
104
|
+
# end
|
|
105
|
+
#
|
|
106
|
+
# def to_a = @record.dup
|
|
107
|
+
# end
|
|
108
|
+
#
|
|
109
|
+
# # Lets define some rules
|
|
110
|
+
#
|
|
111
|
+
# Farce::Proxy.define(MyClass, layer: :outer) do
|
|
112
|
+
# # Capacity doesn't change, so we can memoize it.
|
|
113
|
+
# def capacity = @capacity ||= super
|
|
114
|
+
#
|
|
115
|
+
# # Maybe a common use-case is adding MyClass instances to MyClass instances?
|
|
116
|
+
# # If so, maybe we want to proxy these as well? Make everything else shareable.
|
|
117
|
+
# def add(value)
|
|
118
|
+
# value = __proxy__(value) if value.is_a? MyClass
|
|
119
|
+
# super __make_shareable__(value)
|
|
120
|
+
# end
|
|
121
|
+
# end
|
|
122
|
+
#
|
|
123
|
+
# Farce::Proxy.define(MyClass, layer: :inner) do
|
|
124
|
+
# # to_a already creates a new array every time, we don't have to copy it again
|
|
125
|
+
# def to_a = __move__(super)
|
|
126
|
+
# end
|
|
127
|
+
# ```
|
|
128
|
+
#
|
|
129
|
+
# Proxies should mimic the original object's interface as closely as possible, but nothing keeps you from adding
|
|
130
|
+
# custom methods:
|
|
131
|
+
#
|
|
132
|
+
# ```ruby
|
|
133
|
+
# class MyClass
|
|
134
|
+
# def is_proxy? = false
|
|
135
|
+
# end
|
|
136
|
+
#
|
|
137
|
+
# Farce::Proxy.define(MyClass) do
|
|
138
|
+
# def is_proxy? = true
|
|
139
|
+
# def proxy_method = "Hi from the proxy!!!"
|
|
140
|
+
# end
|
|
141
|
+
#
|
|
142
|
+
# my_instance = MyClass.new
|
|
143
|
+
# my_proxy = Farce::Proxy.create(my_instance)
|
|
144
|
+
#
|
|
145
|
+
# my_instance.is_proxy? # => false
|
|
146
|
+
# my_proxy.is_proxy? # => true
|
|
147
|
+
#
|
|
148
|
+
# my_proxy.proxy_method if my_proxy.is_proxy? # => "Hi from the proxy!!!"
|
|
149
|
+
# ```
|
|
150
|
+
class Proxy < BasicObject
|
|
151
|
+
include ::Farce.const_get(:Internal)::Autoloads
|
|
152
|
+
include ::Farce.const_get(:Internal)::Delegation
|
|
153
|
+
|
|
154
|
+
REGISTER = Register.new
|
|
155
|
+
SELF = ::Object.new.freeze
|
|
156
|
+
UNDEFINED = ::Farce.const_get(:UNDEFINED)
|
|
157
|
+
ProxyOwner = ::Farce.const_get(:Internal)::ProxyOwner
|
|
158
|
+
|
|
159
|
+
private_constant :Supervisor, :Wrapper, :REGISTER, :SELF, :UNDEFINED, :ProxyOwner
|
|
160
|
+
|
|
161
|
+
define_method(:respond_to?, ::Kernel.instance_method(:respond_to?))
|
|
162
|
+
define_method(:__freeze__, ::Kernel.instance_method(:freeze))
|
|
163
|
+
private :__freeze__
|
|
164
|
+
|
|
165
|
+
# @return [Farce::Proxy::Register] the default register used by the proxy class
|
|
166
|
+
def self.default_register = REGISTER
|
|
167
|
+
|
|
168
|
+
# @overload define(klass, layer: :outer)
|
|
169
|
+
# (see Farce::Proxy::Register#define)
|
|
170
|
+
# @see .default_register
|
|
171
|
+
# @see Farce::Proxy::Register#define
|
|
172
|
+
def self.define(...) = REGISTER.define(...)
|
|
173
|
+
|
|
174
|
+
# @overload create(object, register: nil, scheduler: nil)
|
|
175
|
+
# Creates a new proxy for the given object if it is not Ractor-shareable.
|
|
176
|
+
# Returns the given object otherwise.
|
|
177
|
+
# @param (see Farce::Proxy#initialize)
|
|
178
|
+
#
|
|
179
|
+
# @overload create(register: nil)
|
|
180
|
+
# Creates a new Ractor, runs the given block within it, and returns the result.
|
|
181
|
+
# If the result is not Ractor-shareable, it will be proxied.
|
|
182
|
+
# The new Ractor stops after its proxies are collected.
|
|
183
|
+
#
|
|
184
|
+
# @yield The block to be executed within the new Ractor.
|
|
185
|
+
# @yieldreceiver [nil]
|
|
186
|
+
#
|
|
187
|
+
# @overload create(scheduler:, register: nil)
|
|
188
|
+
# Schedules the given block to be executed by the specified scheduler.
|
|
189
|
+
# Returns its result. If the result is not Ractor-shareable, it will be proxied.
|
|
190
|
+
#
|
|
191
|
+
# @yield The block to be executed by the scheduler.
|
|
192
|
+
# @yieldreceiver [nil]
|
|
193
|
+
#
|
|
194
|
+
# @return [BasicObject, Farce::Proxy] the proxied object or the original object if it is Ractor-shareable
|
|
195
|
+
def self.create(object = UNDEFINED, register: nil, scheduler: nil, &)
|
|
196
|
+
if UNDEFINED.equal?(object)
|
|
197
|
+
initializer = ::Farce::Strict::Atom.new(::Farce::Ractor.shareable_proc(&))
|
|
198
|
+
success = ::Farce::Strict::Atom.new
|
|
199
|
+
result = ::Farce::Strict::Atom.new
|
|
200
|
+
owned = scheduler.nil?
|
|
201
|
+
scheduler ||= owner_scheduler
|
|
202
|
+
|
|
203
|
+
schedule_initializer(scheduler, initializer, register, success, result, owned)
|
|
204
|
+
|
|
205
|
+
success.wait_until_non_nil
|
|
206
|
+
return result.swap(nil) if success.value
|
|
207
|
+
raise ::Farce::Ractor::RemoteError, result.swap(nil)
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
return object if Ractor.shareable?(object)
|
|
211
|
+
new(object, register:, scheduler:)
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
def self.owner_scheduler = ::Farce::ThreadScheduler.new { |*args, &block| ::Farce::Ractor.new(*args, &block) }
|
|
215
|
+
private_class_method :owner_scheduler
|
|
216
|
+
|
|
217
|
+
def self.schedule_initializer(scheduler, *)
|
|
218
|
+
scheduler.schedule(*) do |initializer, register, success, result, owned|
|
|
219
|
+
local_scheduler = ::Farce::Proxy.__send__(:initialize_proxy, initializer, register, success, result, owned)
|
|
220
|
+
result = nil
|
|
221
|
+
ProxyOwner.drain_scheduler(local_scheduler) if local_scheduler
|
|
222
|
+
rescue ::StandardError => e
|
|
223
|
+
raise unless result
|
|
224
|
+
result.value = -"#{e.class}: #{e.message}"
|
|
225
|
+
success.value = false
|
|
226
|
+
ensure
|
|
227
|
+
local_scheduler&.close
|
|
228
|
+
end
|
|
229
|
+
end
|
|
230
|
+
private_class_method :schedule_initializer
|
|
231
|
+
|
|
232
|
+
# Keep initializer and proxy temporaries out of the frame that drains the owner.
|
|
233
|
+
def self.initialize_proxy(initializer, register, success, result, owned)
|
|
234
|
+
initialized = initializer.swap(nil).call
|
|
235
|
+
local_scheduler = nil
|
|
236
|
+
local_scheduler = ProxyOwner.create_scheduler if owned && !::Farce::Ractor.shareable?(initialized)
|
|
237
|
+
created = ::Farce::Proxy.create(initialized, register:, scheduler: local_scheduler)
|
|
238
|
+
# Resolve shareability before the atom setter. Its native traversal scratch
|
|
239
|
+
# state can otherwise retain the proxy while the owner drains its scheduler.
|
|
240
|
+
::Farce::Ractor.make_shareable(created)
|
|
241
|
+
result.value = created
|
|
242
|
+
success.value = true
|
|
243
|
+
local_scheduler
|
|
244
|
+
rescue ::Exception # rubocop:disable Lint/RescueException -- Close the owned scheduler before propagating initialization failures.
|
|
245
|
+
local_scheduler&.close
|
|
246
|
+
raise
|
|
247
|
+
end
|
|
248
|
+
private_class_method :initialize_proxy
|
|
249
|
+
|
|
250
|
+
# @param object [BasicObject] the object to be managed by the proxy
|
|
251
|
+
# @param register [Farce::Proxy::Register, nil] the register to use
|
|
252
|
+
# @param scheduler [Farce::Abstract::Scheduler, nil] the scheduler to use
|
|
253
|
+
def initialize(object, register: nil, scheduler: nil)
|
|
254
|
+
@token = ::Object.new.freeze # cross-ractor finalizers don't work reliably across Ruby versions
|
|
255
|
+
@supervisor = Supervisor.new(register || REGISTER, scheduler || ::Farce, ::Farce::WeakValue.new(@token))
|
|
256
|
+
@proxy_class = ::Kernel.instance_method(:class).bind_call(self)
|
|
257
|
+
@object_class = ::Kernel.instance_method(:class).bind_call(object)
|
|
258
|
+
@object_id = ::BasicObject.instance_method(:__id__).bind_call(object)
|
|
259
|
+
__freeze__
|
|
260
|
+
@supervisor.run(object)
|
|
261
|
+
end
|
|
262
|
+
|
|
263
|
+
# @return [true] proxies can be shared even when their targets cannot
|
|
264
|
+
def ractor_shareable? = true
|
|
265
|
+
|
|
266
|
+
# @note Does not call `#inspect` on the proxied object itself, to avoid Ractor round-trips
|
|
267
|
+
# @return [String] a string representation of the proxy object
|
|
268
|
+
def inspect = "#<#{@proxy_class.inspect} object=#<#{@object_class.inspect}:0x#{@object_id.to_s(16)}>>"
|
|
269
|
+
|
|
270
|
+
# @api private
|
|
271
|
+
def pretty_print(pp)
|
|
272
|
+
pp.group(1, "#<#{@proxy_class.inspect} ", ">") do
|
|
273
|
+
pp.text "object="
|
|
274
|
+
pp.text "#<#{@object_class.inspect}:0x#{@object_id.to_s(16)}>"
|
|
275
|
+
end
|
|
276
|
+
end
|
|
277
|
+
|
|
278
|
+
private
|
|
279
|
+
|
|
280
|
+
# @api private
|
|
281
|
+
def marshal_dump = ::Kernel.raise(::TypeError, "Farce::Proxy cannot be marshaled")
|
|
282
|
+
|
|
283
|
+
# Dispatches method calls to be processed by the Ractor the proxied object resides in.
|
|
284
|
+
def method_missing(...)
|
|
285
|
+
result = @supervisor.send(...)
|
|
286
|
+
SELF.equal?(result) ? self : result
|
|
287
|
+
end
|
|
288
|
+
|
|
289
|
+
def respond_to_missing?(...) = @supervisor.method_defined?(...)
|
|
290
|
+
end
|
|
291
|
+
end
|