farce 0 → 0.1.0
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 +4 -4
- data/CHANGELOG.md +9 -0
- data/CODE_OF_CONDUCT.md +26 -0
- data/CONTRIBUTING.md +71 -0
- data/MIT-LICENSE +20 -0
- data/README.md +1525 -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/fiber_scheduler.rb +20 -0
- data/lib/farce/engine/ruby/3.4/port.rb +186 -0
- data/lib/farce/engine/ruby/3.4/ractor_methods.rb +26 -0
- data/lib/farce/engine/ruby/3.4/ractor_selector.rb +92 -0
- data/lib/farce/engine/ruby/3.4/vault.rb +56 -0
- data/lib/farce/engine/ruby/4.0/port.rb +19 -0
- data/lib/farce/engine/ruby/4.0/ractor_methods.rb +20 -0
- data/lib/farce/engine/ruby/4.0/ractor_selector.rb +108 -0
- data/lib/farce/engine/ruby/4.0/vault.rb +74 -0
- data/lib/farce/engine/ruby/4.1/port.rb +21 -0
- data/lib/farce/engine/ruby/4.1/ractor_methods.rb +22 -0
- data/lib/farce/engine/ruby/4.1/ractor_selector.rb +25 -0
- data/lib/farce/engine/ruby/4.1/vault.rb +5 -0
- data/lib/farce/engine/ruby/fiber_scheduler.rb +32 -0
- data/lib/farce/engine/ruby/key_lock_map.rb +28 -0
- data/lib/farce/engine/ruby/shared/lease.rb +26 -0
- data/lib/farce/engine/ruby/shared/lease_pool.rb +24 -0
- data/lib/farce/engine/ruby/shared/main_scheduler.rb +9 -0
- data/lib/farce/engine/ruby/shared/parallel_scheduler.rb +9 -0
- data/lib/farce/engine/ruby/shared/proxy_owner.rb +31 -0
- data/lib/farce/engine/ruby/shared/ractor_methods.rb +34 -0
- data/lib/farce/engine/ruby/shared/ractor_selector.rb +328 -0
- data/lib/farce/engine/ruby/shared/strict_map.rb +13 -0
- data/lib/farce/engine/ruby/shared/unshared_vector.rb +14 -0
- data/lib/farce/engine/ruby/shared/vault.rb +144 -0
- data/lib/farce/engine/ruby/shared/vault_weak_map.rb +336 -0
- data/lib/farce/engine/ruby/shared/weak_atom.rb +54 -0
- data/lib/farce/engine/ruby/shared/weak_map.rb +413 -0
- data/lib/farce/engine/ruby.rb +130 -0
- data/lib/farce/engine/shared/atom.rb +10 -0
- data/lib/farce/engine/shared/exchanger.rb +130 -0
- data/lib/farce/engine/shared/identity_key.rb +22 -0
- data/lib/farce/engine/shared/lease.rb +10 -0
- data/lib/farce/engine/shared/lease_pool.rb +10 -0
- data/lib/farce/engine/shared/main_scheduler.rb +20 -0
- data/lib/farce/engine/shared/map_key_coordination.rb +226 -0
- data/lib/farce/engine/shared/parallel_scheduler.rb +9 -0
- data/lib/farce/engine/shared/port.rb +44 -0
- data/lib/farce/engine/shared/portable_bounded_map.rb +619 -0
- data/lib/farce/engine/shared/proxy_owner.rb +43 -0
- data/lib/farce/engine/shared/queue.rb +233 -0
- data/lib/farce/engine/shared/ractor_methods.rb +73 -0
- data/lib/farce/engine/shared/rebindable.rb +15 -0
- data/lib/farce/engine/shared/strict_atom.rb +73 -0
- data/lib/farce/engine/shared/strict_map.rb +117 -0
- data/lib/farce/engine/shared/strict_queue_values.rb +27 -0
- data/lib/farce/engine/shared/strict_tree_map.rb +56 -0
- data/lib/farce/engine/shared/transaction_map_backend.rb +20 -0
- data/lib/farce/engine/shared/trie.rb +317 -0
- data/lib/farce/engine/shared/trie_builder.rb +199 -0
- data/lib/farce/engine/shared/unshareable.rb +14 -0
- data/lib/farce/engine/shared/unshared_atom.rb +256 -0
- data/lib/farce/engine/shared/unshared_priority_queue.rb +9 -0
- data/lib/farce/engine/shared/unshared_queue.rb +18 -0
- data/lib/farce/engine/shared/unshared_signal.rb +21 -0
- data/lib/farce/engine/shared/unshared_vector.rb +377 -0
- data/lib/farce/engine/shared/unshared_weak_atom.rb +30 -0
- data/lib/farce/engine/shared/unshared_weak_map.rb +445 -0
- data/lib/farce/engine/shared/vault.rb +31 -0
- data/lib/farce/engine/shared/vector.rb +83 -0
- data/lib/farce/engine/shared/weak_atom/base.rb +189 -0
- data/lib/farce/engine/shared/weak_atom.rb +20 -0
- data/lib/farce/engine/shared/weak_map/cell.rb +99 -0
- data/lib/farce/engine/shared/weak_map/index.rb +249 -0
- data/lib/farce/engine/shared/weak_map/lock.rb +80 -0
- data/lib/farce/engine/shared/weak_map/reference.rb +33 -0
- data/lib/farce/engine/shared.rb +63 -0
- data/lib/farce/engine/truffleruby/fiber_scheduler.rb +13 -0
- data/lib/farce/engine/truffleruby/lock.rb +121 -0
- data/lib/farce/engine/truffleruby/map.rb +656 -0
- data/lib/farce/engine/truffleruby/native/counter.rb +105 -0
- data/lib/farce/engine/truffleruby/native/flag.rb +77 -0
- data/lib/farce/engine/truffleruby/native/ordered_array_support.rb +67 -0
- data/lib/farce/engine/truffleruby/native/priority_queue.rb +388 -0
- data/lib/farce/engine/truffleruby/native/tree_map.rb +51 -0
- data/lib/farce/engine/truffleruby/native/unsafe_tree_map.rb +350 -0
- data/lib/farce/engine/truffleruby/signal.rb +77 -0
- data/lib/farce/engine/truffleruby.rb +148 -0
- data/lib/farce/envelope.rb +335 -0
- data/lib/farce/error.rb +35 -0
- data/lib/farce/exchanger.rb +41 -0
- data/lib/farce/flag.rb +27 -0
- data/lib/farce/integrations/active_support/blank.rb +68 -0
- data/lib/farce/integrations/active_support/clock.rb +17 -0
- data/lib/farce/integrations/active_support/duplicable.rb +78 -0
- data/lib/farce/integrations/active_support/map.rb +175 -0
- data/lib/farce/integrations/active_support/set.rb +60 -0
- data/lib/farce/integrations/active_support/value_serialization.rb +17 -0
- data/lib/farce/integrations/active_support/vector.rb +231 -0
- data/lib/farce/integrations/active_support.rb +9 -0
- data/lib/farce/integrations/activesupport.rb +5 -0
- data/lib/farce/integrations/bson.rb +108 -0
- data/lib/farce/integrations/cbor.rb +51 -0
- data/lib/farce/integrations/concurrent.rb +112 -0
- data/lib/farce/integrations/dry-types.rb +5 -0
- data/lib/farce/integrations/dry_types.rb +694 -0
- data/lib/farce/integrations/json.rb +7 -0
- data/lib/farce/integrations/msgpack.rb +112 -0
- data/lib/farce/integrations/oj.rb +69 -0
- data/lib/farce/integrations/psych.rb +191 -0
- data/lib/farce/integrations/ractor-sharing.rb +5 -0
- data/lib/farce/integrations/ractor-tmvar.rb +5 -0
- data/lib/farce/integrations/ractor_sharing.rb +172 -0
- data/lib/farce/integrations/ractor_tmvar.rb +64 -0
- data/lib/farce/integrations/shared/to_json.rb +42 -0
- data/lib/farce/integrations/sorted_set.rb +11 -0
- data/lib/farce/integrations/weakref.rb +38 -0
- data/lib/farce/integrations/yajl.rb +10 -0
- data/lib/farce/integrations.rb +156 -0
- data/lib/farce/internal/_frozen_config.rb +11 -0
- data/lib/farce/internal/autoloads.rb +47 -0
- data/lib/farce/internal/blocking_priority_queue.rb +122 -0
- data/lib/farce/internal/converter.rb +106 -0
- data/lib/farce/internal/copyable.rb +31 -0
- data/lib/farce/internal/delegation.rb +20 -0
- data/lib/farce/internal/external_transaction.rb +59 -0
- data/lib/farce/internal/fake_ractor.rb +179 -0
- data/lib/farce/internal/freeze.rb +118 -0
- data/lib/farce/internal/inspect.rb +157 -0
- data/lib/farce/internal/key_lock_map.rb +29 -0
- data/lib/farce/internal/key_normalizer.rb +289 -0
- data/lib/farce/internal/lease_initialization.rb +115 -0
- data/lib/farce/internal/lease_map.rb +441 -0
- data/lib/farce/internal/lease_pool_state.rb +228 -0
- data/lib/farce/internal/lease_state.rb +342 -0
- data/lib/farce/internal/lease_waiting.rb +13 -0
- data/lib/farce/internal/managed_queue.rb +26 -0
- data/lib/farce/internal/map_value_modes.rb +227 -0
- data/lib/farce/internal/marshal_support.rb +227 -0
- data/lib/farce/internal/mixin.rb +20 -0
- data/lib/farce/internal/mutable_ordered_key_lock_map.rb +63 -0
- data/lib/farce/internal/noncopyable.rb +17 -0
- data/lib/farce/internal/ordered_key_lock_map.rb +69 -0
- data/lib/farce/internal/pool_supervisor.rb +41 -0
- data/lib/farce/internal/pool_worker.rb +61 -0
- data/lib/farce/internal/portable_transaction/reservation_entry.rb +29 -0
- data/lib/farce/internal/portable_transaction/strong_map_entry.rb +137 -0
- data/lib/farce/internal/portable_transaction/strong_map_size_entry.rb +44 -0
- data/lib/farce/internal/portable_transaction/tree_entry.rb +64 -0
- data/lib/farce/internal/portable_transaction.rb +164 -0
- data/lib/farce/internal/proxy_owner_notifications.rb +29 -0
- data/lib/farce/internal/reservation_waiting.rb +24 -0
- data/lib/farce/internal/scheduled_task.rb +48 -0
- data/lib/farce/internal/scheduler_io.rb +171 -0
- data/lib/farce/internal/scheduler_lifecycle.rb +314 -0
- data/lib/farce/internal/select_scheduler.rb +235 -0
- data/lib/farce/internal/storage.rb +162 -0
- data/lib/farce/internal/strict_lease.rb +30 -0
- data/lib/farce/internal/strict_lease_map.rb +20 -0
- data/lib/farce/internal/strict_lease_pool.rb +29 -0
- data/lib/farce/internal/thread_pool.rb +166 -0
- data/lib/farce/internal/transaction_conflict.rb +9 -0
- data/lib/farce/internal/transaction_freeze_guard.rb +30 -0
- data/lib/farce/internal/transaction_map_snapshot.rb +123 -0
- data/lib/farce/internal/undefined.rb +22 -0
- data/lib/farce/internal/unshared_lease.rb +29 -0
- data/lib/farce/internal/unshared_lease_pool.rb +34 -0
- data/lib/farce/internal/unshared_queue_waiting.rb +28 -0
- data/lib/farce/internal/value_serialization.rb +13 -0
- data/lib/farce/internal/weak_map_value_modes.rb +58 -0
- data/lib/farce/internal/weak_mode_manager.rb +26 -0
- data/lib/farce/internal.rb +129 -0
- data/lib/farce/lazy.rb +100 -0
- data/lib/farce/lazy_ref.rb +40 -0
- data/lib/farce/lease.rb +28 -0
- data/lib/farce/lease_map.rb +34 -0
- data/lib/farce/lease_pool.rb +29 -0
- data/lib/farce/lfu_map.rb +53 -0
- data/lib/farce/local/atom.rb +23 -0
- data/lib/farce/local/counter.rb +88 -0
- data/lib/farce/local/flag.rb +65 -0
- data/lib/farce/local/lazy.rb +38 -0
- data/lib/farce/local/lazy_ref.rb +36 -0
- data/lib/farce/local/lease.rb +77 -0
- data/lib/farce/local/lease_map.rb +80 -0
- data/lib/farce/local/lease_pool.rb +38 -0
- data/lib/farce/local/lfu_map.rb +55 -0
- data/lib/farce/local/lru_map.rb +62 -0
- data/lib/farce/local/map.rb +47 -0
- data/lib/farce/local/molecule.rb +45 -0
- data/lib/farce/local/priority_queue.rb +53 -0
- data/lib/farce/local/queue.rb +32 -0
- data/lib/farce/local/scoped.rb +166 -0
- data/lib/farce/local/set.rb +18 -0
- data/lib/farce/local/sorted_set.rb +50 -0
- data/lib/farce/local/timer_queue.rb +41 -0
- data/lib/farce/local/tree_map.rb +47 -0
- data/lib/farce/local/vector.rb +30 -0
- data/lib/farce/local/weak_atom.rb +23 -0
- data/lib/farce/local/weak_key_map.rb +39 -0
- data/lib/farce/local/weak_map.rb +39 -0
- data/lib/farce/local/weak_set.rb +18 -0
- data/lib/farce/local/weak_value_map.rb +39 -0
- data/lib/farce/local.rb +35 -0
- data/lib/farce/lock.rb +54 -0
- data/lib/farce/lru_map.rb +63 -0
- data/lib/farce/map.rb +58 -0
- data/lib/farce/mode_manager.rb +142 -0
- data/lib/farce/molecule.rb +60 -0
- data/lib/farce/mutable.rb +171 -0
- data/lib/farce/pool.rb +332 -0
- data/lib/farce/port.rb +134 -0
- data/lib/farce/priority_queue.rb +70 -0
- data/lib/farce/proxy/register.rb +101 -0
- data/lib/farce/proxy/supervisor.rb +137 -0
- data/lib/farce/proxy/wrapper.rb +49 -0
- data/lib/farce/proxy.rb +291 -0
- data/lib/farce/queue.rb +60 -0
- data/lib/farce/ractor.rb +269 -0
- data/lib/farce/read_write_lock.rb +256 -0
- data/lib/farce/reference.rb +158 -0
- data/lib/farce/resolv/dns.rb +42 -0
- data/lib/farce/resolv.rb +85 -0
- data/lib/farce/scheduler.rb +536 -0
- data/lib/farce/set.rb +39 -0
- data/lib/farce/shareable.rb +150 -0
- data/lib/farce/signal.rb +97 -0
- data/lib/farce/sorted_set.rb +36 -0
- data/lib/farce/strict/atom.rb +34 -0
- data/lib/farce/strict/counter.rb +10 -0
- data/lib/farce/strict/exchanger.rb +25 -0
- data/lib/farce/strict/flag.rb +10 -0
- data/lib/farce/strict/lazy.rb +26 -0
- data/lib/farce/strict/lazy_ref.rb +26 -0
- data/lib/farce/strict/lease.rb +27 -0
- data/lib/farce/strict/lease_map.rb +33 -0
- data/lib/farce/strict/lease_pool.rb +28 -0
- data/lib/farce/strict/lfu_map.rb +27 -0
- data/lib/farce/strict/lru_map.rb +28 -0
- data/lib/farce/strict/map.rb +54 -0
- data/lib/farce/strict/molecule.rb +16 -0
- data/lib/farce/strict/port.rb +40 -0
- data/lib/farce/strict/priority_queue.rb +20 -0
- data/lib/farce/strict/queue.rb +17 -0
- data/lib/farce/strict/set.rb +14 -0
- data/lib/farce/strict/sorted_set.rb +19 -0
- data/lib/farce/strict/timer_queue.rb +13 -0
- data/lib/farce/strict/tree_map.rb +27 -0
- data/lib/farce/strict/vector.rb +24 -0
- data/lib/farce/strict/weak_atom.rb +31 -0
- data/lib/farce/strict/weak_key_map.rb +49 -0
- data/lib/farce/strict/weak_map.rb +49 -0
- data/lib/farce/strict/weak_set.rb +14 -0
- data/lib/farce/strict/weak_value_map.rb +48 -0
- data/lib/farce/strict.rb +28 -0
- data/lib/farce/system.rb +40 -0
- data/lib/farce/thread_scheduler.rb +70 -0
- data/lib/farce/timer_queue.rb +66 -0
- data/lib/farce/transaction/atom.rb +73 -0
- data/lib/farce/transaction/map.rb +25 -0
- data/lib/farce/transaction/map_operations.rb +143 -0
- data/lib/farce/transaction/molecule.rb +90 -0
- data/lib/farce/transaction/mutable.rb +61 -0
- data/lib/farce/transaction/set.rb +17 -0
- data/lib/farce/transaction/set_operations.rb +48 -0
- data/lib/farce/transaction/sorted_set.rb +18 -0
- data/lib/farce/transaction/tree_map.rb +78 -0
- data/lib/farce/transaction/vector.rb +139 -0
- data/lib/farce/transaction/wrapper.rb +202 -0
- data/lib/farce/transaction.rb +343 -0
- data/lib/farce/tree_map.rb +56 -0
- data/lib/farce/unsafe/lfu_map.rb +36 -0
- data/lib/farce/unsafe/lru_map.rb +36 -0
- data/lib/farce/unsafe/tree_map.rb +21 -0
- data/lib/farce/unsafe.rb +29 -0
- data/lib/farce/unshareable.rb +67 -0
- data/lib/farce/unshared/atom.rb +24 -0
- data/lib/farce/unshared/counter.rb +10 -0
- data/lib/farce/unshared/flag.rb +10 -0
- data/lib/farce/unshared/lazy.rb +38 -0
- data/lib/farce/unshared/lazy_ref.rb +26 -0
- data/lib/farce/unshared/lease.rb +27 -0
- data/lib/farce/unshared/lease_map.rb +26 -0
- data/lib/farce/unshared/lease_pool.rb +25 -0
- data/lib/farce/unshared/lfu_map.rb +20 -0
- data/lib/farce/unshared/lru_map.rb +20 -0
- data/lib/farce/unshared/map.rb +49 -0
- data/lib/farce/unshared/molecule.rb +16 -0
- data/lib/farce/unshared/priority_queue.rb +16 -0
- data/lib/farce/unshared/queue.rb +27 -0
- data/lib/farce/unshared/set.rb +14 -0
- data/lib/farce/unshared/sorted_set.rb +28 -0
- data/lib/farce/unshared/timer_queue.rb +16 -0
- data/lib/farce/unshared/tree_map.rb +21 -0
- data/lib/farce/unshared/vector.rb +19 -0
- data/lib/farce/unshared/weak_atom.rb +31 -0
- data/lib/farce/unshared/weak_key_map.rb +44 -0
- data/lib/farce/unshared/weak_map.rb +44 -0
- data/lib/farce/unshared/weak_set.rb +14 -0
- data/lib/farce/unshared/weak_value_map.rb +43 -0
- data/lib/farce/unshared.rb +28 -0
- data/lib/farce/vector.rb +228 -0
- data/lib/farce/version.rb +8 -0
- data/lib/farce/walker/definitions.rb +186 -0
- data/lib/farce/walker/modification.rb +355 -0
- data/lib/farce/walker.rb +319 -0
- data/lib/farce/weak_atom.rb +121 -0
- data/lib/farce/weak_key_map.rb +31 -0
- data/lib/farce/weak_map.rb +33 -0
- data/lib/farce/weak_ref.rb +67 -0
- data/lib/farce/weak_set.rb +82 -0
- data/lib/farce/weak_value.rb +195 -0
- data/lib/farce/weak_value_map.rb +32 -0
- data/lib/farce.rb +519 -0
- metadata +435 -8
data/ext/farce/vector.c
ADDED
|
@@ -0,0 +1,1195 @@
|
|
|
1
|
+
#include "containers.h"
|
|
2
|
+
#include "transaction.h"
|
|
3
|
+
#include "ruby/io.h"
|
|
4
|
+
#include "ruby/fiber/scheduler.h"
|
|
5
|
+
|
|
6
|
+
#include <errno.h>
|
|
7
|
+
#include <fcntl.h>
|
|
8
|
+
#include <math.h>
|
|
9
|
+
#include <stdint.h>
|
|
10
|
+
#include <stdlib.h>
|
|
11
|
+
#include <time.h>
|
|
12
|
+
#include <unistd.h>
|
|
13
|
+
|
|
14
|
+
#define VECTOR_INITIAL_CAPACITY 16
|
|
15
|
+
static VALUE cVector;
|
|
16
|
+
static ID vector_values_id, vector_updating_id, vector_mutex_id;
|
|
17
|
+
static VALUE vector_transaction_marker;
|
|
18
|
+
|
|
19
|
+
typedef struct vector_waiter vector_waiter_t;
|
|
20
|
+
|
|
21
|
+
struct vector_waiter {
|
|
22
|
+
int read_fd;
|
|
23
|
+
int write_fd;
|
|
24
|
+
bool notified;
|
|
25
|
+
vector_waiter_t *next;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
typedef struct {
|
|
29
|
+
pthread_mutex_t lock;
|
|
30
|
+
VALUE *values;
|
|
31
|
+
size_t capacity;
|
|
32
|
+
size_t size;
|
|
33
|
+
vector_waiter_t *waiters;
|
|
34
|
+
uint64_t generation;
|
|
35
|
+
bool compare_by_identity;
|
|
36
|
+
uint8_t updating;
|
|
37
|
+
bool initialized;
|
|
38
|
+
VALUE updating_fiber;
|
|
39
|
+
VALUE updating_thread;
|
|
40
|
+
} vector_t;
|
|
41
|
+
|
|
42
|
+
typedef struct {
|
|
43
|
+
bool finite;
|
|
44
|
+
double deadline;
|
|
45
|
+
} vector_timeout_t;
|
|
46
|
+
|
|
47
|
+
static void
|
|
48
|
+
vector_mark(void *pointer)
|
|
49
|
+
{
|
|
50
|
+
vector_t *vector = pointer;
|
|
51
|
+
rb_gc_mark_movable(vector->updating_fiber);
|
|
52
|
+
rb_gc_mark_movable(vector->updating_thread);
|
|
53
|
+
if (!vector->values) return;
|
|
54
|
+
for (size_t index = 0; index < vector->size; index++) {
|
|
55
|
+
rb_gc_mark_movable(vector->values[index]);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
static void
|
|
60
|
+
vector_compact(void *pointer)
|
|
61
|
+
{
|
|
62
|
+
vector_t *vector = pointer;
|
|
63
|
+
vector->updating_fiber = rb_gc_location(vector->updating_fiber);
|
|
64
|
+
vector->updating_thread = rb_gc_location(vector->updating_thread);
|
|
65
|
+
if (!vector->values) return;
|
|
66
|
+
for (size_t index = 0; index < vector->size; index++) {
|
|
67
|
+
vector->values[index] = rb_gc_location(vector->values[index]);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
static void
|
|
72
|
+
vector_free(void *pointer)
|
|
73
|
+
{
|
|
74
|
+
vector_t *vector = pointer;
|
|
75
|
+
pthread_mutex_destroy(&vector->lock);
|
|
76
|
+
free(vector->values);
|
|
77
|
+
ruby_xfree(vector);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
static size_t
|
|
81
|
+
vector_memsize(const void *pointer)
|
|
82
|
+
{
|
|
83
|
+
const vector_t *vector = pointer;
|
|
84
|
+
return vector ? sizeof(vector_t) + vector->capacity * sizeof(VALUE) : 0;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
static const rb_data_type_t vector_type = {
|
|
88
|
+
.wrap_struct_name = "Ractor::Containers::Vector",
|
|
89
|
+
.function = {
|
|
90
|
+
.dmark = vector_mark,
|
|
91
|
+
.dfree = vector_free,
|
|
92
|
+
.dsize = vector_memsize,
|
|
93
|
+
.dcompact = vector_compact,
|
|
94
|
+
},
|
|
95
|
+
.flags = RUBY_TYPED_FROZEN_SHAREABLE,
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
static void
|
|
99
|
+
vector_validate_references(VALUE self)
|
|
100
|
+
{
|
|
101
|
+
vector_t *vector;
|
|
102
|
+
TypedData_Get_Struct(self, vector_t, &vector_type, vector);
|
|
103
|
+
for (size_t index = 0; index < vector->size; index++) {
|
|
104
|
+
containers_check_shareable(vector->values[index]);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
static VALUE
|
|
109
|
+
vector_allocate(VALUE klass)
|
|
110
|
+
{
|
|
111
|
+
vector_t *vector;
|
|
112
|
+
VALUE object = TypedData_Make_Struct(klass, vector_t, &vector_type, vector);
|
|
113
|
+
pthread_mutex_init(&vector->lock, NULL);
|
|
114
|
+
vector->values = NULL;
|
|
115
|
+
vector->capacity = 0;
|
|
116
|
+
vector->size = 0;
|
|
117
|
+
vector->waiters = NULL;
|
|
118
|
+
vector->generation = 0;
|
|
119
|
+
vector->compare_by_identity = false;
|
|
120
|
+
vector->updating = false;
|
|
121
|
+
vector->initialized = false;
|
|
122
|
+
vector->updating_fiber = Qnil;
|
|
123
|
+
vector->updating_thread = Qnil;
|
|
124
|
+
return object;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
static vector_t *
|
|
128
|
+
get_vector(VALUE self)
|
|
129
|
+
{
|
|
130
|
+
vector_t *vector;
|
|
131
|
+
TypedData_Get_Struct(self, vector_t, &vector_type, vector);
|
|
132
|
+
if (!vector->initialized) rb_raise(rb_eRuntimeError, "uninitialized Vector");
|
|
133
|
+
return vector;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
static VALUE *
|
|
137
|
+
vector_allocate_values(size_t capacity)
|
|
138
|
+
{
|
|
139
|
+
if (capacity > SIZE_MAX / sizeof(VALUE)) return NULL;
|
|
140
|
+
VALUE *values = malloc(capacity * sizeof(VALUE));
|
|
141
|
+
if (!values) return NULL;
|
|
142
|
+
for (size_t index = 0; index < capacity; index++) values[index] = Qnil;
|
|
143
|
+
return values;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
static bool
|
|
147
|
+
vector_ensure_capacity(vector_t *vector, size_t required)
|
|
148
|
+
{
|
|
149
|
+
if (required <= vector->capacity) return true;
|
|
150
|
+
|
|
151
|
+
size_t capacity = vector->capacity ? vector->capacity : VECTOR_INITIAL_CAPACITY;
|
|
152
|
+
while (capacity < required) {
|
|
153
|
+
if (capacity > SIZE_MAX / 2) {
|
|
154
|
+
capacity = required;
|
|
155
|
+
break;
|
|
156
|
+
}
|
|
157
|
+
capacity *= 2;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
VALUE *values = vector_allocate_values(capacity);
|
|
161
|
+
if (!values) return false;
|
|
162
|
+
for (size_t index = 0; index < vector->size; index++) values[index] = vector->values[index];
|
|
163
|
+
free(vector->values);
|
|
164
|
+
vector->values = values;
|
|
165
|
+
vector->capacity = capacity;
|
|
166
|
+
return true;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
static double
|
|
170
|
+
vector_monotonic_now(void)
|
|
171
|
+
{
|
|
172
|
+
struct timespec now;
|
|
173
|
+
#ifdef CLOCK_MONOTONIC
|
|
174
|
+
if (clock_gettime(CLOCK_MONOTONIC, &now) == 0) {
|
|
175
|
+
return (double)now.tv_sec + (double)now.tv_nsec / 1000000000.0;
|
|
176
|
+
}
|
|
177
|
+
#endif
|
|
178
|
+
struct timeval fallback;
|
|
179
|
+
gettimeofday(&fallback, NULL);
|
|
180
|
+
return (double)fallback.tv_sec + (double)fallback.tv_usec / 1000000.0;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
static vector_timeout_t
|
|
184
|
+
vector_parse_timeout(VALUE value)
|
|
185
|
+
{
|
|
186
|
+
vector_timeout_t timeout = {.finite = false, .deadline = 0};
|
|
187
|
+
if (NIL_P(value)) return timeout;
|
|
188
|
+
|
|
189
|
+
double seconds = NUM2DBL(value);
|
|
190
|
+
if (!isfinite(seconds) || seconds < 0) {
|
|
191
|
+
rb_raise(rb_eArgError, "timeout must be a finite, non-negative number or nil");
|
|
192
|
+
}
|
|
193
|
+
timeout.finite = true;
|
|
194
|
+
timeout.deadline = vector_monotonic_now() + seconds;
|
|
195
|
+
return timeout;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
static VALUE
|
|
199
|
+
vector_timeout_keyword(VALUE keywords)
|
|
200
|
+
{
|
|
201
|
+
VALUE timeout = Qnil;
|
|
202
|
+
if (!NIL_P(keywords)) {
|
|
203
|
+
ID keyword_ids[] = {rb_intern("timeout")};
|
|
204
|
+
VALUE keyword_values[1];
|
|
205
|
+
rb_get_kwargs(keywords, keyword_ids, 0, 1, keyword_values);
|
|
206
|
+
if (keyword_values[0] != Qundef) timeout = keyword_values[0];
|
|
207
|
+
}
|
|
208
|
+
return timeout;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
static void
|
|
212
|
+
vector_set_fd_flags(int fd)
|
|
213
|
+
{
|
|
214
|
+
#ifdef _WIN32
|
|
215
|
+
(void)fd;
|
|
216
|
+
#else
|
|
217
|
+
int flags = fcntl(fd, F_GETFL, 0);
|
|
218
|
+
if (flags >= 0) (void)fcntl(fd, F_SETFL, flags | O_NONBLOCK);
|
|
219
|
+
flags = fcntl(fd, F_GETFD, 0);
|
|
220
|
+
if (flags >= 0) (void)fcntl(fd, F_SETFD, flags | FD_CLOEXEC);
|
|
221
|
+
#endif
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/* Called with vector->lock held. Each waiter has an independent pipe, so a
|
|
225
|
+
* transition wakes every waiting thread or scheduled fiber. */
|
|
226
|
+
static void
|
|
227
|
+
vector_notify_waiters_locked(vector_t *vector)
|
|
228
|
+
{
|
|
229
|
+
unsigned char byte = 1;
|
|
230
|
+
vector->generation++;
|
|
231
|
+
for (vector_waiter_t *waiter = vector->waiters; waiter; waiter = waiter->next) {
|
|
232
|
+
if (waiter->notified) continue;
|
|
233
|
+
ssize_t result;
|
|
234
|
+
do {
|
|
235
|
+
result = write(waiter->write_fd, &byte, 1);
|
|
236
|
+
} while (result < 0 && errno == EINTR);
|
|
237
|
+
if (result == 1 || (result < 0 && (errno == EAGAIN || errno == EWOULDBLOCK))) {
|
|
238
|
+
waiter->notified = true;
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
static void
|
|
244
|
+
vector_finish_update_locked(vector_t *vector)
|
|
245
|
+
{
|
|
246
|
+
vector->updating = false;
|
|
247
|
+
vector_notify_waiters_locked(vector);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
typedef struct {
|
|
251
|
+
vector_t *vector;
|
|
252
|
+
vector_waiter_t waiter;
|
|
253
|
+
vector_timeout_t *timeout;
|
|
254
|
+
} vector_wait_context_t;
|
|
255
|
+
|
|
256
|
+
static bool
|
|
257
|
+
vector_wait_for_descriptor(int fd, vector_timeout_t *timeout)
|
|
258
|
+
{
|
|
259
|
+
VALUE wait_timeout = Qnil;
|
|
260
|
+
if (timeout->finite) {
|
|
261
|
+
double remaining = timeout->deadline - vector_monotonic_now();
|
|
262
|
+
if (remaining <= 0) return false;
|
|
263
|
+
wait_timeout = DBL2NUM(remaining);
|
|
264
|
+
}
|
|
265
|
+
return containers_wait_for_readable(fd, wait_timeout);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
static VALUE
|
|
269
|
+
vector_wait_body(VALUE opaque)
|
|
270
|
+
{
|
|
271
|
+
vector_wait_context_t *context = (vector_wait_context_t *)opaque;
|
|
272
|
+
return vector_wait_for_descriptor(context->waiter.read_fd, context->timeout) ? Qtrue : Qfalse;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
static VALUE
|
|
276
|
+
vector_wait_cleanup(VALUE opaque)
|
|
277
|
+
{
|
|
278
|
+
vector_wait_context_t *context = (vector_wait_context_t *)opaque;
|
|
279
|
+
vector_waiter_t **link;
|
|
280
|
+
|
|
281
|
+
pthread_mutex_lock(&context->vector->lock);
|
|
282
|
+
for (link = &context->vector->waiters; *link; link = &(*link)->next) {
|
|
283
|
+
if (*link == &context->waiter) {
|
|
284
|
+
*link = context->waiter.next;
|
|
285
|
+
break;
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
pthread_mutex_unlock(&context->vector->lock);
|
|
289
|
+
close(context->waiter.read_fd);
|
|
290
|
+
close(context->waiter.write_fd);
|
|
291
|
+
return Qnil;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
/* Called with vector->lock held and always returns with it released. The
|
|
295
|
+
* waiter is registered before unlocking, which closes the check-to-wait race
|
|
296
|
+
* without requiring a permanent descriptor on every Vector. */
|
|
297
|
+
static bool
|
|
298
|
+
vector_wait_once(vector_t *vector, vector_timeout_t *timeout)
|
|
299
|
+
{
|
|
300
|
+
int descriptors[2];
|
|
301
|
+
if (pipe(descriptors) != 0) {
|
|
302
|
+
pthread_mutex_unlock(&vector->lock);
|
|
303
|
+
rb_sys_fail("pipe");
|
|
304
|
+
}
|
|
305
|
+
vector_set_fd_flags(descriptors[0]);
|
|
306
|
+
vector_set_fd_flags(descriptors[1]);
|
|
307
|
+
|
|
308
|
+
vector_wait_context_t context = {
|
|
309
|
+
.vector = vector,
|
|
310
|
+
.waiter = {
|
|
311
|
+
.read_fd = descriptors[0],
|
|
312
|
+
.write_fd = descriptors[1],
|
|
313
|
+
.notified = false,
|
|
314
|
+
.next = vector->waiters,
|
|
315
|
+
},
|
|
316
|
+
.timeout = timeout,
|
|
317
|
+
};
|
|
318
|
+
vector->waiters = &context.waiter;
|
|
319
|
+
pthread_mutex_unlock(&vector->lock);
|
|
320
|
+
return RTEST(rb_ensure(vector_wait_body, (VALUE)&context, vector_wait_cleanup, (VALUE)&context));
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
static void
|
|
324
|
+
vector_check_reservation_wait_locked(vector_t *vector)
|
|
325
|
+
{
|
|
326
|
+
if (vector->updating != FARCE_TRANSACTION_RESERVED) return;
|
|
327
|
+
pthread_mutex_unlock(&vector->lock);
|
|
328
|
+
VALUE fiber = rb_fiber_current();
|
|
329
|
+
VALUE thread = rb_thread_current();
|
|
330
|
+
VALUE scheduler = rb_fiber_scheduler_current();
|
|
331
|
+
pthread_mutex_lock(&vector->lock);
|
|
332
|
+
if (vector->updating != FARCE_TRANSACTION_RESERVED) return;
|
|
333
|
+
if (vector->updating_fiber == fiber ||
|
|
334
|
+
(vector->updating_thread == thread && NIL_P(scheduler))) {
|
|
335
|
+
pthread_mutex_unlock(&vector->lock);
|
|
336
|
+
rb_raise(rb_eThreadError, "deadlock; recursive or unscheduled vector transaction access");
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
/* Called with the short mutex held. Return with it held on success. */
|
|
341
|
+
static bool
|
|
342
|
+
vector_wait_for_reservation_locked(vector_t *vector, vector_timeout_t *timeout)
|
|
343
|
+
{
|
|
344
|
+
while (vector->updating == FARCE_TRANSACTION_RESERVED) {
|
|
345
|
+
vector_check_reservation_wait_locked(vector);
|
|
346
|
+
if (vector->updating != FARCE_TRANSACTION_RESERVED) break;
|
|
347
|
+
if (!vector_wait_once(vector, timeout) && timeout->finite) return false;
|
|
348
|
+
pthread_mutex_lock(&vector->lock);
|
|
349
|
+
}
|
|
350
|
+
return true;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
static void
|
|
354
|
+
vector_read_lock(vector_t *vector)
|
|
355
|
+
{
|
|
356
|
+
vector_timeout_t timeout = {.finite = false, .deadline = 0};
|
|
357
|
+
pthread_mutex_lock(&vector->lock);
|
|
358
|
+
(void)vector_wait_for_reservation_locked(vector, &timeout);
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
static VALUE
|
|
362
|
+
vector_freeze(VALUE self)
|
|
363
|
+
{
|
|
364
|
+
vector_t *vector;
|
|
365
|
+
TypedData_Get_Struct(self, vector_t, &vector_type, vector);
|
|
366
|
+
vector_read_lock(vector);
|
|
367
|
+
rb_obj_freeze(self);
|
|
368
|
+
pthread_mutex_unlock(&vector->lock);
|
|
369
|
+
return self;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
/* Returns with vector->lock held on success and released on timeout. */
|
|
373
|
+
static bool
|
|
374
|
+
vector_lock_for_update(vector_t *vector, vector_timeout_t *timeout)
|
|
375
|
+
{
|
|
376
|
+
for (;;) {
|
|
377
|
+
pthread_mutex_lock(&vector->lock);
|
|
378
|
+
if (!vector->updating) return true;
|
|
379
|
+
vector_check_reservation_wait_locked(vector);
|
|
380
|
+
if (!vector->updating) return true;
|
|
381
|
+
if (!vector_wait_once(vector, timeout)) return false;
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
static long long
|
|
386
|
+
vector_convert_index(VALUE value)
|
|
387
|
+
{
|
|
388
|
+
return NUM2LL(value);
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
static bool
|
|
392
|
+
vector_lookup_index(vector_t *vector, long long raw, size_t *index)
|
|
393
|
+
{
|
|
394
|
+
if (raw < 0) {
|
|
395
|
+
unsigned long long distance = (unsigned long long)(-(raw + 1)) + 1;
|
|
396
|
+
if (distance > vector->size) return false;
|
|
397
|
+
*index = vector->size - (size_t)distance;
|
|
398
|
+
return true;
|
|
399
|
+
}
|
|
400
|
+
if ((unsigned long long)raw >= vector->size) return false;
|
|
401
|
+
*index = (size_t)raw;
|
|
402
|
+
return true;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
/* Called with the lock held. Raises only after releasing it. */
|
|
406
|
+
static size_t
|
|
407
|
+
vector_assignment_index(vector_t *vector, long long raw)
|
|
408
|
+
{
|
|
409
|
+
if (raw < 0) {
|
|
410
|
+
unsigned long long distance = (unsigned long long)(-(raw + 1)) + 1;
|
|
411
|
+
if (distance > vector->size) {
|
|
412
|
+
pthread_mutex_unlock(&vector->lock);
|
|
413
|
+
rb_raise(rb_eIndexError, "index %lld too small for vector; minimum: -%zu", raw, vector->size);
|
|
414
|
+
}
|
|
415
|
+
return vector->size - (size_t)distance;
|
|
416
|
+
}
|
|
417
|
+
if ((unsigned long long)raw >= SIZE_MAX) {
|
|
418
|
+
pthread_mutex_unlock(&vector->lock);
|
|
419
|
+
rb_raise(rb_eIndexError, "index %lld too large", raw);
|
|
420
|
+
}
|
|
421
|
+
return (size_t)raw;
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
static VALUE
|
|
425
|
+
vector_initialize(int argc, VALUE *argv, VALUE self)
|
|
426
|
+
{
|
|
427
|
+
VALUE source = Qnil;
|
|
428
|
+
VALUE keywords = Qnil;
|
|
429
|
+
VALUE identity = Qundef;
|
|
430
|
+
ID keyword_ids[] = {rb_intern("compare_by_identity")};
|
|
431
|
+
VALUE keyword_values[1];
|
|
432
|
+
vector_t *vector;
|
|
433
|
+
|
|
434
|
+
rb_check_frozen(self);
|
|
435
|
+
rb_scan_args(argc, argv, "01:", &source, &keywords);
|
|
436
|
+
if (!NIL_P(keywords)) {
|
|
437
|
+
rb_get_kwargs(keywords, keyword_ids, 0, 1, keyword_values);
|
|
438
|
+
identity = keyword_values[0];
|
|
439
|
+
}
|
|
440
|
+
TypedData_Get_Struct(self, vector_t, &vector_type, vector);
|
|
441
|
+
if (vector->initialized) rb_raise(rb_eRuntimeError, "Vector is already initialized");
|
|
442
|
+
|
|
443
|
+
vector->compare_by_identity = identity == Qundef
|
|
444
|
+
? false
|
|
445
|
+
: containers_strict_bool(identity, "compare_by_identity");
|
|
446
|
+
|
|
447
|
+
size_t size = 0;
|
|
448
|
+
if (!NIL_P(source)) {
|
|
449
|
+
Check_Type(source, T_ARRAY);
|
|
450
|
+
size = (size_t)RARRAY_LEN(source);
|
|
451
|
+
}
|
|
452
|
+
size_t capacity = size > VECTOR_INITIAL_CAPACITY ? size : VECTOR_INITIAL_CAPACITY;
|
|
453
|
+
vector->values = vector_allocate_values(capacity);
|
|
454
|
+
if (!vector->values) rb_memerror();
|
|
455
|
+
vector->capacity = capacity;
|
|
456
|
+
|
|
457
|
+
for (size_t index = 0; index < size; index++) {
|
|
458
|
+
VALUE value = RARRAY_AREF(source, (long)index);
|
|
459
|
+
containers_check_shareable(value);
|
|
460
|
+
vector->values[index] = value;
|
|
461
|
+
}
|
|
462
|
+
vector->size = size;
|
|
463
|
+
vector->initialized = true;
|
|
464
|
+
return containers_publish_native_with_references(self, vector_validate_references);
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
static VALUE
|
|
468
|
+
vector_snapshot_locked(VALUE self)
|
|
469
|
+
{
|
|
470
|
+
vector_t *vector = get_vector(self);
|
|
471
|
+
return rb_ary_new_from_values((long)vector->size, vector->values);
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
static VALUE
|
|
475
|
+
vector_snapshot_unlock(VALUE self)
|
|
476
|
+
{
|
|
477
|
+
pthread_mutex_unlock(&get_vector(self)->lock);
|
|
478
|
+
return Qnil;
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
static VALUE
|
|
482
|
+
vector_snapshot(VALUE self)
|
|
483
|
+
{
|
|
484
|
+
vector_read_lock(get_vector(self));
|
|
485
|
+
return rb_ensure(vector_snapshot_locked, self, vector_snapshot_unlock, self);
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
static VALUE
|
|
489
|
+
vector_size(VALUE self)
|
|
490
|
+
{
|
|
491
|
+
vector_t *vector = get_vector(self);
|
|
492
|
+
size_t size;
|
|
493
|
+
vector_read_lock(vector);
|
|
494
|
+
size = vector->size;
|
|
495
|
+
pthread_mutex_unlock(&vector->lock);
|
|
496
|
+
return SIZET2NUM(size);
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
/* Never retain a backing-buffer pointer or hold the lock across a Ruby yield. */
|
|
500
|
+
static VALUE
|
|
501
|
+
vector_each(VALUE self)
|
|
502
|
+
{
|
|
503
|
+
RETURN_ENUMERATOR(self, 0, NULL);
|
|
504
|
+
vector_t *vector = get_vector(self);
|
|
505
|
+
vector_read_lock(vector);
|
|
506
|
+
size_t limit = vector->size;
|
|
507
|
+
pthread_mutex_unlock(&vector->lock);
|
|
508
|
+
|
|
509
|
+
for (size_t index = 0; index < limit; index++) {
|
|
510
|
+
vector_read_lock(vector);
|
|
511
|
+
if (index >= vector->size) {
|
|
512
|
+
pthread_mutex_unlock(&vector->lock);
|
|
513
|
+
break;
|
|
514
|
+
}
|
|
515
|
+
VALUE value = vector->values[index];
|
|
516
|
+
pthread_mutex_unlock(&vector->lock);
|
|
517
|
+
rb_yield(value);
|
|
518
|
+
}
|
|
519
|
+
return self;
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
static VALUE
|
|
523
|
+
vector_reverse_each(VALUE self)
|
|
524
|
+
{
|
|
525
|
+
RETURN_ENUMERATOR(self, 0, NULL);
|
|
526
|
+
vector_t *vector = get_vector(self);
|
|
527
|
+
vector_read_lock(vector);
|
|
528
|
+
size_t index = vector->size;
|
|
529
|
+
pthread_mutex_unlock(&vector->lock);
|
|
530
|
+
|
|
531
|
+
while (index > 0) {
|
|
532
|
+
vector_read_lock(vector);
|
|
533
|
+
if (index > vector->size) index = vector->size;
|
|
534
|
+
if (index == 0) {
|
|
535
|
+
pthread_mutex_unlock(&vector->lock);
|
|
536
|
+
break;
|
|
537
|
+
}
|
|
538
|
+
VALUE value = vector->values[--index];
|
|
539
|
+
pthread_mutex_unlock(&vector->lock);
|
|
540
|
+
rb_yield(value);
|
|
541
|
+
}
|
|
542
|
+
return self;
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
static VALUE
|
|
546
|
+
vector_fetch(VALUE self, VALUE index_value)
|
|
547
|
+
{
|
|
548
|
+
vector_t *vector = get_vector(self);
|
|
549
|
+
long long raw = vector_convert_index(index_value);
|
|
550
|
+
size_t index;
|
|
551
|
+
vector_read_lock(vector);
|
|
552
|
+
bool found = vector_lookup_index(vector, raw, &index);
|
|
553
|
+
VALUE result = found ? vector->values[index] : Qnil;
|
|
554
|
+
pthread_mutex_unlock(&vector->lock);
|
|
555
|
+
if (found) return result;
|
|
556
|
+
if (rb_block_given_p()) return rb_yield(index_value);
|
|
557
|
+
rb_raise(rb_eIndexError, "index %lld outside of vector bounds", raw);
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
static VALUE
|
|
561
|
+
vector_compare_by_identity_p(VALUE self)
|
|
562
|
+
{
|
|
563
|
+
return get_vector(self)->compare_by_identity ? Qtrue : Qfalse;
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
static VALUE
|
|
567
|
+
vector_get_fast(VALUE self, VALUE index_value)
|
|
568
|
+
{
|
|
569
|
+
vector_t *vector = get_vector(self);
|
|
570
|
+
long long raw = vector_convert_index(index_value);
|
|
571
|
+
size_t index;
|
|
572
|
+
VALUE result = Qnil;
|
|
573
|
+
vector_read_lock(vector);
|
|
574
|
+
if (vector_lookup_index(vector, raw, &index)) result = vector->values[index];
|
|
575
|
+
pthread_mutex_unlock(&vector->lock);
|
|
576
|
+
return result;
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
static VALUE
|
|
580
|
+
vector_get(int argc, VALUE *argv, VALUE self)
|
|
581
|
+
{
|
|
582
|
+
VALUE index_value;
|
|
583
|
+
VALUE keywords = Qnil;
|
|
584
|
+
rb_scan_args(argc, argv, "1:", &index_value, &keywords);
|
|
585
|
+
long long raw = vector_convert_index(index_value);
|
|
586
|
+
vector_timeout_t timeout = vector_parse_timeout(vector_timeout_keyword(keywords));
|
|
587
|
+
vector_t *vector = get_vector(self);
|
|
588
|
+
|
|
589
|
+
if (!vector_lock_for_update(vector, &timeout)) return Qnil;
|
|
590
|
+
size_t index;
|
|
591
|
+
VALUE result = vector_lookup_index(vector, raw, &index) ? vector->values[index] : Qnil;
|
|
592
|
+
pthread_mutex_unlock(&vector->lock);
|
|
593
|
+
return result;
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
static VALUE
|
|
597
|
+
vector_store_internal(VALUE self, vector_t *vector, long long raw, VALUE value, vector_timeout_t *timeout)
|
|
598
|
+
{
|
|
599
|
+
rb_check_frozen(self);
|
|
600
|
+
if (!vector_lock_for_update(vector, timeout)) return Qfalse;
|
|
601
|
+
size_t index = vector_assignment_index(vector, raw);
|
|
602
|
+
if (!vector_ensure_capacity(vector, index + 1)) {
|
|
603
|
+
pthread_mutex_unlock(&vector->lock);
|
|
604
|
+
rb_memerror();
|
|
605
|
+
}
|
|
606
|
+
if (index >= vector->size) vector->size = index + 1;
|
|
607
|
+
vector->values[index] = value;
|
|
608
|
+
vector_notify_waiters_locked(vector);
|
|
609
|
+
pthread_mutex_unlock(&vector->lock);
|
|
610
|
+
return value;
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
static VALUE
|
|
614
|
+
vector_set_fast(VALUE self, VALUE index_value, VALUE value)
|
|
615
|
+
{
|
|
616
|
+
vector_t *vector = get_vector(self);
|
|
617
|
+
rb_check_frozen(self);
|
|
618
|
+
long long raw = vector_convert_index(index_value);
|
|
619
|
+
rb_check_frozen(self);
|
|
620
|
+
vector_timeout_t timeout = {.finite = false, .deadline = 0};
|
|
621
|
+
containers_check_shareable(value);
|
|
622
|
+
return vector_store_internal(self, vector, raw, value, &timeout);
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
static VALUE
|
|
626
|
+
vector_store(int argc, VALUE *argv, VALUE self)
|
|
627
|
+
{
|
|
628
|
+
VALUE index_value;
|
|
629
|
+
VALUE value;
|
|
630
|
+
VALUE keywords = Qnil;
|
|
631
|
+
rb_scan_args(argc, argv, "2:", &index_value, &value, &keywords);
|
|
632
|
+
rb_check_frozen(self);
|
|
633
|
+
long long raw = vector_convert_index(index_value);
|
|
634
|
+
vector_timeout_t timeout = vector_parse_timeout(vector_timeout_keyword(keywords));
|
|
635
|
+
rb_check_frozen(self);
|
|
636
|
+
vector_t *vector = get_vector(self);
|
|
637
|
+
containers_check_shareable(value);
|
|
638
|
+
return vector_store_internal(self, vector, raw, value, &timeout);
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
static VALUE
|
|
642
|
+
vector_clear(VALUE self)
|
|
643
|
+
{
|
|
644
|
+
vector_t *vector = get_vector(self);
|
|
645
|
+
rb_check_frozen(self);
|
|
646
|
+
vector_timeout_t timeout = {.finite = false, .deadline = 0};
|
|
647
|
+
(void)vector_lock_for_update(vector, &timeout);
|
|
648
|
+
for (size_t index = 0; index < vector->size; index++) vector->values[index] = Qnil;
|
|
649
|
+
vector->size = 0;
|
|
650
|
+
vector_notify_waiters_locked(vector);
|
|
651
|
+
pthread_mutex_unlock(&vector->lock);
|
|
652
|
+
return self;
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
static VALUE
|
|
656
|
+
vector_push(int argc, VALUE *argv, VALUE self)
|
|
657
|
+
{
|
|
658
|
+
VALUE value;
|
|
659
|
+
VALUE keywords = Qnil;
|
|
660
|
+
rb_scan_args(argc, argv, "1:", &value, &keywords);
|
|
661
|
+
rb_check_frozen(self);
|
|
662
|
+
vector_timeout_t timeout = vector_parse_timeout(vector_timeout_keyword(keywords));
|
|
663
|
+
rb_check_frozen(self);
|
|
664
|
+
vector_t *vector = get_vector(self);
|
|
665
|
+
containers_check_shareable(value);
|
|
666
|
+
|
|
667
|
+
if (!vector_lock_for_update(vector, &timeout)) return Qfalse;
|
|
668
|
+
if (vector->size == SIZE_MAX || !vector_ensure_capacity(vector, vector->size + 1)) {
|
|
669
|
+
pthread_mutex_unlock(&vector->lock);
|
|
670
|
+
rb_memerror();
|
|
671
|
+
}
|
|
672
|
+
vector->values[vector->size++] = value;
|
|
673
|
+
vector_notify_waiters_locked(vector);
|
|
674
|
+
pthread_mutex_unlock(&vector->lock);
|
|
675
|
+
return self;
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
static VALUE
|
|
679
|
+
vector_pop(int argc, VALUE *argv, VALUE self)
|
|
680
|
+
{
|
|
681
|
+
VALUE keywords = Qnil;
|
|
682
|
+
rb_scan_args(argc, argv, "0:", &keywords);
|
|
683
|
+
rb_check_frozen(self);
|
|
684
|
+
vector_timeout_t timeout = vector_parse_timeout(vector_timeout_keyword(keywords));
|
|
685
|
+
rb_check_frozen(self);
|
|
686
|
+
vector_t *vector = get_vector(self);
|
|
687
|
+
|
|
688
|
+
if (!vector_lock_for_update(vector, &timeout)) return Qnil;
|
|
689
|
+
VALUE result = Qnil;
|
|
690
|
+
if (vector->size > 0) {
|
|
691
|
+
size_t index = --vector->size;
|
|
692
|
+
result = vector->values[index];
|
|
693
|
+
vector->values[index] = Qnil;
|
|
694
|
+
vector_notify_waiters_locked(vector);
|
|
695
|
+
}
|
|
696
|
+
pthread_mutex_unlock(&vector->lock);
|
|
697
|
+
return result;
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
static VALUE
|
|
701
|
+
vector_swap(int argc, VALUE *argv, VALUE self)
|
|
702
|
+
{
|
|
703
|
+
VALUE index_value;
|
|
704
|
+
VALUE replacement;
|
|
705
|
+
VALUE keywords = Qnil;
|
|
706
|
+
rb_scan_args(argc, argv, "2:", &index_value, &replacement, &keywords);
|
|
707
|
+
rb_check_frozen(self);
|
|
708
|
+
long long raw = vector_convert_index(index_value);
|
|
709
|
+
vector_timeout_t timeout = vector_parse_timeout(vector_timeout_keyword(keywords));
|
|
710
|
+
rb_check_frozen(self);
|
|
711
|
+
vector_t *vector = get_vector(self);
|
|
712
|
+
containers_check_shareable(replacement);
|
|
713
|
+
|
|
714
|
+
if (!vector_lock_for_update(vector, &timeout)) return Qnil;
|
|
715
|
+
size_t index = vector_assignment_index(vector, raw);
|
|
716
|
+
if (!vector_ensure_capacity(vector, index + 1)) {
|
|
717
|
+
pthread_mutex_unlock(&vector->lock);
|
|
718
|
+
rb_memerror();
|
|
719
|
+
}
|
|
720
|
+
VALUE previous = index < vector->size ? vector->values[index] : Qnil;
|
|
721
|
+
if (index >= vector->size) vector->size = index + 1;
|
|
722
|
+
vector->values[index] = replacement;
|
|
723
|
+
vector_notify_waiters_locked(vector);
|
|
724
|
+
pthread_mutex_unlock(&vector->lock);
|
|
725
|
+
return previous;
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
typedef struct {
|
|
729
|
+
VALUE self;
|
|
730
|
+
vector_t *vector;
|
|
731
|
+
size_t index;
|
|
732
|
+
VALUE current;
|
|
733
|
+
VALUE argument;
|
|
734
|
+
VALUE replacement;
|
|
735
|
+
bool identity;
|
|
736
|
+
bool complete;
|
|
737
|
+
} vector_operation_t;
|
|
738
|
+
|
|
739
|
+
static VALUE
|
|
740
|
+
vector_operation_cleanup(VALUE opaque)
|
|
741
|
+
{
|
|
742
|
+
vector_operation_t *operation = (vector_operation_t *)opaque;
|
|
743
|
+
if (!operation->complete) {
|
|
744
|
+
pthread_mutex_lock(&operation->vector->lock);
|
|
745
|
+
vector_finish_update_locked(operation->vector);
|
|
746
|
+
pthread_mutex_unlock(&operation->vector->lock);
|
|
747
|
+
}
|
|
748
|
+
return Qnil;
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
static VALUE
|
|
752
|
+
vector_store_if_absent_body(VALUE opaque)
|
|
753
|
+
{
|
|
754
|
+
vector_operation_t *operation = (vector_operation_t *)opaque;
|
|
755
|
+
VALUE result = rb_yield_values(0);
|
|
756
|
+
rb_check_frozen(operation->self);
|
|
757
|
+
containers_check_shareable(result);
|
|
758
|
+
pthread_mutex_lock(&operation->vector->lock);
|
|
759
|
+
if (operation->index >= operation->vector->size) {
|
|
760
|
+
operation->vector->size = operation->index + 1;
|
|
761
|
+
}
|
|
762
|
+
operation->vector->values[operation->index] = result;
|
|
763
|
+
vector_finish_update_locked(operation->vector);
|
|
764
|
+
pthread_mutex_unlock(&operation->vector->lock);
|
|
765
|
+
operation->complete = true;
|
|
766
|
+
return result;
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
static VALUE
|
|
770
|
+
vector_store_if_absent(int argc, VALUE *argv, VALUE self)
|
|
771
|
+
{
|
|
772
|
+
VALUE index_value;
|
|
773
|
+
VALUE keywords = Qnil;
|
|
774
|
+
rb_scan_args(argc, argv, "1:", &index_value, &keywords);
|
|
775
|
+
rb_check_frozen(self);
|
|
776
|
+
long long raw = vector_convert_index(index_value);
|
|
777
|
+
vector_timeout_t timeout = vector_parse_timeout(vector_timeout_keyword(keywords));
|
|
778
|
+
rb_check_frozen(self);
|
|
779
|
+
vector_t *vector = get_vector(self);
|
|
780
|
+
rb_need_block();
|
|
781
|
+
|
|
782
|
+
if (!vector_lock_for_update(vector, &timeout)) return Qnil;
|
|
783
|
+
size_t index = vector_assignment_index(vector, raw);
|
|
784
|
+
if (!vector_ensure_capacity(vector, index + 1)) {
|
|
785
|
+
pthread_mutex_unlock(&vector->lock);
|
|
786
|
+
rb_memerror();
|
|
787
|
+
}
|
|
788
|
+
if (index < vector->size && !NIL_P(vector->values[index])) {
|
|
789
|
+
VALUE value = vector->values[index];
|
|
790
|
+
pthread_mutex_unlock(&vector->lock);
|
|
791
|
+
return value;
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
vector_operation_t operation = {
|
|
795
|
+
.self = self,
|
|
796
|
+
.vector = vector,
|
|
797
|
+
.index = index,
|
|
798
|
+
.complete = false,
|
|
799
|
+
};
|
|
800
|
+
vector->updating = true;
|
|
801
|
+
pthread_mutex_unlock(&vector->lock);
|
|
802
|
+
return rb_ensure(
|
|
803
|
+
vector_store_if_absent_body,
|
|
804
|
+
(VALUE)&operation,
|
|
805
|
+
vector_operation_cleanup,
|
|
806
|
+
(VALUE)&operation
|
|
807
|
+
);
|
|
808
|
+
}
|
|
809
|
+
|
|
810
|
+
static VALUE
|
|
811
|
+
vector_cas_body(VALUE opaque)
|
|
812
|
+
{
|
|
813
|
+
vector_operation_t *operation = (vector_operation_t *)opaque;
|
|
814
|
+
bool matches = operation->identity
|
|
815
|
+
? operation->current == operation->argument
|
|
816
|
+
: RTEST(rb_equal(operation->current, operation->argument));
|
|
817
|
+
rb_check_frozen(operation->self);
|
|
818
|
+
|
|
819
|
+
pthread_mutex_lock(&operation->vector->lock);
|
|
820
|
+
if (matches) operation->vector->values[operation->index] = operation->replacement;
|
|
821
|
+
vector_finish_update_locked(operation->vector);
|
|
822
|
+
pthread_mutex_unlock(&operation->vector->lock);
|
|
823
|
+
operation->complete = true;
|
|
824
|
+
return matches ? Qtrue : Qfalse;
|
|
825
|
+
}
|
|
826
|
+
|
|
827
|
+
static VALUE
|
|
828
|
+
vector_compare_and_set(int argc, VALUE *argv, VALUE self)
|
|
829
|
+
{
|
|
830
|
+
VALUE index_value;
|
|
831
|
+
VALUE expected;
|
|
832
|
+
VALUE replacement;
|
|
833
|
+
VALUE keywords = Qnil;
|
|
834
|
+
rb_scan_args(argc, argv, "3:", &index_value, &expected, &replacement, &keywords);
|
|
835
|
+
rb_check_frozen(self);
|
|
836
|
+
long long raw = vector_convert_index(index_value);
|
|
837
|
+
vector_timeout_t timeout = vector_parse_timeout(vector_timeout_keyword(keywords));
|
|
838
|
+
rb_check_frozen(self);
|
|
839
|
+
vector_t *vector = get_vector(self);
|
|
840
|
+
containers_check_shareable(expected);
|
|
841
|
+
containers_check_shareable(replacement);
|
|
842
|
+
|
|
843
|
+
if (!vector_lock_for_update(vector, &timeout)) return Qfalse;
|
|
844
|
+
size_t index;
|
|
845
|
+
if (!vector_lookup_index(vector, raw, &index)) {
|
|
846
|
+
pthread_mutex_unlock(&vector->lock);
|
|
847
|
+
return Qfalse;
|
|
848
|
+
}
|
|
849
|
+
vector_operation_t operation = {
|
|
850
|
+
.self = self,
|
|
851
|
+
.vector = vector,
|
|
852
|
+
.index = index,
|
|
853
|
+
.current = vector->values[index],
|
|
854
|
+
.argument = expected,
|
|
855
|
+
.replacement = replacement,
|
|
856
|
+
.identity = vector->compare_by_identity,
|
|
857
|
+
.complete = false,
|
|
858
|
+
};
|
|
859
|
+
vector->updating = true;
|
|
860
|
+
pthread_mutex_unlock(&vector->lock);
|
|
861
|
+
return rb_ensure(vector_cas_body, (VALUE)&operation, vector_operation_cleanup, (VALUE)&operation);
|
|
862
|
+
}
|
|
863
|
+
|
|
864
|
+
static VALUE
|
|
865
|
+
vector_upsert_body(VALUE opaque)
|
|
866
|
+
{
|
|
867
|
+
vector_operation_t *operation = (vector_operation_t *)opaque;
|
|
868
|
+
VALUE result = rb_yield(operation->current);
|
|
869
|
+
rb_check_frozen(operation->self);
|
|
870
|
+
containers_check_shareable(result);
|
|
871
|
+
pthread_mutex_lock(&operation->vector->lock);
|
|
872
|
+
operation->vector->values[operation->index] = result;
|
|
873
|
+
vector_finish_update_locked(operation->vector);
|
|
874
|
+
pthread_mutex_unlock(&operation->vector->lock);
|
|
875
|
+
operation->complete = true;
|
|
876
|
+
return result;
|
|
877
|
+
}
|
|
878
|
+
|
|
879
|
+
static VALUE
|
|
880
|
+
vector_upsert(int argc, VALUE *argv, VALUE self)
|
|
881
|
+
{
|
|
882
|
+
VALUE index_value;
|
|
883
|
+
VALUE initial;
|
|
884
|
+
VALUE keywords = Qnil;
|
|
885
|
+
rb_scan_args(argc, argv, "2:", &index_value, &initial, &keywords);
|
|
886
|
+
rb_check_frozen(self);
|
|
887
|
+
long long raw = vector_convert_index(index_value);
|
|
888
|
+
vector_timeout_t timeout = vector_parse_timeout(vector_timeout_keyword(keywords));
|
|
889
|
+
rb_check_frozen(self);
|
|
890
|
+
vector_t *vector = get_vector(self);
|
|
891
|
+
containers_check_shareable(initial);
|
|
892
|
+
rb_need_block();
|
|
893
|
+
|
|
894
|
+
if (!vector_lock_for_update(vector, &timeout)) return Qnil;
|
|
895
|
+
size_t index = vector_assignment_index(vector, raw);
|
|
896
|
+
if (!vector_ensure_capacity(vector, index + 1)) {
|
|
897
|
+
pthread_mutex_unlock(&vector->lock);
|
|
898
|
+
rb_memerror();
|
|
899
|
+
}
|
|
900
|
+
if (index >= vector->size) vector->size = index + 1;
|
|
901
|
+
if (NIL_P(vector->values[index])) {
|
|
902
|
+
vector->values[index] = initial;
|
|
903
|
+
vector_notify_waiters_locked(vector);
|
|
904
|
+
pthread_mutex_unlock(&vector->lock);
|
|
905
|
+
return initial;
|
|
906
|
+
}
|
|
907
|
+
|
|
908
|
+
vector_operation_t operation = {
|
|
909
|
+
.self = self,
|
|
910
|
+
.vector = vector,
|
|
911
|
+
.index = index,
|
|
912
|
+
.current = vector->values[index],
|
|
913
|
+
.complete = false,
|
|
914
|
+
};
|
|
915
|
+
vector->updating = true;
|
|
916
|
+
pthread_mutex_unlock(&vector->lock);
|
|
917
|
+
return rb_ensure(vector_upsert_body, (VALUE)&operation, vector_operation_cleanup, (VALUE)&operation);
|
|
918
|
+
}
|
|
919
|
+
|
|
920
|
+
static VALUE
|
|
921
|
+
vector_update_body(VALUE opaque)
|
|
922
|
+
{
|
|
923
|
+
vector_operation_t *operation = (vector_operation_t *)opaque;
|
|
924
|
+
VALUE result = rb_yield(operation->current);
|
|
925
|
+
rb_check_frozen(operation->self);
|
|
926
|
+
containers_check_shareable(result);
|
|
927
|
+
pthread_mutex_lock(&operation->vector->lock);
|
|
928
|
+
if (operation->index >= operation->vector->size) {
|
|
929
|
+
operation->vector->size = operation->index + 1;
|
|
930
|
+
}
|
|
931
|
+
operation->vector->values[operation->index] = result;
|
|
932
|
+
vector_finish_update_locked(operation->vector);
|
|
933
|
+
pthread_mutex_unlock(&operation->vector->lock);
|
|
934
|
+
operation->complete = true;
|
|
935
|
+
return result;
|
|
936
|
+
}
|
|
937
|
+
|
|
938
|
+
static VALUE
|
|
939
|
+
vector_update(int argc, VALUE *argv, VALUE self)
|
|
940
|
+
{
|
|
941
|
+
VALUE index_value;
|
|
942
|
+
VALUE keywords = Qnil;
|
|
943
|
+
rb_scan_args(argc, argv, "1:", &index_value, &keywords);
|
|
944
|
+
rb_check_frozen(self);
|
|
945
|
+
long long raw = vector_convert_index(index_value);
|
|
946
|
+
vector_timeout_t timeout = vector_parse_timeout(vector_timeout_keyword(keywords));
|
|
947
|
+
rb_check_frozen(self);
|
|
948
|
+
vector_t *vector = get_vector(self);
|
|
949
|
+
rb_need_block();
|
|
950
|
+
|
|
951
|
+
if (!vector_lock_for_update(vector, &timeout)) return Qnil;
|
|
952
|
+
size_t index = vector_assignment_index(vector, raw);
|
|
953
|
+
if (!vector_ensure_capacity(vector, index + 1)) {
|
|
954
|
+
pthread_mutex_unlock(&vector->lock);
|
|
955
|
+
rb_memerror();
|
|
956
|
+
}
|
|
957
|
+
|
|
958
|
+
vector_operation_t operation = {
|
|
959
|
+
.self = self,
|
|
960
|
+
.vector = vector,
|
|
961
|
+
.index = index,
|
|
962
|
+
.current = index < vector->size ? vector->values[index] : Qnil,
|
|
963
|
+
.complete = false,
|
|
964
|
+
};
|
|
965
|
+
vector->updating = true;
|
|
966
|
+
pthread_mutex_unlock(&vector->lock);
|
|
967
|
+
return rb_ensure(vector_update_body, (VALUE)&operation, vector_operation_cleanup, (VALUE)&operation);
|
|
968
|
+
}
|
|
969
|
+
|
|
970
|
+
static bool
|
|
971
|
+
vector_values_equal(vector_t *vector, VALUE left, VALUE right)
|
|
972
|
+
{
|
|
973
|
+
return vector->compare_by_identity ? left == right : RTEST(rb_equal(left, right));
|
|
974
|
+
}
|
|
975
|
+
|
|
976
|
+
static VALUE
|
|
977
|
+
vector_wait_until_changed(int argc, VALUE *argv, VALUE self)
|
|
978
|
+
{
|
|
979
|
+
VALUE index_value;
|
|
980
|
+
VALUE expected;
|
|
981
|
+
VALUE keywords = Qnil;
|
|
982
|
+
rb_scan_args(argc, argv, "2:", &index_value, &expected, &keywords);
|
|
983
|
+
long long raw = vector_convert_index(index_value);
|
|
984
|
+
vector_timeout_t timeout = vector_parse_timeout(vector_timeout_keyword(keywords));
|
|
985
|
+
vector_t *vector = get_vector(self);
|
|
986
|
+
containers_check_shareable(expected);
|
|
987
|
+
|
|
988
|
+
for (;;) {
|
|
989
|
+
uint64_t generation;
|
|
990
|
+
size_t index;
|
|
991
|
+
pthread_mutex_lock(&vector->lock);
|
|
992
|
+
if (!vector_wait_for_reservation_locked(vector, &timeout)) return Qnil;
|
|
993
|
+
VALUE current = vector_lookup_index(vector, raw, &index) ? vector->values[index] : Qnil;
|
|
994
|
+
generation = vector->generation;
|
|
995
|
+
pthread_mutex_unlock(&vector->lock);
|
|
996
|
+
|
|
997
|
+
if (!vector_values_equal(vector, current, expected)) return current;
|
|
998
|
+
|
|
999
|
+
pthread_mutex_lock(&vector->lock);
|
|
1000
|
+
if (!vector_wait_for_reservation_locked(vector, &timeout)) return Qnil;
|
|
1001
|
+
if (vector->generation != generation) {
|
|
1002
|
+
pthread_mutex_unlock(&vector->lock);
|
|
1003
|
+
continue;
|
|
1004
|
+
}
|
|
1005
|
+
if (!vector_wait_once(vector, &timeout)) return Qnil;
|
|
1006
|
+
}
|
|
1007
|
+
}
|
|
1008
|
+
|
|
1009
|
+
static VALUE
|
|
1010
|
+
vector_wait_until_non_nil(int argc, VALUE *argv, VALUE self)
|
|
1011
|
+
{
|
|
1012
|
+
VALUE index_value;
|
|
1013
|
+
VALUE keywords = Qnil;
|
|
1014
|
+
rb_scan_args(argc, argv, "1:", &index_value, &keywords);
|
|
1015
|
+
long long raw = vector_convert_index(index_value);
|
|
1016
|
+
vector_timeout_t timeout = vector_parse_timeout(vector_timeout_keyword(keywords));
|
|
1017
|
+
vector_t *vector = get_vector(self);
|
|
1018
|
+
|
|
1019
|
+
for (;;) {
|
|
1020
|
+
pthread_mutex_lock(&vector->lock);
|
|
1021
|
+
if (!vector_wait_for_reservation_locked(vector, &timeout)) return Qnil;
|
|
1022
|
+
size_t index;
|
|
1023
|
+
VALUE current = vector_lookup_index(vector, raw, &index) ? vector->values[index] : Qnil;
|
|
1024
|
+
if (!NIL_P(current)) {
|
|
1025
|
+
pthread_mutex_unlock(&vector->lock);
|
|
1026
|
+
return current;
|
|
1027
|
+
}
|
|
1028
|
+
if (!vector_wait_once(vector, &timeout)) return Qnil;
|
|
1029
|
+
}
|
|
1030
|
+
}
|
|
1031
|
+
|
|
1032
|
+
static VALUE
|
|
1033
|
+
unshared_vector_capture_values(VALUE self)
|
|
1034
|
+
{
|
|
1035
|
+
return rb_ivar_get(self, vector_values_id);
|
|
1036
|
+
}
|
|
1037
|
+
|
|
1038
|
+
static VALUE
|
|
1039
|
+
unshared_vector_read_values(VALUE self)
|
|
1040
|
+
{
|
|
1041
|
+
if (rb_ivar_get(self, vector_updating_id) == vector_transaction_marker) {
|
|
1042
|
+
VALUE mutex = rb_ivar_get(self, vector_mutex_id);
|
|
1043
|
+
return rb_mutex_synchronize(mutex, unshared_vector_capture_values, self);
|
|
1044
|
+
}
|
|
1045
|
+
return unshared_vector_capture_values(self);
|
|
1046
|
+
}
|
|
1047
|
+
|
|
1048
|
+
/* An unshared Array is confined to one Ractor. Its reads run under that
|
|
1049
|
+
* Ractor's GVL, so no Ruby mutex is needed between yields on CRuby. */
|
|
1050
|
+
static VALUE
|
|
1051
|
+
unshared_vector_each(VALUE self)
|
|
1052
|
+
{
|
|
1053
|
+
RETURN_ENUMERATOR(self, 0, NULL);
|
|
1054
|
+
VALUE values = unshared_vector_read_values(self);
|
|
1055
|
+
Check_Type(values, T_ARRAY);
|
|
1056
|
+
long limit = RARRAY_LEN(values);
|
|
1057
|
+
for (long index = 0; index < limit && index < RARRAY_LEN(values); index++) {
|
|
1058
|
+
rb_yield(RARRAY_AREF(values, index));
|
|
1059
|
+
}
|
|
1060
|
+
RB_GC_GUARD(values);
|
|
1061
|
+
return self;
|
|
1062
|
+
}
|
|
1063
|
+
|
|
1064
|
+
static VALUE
|
|
1065
|
+
unshared_vector_reverse_each(VALUE self)
|
|
1066
|
+
{
|
|
1067
|
+
RETURN_ENUMERATOR(self, 0, NULL);
|
|
1068
|
+
VALUE values = unshared_vector_read_values(self);
|
|
1069
|
+
Check_Type(values, T_ARRAY);
|
|
1070
|
+
long index = RARRAY_LEN(values);
|
|
1071
|
+
while (index > 0) {
|
|
1072
|
+
if (index > RARRAY_LEN(values)) index = RARRAY_LEN(values);
|
|
1073
|
+
if (index == 0) break;
|
|
1074
|
+
rb_yield(RARRAY_AREF(values, --index));
|
|
1075
|
+
}
|
|
1076
|
+
RB_GC_GUARD(values);
|
|
1077
|
+
return self;
|
|
1078
|
+
}
|
|
1079
|
+
|
|
1080
|
+
static bool
|
|
1081
|
+
vector_transaction_valid(farce_transaction_entry_t *entry)
|
|
1082
|
+
{
|
|
1083
|
+
vector_t *vector = entry->source_data;
|
|
1084
|
+
return vector->generation == entry->version && !vector->updating;
|
|
1085
|
+
}
|
|
1086
|
+
|
|
1087
|
+
static void
|
|
1088
|
+
vector_transaction_apply(farce_transaction_entry_t *entry)
|
|
1089
|
+
{
|
|
1090
|
+
vector_t *vector = entry->source_data;
|
|
1091
|
+
vector_t *copy = entry->working_data;
|
|
1092
|
+
VALUE *values = vector->values;
|
|
1093
|
+
size_t capacity = vector->capacity, size = vector->size;
|
|
1094
|
+
vector->values = copy->values;
|
|
1095
|
+
vector->capacity = copy->capacity;
|
|
1096
|
+
vector->size = copy->size;
|
|
1097
|
+
copy->values = values;
|
|
1098
|
+
copy->capacity = capacity;
|
|
1099
|
+
copy->size = size;
|
|
1100
|
+
}
|
|
1101
|
+
|
|
1102
|
+
static void
|
|
1103
|
+
vector_transaction_notify(farce_transaction_entry_t *entry)
|
|
1104
|
+
{
|
|
1105
|
+
vector_notify_waiters_locked(entry->source_data);
|
|
1106
|
+
}
|
|
1107
|
+
|
|
1108
|
+
static void
|
|
1109
|
+
vector_transaction_reserve(farce_transaction_entry_t *entry, VALUE fiber, VALUE thread)
|
|
1110
|
+
{
|
|
1111
|
+
vector_t *vector = entry->source_data;
|
|
1112
|
+
vector->updating = FARCE_TRANSACTION_RESERVED;
|
|
1113
|
+
vector->updating_fiber = fiber;
|
|
1114
|
+
vector->updating_thread = thread;
|
|
1115
|
+
}
|
|
1116
|
+
|
|
1117
|
+
static void
|
|
1118
|
+
vector_transaction_release(farce_transaction_entry_t *entry)
|
|
1119
|
+
{
|
|
1120
|
+
vector_t *vector = entry->source_data;
|
|
1121
|
+
vector->updating_fiber = Qnil;
|
|
1122
|
+
vector->updating_thread = Qnil;
|
|
1123
|
+
vector_finish_update_locked(vector);
|
|
1124
|
+
}
|
|
1125
|
+
|
|
1126
|
+
static const farce_transaction_ops_t vector_transaction_ops = {
|
|
1127
|
+
vector_transaction_valid, vector_transaction_apply, vector_transaction_notify,
|
|
1128
|
+
vector_transaction_reserve, vector_transaction_release,
|
|
1129
|
+
};
|
|
1130
|
+
|
|
1131
|
+
static VALUE
|
|
1132
|
+
vector_transaction_snapshot(VALUE self)
|
|
1133
|
+
{
|
|
1134
|
+
vector_t *source = get_vector(self);
|
|
1135
|
+
VALUE working = vector_allocate(cVector);
|
|
1136
|
+
vector_t *copy;
|
|
1137
|
+
TypedData_Get_Struct(working, vector_t, &vector_type, copy);
|
|
1138
|
+
farce_transaction_entry_t *entry;
|
|
1139
|
+
VALUE result = farce_transaction_entry_new(
|
|
1140
|
+
self, working, source, copy, &source->lock, &vector_transaction_ops, &entry
|
|
1141
|
+
);
|
|
1142
|
+
pthread_mutex_lock(&source->lock);
|
|
1143
|
+
copy->values = malloc(source->capacity * sizeof(VALUE));
|
|
1144
|
+
if (!copy->values) {
|
|
1145
|
+
pthread_mutex_unlock(&source->lock);
|
|
1146
|
+
rb_memerror();
|
|
1147
|
+
}
|
|
1148
|
+
memcpy(copy->values, source->values, source->size * sizeof(VALUE));
|
|
1149
|
+
for (size_t i = source->size; i < source->capacity; i++) copy->values[i] = Qnil;
|
|
1150
|
+
copy->capacity = source->capacity;
|
|
1151
|
+
copy->size = source->size;
|
|
1152
|
+
copy->compare_by_identity = source->compare_by_identity;
|
|
1153
|
+
copy->initialized = true;
|
|
1154
|
+
entry->version = source->generation;
|
|
1155
|
+
pthread_mutex_unlock(&source->lock);
|
|
1156
|
+
return result;
|
|
1157
|
+
}
|
|
1158
|
+
|
|
1159
|
+
void
|
|
1160
|
+
containers_init_vector(VALUE namespace)
|
|
1161
|
+
{
|
|
1162
|
+
vector_values_id = rb_intern("@values");
|
|
1163
|
+
vector_updating_id = rb_intern("@updating");
|
|
1164
|
+
vector_mutex_id = rb_intern("@mutex");
|
|
1165
|
+
vector_transaction_marker = ID2SYM(rb_intern("transaction"));
|
|
1166
|
+
VALUE iteration = rb_define_module_under(namespace, "UnsharedVectorIteration");
|
|
1167
|
+
rb_define_method(iteration, "each", unshared_vector_each, 0);
|
|
1168
|
+
rb_define_method(iteration, "reverse_each", unshared_vector_reverse_each, 0);
|
|
1169
|
+
|
|
1170
|
+
cVector = rb_define_class_under(namespace, "Vector", rb_cObject);
|
|
1171
|
+
rb_define_alloc_func(cVector, vector_allocate);
|
|
1172
|
+
rb_define_method(cVector, "freeze", vector_freeze, 0);
|
|
1173
|
+
rb_define_method(cVector, "initialize", vector_initialize, -1);
|
|
1174
|
+
rb_define_method(cVector, "size", vector_size, 0);
|
|
1175
|
+
rb_define_method(cVector, "snapshot", vector_snapshot, 0);
|
|
1176
|
+
rb_define_method(cVector, "each", vector_each, 0);
|
|
1177
|
+
rb_define_method(cVector, "reverse_each", vector_reverse_each, 0);
|
|
1178
|
+
rb_define_method(cVector, "fetch", vector_fetch, 1);
|
|
1179
|
+
rb_define_method(cVector, "clear", vector_clear, 0);
|
|
1180
|
+
rb_define_method(cVector, "[]", vector_get_fast, 1);
|
|
1181
|
+
rb_define_method(cVector, "[]=", vector_set_fast, 2);
|
|
1182
|
+
rb_define_method(cVector, "get", vector_get, -1);
|
|
1183
|
+
rb_define_method(cVector, "store", vector_store, -1);
|
|
1184
|
+
rb_define_method(cVector, "push", vector_push, -1);
|
|
1185
|
+
rb_define_method(cVector, "pop", vector_pop, -1);
|
|
1186
|
+
rb_define_method(cVector, "swap", vector_swap, -1);
|
|
1187
|
+
rb_define_method(cVector, "store_if_absent", vector_store_if_absent, -1);
|
|
1188
|
+
rb_define_method(cVector, "transaction_snapshot", vector_transaction_snapshot, 0);
|
|
1189
|
+
rb_define_method(cVector, "compare_and_set", vector_compare_and_set, -1);
|
|
1190
|
+
rb_define_method(cVector, "upsert", vector_upsert, -1);
|
|
1191
|
+
rb_define_method(cVector, "update", vector_update, -1);
|
|
1192
|
+
rb_define_method(cVector, "wait_until_changed", vector_wait_until_changed, -1);
|
|
1193
|
+
rb_define_method(cVector, "wait_until_non_nil", vector_wait_until_non_nil, -1);
|
|
1194
|
+
rb_define_method(cVector, "compare_by_identity?", vector_compare_by_identity_p, 0);
|
|
1195
|
+
}
|