farce 0.0.1.alpha2-x86_64-linux-gnu
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/CODE_OF_CONDUCT.md +26 -0
- data/CONTRIBUTING.md +71 -0
- data/MIT-LICENSE +20 -0
- data/README.md +1524 -0
- data/SECURITY.md +10 -0
- data/docs/benchmarks.md +185 -0
- data/docs/gems/dry-types.md +290 -0
- data/docs/gems/msgpack.md +70 -0
- data/docs/gems/ractor-shim.md +63 -0
- data/docs/modes.md +628 -0
- data/docs/scopes.md +649 -0
- data/docs/variants.md +346 -0
- data/ext/ext_helper.rb +20 -0
- data/ext/farce/README.md +21 -0
- data/ext/farce/atom.c +1023 -0
- data/ext/farce/bounded_map.c +1683 -0
- data/ext/farce/containers.h +77 -0
- data/ext/farce/counter.c +399 -0
- data/ext/farce/darwin.c +100 -0
- data/ext/farce/depend +12 -0
- data/ext/farce/dict.c +1523 -0
- data/ext/farce/dict.h +152 -0
- data/ext/farce/drivers.c +237 -0
- data/ext/farce/exchanger.c +299 -0
- data/ext/farce/extconf.rb +99 -0
- data/ext/farce/farce.c +359 -0
- data/ext/farce/flag.c +288 -0
- data/ext/farce/io.c +338 -0
- data/ext/farce/lock.c +510 -0
- data/ext/farce/map.c +2240 -0
- data/ext/farce/priority_queue.c +2056 -0
- data/ext/farce/queue.c +1059 -0
- data/ext/farce/reactor.c +820 -0
- data/ext/farce/reactor.h +103 -0
- data/ext/farce/shareable.h +31 -0
- data/ext/farce/signal.c +350 -0
- data/ext/farce/transaction.c +354 -0
- data/ext/farce/transaction.h +40 -0
- data/ext/farce/tree_map.c +1953 -0
- data/ext/farce/trie.c +2020 -0
- data/ext/farce/unshareable.c +155 -0
- data/ext/farce/unshared_io_pool.h +234 -0
- data/ext/farce/unshared_signal.c +263 -0
- data/ext/farce/unshared_wait.h +193 -0
- data/ext/farce/unsupported.c +6 -0
- data/ext/farce/vector.c +1195 -0
- data/ext/farce/weak_map.c +1714 -0
- data/ext/java/org/farce/BoundedMap.java +394 -0
- data/ext/java/org/farce/FiberScheduler.java +141 -0
- data/ext/java/org/farce/PriorityKey.java +45 -0
- data/ext/java/org/farce/PriorityQueue.java +373 -0
- data/ext/java/org/farce/QueueSignal.java +28 -0
- data/ext/rebind/README.md +14 -0
- data/ext/rebind/extconf.rb +10 -0
- data/ext/rebind/rebind.c +186 -0
- data/lib/farce/_yard/internal.rb +13 -0
- data/lib/farce/_yard/macros.rb +61 -0
- data/lib/farce/_yard/ractor.rb +46 -0
- data/lib/farce/abstract/atom.rb +186 -0
- data/lib/farce/abstract/bounded_map.rb +232 -0
- data/lib/farce/abstract/collection.rb +151 -0
- data/lib/farce/abstract/concurrent_map.rb +381 -0
- data/lib/farce/abstract/counter.rb +193 -0
- data/lib/farce/abstract/duplicable_map.rb +229 -0
- data/lib/farce/abstract/exchanger.rb +26 -0
- data/lib/farce/abstract/flag.rb +71 -0
- data/lib/farce/abstract/lazy.rb +115 -0
- data/lib/farce/abstract/lease.rb +104 -0
- data/lib/farce/abstract/lease_map.rb +261 -0
- data/lib/farce/abstract/lease_pool.rb +91 -0
- data/lib/farce/abstract/lfu_map.rb +20 -0
- data/lib/farce/abstract/lru_map.rb +25 -0
- data/lib/farce/abstract/map.rb +345 -0
- data/lib/farce/abstract/molecule.rb +245 -0
- data/lib/farce/abstract/port.rb +74 -0
- data/lib/farce/abstract/priority_queue.rb +117 -0
- data/lib/farce/abstract/queue.rb +269 -0
- data/lib/farce/abstract/scheduler.rb +111 -0
- data/lib/farce/abstract/set.rb +910 -0
- data/lib/farce/abstract/sorted_set.rb +172 -0
- data/lib/farce/abstract/timer_queue.rb +136 -0
- data/lib/farce/abstract/tree_map.rb +269 -0
- data/lib/farce/abstract/value.rb +68 -0
- data/lib/farce/abstract/vector.rb +1126 -0
- data/lib/farce/abstract/weak_atom.rb +32 -0
- data/lib/farce/abstract/weak_key_map.rb +12 -0
- data/lib/farce/abstract/weak_map.rb +13 -0
- data/lib/farce/abstract/weak_set.rb +12 -0
- data/lib/farce/abstract/weak_value_map.rb +12 -0
- data/lib/farce/abstract.rb +17 -0
- data/lib/farce/atom.rb +293 -0
- data/lib/farce/class_mirror.rb +87 -0
- data/lib/farce/clock.rb +121 -0
- data/lib/farce/config.rb +229 -0
- data/lib/farce/counter.rb +71 -0
- data/lib/farce/deduper.rb +122 -0
- data/lib/farce/engine/jruby/bounded_map.rb +314 -0
- data/lib/farce/engine/jruby/fiber_scheduler.jar +0 -0
- data/lib/farce/engine/jruby/fiber_scheduler.rb +119 -0
- data/lib/farce/engine/jruby/lease_waiting.rb +19 -0
- data/lib/farce/engine/jruby/map.rb +505 -0
- data/lib/farce/engine/jruby/mutable_numeric_copy.rb +42 -0
- data/lib/farce/engine/jruby/signal.rb +147 -0
- data/lib/farce/engine/jruby.rb +63 -0
- data/lib/farce/engine/jvm/concurrent_weak_registry.rb +55 -0
- data/lib/farce/engine/jvm/counter.rb +102 -0
- data/lib/farce/engine/jvm/extension.rb +32 -0
- data/lib/farce/engine/jvm/farce.jar +0 -0
- data/lib/farce/engine/jvm/flag.rb +79 -0
- data/lib/farce/engine/jvm/priority_queue.rb +217 -0
- data/lib/farce/engine/jvm/tree_map.rb +350 -0
- data/lib/farce/engine/jvm/types.rb +180 -0
- data/lib/farce/engine/jvm.rb +19 -0
- data/lib/farce/engine/ruby/3.4/farce.so +0 -0
- data/lib/farce/engine/ruby/3.4/fiber_scheduler.rb +20 -0
- data/lib/farce/engine/ruby/3.4/port.rb +186 -0
- data/lib/farce/engine/ruby/3.4/ractor_methods.rb +26 -0
- data/lib/farce/engine/ruby/3.4/ractor_selector.rb +92 -0
- data/lib/farce/engine/ruby/3.4/rebind.so +0 -0
- data/lib/farce/engine/ruby/3.4/vault.rb +56 -0
- data/lib/farce/engine/ruby/4.0/farce.so +0 -0
- data/lib/farce/engine/ruby/4.0/port.rb +19 -0
- data/lib/farce/engine/ruby/4.0/ractor_methods.rb +20 -0
- data/lib/farce/engine/ruby/4.0/ractor_selector.rb +108 -0
- data/lib/farce/engine/ruby/4.0/rebind.so +0 -0
- data/lib/farce/engine/ruby/4.0/vault.rb +74 -0
- data/lib/farce/engine/ruby/4.1/port.rb +21 -0
- data/lib/farce/engine/ruby/4.1/ractor_methods.rb +22 -0
- data/lib/farce/engine/ruby/4.1/ractor_selector.rb +25 -0
- data/lib/farce/engine/ruby/4.1/vault.rb +5 -0
- data/lib/farce/engine/ruby/fiber_scheduler.rb +32 -0
- data/lib/farce/engine/ruby/key_lock_map.rb +28 -0
- data/lib/farce/engine/ruby/shared/lease.rb +26 -0
- data/lib/farce/engine/ruby/shared/lease_pool.rb +24 -0
- data/lib/farce/engine/ruby/shared/main_scheduler.rb +9 -0
- data/lib/farce/engine/ruby/shared/parallel_scheduler.rb +9 -0
- data/lib/farce/engine/ruby/shared/proxy_owner.rb +31 -0
- data/lib/farce/engine/ruby/shared/ractor_methods.rb +34 -0
- data/lib/farce/engine/ruby/shared/ractor_selector.rb +328 -0
- data/lib/farce/engine/ruby/shared/strict_map.rb +13 -0
- data/lib/farce/engine/ruby/shared/unshared_vector.rb +14 -0
- data/lib/farce/engine/ruby/shared/vault.rb +144 -0
- data/lib/farce/engine/ruby/shared/vault_weak_map.rb +336 -0
- data/lib/farce/engine/ruby/shared/weak_atom.rb +54 -0
- data/lib/farce/engine/ruby/shared/weak_map.rb +413 -0
- data/lib/farce/engine/ruby.rb +130 -0
- data/lib/farce/engine/shared/atom.rb +10 -0
- data/lib/farce/engine/shared/exchanger.rb +130 -0
- data/lib/farce/engine/shared/identity_key.rb +22 -0
- data/lib/farce/engine/shared/lease.rb +10 -0
- data/lib/farce/engine/shared/lease_pool.rb +10 -0
- data/lib/farce/engine/shared/main_scheduler.rb +20 -0
- data/lib/farce/engine/shared/map_key_coordination.rb +226 -0
- data/lib/farce/engine/shared/parallel_scheduler.rb +9 -0
- data/lib/farce/engine/shared/port.rb +44 -0
- data/lib/farce/engine/shared/portable_bounded_map.rb +619 -0
- data/lib/farce/engine/shared/proxy_owner.rb +43 -0
- data/lib/farce/engine/shared/queue.rb +233 -0
- data/lib/farce/engine/shared/ractor_methods.rb +73 -0
- data/lib/farce/engine/shared/rebindable.rb +15 -0
- data/lib/farce/engine/shared/strict_atom.rb +73 -0
- data/lib/farce/engine/shared/strict_map.rb +117 -0
- data/lib/farce/engine/shared/strict_queue_values.rb +27 -0
- data/lib/farce/engine/shared/strict_tree_map.rb +56 -0
- data/lib/farce/engine/shared/transaction_map_backend.rb +20 -0
- data/lib/farce/engine/shared/trie.rb +317 -0
- data/lib/farce/engine/shared/trie_builder.rb +199 -0
- data/lib/farce/engine/shared/unshareable.rb +14 -0
- data/lib/farce/engine/shared/unshared_atom.rb +256 -0
- data/lib/farce/engine/shared/unshared_priority_queue.rb +9 -0
- data/lib/farce/engine/shared/unshared_queue.rb +18 -0
- data/lib/farce/engine/shared/unshared_signal.rb +21 -0
- data/lib/farce/engine/shared/unshared_vector.rb +377 -0
- data/lib/farce/engine/shared/unshared_weak_atom.rb +30 -0
- data/lib/farce/engine/shared/unshared_weak_map.rb +445 -0
- data/lib/farce/engine/shared/vault.rb +31 -0
- data/lib/farce/engine/shared/vector.rb +83 -0
- data/lib/farce/engine/shared/weak_atom/base.rb +189 -0
- data/lib/farce/engine/shared/weak_atom.rb +20 -0
- data/lib/farce/engine/shared/weak_map/cell.rb +99 -0
- data/lib/farce/engine/shared/weak_map/index.rb +249 -0
- data/lib/farce/engine/shared/weak_map/lock.rb +80 -0
- data/lib/farce/engine/shared/weak_map/reference.rb +33 -0
- data/lib/farce/engine/shared.rb +63 -0
- data/lib/farce/engine/truffleruby/fiber_scheduler.rb +13 -0
- data/lib/farce/engine/truffleruby/lock.rb +121 -0
- data/lib/farce/engine/truffleruby/map.rb +656 -0
- data/lib/farce/engine/truffleruby/native/counter.rb +105 -0
- data/lib/farce/engine/truffleruby/native/flag.rb +77 -0
- data/lib/farce/engine/truffleruby/native/ordered_array_support.rb +67 -0
- data/lib/farce/engine/truffleruby/native/priority_queue.rb +388 -0
- data/lib/farce/engine/truffleruby/native/tree_map.rb +51 -0
- data/lib/farce/engine/truffleruby/native/unsafe_tree_map.rb +350 -0
- data/lib/farce/engine/truffleruby/signal.rb +77 -0
- data/lib/farce/engine/truffleruby.rb +148 -0
- data/lib/farce/envelope.rb +335 -0
- data/lib/farce/error.rb +35 -0
- data/lib/farce/exchanger.rb +41 -0
- data/lib/farce/flag.rb +27 -0
- data/lib/farce/integrations/active_support/blank.rb +68 -0
- data/lib/farce/integrations/active_support/clock.rb +17 -0
- data/lib/farce/integrations/active_support/duplicable.rb +78 -0
- data/lib/farce/integrations/active_support/map.rb +175 -0
- data/lib/farce/integrations/active_support/set.rb +60 -0
- data/lib/farce/integrations/active_support/value_serialization.rb +17 -0
- data/lib/farce/integrations/active_support/vector.rb +231 -0
- data/lib/farce/integrations/active_support.rb +9 -0
- data/lib/farce/integrations/activesupport.rb +5 -0
- data/lib/farce/integrations/bson.rb +108 -0
- data/lib/farce/integrations/cbor.rb +51 -0
- data/lib/farce/integrations/concurrent.rb +112 -0
- data/lib/farce/integrations/dry-types.rb +5 -0
- data/lib/farce/integrations/dry_types.rb +694 -0
- data/lib/farce/integrations/json.rb +7 -0
- data/lib/farce/integrations/msgpack.rb +112 -0
- data/lib/farce/integrations/oj.rb +69 -0
- data/lib/farce/integrations/psych.rb +191 -0
- data/lib/farce/integrations/ractor-sharing.rb +5 -0
- data/lib/farce/integrations/ractor-tmvar.rb +5 -0
- data/lib/farce/integrations/ractor_sharing.rb +172 -0
- data/lib/farce/integrations/ractor_tmvar.rb +64 -0
- data/lib/farce/integrations/shared/to_json.rb +42 -0
- data/lib/farce/integrations/sorted_set.rb +11 -0
- data/lib/farce/integrations/weakref.rb +38 -0
- data/lib/farce/integrations/yajl.rb +10 -0
- data/lib/farce/integrations.rb +156 -0
- data/lib/farce/internal/_frozen_config.rb +11 -0
- data/lib/farce/internal/autoloads.rb +47 -0
- data/lib/farce/internal/blocking_priority_queue.rb +122 -0
- data/lib/farce/internal/converter.rb +106 -0
- data/lib/farce/internal/copyable.rb +31 -0
- data/lib/farce/internal/delegation.rb +20 -0
- data/lib/farce/internal/external_transaction.rb +59 -0
- data/lib/farce/internal/fake_ractor.rb +179 -0
- data/lib/farce/internal/freeze.rb +118 -0
- data/lib/farce/internal/inspect.rb +157 -0
- data/lib/farce/internal/key_lock_map.rb +29 -0
- data/lib/farce/internal/key_normalizer.rb +289 -0
- data/lib/farce/internal/lease_initialization.rb +115 -0
- data/lib/farce/internal/lease_map.rb +441 -0
- data/lib/farce/internal/lease_pool_state.rb +228 -0
- data/lib/farce/internal/lease_state.rb +342 -0
- data/lib/farce/internal/lease_waiting.rb +13 -0
- data/lib/farce/internal/managed_queue.rb +26 -0
- data/lib/farce/internal/map_value_modes.rb +227 -0
- data/lib/farce/internal/marshal_support.rb +227 -0
- data/lib/farce/internal/mixin.rb +20 -0
- data/lib/farce/internal/mutable_ordered_key_lock_map.rb +63 -0
- data/lib/farce/internal/noncopyable.rb +17 -0
- data/lib/farce/internal/ordered_key_lock_map.rb +69 -0
- data/lib/farce/internal/pool_supervisor.rb +41 -0
- data/lib/farce/internal/pool_worker.rb +61 -0
- data/lib/farce/internal/portable_transaction/reservation_entry.rb +29 -0
- data/lib/farce/internal/portable_transaction/strong_map_entry.rb +137 -0
- data/lib/farce/internal/portable_transaction/strong_map_size_entry.rb +44 -0
- data/lib/farce/internal/portable_transaction/tree_entry.rb +64 -0
- data/lib/farce/internal/portable_transaction.rb +164 -0
- data/lib/farce/internal/proxy_owner_notifications.rb +29 -0
- data/lib/farce/internal/reservation_waiting.rb +24 -0
- data/lib/farce/internal/scheduled_task.rb +48 -0
- data/lib/farce/internal/scheduler_io.rb +171 -0
- data/lib/farce/internal/scheduler_lifecycle.rb +314 -0
- data/lib/farce/internal/select_scheduler.rb +235 -0
- data/lib/farce/internal/storage.rb +162 -0
- data/lib/farce/internal/strict_lease.rb +30 -0
- data/lib/farce/internal/strict_lease_map.rb +20 -0
- data/lib/farce/internal/strict_lease_pool.rb +29 -0
- data/lib/farce/internal/thread_pool.rb +166 -0
- data/lib/farce/internal/transaction_conflict.rb +9 -0
- data/lib/farce/internal/transaction_freeze_guard.rb +30 -0
- data/lib/farce/internal/transaction_map_snapshot.rb +123 -0
- data/lib/farce/internal/undefined.rb +22 -0
- data/lib/farce/internal/unshared_lease.rb +29 -0
- data/lib/farce/internal/unshared_lease_pool.rb +34 -0
- data/lib/farce/internal/unshared_queue_waiting.rb +28 -0
- data/lib/farce/internal/value_serialization.rb +13 -0
- data/lib/farce/internal/weak_map_value_modes.rb +58 -0
- data/lib/farce/internal/weak_mode_manager.rb +26 -0
- data/lib/farce/internal.rb +129 -0
- data/lib/farce/lazy.rb +100 -0
- data/lib/farce/lazy_ref.rb +40 -0
- data/lib/farce/lease.rb +28 -0
- data/lib/farce/lease_map.rb +34 -0
- data/lib/farce/lease_pool.rb +29 -0
- data/lib/farce/lfu_map.rb +53 -0
- data/lib/farce/local/atom.rb +23 -0
- data/lib/farce/local/counter.rb +88 -0
- data/lib/farce/local/flag.rb +65 -0
- data/lib/farce/local/lazy.rb +38 -0
- data/lib/farce/local/lazy_ref.rb +36 -0
- data/lib/farce/local/lease.rb +77 -0
- data/lib/farce/local/lease_map.rb +80 -0
- data/lib/farce/local/lease_pool.rb +38 -0
- data/lib/farce/local/lfu_map.rb +55 -0
- data/lib/farce/local/lru_map.rb +62 -0
- data/lib/farce/local/map.rb +47 -0
- data/lib/farce/local/molecule.rb +45 -0
- data/lib/farce/local/priority_queue.rb +53 -0
- data/lib/farce/local/queue.rb +32 -0
- data/lib/farce/local/scoped.rb +166 -0
- data/lib/farce/local/set.rb +18 -0
- data/lib/farce/local/sorted_set.rb +50 -0
- data/lib/farce/local/timer_queue.rb +41 -0
- data/lib/farce/local/tree_map.rb +47 -0
- data/lib/farce/local/vector.rb +30 -0
- data/lib/farce/local/weak_atom.rb +23 -0
- data/lib/farce/local/weak_key_map.rb +39 -0
- data/lib/farce/local/weak_map.rb +39 -0
- data/lib/farce/local/weak_set.rb +18 -0
- data/lib/farce/local/weak_value_map.rb +39 -0
- data/lib/farce/local.rb +35 -0
- data/lib/farce/lock.rb +54 -0
- data/lib/farce/lru_map.rb +63 -0
- data/lib/farce/map.rb +58 -0
- data/lib/farce/mode_manager.rb +142 -0
- data/lib/farce/molecule.rb +60 -0
- data/lib/farce/mutable.rb +171 -0
- data/lib/farce/pool.rb +332 -0
- data/lib/farce/port.rb +134 -0
- data/lib/farce/priority_queue.rb +70 -0
- data/lib/farce/proxy/register.rb +101 -0
- data/lib/farce/proxy/supervisor.rb +137 -0
- data/lib/farce/proxy/wrapper.rb +49 -0
- data/lib/farce/proxy.rb +291 -0
- data/lib/farce/queue.rb +60 -0
- data/lib/farce/ractor.rb +269 -0
- data/lib/farce/read_write_lock.rb +256 -0
- data/lib/farce/reference.rb +158 -0
- data/lib/farce/resolv/dns.rb +42 -0
- data/lib/farce/resolv.rb +85 -0
- data/lib/farce/scheduler.rb +536 -0
- data/lib/farce/set.rb +39 -0
- data/lib/farce/shareable.rb +150 -0
- data/lib/farce/signal.rb +97 -0
- data/lib/farce/sorted_set.rb +36 -0
- data/lib/farce/strict/atom.rb +34 -0
- data/lib/farce/strict/counter.rb +10 -0
- data/lib/farce/strict/exchanger.rb +25 -0
- data/lib/farce/strict/flag.rb +10 -0
- data/lib/farce/strict/lazy.rb +26 -0
- data/lib/farce/strict/lazy_ref.rb +26 -0
- data/lib/farce/strict/lease.rb +27 -0
- data/lib/farce/strict/lease_map.rb +33 -0
- data/lib/farce/strict/lease_pool.rb +28 -0
- data/lib/farce/strict/lfu_map.rb +27 -0
- data/lib/farce/strict/lru_map.rb +28 -0
- data/lib/farce/strict/map.rb +54 -0
- data/lib/farce/strict/molecule.rb +16 -0
- data/lib/farce/strict/port.rb +40 -0
- data/lib/farce/strict/priority_queue.rb +20 -0
- data/lib/farce/strict/queue.rb +17 -0
- data/lib/farce/strict/set.rb +14 -0
- data/lib/farce/strict/sorted_set.rb +19 -0
- data/lib/farce/strict/timer_queue.rb +13 -0
- data/lib/farce/strict/tree_map.rb +27 -0
- data/lib/farce/strict/vector.rb +24 -0
- data/lib/farce/strict/weak_atom.rb +31 -0
- data/lib/farce/strict/weak_key_map.rb +49 -0
- data/lib/farce/strict/weak_map.rb +49 -0
- data/lib/farce/strict/weak_set.rb +14 -0
- data/lib/farce/strict/weak_value_map.rb +48 -0
- data/lib/farce/strict.rb +28 -0
- data/lib/farce/system.rb +40 -0
- data/lib/farce/thread_scheduler.rb +70 -0
- data/lib/farce/timer_queue.rb +66 -0
- data/lib/farce/transaction/atom.rb +73 -0
- data/lib/farce/transaction/map.rb +25 -0
- data/lib/farce/transaction/map_operations.rb +143 -0
- data/lib/farce/transaction/molecule.rb +90 -0
- data/lib/farce/transaction/mutable.rb +61 -0
- data/lib/farce/transaction/set.rb +17 -0
- data/lib/farce/transaction/set_operations.rb +48 -0
- data/lib/farce/transaction/sorted_set.rb +18 -0
- data/lib/farce/transaction/tree_map.rb +78 -0
- data/lib/farce/transaction/vector.rb +139 -0
- data/lib/farce/transaction/wrapper.rb +202 -0
- data/lib/farce/transaction.rb +343 -0
- data/lib/farce/tree_map.rb +56 -0
- data/lib/farce/unsafe/lfu_map.rb +36 -0
- data/lib/farce/unsafe/lru_map.rb +36 -0
- data/lib/farce/unsafe/tree_map.rb +21 -0
- data/lib/farce/unsafe.rb +29 -0
- data/lib/farce/unshareable.rb +67 -0
- data/lib/farce/unshared/atom.rb +24 -0
- data/lib/farce/unshared/counter.rb +10 -0
- data/lib/farce/unshared/flag.rb +10 -0
- data/lib/farce/unshared/lazy.rb +38 -0
- data/lib/farce/unshared/lazy_ref.rb +26 -0
- data/lib/farce/unshared/lease.rb +27 -0
- data/lib/farce/unshared/lease_map.rb +26 -0
- data/lib/farce/unshared/lease_pool.rb +25 -0
- data/lib/farce/unshared/lfu_map.rb +20 -0
- data/lib/farce/unshared/lru_map.rb +20 -0
- data/lib/farce/unshared/map.rb +49 -0
- data/lib/farce/unshared/molecule.rb +16 -0
- data/lib/farce/unshared/priority_queue.rb +16 -0
- data/lib/farce/unshared/queue.rb +27 -0
- data/lib/farce/unshared/set.rb +14 -0
- data/lib/farce/unshared/sorted_set.rb +28 -0
- data/lib/farce/unshared/timer_queue.rb +16 -0
- data/lib/farce/unshared/tree_map.rb +21 -0
- data/lib/farce/unshared/vector.rb +19 -0
- data/lib/farce/unshared/weak_atom.rb +31 -0
- data/lib/farce/unshared/weak_key_map.rb +44 -0
- data/lib/farce/unshared/weak_map.rb +44 -0
- data/lib/farce/unshared/weak_set.rb +14 -0
- data/lib/farce/unshared/weak_value_map.rb +43 -0
- data/lib/farce/unshared.rb +28 -0
- data/lib/farce/vector.rb +228 -0
- data/lib/farce/version.rb +8 -0
- data/lib/farce/walker/definitions.rb +186 -0
- data/lib/farce/walker/modification.rb +355 -0
- data/lib/farce/walker.rb +319 -0
- data/lib/farce/weak_atom.rb +121 -0
- data/lib/farce/weak_key_map.rb +31 -0
- data/lib/farce/weak_map.rb +33 -0
- data/lib/farce/weak_ref.rb +67 -0
- data/lib/farce/weak_set.rb +82 -0
- data/lib/farce/weak_value.rb +195 -0
- data/lib/farce/weak_value_map.rb +32 -0
- data/lib/farce.rb +519 -0
- metadata +468 -0
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# shareable_constant_value: literal
|
|
3
|
+
# warn_indent: true
|
|
4
|
+
|
|
5
|
+
module Farce
|
|
6
|
+
module Local
|
|
7
|
+
# A scoped set maintained in ascending order.
|
|
8
|
+
class SortedSet < Farce::Abstract::SortedSet
|
|
9
|
+
include Shareable::Delegated
|
|
10
|
+
|
|
11
|
+
class MutableTreeMap < Farce::Local::TreeMap # :nodoc:
|
|
12
|
+
BackingState = Data.define(:map, :key_locks)
|
|
13
|
+
private_constant :BackingState
|
|
14
|
+
|
|
15
|
+
private
|
|
16
|
+
|
|
17
|
+
def prepare_key(key) = key
|
|
18
|
+
|
|
19
|
+
def new_copied_scoped_value(map)
|
|
20
|
+
BackingState.new(map, Internal::MutableOrderedKeyLockMap.new)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def new_scoped_value(entries = nil, **)
|
|
24
|
+
unless Internal::KeyNormalizer.canonical_entries?(entries)
|
|
25
|
+
return BackingState.new(
|
|
26
|
+
Internal::MutableTreeMap.new(entries, **),
|
|
27
|
+
Internal::MutableOrderedKeyLockMap.new,
|
|
28
|
+
)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
map = Internal::MutableTreeMap.new(nil, **)
|
|
32
|
+
entries.each { map[_1] = _2 }
|
|
33
|
+
BackingState.new(map, Internal::MutableOrderedKeyLockMap.new)
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
private_constant :MutableTreeMap
|
|
37
|
+
|
|
38
|
+
# The scope with independent contents.
|
|
39
|
+
# @return [Symbol] The configured scope name.
|
|
40
|
+
def scope = map_backend.scope
|
|
41
|
+
|
|
42
|
+
private
|
|
43
|
+
|
|
44
|
+
def new_map(entries = nil, compare_keys_by_identity: false, scope: :ractor, **)
|
|
45
|
+
raise ArgumentError, "sorted sets do not support identity comparison" if compare_keys_by_identity
|
|
46
|
+
MutableTreeMap.new(entries, scope:)
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# shareable_constant_value: literal
|
|
3
|
+
# warn_indent: true
|
|
4
|
+
|
|
5
|
+
module Farce
|
|
6
|
+
module Local
|
|
7
|
+
# A shareable timer queue with independent mutable storage in each scope.
|
|
8
|
+
class TimerQueue < Abstract::TimerQueue
|
|
9
|
+
include Shareable::Unfreezable
|
|
10
|
+
include Scoped
|
|
11
|
+
|
|
12
|
+
# @overload initialize(capacity: nil, track_age: false, scope: :ractor)
|
|
13
|
+
# @!macro scopes
|
|
14
|
+
# @param capacity [Integer, nil] the maximum number of values, or nil for an unbounded queue
|
|
15
|
+
# @param track_age [Boolean] whether to track enqueue age and queue generations
|
|
16
|
+
# @param scope [Symbol] the scope of the queue
|
|
17
|
+
# @param fiber_wait [:auto, :io, :block] how scheduled fibers wait on CRuby
|
|
18
|
+
# @return [TimerQueue]
|
|
19
|
+
def initialize(capacity: nil, track_age: false, scope: :ractor, fiber_wait: :auto)
|
|
20
|
+
super
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# @return [Symbol] always returns `:local`
|
|
24
|
+
def mode = :local
|
|
25
|
+
|
|
26
|
+
# @api private
|
|
27
|
+
def fiber_wait = internal_signal.fiber_wait
|
|
28
|
+
|
|
29
|
+
private
|
|
30
|
+
|
|
31
|
+
def eager_scoped_value? = false
|
|
32
|
+
def internal_queue = scoped_value.first
|
|
33
|
+
def internal_signal = scoped_value.last
|
|
34
|
+
|
|
35
|
+
def new_scoped_value(capacity: nil, fiber_wait: :auto, track_age: false)
|
|
36
|
+
signal = Internal::UnsharedSignal.for(fiber_wait)
|
|
37
|
+
[Internal::UnsharedPriorityQueue.new(capacity:, signal:, track_age:), signal]
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# shareable_constant_value: literal
|
|
3
|
+
# warn_indent: true
|
|
4
|
+
|
|
5
|
+
module Farce
|
|
6
|
+
module Local
|
|
7
|
+
# A shareable tree map with independent mutable storage in each scope.
|
|
8
|
+
#
|
|
9
|
+
# @!method initialize(entries = nil, scope: :ractor)
|
|
10
|
+
# @!macro scopes
|
|
11
|
+
# @param entries [Hash, Farce::Abstract::Map, #each, #to_hash, nil] the entries to store initially
|
|
12
|
+
# @param scope [Symbol] the scope of the tree map
|
|
13
|
+
# @return [TreeMap]
|
|
14
|
+
class TreeMap < Abstract::TreeMap
|
|
15
|
+
include Shareable::Tracked
|
|
16
|
+
include Scoped::Tracked
|
|
17
|
+
|
|
18
|
+
State = Data.define(:map, :key_locks)
|
|
19
|
+
private_constant :State
|
|
20
|
+
|
|
21
|
+
protected
|
|
22
|
+
|
|
23
|
+
def internal_map = scoped_value.map
|
|
24
|
+
def transaction_key_locks = scoped_value.key_locks
|
|
25
|
+
|
|
26
|
+
private
|
|
27
|
+
|
|
28
|
+
def freeze_scoped_value(state) = state.map.freeze
|
|
29
|
+
|
|
30
|
+
def new_copied_scoped_value(map)
|
|
31
|
+
State.new(map, Internal::OrderedKeyLockMap.new)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def with_key_lock(key, &) = scoped_value.key_locks.synchronize(key, &)
|
|
35
|
+
|
|
36
|
+
def new_scoped_value(entries = nil, **)
|
|
37
|
+
unless Internal::KeyNormalizer.canonical_entries?(entries)
|
|
38
|
+
return State.new(Internal::TreeMap.new(entries, **), Internal::OrderedKeyLockMap.new)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
map = Internal::TreeMap.new(nil, **)
|
|
42
|
+
entries.each { map[_1] = _2 }
|
|
43
|
+
State.new(map, Internal::OrderedKeyLockMap.new)
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# shareable_constant_value: literal
|
|
3
|
+
# warn_indent: true
|
|
4
|
+
|
|
5
|
+
module Farce
|
|
6
|
+
module Local
|
|
7
|
+
# A shareable vector with independent mutable storage in each scope.
|
|
8
|
+
#
|
|
9
|
+
# @!method initialize(source = nil, compare_by_identity: false, scope: :ractor)
|
|
10
|
+
# @!macro scopes
|
|
11
|
+
# @param source [Array, nil] the initial values
|
|
12
|
+
# @param compare_by_identity [Boolean] whether values are compared by identity
|
|
13
|
+
# @param scope [Symbol] the scope of the vector
|
|
14
|
+
# @return [Vector]
|
|
15
|
+
class Vector < Abstract::Vector
|
|
16
|
+
include Shareable::Tracked
|
|
17
|
+
include Scoped::Tracked
|
|
18
|
+
|
|
19
|
+
protected
|
|
20
|
+
|
|
21
|
+
def internal_vector = scoped_value
|
|
22
|
+
|
|
23
|
+
def build_derived_vector(values)
|
|
24
|
+
self.class.new(values, compare_by_identity: compare_by_identity?, scope:)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
private def new_scoped_value(...) = Internal::UnsharedVector.new(...)
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# shareable_constant_value: literal
|
|
3
|
+
# warn_indent: true
|
|
4
|
+
|
|
5
|
+
module Farce
|
|
6
|
+
module Local
|
|
7
|
+
# A shareable weak atom with independent mutable storage in each scope.
|
|
8
|
+
#
|
|
9
|
+
# @!method initialize(value = nil, compare_by_identity: false, scope: :ractor)
|
|
10
|
+
# @!macro scopes
|
|
11
|
+
# @param value [BasicObject, nil] the initial value
|
|
12
|
+
# @param compare_by_identity [Boolean] whether comparisons use object identity instead of equality
|
|
13
|
+
# @param scope [Symbol] the scope of the weak atomic reference
|
|
14
|
+
# @return [WeakAtom]
|
|
15
|
+
class WeakAtom < Abstract::WeakAtom
|
|
16
|
+
include Shareable::Tracked
|
|
17
|
+
include Scoped::Tracked
|
|
18
|
+
|
|
19
|
+
protected def internal_atom = scoped_value
|
|
20
|
+
private def new_scoped_value(...) = Internal::UnsharedWeakAtom.new(...)
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# shareable_constant_value: literal
|
|
3
|
+
# warn_indent: true
|
|
4
|
+
|
|
5
|
+
module Farce
|
|
6
|
+
module Local
|
|
7
|
+
# A shareable weak key map with independent mutable storage in each scope.
|
|
8
|
+
#
|
|
9
|
+
# @!method initialize(initial_mapping = nil, scope: :ractor, **options)
|
|
10
|
+
# @!macro scopes
|
|
11
|
+
# @param initial_mapping [Hash, Farce::Abstract::Map, #each, nil] the entries to store initially
|
|
12
|
+
# @param scope [Symbol] the scope of the weak key map
|
|
13
|
+
# @option options [Boolean] compare_by_identity (false) whether keys and values are compared by identity
|
|
14
|
+
# @option options [Boolean] compare_keys_by_identity (compare_by_identity) whether keys are compared by identity
|
|
15
|
+
# @option options [Boolean] compare_values_by_identity (compare_by_identity)
|
|
16
|
+
# whether values are compared by identity
|
|
17
|
+
# @return [WeakKeyMap]
|
|
18
|
+
class WeakKeyMap < Abstract::WeakKeyMap
|
|
19
|
+
include Shareable::Tracked
|
|
20
|
+
include Scoped::Tracked
|
|
21
|
+
|
|
22
|
+
protected
|
|
23
|
+
|
|
24
|
+
def internal_map = scoped_value
|
|
25
|
+
|
|
26
|
+
private
|
|
27
|
+
|
|
28
|
+
def new_scoped_value(entries = nil, **)
|
|
29
|
+
unless @key_normalizer || Internal::KeyNormalizer.canonical_entries?(entries)
|
|
30
|
+
return Internal::UnsharedWeakKeyMap.new(entries, **)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
map = Internal::UnsharedWeakKeyMap.new(nil, **)
|
|
34
|
+
entries&.each { map[_1] = _2 }
|
|
35
|
+
Internal::KeyNormalizer.wrap_concurrent(map, @key_normalizer)
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# shareable_constant_value: literal
|
|
3
|
+
# warn_indent: true
|
|
4
|
+
|
|
5
|
+
module Farce
|
|
6
|
+
module Local
|
|
7
|
+
# A shareable weak map with independent mutable storage in each scope.
|
|
8
|
+
#
|
|
9
|
+
# @!method initialize(initial_mapping = nil, scope: :ractor, **options)
|
|
10
|
+
# @!macro scopes
|
|
11
|
+
# @param initial_mapping [Hash, Farce::Abstract::Map, #each, nil] the entries to store initially
|
|
12
|
+
# @param scope [Symbol] the scope of the weak map
|
|
13
|
+
# @option options [Boolean] compare_by_identity (false) whether keys and values are compared by identity
|
|
14
|
+
# @option options [Boolean] compare_keys_by_identity (compare_by_identity) whether keys are compared by identity
|
|
15
|
+
# @option options [Boolean] compare_values_by_identity (compare_by_identity)
|
|
16
|
+
# whether values are compared by identity
|
|
17
|
+
# @return [WeakMap]
|
|
18
|
+
class WeakMap < Abstract::WeakMap
|
|
19
|
+
include Shareable::Tracked
|
|
20
|
+
include Scoped::Tracked
|
|
21
|
+
|
|
22
|
+
protected
|
|
23
|
+
|
|
24
|
+
def internal_map = scoped_value
|
|
25
|
+
|
|
26
|
+
private
|
|
27
|
+
|
|
28
|
+
def new_scoped_value(entries = nil, **)
|
|
29
|
+
unless @key_normalizer || Internal::KeyNormalizer.canonical_entries?(entries)
|
|
30
|
+
return Internal::UnsharedWeakMap.new(entries, **)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
map = Internal::UnsharedWeakMap.new(nil, **)
|
|
34
|
+
entries&.each { map[_1] = _2 }
|
|
35
|
+
Internal::KeyNormalizer.wrap_concurrent(map, @key_normalizer)
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# shareable_constant_value: literal
|
|
3
|
+
# warn_indent: true
|
|
4
|
+
|
|
5
|
+
module Farce
|
|
6
|
+
module Local
|
|
7
|
+
# A shareable weak set with independent mutable contents in each scope.
|
|
8
|
+
class WeakSet < Farce::Abstract::WeakSet
|
|
9
|
+
include Shareable::Delegated
|
|
10
|
+
|
|
11
|
+
# The scope with independent contents.
|
|
12
|
+
# @return [Symbol] The configured scope name.
|
|
13
|
+
def scope = map_backend.scope
|
|
14
|
+
|
|
15
|
+
private def new_map(...) = Farce::Local::WeakMap.new(...)
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# shareable_constant_value: literal
|
|
3
|
+
# warn_indent: true
|
|
4
|
+
|
|
5
|
+
module Farce
|
|
6
|
+
module Local
|
|
7
|
+
# A shareable weak value map with independent mutable storage in each scope.
|
|
8
|
+
#
|
|
9
|
+
# @!method initialize(initial_mapping = nil, scope: :ractor, **options)
|
|
10
|
+
# @!macro scopes
|
|
11
|
+
# @param initial_mapping [Hash, Farce::Abstract::Map, #each, nil] the entries to store initially
|
|
12
|
+
# @param scope [Symbol] the scope of the weak value map
|
|
13
|
+
# @option options [Boolean] compare_by_identity (false) whether keys and values are compared by identity
|
|
14
|
+
# @option options [Boolean] compare_keys_by_identity (compare_by_identity) whether keys are compared by identity
|
|
15
|
+
# @option options [Boolean] compare_values_by_identity (compare_by_identity)
|
|
16
|
+
# whether values are compared by identity
|
|
17
|
+
# @return [WeakValueMap]
|
|
18
|
+
class WeakValueMap < Abstract::WeakValueMap
|
|
19
|
+
include Shareable::Tracked
|
|
20
|
+
include Scoped::Tracked
|
|
21
|
+
|
|
22
|
+
protected
|
|
23
|
+
|
|
24
|
+
def internal_map = scoped_value
|
|
25
|
+
|
|
26
|
+
private
|
|
27
|
+
|
|
28
|
+
def new_scoped_value(entries = nil, **)
|
|
29
|
+
unless @key_normalizer || Internal::KeyNormalizer.canonical_entries?(entries)
|
|
30
|
+
return Internal::UnsharedWeakValueMap.new(entries, **)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
map = Internal::UnsharedWeakValueMap.new(nil, **)
|
|
34
|
+
entries&.each { map[_1] = _2 }
|
|
35
|
+
Internal::KeyNormalizer.wrap_concurrent(map, @key_normalizer)
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
data/lib/farce/local.rb
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# shareable_constant_value: literal
|
|
3
|
+
# warn_indent: true
|
|
4
|
+
|
|
5
|
+
module Farce
|
|
6
|
+
# Shareable containers whose mutable contents belong to the current scope.
|
|
7
|
+
# Scopes may be :ractor, :thread_group, :thread, :fiber_storage, or :fiber.
|
|
8
|
+
# Initial contents are copied between Ractors. Within a Ractor, each scope has
|
|
9
|
+
# its own container, but objects supplied as initial contents may be shared.
|
|
10
|
+
module Local
|
|
11
|
+
include Internal::Autoloads
|
|
12
|
+
|
|
13
|
+
# @overload enfarce(object, freeze: nil, scope: :ractor)
|
|
14
|
+
# Converts vanilla Ruby objects into their {Farce::Local} equivalents.
|
|
15
|
+
#
|
|
16
|
+
# @!macro scopes
|
|
17
|
+
#
|
|
18
|
+
# @example
|
|
19
|
+
# Farce::Local.enfarce({ foo: ["bar"] })
|
|
20
|
+
# # => #<Farce::Local::Map {foo: #<Farce::Local::Vector ["bar"]>}>
|
|
21
|
+
#
|
|
22
|
+
#
|
|
23
|
+
# @yield [object] Optional block, called with any object that doesn't have a specific conversion defined.
|
|
24
|
+
# @yieldparam object [BasicObject] the object to convert
|
|
25
|
+
# @yieldreturn [BasicObject] the converted object
|
|
26
|
+
# @param object [BasicObject] The root object to convert
|
|
27
|
+
# @param freeze [Boolean, nil]
|
|
28
|
+
# Whether to freeze the converted objects.
|
|
29
|
+
# If set to `nil` (default), the freezing behavior will depend on the original object's frozen state.
|
|
30
|
+
# @param scope [Symbol] The scope for the local container, e.g., `:ractor`.
|
|
31
|
+
# @return [BasicObject] the converted object
|
|
32
|
+
# @see Farce.enfarce
|
|
33
|
+
def self.enfarce(object, **, &) = Internal::Converter.new(self, **, &).convert(object)
|
|
34
|
+
end
|
|
35
|
+
end
|
data/lib/farce/lock.rb
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# shareable_constant_value: literal
|
|
3
|
+
# warn_indent: true
|
|
4
|
+
|
|
5
|
+
module Farce
|
|
6
|
+
# @note
|
|
7
|
+
# On JRuby and TruffleRuby, this will be a subclass of Mutex. On CRuby, it will be a custom implementation,
|
|
8
|
+
# as Mutex instances are not Ractor-shareable.
|
|
9
|
+
#
|
|
10
|
+
# A Ractor-shareable mutual-exclusion lock with the same interface as Ruby's Mutex.
|
|
11
|
+
#
|
|
12
|
+
# Lock ownership belongs to the current Fiber. Attempting to lock the same lock recursively raises
|
|
13
|
+
# ThreadError. Waiting for a lock cooperates with an installed Fiber scheduler.
|
|
14
|
+
#
|
|
15
|
+
# @!method lock
|
|
16
|
+
# Acquire the lock, waiting until it is available.
|
|
17
|
+
# @return [self]
|
|
18
|
+
# @raise [ThreadError] if the current Fiber already owns the lock
|
|
19
|
+
#
|
|
20
|
+
# @!method locked?
|
|
21
|
+
# @return [Boolean] whether any Fiber owns the lock
|
|
22
|
+
#
|
|
23
|
+
# @!method owned?
|
|
24
|
+
# @return [Boolean] whether the current Fiber owns the lock
|
|
25
|
+
#
|
|
26
|
+
# @!method sleep(timeout = nil)
|
|
27
|
+
# Release the lock while sleeping, then reacquire it before returning.
|
|
28
|
+
# @param timeout [Numeric, nil] the maximum number of seconds to sleep, or nil to sleep indefinitely
|
|
29
|
+
# @return [nil]
|
|
30
|
+
# @raise [ThreadError] if the current Fiber does not own the lock
|
|
31
|
+
#
|
|
32
|
+
# @!method synchronize
|
|
33
|
+
# Acquire the lock for the duration of the block.
|
|
34
|
+
# @yield the block to run while holding the lock
|
|
35
|
+
# @return [Object] the block result
|
|
36
|
+
# @raise [ThreadError] if no block is given or the current Fiber already owns the lock
|
|
37
|
+
#
|
|
38
|
+
# @!method try_lock
|
|
39
|
+
# Attempt to acquire the lock without waiting.
|
|
40
|
+
# @return [Boolean] whether the lock was acquired
|
|
41
|
+
#
|
|
42
|
+
# @!method unlock
|
|
43
|
+
# Release the lock.
|
|
44
|
+
# @return [self]
|
|
45
|
+
# @raise [ThreadError] if the current Fiber does not own the lock
|
|
46
|
+
class Lock < Internal::Lock
|
|
47
|
+
include Internal::MarshalSupport::Reject
|
|
48
|
+
include Internal::Copyable
|
|
49
|
+
include Shareable::Native
|
|
50
|
+
include Shareable::Unfreezable
|
|
51
|
+
|
|
52
|
+
private def instance_variables_to_inspect = Internal::EMPTY_ARRAY
|
|
53
|
+
end
|
|
54
|
+
end
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# shareable_constant_value: literal
|
|
3
|
+
# warn_indent: true
|
|
4
|
+
|
|
5
|
+
module Farce
|
|
6
|
+
# A Ractor-shareable bounded map that evicts the least recently used entry.
|
|
7
|
+
# Transfer modes apply to values. Equality-mode mutable string keys become immutable.
|
|
8
|
+
# Individual reads and writes update recency. Iteration and key observation do not.
|
|
9
|
+
#
|
|
10
|
+
# @example Keeping copied values in a small cache
|
|
11
|
+
# cache = Farce::LRUMap.new(max_size: 2, mode: :copy)
|
|
12
|
+
#
|
|
13
|
+
# cache[:first] = [1]
|
|
14
|
+
# cache[:second] = [2]
|
|
15
|
+
# cache[:first] # makes sure :first was accessed more recently than :second
|
|
16
|
+
# cache[:third] = [3]
|
|
17
|
+
#
|
|
18
|
+
# cache.key?(:second) # => false
|
|
19
|
+
class LRUMap < Farce::Abstract::LRUMap
|
|
20
|
+
include Shareable::Delegated
|
|
21
|
+
|
|
22
|
+
# @!macro modes
|
|
23
|
+
# @param entries [Hash, Array<Array(BasicObject, BasicObject)>, Abstract::Map, #each, nil]
|
|
24
|
+
# Optional initial entries. Entries are stored sequentially and may be evicted.
|
|
25
|
+
# @param mode [Symbol] The default value transfer mode.
|
|
26
|
+
def initialize(entries = nil, mode: :copy, **)
|
|
27
|
+
@manager = marshal_mode_manager(mode)
|
|
28
|
+
super(entries, **)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# The default transfer mode for values.
|
|
32
|
+
# @return [Symbol]
|
|
33
|
+
def mode = @manager.mode
|
|
34
|
+
|
|
35
|
+
# Keys must be shareable, except equality-mode strings that can be snapshotted.
|
|
36
|
+
# @return [true]
|
|
37
|
+
def shareable_keys? = true
|
|
38
|
+
|
|
39
|
+
# Values are stored in Ractor-shareable representations.
|
|
40
|
+
# @return [true]
|
|
41
|
+
def shareable_values? = true
|
|
42
|
+
|
|
43
|
+
protected
|
|
44
|
+
|
|
45
|
+
def unwrap_value(value) = @manager.unwrap(value)
|
|
46
|
+
|
|
47
|
+
def wrap_value(value)
|
|
48
|
+
check_frozen!
|
|
49
|
+
@manager.wrap(value)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
private
|
|
53
|
+
|
|
54
|
+
def each_for_inspect(&) = internal_map.each(&)
|
|
55
|
+
def inspect_value(inspector, value) = super(inspector, value, @manager)
|
|
56
|
+
def new_bounded_map(...) = Internal::StrictLRUMap.new(...)
|
|
57
|
+
def freeze_backend = internal_map
|
|
58
|
+
|
|
59
|
+
def new_key_locks(compare_keys_by_identity:)
|
|
60
|
+
Internal::KeyLockMap.new(registry_class: Farce::Strict::Map, compare_keys_by_identity:)
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
data/lib/farce/map.rb
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# shareable_constant_value: literal
|
|
3
|
+
# warn_indent: true
|
|
4
|
+
|
|
5
|
+
module Farce
|
|
6
|
+
# A concurrent map with direct shareable keys and strongly retained values.
|
|
7
|
+
# Transfer modes apply only to values. String keys use their frozen unary-minus form,
|
|
8
|
+
# except when comparing keys by identity. Other keys must already be shareable.
|
|
9
|
+
# Values stored through this map's mode manager are automatically unwrapped.
|
|
10
|
+
# Updates on different keys can run concurrently.
|
|
11
|
+
#
|
|
12
|
+
# Clearing the map invalidates unfinished updates so they cannot restore removed entries.
|
|
13
|
+
#
|
|
14
|
+
# `dup` and `clone` copy entries into independent storage while sharing stored values.
|
|
15
|
+
# Move-mode envelopes keep their ownership across copies. `dup` and ordinary `clone`
|
|
16
|
+
# remain Ractor-shareable.
|
|
17
|
+
#
|
|
18
|
+
# @example Atomically updating a value
|
|
19
|
+
# map = Farce::Map.new({ count: 0 })
|
|
20
|
+
# map.update(:count) { |count| count + 1 } # => 1
|
|
21
|
+
#
|
|
22
|
+
# @example Automatically making values shareable
|
|
23
|
+
# map = Farce::Map.new(mode: :make_shareable)
|
|
24
|
+
# map[:jobs] = []
|
|
25
|
+
# map.update(:jobs) { |items| items + [:job] } # => [:job]
|
|
26
|
+
# map[:jobs] # => [:job]
|
|
27
|
+
# Ractor.shareable?(map[:jobs]) # => true
|
|
28
|
+
#
|
|
29
|
+
# @!parse
|
|
30
|
+
# class Map
|
|
31
|
+
# # @!macro map_initialization
|
|
32
|
+
# # @!macro modes
|
|
33
|
+
# # @param mode [Symbol] The default transfer mode for values. Defaults to `:copy`.
|
|
34
|
+
# # @return [Map]
|
|
35
|
+
# def initialize(
|
|
36
|
+
# initial_mapping = nil,
|
|
37
|
+
# mode: :copy,
|
|
38
|
+
# normalize_keys: nil,
|
|
39
|
+
# compare_by_identity: false,
|
|
40
|
+
# compare_keys_by_identity: compare_by_identity,
|
|
41
|
+
# compare_values_by_identity: compare_by_identity
|
|
42
|
+
# )
|
|
43
|
+
# end
|
|
44
|
+
# end
|
|
45
|
+
class Map < Abstract::ConcurrentMap
|
|
46
|
+
include Internal::MapValueModes
|
|
47
|
+
include Shareable::Delegated
|
|
48
|
+
|
|
49
|
+
private
|
|
50
|
+
|
|
51
|
+
def new_map(**) = Internal::StrictMap.new(**)
|
|
52
|
+
def freeze_backend = @map
|
|
53
|
+
|
|
54
|
+
Internal.prepare_map_access(self, :modes)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
Internal::KeyNormalizer.prepare_concurrent_class(Internal::StrictMap)
|
|
58
|
+
end
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# shareable_constant_value: literal
|
|
3
|
+
# warn_indent: true
|
|
4
|
+
|
|
5
|
+
module Farce
|
|
6
|
+
# A mode manager encapsulates logic for dealing with Ruby objects that aren't Ractor-shareable.
|
|
7
|
+
# It is a perfect companion piece for APIs that require Ractor-shareable objects.
|
|
8
|
+
#
|
|
9
|
+
# For instance, [Ratomic::Queue](https://mperham.github.io/ratomic/Ratomic/Queue.html) expects Ractor-shareable
|
|
10
|
+
# objects without checking for them. Creating a thin wrapper with a mode manager allows you to use it safely and with
|
|
11
|
+
# non-shareable objects.
|
|
12
|
+
#
|
|
13
|
+
# ```ruby
|
|
14
|
+
# require "ratomic"
|
|
15
|
+
# require "farce"
|
|
16
|
+
#
|
|
17
|
+
# class MyQueue
|
|
18
|
+
# include Farce::Shareable
|
|
19
|
+
#
|
|
20
|
+
# def initialize(capacity: 1024, mode: :copy)
|
|
21
|
+
# @queue = Ratomic::Queue.new(capacity)
|
|
22
|
+
# @manager = Farce::ModeManager.new(mode:)
|
|
23
|
+
# end
|
|
24
|
+
#
|
|
25
|
+
# def pop = @manager.unwrap(@queue.pop)
|
|
26
|
+
#
|
|
27
|
+
# def push(value, mode: nil)
|
|
28
|
+
# @queue.push(@manager.wrap(value, mode:))
|
|
29
|
+
# self
|
|
30
|
+
# end
|
|
31
|
+
# end
|
|
32
|
+
#
|
|
33
|
+
# queue = MyQueue.new(capacity: 100, mode: :move)
|
|
34
|
+
#
|
|
35
|
+
# # consume the queue on a different Ractor
|
|
36
|
+
# Ractor.new(queue) { |q| p Ractor.shareable?(q.pop) }
|
|
37
|
+
#
|
|
38
|
+
# object = Object.new
|
|
39
|
+
# q.push(object)
|
|
40
|
+
#
|
|
41
|
+
# # push moved the object into the queue, so it is no longer accessible on the current Ractor
|
|
42
|
+
# Ractor::MovedObject === object # => true
|
|
43
|
+
# ```
|
|
44
|
+
#
|
|
45
|
+
# It is recommended to use a different mode manager for each instance of a queue, port, or whatever else you're
|
|
46
|
+
# wrapping, as {#wrap} may create an {Envelope} and {#unwrap} will only unwrap envelopes created by the same mode
|
|
47
|
+
# manager. This way users can still safely send envelopes they created between Ractors without them getting
|
|
48
|
+
# unexpectedly claimed.
|
|
49
|
+
#
|
|
50
|
+
# @!macro modes
|
|
51
|
+
class ModeManager
|
|
52
|
+
include Internal::Copyable
|
|
53
|
+
include Shareable::Immutable
|
|
54
|
+
|
|
55
|
+
# @return [Symbol] The default mode to use when wrapping values.
|
|
56
|
+
attr_reader :mode
|
|
57
|
+
|
|
58
|
+
# @!macro modes
|
|
59
|
+
# @param mode [Symbol] The default mode to use when wrapping values. Must be one of the valid modes.
|
|
60
|
+
# @param register [Proxy::Register, nil] The register to use for generating Farce::Proxy instances.
|
|
61
|
+
def initialize(mode: :copy, register: nil)
|
|
62
|
+
raise ArgumentError, "invalid mode: #{mode.inspect}" unless Farce::MODES.include?(mode)
|
|
63
|
+
@mode = mode
|
|
64
|
+
@register = register
|
|
65
|
+
super()
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# @api private
|
|
69
|
+
def marshal_dump
|
|
70
|
+
raise TypeError, "proxy registers cannot be marshaled" if @register
|
|
71
|
+
[1, mode]
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# @api private
|
|
75
|
+
def marshal_load(data)
|
|
76
|
+
mode, = Internal::MarshalSupport.payload(data, 1)
|
|
77
|
+
initialize(mode:)
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# Wraps a value in an envelope if it is not Ractor-shareable.
|
|
81
|
+
#
|
|
82
|
+
# @!macro modes
|
|
83
|
+
# @param value [BasicObject] The value to wrap.
|
|
84
|
+
# @param mode [Symbol, nil] The mode to use when wrapping the value. If `nil`, the default mode will be used.
|
|
85
|
+
# @return [BasicObject, Envelope]
|
|
86
|
+
# The prepared value, or a managed envelope containing it.
|
|
87
|
+
def wrap(value, mode: nil)
|
|
88
|
+
return value if Ractor.shareable?(value)
|
|
89
|
+
|
|
90
|
+
case mode || self.mode
|
|
91
|
+
when :copy then Envelope::Copy.new(value, self)
|
|
92
|
+
when :dedup then Ractor.make_shareable(Farce.dedup(value))
|
|
93
|
+
when :local then Envelope::Local.new(value, self)
|
|
94
|
+
when :make_shareable then Ractor.make_shareable(value)
|
|
95
|
+
when :move then Envelope::Move.new(value, self)
|
|
96
|
+
when :mutable then Mutable.new(value)
|
|
97
|
+
when :raise then raise Ractor::IsolationError, "value is not Ractor-shareable: #{value.inspect}"
|
|
98
|
+
when :shareable_copy then Ractor.make_shareable(value, copy: true)
|
|
99
|
+
when :proxy then Proxy.new(value, register: @register)
|
|
100
|
+
else raise ArgumentError, "invalid mode: #{mode.inspect}"
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
# Unwraps any envelope created by this mode manager, returning the value inside.
|
|
105
|
+
# Other options, including other envelopes, will be returned as-is.
|
|
106
|
+
# @param value [BasicObject, Envelope] The value to unwrap.
|
|
107
|
+
# @return [BasicObject]
|
|
108
|
+
# The value inside the envelope if it was created by this mode manager, or the value itself if it was not.
|
|
109
|
+
def unwrap(value)
|
|
110
|
+
return value unless managed_envelope?(value)
|
|
111
|
+
value.value
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
# Compares two values managed by this instance. Compatible envelopes can be
|
|
115
|
+
# compared without claiming or opening them.
|
|
116
|
+
# @param left [BasicObject, Envelope] The stored value.
|
|
117
|
+
# @param right [BasicObject, Envelope] The comparison value.
|
|
118
|
+
# @param identity [Boolean] Whether to compare by identity instead of equality.
|
|
119
|
+
# @return [Boolean] Whether the values match.
|
|
120
|
+
def same_value?(left, right, identity: false)
|
|
121
|
+
left_managed = managed_envelope?(left)
|
|
122
|
+
right_managed = managed_envelope?(right)
|
|
123
|
+
|
|
124
|
+
if left_managed && right_managed
|
|
125
|
+
return false if identity && Envelope::Move === left && Envelope::Local === right && !left.owned?
|
|
126
|
+
return left.same_value?(right, identity:)
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
return false if identity && left_managed
|
|
130
|
+
return left.same_value?(right) if left_managed
|
|
131
|
+
|
|
132
|
+
left = unwrap(left)
|
|
133
|
+
right = unwrap(right)
|
|
134
|
+
return BasicObject.instance_method(:equal?).bind_call(left, right) if identity
|
|
135
|
+
|
|
136
|
+
left == right
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
# @return [Boolean] Whether the value is an envelope managed by this mode manager.
|
|
140
|
+
def managed_envelope?(value) = Envelope === value && value.auto_unwrap.equal?(self)
|
|
141
|
+
end
|
|
142
|
+
end
|