farce 0.0.1.alpha2-x86_64-linux-gnu
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/CODE_OF_CONDUCT.md +26 -0
- data/CONTRIBUTING.md +71 -0
- data/MIT-LICENSE +20 -0
- data/README.md +1524 -0
- data/SECURITY.md +10 -0
- data/docs/benchmarks.md +185 -0
- data/docs/gems/dry-types.md +290 -0
- data/docs/gems/msgpack.md +70 -0
- data/docs/gems/ractor-shim.md +63 -0
- data/docs/modes.md +628 -0
- data/docs/scopes.md +649 -0
- data/docs/variants.md +346 -0
- data/ext/ext_helper.rb +20 -0
- data/ext/farce/README.md +21 -0
- data/ext/farce/atom.c +1023 -0
- data/ext/farce/bounded_map.c +1683 -0
- data/ext/farce/containers.h +77 -0
- data/ext/farce/counter.c +399 -0
- data/ext/farce/darwin.c +100 -0
- data/ext/farce/depend +12 -0
- data/ext/farce/dict.c +1523 -0
- data/ext/farce/dict.h +152 -0
- data/ext/farce/drivers.c +237 -0
- data/ext/farce/exchanger.c +299 -0
- data/ext/farce/extconf.rb +99 -0
- data/ext/farce/farce.c +359 -0
- data/ext/farce/flag.c +288 -0
- data/ext/farce/io.c +338 -0
- data/ext/farce/lock.c +510 -0
- data/ext/farce/map.c +2240 -0
- data/ext/farce/priority_queue.c +2056 -0
- data/ext/farce/queue.c +1059 -0
- data/ext/farce/reactor.c +820 -0
- data/ext/farce/reactor.h +103 -0
- data/ext/farce/shareable.h +31 -0
- data/ext/farce/signal.c +350 -0
- data/ext/farce/transaction.c +354 -0
- data/ext/farce/transaction.h +40 -0
- data/ext/farce/tree_map.c +1953 -0
- data/ext/farce/trie.c +2020 -0
- data/ext/farce/unshareable.c +155 -0
- data/ext/farce/unshared_io_pool.h +234 -0
- data/ext/farce/unshared_signal.c +263 -0
- data/ext/farce/unshared_wait.h +193 -0
- data/ext/farce/unsupported.c +6 -0
- data/ext/farce/vector.c +1195 -0
- data/ext/farce/weak_map.c +1714 -0
- data/ext/java/org/farce/BoundedMap.java +394 -0
- data/ext/java/org/farce/FiberScheduler.java +141 -0
- data/ext/java/org/farce/PriorityKey.java +45 -0
- data/ext/java/org/farce/PriorityQueue.java +373 -0
- data/ext/java/org/farce/QueueSignal.java +28 -0
- data/ext/rebind/README.md +14 -0
- data/ext/rebind/extconf.rb +10 -0
- data/ext/rebind/rebind.c +186 -0
- data/lib/farce/_yard/internal.rb +13 -0
- data/lib/farce/_yard/macros.rb +61 -0
- data/lib/farce/_yard/ractor.rb +46 -0
- data/lib/farce/abstract/atom.rb +186 -0
- data/lib/farce/abstract/bounded_map.rb +232 -0
- data/lib/farce/abstract/collection.rb +151 -0
- data/lib/farce/abstract/concurrent_map.rb +381 -0
- data/lib/farce/abstract/counter.rb +193 -0
- data/lib/farce/abstract/duplicable_map.rb +229 -0
- data/lib/farce/abstract/exchanger.rb +26 -0
- data/lib/farce/abstract/flag.rb +71 -0
- data/lib/farce/abstract/lazy.rb +115 -0
- data/lib/farce/abstract/lease.rb +104 -0
- data/lib/farce/abstract/lease_map.rb +261 -0
- data/lib/farce/abstract/lease_pool.rb +91 -0
- data/lib/farce/abstract/lfu_map.rb +20 -0
- data/lib/farce/abstract/lru_map.rb +25 -0
- data/lib/farce/abstract/map.rb +345 -0
- data/lib/farce/abstract/molecule.rb +245 -0
- data/lib/farce/abstract/port.rb +74 -0
- data/lib/farce/abstract/priority_queue.rb +117 -0
- data/lib/farce/abstract/queue.rb +269 -0
- data/lib/farce/abstract/scheduler.rb +111 -0
- data/lib/farce/abstract/set.rb +910 -0
- data/lib/farce/abstract/sorted_set.rb +172 -0
- data/lib/farce/abstract/timer_queue.rb +136 -0
- data/lib/farce/abstract/tree_map.rb +269 -0
- data/lib/farce/abstract/value.rb +68 -0
- data/lib/farce/abstract/vector.rb +1126 -0
- data/lib/farce/abstract/weak_atom.rb +32 -0
- data/lib/farce/abstract/weak_key_map.rb +12 -0
- data/lib/farce/abstract/weak_map.rb +13 -0
- data/lib/farce/abstract/weak_set.rb +12 -0
- data/lib/farce/abstract/weak_value_map.rb +12 -0
- data/lib/farce/abstract.rb +17 -0
- data/lib/farce/atom.rb +293 -0
- data/lib/farce/class_mirror.rb +87 -0
- data/lib/farce/clock.rb +121 -0
- data/lib/farce/config.rb +229 -0
- data/lib/farce/counter.rb +71 -0
- data/lib/farce/deduper.rb +122 -0
- data/lib/farce/engine/jruby/bounded_map.rb +314 -0
- data/lib/farce/engine/jruby/fiber_scheduler.jar +0 -0
- data/lib/farce/engine/jruby/fiber_scheduler.rb +119 -0
- data/lib/farce/engine/jruby/lease_waiting.rb +19 -0
- data/lib/farce/engine/jruby/map.rb +505 -0
- data/lib/farce/engine/jruby/mutable_numeric_copy.rb +42 -0
- data/lib/farce/engine/jruby/signal.rb +147 -0
- data/lib/farce/engine/jruby.rb +63 -0
- data/lib/farce/engine/jvm/concurrent_weak_registry.rb +55 -0
- data/lib/farce/engine/jvm/counter.rb +102 -0
- data/lib/farce/engine/jvm/extension.rb +32 -0
- data/lib/farce/engine/jvm/farce.jar +0 -0
- data/lib/farce/engine/jvm/flag.rb +79 -0
- data/lib/farce/engine/jvm/priority_queue.rb +217 -0
- data/lib/farce/engine/jvm/tree_map.rb +350 -0
- data/lib/farce/engine/jvm/types.rb +180 -0
- data/lib/farce/engine/jvm.rb +19 -0
- data/lib/farce/engine/ruby/3.4/farce.so +0 -0
- data/lib/farce/engine/ruby/3.4/fiber_scheduler.rb +20 -0
- data/lib/farce/engine/ruby/3.4/port.rb +186 -0
- data/lib/farce/engine/ruby/3.4/ractor_methods.rb +26 -0
- data/lib/farce/engine/ruby/3.4/ractor_selector.rb +92 -0
- data/lib/farce/engine/ruby/3.4/rebind.so +0 -0
- data/lib/farce/engine/ruby/3.4/vault.rb +56 -0
- data/lib/farce/engine/ruby/4.0/farce.so +0 -0
- data/lib/farce/engine/ruby/4.0/port.rb +19 -0
- data/lib/farce/engine/ruby/4.0/ractor_methods.rb +20 -0
- data/lib/farce/engine/ruby/4.0/ractor_selector.rb +108 -0
- data/lib/farce/engine/ruby/4.0/rebind.so +0 -0
- data/lib/farce/engine/ruby/4.0/vault.rb +74 -0
- data/lib/farce/engine/ruby/4.1/port.rb +21 -0
- data/lib/farce/engine/ruby/4.1/ractor_methods.rb +22 -0
- data/lib/farce/engine/ruby/4.1/ractor_selector.rb +25 -0
- data/lib/farce/engine/ruby/4.1/vault.rb +5 -0
- data/lib/farce/engine/ruby/fiber_scheduler.rb +32 -0
- data/lib/farce/engine/ruby/key_lock_map.rb +28 -0
- data/lib/farce/engine/ruby/shared/lease.rb +26 -0
- data/lib/farce/engine/ruby/shared/lease_pool.rb +24 -0
- data/lib/farce/engine/ruby/shared/main_scheduler.rb +9 -0
- data/lib/farce/engine/ruby/shared/parallel_scheduler.rb +9 -0
- data/lib/farce/engine/ruby/shared/proxy_owner.rb +31 -0
- data/lib/farce/engine/ruby/shared/ractor_methods.rb +34 -0
- data/lib/farce/engine/ruby/shared/ractor_selector.rb +328 -0
- data/lib/farce/engine/ruby/shared/strict_map.rb +13 -0
- data/lib/farce/engine/ruby/shared/unshared_vector.rb +14 -0
- data/lib/farce/engine/ruby/shared/vault.rb +144 -0
- data/lib/farce/engine/ruby/shared/vault_weak_map.rb +336 -0
- data/lib/farce/engine/ruby/shared/weak_atom.rb +54 -0
- data/lib/farce/engine/ruby/shared/weak_map.rb +413 -0
- data/lib/farce/engine/ruby.rb +130 -0
- data/lib/farce/engine/shared/atom.rb +10 -0
- data/lib/farce/engine/shared/exchanger.rb +130 -0
- data/lib/farce/engine/shared/identity_key.rb +22 -0
- data/lib/farce/engine/shared/lease.rb +10 -0
- data/lib/farce/engine/shared/lease_pool.rb +10 -0
- data/lib/farce/engine/shared/main_scheduler.rb +20 -0
- data/lib/farce/engine/shared/map_key_coordination.rb +226 -0
- data/lib/farce/engine/shared/parallel_scheduler.rb +9 -0
- data/lib/farce/engine/shared/port.rb +44 -0
- data/lib/farce/engine/shared/portable_bounded_map.rb +619 -0
- data/lib/farce/engine/shared/proxy_owner.rb +43 -0
- data/lib/farce/engine/shared/queue.rb +233 -0
- data/lib/farce/engine/shared/ractor_methods.rb +73 -0
- data/lib/farce/engine/shared/rebindable.rb +15 -0
- data/lib/farce/engine/shared/strict_atom.rb +73 -0
- data/lib/farce/engine/shared/strict_map.rb +117 -0
- data/lib/farce/engine/shared/strict_queue_values.rb +27 -0
- data/lib/farce/engine/shared/strict_tree_map.rb +56 -0
- data/lib/farce/engine/shared/transaction_map_backend.rb +20 -0
- data/lib/farce/engine/shared/trie.rb +317 -0
- data/lib/farce/engine/shared/trie_builder.rb +199 -0
- data/lib/farce/engine/shared/unshareable.rb +14 -0
- data/lib/farce/engine/shared/unshared_atom.rb +256 -0
- data/lib/farce/engine/shared/unshared_priority_queue.rb +9 -0
- data/lib/farce/engine/shared/unshared_queue.rb +18 -0
- data/lib/farce/engine/shared/unshared_signal.rb +21 -0
- data/lib/farce/engine/shared/unshared_vector.rb +377 -0
- data/lib/farce/engine/shared/unshared_weak_atom.rb +30 -0
- data/lib/farce/engine/shared/unshared_weak_map.rb +445 -0
- data/lib/farce/engine/shared/vault.rb +31 -0
- data/lib/farce/engine/shared/vector.rb +83 -0
- data/lib/farce/engine/shared/weak_atom/base.rb +189 -0
- data/lib/farce/engine/shared/weak_atom.rb +20 -0
- data/lib/farce/engine/shared/weak_map/cell.rb +99 -0
- data/lib/farce/engine/shared/weak_map/index.rb +249 -0
- data/lib/farce/engine/shared/weak_map/lock.rb +80 -0
- data/lib/farce/engine/shared/weak_map/reference.rb +33 -0
- data/lib/farce/engine/shared.rb +63 -0
- data/lib/farce/engine/truffleruby/fiber_scheduler.rb +13 -0
- data/lib/farce/engine/truffleruby/lock.rb +121 -0
- data/lib/farce/engine/truffleruby/map.rb +656 -0
- data/lib/farce/engine/truffleruby/native/counter.rb +105 -0
- data/lib/farce/engine/truffleruby/native/flag.rb +77 -0
- data/lib/farce/engine/truffleruby/native/ordered_array_support.rb +67 -0
- data/lib/farce/engine/truffleruby/native/priority_queue.rb +388 -0
- data/lib/farce/engine/truffleruby/native/tree_map.rb +51 -0
- data/lib/farce/engine/truffleruby/native/unsafe_tree_map.rb +350 -0
- data/lib/farce/engine/truffleruby/signal.rb +77 -0
- data/lib/farce/engine/truffleruby.rb +148 -0
- data/lib/farce/envelope.rb +335 -0
- data/lib/farce/error.rb +35 -0
- data/lib/farce/exchanger.rb +41 -0
- data/lib/farce/flag.rb +27 -0
- data/lib/farce/integrations/active_support/blank.rb +68 -0
- data/lib/farce/integrations/active_support/clock.rb +17 -0
- data/lib/farce/integrations/active_support/duplicable.rb +78 -0
- data/lib/farce/integrations/active_support/map.rb +175 -0
- data/lib/farce/integrations/active_support/set.rb +60 -0
- data/lib/farce/integrations/active_support/value_serialization.rb +17 -0
- data/lib/farce/integrations/active_support/vector.rb +231 -0
- data/lib/farce/integrations/active_support.rb +9 -0
- data/lib/farce/integrations/activesupport.rb +5 -0
- data/lib/farce/integrations/bson.rb +108 -0
- data/lib/farce/integrations/cbor.rb +51 -0
- data/lib/farce/integrations/concurrent.rb +112 -0
- data/lib/farce/integrations/dry-types.rb +5 -0
- data/lib/farce/integrations/dry_types.rb +694 -0
- data/lib/farce/integrations/json.rb +7 -0
- data/lib/farce/integrations/msgpack.rb +112 -0
- data/lib/farce/integrations/oj.rb +69 -0
- data/lib/farce/integrations/psych.rb +191 -0
- data/lib/farce/integrations/ractor-sharing.rb +5 -0
- data/lib/farce/integrations/ractor-tmvar.rb +5 -0
- data/lib/farce/integrations/ractor_sharing.rb +172 -0
- data/lib/farce/integrations/ractor_tmvar.rb +64 -0
- data/lib/farce/integrations/shared/to_json.rb +42 -0
- data/lib/farce/integrations/sorted_set.rb +11 -0
- data/lib/farce/integrations/weakref.rb +38 -0
- data/lib/farce/integrations/yajl.rb +10 -0
- data/lib/farce/integrations.rb +156 -0
- data/lib/farce/internal/_frozen_config.rb +11 -0
- data/lib/farce/internal/autoloads.rb +47 -0
- data/lib/farce/internal/blocking_priority_queue.rb +122 -0
- data/lib/farce/internal/converter.rb +106 -0
- data/lib/farce/internal/copyable.rb +31 -0
- data/lib/farce/internal/delegation.rb +20 -0
- data/lib/farce/internal/external_transaction.rb +59 -0
- data/lib/farce/internal/fake_ractor.rb +179 -0
- data/lib/farce/internal/freeze.rb +118 -0
- data/lib/farce/internal/inspect.rb +157 -0
- data/lib/farce/internal/key_lock_map.rb +29 -0
- data/lib/farce/internal/key_normalizer.rb +289 -0
- data/lib/farce/internal/lease_initialization.rb +115 -0
- data/lib/farce/internal/lease_map.rb +441 -0
- data/lib/farce/internal/lease_pool_state.rb +228 -0
- data/lib/farce/internal/lease_state.rb +342 -0
- data/lib/farce/internal/lease_waiting.rb +13 -0
- data/lib/farce/internal/managed_queue.rb +26 -0
- data/lib/farce/internal/map_value_modes.rb +227 -0
- data/lib/farce/internal/marshal_support.rb +227 -0
- data/lib/farce/internal/mixin.rb +20 -0
- data/lib/farce/internal/mutable_ordered_key_lock_map.rb +63 -0
- data/lib/farce/internal/noncopyable.rb +17 -0
- data/lib/farce/internal/ordered_key_lock_map.rb +69 -0
- data/lib/farce/internal/pool_supervisor.rb +41 -0
- data/lib/farce/internal/pool_worker.rb +61 -0
- data/lib/farce/internal/portable_transaction/reservation_entry.rb +29 -0
- data/lib/farce/internal/portable_transaction/strong_map_entry.rb +137 -0
- data/lib/farce/internal/portable_transaction/strong_map_size_entry.rb +44 -0
- data/lib/farce/internal/portable_transaction/tree_entry.rb +64 -0
- data/lib/farce/internal/portable_transaction.rb +164 -0
- data/lib/farce/internal/proxy_owner_notifications.rb +29 -0
- data/lib/farce/internal/reservation_waiting.rb +24 -0
- data/lib/farce/internal/scheduled_task.rb +48 -0
- data/lib/farce/internal/scheduler_io.rb +171 -0
- data/lib/farce/internal/scheduler_lifecycle.rb +314 -0
- data/lib/farce/internal/select_scheduler.rb +235 -0
- data/lib/farce/internal/storage.rb +162 -0
- data/lib/farce/internal/strict_lease.rb +30 -0
- data/lib/farce/internal/strict_lease_map.rb +20 -0
- data/lib/farce/internal/strict_lease_pool.rb +29 -0
- data/lib/farce/internal/thread_pool.rb +166 -0
- data/lib/farce/internal/transaction_conflict.rb +9 -0
- data/lib/farce/internal/transaction_freeze_guard.rb +30 -0
- data/lib/farce/internal/transaction_map_snapshot.rb +123 -0
- data/lib/farce/internal/undefined.rb +22 -0
- data/lib/farce/internal/unshared_lease.rb +29 -0
- data/lib/farce/internal/unshared_lease_pool.rb +34 -0
- data/lib/farce/internal/unshared_queue_waiting.rb +28 -0
- data/lib/farce/internal/value_serialization.rb +13 -0
- data/lib/farce/internal/weak_map_value_modes.rb +58 -0
- data/lib/farce/internal/weak_mode_manager.rb +26 -0
- data/lib/farce/internal.rb +129 -0
- data/lib/farce/lazy.rb +100 -0
- data/lib/farce/lazy_ref.rb +40 -0
- data/lib/farce/lease.rb +28 -0
- data/lib/farce/lease_map.rb +34 -0
- data/lib/farce/lease_pool.rb +29 -0
- data/lib/farce/lfu_map.rb +53 -0
- data/lib/farce/local/atom.rb +23 -0
- data/lib/farce/local/counter.rb +88 -0
- data/lib/farce/local/flag.rb +65 -0
- data/lib/farce/local/lazy.rb +38 -0
- data/lib/farce/local/lazy_ref.rb +36 -0
- data/lib/farce/local/lease.rb +77 -0
- data/lib/farce/local/lease_map.rb +80 -0
- data/lib/farce/local/lease_pool.rb +38 -0
- data/lib/farce/local/lfu_map.rb +55 -0
- data/lib/farce/local/lru_map.rb +62 -0
- data/lib/farce/local/map.rb +47 -0
- data/lib/farce/local/molecule.rb +45 -0
- data/lib/farce/local/priority_queue.rb +53 -0
- data/lib/farce/local/queue.rb +32 -0
- data/lib/farce/local/scoped.rb +166 -0
- data/lib/farce/local/set.rb +18 -0
- data/lib/farce/local/sorted_set.rb +50 -0
- data/lib/farce/local/timer_queue.rb +41 -0
- data/lib/farce/local/tree_map.rb +47 -0
- data/lib/farce/local/vector.rb +30 -0
- data/lib/farce/local/weak_atom.rb +23 -0
- data/lib/farce/local/weak_key_map.rb +39 -0
- data/lib/farce/local/weak_map.rb +39 -0
- data/lib/farce/local/weak_set.rb +18 -0
- data/lib/farce/local/weak_value_map.rb +39 -0
- data/lib/farce/local.rb +35 -0
- data/lib/farce/lock.rb +54 -0
- data/lib/farce/lru_map.rb +63 -0
- data/lib/farce/map.rb +58 -0
- data/lib/farce/mode_manager.rb +142 -0
- data/lib/farce/molecule.rb +60 -0
- data/lib/farce/mutable.rb +171 -0
- data/lib/farce/pool.rb +332 -0
- data/lib/farce/port.rb +134 -0
- data/lib/farce/priority_queue.rb +70 -0
- data/lib/farce/proxy/register.rb +101 -0
- data/lib/farce/proxy/supervisor.rb +137 -0
- data/lib/farce/proxy/wrapper.rb +49 -0
- data/lib/farce/proxy.rb +291 -0
- data/lib/farce/queue.rb +60 -0
- data/lib/farce/ractor.rb +269 -0
- data/lib/farce/read_write_lock.rb +256 -0
- data/lib/farce/reference.rb +158 -0
- data/lib/farce/resolv/dns.rb +42 -0
- data/lib/farce/resolv.rb +85 -0
- data/lib/farce/scheduler.rb +536 -0
- data/lib/farce/set.rb +39 -0
- data/lib/farce/shareable.rb +150 -0
- data/lib/farce/signal.rb +97 -0
- data/lib/farce/sorted_set.rb +36 -0
- data/lib/farce/strict/atom.rb +34 -0
- data/lib/farce/strict/counter.rb +10 -0
- data/lib/farce/strict/exchanger.rb +25 -0
- data/lib/farce/strict/flag.rb +10 -0
- data/lib/farce/strict/lazy.rb +26 -0
- data/lib/farce/strict/lazy_ref.rb +26 -0
- data/lib/farce/strict/lease.rb +27 -0
- data/lib/farce/strict/lease_map.rb +33 -0
- data/lib/farce/strict/lease_pool.rb +28 -0
- data/lib/farce/strict/lfu_map.rb +27 -0
- data/lib/farce/strict/lru_map.rb +28 -0
- data/lib/farce/strict/map.rb +54 -0
- data/lib/farce/strict/molecule.rb +16 -0
- data/lib/farce/strict/port.rb +40 -0
- data/lib/farce/strict/priority_queue.rb +20 -0
- data/lib/farce/strict/queue.rb +17 -0
- data/lib/farce/strict/set.rb +14 -0
- data/lib/farce/strict/sorted_set.rb +19 -0
- data/lib/farce/strict/timer_queue.rb +13 -0
- data/lib/farce/strict/tree_map.rb +27 -0
- data/lib/farce/strict/vector.rb +24 -0
- data/lib/farce/strict/weak_atom.rb +31 -0
- data/lib/farce/strict/weak_key_map.rb +49 -0
- data/lib/farce/strict/weak_map.rb +49 -0
- data/lib/farce/strict/weak_set.rb +14 -0
- data/lib/farce/strict/weak_value_map.rb +48 -0
- data/lib/farce/strict.rb +28 -0
- data/lib/farce/system.rb +40 -0
- data/lib/farce/thread_scheduler.rb +70 -0
- data/lib/farce/timer_queue.rb +66 -0
- data/lib/farce/transaction/atom.rb +73 -0
- data/lib/farce/transaction/map.rb +25 -0
- data/lib/farce/transaction/map_operations.rb +143 -0
- data/lib/farce/transaction/molecule.rb +90 -0
- data/lib/farce/transaction/mutable.rb +61 -0
- data/lib/farce/transaction/set.rb +17 -0
- data/lib/farce/transaction/set_operations.rb +48 -0
- data/lib/farce/transaction/sorted_set.rb +18 -0
- data/lib/farce/transaction/tree_map.rb +78 -0
- data/lib/farce/transaction/vector.rb +139 -0
- data/lib/farce/transaction/wrapper.rb +202 -0
- data/lib/farce/transaction.rb +343 -0
- data/lib/farce/tree_map.rb +56 -0
- data/lib/farce/unsafe/lfu_map.rb +36 -0
- data/lib/farce/unsafe/lru_map.rb +36 -0
- data/lib/farce/unsafe/tree_map.rb +21 -0
- data/lib/farce/unsafe.rb +29 -0
- data/lib/farce/unshareable.rb +67 -0
- data/lib/farce/unshared/atom.rb +24 -0
- data/lib/farce/unshared/counter.rb +10 -0
- data/lib/farce/unshared/flag.rb +10 -0
- data/lib/farce/unshared/lazy.rb +38 -0
- data/lib/farce/unshared/lazy_ref.rb +26 -0
- data/lib/farce/unshared/lease.rb +27 -0
- data/lib/farce/unshared/lease_map.rb +26 -0
- data/lib/farce/unshared/lease_pool.rb +25 -0
- data/lib/farce/unshared/lfu_map.rb +20 -0
- data/lib/farce/unshared/lru_map.rb +20 -0
- data/lib/farce/unshared/map.rb +49 -0
- data/lib/farce/unshared/molecule.rb +16 -0
- data/lib/farce/unshared/priority_queue.rb +16 -0
- data/lib/farce/unshared/queue.rb +27 -0
- data/lib/farce/unshared/set.rb +14 -0
- data/lib/farce/unshared/sorted_set.rb +28 -0
- data/lib/farce/unshared/timer_queue.rb +16 -0
- data/lib/farce/unshared/tree_map.rb +21 -0
- data/lib/farce/unshared/vector.rb +19 -0
- data/lib/farce/unshared/weak_atom.rb +31 -0
- data/lib/farce/unshared/weak_key_map.rb +44 -0
- data/lib/farce/unshared/weak_map.rb +44 -0
- data/lib/farce/unshared/weak_set.rb +14 -0
- data/lib/farce/unshared/weak_value_map.rb +43 -0
- data/lib/farce/unshared.rb +28 -0
- data/lib/farce/vector.rb +228 -0
- data/lib/farce/version.rb +8 -0
- data/lib/farce/walker/definitions.rb +186 -0
- data/lib/farce/walker/modification.rb +355 -0
- data/lib/farce/walker.rb +319 -0
- data/lib/farce/weak_atom.rb +121 -0
- data/lib/farce/weak_key_map.rb +31 -0
- data/lib/farce/weak_map.rb +33 -0
- data/lib/farce/weak_ref.rb +67 -0
- data/lib/farce/weak_set.rb +82 -0
- data/lib/farce/weak_value.rb +195 -0
- data/lib/farce/weak_value_map.rb +32 -0
- data/lib/farce.rb +519 -0
- metadata +468 -0
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# shareable_constant_value: literal
|
|
3
|
+
# warn_indent: true
|
|
4
|
+
|
|
5
|
+
module Farce
|
|
6
|
+
module Internal # :nodoc: all
|
|
7
|
+
module PortableTransaction
|
|
8
|
+
# Validate a size-only read without comparing values. Keep the index locked
|
|
9
|
+
# through publication so additions and removals wait. Cells created after
|
|
10
|
+
# preparation use temporary locks. A live absent cell remains reserved by
|
|
11
|
+
# its initializer and invalidates the attempt. Present or retired cells
|
|
12
|
+
# cannot change membership without the index lock.
|
|
13
|
+
class StrongMapSizeEntry < StrongMapEntry
|
|
14
|
+
def initialize(source, size)
|
|
15
|
+
super(source, nil, :strong_map, size)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def prepare
|
|
19
|
+
capture_cells
|
|
20
|
+
@cell_locks = {}.compare_by_identity
|
|
21
|
+
@cells.each { @cell_locks[it.instance_variable_get(:@mutex)] = true }
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def valid?
|
|
25
|
+
return false if @index_lock.instance_variable_get(:@reserved)
|
|
26
|
+
current = @index.instance_variable_get(:@entries).compact
|
|
27
|
+
acquired = []
|
|
28
|
+
begin
|
|
29
|
+
current.each do |cell|
|
|
30
|
+
lock = cell.instance_variable_get(:@mutex)
|
|
31
|
+
next if @cell_locks.key?(lock)
|
|
32
|
+
return false unless lock.try_lock
|
|
33
|
+
acquired << lock
|
|
34
|
+
end
|
|
35
|
+
return false if current.any? { it.instance_variable_get(:@reserved) }
|
|
36
|
+
current.count { !it.instance_variable_get(:@retired) && it.instance_variable_get(:@present) } == @baseline
|
|
37
|
+
ensure
|
|
38
|
+
acquired.reverse_each(&:unlock)
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# shareable_constant_value: literal
|
|
3
|
+
# warn_indent: true
|
|
4
|
+
|
|
5
|
+
module Farce
|
|
6
|
+
module Internal # :nodoc: all
|
|
7
|
+
module PortableTransaction
|
|
8
|
+
# Keep the state object and lock stable for ordinary operations already
|
|
9
|
+
# waiting on them. Only replace storage under that existing lock.
|
|
10
|
+
class TreeEntry < Entry
|
|
11
|
+
def initialize(source, working, revision, lock, field) # rubocop:disable Lint/MissingSuper
|
|
12
|
+
@source = source
|
|
13
|
+
@working = working
|
|
14
|
+
@state = source.instance_variable_get(:@state)
|
|
15
|
+
@baseline = revision
|
|
16
|
+
@locks = [lock]
|
|
17
|
+
@field = field
|
|
18
|
+
@dirty = @applied = false
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def prepare
|
|
22
|
+
state = @working.instance_variable_get(:@state)
|
|
23
|
+
@replacement = @field == :tree ? state.tree : state.entries
|
|
24
|
+
@next_version = @baseline + 1
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def valid?
|
|
28
|
+
return false if @dirty && @source.frozen?
|
|
29
|
+
owner = if @field == :tree
|
|
30
|
+
@source.instance_variable_get(:@guard).instance_variable_get(:@owner)
|
|
31
|
+
else
|
|
32
|
+
@state.operation_owner
|
|
33
|
+
end
|
|
34
|
+
return false if owner || @state.revision != @baseline
|
|
35
|
+
@original = @field == :tree ? @state.tree : @state.entries
|
|
36
|
+
true
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def apply
|
|
40
|
+
return unless @dirty
|
|
41
|
+
@applied = true
|
|
42
|
+
replace(@replacement, @next_version)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def restore
|
|
46
|
+
replace(@original, @baseline) if @applied
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def notify = nil
|
|
50
|
+
|
|
51
|
+
private
|
|
52
|
+
|
|
53
|
+
def replace(storage, revision)
|
|
54
|
+
if @field == :tree
|
|
55
|
+
@state.tree = storage
|
|
56
|
+
else
|
|
57
|
+
@state.instance_variable_set(:@entries, storage)
|
|
58
|
+
end
|
|
59
|
+
@state.revision = revision
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# shareable_constant_value: literal
|
|
3
|
+
# warn_indent: true
|
|
4
|
+
|
|
5
|
+
module Farce
|
|
6
|
+
module Internal # :nodoc: all
|
|
7
|
+
# Portable commits use existing mutexes. Validation compares original
|
|
8
|
+
# storage by identity without invoking user equality or hash callbacks.
|
|
9
|
+
module PortableTransaction
|
|
10
|
+
include Autoloads
|
|
11
|
+
|
|
12
|
+
def self.same?(left, right) = BasicObject.instance_method(:equal?).bind_call(left, right)
|
|
13
|
+
|
|
14
|
+
def self.snapshot(source, kind)
|
|
15
|
+
case kind
|
|
16
|
+
when :atom
|
|
17
|
+
value, version = source.instance_variable_get(:@mutex).synchronize do
|
|
18
|
+
[source.instance_variable_get(:@value), source.instance_variable_get(:@version)]
|
|
19
|
+
end
|
|
20
|
+
working = source.class.new(value, compare_by_identity: source.compare_by_identity?)
|
|
21
|
+
Entry.new(source, working, kind, version)
|
|
22
|
+
when :vector
|
|
23
|
+
values = source.snapshot
|
|
24
|
+
working = source.class.new(values, compare_by_identity: source.compare_by_identity?)
|
|
25
|
+
Entry.new(source, working, kind, values)
|
|
26
|
+
when :map, :strong_map
|
|
27
|
+
pairs = source.transaction_pairs
|
|
28
|
+
working = source.class.new(
|
|
29
|
+
compare_keys_by_identity: source.compare_keys_by_identity?,
|
|
30
|
+
compare_values_by_identity: source.compare_values_by_identity?,
|
|
31
|
+
)
|
|
32
|
+
pairs.each { |key, value| working[key] = value }
|
|
33
|
+
baseline = {}.compare_by_identity
|
|
34
|
+
pairs.each { |key, value| baseline[key] = value }
|
|
35
|
+
entry_class = kind == :strong_map ? StrongMapEntry : Entry
|
|
36
|
+
entry_class.new(source, working, kind, baseline)
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def self.commit(entries, guards = [])
|
|
41
|
+
entries.each(&:prepare)
|
|
42
|
+
locks = entries.flat_map(&:locks).uniq.sort_by(&:object_id)
|
|
43
|
+
acquired = []
|
|
44
|
+
begin
|
|
45
|
+
locks.each do |lock|
|
|
46
|
+
return false unless lock.try_lock
|
|
47
|
+
acquired << lock
|
|
48
|
+
end
|
|
49
|
+
return false if guards.any?(&:value)
|
|
50
|
+
return false unless entries.all?(&:valid?)
|
|
51
|
+
begin
|
|
52
|
+
entries.each(&:apply)
|
|
53
|
+
unless !block_given? || yield
|
|
54
|
+
entries.reverse_each(&:restore)
|
|
55
|
+
return false
|
|
56
|
+
end
|
|
57
|
+
rescue Exception # rubocop:disable Lint/RescueException -- restore before propagating any interruption
|
|
58
|
+
entries.reverse_each(&:restore)
|
|
59
|
+
raise
|
|
60
|
+
end
|
|
61
|
+
true
|
|
62
|
+
ensure
|
|
63
|
+
acquired.reverse_each(&:unlock)
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
class Entry
|
|
68
|
+
attr_reader :working, :locks
|
|
69
|
+
|
|
70
|
+
def initialize(source, working, kind, baseline)
|
|
71
|
+
@source = source
|
|
72
|
+
@working = working
|
|
73
|
+
@kind = kind
|
|
74
|
+
@baseline = baseline
|
|
75
|
+
@dirty = false
|
|
76
|
+
@applied = false
|
|
77
|
+
@field = { atom: :@value, vector: :@values, map: :@map }.fetch(kind)
|
|
78
|
+
@locks = if kind == :map
|
|
79
|
+
%i[@reservation_mutex @state_mutex].map { source.instance_variable_get(it) }
|
|
80
|
+
else
|
|
81
|
+
[source.instance_variable_get(:@mutex)]
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def write!
|
|
86
|
+
@dirty = true
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def prepare
|
|
90
|
+
@replacement = @working.instance_variable_get(@field)
|
|
91
|
+
@next_version = @baseline + 1 if @kind == :atom
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def valid?
|
|
95
|
+
frozen = @kind == :map ? @source.frozen? : @source.instance_variable_get(:@farce_frozen)
|
|
96
|
+
return false if @dirty && frozen
|
|
97
|
+
@original = @source.instance_variable_get(@field)
|
|
98
|
+
case @kind
|
|
99
|
+
when :atom
|
|
100
|
+
!@source.instance_variable_get(:@updating) &&
|
|
101
|
+
@source.instance_variable_get(:@version) == @baseline
|
|
102
|
+
when :vector
|
|
103
|
+
!@source.instance_variable_get(:@updating) && @original.size == @baseline.size &&
|
|
104
|
+
@baseline.each_index.all? { PortableTransaction.same?(@original[it], @baseline[it]) }
|
|
105
|
+
when :map
|
|
106
|
+
return false unless @source.transaction_idle?
|
|
107
|
+
pairs = @source.transaction_pairs
|
|
108
|
+
pairs.size == @baseline.size && pairs.all? do |key, value|
|
|
109
|
+
@baseline.key?(key) && PortableTransaction.same?(@baseline[key], value)
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def apply
|
|
115
|
+
return unless @dirty
|
|
116
|
+
@applied = true
|
|
117
|
+
@source.instance_variable_set(@field, @replacement)
|
|
118
|
+
@source.instance_variable_set(:@version, @next_version) if @kind == :atom
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
# CRuby's Array iteration captures storage without its Ruby mutex.
|
|
122
|
+
# Hide provisional replacements from newly starting iterators.
|
|
123
|
+
def reserve
|
|
124
|
+
return unless @kind == :vector
|
|
125
|
+
@reserved = true
|
|
126
|
+
@source.instance_variable_set(:@updating, :transaction)
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def unreserve
|
|
130
|
+
return unless @reserved
|
|
131
|
+
@source.instance_variable_set(:@updating, false)
|
|
132
|
+
@reserved = false
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
def restore
|
|
136
|
+
return unless @applied
|
|
137
|
+
@source.instance_variable_set(@field, @original)
|
|
138
|
+
@source.instance_variable_set(:@version, @baseline) if @kind == :atom
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def notify
|
|
142
|
+
return unless @dirty
|
|
143
|
+
signal = @source.instance_variable_get(@kind == :map ? :@change_signal : :@signal)
|
|
144
|
+
if @kind == :atom
|
|
145
|
+
@locks.first.synchronize { signal.broadcast }
|
|
146
|
+
else
|
|
147
|
+
signal.broadcast
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
# Validate only cardinality while ordinary map mutations are excluded by
|
|
153
|
+
# the existing reservation and state locks. This entry never publishes data.
|
|
154
|
+
class MapSizeEntry < Entry
|
|
155
|
+
def initialize(source, size)
|
|
156
|
+
super(source, nil, :map, size)
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
def prepare = nil
|
|
160
|
+
def valid? = @source.transaction_idle? && @source.size == @baseline
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# shareable_constant_value: literal
|
|
3
|
+
# warn_indent: true
|
|
4
|
+
|
|
5
|
+
module Farce
|
|
6
|
+
module Internal
|
|
7
|
+
# Close proxy channels when their owner exits, without retaining unused proxies.
|
|
8
|
+
module ProxyOwnerNotifications
|
|
9
|
+
def initialize
|
|
10
|
+
@alive = Flag.new(true)
|
|
11
|
+
@supervisors = Strict::Map.new(compare_by_identity: true)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def alive? = @alive.value
|
|
15
|
+
|
|
16
|
+
def register(supervisor)
|
|
17
|
+
@supervisors[supervisor] = true
|
|
18
|
+
supervisor.stop unless alive?
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def unregister(supervisor) = @supervisors.delete(supervisor)
|
|
22
|
+
|
|
23
|
+
def stop
|
|
24
|
+
@alive.value = false
|
|
25
|
+
@supervisors.each_key(&:stop)
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# shareable_constant_value: literal
|
|
3
|
+
# warn_indent: true
|
|
4
|
+
|
|
5
|
+
module Farce
|
|
6
|
+
# @!visibility private
|
|
7
|
+
module Internal # :nodoc: all
|
|
8
|
+
# Keep reservation waits cancelable without shortening their overall deadline.
|
|
9
|
+
module ReservationWaiting
|
|
10
|
+
def self.wait(signal, generation, deadline)
|
|
11
|
+
while true
|
|
12
|
+
remaining = deadline - Clock.now if deadline
|
|
13
|
+
return false if remaining && !remaining.positive?
|
|
14
|
+
|
|
15
|
+
# LeaseWaiting supplies the full remaining timeout on default engines
|
|
16
|
+
# and short cancellation slices on JRuby. Reuse that existing hook
|
|
17
|
+
# without changing how other Signal users wait.
|
|
18
|
+
interval = LeaseWaiting.wait_interval(remaining)
|
|
19
|
+
return true if signal.wait(generation, timeout: interval) { false }
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# shareable_constant_value: literal
|
|
3
|
+
# warn_indent: true
|
|
4
|
+
|
|
5
|
+
module Farce
|
|
6
|
+
module Internal # :nodoc: all
|
|
7
|
+
# A shareable task and its transferred arguments.
|
|
8
|
+
class ScheduledTask
|
|
9
|
+
include Internal::Copyable
|
|
10
|
+
include Shareable::Immutable
|
|
11
|
+
|
|
12
|
+
MANAGER = ModeManager.new
|
|
13
|
+
|
|
14
|
+
def initialize(args, block, mode)
|
|
15
|
+
@block = Ractor.shareable_proc(&block)
|
|
16
|
+
@unwrap = false
|
|
17
|
+
@args = args.map! do |arg|
|
|
18
|
+
wrapped = MANAGER.wrap(arg, mode:)
|
|
19
|
+
@unwrap = true if MANAGER.managed_envelope?(wrapped)
|
|
20
|
+
wrapped
|
|
21
|
+
end.freeze
|
|
22
|
+
super()
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def to_proc(completion = nil)
|
|
26
|
+
if completion
|
|
27
|
+
if @unwrap
|
|
28
|
+
lambda do
|
|
29
|
+
@block.call(*@args.map { |arg| MANAGER.unwrap(arg) })
|
|
30
|
+
ensure
|
|
31
|
+
completion.task_finished
|
|
32
|
+
end
|
|
33
|
+
else
|
|
34
|
+
lambda do
|
|
35
|
+
@block.call(*@args)
|
|
36
|
+
ensure
|
|
37
|
+
completion.task_finished
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
elsif @unwrap
|
|
41
|
+
-> { @block.call(*@args.map { |arg| MANAGER.unwrap(arg) }) }
|
|
42
|
+
else
|
|
43
|
+
-> { @block.call(*@args) }
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
require "fcntl"
|
|
3
|
+
require "socket"
|
|
4
|
+
require "io/nonblock"
|
|
5
|
+
|
|
6
|
+
module Farce
|
|
7
|
+
module Internal
|
|
8
|
+
# IO hooks and helpers shared by the scheduler implementations.
|
|
9
|
+
module SchedulerIO
|
|
10
|
+
def io_select(readers = nil, writers = nil, errors = nil, timeout = nil)
|
|
11
|
+
check_fiber
|
|
12
|
+
timeout = duration_value(timeout)
|
|
13
|
+
groups = [readers, writers, errors].map { |list| list.nil? ? [] : list.to_ary.dup }
|
|
14
|
+
ios = groups.map { |list| list.map(&:to_io) }
|
|
15
|
+
check_fiber # to_ary/to_io can run arbitrary Ruby
|
|
16
|
+
|
|
17
|
+
found = select_result(groups, ios, 0)
|
|
18
|
+
return checkpoint_result(found) if found || timeout&.zero?
|
|
19
|
+
token = arm_wait
|
|
20
|
+
|
|
21
|
+
register_select(token, groups, ios)
|
|
22
|
+
wait_with_timeout(token, timeout, nil)
|
|
23
|
+
ensure
|
|
24
|
+
retire_wait(token) if token
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
SINGLE_TRANSFER = defined?(IO::Buffer::VERSION) && IO::Buffer::VERSION >= 3
|
|
28
|
+
private_constant :SINGLE_TRANSFER
|
|
29
|
+
|
|
30
|
+
if SINGLE_TRANSFER
|
|
31
|
+
def io_pread(io, buffer, file_offset, offset, length)
|
|
32
|
+
positional_io(false, io, buffer, length, file_offset, offset, single_transfer: true)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def io_pwrite(io, buffer, file_offset, offset, length)
|
|
36
|
+
positional_io(true, io, buffer, length, file_offset, offset, single_transfer: true)
|
|
37
|
+
end
|
|
38
|
+
else
|
|
39
|
+
def io_pread(io, buffer, file_offset, minimum, offset = 0)
|
|
40
|
+
positional_io(false, io, buffer, minimum, file_offset, offset)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def io_pwrite(io, buffer, file_offset, minimum, offset = 0)
|
|
44
|
+
positional_io(true, io, buffer, minimum, file_offset, offset)
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
private
|
|
49
|
+
|
|
50
|
+
def owner_thread = Thread.current
|
|
51
|
+
def start_fiber(fiber) = fiber.resume
|
|
52
|
+
def policy_changed = nil
|
|
53
|
+
def dispatch_budget = 256
|
|
54
|
+
def finish_fiber = nil
|
|
55
|
+
def begin_close = nil
|
|
56
|
+
def begin_shutdown = nil
|
|
57
|
+
|
|
58
|
+
def select_snapshot(groups, timeout)
|
|
59
|
+
Fiber.blocking { IO.select(*groups, timeout) }
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# Coercion belongs to admission. A mutable wrapper must not switch the IO
|
|
63
|
+
# being observed after its wait has been registered.
|
|
64
|
+
def select_result(groups, ios, timeout)
|
|
65
|
+
selected = select_snapshot(ios, timeout)
|
|
66
|
+
return unless selected
|
|
67
|
+
identities = selected.map do |list|
|
|
68
|
+
list.each_with_object({}.compare_by_identity) { |io, set| set[io] = true }
|
|
69
|
+
end
|
|
70
|
+
groups.each_with_index.map do |list, i|
|
|
71
|
+
list.each_with_index.filter_map { |object, j| object if identities[i].key?(ios[i][j]) }
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def readiness(result)
|
|
76
|
+
return 0 unless result
|
|
77
|
+
(result[0].empty? ? 0 : IO::READABLE) |
|
|
78
|
+
(result[1].empty? ? 0 : IO::WRITABLE) |
|
|
79
|
+
(result[2].empty? ? 0 : IO::PRIORITY)
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def transfer_arguments(io, buffer, minimum, offset, writing)
|
|
83
|
+
check_fiber
|
|
84
|
+
io = io.to_io
|
|
85
|
+
minimum, offset = Integer(minimum), Integer(offset)
|
|
86
|
+
check_fiber
|
|
87
|
+
io.fileno
|
|
88
|
+
raise ArgumentError, "minimum and offset exceed buffer size" if
|
|
89
|
+
minimum.negative? || offset.negative? || offset > buffer.size || minimum > buffer.size - offset
|
|
90
|
+
raise IO::Buffer::AccessError, "buffer is readonly" if !writing && buffer.readonly?
|
|
91
|
+
[io, minimum, offset]
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def locked_buffer(buffer, &work)
|
|
95
|
+
!SINGLE_TRANSFER && buffer.locked? ? work.call : buffer.locked(&work)
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def duplicate_io(io, writing)
|
|
99
|
+
# IO#dup flushes Ruby's buffers. During an io_write hook that would flush
|
|
100
|
+
# the same payload twice. Duplicate the descriptor below Ruby buffering.
|
|
101
|
+
Fiber.blocking do
|
|
102
|
+
copy = IO.for_fd(io.fcntl(Fcntl::F_DUPFD, 0), writing ? "w" : "r")
|
|
103
|
+
copy.close_on_exec = true
|
|
104
|
+
copy
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def positional_io(writing, io, buffer, minimum, file_offset, offset, single_transfer: false)
|
|
109
|
+
io, minimum, offset = transfer_arguments(io, buffer, minimum, offset, writing)
|
|
110
|
+
file_offset = Integer(file_offset)
|
|
111
|
+
check_fiber
|
|
112
|
+
locked_buffer(buffer) do
|
|
113
|
+
copy = duplicate_io(io, writing)
|
|
114
|
+
capacity = single_transfer ? minimum : buffer.size - offset
|
|
115
|
+
minimum = capacity.zero? ? 0 : 1 if single_transfer
|
|
116
|
+
data = buffer.get_string(offset, capacity) if writing
|
|
117
|
+
value = background do
|
|
118
|
+
worker_transfer(copy, writing, data, capacity, minimum, file_offset)
|
|
119
|
+
end
|
|
120
|
+
if writing || value.is_a?(Integer)
|
|
121
|
+
value
|
|
122
|
+
else
|
|
123
|
+
buffer.set_string(value, offset)
|
|
124
|
+
value.bytesize
|
|
125
|
+
end
|
|
126
|
+
ensure
|
|
127
|
+
copy&.close
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
# File workers own copies. A cancelled caller waits for acknowledgement
|
|
132
|
+
# before its ensure closes the duplicated IO or releases any borrowed state.
|
|
133
|
+
def file_transfer(writing, io, buffer, minimum, offset, capacity = buffer.size - offset)
|
|
134
|
+
copy = duplicate_io(io, writing)
|
|
135
|
+
data = buffer.get_string(offset, capacity) if writing
|
|
136
|
+
value = background do
|
|
137
|
+
worker_transfer(copy, writing, data, capacity, minimum)
|
|
138
|
+
end
|
|
139
|
+
return value if writing || value.is_a?(Integer)
|
|
140
|
+
buffer.set_string(value, offset)
|
|
141
|
+
value.bytesize
|
|
142
|
+
ensure
|
|
143
|
+
copy&.close
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
def worker_transfer(io, writing, data, capacity, minimum, position = nil)
|
|
147
|
+
total = 0
|
|
148
|
+
output = +"".b
|
|
149
|
+
return writing ? 0 : output if capacity.zero?
|
|
150
|
+
loop do
|
|
151
|
+
remaining = capacity - total
|
|
152
|
+
if writing
|
|
153
|
+
chunk = data.byteslice(total, remaining)
|
|
154
|
+
amount = position ? io.pwrite(chunk, position + total) : io.syswrite(chunk)
|
|
155
|
+
else
|
|
156
|
+
chunk = position ? io.pread(remaining, position + total) : io.sysread(remaining)
|
|
157
|
+
output << chunk
|
|
158
|
+
amount = chunk.bytesize
|
|
159
|
+
end
|
|
160
|
+
total += amount
|
|
161
|
+
break if amount.zero? || total >= minimum
|
|
162
|
+
end
|
|
163
|
+
writing ? total : output
|
|
164
|
+
rescue EOFError
|
|
165
|
+
output
|
|
166
|
+
rescue SystemCallError => e
|
|
167
|
+
total.positive? ? (writing ? total : output) : -e.errno
|
|
168
|
+
end
|
|
169
|
+
end
|
|
170
|
+
end
|
|
171
|
+
end
|