farce 0.0.1.alpha2-x86-linux-gnu
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/CODE_OF_CONDUCT.md +26 -0
- data/CONTRIBUTING.md +71 -0
- data/MIT-LICENSE +20 -0
- data/README.md +1524 -0
- data/SECURITY.md +10 -0
- data/docs/benchmarks.md +185 -0
- data/docs/gems/dry-types.md +290 -0
- data/docs/gems/msgpack.md +70 -0
- data/docs/gems/ractor-shim.md +63 -0
- data/docs/modes.md +628 -0
- data/docs/scopes.md +649 -0
- data/docs/variants.md +346 -0
- data/ext/ext_helper.rb +20 -0
- data/ext/farce/README.md +21 -0
- data/ext/farce/atom.c +1023 -0
- data/ext/farce/bounded_map.c +1683 -0
- data/ext/farce/containers.h +77 -0
- data/ext/farce/counter.c +399 -0
- data/ext/farce/darwin.c +100 -0
- data/ext/farce/depend +12 -0
- data/ext/farce/dict.c +1523 -0
- data/ext/farce/dict.h +152 -0
- data/ext/farce/drivers.c +237 -0
- data/ext/farce/exchanger.c +299 -0
- data/ext/farce/extconf.rb +99 -0
- data/ext/farce/farce.c +359 -0
- data/ext/farce/flag.c +288 -0
- data/ext/farce/io.c +338 -0
- data/ext/farce/lock.c +510 -0
- data/ext/farce/map.c +2240 -0
- data/ext/farce/priority_queue.c +2056 -0
- data/ext/farce/queue.c +1059 -0
- data/ext/farce/reactor.c +820 -0
- data/ext/farce/reactor.h +103 -0
- data/ext/farce/shareable.h +31 -0
- data/ext/farce/signal.c +350 -0
- data/ext/farce/transaction.c +354 -0
- data/ext/farce/transaction.h +40 -0
- data/ext/farce/tree_map.c +1953 -0
- data/ext/farce/trie.c +2020 -0
- data/ext/farce/unshareable.c +155 -0
- data/ext/farce/unshared_io_pool.h +234 -0
- data/ext/farce/unshared_signal.c +263 -0
- data/ext/farce/unshared_wait.h +193 -0
- data/ext/farce/unsupported.c +6 -0
- data/ext/farce/vector.c +1195 -0
- data/ext/farce/weak_map.c +1714 -0
- data/ext/java/org/farce/BoundedMap.java +394 -0
- data/ext/java/org/farce/FiberScheduler.java +141 -0
- data/ext/java/org/farce/PriorityKey.java +45 -0
- data/ext/java/org/farce/PriorityQueue.java +373 -0
- data/ext/java/org/farce/QueueSignal.java +28 -0
- data/ext/rebind/README.md +14 -0
- data/ext/rebind/extconf.rb +10 -0
- data/ext/rebind/rebind.c +186 -0
- data/lib/farce/_yard/internal.rb +13 -0
- data/lib/farce/_yard/macros.rb +61 -0
- data/lib/farce/_yard/ractor.rb +46 -0
- data/lib/farce/abstract/atom.rb +186 -0
- data/lib/farce/abstract/bounded_map.rb +232 -0
- data/lib/farce/abstract/collection.rb +151 -0
- data/lib/farce/abstract/concurrent_map.rb +381 -0
- data/lib/farce/abstract/counter.rb +193 -0
- data/lib/farce/abstract/duplicable_map.rb +229 -0
- data/lib/farce/abstract/exchanger.rb +26 -0
- data/lib/farce/abstract/flag.rb +71 -0
- data/lib/farce/abstract/lazy.rb +115 -0
- data/lib/farce/abstract/lease.rb +104 -0
- data/lib/farce/abstract/lease_map.rb +261 -0
- data/lib/farce/abstract/lease_pool.rb +91 -0
- data/lib/farce/abstract/lfu_map.rb +20 -0
- data/lib/farce/abstract/lru_map.rb +25 -0
- data/lib/farce/abstract/map.rb +345 -0
- data/lib/farce/abstract/molecule.rb +245 -0
- data/lib/farce/abstract/port.rb +74 -0
- data/lib/farce/abstract/priority_queue.rb +117 -0
- data/lib/farce/abstract/queue.rb +269 -0
- data/lib/farce/abstract/scheduler.rb +111 -0
- data/lib/farce/abstract/set.rb +910 -0
- data/lib/farce/abstract/sorted_set.rb +172 -0
- data/lib/farce/abstract/timer_queue.rb +136 -0
- data/lib/farce/abstract/tree_map.rb +269 -0
- data/lib/farce/abstract/value.rb +68 -0
- data/lib/farce/abstract/vector.rb +1126 -0
- data/lib/farce/abstract/weak_atom.rb +32 -0
- data/lib/farce/abstract/weak_key_map.rb +12 -0
- data/lib/farce/abstract/weak_map.rb +13 -0
- data/lib/farce/abstract/weak_set.rb +12 -0
- data/lib/farce/abstract/weak_value_map.rb +12 -0
- data/lib/farce/abstract.rb +17 -0
- data/lib/farce/atom.rb +293 -0
- data/lib/farce/class_mirror.rb +87 -0
- data/lib/farce/clock.rb +121 -0
- data/lib/farce/config.rb +229 -0
- data/lib/farce/counter.rb +71 -0
- data/lib/farce/deduper.rb +122 -0
- data/lib/farce/engine/jruby/bounded_map.rb +314 -0
- data/lib/farce/engine/jruby/fiber_scheduler.jar +0 -0
- data/lib/farce/engine/jruby/fiber_scheduler.rb +119 -0
- data/lib/farce/engine/jruby/lease_waiting.rb +19 -0
- data/lib/farce/engine/jruby/map.rb +505 -0
- data/lib/farce/engine/jruby/mutable_numeric_copy.rb +42 -0
- data/lib/farce/engine/jruby/signal.rb +147 -0
- data/lib/farce/engine/jruby.rb +63 -0
- data/lib/farce/engine/jvm/concurrent_weak_registry.rb +55 -0
- data/lib/farce/engine/jvm/counter.rb +102 -0
- data/lib/farce/engine/jvm/extension.rb +32 -0
- data/lib/farce/engine/jvm/farce.jar +0 -0
- data/lib/farce/engine/jvm/flag.rb +79 -0
- data/lib/farce/engine/jvm/priority_queue.rb +217 -0
- data/lib/farce/engine/jvm/tree_map.rb +350 -0
- data/lib/farce/engine/jvm/types.rb +180 -0
- data/lib/farce/engine/jvm.rb +19 -0
- data/lib/farce/engine/ruby/3.4/farce.so +0 -0
- data/lib/farce/engine/ruby/3.4/fiber_scheduler.rb +20 -0
- data/lib/farce/engine/ruby/3.4/port.rb +186 -0
- data/lib/farce/engine/ruby/3.4/ractor_methods.rb +26 -0
- data/lib/farce/engine/ruby/3.4/ractor_selector.rb +92 -0
- data/lib/farce/engine/ruby/3.4/rebind.so +0 -0
- data/lib/farce/engine/ruby/3.4/vault.rb +56 -0
- data/lib/farce/engine/ruby/4.0/farce.so +0 -0
- data/lib/farce/engine/ruby/4.0/port.rb +19 -0
- data/lib/farce/engine/ruby/4.0/ractor_methods.rb +20 -0
- data/lib/farce/engine/ruby/4.0/ractor_selector.rb +108 -0
- data/lib/farce/engine/ruby/4.0/rebind.so +0 -0
- data/lib/farce/engine/ruby/4.0/vault.rb +74 -0
- data/lib/farce/engine/ruby/4.1/port.rb +21 -0
- data/lib/farce/engine/ruby/4.1/ractor_methods.rb +22 -0
- data/lib/farce/engine/ruby/4.1/ractor_selector.rb +25 -0
- data/lib/farce/engine/ruby/4.1/vault.rb +5 -0
- data/lib/farce/engine/ruby/fiber_scheduler.rb +32 -0
- data/lib/farce/engine/ruby/key_lock_map.rb +28 -0
- data/lib/farce/engine/ruby/shared/lease.rb +26 -0
- data/lib/farce/engine/ruby/shared/lease_pool.rb +24 -0
- data/lib/farce/engine/ruby/shared/main_scheduler.rb +9 -0
- data/lib/farce/engine/ruby/shared/parallel_scheduler.rb +9 -0
- data/lib/farce/engine/ruby/shared/proxy_owner.rb +31 -0
- data/lib/farce/engine/ruby/shared/ractor_methods.rb +34 -0
- data/lib/farce/engine/ruby/shared/ractor_selector.rb +328 -0
- data/lib/farce/engine/ruby/shared/strict_map.rb +13 -0
- data/lib/farce/engine/ruby/shared/unshared_vector.rb +14 -0
- data/lib/farce/engine/ruby/shared/vault.rb +144 -0
- data/lib/farce/engine/ruby/shared/vault_weak_map.rb +336 -0
- data/lib/farce/engine/ruby/shared/weak_atom.rb +54 -0
- data/lib/farce/engine/ruby/shared/weak_map.rb +413 -0
- data/lib/farce/engine/ruby.rb +130 -0
- data/lib/farce/engine/shared/atom.rb +10 -0
- data/lib/farce/engine/shared/exchanger.rb +130 -0
- data/lib/farce/engine/shared/identity_key.rb +22 -0
- data/lib/farce/engine/shared/lease.rb +10 -0
- data/lib/farce/engine/shared/lease_pool.rb +10 -0
- data/lib/farce/engine/shared/main_scheduler.rb +20 -0
- data/lib/farce/engine/shared/map_key_coordination.rb +226 -0
- data/lib/farce/engine/shared/parallel_scheduler.rb +9 -0
- data/lib/farce/engine/shared/port.rb +44 -0
- data/lib/farce/engine/shared/portable_bounded_map.rb +619 -0
- data/lib/farce/engine/shared/proxy_owner.rb +43 -0
- data/lib/farce/engine/shared/queue.rb +233 -0
- data/lib/farce/engine/shared/ractor_methods.rb +73 -0
- data/lib/farce/engine/shared/rebindable.rb +15 -0
- data/lib/farce/engine/shared/strict_atom.rb +73 -0
- data/lib/farce/engine/shared/strict_map.rb +117 -0
- data/lib/farce/engine/shared/strict_queue_values.rb +27 -0
- data/lib/farce/engine/shared/strict_tree_map.rb +56 -0
- data/lib/farce/engine/shared/transaction_map_backend.rb +20 -0
- data/lib/farce/engine/shared/trie.rb +317 -0
- data/lib/farce/engine/shared/trie_builder.rb +199 -0
- data/lib/farce/engine/shared/unshareable.rb +14 -0
- data/lib/farce/engine/shared/unshared_atom.rb +256 -0
- data/lib/farce/engine/shared/unshared_priority_queue.rb +9 -0
- data/lib/farce/engine/shared/unshared_queue.rb +18 -0
- data/lib/farce/engine/shared/unshared_signal.rb +21 -0
- data/lib/farce/engine/shared/unshared_vector.rb +377 -0
- data/lib/farce/engine/shared/unshared_weak_atom.rb +30 -0
- data/lib/farce/engine/shared/unshared_weak_map.rb +445 -0
- data/lib/farce/engine/shared/vault.rb +31 -0
- data/lib/farce/engine/shared/vector.rb +83 -0
- data/lib/farce/engine/shared/weak_atom/base.rb +189 -0
- data/lib/farce/engine/shared/weak_atom.rb +20 -0
- data/lib/farce/engine/shared/weak_map/cell.rb +99 -0
- data/lib/farce/engine/shared/weak_map/index.rb +249 -0
- data/lib/farce/engine/shared/weak_map/lock.rb +80 -0
- data/lib/farce/engine/shared/weak_map/reference.rb +33 -0
- data/lib/farce/engine/shared.rb +63 -0
- data/lib/farce/engine/truffleruby/fiber_scheduler.rb +13 -0
- data/lib/farce/engine/truffleruby/lock.rb +121 -0
- data/lib/farce/engine/truffleruby/map.rb +656 -0
- data/lib/farce/engine/truffleruby/native/counter.rb +105 -0
- data/lib/farce/engine/truffleruby/native/flag.rb +77 -0
- data/lib/farce/engine/truffleruby/native/ordered_array_support.rb +67 -0
- data/lib/farce/engine/truffleruby/native/priority_queue.rb +388 -0
- data/lib/farce/engine/truffleruby/native/tree_map.rb +51 -0
- data/lib/farce/engine/truffleruby/native/unsafe_tree_map.rb +350 -0
- data/lib/farce/engine/truffleruby/signal.rb +77 -0
- data/lib/farce/engine/truffleruby.rb +148 -0
- data/lib/farce/envelope.rb +335 -0
- data/lib/farce/error.rb +35 -0
- data/lib/farce/exchanger.rb +41 -0
- data/lib/farce/flag.rb +27 -0
- data/lib/farce/integrations/active_support/blank.rb +68 -0
- data/lib/farce/integrations/active_support/clock.rb +17 -0
- data/lib/farce/integrations/active_support/duplicable.rb +78 -0
- data/lib/farce/integrations/active_support/map.rb +175 -0
- data/lib/farce/integrations/active_support/set.rb +60 -0
- data/lib/farce/integrations/active_support/value_serialization.rb +17 -0
- data/lib/farce/integrations/active_support/vector.rb +231 -0
- data/lib/farce/integrations/active_support.rb +9 -0
- data/lib/farce/integrations/activesupport.rb +5 -0
- data/lib/farce/integrations/bson.rb +108 -0
- data/lib/farce/integrations/cbor.rb +51 -0
- data/lib/farce/integrations/concurrent.rb +112 -0
- data/lib/farce/integrations/dry-types.rb +5 -0
- data/lib/farce/integrations/dry_types.rb +694 -0
- data/lib/farce/integrations/json.rb +7 -0
- data/lib/farce/integrations/msgpack.rb +112 -0
- data/lib/farce/integrations/oj.rb +69 -0
- data/lib/farce/integrations/psych.rb +191 -0
- data/lib/farce/integrations/ractor-sharing.rb +5 -0
- data/lib/farce/integrations/ractor-tmvar.rb +5 -0
- data/lib/farce/integrations/ractor_sharing.rb +172 -0
- data/lib/farce/integrations/ractor_tmvar.rb +64 -0
- data/lib/farce/integrations/shared/to_json.rb +42 -0
- data/lib/farce/integrations/sorted_set.rb +11 -0
- data/lib/farce/integrations/weakref.rb +38 -0
- data/lib/farce/integrations/yajl.rb +10 -0
- data/lib/farce/integrations.rb +156 -0
- data/lib/farce/internal/_frozen_config.rb +11 -0
- data/lib/farce/internal/autoloads.rb +47 -0
- data/lib/farce/internal/blocking_priority_queue.rb +122 -0
- data/lib/farce/internal/converter.rb +106 -0
- data/lib/farce/internal/copyable.rb +31 -0
- data/lib/farce/internal/delegation.rb +20 -0
- data/lib/farce/internal/external_transaction.rb +59 -0
- data/lib/farce/internal/fake_ractor.rb +179 -0
- data/lib/farce/internal/freeze.rb +118 -0
- data/lib/farce/internal/inspect.rb +157 -0
- data/lib/farce/internal/key_lock_map.rb +29 -0
- data/lib/farce/internal/key_normalizer.rb +289 -0
- data/lib/farce/internal/lease_initialization.rb +115 -0
- data/lib/farce/internal/lease_map.rb +441 -0
- data/lib/farce/internal/lease_pool_state.rb +228 -0
- data/lib/farce/internal/lease_state.rb +342 -0
- data/lib/farce/internal/lease_waiting.rb +13 -0
- data/lib/farce/internal/managed_queue.rb +26 -0
- data/lib/farce/internal/map_value_modes.rb +227 -0
- data/lib/farce/internal/marshal_support.rb +227 -0
- data/lib/farce/internal/mixin.rb +20 -0
- data/lib/farce/internal/mutable_ordered_key_lock_map.rb +63 -0
- data/lib/farce/internal/noncopyable.rb +17 -0
- data/lib/farce/internal/ordered_key_lock_map.rb +69 -0
- data/lib/farce/internal/pool_supervisor.rb +41 -0
- data/lib/farce/internal/pool_worker.rb +61 -0
- data/lib/farce/internal/portable_transaction/reservation_entry.rb +29 -0
- data/lib/farce/internal/portable_transaction/strong_map_entry.rb +137 -0
- data/lib/farce/internal/portable_transaction/strong_map_size_entry.rb +44 -0
- data/lib/farce/internal/portable_transaction/tree_entry.rb +64 -0
- data/lib/farce/internal/portable_transaction.rb +164 -0
- data/lib/farce/internal/proxy_owner_notifications.rb +29 -0
- data/lib/farce/internal/reservation_waiting.rb +24 -0
- data/lib/farce/internal/scheduled_task.rb +48 -0
- data/lib/farce/internal/scheduler_io.rb +171 -0
- data/lib/farce/internal/scheduler_lifecycle.rb +314 -0
- data/lib/farce/internal/select_scheduler.rb +235 -0
- data/lib/farce/internal/storage.rb +162 -0
- data/lib/farce/internal/strict_lease.rb +30 -0
- data/lib/farce/internal/strict_lease_map.rb +20 -0
- data/lib/farce/internal/strict_lease_pool.rb +29 -0
- data/lib/farce/internal/thread_pool.rb +166 -0
- data/lib/farce/internal/transaction_conflict.rb +9 -0
- data/lib/farce/internal/transaction_freeze_guard.rb +30 -0
- data/lib/farce/internal/transaction_map_snapshot.rb +123 -0
- data/lib/farce/internal/undefined.rb +22 -0
- data/lib/farce/internal/unshared_lease.rb +29 -0
- data/lib/farce/internal/unshared_lease_pool.rb +34 -0
- data/lib/farce/internal/unshared_queue_waiting.rb +28 -0
- data/lib/farce/internal/value_serialization.rb +13 -0
- data/lib/farce/internal/weak_map_value_modes.rb +58 -0
- data/lib/farce/internal/weak_mode_manager.rb +26 -0
- data/lib/farce/internal.rb +129 -0
- data/lib/farce/lazy.rb +100 -0
- data/lib/farce/lazy_ref.rb +40 -0
- data/lib/farce/lease.rb +28 -0
- data/lib/farce/lease_map.rb +34 -0
- data/lib/farce/lease_pool.rb +29 -0
- data/lib/farce/lfu_map.rb +53 -0
- data/lib/farce/local/atom.rb +23 -0
- data/lib/farce/local/counter.rb +88 -0
- data/lib/farce/local/flag.rb +65 -0
- data/lib/farce/local/lazy.rb +38 -0
- data/lib/farce/local/lazy_ref.rb +36 -0
- data/lib/farce/local/lease.rb +77 -0
- data/lib/farce/local/lease_map.rb +80 -0
- data/lib/farce/local/lease_pool.rb +38 -0
- data/lib/farce/local/lfu_map.rb +55 -0
- data/lib/farce/local/lru_map.rb +62 -0
- data/lib/farce/local/map.rb +47 -0
- data/lib/farce/local/molecule.rb +45 -0
- data/lib/farce/local/priority_queue.rb +53 -0
- data/lib/farce/local/queue.rb +32 -0
- data/lib/farce/local/scoped.rb +166 -0
- data/lib/farce/local/set.rb +18 -0
- data/lib/farce/local/sorted_set.rb +50 -0
- data/lib/farce/local/timer_queue.rb +41 -0
- data/lib/farce/local/tree_map.rb +47 -0
- data/lib/farce/local/vector.rb +30 -0
- data/lib/farce/local/weak_atom.rb +23 -0
- data/lib/farce/local/weak_key_map.rb +39 -0
- data/lib/farce/local/weak_map.rb +39 -0
- data/lib/farce/local/weak_set.rb +18 -0
- data/lib/farce/local/weak_value_map.rb +39 -0
- data/lib/farce/local.rb +35 -0
- data/lib/farce/lock.rb +54 -0
- data/lib/farce/lru_map.rb +63 -0
- data/lib/farce/map.rb +58 -0
- data/lib/farce/mode_manager.rb +142 -0
- data/lib/farce/molecule.rb +60 -0
- data/lib/farce/mutable.rb +171 -0
- data/lib/farce/pool.rb +332 -0
- data/lib/farce/port.rb +134 -0
- data/lib/farce/priority_queue.rb +70 -0
- data/lib/farce/proxy/register.rb +101 -0
- data/lib/farce/proxy/supervisor.rb +137 -0
- data/lib/farce/proxy/wrapper.rb +49 -0
- data/lib/farce/proxy.rb +291 -0
- data/lib/farce/queue.rb +60 -0
- data/lib/farce/ractor.rb +269 -0
- data/lib/farce/read_write_lock.rb +256 -0
- data/lib/farce/reference.rb +158 -0
- data/lib/farce/resolv/dns.rb +42 -0
- data/lib/farce/resolv.rb +85 -0
- data/lib/farce/scheduler.rb +536 -0
- data/lib/farce/set.rb +39 -0
- data/lib/farce/shareable.rb +150 -0
- data/lib/farce/signal.rb +97 -0
- data/lib/farce/sorted_set.rb +36 -0
- data/lib/farce/strict/atom.rb +34 -0
- data/lib/farce/strict/counter.rb +10 -0
- data/lib/farce/strict/exchanger.rb +25 -0
- data/lib/farce/strict/flag.rb +10 -0
- data/lib/farce/strict/lazy.rb +26 -0
- data/lib/farce/strict/lazy_ref.rb +26 -0
- data/lib/farce/strict/lease.rb +27 -0
- data/lib/farce/strict/lease_map.rb +33 -0
- data/lib/farce/strict/lease_pool.rb +28 -0
- data/lib/farce/strict/lfu_map.rb +27 -0
- data/lib/farce/strict/lru_map.rb +28 -0
- data/lib/farce/strict/map.rb +54 -0
- data/lib/farce/strict/molecule.rb +16 -0
- data/lib/farce/strict/port.rb +40 -0
- data/lib/farce/strict/priority_queue.rb +20 -0
- data/lib/farce/strict/queue.rb +17 -0
- data/lib/farce/strict/set.rb +14 -0
- data/lib/farce/strict/sorted_set.rb +19 -0
- data/lib/farce/strict/timer_queue.rb +13 -0
- data/lib/farce/strict/tree_map.rb +27 -0
- data/lib/farce/strict/vector.rb +24 -0
- data/lib/farce/strict/weak_atom.rb +31 -0
- data/lib/farce/strict/weak_key_map.rb +49 -0
- data/lib/farce/strict/weak_map.rb +49 -0
- data/lib/farce/strict/weak_set.rb +14 -0
- data/lib/farce/strict/weak_value_map.rb +48 -0
- data/lib/farce/strict.rb +28 -0
- data/lib/farce/system.rb +40 -0
- data/lib/farce/thread_scheduler.rb +70 -0
- data/lib/farce/timer_queue.rb +66 -0
- data/lib/farce/transaction/atom.rb +73 -0
- data/lib/farce/transaction/map.rb +25 -0
- data/lib/farce/transaction/map_operations.rb +143 -0
- data/lib/farce/transaction/molecule.rb +90 -0
- data/lib/farce/transaction/mutable.rb +61 -0
- data/lib/farce/transaction/set.rb +17 -0
- data/lib/farce/transaction/set_operations.rb +48 -0
- data/lib/farce/transaction/sorted_set.rb +18 -0
- data/lib/farce/transaction/tree_map.rb +78 -0
- data/lib/farce/transaction/vector.rb +139 -0
- data/lib/farce/transaction/wrapper.rb +202 -0
- data/lib/farce/transaction.rb +343 -0
- data/lib/farce/tree_map.rb +56 -0
- data/lib/farce/unsafe/lfu_map.rb +36 -0
- data/lib/farce/unsafe/lru_map.rb +36 -0
- data/lib/farce/unsafe/tree_map.rb +21 -0
- data/lib/farce/unsafe.rb +29 -0
- data/lib/farce/unshareable.rb +67 -0
- data/lib/farce/unshared/atom.rb +24 -0
- data/lib/farce/unshared/counter.rb +10 -0
- data/lib/farce/unshared/flag.rb +10 -0
- data/lib/farce/unshared/lazy.rb +38 -0
- data/lib/farce/unshared/lazy_ref.rb +26 -0
- data/lib/farce/unshared/lease.rb +27 -0
- data/lib/farce/unshared/lease_map.rb +26 -0
- data/lib/farce/unshared/lease_pool.rb +25 -0
- data/lib/farce/unshared/lfu_map.rb +20 -0
- data/lib/farce/unshared/lru_map.rb +20 -0
- data/lib/farce/unshared/map.rb +49 -0
- data/lib/farce/unshared/molecule.rb +16 -0
- data/lib/farce/unshared/priority_queue.rb +16 -0
- data/lib/farce/unshared/queue.rb +27 -0
- data/lib/farce/unshared/set.rb +14 -0
- data/lib/farce/unshared/sorted_set.rb +28 -0
- data/lib/farce/unshared/timer_queue.rb +16 -0
- data/lib/farce/unshared/tree_map.rb +21 -0
- data/lib/farce/unshared/vector.rb +19 -0
- data/lib/farce/unshared/weak_atom.rb +31 -0
- data/lib/farce/unshared/weak_key_map.rb +44 -0
- data/lib/farce/unshared/weak_map.rb +44 -0
- data/lib/farce/unshared/weak_set.rb +14 -0
- data/lib/farce/unshared/weak_value_map.rb +43 -0
- data/lib/farce/unshared.rb +28 -0
- data/lib/farce/vector.rb +228 -0
- data/lib/farce/version.rb +8 -0
- data/lib/farce/walker/definitions.rb +186 -0
- data/lib/farce/walker/modification.rb +355 -0
- data/lib/farce/walker.rb +319 -0
- data/lib/farce/weak_atom.rb +121 -0
- data/lib/farce/weak_key_map.rb +31 -0
- data/lib/farce/weak_map.rb +33 -0
- data/lib/farce/weak_ref.rb +67 -0
- data/lib/farce/weak_set.rb +82 -0
- data/lib/farce/weak_value.rb +195 -0
- data/lib/farce/weak_value_map.rb +32 -0
- data/lib/farce.rb +519 -0
- metadata +468 -0
|
@@ -0,0 +1,1953 @@
|
|
|
1
|
+
#include "containers.h"
|
|
2
|
+
#include "transaction.h"
|
|
3
|
+
#include <ruby/atomic.h>
|
|
4
|
+
#include <ruby/fiber/scheduler.h>
|
|
5
|
+
#include <ruby/io.h>
|
|
6
|
+
#include <ruby/ractor.h>
|
|
7
|
+
#include <ruby/thread.h>
|
|
8
|
+
|
|
9
|
+
#include <errno.h>
|
|
10
|
+
#include <fcntl.h>
|
|
11
|
+
#include <math.h>
|
|
12
|
+
#include <pthread.h>
|
|
13
|
+
#include <stdbool.h>
|
|
14
|
+
#include <stddef.h>
|
|
15
|
+
#include <stdint.h>
|
|
16
|
+
#include <unistd.h>
|
|
17
|
+
|
|
18
|
+
/*
|
|
19
|
+
* A clean-room implementation of the conventional red-black-tree algorithms.
|
|
20
|
+
*
|
|
21
|
+
* This file intentionally shares no implementation with the Kazlib-backed
|
|
22
|
+
* priority queue. The generic link layer knows nothing about Ruby objects;
|
|
23
|
+
* each map node stores one key/value pair.
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
typedef enum {
|
|
27
|
+
TC_RED = 0,
|
|
28
|
+
TC_BLACK = 1
|
|
29
|
+
} tc_color;
|
|
30
|
+
|
|
31
|
+
enum {
|
|
32
|
+
TC_MAP_UNINITIALIZED = 0,
|
|
33
|
+
TC_MAP_PUBLISHING = 1,
|
|
34
|
+
TC_MAP_PUBLISHED = 2,
|
|
35
|
+
TC_MAP_INITIALIZED = 3,
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
static inline bool
|
|
39
|
+
tc_core_map_publication_busy(rb_atomic_t state)
|
|
40
|
+
{
|
|
41
|
+
return state == TC_MAP_PUBLISHING || state == TC_MAP_PUBLISHED;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
typedef struct tc_link {
|
|
45
|
+
struct tc_link *left;
|
|
46
|
+
struct tc_link *right;
|
|
47
|
+
struct tc_link *parent;
|
|
48
|
+
tc_color color;
|
|
49
|
+
} tc_link;
|
|
50
|
+
|
|
51
|
+
static ID tc_id_compare;
|
|
52
|
+
static VALUE tc_eIsolationError;
|
|
53
|
+
|
|
54
|
+
static inline tc_color
|
|
55
|
+
tc_link_color(const tc_link *link)
|
|
56
|
+
{
|
|
57
|
+
return link == NULL ? TC_BLACK : link->color;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
static inline void
|
|
61
|
+
tc_link_set_color(tc_link *link, tc_color color)
|
|
62
|
+
{
|
|
63
|
+
if (link != NULL) link->color = color;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
static tc_link *
|
|
67
|
+
tc_link_minimum(tc_link *link)
|
|
68
|
+
{
|
|
69
|
+
if (link == NULL) return NULL;
|
|
70
|
+
while (link->left != NULL) link = link->left;
|
|
71
|
+
return link;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
static tc_link *
|
|
75
|
+
tc_link_maximum(tc_link *link)
|
|
76
|
+
{
|
|
77
|
+
if (link == NULL) return NULL;
|
|
78
|
+
while (link->right != NULL) link = link->right;
|
|
79
|
+
return link;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
static tc_link *
|
|
83
|
+
tc_link_successor(tc_link *link)
|
|
84
|
+
{
|
|
85
|
+
tc_link *parent;
|
|
86
|
+
|
|
87
|
+
if (link == NULL) return NULL;
|
|
88
|
+
if (link->right != NULL) return tc_link_minimum(link->right);
|
|
89
|
+
|
|
90
|
+
parent = link->parent;
|
|
91
|
+
while (parent != NULL && link == parent->right) {
|
|
92
|
+
link = parent;
|
|
93
|
+
parent = parent->parent;
|
|
94
|
+
}
|
|
95
|
+
return parent;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
static void
|
|
99
|
+
tc_rotate_left(tc_link **root, tc_link *upper)
|
|
100
|
+
{
|
|
101
|
+
tc_link *lower = upper->right;
|
|
102
|
+
|
|
103
|
+
upper->right = lower->left;
|
|
104
|
+
if (lower->left != NULL) lower->left->parent = upper;
|
|
105
|
+
|
|
106
|
+
lower->parent = upper->parent;
|
|
107
|
+
if (upper->parent == NULL) {
|
|
108
|
+
*root = lower;
|
|
109
|
+
}
|
|
110
|
+
else if (upper == upper->parent->left) {
|
|
111
|
+
upper->parent->left = lower;
|
|
112
|
+
}
|
|
113
|
+
else {
|
|
114
|
+
upper->parent->right = lower;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
lower->left = upper;
|
|
118
|
+
upper->parent = lower;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
static void
|
|
122
|
+
tc_rotate_right(tc_link **root, tc_link *upper)
|
|
123
|
+
{
|
|
124
|
+
tc_link *lower = upper->left;
|
|
125
|
+
|
|
126
|
+
upper->left = lower->right;
|
|
127
|
+
if (lower->right != NULL) lower->right->parent = upper;
|
|
128
|
+
|
|
129
|
+
lower->parent = upper->parent;
|
|
130
|
+
if (upper->parent == NULL) {
|
|
131
|
+
*root = lower;
|
|
132
|
+
}
|
|
133
|
+
else if (upper == upper->parent->right) {
|
|
134
|
+
upper->parent->right = lower;
|
|
135
|
+
}
|
|
136
|
+
else {
|
|
137
|
+
upper->parent->left = lower;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
lower->right = upper;
|
|
141
|
+
upper->parent = lower;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
static void
|
|
145
|
+
tc_insert_fix(tc_link **root, tc_link *node)
|
|
146
|
+
{
|
|
147
|
+
while (node->parent != NULL && node->parent->color == TC_RED) {
|
|
148
|
+
tc_link *parent = node->parent;
|
|
149
|
+
tc_link *grandparent = parent->parent;
|
|
150
|
+
|
|
151
|
+
if (parent == grandparent->left) {
|
|
152
|
+
tc_link *uncle = grandparent->right;
|
|
153
|
+
if (tc_link_color(uncle) == TC_RED) {
|
|
154
|
+
parent->color = TC_BLACK;
|
|
155
|
+
uncle->color = TC_BLACK;
|
|
156
|
+
grandparent->color = TC_RED;
|
|
157
|
+
node = grandparent;
|
|
158
|
+
}
|
|
159
|
+
else {
|
|
160
|
+
if (node == parent->right) {
|
|
161
|
+
node = parent;
|
|
162
|
+
tc_rotate_left(root, node);
|
|
163
|
+
parent = node->parent;
|
|
164
|
+
grandparent = parent->parent;
|
|
165
|
+
}
|
|
166
|
+
parent->color = TC_BLACK;
|
|
167
|
+
grandparent->color = TC_RED;
|
|
168
|
+
tc_rotate_right(root, grandparent);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
else {
|
|
172
|
+
tc_link *uncle = grandparent->left;
|
|
173
|
+
if (tc_link_color(uncle) == TC_RED) {
|
|
174
|
+
parent->color = TC_BLACK;
|
|
175
|
+
uncle->color = TC_BLACK;
|
|
176
|
+
grandparent->color = TC_RED;
|
|
177
|
+
node = grandparent;
|
|
178
|
+
}
|
|
179
|
+
else {
|
|
180
|
+
if (node == parent->left) {
|
|
181
|
+
node = parent;
|
|
182
|
+
tc_rotate_right(root, node);
|
|
183
|
+
parent = node->parent;
|
|
184
|
+
grandparent = parent->parent;
|
|
185
|
+
}
|
|
186
|
+
parent->color = TC_BLACK;
|
|
187
|
+
grandparent->color = TC_RED;
|
|
188
|
+
tc_rotate_left(root, grandparent);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
(*root)->color = TC_BLACK;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
static void
|
|
197
|
+
tc_insert_link(tc_link **root, tc_link *parent, tc_link *node, bool insert_left)
|
|
198
|
+
{
|
|
199
|
+
node->left = NULL;
|
|
200
|
+
node->right = NULL;
|
|
201
|
+
node->parent = parent;
|
|
202
|
+
node->color = TC_RED;
|
|
203
|
+
|
|
204
|
+
if (parent == NULL) {
|
|
205
|
+
*root = node;
|
|
206
|
+
}
|
|
207
|
+
else if (insert_left) {
|
|
208
|
+
parent->left = node;
|
|
209
|
+
}
|
|
210
|
+
else {
|
|
211
|
+
parent->right = node;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
tc_insert_fix(root, node);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
static void
|
|
218
|
+
tc_transplant(tc_link **root, tc_link *old_link, tc_link *new_link)
|
|
219
|
+
{
|
|
220
|
+
if (old_link->parent == NULL) {
|
|
221
|
+
*root = new_link;
|
|
222
|
+
}
|
|
223
|
+
else if (old_link == old_link->parent->left) {
|
|
224
|
+
old_link->parent->left = new_link;
|
|
225
|
+
}
|
|
226
|
+
else {
|
|
227
|
+
old_link->parent->right = new_link;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
if (new_link != NULL) new_link->parent = old_link->parent;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/* `node` may be NULL. `parent` is its logical parent in that case. */
|
|
234
|
+
static void
|
|
235
|
+
tc_delete_fix(tc_link **root, tc_link *node, tc_link *parent)
|
|
236
|
+
{
|
|
237
|
+
while (node != *root && tc_link_color(node) == TC_BLACK) {
|
|
238
|
+
tc_link *sibling;
|
|
239
|
+
|
|
240
|
+
if (parent == NULL) break;
|
|
241
|
+
|
|
242
|
+
if (node == parent->left) {
|
|
243
|
+
sibling = parent->right;
|
|
244
|
+
|
|
245
|
+
if (tc_link_color(sibling) == TC_RED) {
|
|
246
|
+
sibling->color = TC_BLACK;
|
|
247
|
+
parent->color = TC_RED;
|
|
248
|
+
tc_rotate_left(root, parent);
|
|
249
|
+
sibling = parent->right;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
if (sibling == NULL) {
|
|
253
|
+
node = parent;
|
|
254
|
+
parent = node->parent;
|
|
255
|
+
continue;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
if (tc_link_color(sibling->left) == TC_BLACK &&
|
|
259
|
+
tc_link_color(sibling->right) == TC_BLACK) {
|
|
260
|
+
sibling->color = TC_RED;
|
|
261
|
+
node = parent;
|
|
262
|
+
parent = node->parent;
|
|
263
|
+
}
|
|
264
|
+
else {
|
|
265
|
+
if (tc_link_color(sibling->right) == TC_BLACK) {
|
|
266
|
+
tc_link_set_color(sibling->left, TC_BLACK);
|
|
267
|
+
sibling->color = TC_RED;
|
|
268
|
+
tc_rotate_right(root, sibling);
|
|
269
|
+
sibling = parent->right;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
sibling->color = parent->color;
|
|
273
|
+
parent->color = TC_BLACK;
|
|
274
|
+
tc_link_set_color(sibling->right, TC_BLACK);
|
|
275
|
+
tc_rotate_left(root, parent);
|
|
276
|
+
node = *root;
|
|
277
|
+
parent = NULL;
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
else {
|
|
281
|
+
sibling = parent->left;
|
|
282
|
+
|
|
283
|
+
if (tc_link_color(sibling) == TC_RED) {
|
|
284
|
+
sibling->color = TC_BLACK;
|
|
285
|
+
parent->color = TC_RED;
|
|
286
|
+
tc_rotate_right(root, parent);
|
|
287
|
+
sibling = parent->left;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
if (sibling == NULL) {
|
|
291
|
+
node = parent;
|
|
292
|
+
parent = node->parent;
|
|
293
|
+
continue;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
if (tc_link_color(sibling->right) == TC_BLACK &&
|
|
297
|
+
tc_link_color(sibling->left) == TC_BLACK) {
|
|
298
|
+
sibling->color = TC_RED;
|
|
299
|
+
node = parent;
|
|
300
|
+
parent = node->parent;
|
|
301
|
+
}
|
|
302
|
+
else {
|
|
303
|
+
if (tc_link_color(sibling->left) == TC_BLACK) {
|
|
304
|
+
tc_link_set_color(sibling->right, TC_BLACK);
|
|
305
|
+
sibling->color = TC_RED;
|
|
306
|
+
tc_rotate_left(root, sibling);
|
|
307
|
+
sibling = parent->left;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
sibling->color = parent->color;
|
|
311
|
+
parent->color = TC_BLACK;
|
|
312
|
+
tc_link_set_color(sibling->left, TC_BLACK);
|
|
313
|
+
tc_rotate_right(root, parent);
|
|
314
|
+
node = *root;
|
|
315
|
+
parent = NULL;
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
tc_link_set_color(node, TC_BLACK);
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
static void
|
|
324
|
+
tc_remove_link(tc_link **root, tc_link *removed)
|
|
325
|
+
{
|
|
326
|
+
tc_link *moved = removed;
|
|
327
|
+
tc_link *replacement;
|
|
328
|
+
tc_link *replacement_parent;
|
|
329
|
+
tc_color original_color = moved->color;
|
|
330
|
+
|
|
331
|
+
if (removed->left == NULL) {
|
|
332
|
+
replacement = removed->right;
|
|
333
|
+
replacement_parent = removed->parent;
|
|
334
|
+
tc_transplant(root, removed, removed->right);
|
|
335
|
+
}
|
|
336
|
+
else if (removed->right == NULL) {
|
|
337
|
+
replacement = removed->left;
|
|
338
|
+
replacement_parent = removed->parent;
|
|
339
|
+
tc_transplant(root, removed, removed->left);
|
|
340
|
+
}
|
|
341
|
+
else {
|
|
342
|
+
moved = tc_link_minimum(removed->right);
|
|
343
|
+
original_color = moved->color;
|
|
344
|
+
replacement = moved->right;
|
|
345
|
+
|
|
346
|
+
if (moved->parent == removed) {
|
|
347
|
+
replacement_parent = moved;
|
|
348
|
+
if (replacement != NULL) replacement->parent = moved;
|
|
349
|
+
}
|
|
350
|
+
else {
|
|
351
|
+
replacement_parent = moved->parent;
|
|
352
|
+
tc_transplant(root, moved, moved->right);
|
|
353
|
+
moved->right = removed->right;
|
|
354
|
+
moved->right->parent = moved;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
tc_transplant(root, removed, moved);
|
|
358
|
+
moved->left = removed->left;
|
|
359
|
+
moved->left->parent = moved;
|
|
360
|
+
moved->color = removed->color;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
if (original_color == TC_BLACK) {
|
|
364
|
+
tc_delete_fix(root, replacement, replacement_parent);
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
typedef struct {
|
|
369
|
+
unsigned int *guard;
|
|
370
|
+
VALUE left;
|
|
371
|
+
VALUE right;
|
|
372
|
+
} tc_call_args;
|
|
373
|
+
|
|
374
|
+
static VALUE
|
|
375
|
+
tc_compare_body(VALUE opaque)
|
|
376
|
+
{
|
|
377
|
+
tc_call_args *args = (tc_call_args *)opaque;
|
|
378
|
+
VALUE result = rb_funcall(args->left, tc_id_compare, 1, args->right);
|
|
379
|
+
return INT2NUM(rb_cmpint(result, args->left, args->right));
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
static VALUE
|
|
383
|
+
tc_call_ensure(VALUE opaque)
|
|
384
|
+
{
|
|
385
|
+
tc_call_args *args = (tc_call_args *)opaque;
|
|
386
|
+
(*args->guard)--;
|
|
387
|
+
return Qnil;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
static int
|
|
391
|
+
tc_compare_keys(unsigned int *guard, VALUE left, VALUE right)
|
|
392
|
+
{
|
|
393
|
+
tc_call_args args = {guard, left, right};
|
|
394
|
+
VALUE result;
|
|
395
|
+
|
|
396
|
+
if (FIXNUM_P(left) && FIXNUM_P(right)) {
|
|
397
|
+
intptr_t left_value = (intptr_t)left;
|
|
398
|
+
intptr_t right_value = (intptr_t)right;
|
|
399
|
+
if (left_value < right_value) return -1;
|
|
400
|
+
if (left_value > right_value) return 1;
|
|
401
|
+
return 0;
|
|
402
|
+
}
|
|
403
|
+
if (CLASS_OF(left) == rb_cString && CLASS_OF(right) == rb_cString) {
|
|
404
|
+
return rb_str_cmp(left, right);
|
|
405
|
+
}
|
|
406
|
+
if (CLASS_OF(left) == rb_cFloat && CLASS_OF(right) == rb_cFloat) {
|
|
407
|
+
double left_value = RFLOAT_VALUE(left);
|
|
408
|
+
double right_value = RFLOAT_VALUE(right);
|
|
409
|
+
|
|
410
|
+
if (!isnan(left_value) && !isnan(right_value)) {
|
|
411
|
+
if (left_value < right_value) return -1;
|
|
412
|
+
if (left_value > right_value) return 1;
|
|
413
|
+
return 0;
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
(*guard)++;
|
|
418
|
+
result = rb_ensure(tc_compare_body, (VALUE)&args, tc_call_ensure, (VALUE)&args);
|
|
419
|
+
return NUM2INT(result);
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
typedef struct tc_map_node {
|
|
423
|
+
tc_link link;
|
|
424
|
+
VALUE key;
|
|
425
|
+
VALUE value;
|
|
426
|
+
} tc_map_node;
|
|
427
|
+
|
|
428
|
+
typedef struct {
|
|
429
|
+
tc_link *root;
|
|
430
|
+
size_t size;
|
|
431
|
+
unsigned int guard;
|
|
432
|
+
uint64_t revision;
|
|
433
|
+
} tc_map;
|
|
434
|
+
|
|
435
|
+
#define TC_MAP_NODE(link_pointer) ((tc_map_node *)(link_pointer))
|
|
436
|
+
|
|
437
|
+
static void
|
|
438
|
+
tc_map_store_node_value(VALUE self, tc_map_node *node, VALUE value)
|
|
439
|
+
{
|
|
440
|
+
RB_OBJ_WRITE(self, &node->value, value);
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
static void
|
|
444
|
+
tc_map_mark_node(tc_link *link)
|
|
445
|
+
{
|
|
446
|
+
tc_map_node *node;
|
|
447
|
+
|
|
448
|
+
if (link == NULL) return;
|
|
449
|
+
node = TC_MAP_NODE(link);
|
|
450
|
+
rb_gc_mark_movable(node->key);
|
|
451
|
+
rb_gc_mark_movable(node->value);
|
|
452
|
+
tc_map_mark_node(link->left);
|
|
453
|
+
tc_map_mark_node(link->right);
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
static void
|
|
457
|
+
tc_map_validate_shareable_node(tc_link *link)
|
|
458
|
+
{
|
|
459
|
+
if (link == NULL) return;
|
|
460
|
+
tc_map_node *node = TC_MAP_NODE(link);
|
|
461
|
+
containers_check_shareable(node->key);
|
|
462
|
+
containers_check_shareable(node->value);
|
|
463
|
+
tc_map_validate_shareable_node(link->left);
|
|
464
|
+
tc_map_validate_shareable_node(link->right);
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
static void
|
|
468
|
+
tc_map_compact_node(tc_link *link)
|
|
469
|
+
{
|
|
470
|
+
tc_map_node *node;
|
|
471
|
+
|
|
472
|
+
if (link == NULL) return;
|
|
473
|
+
node = TC_MAP_NODE(link);
|
|
474
|
+
node->key = rb_gc_location(node->key);
|
|
475
|
+
node->value = rb_gc_location(node->value);
|
|
476
|
+
tc_map_compact_node(link->left);
|
|
477
|
+
tc_map_compact_node(link->right);
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
static void
|
|
481
|
+
tc_map_free_node(tc_link *link)
|
|
482
|
+
{
|
|
483
|
+
tc_map_node *node;
|
|
484
|
+
|
|
485
|
+
if (link == NULL) return;
|
|
486
|
+
tc_map_free_node(link->left);
|
|
487
|
+
tc_map_free_node(link->right);
|
|
488
|
+
node = TC_MAP_NODE(link);
|
|
489
|
+
xfree(node);
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
static tc_map_node *
|
|
493
|
+
tc_map_find(tc_map *map, VALUE key, tc_map_node **parent_out, bool *left_out)
|
|
494
|
+
{
|
|
495
|
+
tc_link *link = map->root;
|
|
496
|
+
tc_map_node *parent = NULL;
|
|
497
|
+
bool insert_left = false;
|
|
498
|
+
|
|
499
|
+
while (link != NULL) {
|
|
500
|
+
tc_map_node *node = TC_MAP_NODE(link);
|
|
501
|
+
int comparison = tc_compare_keys(&map->guard, key, node->key);
|
|
502
|
+
|
|
503
|
+
if (comparison == 0) {
|
|
504
|
+
if (parent_out != NULL) *parent_out = parent;
|
|
505
|
+
if (left_out != NULL) *left_out = insert_left;
|
|
506
|
+
return node;
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
parent = node;
|
|
510
|
+
insert_left = comparison < 0;
|
|
511
|
+
link = insert_left ? link->left : link->right;
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
if (parent_out != NULL) *parent_out = parent;
|
|
515
|
+
if (left_out != NULL) *left_out = insert_left;
|
|
516
|
+
return NULL;
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
static VALUE
|
|
520
|
+
tc_map_store_unlocked(VALUE self, tc_map *map, VALUE key, VALUE value,
|
|
521
|
+
bool honor_freeze)
|
|
522
|
+
{
|
|
523
|
+
tc_map_node *parent;
|
|
524
|
+
tc_map_node *node;
|
|
525
|
+
bool insert_left;
|
|
526
|
+
|
|
527
|
+
node = tc_map_find(map, key, &parent, &insert_left);
|
|
528
|
+
/* A comparator is arbitrary Ruby code and may freeze the receiver. The
|
|
529
|
+
* pre-operation check is therefore insufficient: honor local-container
|
|
530
|
+
* freeze semantics again after the last callback and before committing. */
|
|
531
|
+
if (honor_freeze) rb_check_frozen(self);
|
|
532
|
+
if (node != NULL) {
|
|
533
|
+
tc_map_store_node_value(self, node, value);
|
|
534
|
+
map->revision++;
|
|
535
|
+
return value;
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
node = ALLOC(tc_map_node);
|
|
539
|
+
node->key = Qnil;
|
|
540
|
+
node->value = Qnil;
|
|
541
|
+
RB_OBJ_WRITE(self, &node->key, key);
|
|
542
|
+
RB_OBJ_WRITE(self, &node->value, value);
|
|
543
|
+
tc_insert_link(&map->root, parent == NULL ? NULL : &parent->link,
|
|
544
|
+
&node->link, insert_left);
|
|
545
|
+
map->size++;
|
|
546
|
+
map->revision++;
|
|
547
|
+
return value;
|
|
548
|
+
}
|
|
549
|
+
/* ------------------------------------------------------------------------- */
|
|
550
|
+
/* UnsafeTreeMap / TreeMap / ShareableTreeMap */
|
|
551
|
+
|
|
552
|
+
/* The three maps deliberately share the exact same node layout and red-black
|
|
553
|
+
* tree operations. The policy bits decide whether a public operation takes
|
|
554
|
+
* the mutex and whether values and the container must be Ractor-shareable.
|
|
555
|
+
* Shared containers require shareable keys and values. The mutable local
|
|
556
|
+
* adapter can retain ordinary Ruby keys because its comparator callbacks never
|
|
557
|
+
* cross a Ractor boundary. */
|
|
558
|
+
typedef struct tc_core_map_waiter tc_core_map_waiter;
|
|
559
|
+
|
|
560
|
+
struct tc_core_map_waiter {
|
|
561
|
+
int read_fd;
|
|
562
|
+
int write_fd;
|
|
563
|
+
bool notified;
|
|
564
|
+
tc_core_map_waiter *next;
|
|
565
|
+
};
|
|
566
|
+
|
|
567
|
+
typedef struct {
|
|
568
|
+
tc_map map; /* Must stay first: the generic map algorithm operates on it. */
|
|
569
|
+
pthread_mutex_t lock;
|
|
570
|
+
VALUE owner_fiber;
|
|
571
|
+
VALUE owner_ruby_thread;
|
|
572
|
+
tc_core_map_waiter *waiters;
|
|
573
|
+
tc_core_map_waiter *last_waiter;
|
|
574
|
+
/* Omitted from dmark so the publisher itself is not part of the
|
|
575
|
+
* recursively shareable graph. */
|
|
576
|
+
VALUE publication_owner_fiber;
|
|
577
|
+
VALUE publication_owner_ruby_thread;
|
|
578
|
+
bool mutex_initialized;
|
|
579
|
+
bool synchronized;
|
|
580
|
+
bool shareable_container;
|
|
581
|
+
bool shareable_keys;
|
|
582
|
+
bool transaction_reserved;
|
|
583
|
+
rb_atomic_t publication_state;
|
|
584
|
+
} tc_core_map;
|
|
585
|
+
|
|
586
|
+
static const rb_data_type_t tc_unsafe_map_type;
|
|
587
|
+
static const rb_data_type_t tc_map_type;
|
|
588
|
+
static const rb_data_type_t tc_shared_map_type;
|
|
589
|
+
|
|
590
|
+
static void tc_core_map_wait_for_publication(VALUE self, tc_core_map *core);
|
|
591
|
+
|
|
592
|
+
static void
|
|
593
|
+
tc_core_map_mark(void *opaque)
|
|
594
|
+
{
|
|
595
|
+
tc_core_map *core = opaque;
|
|
596
|
+
if (core == NULL) return;
|
|
597
|
+
tc_map_mark_node(core->map.root);
|
|
598
|
+
rb_gc_mark_movable(core->owner_fiber);
|
|
599
|
+
rb_gc_mark_movable(core->owner_ruby_thread);
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
static void
|
|
603
|
+
tc_core_map_compact(void *opaque)
|
|
604
|
+
{
|
|
605
|
+
tc_core_map *core = opaque;
|
|
606
|
+
if (core == NULL) return;
|
|
607
|
+
tc_map_compact_node(core->map.root);
|
|
608
|
+
core->owner_fiber = rb_gc_location(core->owner_fiber);
|
|
609
|
+
core->owner_ruby_thread = rb_gc_location(core->owner_ruby_thread);
|
|
610
|
+
core->publication_owner_fiber =
|
|
611
|
+
rb_gc_location(core->publication_owner_fiber);
|
|
612
|
+
core->publication_owner_ruby_thread =
|
|
613
|
+
rb_gc_location(core->publication_owner_ruby_thread);
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
static void
|
|
617
|
+
tc_core_map_free(void *opaque)
|
|
618
|
+
{
|
|
619
|
+
tc_core_map *core = opaque;
|
|
620
|
+
|
|
621
|
+
if (core == NULL) return;
|
|
622
|
+
tc_map_free_node(core->map.root);
|
|
623
|
+
if (core->mutex_initialized) pthread_mutex_destroy(&core->lock);
|
|
624
|
+
xfree(core);
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
static size_t
|
|
628
|
+
tc_core_map_memsize(const void *opaque)
|
|
629
|
+
{
|
|
630
|
+
const tc_core_map *core = opaque;
|
|
631
|
+
|
|
632
|
+
return core == NULL
|
|
633
|
+
? 0
|
|
634
|
+
: sizeof(*core) + core->map.size * sizeof(tc_map_node);
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
static const rb_data_type_t tc_unsafe_map_type = {
|
|
638
|
+
.wrap_struct_name = "Farce::Internal::UnsafeTreeMap",
|
|
639
|
+
.function = {
|
|
640
|
+
.dmark = tc_core_map_mark,
|
|
641
|
+
.dfree = tc_core_map_free,
|
|
642
|
+
.dsize = tc_core_map_memsize,
|
|
643
|
+
.dcompact = tc_core_map_compact,
|
|
644
|
+
},
|
|
645
|
+
.flags = RUBY_TYPED_FREE_IMMEDIATELY,
|
|
646
|
+
};
|
|
647
|
+
|
|
648
|
+
static const rb_data_type_t tc_map_type = {
|
|
649
|
+
.wrap_struct_name = "Farce::Internal::TreeMap",
|
|
650
|
+
.function = {
|
|
651
|
+
.dmark = tc_core_map_mark,
|
|
652
|
+
.dfree = tc_core_map_free,
|
|
653
|
+
.dsize = tc_core_map_memsize,
|
|
654
|
+
.dcompact = tc_core_map_compact,
|
|
655
|
+
},
|
|
656
|
+
.flags = RUBY_TYPED_FREE_IMMEDIATELY,
|
|
657
|
+
};
|
|
658
|
+
|
|
659
|
+
static const rb_data_type_t tc_shared_map_type = {
|
|
660
|
+
.wrap_struct_name = "Farce::Internal::ShareableTreeMap",
|
|
661
|
+
.function = {
|
|
662
|
+
.dmark = tc_core_map_mark,
|
|
663
|
+
.dfree = tc_core_map_free,
|
|
664
|
+
.dsize = tc_core_map_memsize,
|
|
665
|
+
.dcompact = tc_core_map_compact,
|
|
666
|
+
},
|
|
667
|
+
.flags = RUBY_TYPED_FROZEN_SHAREABLE,
|
|
668
|
+
};
|
|
669
|
+
|
|
670
|
+
static VALUE
|
|
671
|
+
tc_core_map_allocate(VALUE klass, const rb_data_type_t *type,
|
|
672
|
+
bool synchronized, bool shareable_container,
|
|
673
|
+
bool shareable_keys)
|
|
674
|
+
{
|
|
675
|
+
tc_core_map *core;
|
|
676
|
+
VALUE self = TypedData_Make_Struct(klass, tc_core_map, type, core);
|
|
677
|
+
int error;
|
|
678
|
+
|
|
679
|
+
core->map.root = NULL;
|
|
680
|
+
core->map.size = 0;
|
|
681
|
+
core->map.guard = 0;
|
|
682
|
+
core->map.revision = 0;
|
|
683
|
+
core->transaction_reserved = false;
|
|
684
|
+
core->owner_fiber = Qnil;
|
|
685
|
+
core->owner_ruby_thread = Qnil;
|
|
686
|
+
core->waiters = NULL;
|
|
687
|
+
core->last_waiter = NULL;
|
|
688
|
+
core->publication_owner_fiber = Qnil;
|
|
689
|
+
core->publication_owner_ruby_thread = Qnil;
|
|
690
|
+
core->mutex_initialized = false;
|
|
691
|
+
core->synchronized = synchronized;
|
|
692
|
+
core->shareable_container = shareable_container;
|
|
693
|
+
core->shareable_keys = shareable_keys;
|
|
694
|
+
RUBY_ATOMIC_SET(core->publication_state, TC_MAP_UNINITIALIZED);
|
|
695
|
+
|
|
696
|
+
if (synchronized) {
|
|
697
|
+
error = pthread_mutex_init(&core->lock, NULL);
|
|
698
|
+
if (error != 0) rb_syserr_fail(error, "pthread_mutex_init");
|
|
699
|
+
core->mutex_initialized = true;
|
|
700
|
+
}
|
|
701
|
+
return self;
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
static VALUE
|
|
705
|
+
tc_unsafe_map_allocate(VALUE klass)
|
|
706
|
+
{
|
|
707
|
+
return tc_core_map_allocate(klass, &tc_unsafe_map_type, false, false, true);
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
static VALUE
|
|
711
|
+
tc_map_allocate(VALUE klass)
|
|
712
|
+
{
|
|
713
|
+
return tc_core_map_allocate(klass, &tc_map_type, true, false, true);
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
static VALUE
|
|
717
|
+
tc_mutable_map_allocate(VALUE klass)
|
|
718
|
+
{
|
|
719
|
+
return tc_core_map_allocate(klass, &tc_map_type, true, false, false);
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
static VALUE
|
|
723
|
+
tc_shareable_map_allocate(VALUE klass)
|
|
724
|
+
{
|
|
725
|
+
return tc_core_map_allocate(klass, &tc_shared_map_type, true, true, true);
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
static tc_core_map *
|
|
729
|
+
tc_core_map_get_raw(VALUE self)
|
|
730
|
+
{
|
|
731
|
+
tc_core_map *core;
|
|
732
|
+
|
|
733
|
+
if (rb_typeddata_is_kind_of(self, &tc_unsafe_map_type)) {
|
|
734
|
+
TypedData_Get_Struct(self, tc_core_map, &tc_unsafe_map_type, core);
|
|
735
|
+
return core;
|
|
736
|
+
}
|
|
737
|
+
if (rb_typeddata_is_kind_of(self, &tc_map_type)) {
|
|
738
|
+
TypedData_Get_Struct(self, tc_core_map, &tc_map_type, core);
|
|
739
|
+
return core;
|
|
740
|
+
}
|
|
741
|
+
if (rb_typeddata_is_kind_of(self, &tc_shared_map_type)) {
|
|
742
|
+
TypedData_Get_Struct(self, tc_core_map, &tc_shared_map_type, core);
|
|
743
|
+
return core;
|
|
744
|
+
}
|
|
745
|
+
rb_raise(rb_eTypeError, "wrong tree map type");
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
static void
|
|
749
|
+
tc_core_map_validate_references(VALUE self)
|
|
750
|
+
{
|
|
751
|
+
tc_core_map *core;
|
|
752
|
+
TypedData_Get_Struct(self, tc_core_map, &tc_shared_map_type, core);
|
|
753
|
+
tc_map_validate_shareable_node(core->map.root);
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
static tc_core_map *
|
|
757
|
+
tc_core_map_get(VALUE self)
|
|
758
|
+
{
|
|
759
|
+
tc_core_map *core = tc_core_map_get_raw(self);
|
|
760
|
+
|
|
761
|
+
if (core->synchronized) {
|
|
762
|
+
rb_atomic_t state = RUBY_ATOMIC_LOAD(core->publication_state);
|
|
763
|
+
|
|
764
|
+
/* A clean frozen typed object can become visible before recursive
|
|
765
|
+
* publication returns. Join every active publication, then recheck so
|
|
766
|
+
* a failed traversal cannot expose a half-published shared map. The
|
|
767
|
+
* steady-state path is one atomic load. */
|
|
768
|
+
if (tc_core_map_publication_busy(state)) {
|
|
769
|
+
tc_core_map_wait_for_publication(self, core);
|
|
770
|
+
state = RUBY_ATOMIC_LOAD(core->publication_state);
|
|
771
|
+
}
|
|
772
|
+
if (state != TC_MAP_INITIALIZED) {
|
|
773
|
+
rb_raise(rb_eRuntimeError, "uninitialized tree map");
|
|
774
|
+
}
|
|
775
|
+
return core;
|
|
776
|
+
}
|
|
777
|
+
if (RUBY_ATOMIC_LOAD(core->publication_state) != TC_MAP_INITIALIZED) {
|
|
778
|
+
rb_raise(rb_eRuntimeError, "uninitialized tree map");
|
|
779
|
+
}
|
|
780
|
+
return core;
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
typedef struct {
|
|
784
|
+
tc_core_map *core;
|
|
785
|
+
tc_core_map_waiter waiter;
|
|
786
|
+
} tc_core_map_fiber_wait;
|
|
787
|
+
|
|
788
|
+
static void
|
|
789
|
+
tc_core_map_set_fd_flags(int fd)
|
|
790
|
+
{
|
|
791
|
+
#ifdef _WIN32
|
|
792
|
+
(void)fd;
|
|
793
|
+
#else
|
|
794
|
+
int flags = fcntl(fd, F_GETFL, 0);
|
|
795
|
+
if (flags >= 0) (void)fcntl(fd, F_SETFL, flags | O_NONBLOCK);
|
|
796
|
+
flags = fcntl(fd, F_GETFD, 0);
|
|
797
|
+
if (flags >= 0) (void)fcntl(fd, F_SETFD, flags | FD_CLOEXEC);
|
|
798
|
+
#endif
|
|
799
|
+
}
|
|
800
|
+
|
|
801
|
+
/* Called with core->lock held. The waiter remains linked until its ensure
|
|
802
|
+
* cleanup so an interrupt cannot leave a dangling stack pointer behind. */
|
|
803
|
+
static void
|
|
804
|
+
tc_core_map_notify_one_locked(tc_core_map *core)
|
|
805
|
+
{
|
|
806
|
+
unsigned char byte = 1;
|
|
807
|
+
|
|
808
|
+
for (tc_core_map_waiter *waiter = core->waiters;
|
|
809
|
+
waiter != NULL;
|
|
810
|
+
waiter = waiter->next) {
|
|
811
|
+
ssize_t result;
|
|
812
|
+
|
|
813
|
+
if (waiter->notified) continue;
|
|
814
|
+
do {
|
|
815
|
+
result = write(waiter->write_fd, &byte, 1);
|
|
816
|
+
} while (result < 0 && errno == EINTR);
|
|
817
|
+
if (result == 1 ||
|
|
818
|
+
(result < 0 && (errno == EAGAIN || errno == EWOULDBLOCK))) {
|
|
819
|
+
waiter->notified = true;
|
|
820
|
+
return;
|
|
821
|
+
}
|
|
822
|
+
}
|
|
823
|
+
}
|
|
824
|
+
|
|
825
|
+
static VALUE
|
|
826
|
+
tc_core_map_fiber_wait_body(VALUE opaque)
|
|
827
|
+
{
|
|
828
|
+
tc_core_map_fiber_wait *wait = (tc_core_map_fiber_wait *)opaque;
|
|
829
|
+
return containers_wait_for_readable(wait->waiter.read_fd, Qnil) ? Qtrue : Qfalse;
|
|
830
|
+
}
|
|
831
|
+
|
|
832
|
+
static VALUE
|
|
833
|
+
tc_core_map_fiber_wait_cleanup(VALUE opaque)
|
|
834
|
+
{
|
|
835
|
+
tc_core_map_fiber_wait *wait = (tc_core_map_fiber_wait *)opaque;
|
|
836
|
+
tc_core_map_waiter *previous = NULL;
|
|
837
|
+
tc_core_map_waiter **cursor;
|
|
838
|
+
|
|
839
|
+
pthread_mutex_lock(&wait->core->lock);
|
|
840
|
+
for (cursor = &wait->core->waiters; *cursor != NULL; cursor = &(*cursor)->next) {
|
|
841
|
+
if (*cursor == &wait->waiter) {
|
|
842
|
+
*cursor = wait->waiter.next;
|
|
843
|
+
if (wait->core->last_waiter == &wait->waiter) {
|
|
844
|
+
wait->core->last_waiter = previous;
|
|
845
|
+
}
|
|
846
|
+
break;
|
|
847
|
+
}
|
|
848
|
+
previous = *cursor;
|
|
849
|
+
}
|
|
850
|
+
/* Notification makes a waiter runnable, but does not reserve ownership.
|
|
851
|
+
* Always pass the baton if a notified waiter leaves while ownerless,
|
|
852
|
+
* including cancellation after rb_io_wait returned but before the lock
|
|
853
|
+
* acquisition loop claimed ownership. */
|
|
854
|
+
if (wait->waiter.notified &&
|
|
855
|
+
NIL_P(wait->core->owner_fiber) &&
|
|
856
|
+
!tc_core_map_publication_busy(
|
|
857
|
+
RUBY_ATOMIC_LOAD(wait->core->publication_state)
|
|
858
|
+
)) {
|
|
859
|
+
tc_core_map_notify_one_locked(wait->core);
|
|
860
|
+
}
|
|
861
|
+
pthread_mutex_unlock(&wait->core->lock);
|
|
862
|
+
|
|
863
|
+
close(wait->waiter.read_fd);
|
|
864
|
+
close(wait->waiter.write_fd);
|
|
865
|
+
return Qnil;
|
|
866
|
+
}
|
|
867
|
+
|
|
868
|
+
/* Called with core->lock held and always returns with it released. */
|
|
869
|
+
static void
|
|
870
|
+
tc_core_map_wait_once(tc_core_map *core)
|
|
871
|
+
{
|
|
872
|
+
int descriptors[2];
|
|
873
|
+
|
|
874
|
+
if (pipe(descriptors) != 0) {
|
|
875
|
+
pthread_mutex_unlock(&core->lock);
|
|
876
|
+
rb_sys_fail("pipe");
|
|
877
|
+
}
|
|
878
|
+
tc_core_map_set_fd_flags(descriptors[0]);
|
|
879
|
+
tc_core_map_set_fd_flags(descriptors[1]);
|
|
880
|
+
|
|
881
|
+
tc_core_map_fiber_wait wait = {
|
|
882
|
+
.core = core,
|
|
883
|
+
.waiter = {
|
|
884
|
+
.read_fd = descriptors[0],
|
|
885
|
+
.write_fd = descriptors[1],
|
|
886
|
+
.notified = false,
|
|
887
|
+
.next = NULL,
|
|
888
|
+
},
|
|
889
|
+
};
|
|
890
|
+
|
|
891
|
+
if (core->last_waiter != NULL) core->last_waiter->next = &wait.waiter;
|
|
892
|
+
else core->waiters = &wait.waiter;
|
|
893
|
+
core->last_waiter = &wait.waiter;
|
|
894
|
+
pthread_mutex_unlock(&core->lock);
|
|
895
|
+
|
|
896
|
+
(void)rb_ensure(
|
|
897
|
+
tc_core_map_fiber_wait_body,
|
|
898
|
+
(VALUE)&wait,
|
|
899
|
+
tc_core_map_fiber_wait_cleanup,
|
|
900
|
+
(VALUE)&wait
|
|
901
|
+
);
|
|
902
|
+
}
|
|
903
|
+
|
|
904
|
+
/* CRuby needs logical Fiber ownership rather than pthread ownership: multiple
|
|
905
|
+
* scheduled Fibers can contend on the same native thread. Descriptor waits
|
|
906
|
+
* let rb_io_wait park only the current Fiber while its owner resumes. */
|
|
907
|
+
static void
|
|
908
|
+
tc_core_map_lock(VALUE self, tc_core_map *core)
|
|
909
|
+
{
|
|
910
|
+
VALUE current = rb_fiber_current();
|
|
911
|
+
VALUE current_thread = rb_thread_current();
|
|
912
|
+
VALUE scheduler = rb_fiber_scheduler_current();
|
|
913
|
+
|
|
914
|
+
(void)self;
|
|
915
|
+
for (;;) {
|
|
916
|
+
bool publication_busy;
|
|
917
|
+
|
|
918
|
+
pthread_mutex_lock(&core->lock);
|
|
919
|
+
publication_busy = tc_core_map_publication_busy(
|
|
920
|
+
RUBY_ATOMIC_LOAD(core->publication_state)
|
|
921
|
+
);
|
|
922
|
+
if (core->owner_fiber == current ||
|
|
923
|
+
(publication_busy && core->publication_owner_fiber == current)) {
|
|
924
|
+
pthread_mutex_unlock(&core->lock);
|
|
925
|
+
rb_raise(rb_eThreadError, "deadlock; recursive tree map access");
|
|
926
|
+
}
|
|
927
|
+
if (NIL_P(core->owner_fiber) && !publication_busy) {
|
|
928
|
+
core->owner_fiber = current;
|
|
929
|
+
core->owner_ruby_thread = current_thread;
|
|
930
|
+
pthread_mutex_unlock(&core->lock);
|
|
931
|
+
return;
|
|
932
|
+
}
|
|
933
|
+
if ((core->owner_ruby_thread == current_thread ||
|
|
934
|
+
(publication_busy &&
|
|
935
|
+
core->publication_owner_ruby_thread == current_thread)) &&
|
|
936
|
+
NIL_P(scheduler)) {
|
|
937
|
+
pthread_mutex_unlock(&core->lock);
|
|
938
|
+
rb_raise(
|
|
939
|
+
rb_eThreadError,
|
|
940
|
+
"deadlock; tree map is owned by another unscheduled fiber"
|
|
941
|
+
);
|
|
942
|
+
}
|
|
943
|
+
tc_core_map_wait_once(core);
|
|
944
|
+
}
|
|
945
|
+
}
|
|
946
|
+
|
|
947
|
+
static void
|
|
948
|
+
tc_core_map_unlock(tc_core_map *core)
|
|
949
|
+
{
|
|
950
|
+
rb_atomic_t state;
|
|
951
|
+
|
|
952
|
+
pthread_mutex_lock(&core->lock);
|
|
953
|
+
state = RUBY_ATOMIC_LOAD(core->publication_state);
|
|
954
|
+
if (state == TC_MAP_PUBLISHED) {
|
|
955
|
+
RUBY_ATOMIC_SET(core->publication_state, TC_MAP_INITIALIZED);
|
|
956
|
+
}
|
|
957
|
+
else if (state == TC_MAP_PUBLISHING) {
|
|
958
|
+
RUBY_ATOMIC_SET(core->publication_state, TC_MAP_UNINITIALIZED);
|
|
959
|
+
}
|
|
960
|
+
core->owner_fiber = Qnil;
|
|
961
|
+
core->owner_ruby_thread = Qnil;
|
|
962
|
+
core->publication_owner_fiber = Qnil;
|
|
963
|
+
core->publication_owner_ruby_thread = Qnil;
|
|
964
|
+
tc_core_map_notify_one_locked(core);
|
|
965
|
+
pthread_mutex_unlock(&core->lock);
|
|
966
|
+
}
|
|
967
|
+
|
|
968
|
+
typedef struct {
|
|
969
|
+
VALUE self;
|
|
970
|
+
tc_core_map *core;
|
|
971
|
+
bool acquired;
|
|
972
|
+
} tc_core_map_publication_wait;
|
|
973
|
+
|
|
974
|
+
static VALUE
|
|
975
|
+
tc_core_map_publication_wait_body(VALUE opaque)
|
|
976
|
+
{
|
|
977
|
+
tc_core_map_publication_wait *wait =
|
|
978
|
+
(tc_core_map_publication_wait *)opaque;
|
|
979
|
+
|
|
980
|
+
tc_core_map_lock(wait->self, wait->core);
|
|
981
|
+
wait->acquired = true;
|
|
982
|
+
return Qnil;
|
|
983
|
+
}
|
|
984
|
+
|
|
985
|
+
static VALUE
|
|
986
|
+
tc_core_map_publication_wait_ensure(VALUE opaque)
|
|
987
|
+
{
|
|
988
|
+
tc_core_map_publication_wait *wait =
|
|
989
|
+
(tc_core_map_publication_wait *)opaque;
|
|
990
|
+
|
|
991
|
+
if (wait->acquired) tc_core_map_unlock(wait->core);
|
|
992
|
+
return Qnil;
|
|
993
|
+
}
|
|
994
|
+
|
|
995
|
+
static void
|
|
996
|
+
tc_core_map_wait_for_publication(VALUE self, tc_core_map *core)
|
|
997
|
+
{
|
|
998
|
+
tc_core_map_publication_wait wait = {
|
|
999
|
+
.self = self,
|
|
1000
|
+
.core = core,
|
|
1001
|
+
.acquired = false,
|
|
1002
|
+
};
|
|
1003
|
+
|
|
1004
|
+
/* Retaining the publisher as logical owner makes recursive publication
|
|
1005
|
+
* callbacks fail with the ordinary ThreadError, while other Ractors and
|
|
1006
|
+
* scheduler-managed Fibers park until the outer operation unlocks. */
|
|
1007
|
+
(void)rb_ensure(
|
|
1008
|
+
tc_core_map_publication_wait_body,
|
|
1009
|
+
(VALUE)&wait,
|
|
1010
|
+
tc_core_map_publication_wait_ensure,
|
|
1011
|
+
(VALUE)&wait
|
|
1012
|
+
);
|
|
1013
|
+
}
|
|
1014
|
+
|
|
1015
|
+
typedef VALUE (*tc_core_map_operation)(VALUE opaque);
|
|
1016
|
+
|
|
1017
|
+
typedef struct {
|
|
1018
|
+
VALUE self;
|
|
1019
|
+
tc_core_map *core;
|
|
1020
|
+
VALUE key;
|
|
1021
|
+
VALUE value;
|
|
1022
|
+
bool found;
|
|
1023
|
+
} tc_core_map_arguments;
|
|
1024
|
+
|
|
1025
|
+
typedef struct {
|
|
1026
|
+
tc_core_map_operation operation;
|
|
1027
|
+
VALUE argument;
|
|
1028
|
+
} tc_core_map_call;
|
|
1029
|
+
|
|
1030
|
+
static VALUE
|
|
1031
|
+
tc_core_map_call_body(VALUE opaque)
|
|
1032
|
+
{
|
|
1033
|
+
tc_core_map_call *call = (tc_core_map_call *)opaque;
|
|
1034
|
+
return call->operation(call->argument);
|
|
1035
|
+
}
|
|
1036
|
+
|
|
1037
|
+
static VALUE
|
|
1038
|
+
tc_core_map_unlock_ensure(VALUE opaque)
|
|
1039
|
+
{
|
|
1040
|
+
tc_core_map_unlock((tc_core_map *)opaque);
|
|
1041
|
+
return Qnil;
|
|
1042
|
+
}
|
|
1043
|
+
|
|
1044
|
+
static VALUE
|
|
1045
|
+
tc_core_map_call_locked(VALUE self, tc_core_map *core,
|
|
1046
|
+
tc_core_map_operation operation,
|
|
1047
|
+
void *arguments)
|
|
1048
|
+
{
|
|
1049
|
+
tc_core_map_call call;
|
|
1050
|
+
|
|
1051
|
+
if (!core->synchronized) return operation((VALUE)arguments);
|
|
1052
|
+
tc_core_map_lock(self, core);
|
|
1053
|
+
call.operation = operation;
|
|
1054
|
+
call.argument = (VALUE)arguments;
|
|
1055
|
+
return rb_ensure(
|
|
1056
|
+
tc_core_map_call_body,
|
|
1057
|
+
(VALUE)&call,
|
|
1058
|
+
tc_core_map_unlock_ensure,
|
|
1059
|
+
(VALUE)core
|
|
1060
|
+
);
|
|
1061
|
+
}
|
|
1062
|
+
|
|
1063
|
+
static void
|
|
1064
|
+
tc_core_map_check_key(const tc_core_map *core, VALUE key)
|
|
1065
|
+
{
|
|
1066
|
+
if (core->shareable_keys && !rb_ractor_shareable_p(key)) {
|
|
1067
|
+
rb_raise(tc_eIsolationError, "key is not shareable: %" PRIsVALUE,
|
|
1068
|
+
rb_inspect(key));
|
|
1069
|
+
}
|
|
1070
|
+
}
|
|
1071
|
+
|
|
1072
|
+
static void
|
|
1073
|
+
tc_core_map_check_value(const tc_core_map *core, VALUE value)
|
|
1074
|
+
{
|
|
1075
|
+
if (core->shareable_container && !rb_ractor_shareable_p(value)) {
|
|
1076
|
+
rb_raise(tc_eIsolationError, "value is not shareable: %" PRIsVALUE,
|
|
1077
|
+
rb_inspect(value));
|
|
1078
|
+
}
|
|
1079
|
+
}
|
|
1080
|
+
|
|
1081
|
+
static void
|
|
1082
|
+
tc_core_map_check_mutation(VALUE self, const tc_core_map *core)
|
|
1083
|
+
{
|
|
1084
|
+
rb_check_frozen(self);
|
|
1085
|
+
if (core->map.guard != 0) {
|
|
1086
|
+
rb_raise(rb_eRuntimeError, "container cannot be modified during comparison");
|
|
1087
|
+
}
|
|
1088
|
+
}
|
|
1089
|
+
|
|
1090
|
+
static int
|
|
1091
|
+
tc_core_map_initialize_entry(VALUE key, VALUE value, VALUE opaque)
|
|
1092
|
+
{
|
|
1093
|
+
tc_core_map_arguments *arguments = (tc_core_map_arguments *)opaque;
|
|
1094
|
+
|
|
1095
|
+
key = containers_normalize_string_key(key);
|
|
1096
|
+
tc_core_map_check_key(arguments->core, key);
|
|
1097
|
+
tc_core_map_check_value(arguments->core, value);
|
|
1098
|
+
tc_map_store_unlocked(
|
|
1099
|
+
arguments->self,
|
|
1100
|
+
&arguments->core->map,
|
|
1101
|
+
key,
|
|
1102
|
+
value,
|
|
1103
|
+
true
|
|
1104
|
+
);
|
|
1105
|
+
return ST_CONTINUE;
|
|
1106
|
+
}
|
|
1107
|
+
|
|
1108
|
+
typedef struct {
|
|
1109
|
+
VALUE self;
|
|
1110
|
+
tc_core_map *core;
|
|
1111
|
+
} tc_core_map_publication;
|
|
1112
|
+
|
|
1113
|
+
static VALUE
|
|
1114
|
+
tc_core_map_make_shareable(VALUE opaque)
|
|
1115
|
+
{
|
|
1116
|
+
tc_core_map_publication *publication =
|
|
1117
|
+
(tc_core_map_publication *)opaque;
|
|
1118
|
+
|
|
1119
|
+
return containers_publish_native_with_references(
|
|
1120
|
+
publication->self,
|
|
1121
|
+
tc_core_map_validate_references
|
|
1122
|
+
);
|
|
1123
|
+
}
|
|
1124
|
+
|
|
1125
|
+
static VALUE
|
|
1126
|
+
tc_core_map_finish_publication(VALUE opaque)
|
|
1127
|
+
{
|
|
1128
|
+
tc_core_map_publication *publication =
|
|
1129
|
+
(tc_core_map_publication *)opaque;
|
|
1130
|
+
bool shareable = rb_ractor_shareable_p(publication->self);
|
|
1131
|
+
|
|
1132
|
+
/* rb_ractor_make_shareable can be interrupted after setting the object's
|
|
1133
|
+
* shareable flag. Publish the initialized bit whenever the object reached
|
|
1134
|
+
* that coherent state, even if the caller is about to receive an async
|
|
1135
|
+
* exception. The outer operation ensure retains the publishing state until
|
|
1136
|
+
* it clears the logical lock and wakes a waiter. */
|
|
1137
|
+
pthread_mutex_lock(&publication->core->lock);
|
|
1138
|
+
if (shareable) {
|
|
1139
|
+
RUBY_ATOMIC_SET(publication->core->publication_state, TC_MAP_PUBLISHED);
|
|
1140
|
+
}
|
|
1141
|
+
pthread_mutex_unlock(&publication->core->lock);
|
|
1142
|
+
return Qnil;
|
|
1143
|
+
}
|
|
1144
|
+
|
|
1145
|
+
static VALUE
|
|
1146
|
+
tc_core_map_publish_shareable(VALUE self, tc_core_map *core)
|
|
1147
|
+
{
|
|
1148
|
+
tc_core_map_publication publication = {
|
|
1149
|
+
.self = self,
|
|
1150
|
+
.core = core,
|
|
1151
|
+
};
|
|
1152
|
+
|
|
1153
|
+
/* Install the gate before primitive freeze can make this typed object
|
|
1154
|
+
* Ractor-visible. Move the logical owner out of dmark before recursive
|
|
1155
|
+
* sharing, while retaining it for callback reentry/deadlock detection. */
|
|
1156
|
+
pthread_mutex_lock(&core->lock);
|
|
1157
|
+
RUBY_ATOMIC_SET(core->publication_state, TC_MAP_PUBLISHING);
|
|
1158
|
+
core->publication_owner_fiber = core->owner_fiber;
|
|
1159
|
+
core->publication_owner_ruby_thread = core->owner_ruby_thread;
|
|
1160
|
+
core->owner_fiber = Qnil;
|
|
1161
|
+
core->owner_ruby_thread = Qnil;
|
|
1162
|
+
pthread_mutex_unlock(&core->lock);
|
|
1163
|
+
return rb_ensure(
|
|
1164
|
+
tc_core_map_make_shareable,
|
|
1165
|
+
(VALUE)&publication,
|
|
1166
|
+
tc_core_map_finish_publication,
|
|
1167
|
+
(VALUE)&publication
|
|
1168
|
+
);
|
|
1169
|
+
}
|
|
1170
|
+
|
|
1171
|
+
static void
|
|
1172
|
+
tc_core_map_check_initializable(VALUE self, tc_core_map *core)
|
|
1173
|
+
{
|
|
1174
|
+
rb_atomic_t state = RUBY_ATOMIC_LOAD(core->publication_state);
|
|
1175
|
+
|
|
1176
|
+
if (state == TC_MAP_INITIALIZED) {
|
|
1177
|
+
rb_raise(rb_eRuntimeError, "tree map is already initialized");
|
|
1178
|
+
}
|
|
1179
|
+
rb_check_frozen(self);
|
|
1180
|
+
|
|
1181
|
+
/* Publication marks the object busy before primitive freeze. Reject the
|
|
1182
|
+
* narrow pre-freeze window here so a competing initializer cannot enter
|
|
1183
|
+
* #to_hash while publication is already in progress. */
|
|
1184
|
+
if (RUBY_ATOMIC_LOAD(core->publication_state) != TC_MAP_UNINITIALIZED) {
|
|
1185
|
+
rb_raise(rb_eRuntimeError, "tree map is already initialized");
|
|
1186
|
+
}
|
|
1187
|
+
}
|
|
1188
|
+
|
|
1189
|
+
static VALUE
|
|
1190
|
+
tc_core_map_initialize_body(VALUE opaque)
|
|
1191
|
+
{
|
|
1192
|
+
tc_core_map_arguments *arguments = (tc_core_map_arguments *)opaque;
|
|
1193
|
+
tc_core_map *core = arguments->core;
|
|
1194
|
+
|
|
1195
|
+
/* #to_hash runs before this synchronized body and may itself initialize
|
|
1196
|
+
* the receiver. Two shared-map initializers can also pass the fast outer
|
|
1197
|
+
* check concurrently. The check under the operation lock is authoritative
|
|
1198
|
+
* and must precede every mutation. */
|
|
1199
|
+
if (RUBY_ATOMIC_LOAD(core->publication_state) != TC_MAP_UNINITIALIZED) {
|
|
1200
|
+
rb_raise(rb_eRuntimeError, "tree map is already initialized");
|
|
1201
|
+
}
|
|
1202
|
+
/* Shareable maps are frozen only after initialization succeeds. A freeze
|
|
1203
|
+
* performed before initialization or from #to_hash/comparison callbacks
|
|
1204
|
+
* must still prevent publication, just as it does for the ordinary
|
|
1205
|
+
* variants and the Ruby/JVM implementations. */
|
|
1206
|
+
rb_check_frozen(arguments->self);
|
|
1207
|
+
tc_core_map_check_mutation(arguments->self, core);
|
|
1208
|
+
tc_map_free_node(core->map.root);
|
|
1209
|
+
core->map.root = NULL;
|
|
1210
|
+
core->map.size = 0;
|
|
1211
|
+
if (!NIL_P(arguments->value)) {
|
|
1212
|
+
rb_hash_foreach(arguments->value, tc_core_map_initialize_entry, opaque);
|
|
1213
|
+
}
|
|
1214
|
+
|
|
1215
|
+
if (core->shareable_container) {
|
|
1216
|
+
/* Publication installs its logical gate before primitive freezing and
|
|
1217
|
+
* keeps that whole transition under nested ensure cleanup. */
|
|
1218
|
+
return tc_core_map_publish_shareable(arguments->self, core);
|
|
1219
|
+
}
|
|
1220
|
+
RUBY_ATOMIC_SET(core->publication_state, TC_MAP_INITIALIZED);
|
|
1221
|
+
return arguments->self;
|
|
1222
|
+
}
|
|
1223
|
+
|
|
1224
|
+
static VALUE
|
|
1225
|
+
tc_core_map_initialize(int argc, VALUE *argv, VALUE self)
|
|
1226
|
+
{
|
|
1227
|
+
tc_core_map *core = tc_core_map_get_raw(self);
|
|
1228
|
+
tc_core_map_arguments arguments = {
|
|
1229
|
+
.self = self,
|
|
1230
|
+
.core = core,
|
|
1231
|
+
.key = Qnil,
|
|
1232
|
+
.value = Qnil,
|
|
1233
|
+
};
|
|
1234
|
+
VALUE entries = Qnil;
|
|
1235
|
+
VALUE hash;
|
|
1236
|
+
|
|
1237
|
+
rb_scan_args(argc, argv, "01", &entries);
|
|
1238
|
+
tc_core_map_check_initializable(self, core);
|
|
1239
|
+
|
|
1240
|
+
if (!NIL_P(entries)) {
|
|
1241
|
+
hash = rb_check_hash_type(entries);
|
|
1242
|
+
if (NIL_P(hash)) {
|
|
1243
|
+
rb_raise(rb_eTypeError, "entries must be a Hash or respond to #to_hash");
|
|
1244
|
+
}
|
|
1245
|
+
arguments.value = hash;
|
|
1246
|
+
}
|
|
1247
|
+
|
|
1248
|
+
/* A descendant's #freeze can recursively call #initialize while the first
|
|
1249
|
+
* publication is traversing its instance variables. The shared receiver is
|
|
1250
|
+
* primitively frozen before that traversal, so reject the recursive call
|
|
1251
|
+
* here, before it can wait on the publisher's logical lock. */
|
|
1252
|
+
tc_core_map_check_initializable(self, core);
|
|
1253
|
+
|
|
1254
|
+
return tc_core_map_call_locked(
|
|
1255
|
+
self,
|
|
1256
|
+
core,
|
|
1257
|
+
tc_core_map_initialize_body,
|
|
1258
|
+
&arguments
|
|
1259
|
+
);
|
|
1260
|
+
}
|
|
1261
|
+
|
|
1262
|
+
static VALUE
|
|
1263
|
+
tc_core_map_initialize_copy(VALUE self, VALUE other)
|
|
1264
|
+
{
|
|
1265
|
+
(void)self;
|
|
1266
|
+
(void)other;
|
|
1267
|
+
rb_raise(rb_eTypeError, "tree maps cannot be copied");
|
|
1268
|
+
}
|
|
1269
|
+
|
|
1270
|
+
static VALUE
|
|
1271
|
+
tc_core_map_aref_body(VALUE opaque)
|
|
1272
|
+
{
|
|
1273
|
+
tc_core_map_arguments *arguments = (tc_core_map_arguments *)opaque;
|
|
1274
|
+
tc_map_node *node = tc_map_find(
|
|
1275
|
+
&arguments->core->map,
|
|
1276
|
+
arguments->key,
|
|
1277
|
+
NULL,
|
|
1278
|
+
NULL
|
|
1279
|
+
);
|
|
1280
|
+
|
|
1281
|
+
arguments->found = node != NULL;
|
|
1282
|
+
return node == NULL ? Qnil : node->value;
|
|
1283
|
+
}
|
|
1284
|
+
|
|
1285
|
+
static VALUE
|
|
1286
|
+
tc_core_map_aref(VALUE self, VALUE key)
|
|
1287
|
+
{
|
|
1288
|
+
tc_core_map *core = tc_core_map_get(self);
|
|
1289
|
+
|
|
1290
|
+
key = containers_normalize_string_key(key);
|
|
1291
|
+
tc_core_map_arguments arguments = {
|
|
1292
|
+
.self = self,
|
|
1293
|
+
.core = core,
|
|
1294
|
+
.key = key,
|
|
1295
|
+
.value = Qnil,
|
|
1296
|
+
};
|
|
1297
|
+
|
|
1298
|
+
tc_core_map_check_key(core, key);
|
|
1299
|
+
return tc_core_map_call_locked(self, core, tc_core_map_aref_body, &arguments);
|
|
1300
|
+
}
|
|
1301
|
+
|
|
1302
|
+
static VALUE
|
|
1303
|
+
tc_core_map_prepare_key(VALUE self, VALUE key)
|
|
1304
|
+
{
|
|
1305
|
+
tc_core_map *core = tc_core_map_get(self);
|
|
1306
|
+
key = containers_normalize_string_key(key);
|
|
1307
|
+
tc_core_map_check_key(core, key);
|
|
1308
|
+
return key;
|
|
1309
|
+
}
|
|
1310
|
+
|
|
1311
|
+
static VALUE
|
|
1312
|
+
tc_core_map_fetch(int argc, VALUE *argv, VALUE self)
|
|
1313
|
+
{
|
|
1314
|
+
VALUE key;
|
|
1315
|
+
VALUE normalized_key;
|
|
1316
|
+
VALUE default_value;
|
|
1317
|
+
VALUE result;
|
|
1318
|
+
bool default_given;
|
|
1319
|
+
bool block_given;
|
|
1320
|
+
tc_core_map *core;
|
|
1321
|
+
tc_core_map_arguments arguments;
|
|
1322
|
+
|
|
1323
|
+
rb_scan_args(argc, argv, "11", &key, &default_value);
|
|
1324
|
+
default_given = argc == 2;
|
|
1325
|
+
block_given = rb_block_given_p();
|
|
1326
|
+
if (block_given && default_given) {
|
|
1327
|
+
rb_warn("block supersedes default value argument");
|
|
1328
|
+
}
|
|
1329
|
+
|
|
1330
|
+
core = tc_core_map_get(self);
|
|
1331
|
+
normalized_key = containers_normalize_string_key(key);
|
|
1332
|
+
arguments = (tc_core_map_arguments){
|
|
1333
|
+
.self = self,
|
|
1334
|
+
.core = core,
|
|
1335
|
+
.key = normalized_key,
|
|
1336
|
+
.value = Qnil,
|
|
1337
|
+
.found = false,
|
|
1338
|
+
};
|
|
1339
|
+
tc_core_map_check_key(core, normalized_key);
|
|
1340
|
+
result = tc_core_map_call_locked(
|
|
1341
|
+
self,
|
|
1342
|
+
core,
|
|
1343
|
+
tc_core_map_aref_body,
|
|
1344
|
+
&arguments
|
|
1345
|
+
);
|
|
1346
|
+
|
|
1347
|
+
if (arguments.found) return result;
|
|
1348
|
+
if (block_given) return rb_yield(key);
|
|
1349
|
+
if (default_given) return default_value;
|
|
1350
|
+
containers_raise_key_error(self, key);
|
|
1351
|
+
return Qnil;
|
|
1352
|
+
}
|
|
1353
|
+
|
|
1354
|
+
static VALUE
|
|
1355
|
+
tc_core_map_key_p(VALUE self, VALUE key)
|
|
1356
|
+
{
|
|
1357
|
+
tc_core_map *core = tc_core_map_get(self);
|
|
1358
|
+
|
|
1359
|
+
key = containers_normalize_string_key(key);
|
|
1360
|
+
tc_core_map_arguments arguments = {
|
|
1361
|
+
.self = self,
|
|
1362
|
+
.core = core,
|
|
1363
|
+
.key = key,
|
|
1364
|
+
.value = Qnil,
|
|
1365
|
+
.found = false,
|
|
1366
|
+
};
|
|
1367
|
+
|
|
1368
|
+
tc_core_map_check_key(core, key);
|
|
1369
|
+
(void)tc_core_map_call_locked(
|
|
1370
|
+
self,
|
|
1371
|
+
core,
|
|
1372
|
+
tc_core_map_aref_body,
|
|
1373
|
+
&arguments
|
|
1374
|
+
);
|
|
1375
|
+
return arguments.found ? Qtrue : Qfalse;
|
|
1376
|
+
}
|
|
1377
|
+
|
|
1378
|
+
static VALUE
|
|
1379
|
+
tc_core_map_getkey_body(VALUE opaque)
|
|
1380
|
+
{
|
|
1381
|
+
tc_core_map_arguments *arguments = (tc_core_map_arguments *)opaque;
|
|
1382
|
+
tc_map_node *node = tc_map_find(
|
|
1383
|
+
&arguments->core->map,
|
|
1384
|
+
arguments->key,
|
|
1385
|
+
NULL,
|
|
1386
|
+
NULL
|
|
1387
|
+
);
|
|
1388
|
+
|
|
1389
|
+
return node == NULL ? Qnil : node->key;
|
|
1390
|
+
}
|
|
1391
|
+
|
|
1392
|
+
static VALUE
|
|
1393
|
+
tc_core_map_getkey(VALUE self, VALUE key)
|
|
1394
|
+
{
|
|
1395
|
+
tc_core_map *core = tc_core_map_get(self);
|
|
1396
|
+
|
|
1397
|
+
key = containers_normalize_string_key(key);
|
|
1398
|
+
tc_core_map_arguments arguments = {
|
|
1399
|
+
.self = self,
|
|
1400
|
+
.core = core,
|
|
1401
|
+
.key = key,
|
|
1402
|
+
.value = Qnil,
|
|
1403
|
+
};
|
|
1404
|
+
|
|
1405
|
+
tc_core_map_check_key(core, key);
|
|
1406
|
+
return tc_core_map_call_locked(
|
|
1407
|
+
self,
|
|
1408
|
+
core,
|
|
1409
|
+
tc_core_map_getkey_body,
|
|
1410
|
+
&arguments
|
|
1411
|
+
);
|
|
1412
|
+
}
|
|
1413
|
+
|
|
1414
|
+
static VALUE
|
|
1415
|
+
tc_core_map_store_body(VALUE opaque)
|
|
1416
|
+
{
|
|
1417
|
+
tc_core_map_arguments *arguments = (tc_core_map_arguments *)opaque;
|
|
1418
|
+
|
|
1419
|
+
tc_core_map_check_mutation(arguments->self, arguments->core);
|
|
1420
|
+
return tc_map_store_unlocked(
|
|
1421
|
+
arguments->self,
|
|
1422
|
+
&arguments->core->map,
|
|
1423
|
+
arguments->key,
|
|
1424
|
+
arguments->value,
|
|
1425
|
+
true
|
|
1426
|
+
);
|
|
1427
|
+
}
|
|
1428
|
+
|
|
1429
|
+
static VALUE
|
|
1430
|
+
tc_core_map_store(VALUE self, VALUE key, VALUE value)
|
|
1431
|
+
{
|
|
1432
|
+
tc_core_map *core = tc_core_map_get(self);
|
|
1433
|
+
|
|
1434
|
+
key = containers_normalize_string_key(key);
|
|
1435
|
+
tc_core_map_arguments arguments = {
|
|
1436
|
+
.self = self,
|
|
1437
|
+
.core = core,
|
|
1438
|
+
.key = key,
|
|
1439
|
+
.value = value,
|
|
1440
|
+
};
|
|
1441
|
+
|
|
1442
|
+
tc_core_map_check_key(core, key);
|
|
1443
|
+
tc_core_map_check_value(core, value);
|
|
1444
|
+
return tc_core_map_call_locked(self, core, tc_core_map_store_body, &arguments);
|
|
1445
|
+
}
|
|
1446
|
+
|
|
1447
|
+
static VALUE
|
|
1448
|
+
tc_core_map_delete_body(VALUE opaque)
|
|
1449
|
+
{
|
|
1450
|
+
tc_core_map_arguments *arguments = (tc_core_map_arguments *)opaque;
|
|
1451
|
+
tc_map *map = &arguments->core->map;
|
|
1452
|
+
tc_map_node *node;
|
|
1453
|
+
VALUE value;
|
|
1454
|
+
|
|
1455
|
+
tc_core_map_check_mutation(arguments->self, arguments->core);
|
|
1456
|
+
node = tc_map_find(map, arguments->key, NULL, NULL);
|
|
1457
|
+
if (node == NULL) return Qnil;
|
|
1458
|
+
|
|
1459
|
+
tc_core_map_check_mutation(arguments->self, arguments->core);
|
|
1460
|
+
value = node->value;
|
|
1461
|
+
tc_remove_link(&map->root, &node->link);
|
|
1462
|
+
map->size--;
|
|
1463
|
+
map->revision++;
|
|
1464
|
+
xfree(node);
|
|
1465
|
+
return value;
|
|
1466
|
+
}
|
|
1467
|
+
|
|
1468
|
+
static VALUE
|
|
1469
|
+
tc_core_map_delete(VALUE self, VALUE key)
|
|
1470
|
+
{
|
|
1471
|
+
tc_core_map *core = tc_core_map_get(self);
|
|
1472
|
+
|
|
1473
|
+
key = containers_normalize_string_key(key);
|
|
1474
|
+
tc_core_map_arguments arguments = {
|
|
1475
|
+
.self = self,
|
|
1476
|
+
.core = core,
|
|
1477
|
+
.key = key,
|
|
1478
|
+
.value = Qnil,
|
|
1479
|
+
};
|
|
1480
|
+
|
|
1481
|
+
tc_core_map_check_key(core, key);
|
|
1482
|
+
return tc_core_map_call_locked(self, core, tc_core_map_delete_body, &arguments);
|
|
1483
|
+
}
|
|
1484
|
+
|
|
1485
|
+
static VALUE
|
|
1486
|
+
tc_core_map_first_key_body(VALUE opaque)
|
|
1487
|
+
{
|
|
1488
|
+
tc_core_map_arguments *arguments = (tc_core_map_arguments *)opaque;
|
|
1489
|
+
tc_link *first = tc_link_minimum(arguments->core->map.root);
|
|
1490
|
+
|
|
1491
|
+
return first == NULL ? Qnil : TC_MAP_NODE(first)->key;
|
|
1492
|
+
}
|
|
1493
|
+
|
|
1494
|
+
static VALUE
|
|
1495
|
+
tc_core_map_first_key(VALUE self)
|
|
1496
|
+
{
|
|
1497
|
+
tc_core_map *core = tc_core_map_get(self);
|
|
1498
|
+
tc_core_map_arguments arguments = {.self = self, .core = core};
|
|
1499
|
+
|
|
1500
|
+
return tc_core_map_call_locked(self, core, tc_core_map_first_key_body, &arguments);
|
|
1501
|
+
}
|
|
1502
|
+
|
|
1503
|
+
static VALUE
|
|
1504
|
+
tc_core_map_last_key_body(VALUE opaque)
|
|
1505
|
+
{
|
|
1506
|
+
tc_core_map_arguments *arguments = (tc_core_map_arguments *)opaque;
|
|
1507
|
+
tc_link *last = tc_link_maximum(arguments->core->map.root);
|
|
1508
|
+
|
|
1509
|
+
return last == NULL ? Qnil : TC_MAP_NODE(last)->key;
|
|
1510
|
+
}
|
|
1511
|
+
|
|
1512
|
+
static VALUE
|
|
1513
|
+
tc_core_map_last_key(VALUE self)
|
|
1514
|
+
{
|
|
1515
|
+
tc_core_map *core = tc_core_map_get(self);
|
|
1516
|
+
tc_core_map_arguments arguments = {.self = self, .core = core};
|
|
1517
|
+
|
|
1518
|
+
return tc_core_map_call_locked(self, core, tc_core_map_last_key_body, &arguments);
|
|
1519
|
+
}
|
|
1520
|
+
|
|
1521
|
+
static VALUE
|
|
1522
|
+
tc_core_map_shift_body(VALUE opaque)
|
|
1523
|
+
{
|
|
1524
|
+
tc_core_map_arguments *arguments = (tc_core_map_arguments *)opaque;
|
|
1525
|
+
tc_map *map = &arguments->core->map;
|
|
1526
|
+
tc_link *first;
|
|
1527
|
+
tc_map_node *node;
|
|
1528
|
+
VALUE pair;
|
|
1529
|
+
|
|
1530
|
+
tc_core_map_check_mutation(arguments->self, arguments->core);
|
|
1531
|
+
first = tc_link_minimum(map->root);
|
|
1532
|
+
if (first == NULL) return Qnil;
|
|
1533
|
+
|
|
1534
|
+
node = TC_MAP_NODE(first);
|
|
1535
|
+
pair = rb_assoc_new(node->key, node->value);
|
|
1536
|
+
tc_remove_link(&map->root, first);
|
|
1537
|
+
map->size--;
|
|
1538
|
+
map->revision++;
|
|
1539
|
+
xfree(node);
|
|
1540
|
+
return pair;
|
|
1541
|
+
}
|
|
1542
|
+
|
|
1543
|
+
static VALUE
|
|
1544
|
+
tc_core_map_shift(VALUE self)
|
|
1545
|
+
{
|
|
1546
|
+
tc_core_map *core = tc_core_map_get(self);
|
|
1547
|
+
tc_core_map_arguments arguments = {.self = self, .core = core};
|
|
1548
|
+
|
|
1549
|
+
return tc_core_map_call_locked(self, core, tc_core_map_shift_body, &arguments);
|
|
1550
|
+
}
|
|
1551
|
+
|
|
1552
|
+
static VALUE
|
|
1553
|
+
tc_core_map_pop_body(VALUE opaque)
|
|
1554
|
+
{
|
|
1555
|
+
tc_core_map_arguments *arguments = (tc_core_map_arguments *)opaque;
|
|
1556
|
+
tc_map *map = &arguments->core->map;
|
|
1557
|
+
tc_link *last;
|
|
1558
|
+
tc_map_node *node;
|
|
1559
|
+
VALUE pair;
|
|
1560
|
+
|
|
1561
|
+
tc_core_map_check_mutation(arguments->self, arguments->core);
|
|
1562
|
+
last = tc_link_maximum(map->root);
|
|
1563
|
+
if (last == NULL) return Qnil;
|
|
1564
|
+
|
|
1565
|
+
node = TC_MAP_NODE(last);
|
|
1566
|
+
pair = rb_assoc_new(node->key, node->value);
|
|
1567
|
+
tc_remove_link(&map->root, last);
|
|
1568
|
+
map->size--;
|
|
1569
|
+
map->revision++;
|
|
1570
|
+
xfree(node);
|
|
1571
|
+
return pair;
|
|
1572
|
+
}
|
|
1573
|
+
|
|
1574
|
+
static VALUE
|
|
1575
|
+
tc_core_map_pop(VALUE self)
|
|
1576
|
+
{
|
|
1577
|
+
tc_core_map *core = tc_core_map_get(self);
|
|
1578
|
+
tc_core_map_arguments arguments = {.self = self, .core = core};
|
|
1579
|
+
|
|
1580
|
+
return tc_core_map_call_locked(self, core, tc_core_map_pop_body, &arguments);
|
|
1581
|
+
}
|
|
1582
|
+
|
|
1583
|
+
static VALUE
|
|
1584
|
+
tc_core_map_size_body(VALUE opaque)
|
|
1585
|
+
{
|
|
1586
|
+
tc_core_map_arguments *arguments = (tc_core_map_arguments *)opaque;
|
|
1587
|
+
return SIZET2NUM(arguments->core->map.size);
|
|
1588
|
+
}
|
|
1589
|
+
|
|
1590
|
+
static VALUE
|
|
1591
|
+
tc_core_map_size(VALUE self)
|
|
1592
|
+
{
|
|
1593
|
+
tc_core_map *core = tc_core_map_get(self);
|
|
1594
|
+
tc_core_map_arguments arguments = {.self = self, .core = core};
|
|
1595
|
+
|
|
1596
|
+
return tc_core_map_call_locked(self, core, tc_core_map_size_body, &arguments);
|
|
1597
|
+
}
|
|
1598
|
+
|
|
1599
|
+
static VALUE
|
|
1600
|
+
tc_core_map_enumerator_size(VALUE self, VALUE arguments, VALUE enumerator)
|
|
1601
|
+
{
|
|
1602
|
+
(void)arguments;
|
|
1603
|
+
(void)enumerator;
|
|
1604
|
+
return tc_core_map_size(self);
|
|
1605
|
+
}
|
|
1606
|
+
|
|
1607
|
+
static void
|
|
1608
|
+
tc_core_map_snapshot_node(tc_link *link, VALUE entries)
|
|
1609
|
+
{
|
|
1610
|
+
tc_map_node *node;
|
|
1611
|
+
|
|
1612
|
+
if (link == NULL) return;
|
|
1613
|
+
tc_core_map_snapshot_node(link->left, entries);
|
|
1614
|
+
node = TC_MAP_NODE(link);
|
|
1615
|
+
rb_ary_push(entries, node->key);
|
|
1616
|
+
rb_ary_push(entries, node->value);
|
|
1617
|
+
tc_core_map_snapshot_node(link->right, entries);
|
|
1618
|
+
}
|
|
1619
|
+
|
|
1620
|
+
static VALUE
|
|
1621
|
+
tc_core_map_snapshot_body(VALUE opaque)
|
|
1622
|
+
{
|
|
1623
|
+
tc_core_map_arguments *arguments = (tc_core_map_arguments *)opaque;
|
|
1624
|
+
tc_map *map = &arguments->core->map;
|
|
1625
|
+
VALUE entries = rb_ary_new_capa((long)(map->size * 2));
|
|
1626
|
+
|
|
1627
|
+
tc_core_map_snapshot_node(map->root, entries);
|
|
1628
|
+
return entries;
|
|
1629
|
+
}
|
|
1630
|
+
|
|
1631
|
+
typedef struct {
|
|
1632
|
+
tc_core_map *core;
|
|
1633
|
+
tc_map_node *node;
|
|
1634
|
+
uint64_t revision;
|
|
1635
|
+
VALUE last_key;
|
|
1636
|
+
bool started;
|
|
1637
|
+
} tc_core_map_cursor;
|
|
1638
|
+
|
|
1639
|
+
static tc_map_node *
|
|
1640
|
+
tc_map_find_greater(tc_map *map, VALUE key)
|
|
1641
|
+
{
|
|
1642
|
+
tc_link *link = map->root;
|
|
1643
|
+
tc_map_node *candidate = NULL;
|
|
1644
|
+
|
|
1645
|
+
while (link != NULL) {
|
|
1646
|
+
tc_map_node *node = TC_MAP_NODE(link);
|
|
1647
|
+
int comparison = tc_compare_keys(&map->guard, key, node->key);
|
|
1648
|
+
|
|
1649
|
+
if (comparison < 0) {
|
|
1650
|
+
candidate = node;
|
|
1651
|
+
link = link->left;
|
|
1652
|
+
}
|
|
1653
|
+
else {
|
|
1654
|
+
link = link->right;
|
|
1655
|
+
}
|
|
1656
|
+
}
|
|
1657
|
+
return candidate;
|
|
1658
|
+
}
|
|
1659
|
+
|
|
1660
|
+
static VALUE
|
|
1661
|
+
tc_core_map_cursor_step(VALUE opaque)
|
|
1662
|
+
{
|
|
1663
|
+
tc_core_map_cursor *cursor = (tc_core_map_cursor *)opaque;
|
|
1664
|
+
tc_map *map = &cursor->core->map;
|
|
1665
|
+
tc_map_node *node;
|
|
1666
|
+
|
|
1667
|
+
if (!cursor->started) {
|
|
1668
|
+
tc_link *first = tc_link_minimum(map->root);
|
|
1669
|
+
node = first == NULL ? NULL : TC_MAP_NODE(first);
|
|
1670
|
+
}
|
|
1671
|
+
else if (cursor->revision == map->revision) {
|
|
1672
|
+
tc_link *next = tc_link_successor(&cursor->node->link);
|
|
1673
|
+
node = next == NULL ? NULL : TC_MAP_NODE(next);
|
|
1674
|
+
}
|
|
1675
|
+
else {
|
|
1676
|
+
node = tc_map_find_greater(map, cursor->last_key);
|
|
1677
|
+
}
|
|
1678
|
+
|
|
1679
|
+
cursor->started = true;
|
|
1680
|
+
cursor->node = node;
|
|
1681
|
+
cursor->revision = map->revision;
|
|
1682
|
+
if (node == NULL) return Qnil;
|
|
1683
|
+
cursor->last_key = node->key;
|
|
1684
|
+
return rb_assoc_new(node->key, node->value);
|
|
1685
|
+
}
|
|
1686
|
+
|
|
1687
|
+
static VALUE
|
|
1688
|
+
tc_core_map_each_live(VALUE self)
|
|
1689
|
+
{
|
|
1690
|
+
tc_core_map *core = tc_core_map_get(self);
|
|
1691
|
+
tc_core_map_cursor cursor = {
|
|
1692
|
+
.core = core,
|
|
1693
|
+
.node = NULL,
|
|
1694
|
+
.revision = 0,
|
|
1695
|
+
.last_key = Qnil,
|
|
1696
|
+
.started = false,
|
|
1697
|
+
};
|
|
1698
|
+
VALUE pair;
|
|
1699
|
+
|
|
1700
|
+
RETURN_SIZED_ENUMERATOR(self, 0, NULL, tc_core_map_enumerator_size);
|
|
1701
|
+
while (!NIL_P(pair = tc_core_map_call_locked(
|
|
1702
|
+
self,
|
|
1703
|
+
core,
|
|
1704
|
+
tc_core_map_cursor_step,
|
|
1705
|
+
&cursor
|
|
1706
|
+
))) {
|
|
1707
|
+
rb_yield(pair);
|
|
1708
|
+
}
|
|
1709
|
+
RB_GC_GUARD(cursor.last_key);
|
|
1710
|
+
return self;
|
|
1711
|
+
}
|
|
1712
|
+
|
|
1713
|
+
static VALUE
|
|
1714
|
+
tc_core_map_each(VALUE self)
|
|
1715
|
+
{
|
|
1716
|
+
VALUE entries;
|
|
1717
|
+
long entry_count;
|
|
1718
|
+
long index;
|
|
1719
|
+
tc_core_map *core;
|
|
1720
|
+
tc_core_map_arguments arguments;
|
|
1721
|
+
|
|
1722
|
+
RETURN_SIZED_ENUMERATOR(self, 0, NULL, tc_core_map_enumerator_size);
|
|
1723
|
+
core = tc_core_map_get(self);
|
|
1724
|
+
arguments = (tc_core_map_arguments){.self = self, .core = core};
|
|
1725
|
+
entries = tc_core_map_call_locked(
|
|
1726
|
+
self,
|
|
1727
|
+
core,
|
|
1728
|
+
tc_core_map_snapshot_body,
|
|
1729
|
+
&arguments
|
|
1730
|
+
);
|
|
1731
|
+
entry_count = RARRAY_LEN(entries);
|
|
1732
|
+
for (index = 0; index < entry_count; index += 2) {
|
|
1733
|
+
rb_yield(rb_assoc_new(
|
|
1734
|
+
RARRAY_AREF(entries, index),
|
|
1735
|
+
RARRAY_AREF(entries, index + 1)
|
|
1736
|
+
));
|
|
1737
|
+
}
|
|
1738
|
+
RB_GC_GUARD(entries);
|
|
1739
|
+
return self;
|
|
1740
|
+
}
|
|
1741
|
+
|
|
1742
|
+
static VALUE
|
|
1743
|
+
tc_core_map_empty_body(VALUE opaque)
|
|
1744
|
+
{
|
|
1745
|
+
tc_core_map_arguments *arguments = (tc_core_map_arguments *)opaque;
|
|
1746
|
+
return arguments->core->map.size == 0 ? Qtrue : Qfalse;
|
|
1747
|
+
}
|
|
1748
|
+
|
|
1749
|
+
static VALUE
|
|
1750
|
+
tc_core_map_empty_p(VALUE self)
|
|
1751
|
+
{
|
|
1752
|
+
tc_core_map *core = tc_core_map_get(self);
|
|
1753
|
+
tc_core_map_arguments arguments = {.self = self, .core = core};
|
|
1754
|
+
|
|
1755
|
+
return tc_core_map_call_locked(self, core, tc_core_map_empty_body, &arguments);
|
|
1756
|
+
}
|
|
1757
|
+
|
|
1758
|
+
static VALUE
|
|
1759
|
+
tc_core_map_clear_body(VALUE opaque)
|
|
1760
|
+
{
|
|
1761
|
+
tc_core_map_arguments *arguments = (tc_core_map_arguments *)opaque;
|
|
1762
|
+
tc_map *map = &arguments->core->map;
|
|
1763
|
+
|
|
1764
|
+
tc_core_map_check_mutation(arguments->self, arguments->core);
|
|
1765
|
+
tc_map_free_node(map->root);
|
|
1766
|
+
map->root = NULL;
|
|
1767
|
+
map->size = 0;
|
|
1768
|
+
map->revision++;
|
|
1769
|
+
return arguments->self;
|
|
1770
|
+
}
|
|
1771
|
+
|
|
1772
|
+
static VALUE
|
|
1773
|
+
tc_core_map_clear(VALUE self)
|
|
1774
|
+
{
|
|
1775
|
+
tc_core_map *core = tc_core_map_get(self);
|
|
1776
|
+
tc_core_map_arguments arguments = {.self = self, .core = core};
|
|
1777
|
+
|
|
1778
|
+
return tc_core_map_call_locked(self, core, tc_core_map_clear_body, &arguments);
|
|
1779
|
+
}
|
|
1780
|
+
|
|
1781
|
+
static bool
|
|
1782
|
+
tc_transaction_valid(farce_transaction_entry_t *entry)
|
|
1783
|
+
{
|
|
1784
|
+
tc_core_map *source = entry->source_data;
|
|
1785
|
+
return NIL_P(source->owner_fiber) &&
|
|
1786
|
+
!tc_core_map_publication_busy(RUBY_ATOMIC_LOAD(source->publication_state)) &&
|
|
1787
|
+
source->map.revision == entry->version;
|
|
1788
|
+
}
|
|
1789
|
+
|
|
1790
|
+
static void
|
|
1791
|
+
tc_transaction_apply(farce_transaction_entry_t *entry)
|
|
1792
|
+
{
|
|
1793
|
+
tc_core_map *source = entry->source_data;
|
|
1794
|
+
tc_core_map *working = entry->working_data;
|
|
1795
|
+
tc_link *root = source->map.root;
|
|
1796
|
+
size_t size = source->map.size;
|
|
1797
|
+
source->map.root = working->map.root;
|
|
1798
|
+
source->map.size = working->map.size;
|
|
1799
|
+
source->map.revision++;
|
|
1800
|
+
working->map.root = root;
|
|
1801
|
+
working->map.size = size;
|
|
1802
|
+
working->map.revision++;
|
|
1803
|
+
}
|
|
1804
|
+
|
|
1805
|
+
static void
|
|
1806
|
+
tc_transaction_notify(farce_transaction_entry_t *entry)
|
|
1807
|
+
{
|
|
1808
|
+
tc_core_map_notify_one_locked(entry->source_data);
|
|
1809
|
+
}
|
|
1810
|
+
|
|
1811
|
+
static void
|
|
1812
|
+
tc_transaction_reserve(farce_transaction_entry_t *entry, VALUE fiber, VALUE thread)
|
|
1813
|
+
{
|
|
1814
|
+
tc_core_map *core = entry->source_data;
|
|
1815
|
+
core->transaction_reserved = true;
|
|
1816
|
+
core->owner_fiber = fiber;
|
|
1817
|
+
core->owner_ruby_thread = thread;
|
|
1818
|
+
}
|
|
1819
|
+
|
|
1820
|
+
static void
|
|
1821
|
+
tc_transaction_release(farce_transaction_entry_t *entry)
|
|
1822
|
+
{
|
|
1823
|
+
tc_core_map *core = entry->source_data;
|
|
1824
|
+
core->transaction_reserved = false;
|
|
1825
|
+
core->owner_fiber = Qnil;
|
|
1826
|
+
core->owner_ruby_thread = Qnil;
|
|
1827
|
+
tc_core_map_notify_one_locked(core);
|
|
1828
|
+
}
|
|
1829
|
+
|
|
1830
|
+
static VALUE
|
|
1831
|
+
tc_core_map_freeze(VALUE self)
|
|
1832
|
+
{
|
|
1833
|
+
tc_core_map *core = tc_core_map_get_raw(self);
|
|
1834
|
+
if (!core->mutex_initialized) return rb_obj_freeze(self);
|
|
1835
|
+
pthread_mutex_lock(&core->lock);
|
|
1836
|
+
if (core->transaction_reserved) {
|
|
1837
|
+
pthread_mutex_unlock(&core->lock);
|
|
1838
|
+
tc_core_map_lock(self, core);
|
|
1839
|
+
rb_obj_freeze(self);
|
|
1840
|
+
tc_core_map_unlock(core);
|
|
1841
|
+
}
|
|
1842
|
+
else {
|
|
1843
|
+
rb_obj_freeze(self);
|
|
1844
|
+
pthread_mutex_unlock(&core->lock);
|
|
1845
|
+
}
|
|
1846
|
+
return self;
|
|
1847
|
+
}
|
|
1848
|
+
|
|
1849
|
+
static const farce_transaction_ops_t tc_transaction_ops = {
|
|
1850
|
+
tc_transaction_valid, tc_transaction_apply, tc_transaction_notify,
|
|
1851
|
+
tc_transaction_reserve, tc_transaction_release,
|
|
1852
|
+
};
|
|
1853
|
+
|
|
1854
|
+
typedef struct {
|
|
1855
|
+
tc_core_map_arguments arguments;
|
|
1856
|
+
farce_transaction_entry_t *entry;
|
|
1857
|
+
} tc_transaction_snapshot_arguments;
|
|
1858
|
+
|
|
1859
|
+
static VALUE
|
|
1860
|
+
tc_transaction_snapshot_body(VALUE opaque)
|
|
1861
|
+
{
|
|
1862
|
+
tc_transaction_snapshot_arguments *snapshot = (void *)opaque;
|
|
1863
|
+
snapshot->entry->version = snapshot->arguments.core->map.revision;
|
|
1864
|
+
return tc_core_map_snapshot_body((VALUE)&snapshot->arguments);
|
|
1865
|
+
}
|
|
1866
|
+
|
|
1867
|
+
static VALUE
|
|
1868
|
+
tc_transaction_snapshot(VALUE self)
|
|
1869
|
+
{
|
|
1870
|
+
tc_core_map *source = tc_core_map_get(self);
|
|
1871
|
+
if (!source->synchronized) {
|
|
1872
|
+
rb_raise(rb_eTypeError, "unsafe tree maps do not support transactions");
|
|
1873
|
+
}
|
|
1874
|
+
VALUE working = rb_class_new_instance(0, NULL, rb_obj_class(self));
|
|
1875
|
+
tc_core_map *copy = tc_core_map_get(working);
|
|
1876
|
+
farce_transaction_entry_t *entry;
|
|
1877
|
+
VALUE result = farce_transaction_entry_new(
|
|
1878
|
+
self, working, source, copy, &source->lock, &tc_transaction_ops, &entry
|
|
1879
|
+
);
|
|
1880
|
+
tc_transaction_snapshot_arguments snapshot = {
|
|
1881
|
+
.arguments = {.self = self, .core = source}, .entry = entry,
|
|
1882
|
+
};
|
|
1883
|
+
VALUE pairs = tc_core_map_call_locked(self, source, tc_transaction_snapshot_body, &snapshot);
|
|
1884
|
+
for (long i = 0; i < RARRAY_LEN(pairs); i += 2) {
|
|
1885
|
+
tc_core_map_store(working, RARRAY_AREF(pairs, i), RARRAY_AREF(pairs, i + 1));
|
|
1886
|
+
}
|
|
1887
|
+
RB_GC_GUARD(pairs);
|
|
1888
|
+
return result;
|
|
1889
|
+
}
|
|
1890
|
+
|
|
1891
|
+
static void
|
|
1892
|
+
tc_core_map_define_methods(VALUE klass)
|
|
1893
|
+
{
|
|
1894
|
+
rb_define_method(klass, "freeze", tc_core_map_freeze, 0);
|
|
1895
|
+
rb_define_method(klass, "transaction_snapshot", tc_transaction_snapshot, 0);
|
|
1896
|
+
rb_define_method(klass, "initialize", tc_core_map_initialize, -1);
|
|
1897
|
+
rb_define_method(klass, "initialize_copy", tc_core_map_initialize_copy, 1);
|
|
1898
|
+
rb_define_method(klass, "[]", tc_core_map_aref, 1);
|
|
1899
|
+
rb_define_method(klass, "prepare_key", tc_core_map_prepare_key, 1);
|
|
1900
|
+
rb_define_method(klass, "[]=", tc_core_map_store, 2);
|
|
1901
|
+
rb_define_method(klass, "fetch", tc_core_map_fetch, -1);
|
|
1902
|
+
rb_define_method(klass, "key?", tc_core_map_key_p, 1);
|
|
1903
|
+
rb_define_method(klass, "getkey", tc_core_map_getkey, 1);
|
|
1904
|
+
rb_define_method(klass, "each", tc_core_map_each, 0);
|
|
1905
|
+
rb_define_method(klass, "each_live", tc_core_map_each_live, 0);
|
|
1906
|
+
rb_define_method(klass, "delete", tc_core_map_delete, 1);
|
|
1907
|
+
rb_define_method(klass, "first_key", tc_core_map_first_key, 0);
|
|
1908
|
+
rb_define_method(klass, "last_key", tc_core_map_last_key, 0);
|
|
1909
|
+
rb_define_method(klass, "shift", tc_core_map_shift, 0);
|
|
1910
|
+
rb_define_method(klass, "pop", tc_core_map_pop, 0);
|
|
1911
|
+
rb_define_method(klass, "size", tc_core_map_size, 0);
|
|
1912
|
+
rb_define_method(klass, "length", tc_core_map_size, 0);
|
|
1913
|
+
rb_define_method(klass, "empty?", tc_core_map_empty_p, 0);
|
|
1914
|
+
rb_define_method(klass, "clear", tc_core_map_clear, 0);
|
|
1915
|
+
}
|
|
1916
|
+
void
|
|
1917
|
+
containers_init_tree_maps(VALUE internal)
|
|
1918
|
+
{
|
|
1919
|
+
VALUE unsafe_map_class;
|
|
1920
|
+
VALUE map_class;
|
|
1921
|
+
VALUE mutable_map_class;
|
|
1922
|
+
VALUE shareable_map_class;
|
|
1923
|
+
VALUE ractor;
|
|
1924
|
+
ID ractor_id = rb_intern("Ractor");
|
|
1925
|
+
|
|
1926
|
+
tc_id_compare = rb_intern("<=>");
|
|
1927
|
+
ractor = rb_const_get(rb_cObject, ractor_id);
|
|
1928
|
+
tc_eIsolationError = rb_const_get(ractor, rb_intern("IsolationError"));
|
|
1929
|
+
|
|
1930
|
+
unsafe_map_class = rb_define_class_under(internal, "UnsafeTreeMap", rb_cObject);
|
|
1931
|
+
rb_define_alloc_func(unsafe_map_class, tc_unsafe_map_allocate);
|
|
1932
|
+
tc_core_map_define_methods(unsafe_map_class);
|
|
1933
|
+
|
|
1934
|
+
map_class = rb_define_class_under(internal, "TreeMap", rb_cObject);
|
|
1935
|
+
rb_define_alloc_func(map_class, tc_map_allocate);
|
|
1936
|
+
tc_core_map_define_methods(map_class);
|
|
1937
|
+
|
|
1938
|
+
mutable_map_class = rb_define_class_under(
|
|
1939
|
+
internal,
|
|
1940
|
+
"MutableTreeMap",
|
|
1941
|
+
rb_cObject
|
|
1942
|
+
);
|
|
1943
|
+
rb_define_alloc_func(mutable_map_class, tc_mutable_map_allocate);
|
|
1944
|
+
tc_core_map_define_methods(mutable_map_class);
|
|
1945
|
+
|
|
1946
|
+
shareable_map_class = rb_define_class_under(
|
|
1947
|
+
internal,
|
|
1948
|
+
"ShareableTreeMap",
|
|
1949
|
+
rb_cObject
|
|
1950
|
+
);
|
|
1951
|
+
rb_define_alloc_func(shareable_map_class, tc_shareable_map_allocate);
|
|
1952
|
+
tc_core_map_define_methods(shareable_map_class);
|
|
1953
|
+
}
|