farce 0.0.1.alpha2-x86-linux-gnu
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/CODE_OF_CONDUCT.md +26 -0
- data/CONTRIBUTING.md +71 -0
- data/MIT-LICENSE +20 -0
- data/README.md +1524 -0
- data/SECURITY.md +10 -0
- data/docs/benchmarks.md +185 -0
- data/docs/gems/dry-types.md +290 -0
- data/docs/gems/msgpack.md +70 -0
- data/docs/gems/ractor-shim.md +63 -0
- data/docs/modes.md +628 -0
- data/docs/scopes.md +649 -0
- data/docs/variants.md +346 -0
- data/ext/ext_helper.rb +20 -0
- data/ext/farce/README.md +21 -0
- data/ext/farce/atom.c +1023 -0
- data/ext/farce/bounded_map.c +1683 -0
- data/ext/farce/containers.h +77 -0
- data/ext/farce/counter.c +399 -0
- data/ext/farce/darwin.c +100 -0
- data/ext/farce/depend +12 -0
- data/ext/farce/dict.c +1523 -0
- data/ext/farce/dict.h +152 -0
- data/ext/farce/drivers.c +237 -0
- data/ext/farce/exchanger.c +299 -0
- data/ext/farce/extconf.rb +99 -0
- data/ext/farce/farce.c +359 -0
- data/ext/farce/flag.c +288 -0
- data/ext/farce/io.c +338 -0
- data/ext/farce/lock.c +510 -0
- data/ext/farce/map.c +2240 -0
- data/ext/farce/priority_queue.c +2056 -0
- data/ext/farce/queue.c +1059 -0
- data/ext/farce/reactor.c +820 -0
- data/ext/farce/reactor.h +103 -0
- data/ext/farce/shareable.h +31 -0
- data/ext/farce/signal.c +350 -0
- data/ext/farce/transaction.c +354 -0
- data/ext/farce/transaction.h +40 -0
- data/ext/farce/tree_map.c +1953 -0
- data/ext/farce/trie.c +2020 -0
- data/ext/farce/unshareable.c +155 -0
- data/ext/farce/unshared_io_pool.h +234 -0
- data/ext/farce/unshared_signal.c +263 -0
- data/ext/farce/unshared_wait.h +193 -0
- data/ext/farce/unsupported.c +6 -0
- data/ext/farce/vector.c +1195 -0
- data/ext/farce/weak_map.c +1714 -0
- data/ext/java/org/farce/BoundedMap.java +394 -0
- data/ext/java/org/farce/FiberScheduler.java +141 -0
- data/ext/java/org/farce/PriorityKey.java +45 -0
- data/ext/java/org/farce/PriorityQueue.java +373 -0
- data/ext/java/org/farce/QueueSignal.java +28 -0
- data/ext/rebind/README.md +14 -0
- data/ext/rebind/extconf.rb +10 -0
- data/ext/rebind/rebind.c +186 -0
- data/lib/farce/_yard/internal.rb +13 -0
- data/lib/farce/_yard/macros.rb +61 -0
- data/lib/farce/_yard/ractor.rb +46 -0
- data/lib/farce/abstract/atom.rb +186 -0
- data/lib/farce/abstract/bounded_map.rb +232 -0
- data/lib/farce/abstract/collection.rb +151 -0
- data/lib/farce/abstract/concurrent_map.rb +381 -0
- data/lib/farce/abstract/counter.rb +193 -0
- data/lib/farce/abstract/duplicable_map.rb +229 -0
- data/lib/farce/abstract/exchanger.rb +26 -0
- data/lib/farce/abstract/flag.rb +71 -0
- data/lib/farce/abstract/lazy.rb +115 -0
- data/lib/farce/abstract/lease.rb +104 -0
- data/lib/farce/abstract/lease_map.rb +261 -0
- data/lib/farce/abstract/lease_pool.rb +91 -0
- data/lib/farce/abstract/lfu_map.rb +20 -0
- data/lib/farce/abstract/lru_map.rb +25 -0
- data/lib/farce/abstract/map.rb +345 -0
- data/lib/farce/abstract/molecule.rb +245 -0
- data/lib/farce/abstract/port.rb +74 -0
- data/lib/farce/abstract/priority_queue.rb +117 -0
- data/lib/farce/abstract/queue.rb +269 -0
- data/lib/farce/abstract/scheduler.rb +111 -0
- data/lib/farce/abstract/set.rb +910 -0
- data/lib/farce/abstract/sorted_set.rb +172 -0
- data/lib/farce/abstract/timer_queue.rb +136 -0
- data/lib/farce/abstract/tree_map.rb +269 -0
- data/lib/farce/abstract/value.rb +68 -0
- data/lib/farce/abstract/vector.rb +1126 -0
- data/lib/farce/abstract/weak_atom.rb +32 -0
- data/lib/farce/abstract/weak_key_map.rb +12 -0
- data/lib/farce/abstract/weak_map.rb +13 -0
- data/lib/farce/abstract/weak_set.rb +12 -0
- data/lib/farce/abstract/weak_value_map.rb +12 -0
- data/lib/farce/abstract.rb +17 -0
- data/lib/farce/atom.rb +293 -0
- data/lib/farce/class_mirror.rb +87 -0
- data/lib/farce/clock.rb +121 -0
- data/lib/farce/config.rb +229 -0
- data/lib/farce/counter.rb +71 -0
- data/lib/farce/deduper.rb +122 -0
- data/lib/farce/engine/jruby/bounded_map.rb +314 -0
- data/lib/farce/engine/jruby/fiber_scheduler.jar +0 -0
- data/lib/farce/engine/jruby/fiber_scheduler.rb +119 -0
- data/lib/farce/engine/jruby/lease_waiting.rb +19 -0
- data/lib/farce/engine/jruby/map.rb +505 -0
- data/lib/farce/engine/jruby/mutable_numeric_copy.rb +42 -0
- data/lib/farce/engine/jruby/signal.rb +147 -0
- data/lib/farce/engine/jruby.rb +63 -0
- data/lib/farce/engine/jvm/concurrent_weak_registry.rb +55 -0
- data/lib/farce/engine/jvm/counter.rb +102 -0
- data/lib/farce/engine/jvm/extension.rb +32 -0
- data/lib/farce/engine/jvm/farce.jar +0 -0
- data/lib/farce/engine/jvm/flag.rb +79 -0
- data/lib/farce/engine/jvm/priority_queue.rb +217 -0
- data/lib/farce/engine/jvm/tree_map.rb +350 -0
- data/lib/farce/engine/jvm/types.rb +180 -0
- data/lib/farce/engine/jvm.rb +19 -0
- data/lib/farce/engine/ruby/3.4/farce.so +0 -0
- data/lib/farce/engine/ruby/3.4/fiber_scheduler.rb +20 -0
- data/lib/farce/engine/ruby/3.4/port.rb +186 -0
- data/lib/farce/engine/ruby/3.4/ractor_methods.rb +26 -0
- data/lib/farce/engine/ruby/3.4/ractor_selector.rb +92 -0
- data/lib/farce/engine/ruby/3.4/rebind.so +0 -0
- data/lib/farce/engine/ruby/3.4/vault.rb +56 -0
- data/lib/farce/engine/ruby/4.0/farce.so +0 -0
- data/lib/farce/engine/ruby/4.0/port.rb +19 -0
- data/lib/farce/engine/ruby/4.0/ractor_methods.rb +20 -0
- data/lib/farce/engine/ruby/4.0/ractor_selector.rb +108 -0
- data/lib/farce/engine/ruby/4.0/rebind.so +0 -0
- data/lib/farce/engine/ruby/4.0/vault.rb +74 -0
- data/lib/farce/engine/ruby/4.1/port.rb +21 -0
- data/lib/farce/engine/ruby/4.1/ractor_methods.rb +22 -0
- data/lib/farce/engine/ruby/4.1/ractor_selector.rb +25 -0
- data/lib/farce/engine/ruby/4.1/vault.rb +5 -0
- data/lib/farce/engine/ruby/fiber_scheduler.rb +32 -0
- data/lib/farce/engine/ruby/key_lock_map.rb +28 -0
- data/lib/farce/engine/ruby/shared/lease.rb +26 -0
- data/lib/farce/engine/ruby/shared/lease_pool.rb +24 -0
- data/lib/farce/engine/ruby/shared/main_scheduler.rb +9 -0
- data/lib/farce/engine/ruby/shared/parallel_scheduler.rb +9 -0
- data/lib/farce/engine/ruby/shared/proxy_owner.rb +31 -0
- data/lib/farce/engine/ruby/shared/ractor_methods.rb +34 -0
- data/lib/farce/engine/ruby/shared/ractor_selector.rb +328 -0
- data/lib/farce/engine/ruby/shared/strict_map.rb +13 -0
- data/lib/farce/engine/ruby/shared/unshared_vector.rb +14 -0
- data/lib/farce/engine/ruby/shared/vault.rb +144 -0
- data/lib/farce/engine/ruby/shared/vault_weak_map.rb +336 -0
- data/lib/farce/engine/ruby/shared/weak_atom.rb +54 -0
- data/lib/farce/engine/ruby/shared/weak_map.rb +413 -0
- data/lib/farce/engine/ruby.rb +130 -0
- data/lib/farce/engine/shared/atom.rb +10 -0
- data/lib/farce/engine/shared/exchanger.rb +130 -0
- data/lib/farce/engine/shared/identity_key.rb +22 -0
- data/lib/farce/engine/shared/lease.rb +10 -0
- data/lib/farce/engine/shared/lease_pool.rb +10 -0
- data/lib/farce/engine/shared/main_scheduler.rb +20 -0
- data/lib/farce/engine/shared/map_key_coordination.rb +226 -0
- data/lib/farce/engine/shared/parallel_scheduler.rb +9 -0
- data/lib/farce/engine/shared/port.rb +44 -0
- data/lib/farce/engine/shared/portable_bounded_map.rb +619 -0
- data/lib/farce/engine/shared/proxy_owner.rb +43 -0
- data/lib/farce/engine/shared/queue.rb +233 -0
- data/lib/farce/engine/shared/ractor_methods.rb +73 -0
- data/lib/farce/engine/shared/rebindable.rb +15 -0
- data/lib/farce/engine/shared/strict_atom.rb +73 -0
- data/lib/farce/engine/shared/strict_map.rb +117 -0
- data/lib/farce/engine/shared/strict_queue_values.rb +27 -0
- data/lib/farce/engine/shared/strict_tree_map.rb +56 -0
- data/lib/farce/engine/shared/transaction_map_backend.rb +20 -0
- data/lib/farce/engine/shared/trie.rb +317 -0
- data/lib/farce/engine/shared/trie_builder.rb +199 -0
- data/lib/farce/engine/shared/unshareable.rb +14 -0
- data/lib/farce/engine/shared/unshared_atom.rb +256 -0
- data/lib/farce/engine/shared/unshared_priority_queue.rb +9 -0
- data/lib/farce/engine/shared/unshared_queue.rb +18 -0
- data/lib/farce/engine/shared/unshared_signal.rb +21 -0
- data/lib/farce/engine/shared/unshared_vector.rb +377 -0
- data/lib/farce/engine/shared/unshared_weak_atom.rb +30 -0
- data/lib/farce/engine/shared/unshared_weak_map.rb +445 -0
- data/lib/farce/engine/shared/vault.rb +31 -0
- data/lib/farce/engine/shared/vector.rb +83 -0
- data/lib/farce/engine/shared/weak_atom/base.rb +189 -0
- data/lib/farce/engine/shared/weak_atom.rb +20 -0
- data/lib/farce/engine/shared/weak_map/cell.rb +99 -0
- data/lib/farce/engine/shared/weak_map/index.rb +249 -0
- data/lib/farce/engine/shared/weak_map/lock.rb +80 -0
- data/lib/farce/engine/shared/weak_map/reference.rb +33 -0
- data/lib/farce/engine/shared.rb +63 -0
- data/lib/farce/engine/truffleruby/fiber_scheduler.rb +13 -0
- data/lib/farce/engine/truffleruby/lock.rb +121 -0
- data/lib/farce/engine/truffleruby/map.rb +656 -0
- data/lib/farce/engine/truffleruby/native/counter.rb +105 -0
- data/lib/farce/engine/truffleruby/native/flag.rb +77 -0
- data/lib/farce/engine/truffleruby/native/ordered_array_support.rb +67 -0
- data/lib/farce/engine/truffleruby/native/priority_queue.rb +388 -0
- data/lib/farce/engine/truffleruby/native/tree_map.rb +51 -0
- data/lib/farce/engine/truffleruby/native/unsafe_tree_map.rb +350 -0
- data/lib/farce/engine/truffleruby/signal.rb +77 -0
- data/lib/farce/engine/truffleruby.rb +148 -0
- data/lib/farce/envelope.rb +335 -0
- data/lib/farce/error.rb +35 -0
- data/lib/farce/exchanger.rb +41 -0
- data/lib/farce/flag.rb +27 -0
- data/lib/farce/integrations/active_support/blank.rb +68 -0
- data/lib/farce/integrations/active_support/clock.rb +17 -0
- data/lib/farce/integrations/active_support/duplicable.rb +78 -0
- data/lib/farce/integrations/active_support/map.rb +175 -0
- data/lib/farce/integrations/active_support/set.rb +60 -0
- data/lib/farce/integrations/active_support/value_serialization.rb +17 -0
- data/lib/farce/integrations/active_support/vector.rb +231 -0
- data/lib/farce/integrations/active_support.rb +9 -0
- data/lib/farce/integrations/activesupport.rb +5 -0
- data/lib/farce/integrations/bson.rb +108 -0
- data/lib/farce/integrations/cbor.rb +51 -0
- data/lib/farce/integrations/concurrent.rb +112 -0
- data/lib/farce/integrations/dry-types.rb +5 -0
- data/lib/farce/integrations/dry_types.rb +694 -0
- data/lib/farce/integrations/json.rb +7 -0
- data/lib/farce/integrations/msgpack.rb +112 -0
- data/lib/farce/integrations/oj.rb +69 -0
- data/lib/farce/integrations/psych.rb +191 -0
- data/lib/farce/integrations/ractor-sharing.rb +5 -0
- data/lib/farce/integrations/ractor-tmvar.rb +5 -0
- data/lib/farce/integrations/ractor_sharing.rb +172 -0
- data/lib/farce/integrations/ractor_tmvar.rb +64 -0
- data/lib/farce/integrations/shared/to_json.rb +42 -0
- data/lib/farce/integrations/sorted_set.rb +11 -0
- data/lib/farce/integrations/weakref.rb +38 -0
- data/lib/farce/integrations/yajl.rb +10 -0
- data/lib/farce/integrations.rb +156 -0
- data/lib/farce/internal/_frozen_config.rb +11 -0
- data/lib/farce/internal/autoloads.rb +47 -0
- data/lib/farce/internal/blocking_priority_queue.rb +122 -0
- data/lib/farce/internal/converter.rb +106 -0
- data/lib/farce/internal/copyable.rb +31 -0
- data/lib/farce/internal/delegation.rb +20 -0
- data/lib/farce/internal/external_transaction.rb +59 -0
- data/lib/farce/internal/fake_ractor.rb +179 -0
- data/lib/farce/internal/freeze.rb +118 -0
- data/lib/farce/internal/inspect.rb +157 -0
- data/lib/farce/internal/key_lock_map.rb +29 -0
- data/lib/farce/internal/key_normalizer.rb +289 -0
- data/lib/farce/internal/lease_initialization.rb +115 -0
- data/lib/farce/internal/lease_map.rb +441 -0
- data/lib/farce/internal/lease_pool_state.rb +228 -0
- data/lib/farce/internal/lease_state.rb +342 -0
- data/lib/farce/internal/lease_waiting.rb +13 -0
- data/lib/farce/internal/managed_queue.rb +26 -0
- data/lib/farce/internal/map_value_modes.rb +227 -0
- data/lib/farce/internal/marshal_support.rb +227 -0
- data/lib/farce/internal/mixin.rb +20 -0
- data/lib/farce/internal/mutable_ordered_key_lock_map.rb +63 -0
- data/lib/farce/internal/noncopyable.rb +17 -0
- data/lib/farce/internal/ordered_key_lock_map.rb +69 -0
- data/lib/farce/internal/pool_supervisor.rb +41 -0
- data/lib/farce/internal/pool_worker.rb +61 -0
- data/lib/farce/internal/portable_transaction/reservation_entry.rb +29 -0
- data/lib/farce/internal/portable_transaction/strong_map_entry.rb +137 -0
- data/lib/farce/internal/portable_transaction/strong_map_size_entry.rb +44 -0
- data/lib/farce/internal/portable_transaction/tree_entry.rb +64 -0
- data/lib/farce/internal/portable_transaction.rb +164 -0
- data/lib/farce/internal/proxy_owner_notifications.rb +29 -0
- data/lib/farce/internal/reservation_waiting.rb +24 -0
- data/lib/farce/internal/scheduled_task.rb +48 -0
- data/lib/farce/internal/scheduler_io.rb +171 -0
- data/lib/farce/internal/scheduler_lifecycle.rb +314 -0
- data/lib/farce/internal/select_scheduler.rb +235 -0
- data/lib/farce/internal/storage.rb +162 -0
- data/lib/farce/internal/strict_lease.rb +30 -0
- data/lib/farce/internal/strict_lease_map.rb +20 -0
- data/lib/farce/internal/strict_lease_pool.rb +29 -0
- data/lib/farce/internal/thread_pool.rb +166 -0
- data/lib/farce/internal/transaction_conflict.rb +9 -0
- data/lib/farce/internal/transaction_freeze_guard.rb +30 -0
- data/lib/farce/internal/transaction_map_snapshot.rb +123 -0
- data/lib/farce/internal/undefined.rb +22 -0
- data/lib/farce/internal/unshared_lease.rb +29 -0
- data/lib/farce/internal/unshared_lease_pool.rb +34 -0
- data/lib/farce/internal/unshared_queue_waiting.rb +28 -0
- data/lib/farce/internal/value_serialization.rb +13 -0
- data/lib/farce/internal/weak_map_value_modes.rb +58 -0
- data/lib/farce/internal/weak_mode_manager.rb +26 -0
- data/lib/farce/internal.rb +129 -0
- data/lib/farce/lazy.rb +100 -0
- data/lib/farce/lazy_ref.rb +40 -0
- data/lib/farce/lease.rb +28 -0
- data/lib/farce/lease_map.rb +34 -0
- data/lib/farce/lease_pool.rb +29 -0
- data/lib/farce/lfu_map.rb +53 -0
- data/lib/farce/local/atom.rb +23 -0
- data/lib/farce/local/counter.rb +88 -0
- data/lib/farce/local/flag.rb +65 -0
- data/lib/farce/local/lazy.rb +38 -0
- data/lib/farce/local/lazy_ref.rb +36 -0
- data/lib/farce/local/lease.rb +77 -0
- data/lib/farce/local/lease_map.rb +80 -0
- data/lib/farce/local/lease_pool.rb +38 -0
- data/lib/farce/local/lfu_map.rb +55 -0
- data/lib/farce/local/lru_map.rb +62 -0
- data/lib/farce/local/map.rb +47 -0
- data/lib/farce/local/molecule.rb +45 -0
- data/lib/farce/local/priority_queue.rb +53 -0
- data/lib/farce/local/queue.rb +32 -0
- data/lib/farce/local/scoped.rb +166 -0
- data/lib/farce/local/set.rb +18 -0
- data/lib/farce/local/sorted_set.rb +50 -0
- data/lib/farce/local/timer_queue.rb +41 -0
- data/lib/farce/local/tree_map.rb +47 -0
- data/lib/farce/local/vector.rb +30 -0
- data/lib/farce/local/weak_atom.rb +23 -0
- data/lib/farce/local/weak_key_map.rb +39 -0
- data/lib/farce/local/weak_map.rb +39 -0
- data/lib/farce/local/weak_set.rb +18 -0
- data/lib/farce/local/weak_value_map.rb +39 -0
- data/lib/farce/local.rb +35 -0
- data/lib/farce/lock.rb +54 -0
- data/lib/farce/lru_map.rb +63 -0
- data/lib/farce/map.rb +58 -0
- data/lib/farce/mode_manager.rb +142 -0
- data/lib/farce/molecule.rb +60 -0
- data/lib/farce/mutable.rb +171 -0
- data/lib/farce/pool.rb +332 -0
- data/lib/farce/port.rb +134 -0
- data/lib/farce/priority_queue.rb +70 -0
- data/lib/farce/proxy/register.rb +101 -0
- data/lib/farce/proxy/supervisor.rb +137 -0
- data/lib/farce/proxy/wrapper.rb +49 -0
- data/lib/farce/proxy.rb +291 -0
- data/lib/farce/queue.rb +60 -0
- data/lib/farce/ractor.rb +269 -0
- data/lib/farce/read_write_lock.rb +256 -0
- data/lib/farce/reference.rb +158 -0
- data/lib/farce/resolv/dns.rb +42 -0
- data/lib/farce/resolv.rb +85 -0
- data/lib/farce/scheduler.rb +536 -0
- data/lib/farce/set.rb +39 -0
- data/lib/farce/shareable.rb +150 -0
- data/lib/farce/signal.rb +97 -0
- data/lib/farce/sorted_set.rb +36 -0
- data/lib/farce/strict/atom.rb +34 -0
- data/lib/farce/strict/counter.rb +10 -0
- data/lib/farce/strict/exchanger.rb +25 -0
- data/lib/farce/strict/flag.rb +10 -0
- data/lib/farce/strict/lazy.rb +26 -0
- data/lib/farce/strict/lazy_ref.rb +26 -0
- data/lib/farce/strict/lease.rb +27 -0
- data/lib/farce/strict/lease_map.rb +33 -0
- data/lib/farce/strict/lease_pool.rb +28 -0
- data/lib/farce/strict/lfu_map.rb +27 -0
- data/lib/farce/strict/lru_map.rb +28 -0
- data/lib/farce/strict/map.rb +54 -0
- data/lib/farce/strict/molecule.rb +16 -0
- data/lib/farce/strict/port.rb +40 -0
- data/lib/farce/strict/priority_queue.rb +20 -0
- data/lib/farce/strict/queue.rb +17 -0
- data/lib/farce/strict/set.rb +14 -0
- data/lib/farce/strict/sorted_set.rb +19 -0
- data/lib/farce/strict/timer_queue.rb +13 -0
- data/lib/farce/strict/tree_map.rb +27 -0
- data/lib/farce/strict/vector.rb +24 -0
- data/lib/farce/strict/weak_atom.rb +31 -0
- data/lib/farce/strict/weak_key_map.rb +49 -0
- data/lib/farce/strict/weak_map.rb +49 -0
- data/lib/farce/strict/weak_set.rb +14 -0
- data/lib/farce/strict/weak_value_map.rb +48 -0
- data/lib/farce/strict.rb +28 -0
- data/lib/farce/system.rb +40 -0
- data/lib/farce/thread_scheduler.rb +70 -0
- data/lib/farce/timer_queue.rb +66 -0
- data/lib/farce/transaction/atom.rb +73 -0
- data/lib/farce/transaction/map.rb +25 -0
- data/lib/farce/transaction/map_operations.rb +143 -0
- data/lib/farce/transaction/molecule.rb +90 -0
- data/lib/farce/transaction/mutable.rb +61 -0
- data/lib/farce/transaction/set.rb +17 -0
- data/lib/farce/transaction/set_operations.rb +48 -0
- data/lib/farce/transaction/sorted_set.rb +18 -0
- data/lib/farce/transaction/tree_map.rb +78 -0
- data/lib/farce/transaction/vector.rb +139 -0
- data/lib/farce/transaction/wrapper.rb +202 -0
- data/lib/farce/transaction.rb +343 -0
- data/lib/farce/tree_map.rb +56 -0
- data/lib/farce/unsafe/lfu_map.rb +36 -0
- data/lib/farce/unsafe/lru_map.rb +36 -0
- data/lib/farce/unsafe/tree_map.rb +21 -0
- data/lib/farce/unsafe.rb +29 -0
- data/lib/farce/unshareable.rb +67 -0
- data/lib/farce/unshared/atom.rb +24 -0
- data/lib/farce/unshared/counter.rb +10 -0
- data/lib/farce/unshared/flag.rb +10 -0
- data/lib/farce/unshared/lazy.rb +38 -0
- data/lib/farce/unshared/lazy_ref.rb +26 -0
- data/lib/farce/unshared/lease.rb +27 -0
- data/lib/farce/unshared/lease_map.rb +26 -0
- data/lib/farce/unshared/lease_pool.rb +25 -0
- data/lib/farce/unshared/lfu_map.rb +20 -0
- data/lib/farce/unshared/lru_map.rb +20 -0
- data/lib/farce/unshared/map.rb +49 -0
- data/lib/farce/unshared/molecule.rb +16 -0
- data/lib/farce/unshared/priority_queue.rb +16 -0
- data/lib/farce/unshared/queue.rb +27 -0
- data/lib/farce/unshared/set.rb +14 -0
- data/lib/farce/unshared/sorted_set.rb +28 -0
- data/lib/farce/unshared/timer_queue.rb +16 -0
- data/lib/farce/unshared/tree_map.rb +21 -0
- data/lib/farce/unshared/vector.rb +19 -0
- data/lib/farce/unshared/weak_atom.rb +31 -0
- data/lib/farce/unshared/weak_key_map.rb +44 -0
- data/lib/farce/unshared/weak_map.rb +44 -0
- data/lib/farce/unshared/weak_set.rb +14 -0
- data/lib/farce/unshared/weak_value_map.rb +43 -0
- data/lib/farce/unshared.rb +28 -0
- data/lib/farce/vector.rb +228 -0
- data/lib/farce/version.rb +8 -0
- data/lib/farce/walker/definitions.rb +186 -0
- data/lib/farce/walker/modification.rb +355 -0
- data/lib/farce/walker.rb +319 -0
- data/lib/farce/weak_atom.rb +121 -0
- data/lib/farce/weak_key_map.rb +31 -0
- data/lib/farce/weak_map.rb +33 -0
- data/lib/farce/weak_ref.rb +67 -0
- data/lib/farce/weak_set.rb +82 -0
- data/lib/farce/weak_value.rb +195 -0
- data/lib/farce/weak_value_map.rb +32 -0
- data/lib/farce.rb +519 -0
- metadata +468 -0
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# shareable_constant_value: literal
|
|
3
|
+
# warn_indent: true
|
|
4
|
+
|
|
5
|
+
# @!group ActiveSupport Integration
|
|
6
|
+
require "farce"
|
|
7
|
+
require "msgpack"
|
|
8
|
+
|
|
9
|
+
module Farce
|
|
10
|
+
class Abstract::Vector
|
|
11
|
+
# Serialize a snapshot as a MessagePack array.
|
|
12
|
+
# @note This method is only available if MessagePack has been loaded.
|
|
13
|
+
# @overload to_msgpack(*arguments)
|
|
14
|
+
# @param arguments [Array<Object>] Arguments forwarded to Array#to_msgpack.
|
|
15
|
+
# @return [String, ::MessagePack::Packer] The encoded bytes or supplied packer.
|
|
16
|
+
def to_msgpack(...) = to_a.to_msgpack(...)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
class Abstract::Map
|
|
20
|
+
# Serialize current entries as a MessagePack map.
|
|
21
|
+
# @note This method is only available if MessagePack has been loaded.
|
|
22
|
+
# @overload to_msgpack(*arguments)
|
|
23
|
+
# @param arguments [Array<Object>] Arguments forwarded to Hash#to_msgpack.
|
|
24
|
+
# @return [String, ::MessagePack::Packer] The encoded bytes or supplied packer.
|
|
25
|
+
def to_msgpack(...) = to_h.to_msgpack(...)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
class Abstract::Set
|
|
29
|
+
# Serialize current members as a MessagePack array.
|
|
30
|
+
# @note This method is only available if MessagePack has been loaded.
|
|
31
|
+
# @overload to_msgpack(*arguments)
|
|
32
|
+
# @param arguments [Array<Object>] Arguments forwarded to Array#to_msgpack.
|
|
33
|
+
# @return [String, ::MessagePack::Packer] The encoded bytes or supplied packer.
|
|
34
|
+
def to_msgpack(...) = to_a.to_msgpack(...)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
module Internal::ValueSerialization
|
|
38
|
+
# Serialize the current value as its primitive MessagePack equivalent.
|
|
39
|
+
# @note This method is only available if MessagePack has been loaded.
|
|
40
|
+
# @overload to_msgpack(*arguments)
|
|
41
|
+
# @param arguments [Array<Object>] Arguments forwarded to the value's to_msgpack.
|
|
42
|
+
# @return [String, ::MessagePack::Packer] The encoded bytes or supplied packer.
|
|
43
|
+
def to_msgpack(...) = value.to_msgpack(...)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# @note This module is only available if MessagePack has been loaded.
|
|
47
|
+
module MessagePack
|
|
48
|
+
# Default mapping used by {.factory}.
|
|
49
|
+
DEFAULT_TYPES = { Vector => 0, Map => 1, Counter => 2, Flag => 3, Atom => 4, Set => 5 }.freeze
|
|
50
|
+
|
|
51
|
+
extend self
|
|
52
|
+
|
|
53
|
+
# Build a factory that restores registered Farce classes.
|
|
54
|
+
# Use the same type IDs when writing and reading data.
|
|
55
|
+
# @note This method is only available if MessagePack has been loaded.
|
|
56
|
+
# @param types [Hash{Class => Integer}] Classes and their application extension IDs.
|
|
57
|
+
# @return [::MessagePack::Factory] An independent factory.
|
|
58
|
+
# @!scope class
|
|
59
|
+
def factory(types: DEFAULT_TYPES)
|
|
60
|
+
result = ::MessagePack::Factory.new
|
|
61
|
+
types.each { |klass, type| register_type(result, type, klass) }
|
|
62
|
+
result
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# Register a Farce class on an application-owned factory.
|
|
66
|
+
# Values use the same factory recursively, including nested custom types.
|
|
67
|
+
# Source configuration is replaced by the supplied constructor options.
|
|
68
|
+
# Counters also retain their initial value for reset.
|
|
69
|
+
# @note This method is only available if MessagePack has been loaded.
|
|
70
|
+
# @param factory [::MessagePack::Factory] The factory to configure.
|
|
71
|
+
# @param type [Integer] An unused application extension ID from 0 through 127.
|
|
72
|
+
# @param klass [Class] A concrete Farce vector, map, set, counter, flag, or atom class.
|
|
73
|
+
# @param options [Hash{Symbol => Object}] Constructor options for restored objects.
|
|
74
|
+
# @return [::MessagePack::Factory] The supplied factory.
|
|
75
|
+
# @raise [ArgumentError] If the class is unsupported or the ID or class is already registered.
|
|
76
|
+
# @!scope class
|
|
77
|
+
def register_type(factory, type, klass, **)
|
|
78
|
+
reader = primitive_reader(klass)
|
|
79
|
+
unless type.is_a?(Integer) && (0..127).cover?(type)
|
|
80
|
+
raise ArgumentError, "MessagePack extension IDs must be integers from 0 through 127"
|
|
81
|
+
end
|
|
82
|
+
if factory.registered_types.any? { it[:type] == type || it[:class] == klass }
|
|
83
|
+
raise ArgumentError, "MessagePack extension ID or class is already registered"
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
counter = klass <= Abstract::Counter
|
|
87
|
+
factory.register_type(type, klass,
|
|
88
|
+
packer: lambda { |object, packer|
|
|
89
|
+
packer.write(object.initial) if counter
|
|
90
|
+
packer.write(object.public_send(reader))
|
|
91
|
+
},
|
|
92
|
+
unpacker: lambda { |unpacker|
|
|
93
|
+
object = klass.new(unpacker.read, **)
|
|
94
|
+
object.value = unpacker.read if counter
|
|
95
|
+
object
|
|
96
|
+
},
|
|
97
|
+
recursive: true)
|
|
98
|
+
factory
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
private
|
|
102
|
+
|
|
103
|
+
def primitive_reader(klass)
|
|
104
|
+
raise ArgumentError, "expected a concrete Farce class" unless klass.is_a?(Class)
|
|
105
|
+
return :to_a if klass <= Abstract::Vector || klass <= Abstract::Set
|
|
106
|
+
return :to_h if klass <= Abstract::Map
|
|
107
|
+
return :value if klass <= Abstract::Counter || klass <= Abstract::Flag || klass <= Abstract::Atom
|
|
108
|
+
|
|
109
|
+
raise ArgumentError, "unsupported Farce class: #{klass}"
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
end
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# shareable_constant_value: literal
|
|
3
|
+
# warn_indent: true
|
|
4
|
+
|
|
5
|
+
# @!group Oj Integration
|
|
6
|
+
require "farce"
|
|
7
|
+
require "oj" unless defined?(Oj::VERSION)
|
|
8
|
+
|
|
9
|
+
require "json" unless [].respond_to?(:to_json)
|
|
10
|
+
require_relative "shared/to_json"
|
|
11
|
+
|
|
12
|
+
module Farce
|
|
13
|
+
# Object-mode reconstruction for Oj.
|
|
14
|
+
# @api private
|
|
15
|
+
module Oj
|
|
16
|
+
# @api private
|
|
17
|
+
Constructor = Data.define(:klass, :options, :counter) do
|
|
18
|
+
def call(value, current = nil)
|
|
19
|
+
object = klass.new(value, **options)
|
|
20
|
+
object.value = current if counter
|
|
21
|
+
object
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
private_constant :Constructor
|
|
25
|
+
|
|
26
|
+
extend self
|
|
27
|
+
|
|
28
|
+
# Register a concrete class for Oj object-mode round trips.
|
|
29
|
+
# Registrations are process-global. Configure them before starting concurrent serialization.
|
|
30
|
+
# Repeated registration replaces the previous reconstruction settings for the class.
|
|
31
|
+
# Source settings are replaced by the supplied options. Counters retain their initial value.
|
|
32
|
+
# Constructor options are copied and made Ractor-shareable.
|
|
33
|
+
# Reading move-backed contents can transfer ownership to the serializing Ractor.
|
|
34
|
+
# @param klass [Class] A named Farce vector, map, set, counter, flag, or atom class.
|
|
35
|
+
# @param options [Hash{Symbol => Object}] Constructor options for restored objects.
|
|
36
|
+
# @return [Class] The registered class.
|
|
37
|
+
# @raise [ArgumentError] If the class is unsupported or anonymous.
|
|
38
|
+
# @raise [Ractor::IsolationError] If registration is attempted outside the main native Ractor.
|
|
39
|
+
# @!scope class
|
|
40
|
+
def register_type(klass, **options)
|
|
41
|
+
if Internal.native_ractors? && !Ractor.main?
|
|
42
|
+
raise Ractor::IsolationError, "register Oj types in the main Ractor before concurrent use"
|
|
43
|
+
end
|
|
44
|
+
unless klass.is_a?(Class) && klass.name && !klass.name.start_with?("Farce::Abstract::")
|
|
45
|
+
raise ArgumentError, "expected a named concrete Farce class"
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
reader = if klass <= Abstract::Vector || klass <= Abstract::Set
|
|
49
|
+
:to_a
|
|
50
|
+
elsif klass <= Abstract::Map
|
|
51
|
+
:to_h
|
|
52
|
+
elsif klass <= Abstract::Counter || klass <= Abstract::Flag || klass <= Abstract::Atom
|
|
53
|
+
:value
|
|
54
|
+
else
|
|
55
|
+
raise ArgumentError, "unsupported Farce class: #{klass}"
|
|
56
|
+
end
|
|
57
|
+
counter = klass <= Abstract::Counter
|
|
58
|
+
constructor = Constructor.new(klass, Ractor.make_shareable(options, copy: true), counter)
|
|
59
|
+
Ractor.make_shareable(constructor)
|
|
60
|
+
members = counter ? [:initial, reader] : [reader]
|
|
61
|
+
::Oj.register_odd(klass, constructor, :call, *members)
|
|
62
|
+
klass
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
[Farce::Vector, Farce::Map, Farce::Set, Farce::Counter, Farce::Flag, Farce::Atom].each do |klass|
|
|
68
|
+
Farce::Oj.register_type(klass)
|
|
69
|
+
end
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# shareable_constant_value: literal
|
|
3
|
+
# warn_indent: true
|
|
4
|
+
|
|
5
|
+
# @!group Psych Integration
|
|
6
|
+
require "farce"
|
|
7
|
+
require "psych" unless defined?(Psych::Coder)
|
|
8
|
+
|
|
9
|
+
module Farce
|
|
10
|
+
class Abstract::Map
|
|
11
|
+
# @api private
|
|
12
|
+
# Called by Psych for generating YAML.
|
|
13
|
+
# @param coder [Psych::Coder] The YAML representation.
|
|
14
|
+
# @return [Psych::Coder] The coder containing the serialized fields.
|
|
15
|
+
def encode_with(coder)
|
|
16
|
+
coder["entries"] = to_h
|
|
17
|
+
coder["compare_values_by_identity"] = compare_values_by_identity?
|
|
18
|
+
coder["compare_keys_by_identity"] = compare_keys_by_identity?
|
|
19
|
+
normalizer = instance_variable_get(:@key_normalizer)
|
|
20
|
+
coder["normalize_keys"] = Internal::KeyNormalizer.dump(normalizer) if normalizer
|
|
21
|
+
coder["scope"] = scope if respond_to?(:scope)
|
|
22
|
+
coder
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# @api private
|
|
26
|
+
# Called by Psych when parsing YAML.
|
|
27
|
+
# @param coder [Psych::Coder] The YAML representation.
|
|
28
|
+
# @return [void]
|
|
29
|
+
def init_with(coder)
|
|
30
|
+
options = coder.map.except("entries").transform_keys(&:to_sym)
|
|
31
|
+
if options.key?(:normalize_keys)
|
|
32
|
+
options[:normalize_keys] =
|
|
33
|
+
Internal::KeyNormalizer.restore(options[:normalize_keys])
|
|
34
|
+
end
|
|
35
|
+
initialize(coder["entries"], **options)
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
class Abstract::BoundedMap
|
|
40
|
+
# @api private
|
|
41
|
+
# Called by Psych for generating YAML.
|
|
42
|
+
# @param coder [Psych::Coder] The YAML representation.
|
|
43
|
+
# @return [Psych::Coder] The coder containing the serialized fields.
|
|
44
|
+
def encode_with(coder) = super.tap { it["max_size"] = max_size }
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
module Internal::ValueSerialization
|
|
48
|
+
# @api private
|
|
49
|
+
# Called by Psych for generating YAML.
|
|
50
|
+
# @param coder [Psych::Coder] The YAML representation.
|
|
51
|
+
# @return [Psych::Coder] The coder containing the serialized fields.
|
|
52
|
+
def encode_with(coder)
|
|
53
|
+
coder["value"] = value
|
|
54
|
+
coder["scope"] = scope if respond_to?(:scope)
|
|
55
|
+
coder
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# @api private
|
|
59
|
+
# Called by Psych when parsing YAML.
|
|
60
|
+
# @param coder [Psych::Coder] The YAML representation.
|
|
61
|
+
# @return [void]
|
|
62
|
+
def init_with(coder)
|
|
63
|
+
options = coder.map.except("value").transform_keys(&:to_sym)
|
|
64
|
+
initialize(coder["value"], **options)
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
class Abstract::Atom
|
|
69
|
+
# @api private
|
|
70
|
+
# Called by Psych for generating YAML.
|
|
71
|
+
# @param coder [Psych::Coder] The YAML representation.
|
|
72
|
+
# @return [Psych::Coder] The coder containing the serialized fields.
|
|
73
|
+
def encode_with(coder)
|
|
74
|
+
super
|
|
75
|
+
coder["compare_by_identity"] = compare_by_identity?
|
|
76
|
+
coder["shareable"] = Ractor.shareable?(coder["value"])
|
|
77
|
+
coder
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# @api private
|
|
81
|
+
# Restore the value and its shareability when parsing YAML.
|
|
82
|
+
# @param coder [Psych::Coder] The YAML representation.
|
|
83
|
+
# @return [void]
|
|
84
|
+
def init_with(coder)
|
|
85
|
+
value = coder["value"]
|
|
86
|
+
value = Ractor.make_shareable(value) if coder["shareable"]
|
|
87
|
+
options = coder.map.except("value", "shareable").transform_keys(&:to_sym)
|
|
88
|
+
initialize(value, **options)
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
module Abstract::Counter
|
|
93
|
+
# @api private
|
|
94
|
+
# Called by Psych for generating YAML.
|
|
95
|
+
# @param coder [Psych::Coder] The YAML representation.
|
|
96
|
+
# @return [Psych::Coder] The coder containing the serialized fields.
|
|
97
|
+
def encode_with(coder)
|
|
98
|
+
super
|
|
99
|
+
coder["initial"] = initial
|
|
100
|
+
coder
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
# @api private
|
|
104
|
+
# Called by Psych when parsing YAML.
|
|
105
|
+
# @param coder [Psych::Coder] The YAML representation.
|
|
106
|
+
# @return [void]
|
|
107
|
+
def init_with(coder)
|
|
108
|
+
options = coder.map.except("value", "initial").transform_keys(&:to_sym)
|
|
109
|
+
initialize(coder["initial"], **options)
|
|
110
|
+
self.value = coder["value"]
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
class Atom
|
|
115
|
+
# @api private
|
|
116
|
+
# Called by Psych for generating YAML.
|
|
117
|
+
# @param coder [Psych::Coder] The YAML representation.
|
|
118
|
+
# @return [Psych::Coder] The coder containing the serialized fields.
|
|
119
|
+
def encode_with(coder)
|
|
120
|
+
super
|
|
121
|
+
coder["mode"] = mode
|
|
122
|
+
coder
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
class Abstract::Set
|
|
127
|
+
# @api private
|
|
128
|
+
# Called by Psych for generating YAML.
|
|
129
|
+
# @param coder [Psych::Coder] The YAML representation.
|
|
130
|
+
# @return [Psych::Coder] The coder containing the set representation.
|
|
131
|
+
def encode_with(coder)
|
|
132
|
+
coder["entries"] = each_stored.map do |key, stored|
|
|
133
|
+
element = public_stored(key, stored)
|
|
134
|
+
entry = {
|
|
135
|
+
"value" => element,
|
|
136
|
+
"shareable" => Ractor.shareable?(element),
|
|
137
|
+
}
|
|
138
|
+
if value_modes?
|
|
139
|
+
entry["identity"] = identity_storage_key?(key)
|
|
140
|
+
entry["snapshot"] = key unless entry["identity"]
|
|
141
|
+
entry["mode"] = stored_mode(stored)
|
|
142
|
+
end
|
|
143
|
+
entry
|
|
144
|
+
end
|
|
145
|
+
coder["compare_by_identity"] = compare_by_identity?
|
|
146
|
+
coder["frozen"] = frozen?
|
|
147
|
+
coder["mode"] = mode if respond_to?(:mode)
|
|
148
|
+
coder["scope"] = scope if respond_to?(:scope)
|
|
149
|
+
coder["normalize"] = Internal::KeyNormalizer.dump(@normalizer) if @normalizer
|
|
150
|
+
coder
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
# @api private
|
|
154
|
+
# Called by Psych when parsing YAML.
|
|
155
|
+
# @param coder [Psych::Coder] The YAML representation.
|
|
156
|
+
# @return [self] The restored set.
|
|
157
|
+
def init_with(coder)
|
|
158
|
+
options = { compare_by_identity: coder["compare_by_identity"] }
|
|
159
|
+
options[:mode] = coder["mode"].to_sym if coder["mode"]
|
|
160
|
+
options[:scope] = coder["scope"].to_sym if coder["scope"]
|
|
161
|
+
options[:normalize] = Internal::KeyNormalizer.restore(coder["normalize"]) if coder.map.key?("normalize")
|
|
162
|
+
entries = coder["entries"]
|
|
163
|
+
|
|
164
|
+
if coder["mode"] && value_modes?
|
|
165
|
+
initialize(nil, **options)
|
|
166
|
+
entries.each { restore_yaml_entry(it) }
|
|
167
|
+
else
|
|
168
|
+
values = entries.map { restore_yaml_value(it) }
|
|
169
|
+
initialize(values, **options)
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
freeze if coder["frozen"]
|
|
173
|
+
self
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
private
|
|
177
|
+
|
|
178
|
+
def restore_yaml_entry(entry)
|
|
179
|
+
value = restore_yaml_value(entry)
|
|
180
|
+
key = entry["identity"] ? comparison_key_for_canonical(value) : Ractor.make_shareable(entry["snapshot"])
|
|
181
|
+
mode = entry["mode"]&.to_sym
|
|
182
|
+
payload = @manager.wrap(value, mode:)
|
|
183
|
+
add_stored(key, StoredEntry.new(@manager, payload))
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
def restore_yaml_value(entry)
|
|
187
|
+
value = entry["value"]
|
|
188
|
+
entry["shareable"] ? Ractor.make_shareable(value) : value
|
|
189
|
+
end
|
|
190
|
+
end
|
|
191
|
+
end
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# shareable_constant_value: literal
|
|
3
|
+
# warn_indent: true
|
|
4
|
+
|
|
5
|
+
# @!group ractor-sharing Integration
|
|
6
|
+
require "farce"
|
|
7
|
+
require "ractor/tvar"
|
|
8
|
+
require "ractor/lockvar"
|
|
9
|
+
require "ractor/lockhash"
|
|
10
|
+
require "ractor/keylockhash"
|
|
11
|
+
require "ractor/active_object"
|
|
12
|
+
require "ractor/actor_hash"
|
|
13
|
+
|
|
14
|
+
module Farce
|
|
15
|
+
module Integrations
|
|
16
|
+
# @api private
|
|
17
|
+
module RactorSharing
|
|
18
|
+
# The private marker is published in the same STM commit as all TVars.
|
|
19
|
+
# It distinguishes a failed STM attempt from a post-publication exception.
|
|
20
|
+
class Commit
|
|
21
|
+
def initialize(entries)
|
|
22
|
+
@entries = entries
|
|
23
|
+
@marker = ::Ractor::TVar.new(false)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def committed? = @marker.value
|
|
27
|
+
|
|
28
|
+
def commit
|
|
29
|
+
::Ractor.atomically do
|
|
30
|
+
@entries.each do |entry|
|
|
31
|
+
unless Internal::PortableTransaction.same?(entry.source.value, entry.baseline)
|
|
32
|
+
raise Internal::TransactionConflict, "TVar changed"
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
# Validate the complete read set before staging any writes.
|
|
36
|
+
@entries.each { it.source.value = it.value if it.dirty? } # rubocop:disable Style/CombinableLoops
|
|
37
|
+
@marker.value = true
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
class Entry
|
|
43
|
+
attr_reader :source, :baseline
|
|
44
|
+
attr_accessor :value
|
|
45
|
+
|
|
46
|
+
def initialize(source)
|
|
47
|
+
# atomically is nestable, so it could return before actual publication.
|
|
48
|
+
# Test a private slot before taking a snapshot or any Farce reservations.
|
|
49
|
+
probe = ::Ractor::TVar.new(false)
|
|
50
|
+
begin
|
|
51
|
+
probe.value = false
|
|
52
|
+
rescue ::Ractor::TransactionError
|
|
53
|
+
# Expected outside STM. All publication belongs to our commit step.
|
|
54
|
+
else
|
|
55
|
+
raise ArgumentError, "Farce transactions with TVars cannot run inside Ractor.atomically"
|
|
56
|
+
end
|
|
57
|
+
@source = source
|
|
58
|
+
@value = @baseline = ::Ractor.atomically { source.value }
|
|
59
|
+
@dirty = false
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def working = self
|
|
63
|
+
def write! = @dirty = true
|
|
64
|
+
def dirty? = @dirty
|
|
65
|
+
def commit_group = Commit
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
class Transaction
|
|
71
|
+
# A Ractor::TVar staged alongside ordinary Farce participants.
|
|
72
|
+
#
|
|
73
|
+
# Only the commit step enters Ractor.atomically. Conflicts retry the Farce
|
|
74
|
+
# attempt. Values are made shareable when assigned, matching TVar's setter.
|
|
75
|
+
# Mutating a contained object directly is a side effect of the attempt.
|
|
76
|
+
#
|
|
77
|
+
# Run the Farce transaction outside an enclosing Ractor.atomically block.
|
|
78
|
+
# Writable Concurrent::TVar participants cannot join this commit step.
|
|
79
|
+
#
|
|
80
|
+
# @example Transfer between a TVar and an Atom
|
|
81
|
+
# available = Ractor::TVar.new(10)
|
|
82
|
+
# received = Farce::Strict::Atom.new(0)
|
|
83
|
+
# Farce.transaction do |tx|
|
|
84
|
+
# tx[available].value -= 3
|
|
85
|
+
# tx[received].value += 3
|
|
86
|
+
# end
|
|
87
|
+
class RactorTVar
|
|
88
|
+
include Wrapper
|
|
89
|
+
|
|
90
|
+
# @return [Object] the value staged in this attempt
|
|
91
|
+
def value = access { @working.value }
|
|
92
|
+
|
|
93
|
+
# Stage a shareable replacement.
|
|
94
|
+
# @param value [Object] the replacement value
|
|
95
|
+
# @return [Object] the shareable replacement
|
|
96
|
+
def value=(value)
|
|
97
|
+
# Can't use #write, as a TVar's Ruby facade is always frozen
|
|
98
|
+
access do
|
|
99
|
+
@entry.write!
|
|
100
|
+
@working.value = ::Ractor.make_shareable(value)
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
private
|
|
105
|
+
|
|
106
|
+
def enlist(backend) = @transaction.enlist(backend) { Integrations::RactorSharing::Entry.new(it) }
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
Transaction.define(::Ractor::TVar) do |object, transaction|
|
|
111
|
+
Farce::Transaction::RactorTVar.new(transaction, object, object)
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
[::Ractor::TVar, ::Ractor::LockVar, ::Ractor::LockHash, ::Ractor::KeyLockHash].each do |klass|
|
|
115
|
+
klass.include(Internal::Noncopyable) unless klass < Internal::Noncopyable
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
[::Ractor::LockHash, ::Ractor::KeyLockHash].each do |klass|
|
|
119
|
+
Internal::Converter.define(klass, :Map) do |instance, hash|
|
|
120
|
+
hash.keys.each do |key| # rubocop:disable Style/HashEachMethods
|
|
121
|
+
value = hash.fetch(key, UNDEFINED)
|
|
122
|
+
instance[key] = convert(value) unless UNDEFINED.equal?(value)
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
Walker.define(::Ractor::TVar) do |object, walker|
|
|
128
|
+
walker.update(object, [object.value]) do |target, results|
|
|
129
|
+
::Ractor.atomically { target.value = results.first unless target.value.equal?(results.first) }
|
|
130
|
+
target
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
Walker.define(::Ractor::LockVar) do |object, walker|
|
|
135
|
+
walker.update(object, [object.value]) do |target, results|
|
|
136
|
+
target.update { |value| value.equal?(results.first) ? value : results.first }
|
|
137
|
+
target
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
Walker.define(::Ractor::LockHash) do |object, walker|
|
|
142
|
+
values = object.to_h.flat_map { |key, value| [key, value] }
|
|
143
|
+
walker.update(object, values, hash_keys: true, key_stride: 2) do |target, results|
|
|
144
|
+
results.each_slice(2) { |key, _| walker.check_hash_key(key) }
|
|
145
|
+
target.synchronize do
|
|
146
|
+
values.each_slice(2).with_index do |(key, _), index|
|
|
147
|
+
key_result = results[index * 2]
|
|
148
|
+
target.delete(key) unless key.equal?(key_result)
|
|
149
|
+
end
|
|
150
|
+
results.each_slice(2) { |key, value| target[key] = value }
|
|
151
|
+
end
|
|
152
|
+
target
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
Walker.define(::Ractor::KeyLockHash) do |object, walker|
|
|
157
|
+
values = object.to_h.flat_map { |key, value| [key, value] }
|
|
158
|
+
walker.update(object, values, hash_keys: true, key_stride: 2) do |target, results|
|
|
159
|
+
results.each_slice(2) { |key, _| walker.check_hash_key(key) }
|
|
160
|
+
values.each_slice(2).with_index do |(key, _), index|
|
|
161
|
+
key_result = results[index * 2]
|
|
162
|
+
target.delete(key) unless key.equal?(key_result)
|
|
163
|
+
end
|
|
164
|
+
results.each_slice(2) { |key, value| target[key] = value }
|
|
165
|
+
target
|
|
166
|
+
end
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
Walker.define(::Ractor::ActiveObject::Proxy) do |object, _walker|
|
|
170
|
+
object
|
|
171
|
+
end
|
|
172
|
+
end
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# shareable_constant_value: literal
|
|
3
|
+
# warn_indent: true
|
|
4
|
+
|
|
5
|
+
# @!group ractor-tmvar Integration
|
|
6
|
+
require "farce/integrations/ractor_sharing"
|
|
7
|
+
require "ractor/tmvar"
|
|
8
|
+
|
|
9
|
+
module Farce
|
|
10
|
+
class Transaction
|
|
11
|
+
# A Ractor::TMVar staged alongside TVars and Farce participants.
|
|
12
|
+
#
|
|
13
|
+
# Take, put, and swap operate on the value staged in this attempt.
|
|
14
|
+
# An empty read, take, or swap, or a put into a full TMVar fails the
|
|
15
|
+
# attempt. Farce.transaction retries these conditions within its retry limit.
|
|
16
|
+
# The try_* methods return nil or false without failing the attempt.
|
|
17
|
+
# Assigned values are made shareable, matching Ractor::TVar's setter.
|
|
18
|
+
#
|
|
19
|
+
# Run outside Ractor.atomically, as required by {RactorTVar}.
|
|
20
|
+
# Calls from other Ractors require Ractor-safe TVar methods from ractor-sharing.
|
|
21
|
+
#
|
|
22
|
+
# @example Transfer from a TMVar to an Atom
|
|
23
|
+
# pending = Ractor::TMVar.new(10)
|
|
24
|
+
# received = Farce::Strict::Atom.new(0)
|
|
25
|
+
# Farce.transaction do |tx|
|
|
26
|
+
# tx[received].value += tx[pending].take
|
|
27
|
+
# end
|
|
28
|
+
class RactorTMVar < ::Ractor::TMVar
|
|
29
|
+
include Wrapper
|
|
30
|
+
|
|
31
|
+
# Bind the TMVar's underlying TVar to this attempt's shared snapshot.
|
|
32
|
+
# @param transaction [Transaction] the current attempt
|
|
33
|
+
# @param object [Ractor::TMVar] the participant
|
|
34
|
+
def initialize(transaction, object)
|
|
35
|
+
super
|
|
36
|
+
# TMVar exposes no accessor for its storage. Reuse the TVar wrapper
|
|
37
|
+
# so explicit enrollment of that same TVar shares all staged changes.
|
|
38
|
+
compose(tvar: object.instance_variable_get(:@tvar))
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
Wrapper.inherit(self, :take, :try_take, :read, :try_read, :put, :try_put, :empty?, :swap)
|
|
42
|
+
|
|
43
|
+
private
|
|
44
|
+
|
|
45
|
+
# Translate STM retry conditions before Wrapper marks other exceptions
|
|
46
|
+
# as non-retryable. No upstream STM block runs during staged operations.
|
|
47
|
+
def access
|
|
48
|
+
super do
|
|
49
|
+
yield
|
|
50
|
+
rescue ::Ractor::RetryTransaction
|
|
51
|
+
raise Internal::TransactionConflict, "TMVar is empty or full"
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
Transaction.define(Transaction::RactorTMVar) do |object, transaction|
|
|
58
|
+
object.transaction_wrapper(transaction)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
Transaction.define(::Ractor::TMVar) do |object, transaction|
|
|
62
|
+
Farce::Transaction::RactorTMVar.new(transaction, object)
|
|
63
|
+
end
|
|
64
|
+
end
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# shareable_constant_value: literal
|
|
3
|
+
# warn_indent: true
|
|
4
|
+
|
|
5
|
+
# @!group JSON Integration
|
|
6
|
+
module Farce
|
|
7
|
+
class Abstract::Vector
|
|
8
|
+
# Serialize a logical snapshot as a JSON Array.
|
|
9
|
+
# @note This method is only available if a supported JSON library has been loaded.
|
|
10
|
+
# @overload to_json(*arguments)
|
|
11
|
+
# @param arguments [Array<Object>] Arguments forwarded to Array#to_json.
|
|
12
|
+
# @return [String] The generated JSON.
|
|
13
|
+
def to_json(...) = to_a.to_json(...)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
class Abstract::Map
|
|
17
|
+
# Serialize current entries as a JSON object.
|
|
18
|
+
# @note This method is only available if a supported JSON library has been loaded.
|
|
19
|
+
# @overload to_json(*arguments)
|
|
20
|
+
# @param arguments [Array<Object>] Arguments forwarded to Hash#to_json.
|
|
21
|
+
# @return [String] The generated JSON.
|
|
22
|
+
def to_json(...) = to_h.to_json(...)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
class Abstract::Set
|
|
26
|
+
# Serialize current members as a JSON Array.
|
|
27
|
+
# @note This method is only available if a supported JSON library has been loaded.
|
|
28
|
+
# @overload to_json(*arguments)
|
|
29
|
+
# @param arguments [Array<Object>] Arguments forwarded to Array#to_json.
|
|
30
|
+
# @return [String] The generated JSON.
|
|
31
|
+
def to_json(...) = to_a.to_json(...)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
module Internal::ValueSerialization
|
|
35
|
+
# Serialize the current value as its primitive JSON equivalent.
|
|
36
|
+
# @note This method is only available if a supported JSON library has been loaded.
|
|
37
|
+
# @overload to_json(*arguments)
|
|
38
|
+
# @param arguments [Array<Object>] Arguments forwarded to the current value's to_json.
|
|
39
|
+
# @return [String] The generated JSON.
|
|
40
|
+
def to_json(...) = value.to_json(...)
|
|
41
|
+
end
|
|
42
|
+
end
|