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
data/SECURITY.md
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# Security Policy
|
|
2
|
+
|
|
3
|
+
## Reporting a Vulnerability
|
|
4
|
+
|
|
5
|
+
Please do not open public issues or pull requests about open security vulnerabilities.
|
|
6
|
+
|
|
7
|
+
Report vulnerabilities privately to [security@rkh.im](mailto:security@rkh.im) with the subject line "Farce Security Vulnerability Report".
|
|
8
|
+
Include a description of the vulnerability, affected versions, and steps to reproduce it.
|
|
9
|
+
|
|
10
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md) for general contribution guidelines.
|
data/docs/benchmarks.md
ADDED
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
# @title Benchmarks
|
|
3
|
+
-->
|
|
4
|
+
|
|
5
|
+
# Benchmarks
|
|
6
|
+
|
|
7
|
+
Farce also aims to be fast and efficient, aiming for anywhere between a minimal overhead to outperforming other options.
|
|
8
|
+
|
|
9
|
+
Any numbers quoted here are to be taken with a grain of salt:
|
|
10
|
+
|
|
11
|
+
* They are based on micro-benchmarks, which may not reflect real-world performance.
|
|
12
|
+
* They are momentary snapshots. Gems and Ruby implementations are constantly evolving,
|
|
13
|
+
so these numbers may not be accurate in the future.
|
|
14
|
+
* Concrete numbers are largely measured on a local machine, which may not reflect your deployment environment.
|
|
15
|
+
|
|
16
|
+
You should measure the performance of your own application under realistic conditions.
|
|
17
|
+
|
|
18
|
+
## Map performance
|
|
19
|
+
|
|
20
|
+
A shared map is a very common data structure for tracking state. While the read and write performance is hopefully not the bottleneck for your application.
|
|
21
|
+
|
|
22
|
+
Map implementation | Read | Write | Notes
|
|
23
|
+
-----------------------------|-------------|-------------|-----
|
|
24
|
+
`Hash` | fastest | fastest | Not ractor-shareable (when mutable), not thread-safe
|
|
25
|
+
`Concurrent::Hash` | 1.1x slower | 1.1x slower | Not ractor-shareable
|
|
26
|
+
`Farce::Strict::Map` | 1.2x slower | 1.1x slower |
|
|
27
|
+
`Farce::Unshared::Map` | 1.2x slower | 1.1x slower | Not ractor-shareable
|
|
28
|
+
`Farce::Map` | 1.3x slower | 1.1x slower |
|
|
29
|
+
`Concurrent::Map` | 1.4x slower | 3.2x slower | Not ractor-shareable
|
|
30
|
+
`Hash` + `Mutex` | 2.9x slower | 2.9x slower | Not ractor-shareable
|
|
31
|
+
`Ractor::LockHash` | 2.9x slower | 3.4x slower | Not fiber-friendly
|
|
32
|
+
`Ratomic::Map` | 3.1x slower | 2.7x slower | Breaks isolation, not fiber-friendly
|
|
33
|
+
`Ractor::KeyLockHash` | 3.2x slower | 2.5x slower | Not fiber-friendly
|
|
34
|
+
`Farce::LRUMap` | 3.4x slower | 7.6x slower | Automatic eviction
|
|
35
|
+
`Farce::LFUMap` | 3.5x slower | 7.6x slower | Automatic eviction
|
|
36
|
+
`Farce::Unsafe::TreeMap` | 3.7x slower | 4.1x slower | Ordered entries, not thread-safe, not ractor-shareable
|
|
37
|
+
`HashWithIndifferentAccess` | 4.5x slower | 5.3x slower | Not ractor-shareable
|
|
38
|
+
`RactorSafe::HashMap` | 4.8x slower | 4.8x slower |
|
|
39
|
+
`Farce::TreeMap` | 5.5x slower | 40x slower | Ordered entries
|
|
40
|
+
`Farce::LeaseMap` | 30x slower | 40x slower | Shared ractor for all lease maps
|
|
41
|
+
`Ractor::ActorHash` | 400x slower | 280x slower | Additional ractor per map
|
|
42
|
+
|
|
43
|
+
The map implementations namespaced under `Ractor` are from the [ractor-sharing](https://github.com/ko1/ractor-sharing) gem.
|
|
44
|
+
|
|
45
|
+
Also note that `Ratomic::Map` has a significant performance benefit over all other implementations when repeatedly writing to different keys in very large maps concurrently on a very high number of Ractors due to the underlying [DashMap](https://github.com/xacrimon/dashmap) implementing data sharding. This benefit does not materializes if different Ractors share the keys they use, so its usefulness is slightly hampered by the fact that you cannot iterate over Ratomic's maps at all.
|
|
46
|
+
|
|
47
|
+
## Counter performance
|
|
48
|
+
|
|
49
|
+
> [!CAUTION]
|
|
50
|
+
> If counter performance is your application's bottleneck, **Ruby might not be the right choice for you**.
|
|
51
|
+
|
|
52
|
+
### CRuby (4.0)
|
|
53
|
+
|
|
54
|
+
Implementation | Increment | Read value | Integer size | Note
|
|
55
|
+
---------------------|-------------|----------------|--------------|-------
|
|
56
|
+
farce | fastest | fastest | 64-bit |
|
|
57
|
+
concurrent-ruby-ext | 1.2x slower | same-ish | 32-bit | no ractor support
|
|
58
|
+
ratomic | 1.5x slower | 1.6x slower | 64-bit | no overflow protection
|
|
59
|
+
ractor_safe | 2.1x slower | 2.1x slower | 64-bit |
|
|
60
|
+
concurrent-ruby | 12x slower | 5x slower | 32-bit | no ractor support
|
|
61
|
+
|
|
62
|
+
Performance differences between implementations are consistent between single-threaded and multi-threaded benchmarks.
|
|
63
|
+
|
|
64
|
+
### JRuby
|
|
65
|
+
|
|
66
|
+
Implementation | Increment | Read value | Integer size
|
|
67
|
+
---------------------|-------------|----------------|--------------
|
|
68
|
+
concurrent-ruby-ext | fastest | fastest | 64-bit
|
|
69
|
+
farce | 1.7x slower | 2.2x slower | 64-bit
|
|
70
|
+
concurrent-ruby | 15x slower | 9x slower | 32-bit
|
|
71
|
+
|
|
72
|
+
Farce uses a JVM-specific Ruby implementation, concurrent-ruby uses the same Mutex-based implementation as on other platforms, and concurrent-ruby-ext comes with a Java implementation of an atomic counter (hence also the difference in integer size). The overhead in Farce can largely be attributed to Ruby dispatch overhead.
|
|
73
|
+
|
|
74
|
+
### TruffleRuby
|
|
75
|
+
|
|
76
|
+
Implementation | Increment | Read value | Integer size
|
|
77
|
+
---------------------|-------------|----------------|--------------
|
|
78
|
+
farce | fastest | fastest | 64-bit
|
|
79
|
+
concurrent-ruby | 1.5x slower | 3.5x slower | 32-bit
|
|
80
|
+
|
|
81
|
+
TruffleRuby's performance numbers are not as reliable as other Ruby implementations, and may vary significantly between runs, versions, and whether the GraalVM is in use and has warmed up. Neither farce nor concurrent-ruby use a counter written in C, so they should both be fully optimizable by the GraalVM.
|
|
82
|
+
|
|
83
|
+
## Lock performance
|
|
84
|
+
|
|
85
|
+
> [!CAUTION]
|
|
86
|
+
> If lock performance is your application's bottleneck, you might want to look into **different data structures**.
|
|
87
|
+
> Farce and concurrent-ruby provide plenty of options.
|
|
88
|
+
|
|
89
|
+
### CRuby
|
|
90
|
+
|
|
91
|
+
Farce's locks are **between 5% and 10% slower** than `Mutex` for uncontended locks, and stay below a 50% performance penalty for highly contended locks between threads.
|
|
92
|
+
|
|
93
|
+
### JRuby and TruffleRuby
|
|
94
|
+
|
|
95
|
+
Farce's locks have identical performance to `Mutex` (as they are a subclass of `Mutex`).
|
|
96
|
+
|
|
97
|
+
## Queue performance
|
|
98
|
+
|
|
99
|
+
In the producer/consumer workload in `benchmark/queue.rb`, Ruby's built-in `Thread::Queue` and `Thread::SizedQueue` remain the fastest, but they do not support ractors.
|
|
100
|
+
|
|
101
|
+
Implementation | Performance | Notes
|
|
102
|
+
-------------------------------|-------------|-------------
|
|
103
|
+
`Thread::Queue` | Fastest | no ractor support
|
|
104
|
+
`Thread::SizedQueue` | 1.4x slower | no ractor support
|
|
105
|
+
`Farce::Strict::Queue` | 1.5x slower | only allows sharable objects
|
|
106
|
+
`Farce::Queue` | 2.1x slower |
|
|
107
|
+
`RactorSafe::Queue` | 2.9x slower | only allows sharable objects
|
|
108
|
+
`Ratomic::Queue` | 9.8x slower | breaks ractor isolation
|
|
109
|
+
`RactorQueue` | 30x slower | breaks ractor isolation
|
|
110
|
+
`Ractor::Port` (multiplexing) | 100x slower |
|
|
111
|
+
|
|
112
|
+
## Priority queue performance
|
|
113
|
+
|
|
114
|
+
Many gems implement a priority queue or comparable data structure.
|
|
115
|
+
Farce's implementation is the only one that allows cross-ractor communication.
|
|
116
|
+
The below numbers compare non-blocking APIs, as only Farce implements a blocking API as well.
|
|
117
|
+
|
|
118
|
+
<table>
|
|
119
|
+
<thead>
|
|
120
|
+
<tr>
|
|
121
|
+
<th colspan="2"></th>
|
|
122
|
+
<th colspan="2">CRuby</th>
|
|
123
|
+
<th colspan="2">JRuby</th>
|
|
124
|
+
</tr>
|
|
125
|
+
<tr>
|
|
126
|
+
<th>↓ Gem</th>
|
|
127
|
+
<th>Insertion order →</th>
|
|
128
|
+
<th>Random</th>
|
|
129
|
+
<th>Descending</th>
|
|
130
|
+
<th>Random</th>
|
|
131
|
+
<th>Descending</th>
|
|
132
|
+
</tr>
|
|
133
|
+
</thead>
|
|
134
|
+
<tbody>
|
|
135
|
+
<tr>
|
|
136
|
+
<td colspan="2">farce 0.1.0</td>
|
|
137
|
+
<td>fastest</td>
|
|
138
|
+
<td>fastest</td>
|
|
139
|
+
<td>fastest</td>
|
|
140
|
+
<td>fastest</td>
|
|
141
|
+
</tr>
|
|
142
|
+
<tr>
|
|
143
|
+
<td colspan="2"><a href="https://github.com/mame/rbtree">rbtree</a> 0.4.7</td>
|
|
144
|
+
<td>1.1x slower</td>
|
|
145
|
+
<td>1.2x slower</td>
|
|
146
|
+
<td>–</td>
|
|
147
|
+
<td>–</td>
|
|
148
|
+
</tr>
|
|
149
|
+
<tr>
|
|
150
|
+
<td colspan="2"><a href="https://github.com/boborbt/priority_queue_cxx">priority_queue_cxx</a> 0.3.7</td>
|
|
151
|
+
<td>1.7x slower</td>
|
|
152
|
+
<td>1.7x slower</td>
|
|
153
|
+
<td>–</td>
|
|
154
|
+
<td>–</td>
|
|
155
|
+
</tr>
|
|
156
|
+
<tr>
|
|
157
|
+
<td colspan="2"><a href="https://github.com/socketry/io-event">io-event</a> 1.21.1</td>
|
|
158
|
+
<td>2.6x slower</td>
|
|
159
|
+
<td>3.9x slower</td>
|
|
160
|
+
<td>1.3x slower</td>
|
|
161
|
+
<td>1.7x slower</td>
|
|
162
|
+
</tr>
|
|
163
|
+
<tr>
|
|
164
|
+
<td colspan="2"><a href="https://github.com/rubyworks/pqueue">pqueue</a> 2.2.0</td>
|
|
165
|
+
<td>9.5x slower</td>
|
|
166
|
+
<td>8.0x slower</td>
|
|
167
|
+
<td>2.2x slower</td>
|
|
168
|
+
<td>1.9x slower</td>
|
|
169
|
+
</tr>
|
|
170
|
+
<tr>
|
|
171
|
+
<td colspan="2"><a href="https://github.com/matiasbattocchia/lazy-priority-queue">lazy_priority_queue</a> 0.1.1</td>
|
|
172
|
+
<td>9.9x slower</td>
|
|
173
|
+
<td>7.9x slower</td>
|
|
174
|
+
<td>3.9x slower</td>
|
|
175
|
+
<td>3.2x slower</td>
|
|
176
|
+
</tr>
|
|
177
|
+
<tr>
|
|
178
|
+
<td colspan="2"><a href="https://github.com/philiprehberger/rb-priority-queue">philiprehberger-priority_queue</a> 0.5.0</td>
|
|
179
|
+
<td>13x slower</td>
|
|
180
|
+
<td>19x slower</td>
|
|
181
|
+
<td>7.6x slower</td>
|
|
182
|
+
<td>11x slower</td>
|
|
183
|
+
</tr>
|
|
184
|
+
</tbody>
|
|
185
|
+
</table>
|
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
# @title Gem: dry-types
|
|
3
|
+
-->
|
|
4
|
+
|
|
5
|
+
# Farce / [dry-types](https://dry-rb.org/gems/dry-types/)
|
|
6
|
+
|
|
7
|
+
The opt-in dry-types integration validates input and constructs Farce values
|
|
8
|
+
from the result. It is useful when parsed input is headed to concurrent workers
|
|
9
|
+
or Farce-backed application state.
|
|
10
|
+
|
|
11
|
+
Add `dry-types` to your application and include both type imports:
|
|
12
|
+
|
|
13
|
+
```ruby
|
|
14
|
+
require "dry-types"
|
|
15
|
+
require "farce"
|
|
16
|
+
|
|
17
|
+
module Types
|
|
18
|
+
include Dry.Types()
|
|
19
|
+
include Farce.DryTypes()
|
|
20
|
+
end
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
`Farce.DryTypes()` follows the preceding `Dry.Types()` import. Root Farce types
|
|
24
|
+
use the corresponding dry default types. Namespaces and aliases gain matching
|
|
25
|
+
Farce constants without replacing existing dry types. Loading Farce by itself
|
|
26
|
+
does not load dry-types.
|
|
27
|
+
|
|
28
|
+
The imported constants construct new Farce objects. Use an instance type to
|
|
29
|
+
check an existing Farce object without conversion:
|
|
30
|
+
|
|
31
|
+
```ruby
|
|
32
|
+
module Types
|
|
33
|
+
VectorInstance = Instance(Farce::Vector)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
Types::VectorInstance.try(Farce::Vector.new).success? # => true
|
|
37
|
+
Types::VectorInstance.try([]).failure? # => true
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Vectors
|
|
41
|
+
|
|
42
|
+
Use `Vector.of` to apply a member type before constructing the vector:
|
|
43
|
+
|
|
44
|
+
```ruby
|
|
45
|
+
module Types
|
|
46
|
+
IntegerVector = Vector.of(Coercible::Integer)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
numbers = Types::IntegerVector[["1", 2]]
|
|
50
|
+
numbers.class # => Farce::Vector
|
|
51
|
+
numbers.to_a # => [1, 2]
|
|
52
|
+
numbers.mode # => :copy
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
The bare `Vector` accepts any Array members. Imported dry namespaces control
|
|
56
|
+
the outer native input in the same way as their Array type:
|
|
57
|
+
|
|
58
|
+
```ruby
|
|
59
|
+
Types::Vector.try("one").failure? # => true
|
|
60
|
+
Types::Coercible::Vector["one"].to_a # => ["one"]
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
The resulting values compose with optional types, constraints, `try`, and
|
|
64
|
+
failure blocks:
|
|
65
|
+
|
|
66
|
+
```ruby
|
|
67
|
+
Types::IntegerVector.optional[nil] # => nil
|
|
68
|
+
|
|
69
|
+
result = Types::IntegerVector.try(["invalid"])
|
|
70
|
+
result.failure? # => true
|
|
71
|
+
|
|
72
|
+
Types::IntegerVector.(["invalid"]) { |partial| [:invalid, partial] }
|
|
73
|
+
# => [:invalid, ["invalid"]]
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Member and source coercion complete before Farce construction. Their failure
|
|
77
|
+
blocks receive dry-types' partial native value, never a partially initialized
|
|
78
|
+
Farce collection. Constraints added to a collection type run on its constructed
|
|
79
|
+
Farce result. A size constraint on a Set therefore observes deduplication.
|
|
80
|
+
|
|
81
|
+
dry-types callable defaults return the block result directly. Construct the
|
|
82
|
+
typed value inside the block when each use needs a fresh Farce collection:
|
|
83
|
+
|
|
84
|
+
```ruby
|
|
85
|
+
module Types
|
|
86
|
+
EmptyIntegerVector = IntegerVector.default { IntegerVector[[]] }
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
Types::EmptyIntegerVector[].class # => Farce::Vector
|
|
90
|
+
Types::EmptyIntegerVector[].empty? # => true
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## Maps and schemas
|
|
94
|
+
|
|
95
|
+
Use `Map.map` for homogeneous key and value types:
|
|
96
|
+
|
|
97
|
+
```ruby
|
|
98
|
+
module Types
|
|
99
|
+
ScoreMap = Map.map(String, Coercible::Integer)
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
scores = Types::ScoreMap["Ada" => "10", "Grace" => 12]
|
|
103
|
+
scores.class # => Farce::Map
|
|
104
|
+
scores.to_h # => {"Ada" => 10, "Grace" => 12}
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
dry-types rejects keys that collide after coercion. The integration also
|
|
108
|
+
rejects identity Hash input with structurally equal keys because a structural
|
|
109
|
+
Farce Map could otherwise drop an entry.
|
|
110
|
+
|
|
111
|
+
Use `Map.schema` for fixed keys, defaults, key transforms, and nested types:
|
|
112
|
+
|
|
113
|
+
```ruby
|
|
114
|
+
module Types
|
|
115
|
+
Batch = Map.schema(
|
|
116
|
+
name: String,
|
|
117
|
+
ids: IntegerVector,
|
|
118
|
+
).strict
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
batch = Types::Batch[name: "nightly", ids: ["10", 20]]
|
|
122
|
+
batch[:name] # => "nightly"
|
|
123
|
+
batch[:ids].class # => Farce::Vector
|
|
124
|
+
batch[:ids].to_a # => [10, 20]
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
Schema chaining and `with_key_transform` or `with_type_transform` retain Farce
|
|
128
|
+
construction. A non-strict schema omits unknown keys. Call `.strict` when
|
|
129
|
+
unknown keys should fail.
|
|
130
|
+
|
|
131
|
+
## Sets
|
|
132
|
+
|
|
133
|
+
`Set.of` applies its member type before membership removes duplicates:
|
|
134
|
+
|
|
135
|
+
```ruby
|
|
136
|
+
module Types
|
|
137
|
+
IntegerSet = Set.of(Coercible::Integer)
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
values = Types::IntegerSet[["1", 1, "2"]]
|
|
141
|
+
values.class # => Farce::Set
|
|
142
|
+
values.to_a.sort # => [1, 2]
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
It accepts Arrays and Ruby Sets. The bare `Set` accepts any Array members.
|
|
146
|
+
|
|
147
|
+
## Counters and flags
|
|
148
|
+
|
|
149
|
+
`Counter` uses the imported dry `Integer` type. `Flag` uses the imported dry
|
|
150
|
+
`Bool` type:
|
|
151
|
+
|
|
152
|
+
```ruby
|
|
153
|
+
counter = Types::Coercible::Counter["3"]
|
|
154
|
+
counter.class # => Farce::Counter
|
|
155
|
+
counter.value # => 3
|
|
156
|
+
|
|
157
|
+
flag = Types::Params::Flag["yes"]
|
|
158
|
+
flag.class # => Farce::Flag
|
|
159
|
+
flag.value # => true
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
A namespace only gains a Farce type when it has the corresponding dry type.
|
|
163
|
+
For example, dry-types defines `Coercible::Integer` but no `Coercible::Bool`,
|
|
164
|
+
so `Types::Coercible::Counter` exists while `Types::Coercible::Flag` does not.
|
|
165
|
+
|
|
166
|
+
The dry type validates the initial scalar. Counter and Flag retain their normal
|
|
167
|
+
Farce APIs after construction.
|
|
168
|
+
|
|
169
|
+
## Atoms
|
|
170
|
+
|
|
171
|
+
The bare `Atom` accepts any initial value. Use `Atom.of` to validate or coerce
|
|
172
|
+
the initial contents:
|
|
173
|
+
|
|
174
|
+
```ruby
|
|
175
|
+
module Types
|
|
176
|
+
IntegerAtom = Atom.of(Coercible::Integer)
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
atom = Types::IntegerAtom["4"]
|
|
180
|
+
atom.class # => Farce::Atom
|
|
181
|
+
atom.value # => 4
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
The type applies only at construction. Later writes use the normal Atom API and
|
|
185
|
+
are not revalidated:
|
|
186
|
+
|
|
187
|
+
```ruby
|
|
188
|
+
atom.value = "later"
|
|
189
|
+
atom.value # => "later"
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
Nil as Atom contents differs from an optional Atom constructor:
|
|
193
|
+
|
|
194
|
+
```ruby
|
|
195
|
+
Types::Atom[nil].value # => nil
|
|
196
|
+
Types::Atom.of(Types::Integer.optional)[nil].value # => nil
|
|
197
|
+
Types::Atom.of(Types::Integer).optional[nil] # => nil
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
The first two expressions construct an Atom containing nil. The last expression
|
|
201
|
+
returns nil without constructing an Atom.
|
|
202
|
+
|
|
203
|
+
## Dry imports and Farce variants
|
|
204
|
+
|
|
205
|
+
With no dry namespace arguments, `Farce.DryTypes()` inherits the closest
|
|
206
|
+
`Dry.Types()` import. With no preceding import, it uses the same strict defaults
|
|
207
|
+
as `Dry.Types()`. Pass dry namespace arguments, `default:`, and aliases to
|
|
208
|
+
select an independent source using the normal `Dry.Types()` rules:
|
|
209
|
+
|
|
210
|
+
```ruby
|
|
211
|
+
module CoercingTypes
|
|
212
|
+
include Dry.Types()
|
|
213
|
+
include Farce.DryTypes(:strict, :coercible, default: :coercible)
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
CoercingTypes::Vector["1"].to_a # => ["1"]
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
The dry namespace controls validation and coercion of native input. `variant:`
|
|
220
|
+
selects the Farce class produced after that succeeds:
|
|
221
|
+
|
|
222
|
+
```ruby
|
|
223
|
+
module LocalTypes
|
|
224
|
+
include Dry.Types(default: :coercible)
|
|
225
|
+
include Farce.DryTypes(variant: :local, scope: :fiber)
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
vector = LocalTypes::Vector["job"]
|
|
229
|
+
vector.class # => Farce::Local::Vector
|
|
230
|
+
vector.scope # => :fiber
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
Supported variants are `:shared`, `:strict`, `:unshared`, and `:local`.
|
|
234
|
+
`:shared` is the default and accepts `mode:`. `:local` accepts `scope:`.
|
|
235
|
+
Strict and Unshared variants accept neither option. Farce's `:strict` variant
|
|
236
|
+
and dry-types' `Strict` namespace configure separate parts of the conversion.
|
|
237
|
+
|
|
238
|
+
| `variant:` | Constructed classes |
|
|
239
|
+
| --- | --- |
|
|
240
|
+
| `:shared` | `Farce::Vector`, `Farce::Map`, `Farce::Set`, `Farce::Counter`, `Farce::Flag`, `Farce::Atom` |
|
|
241
|
+
| `:strict` | `Farce::Strict::Vector`, `Farce::Strict::Map`, `Farce::Strict::Set`, `Farce::Strict::Atom` |
|
|
242
|
+
| `:unshared` | `Farce::Unshared::Vector`, `Farce::Unshared::Map`, `Farce::Unshared::Set` |
|
|
243
|
+
| `:local` | `Farce::Local::Vector`, `Farce::Local::Map`, `Farce::Local::Set`, `Farce::Local::Counter`, `Farce::Local::Flag`, `Farce::Local::Atom` |
|
|
244
|
+
|
|
245
|
+
The integration imports only classes that Farce provides for the selected
|
|
246
|
+
variant. Strict has an Atom but no Counter or Flag. Unshared has none of these
|
|
247
|
+
three scalar-backed types.
|
|
248
|
+
|
|
249
|
+
For a single shared collection type, use `.with(mode: ...)`:
|
|
250
|
+
|
|
251
|
+
```ruby
|
|
252
|
+
module Types
|
|
253
|
+
LocalValueVector = Vector.with(mode: :local)
|
|
254
|
+
end
|
|
255
|
+
|
|
256
|
+
payload = []
|
|
257
|
+
vector = Types::LocalValueVector[[payload]]
|
|
258
|
+
vector[0].equal?(payload) # => true
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
The supported shared modes are `:copy`, `:local`, `:make_shareable`,
|
|
262
|
+
`:shareable_copy`, and `:raise`. They apply to collections and Atom. Counter and
|
|
263
|
+
Flag have no transfer mode. `:move` is rejected because dry-types creates and
|
|
264
|
+
examines intermediate values during coercion.
|
|
265
|
+
|
|
266
|
+
## Farce input and ownership
|
|
267
|
+
|
|
268
|
+
Mode-free Strict, Unshared, and Local Farce collections can be converted
|
|
269
|
+
directly. Construction always returns a fresh configured variant.
|
|
270
|
+
|
|
271
|
+
Mode-backed `Farce::Vector`, `Farce::Map`, and `Farce::Set` input is rejected
|
|
272
|
+
before traversal. Materialize one explicitly when reading it is intended:
|
|
273
|
+
|
|
274
|
+
```ruby
|
|
275
|
+
source = Farce::Vector.new(["1"])
|
|
276
|
+
converted = Types::IntegerVector[source.to_a]
|
|
277
|
+
converted.to_a # => [1]
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
The explicit read establishes where transfer and ownership happen. This rule
|
|
281
|
+
also applies when the container's default mode is `:copy` because an individual
|
|
282
|
+
entry might have been inserted with `mode: :move`.
|
|
283
|
+
|
|
284
|
+
Vector materialization uses its normal snapshot. Map and set materialization
|
|
285
|
+
uses normal iteration and is not a globally atomic snapshot during concurrent
|
|
286
|
+
mutation. Validation describes the values processed by that call. Later writes
|
|
287
|
+
through the Farce API are not revalidated. Atom input is treated as its payload
|
|
288
|
+
and is never implicitly read from an existing Atom. Mutable values keep the
|
|
289
|
+
guarantees of the selected Farce mode. The dry type descriptor is application
|
|
290
|
+
configuration and is not promised to be Ractor-shareable.
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
# @title Gem: MessagePack
|
|
3
|
+
-->
|
|
4
|
+
|
|
5
|
+
# Farce / [MessagePack](https://github.com/msgpack/msgpack-ruby)
|
|
6
|
+
|
|
7
|
+
Use MessagePack to store or send Farce values. Add `msgpack` to your Gemfile
|
|
8
|
+
and load it:
|
|
9
|
+
|
|
10
|
+
```ruby
|
|
11
|
+
require "msgpack"
|
|
12
|
+
require "farce"
|
|
13
|
+
|
|
14
|
+
jobs = Farce::Vector.new(["build", "test"])
|
|
15
|
+
bytes = jobs.to_msgpack # or: MessagePack.pack(jobs)
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Vectors, maps, sets, atoms, counters, and flags support `to_msgpack`, including
|
|
19
|
+
nested values and their Farce variants.
|
|
20
|
+
|
|
21
|
+
## Restoring Farce objects
|
|
22
|
+
|
|
23
|
+
By default, unpacking returns ordinary Ruby values: arrays for vectors and sets,
|
|
24
|
+
hashes for maps, integers for counters, booleans for flags, and the stored values
|
|
25
|
+
for atoms. This makes the data easy to use outside Farce:
|
|
26
|
+
|
|
27
|
+
```ruby
|
|
28
|
+
MessagePack.unpack(bytes) # => ["build", "test"]
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
To restore Farce objects instead, use a factory for both packing and unpacking:
|
|
32
|
+
|
|
33
|
+
```ruby
|
|
34
|
+
factory = Farce::MessagePack.factory
|
|
35
|
+
copy = factory.load(factory.dump(jobs))
|
|
36
|
+
copy.class # => Farce::Vector
|
|
37
|
+
copy.to_a # => ["build", "test"]
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
The factory also restores nested Farce objects. It does not change
|
|
41
|
+
`MessagePack.pack` or `to_msgpack`. Restored objects contain the current values,
|
|
42
|
+
and counters retain their initial value for `reset`. Other settings, such as
|
|
43
|
+
transfer modes and local scopes, use constructor defaults unless configured below.
|
|
44
|
+
|
|
45
|
+
## Custom factories
|
|
46
|
+
|
|
47
|
+
Choose extension IDs to fit your application's protocol:
|
|
48
|
+
|
|
49
|
+
```ruby
|
|
50
|
+
factory = Farce::MessagePack.factory(types: {
|
|
51
|
+
Farce::Vector => 40,
|
|
52
|
+
Farce::Counter => 41
|
|
53
|
+
})
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
`types:` replaces the default registrations, which use IDs 0 through 5 for
|
|
57
|
+
Vector, Map, Counter, Flag, Atom, and Set, respectively. Both ends must use the
|
|
58
|
+
same registrations. Register variants explicitly to restore their specific classes.
|
|
59
|
+
|
|
60
|
+
You can also add Farce types to an existing MessagePack factory and supply
|
|
61
|
+
constructor options for restored objects:
|
|
62
|
+
|
|
63
|
+
```ruby
|
|
64
|
+
factory = MessagePack::Factory.new
|
|
65
|
+
Farce::MessagePack.register_type(factory, 60, Farce::Vector, mode: :make_shareable)
|
|
66
|
+
Farce::MessagePack.register_type(factory, 61, Farce::Local::Counter, scope: :fiber)
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Use unused IDs from 0 through 127. Nested values use the same factory, including
|
|
70
|
+
your application's other registered types.
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
# @title Gem: ractor-shim
|
|
3
|
+
-->
|
|
4
|
+
|
|
5
|
+
# Farce / [ractor-shim](https://github.com/eregon/ractor-shim)
|
|
6
|
+
|
|
7
|
+
> [!NOTE]
|
|
8
|
+
> This documentation is up to date for **Farce 0.1.0** and **ractor-shim 0.1.1**.
|
|
9
|
+
|
|
10
|
+
Both Farce and [ractor-shim](https://github.com/eregon/ractor-shim) provide shims for `Ractor` and `Ractor::Port` on platforms that don't fully support them. Farce namespaces these inside the `Farce` module, while ractor-shim installs these classes at top-level (where any shim-unaware code would automatically pick them up).
|
|
11
|
+
|
|
12
|
+
Farce ignores the shims provided by ractor-shim. Both gems can safely coexist in the same application.
|
|
13
|
+
|
|
14
|
+
## Similarities
|
|
15
|
+
|
|
16
|
+
* Both of them will use `Thread` and `Queue` under the hood if Ractors are not available.
|
|
17
|
+
* Both implement most of the public Ractor API.
|
|
18
|
+
* On Ruby implementations not supporting Ractors they both treat all objects as shareable. `Ractor.make_shareable` will not deep-freeze objects, and `Ractor.shareable?` will always return true.
|
|
19
|
+
|
|
20
|
+
## Implementation differences
|
|
21
|
+
|
|
22
|
+
The Ruby requirements for ractor-shim are much broader than for Farce, supporting Ruby 2.7 and later, while Farce requires Ruby 3.4 or newer. Farce is also significantly larger and more complex than ractor-shim. Its primary purpose isn't to provide a shim, but to provide tooling around Ractors and Fiber schedulers. So if all you need is a simple shim, ractor-shim is likely a better choice.
|
|
23
|
+
|
|
24
|
+
However, having these tools at its disposal allows Farce to provide a more complete and in some cases more performant solution.
|
|
25
|
+
|
|
26
|
+
### Things Farce implements that ractor-shim does not
|
|
27
|
+
|
|
28
|
+
Farce supports ractors having multiple threads. The following snippet will work fine with Farce (after `include Farce`), but will raise an exception with ractor-shim on Ruby 2.x, TruffleRuby, and JRuby:
|
|
29
|
+
|
|
30
|
+
```ruby
|
|
31
|
+
Ractor.new do
|
|
32
|
+
Thread.new { p Ractor.current }.join
|
|
33
|
+
end.join
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
The `Ractor.store_if_absent` method provided by ractor-shim may not be thread-safe.
|
|
37
|
+
|
|
38
|
+
`Ractor.shareable_proc` and `Ractor.shareable_lambda` are not properly implemented in ractor-shim. They do not accept a `self` option and will not rebind the passed block. In addition, `shareable_lambda` does not actually produce lambdas on Ruby 3.
|
|
39
|
+
|
|
40
|
+
### Things Farce implements more efficiently
|
|
41
|
+
|
|
42
|
+
* `Ractor.select` on ractor-shim uses busy waiting if native Ractors are not available. Farce uses better synchronization mechanisms to properly avoid busy waiting. Moreover, ractor-shim wraps the select logic in a global mutex, meaning only one `Ractor.select` can be active at any time. Farce does not have this limitation.
|
|
43
|
+
* The `Ractor::Port` shim provided by ractor-shim always creates a new Ractor for each port. Farce only does so if absolutely necessary (on Ruby 3.4, if sending unshareable objects over a non-default port).
|
|
44
|
+
|
|
45
|
+
On Ruby 3.4, the following code will create one Ractor if using Farce, but 6 Ractors if using ractor-shim:
|
|
46
|
+
|
|
47
|
+
```ruby
|
|
48
|
+
ractor = Ractor.new do
|
|
49
|
+
counter = 0
|
|
50
|
+
while port = receive
|
|
51
|
+
counter += 1
|
|
52
|
+
port.send(counter)
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
5.times do
|
|
57
|
+
port = Ractor::Port.new
|
|
58
|
+
ractor.send(port)
|
|
59
|
+
puts "Count is: #{port.receive}"
|
|
60
|
+
ensure
|
|
61
|
+
port.close
|
|
62
|
+
end
|
|
63
|
+
```
|