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
|
@@ -0,0 +1,1714 @@
|
|
|
1
|
+
#include "containers.h"
|
|
2
|
+
|
|
3
|
+
#ifdef RC_HAVE_NATIVE_WEAK_REFERENCES
|
|
4
|
+
|
|
5
|
+
#include "ruby/fiber/scheduler.h"
|
|
6
|
+
#include "ruby/io.h"
|
|
7
|
+
|
|
8
|
+
#include <errno.h>
|
|
9
|
+
#include <fcntl.h>
|
|
10
|
+
#include <math.h>
|
|
11
|
+
#include <stdint.h>
|
|
12
|
+
#include <stdlib.h>
|
|
13
|
+
#include <time.h>
|
|
14
|
+
#include <unistd.h>
|
|
15
|
+
|
|
16
|
+
#define WEAK_MAP_INITIAL_CAPACITY 16
|
|
17
|
+
|
|
18
|
+
/* These functions are exported by Rubies with the probed typed-data weak
|
|
19
|
+
* reference callback, but their declarations intentionally remain internal. */
|
|
20
|
+
void rb_gc_declare_weak_references(VALUE object);
|
|
21
|
+
bool rb_gc_handle_weak_references_alive_p(VALUE object);
|
|
22
|
+
|
|
23
|
+
static VALUE cWeakKeyMap;
|
|
24
|
+
static VALUE weak_map_keep;
|
|
25
|
+
static VALUE weak_map_delete_token;
|
|
26
|
+
static VALUE cWeakValueMap;
|
|
27
|
+
static VALUE cWeakMap;
|
|
28
|
+
static VALUE eIsolationError;
|
|
29
|
+
|
|
30
|
+
typedef enum {
|
|
31
|
+
WEAK_MAP_EMPTY = 0,
|
|
32
|
+
WEAK_MAP_OCCUPIED = 1,
|
|
33
|
+
WEAK_MAP_TOMBSTONE = 2
|
|
34
|
+
} weak_map_slot_state_t;
|
|
35
|
+
|
|
36
|
+
typedef struct {
|
|
37
|
+
VALUE key;
|
|
38
|
+
VALUE value;
|
|
39
|
+
st_index_t hash;
|
|
40
|
+
weak_map_slot_state_t state;
|
|
41
|
+
} weak_map_slot_t;
|
|
42
|
+
|
|
43
|
+
typedef struct weak_map_waiter {
|
|
44
|
+
int read_fd;
|
|
45
|
+
int write_fd;
|
|
46
|
+
bool notified;
|
|
47
|
+
struct weak_map_waiter *next;
|
|
48
|
+
} weak_map_waiter_t;
|
|
49
|
+
|
|
50
|
+
/* Owned by the active operation's C stack, including while its Fiber sleeps. */
|
|
51
|
+
typedef struct weak_map_reservation {
|
|
52
|
+
VALUE key;
|
|
53
|
+
VALUE current;
|
|
54
|
+
st_index_t hash;
|
|
55
|
+
VALUE fiber;
|
|
56
|
+
VALUE thread;
|
|
57
|
+
bool invalidated;
|
|
58
|
+
struct weak_map_reservation *next;
|
|
59
|
+
} weak_map_reservation_t;
|
|
60
|
+
|
|
61
|
+
typedef struct {
|
|
62
|
+
pthread_mutex_t lock;
|
|
63
|
+
weak_map_slot_t *slots;
|
|
64
|
+
size_t capacity;
|
|
65
|
+
size_t size;
|
|
66
|
+
size_t tombstones;
|
|
67
|
+
uint64_t generation;
|
|
68
|
+
uint64_t iteration_epoch;
|
|
69
|
+
weak_map_waiter_t *waiters;
|
|
70
|
+
bool compare_keys_by_identity;
|
|
71
|
+
bool compare_values_by_identity;
|
|
72
|
+
bool weak_keys;
|
|
73
|
+
bool weak_values;
|
|
74
|
+
bool comparing;
|
|
75
|
+
VALUE comparing_owner;
|
|
76
|
+
VALUE comparing_thread;
|
|
77
|
+
weak_map_reservation_t *reservations;
|
|
78
|
+
bool initialized;
|
|
79
|
+
} weak_map_t;
|
|
80
|
+
|
|
81
|
+
typedef struct {
|
|
82
|
+
bool finite;
|
|
83
|
+
double deadline;
|
|
84
|
+
} weak_map_timeout_t;
|
|
85
|
+
|
|
86
|
+
typedef struct {
|
|
87
|
+
VALUE fiber;
|
|
88
|
+
VALUE thread;
|
|
89
|
+
VALUE scheduler;
|
|
90
|
+
} weak_map_execution_context_t;
|
|
91
|
+
|
|
92
|
+
static double
|
|
93
|
+
weak_map_monotonic_now(void)
|
|
94
|
+
{
|
|
95
|
+
struct timespec now;
|
|
96
|
+
#ifdef CLOCK_MONOTONIC
|
|
97
|
+
if (clock_gettime(CLOCK_MONOTONIC, &now) == 0) {
|
|
98
|
+
return (double)now.tv_sec + (double)now.tv_nsec / 1000000000.0;
|
|
99
|
+
}
|
|
100
|
+
#endif
|
|
101
|
+
struct timeval fallback;
|
|
102
|
+
gettimeofday(&fallback, NULL);
|
|
103
|
+
return (double)fallback.tv_sec + (double)fallback.tv_usec / 1000000.0;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
static weak_map_timeout_t
|
|
107
|
+
weak_map_parse_timeout(VALUE timeout)
|
|
108
|
+
{
|
|
109
|
+
weak_map_timeout_t parsed = {.finite = false, .deadline = 0};
|
|
110
|
+
if (NIL_P(timeout)) return parsed;
|
|
111
|
+
double seconds = NUM2DBL(timeout);
|
|
112
|
+
if (!isfinite(seconds) || seconds < 0) {
|
|
113
|
+
rb_raise(rb_eArgError, "timeout must be a finite, non-negative number or nil");
|
|
114
|
+
}
|
|
115
|
+
parsed.finite = true;
|
|
116
|
+
parsed.deadline = weak_map_monotonic_now() + seconds;
|
|
117
|
+
return parsed;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
static void
|
|
121
|
+
weak_map_set_fd_flags(int fd)
|
|
122
|
+
{
|
|
123
|
+
#ifdef _WIN32
|
|
124
|
+
(void)fd;
|
|
125
|
+
#else
|
|
126
|
+
int flags = fcntl(fd, F_GETFL, 0);
|
|
127
|
+
if (flags >= 0) (void)fcntl(fd, F_SETFL, flags | O_NONBLOCK);
|
|
128
|
+
flags = fcntl(fd, F_GETFD, 0);
|
|
129
|
+
if (flags >= 0) (void)fcntl(fd, F_SETFD, flags | FD_CLOEXEC);
|
|
130
|
+
#endif
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
static void
|
|
134
|
+
weak_map_waiter_initialize(weak_map_waiter_t *waiter)
|
|
135
|
+
{
|
|
136
|
+
int descriptors[2];
|
|
137
|
+
if (pipe(descriptors) != 0) rb_sys_fail("pipe");
|
|
138
|
+
waiter->read_fd = descriptors[0];
|
|
139
|
+
waiter->write_fd = descriptors[1];
|
|
140
|
+
waiter->notified = false;
|
|
141
|
+
waiter->next = NULL;
|
|
142
|
+
weak_map_set_fd_flags(waiter->read_fd);
|
|
143
|
+
weak_map_set_fd_flags(waiter->write_fd);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
static void
|
|
147
|
+
weak_map_notify_waiters_locked(weak_map_t *map)
|
|
148
|
+
{
|
|
149
|
+
unsigned char byte = 1;
|
|
150
|
+
map->generation++;
|
|
151
|
+
for (weak_map_waiter_t *waiter = map->waiters; waiter; waiter = waiter->next) {
|
|
152
|
+
if (waiter->notified) continue;
|
|
153
|
+
ssize_t result;
|
|
154
|
+
do {
|
|
155
|
+
result = write(waiter->write_fd, &byte, 1);
|
|
156
|
+
} while (result < 0 && errno == EINTR);
|
|
157
|
+
if (result == 1 || (result < 0 && (errno == EAGAIN || errno == EWOULDBLOCK))) {
|
|
158
|
+
waiter->notified = true;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
static void
|
|
164
|
+
weak_map_mark(void *pointer)
|
|
165
|
+
{
|
|
166
|
+
weak_map_t *map = pointer;
|
|
167
|
+
rb_gc_mark_movable(map->comparing_owner);
|
|
168
|
+
rb_gc_mark_movable(map->comparing_thread);
|
|
169
|
+
for (weak_map_reservation_t *entry = map->reservations; entry; entry = entry->next) {
|
|
170
|
+
rb_gc_mark_movable(entry->key);
|
|
171
|
+
rb_gc_mark_movable(entry->current);
|
|
172
|
+
rb_gc_mark_movable(entry->fiber);
|
|
173
|
+
rb_gc_mark_movable(entry->thread);
|
|
174
|
+
}
|
|
175
|
+
if (!map->slots) return;
|
|
176
|
+
|
|
177
|
+
for (size_t index = 0; index < map->capacity; index++) {
|
|
178
|
+
weak_map_slot_t *slot = &map->slots[index];
|
|
179
|
+
if (slot->state != WEAK_MAP_OCCUPIED) continue;
|
|
180
|
+
if (!map->weak_keys) rb_gc_mark_movable(slot->key);
|
|
181
|
+
if (!map->weak_values) rb_gc_mark_movable(slot->value);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
static void
|
|
186
|
+
weak_map_compact(void *pointer)
|
|
187
|
+
{
|
|
188
|
+
weak_map_t *map = pointer;
|
|
189
|
+
map->comparing_owner = rb_gc_location(map->comparing_owner);
|
|
190
|
+
map->comparing_thread = rb_gc_location(map->comparing_thread);
|
|
191
|
+
for (weak_map_reservation_t *entry = map->reservations; entry; entry = entry->next) {
|
|
192
|
+
entry->key = rb_gc_location(entry->key);
|
|
193
|
+
entry->current = rb_gc_location(entry->current);
|
|
194
|
+
entry->fiber = rb_gc_location(entry->fiber);
|
|
195
|
+
entry->thread = rb_gc_location(entry->thread);
|
|
196
|
+
}
|
|
197
|
+
if (!map->slots) return;
|
|
198
|
+
|
|
199
|
+
for (size_t index = 0; index < map->capacity; index++) {
|
|
200
|
+
weak_map_slot_t *slot = &map->slots[index];
|
|
201
|
+
if (slot->state != WEAK_MAP_OCCUPIED) continue;
|
|
202
|
+
slot->key = rb_gc_location(slot->key);
|
|
203
|
+
slot->value = rb_gc_location(slot->value);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/* The VM invokes this after marking while mutators are stopped. Taking the map
|
|
208
|
+
* mutex here could deadlock if GC stopped a mutator at a safepoint while it
|
|
209
|
+
* owned the mutex, so cleanup deliberately runs lock-free. */
|
|
210
|
+
static void
|
|
211
|
+
weak_map_handle_weak_references(void *pointer)
|
|
212
|
+
{
|
|
213
|
+
weak_map_t *map = pointer;
|
|
214
|
+
if (!map->slots) return;
|
|
215
|
+
bool changed = false;
|
|
216
|
+
|
|
217
|
+
for (size_t index = 0; index < map->capacity; index++) {
|
|
218
|
+
weak_map_slot_t *slot = &map->slots[index];
|
|
219
|
+
if (slot->state != WEAK_MAP_OCCUPIED) continue;
|
|
220
|
+
|
|
221
|
+
bool key_alive = !map->weak_keys || rb_gc_handle_weak_references_alive_p(slot->key);
|
|
222
|
+
bool value_alive = !map->weak_values || rb_gc_handle_weak_references_alive_p(slot->value);
|
|
223
|
+
if (key_alive && value_alive) continue;
|
|
224
|
+
|
|
225
|
+
slot->key = Qnil;
|
|
226
|
+
slot->value = Qnil;
|
|
227
|
+
slot->state = WEAK_MAP_TOMBSTONE;
|
|
228
|
+
map->size--;
|
|
229
|
+
map->tombstones++;
|
|
230
|
+
changed = true;
|
|
231
|
+
}
|
|
232
|
+
if (changed) weak_map_notify_waiters_locked(map);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
static void
|
|
236
|
+
weak_map_free(void *pointer)
|
|
237
|
+
{
|
|
238
|
+
weak_map_t *map = pointer;
|
|
239
|
+
pthread_mutex_destroy(&map->lock);
|
|
240
|
+
free(map->slots);
|
|
241
|
+
ruby_xfree(map);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
static size_t
|
|
245
|
+
weak_map_memsize(const void *pointer)
|
|
246
|
+
{
|
|
247
|
+
const weak_map_t *map = pointer;
|
|
248
|
+
return map ? sizeof(weak_map_t) + map->capacity * sizeof(weak_map_slot_t) : 0;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
static const rb_data_type_t weak_map_type = {
|
|
252
|
+
.wrap_struct_name = "Ractor::Containers::WeakMap",
|
|
253
|
+
.function = {
|
|
254
|
+
.dmark = weak_map_mark,
|
|
255
|
+
.dfree = weak_map_free,
|
|
256
|
+
.dsize = weak_map_memsize,
|
|
257
|
+
.dcompact = weak_map_compact,
|
|
258
|
+
.handle_weak_references = weak_map_handle_weak_references,
|
|
259
|
+
},
|
|
260
|
+
.flags = RUBY_TYPED_FROZEN_SHAREABLE,
|
|
261
|
+
};
|
|
262
|
+
|
|
263
|
+
static void
|
|
264
|
+
weak_map_validate_references(VALUE self)
|
|
265
|
+
{
|
|
266
|
+
weak_map_t *map;
|
|
267
|
+
TypedData_Get_Struct(self, weak_map_t, &weak_map_type, map);
|
|
268
|
+
for (size_t index = 0; index < map->capacity; index++) {
|
|
269
|
+
weak_map_slot_t *slot = &map->slots[index];
|
|
270
|
+
if (slot->state != WEAK_MAP_OCCUPIED) continue;
|
|
271
|
+
containers_check_shareable(slot->key);
|
|
272
|
+
containers_check_shareable(slot->value);
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
static weak_map_slot_t *
|
|
277
|
+
weak_map_allocate_slots(size_t capacity)
|
|
278
|
+
{
|
|
279
|
+
weak_map_slot_t *slots = calloc(capacity, sizeof(weak_map_slot_t));
|
|
280
|
+
if (!slots) rb_memerror();
|
|
281
|
+
return slots;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
static weak_map_slot_t *
|
|
285
|
+
weak_map_allocate_slots_locked(weak_map_t *map, size_t capacity)
|
|
286
|
+
{
|
|
287
|
+
weak_map_slot_t *slots = calloc(capacity, sizeof(weak_map_slot_t));
|
|
288
|
+
if (!slots) {
|
|
289
|
+
pthread_mutex_unlock(&map->lock);
|
|
290
|
+
rb_memerror();
|
|
291
|
+
}
|
|
292
|
+
return slots;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
static VALUE
|
|
296
|
+
weak_map_allocate(VALUE klass)
|
|
297
|
+
{
|
|
298
|
+
weak_map_t *map;
|
|
299
|
+
VALUE object = TypedData_Make_Struct(klass, weak_map_t, &weak_map_type, map);
|
|
300
|
+
pthread_mutex_init(&map->lock, NULL);
|
|
301
|
+
map->slots = NULL;
|
|
302
|
+
map->capacity = 0;
|
|
303
|
+
map->size = 0;
|
|
304
|
+
map->tombstones = 0;
|
|
305
|
+
map->generation = 0;
|
|
306
|
+
map->iteration_epoch = 0;
|
|
307
|
+
map->waiters = NULL;
|
|
308
|
+
map->compare_keys_by_identity = false;
|
|
309
|
+
map->compare_values_by_identity = false;
|
|
310
|
+
map->weak_keys = klass == cWeakKeyMap || klass == cWeakMap;
|
|
311
|
+
map->weak_values = klass == cWeakValueMap || klass == cWeakMap;
|
|
312
|
+
map->comparing = false;
|
|
313
|
+
map->comparing_owner = Qnil;
|
|
314
|
+
map->comparing_thread = Qnil;
|
|
315
|
+
map->reservations = NULL;
|
|
316
|
+
map->initialized = false;
|
|
317
|
+
return object;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
static weak_map_t *
|
|
321
|
+
get_weak_map(VALUE self)
|
|
322
|
+
{
|
|
323
|
+
weak_map_t *map;
|
|
324
|
+
TypedData_Get_Struct(self, weak_map_t, &weak_map_type, map);
|
|
325
|
+
if (!map->initialized) rb_raise(rb_eRuntimeError, "uninitialized weak map");
|
|
326
|
+
return map;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
typedef struct {
|
|
330
|
+
weak_map_t *map;
|
|
331
|
+
weak_map_waiter_t *waiter;
|
|
332
|
+
weak_map_timeout_t *timeout;
|
|
333
|
+
bool registered;
|
|
334
|
+
} weak_map_wait_context_t;
|
|
335
|
+
|
|
336
|
+
static VALUE
|
|
337
|
+
weak_map_wait_body(VALUE opaque)
|
|
338
|
+
{
|
|
339
|
+
weak_map_wait_context_t *context = (weak_map_wait_context_t *)opaque;
|
|
340
|
+
VALUE wait_timeout = Qnil;
|
|
341
|
+
if (context->timeout->finite) {
|
|
342
|
+
double remaining = context->timeout->deadline - weak_map_monotonic_now();
|
|
343
|
+
if (remaining <= 0) return Qfalse;
|
|
344
|
+
wait_timeout = DBL2NUM(remaining);
|
|
345
|
+
}
|
|
346
|
+
return containers_wait_for_readable(context->waiter->read_fd, wait_timeout) ? Qtrue : Qfalse;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
static VALUE
|
|
350
|
+
weak_map_wait_cleanup(VALUE opaque)
|
|
351
|
+
{
|
|
352
|
+
weak_map_wait_context_t *context = (weak_map_wait_context_t *)opaque;
|
|
353
|
+
if (context->registered) {
|
|
354
|
+
pthread_mutex_lock(&context->map->lock);
|
|
355
|
+
weak_map_waiter_t **cursor = &context->map->waiters;
|
|
356
|
+
while (*cursor && *cursor != context->waiter) cursor = &(*cursor)->next;
|
|
357
|
+
if (*cursor) *cursor = context->waiter->next;
|
|
358
|
+
pthread_mutex_unlock(&context->map->lock);
|
|
359
|
+
context->registered = false;
|
|
360
|
+
}
|
|
361
|
+
close(context->waiter->read_fd);
|
|
362
|
+
close(context->waiter->write_fd);
|
|
363
|
+
return Qnil;
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
static bool
|
|
367
|
+
weak_map_wait_once(weak_map_t *map, uint64_t generation, weak_map_timeout_t *timeout)
|
|
368
|
+
{
|
|
369
|
+
weak_map_waiter_t waiter;
|
|
370
|
+
weak_map_waiter_initialize(&waiter);
|
|
371
|
+
|
|
372
|
+
pthread_mutex_lock(&map->lock);
|
|
373
|
+
if (map->generation != generation) {
|
|
374
|
+
pthread_mutex_unlock(&map->lock);
|
|
375
|
+
close(waiter.read_fd);
|
|
376
|
+
close(waiter.write_fd);
|
|
377
|
+
return true;
|
|
378
|
+
}
|
|
379
|
+
waiter.next = map->waiters;
|
|
380
|
+
map->waiters = &waiter;
|
|
381
|
+
pthread_mutex_unlock(&map->lock);
|
|
382
|
+
|
|
383
|
+
weak_map_wait_context_t context = {
|
|
384
|
+
.map = map,
|
|
385
|
+
.waiter = &waiter,
|
|
386
|
+
.timeout = timeout,
|
|
387
|
+
.registered = true,
|
|
388
|
+
};
|
|
389
|
+
return RTEST(rb_ensure(
|
|
390
|
+
weak_map_wait_body,
|
|
391
|
+
(VALUE)&context,
|
|
392
|
+
weak_map_wait_cleanup,
|
|
393
|
+
(VALUE)&context
|
|
394
|
+
));
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
static void
|
|
398
|
+
weak_map_lock_state(weak_map_t *map, weak_map_execution_context_t *execution)
|
|
399
|
+
{
|
|
400
|
+
weak_map_timeout_t timeout = {.finite = false, .deadline = 0};
|
|
401
|
+
execution->fiber = rb_fiber_current();
|
|
402
|
+
execution->thread = rb_thread_current();
|
|
403
|
+
execution->scheduler = rb_fiber_scheduler_current();
|
|
404
|
+
|
|
405
|
+
for (;;) {
|
|
406
|
+
pthread_mutex_lock(&map->lock);
|
|
407
|
+
if (!map->comparing) return;
|
|
408
|
+
|
|
409
|
+
if (map->comparing_owner == execution->fiber) {
|
|
410
|
+
pthread_mutex_unlock(&map->lock);
|
|
411
|
+
rb_raise(rb_eThreadError, "recursive weak-map access from key equality");
|
|
412
|
+
}
|
|
413
|
+
if (map->comparing_thread == execution->thread &&
|
|
414
|
+
NIL_P(execution->scheduler)) {
|
|
415
|
+
pthread_mutex_unlock(&map->lock);
|
|
416
|
+
rb_raise(
|
|
417
|
+
rb_eThreadError,
|
|
418
|
+
"deadlock; weak-map key equality is owned by another unscheduled fiber"
|
|
419
|
+
);
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
uint64_t generation = map->generation;
|
|
423
|
+
pthread_mutex_unlock(&map->lock);
|
|
424
|
+
(void)weak_map_wait_once(map, generation, &timeout);
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
static VALUE
|
|
429
|
+
weak_map_timeout_result(void)
|
|
430
|
+
{
|
|
431
|
+
return rb_block_given_p() ? rb_yield_values(0) : Qnil;
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
static st_index_t
|
|
435
|
+
weak_map_key_hash(weak_map_t *map, VALUE key)
|
|
436
|
+
{
|
|
437
|
+
if (map->compare_keys_by_identity) {
|
|
438
|
+
return (st_index_t)NUM2ULL(rb_obj_id(key));
|
|
439
|
+
}
|
|
440
|
+
return (st_index_t)NUM2LONG(rb_hash(key));
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
typedef struct {
|
|
444
|
+
VALUE left;
|
|
445
|
+
VALUE right;
|
|
446
|
+
} weak_map_eql_arguments_t;
|
|
447
|
+
|
|
448
|
+
static VALUE
|
|
449
|
+
weak_map_eql_protected(VALUE opaque)
|
|
450
|
+
{
|
|
451
|
+
weak_map_eql_arguments_t *arguments = (weak_map_eql_arguments_t *)opaque;
|
|
452
|
+
return rb_eql(arguments->left, arguments->right) ? Qtrue : Qfalse;
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
static bool
|
|
456
|
+
weak_map_keys_equal(
|
|
457
|
+
weak_map_t *map,
|
|
458
|
+
VALUE left,
|
|
459
|
+
VALUE right,
|
|
460
|
+
bool *stale,
|
|
461
|
+
const weak_map_execution_context_t *execution
|
|
462
|
+
)
|
|
463
|
+
{
|
|
464
|
+
*stale = false;
|
|
465
|
+
if (map->compare_keys_by_identity) return left == right;
|
|
466
|
+
|
|
467
|
+
weak_map_eql_arguments_t arguments = {.left = left, .right = right};
|
|
468
|
+
uint64_t generation = map->generation;
|
|
469
|
+
int state = 0;
|
|
470
|
+
|
|
471
|
+
map->comparing = true;
|
|
472
|
+
map->comparing_owner = execution->fiber;
|
|
473
|
+
map->comparing_thread = execution->thread;
|
|
474
|
+
pthread_mutex_unlock(&map->lock);
|
|
475
|
+
|
|
476
|
+
VALUE result = rb_protect(weak_map_eql_protected, (VALUE)&arguments, &state);
|
|
477
|
+
|
|
478
|
+
pthread_mutex_lock(&map->lock);
|
|
479
|
+
*stale = map->generation != generation;
|
|
480
|
+
map->comparing = false;
|
|
481
|
+
map->comparing_owner = Qnil;
|
|
482
|
+
map->comparing_thread = Qnil;
|
|
483
|
+
weak_map_notify_waiters_locked(map);
|
|
484
|
+
if (state) {
|
|
485
|
+
pthread_mutex_unlock(&map->lock);
|
|
486
|
+
rb_jump_tag(state);
|
|
487
|
+
}
|
|
488
|
+
return RTEST(result);
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
static weak_map_reservation_t *
|
|
492
|
+
weak_map_find_reservation_locked(
|
|
493
|
+
weak_map_t *map, VALUE key, st_index_t hash,
|
|
494
|
+
const weak_map_execution_context_t *execution
|
|
495
|
+
)
|
|
496
|
+
{
|
|
497
|
+
for (;;) {
|
|
498
|
+
bool restart = false;
|
|
499
|
+
for (weak_map_reservation_t *entry = map->reservations; entry; entry = entry->next) {
|
|
500
|
+
if (entry->invalidated || entry->hash != hash) continue;
|
|
501
|
+
bool stale;
|
|
502
|
+
bool equal = weak_map_keys_equal(map, entry->key, key, &stale, execution);
|
|
503
|
+
if (stale) {
|
|
504
|
+
restart = true;
|
|
505
|
+
break;
|
|
506
|
+
}
|
|
507
|
+
if (equal) return entry;
|
|
508
|
+
}
|
|
509
|
+
if (!restart) return NULL;
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
static void
|
|
514
|
+
weak_map_check_reservation_wait_locked(
|
|
515
|
+
weak_map_t *map, weak_map_reservation_t *entry,
|
|
516
|
+
const weak_map_execution_context_t *execution
|
|
517
|
+
)
|
|
518
|
+
{
|
|
519
|
+
if (entry->fiber == execution->fiber) {
|
|
520
|
+
pthread_mutex_unlock(&map->lock);
|
|
521
|
+
rb_raise(rb_eThreadError, "deadlock; recursive weak-map access during an update");
|
|
522
|
+
}
|
|
523
|
+
if (entry->thread == execution->thread && NIL_P(execution->scheduler)) {
|
|
524
|
+
pthread_mutex_unlock(&map->lock);
|
|
525
|
+
rb_raise(rb_eThreadError, "deadlock; weak-map update is owned by another unscheduled fiber");
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
static bool
|
|
530
|
+
weak_map_lock_for_key_with_timeout(
|
|
531
|
+
weak_map_t *map, VALUE key, st_index_t hash,
|
|
532
|
+
weak_map_timeout_t *timeout, weak_map_execution_context_t *execution
|
|
533
|
+
)
|
|
534
|
+
{
|
|
535
|
+
for (;;) {
|
|
536
|
+
weak_map_lock_state(map, execution);
|
|
537
|
+
weak_map_reservation_t *entry = weak_map_find_reservation_locked(map, key, hash, execution);
|
|
538
|
+
if (!entry) return true;
|
|
539
|
+
weak_map_check_reservation_wait_locked(map, entry, execution);
|
|
540
|
+
uint64_t generation = map->generation;
|
|
541
|
+
pthread_mutex_unlock(&map->lock);
|
|
542
|
+
if (!weak_map_wait_once(map, generation, timeout)) return false;
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
static void
|
|
547
|
+
weak_map_lock_for_key(
|
|
548
|
+
weak_map_t *map, VALUE key, st_index_t hash, weak_map_execution_context_t *execution
|
|
549
|
+
)
|
|
550
|
+
{
|
|
551
|
+
weak_map_timeout_t timeout = {.finite = false, .deadline = 0};
|
|
552
|
+
(void)weak_map_lock_for_key_with_timeout(map, key, hash, &timeout, execution);
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
static void
|
|
556
|
+
weak_map_begin_reservation_locked(
|
|
557
|
+
weak_map_t *map, weak_map_reservation_t *entry, VALUE key, VALUE current,
|
|
558
|
+
st_index_t hash, const weak_map_execution_context_t *execution
|
|
559
|
+
)
|
|
560
|
+
{
|
|
561
|
+
entry->key = key;
|
|
562
|
+
entry->current = current;
|
|
563
|
+
entry->hash = hash;
|
|
564
|
+
entry->fiber = execution->fiber;
|
|
565
|
+
entry->thread = execution->thread;
|
|
566
|
+
entry->invalidated = false;
|
|
567
|
+
entry->next = map->reservations;
|
|
568
|
+
map->reservations = entry;
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
static void
|
|
572
|
+
weak_map_finish_reservation_locked(weak_map_t *map, weak_map_reservation_t *entry)
|
|
573
|
+
{
|
|
574
|
+
weak_map_reservation_t **cursor = &map->reservations;
|
|
575
|
+
while (*cursor && *cursor != entry) cursor = &(*cursor)->next;
|
|
576
|
+
if (*cursor) *cursor = entry->next;
|
|
577
|
+
weak_map_notify_waiters_locked(map);
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
static size_t
|
|
581
|
+
weak_map_find_slot(
|
|
582
|
+
weak_map_t *map,
|
|
583
|
+
VALUE key,
|
|
584
|
+
st_index_t hash,
|
|
585
|
+
bool *found,
|
|
586
|
+
const weak_map_execution_context_t *execution
|
|
587
|
+
)
|
|
588
|
+
{
|
|
589
|
+
for (;;) {
|
|
590
|
+
size_t mask = map->capacity - 1;
|
|
591
|
+
size_t first_tombstone = SIZE_MAX;
|
|
592
|
+
bool restart = false;
|
|
593
|
+
|
|
594
|
+
for (size_t offset = 0; offset < map->capacity; offset++) {
|
|
595
|
+
size_t index = ((size_t)hash + offset) & mask;
|
|
596
|
+
weak_map_slot_t *slot = &map->slots[index];
|
|
597
|
+
|
|
598
|
+
if (slot->state == WEAK_MAP_EMPTY) {
|
|
599
|
+
*found = false;
|
|
600
|
+
return first_tombstone == SIZE_MAX ? index : first_tombstone;
|
|
601
|
+
}
|
|
602
|
+
if (slot->state == WEAK_MAP_TOMBSTONE) {
|
|
603
|
+
if (first_tombstone == SIZE_MAX) first_tombstone = index;
|
|
604
|
+
continue;
|
|
605
|
+
}
|
|
606
|
+
if (slot->hash == hash) {
|
|
607
|
+
bool stale;
|
|
608
|
+
bool equal = weak_map_keys_equal(
|
|
609
|
+
map,
|
|
610
|
+
slot->key,
|
|
611
|
+
key,
|
|
612
|
+
&stale,
|
|
613
|
+
execution
|
|
614
|
+
);
|
|
615
|
+
|
|
616
|
+
if (stale) {
|
|
617
|
+
restart = true;
|
|
618
|
+
break;
|
|
619
|
+
}
|
|
620
|
+
if (equal) {
|
|
621
|
+
*found = true;
|
|
622
|
+
return index;
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
if (restart) continue;
|
|
628
|
+
*found = false;
|
|
629
|
+
return first_tombstone;
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
static void
|
|
634
|
+
weak_map_resize(weak_map_t *map, size_t new_capacity)
|
|
635
|
+
{
|
|
636
|
+
map->iteration_epoch++;
|
|
637
|
+
weak_map_slot_t *old_slots = map->slots;
|
|
638
|
+
size_t old_capacity = map->capacity;
|
|
639
|
+
map->slots = weak_map_allocate_slots_locked(map, new_capacity);
|
|
640
|
+
map->capacity = new_capacity;
|
|
641
|
+
map->size = 0;
|
|
642
|
+
map->tombstones = 0;
|
|
643
|
+
|
|
644
|
+
for (size_t index = 0; index < old_capacity; index++) {
|
|
645
|
+
weak_map_slot_t *old = &old_slots[index];
|
|
646
|
+
if (old->state != WEAK_MAP_OCCUPIED) continue;
|
|
647
|
+
|
|
648
|
+
size_t mask = map->capacity - 1;
|
|
649
|
+
size_t target = (size_t)old->hash & mask;
|
|
650
|
+
while (map->slots[target].state == WEAK_MAP_OCCUPIED) {
|
|
651
|
+
target = (target + 1) & mask;
|
|
652
|
+
}
|
|
653
|
+
map->slots[target] = *old;
|
|
654
|
+
map->size++;
|
|
655
|
+
}
|
|
656
|
+
free(old_slots);
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
static void
|
|
660
|
+
weak_map_prepare_insert(weak_map_t *map)
|
|
661
|
+
{
|
|
662
|
+
if ((map->size + map->tombstones + 1) * 10 >= map->capacity * 7) {
|
|
663
|
+
weak_map_resize(map, map->capacity * 2);
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
static VALUE
|
|
668
|
+
weak_map_store_locked(
|
|
669
|
+
VALUE self,
|
|
670
|
+
weak_map_t *map,
|
|
671
|
+
VALUE key,
|
|
672
|
+
VALUE value,
|
|
673
|
+
st_index_t hash,
|
|
674
|
+
const weak_map_execution_context_t *execution
|
|
675
|
+
)
|
|
676
|
+
{
|
|
677
|
+
bool found;
|
|
678
|
+
weak_map_prepare_insert(map);
|
|
679
|
+
size_t index = weak_map_find_slot(map, key, hash, &found, execution);
|
|
680
|
+
if (RB_OBJ_FROZEN(self)) {
|
|
681
|
+
pthread_mutex_unlock(&map->lock);
|
|
682
|
+
rb_check_frozen(self);
|
|
683
|
+
}
|
|
684
|
+
weak_map_slot_t *slot = &map->slots[index];
|
|
685
|
+
if (!found) {
|
|
686
|
+
if (slot->state == WEAK_MAP_TOMBSTONE) map->tombstones--;
|
|
687
|
+
slot->key = key;
|
|
688
|
+
slot->hash = hash;
|
|
689
|
+
slot->state = WEAK_MAP_OCCUPIED;
|
|
690
|
+
map->size++;
|
|
691
|
+
map->iteration_epoch++;
|
|
692
|
+
}
|
|
693
|
+
slot->value = value;
|
|
694
|
+
RB_OBJ_WRITTEN(self, Qundef, key);
|
|
695
|
+
RB_OBJ_WRITTEN(self, Qundef, value);
|
|
696
|
+
weak_map_notify_waiters_locked(map);
|
|
697
|
+
return value;
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
static VALUE
|
|
701
|
+
weak_map_normalize_key(weak_map_t *map, VALUE key)
|
|
702
|
+
{
|
|
703
|
+
if (!map->compare_keys_by_identity && RB_TYPE_P(key, T_STRING) && !RB_OBJ_FROZEN(key)) {
|
|
704
|
+
key = containers_normalize_string_key(key);
|
|
705
|
+
}
|
|
706
|
+
return key;
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
static VALUE
|
|
710
|
+
weak_map_prepare_key(weak_map_t *map, VALUE key)
|
|
711
|
+
{
|
|
712
|
+
key = weak_map_normalize_key(map, key);
|
|
713
|
+
containers_check_shareable(key);
|
|
714
|
+
return key;
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
typedef struct {
|
|
718
|
+
VALUE self;
|
|
719
|
+
weak_map_t *map;
|
|
720
|
+
} weak_map_init_context_t;
|
|
721
|
+
|
|
722
|
+
static int
|
|
723
|
+
weak_map_initialize_entry(VALUE key, VALUE value, VALUE opaque)
|
|
724
|
+
{
|
|
725
|
+
weak_map_init_context_t *context = (weak_map_init_context_t *)opaque;
|
|
726
|
+
key = weak_map_prepare_key(context->map, key);
|
|
727
|
+
containers_check_shareable(value);
|
|
728
|
+
st_index_t hash = weak_map_key_hash(context->map, key);
|
|
729
|
+
weak_map_execution_context_t execution;
|
|
730
|
+
weak_map_lock_state(context->map, &execution);
|
|
731
|
+
weak_map_store_locked(context->self, context->map, key, value, hash, &execution);
|
|
732
|
+
pthread_mutex_unlock(&context->map->lock);
|
|
733
|
+
return ST_CONTINUE;
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
static VALUE
|
|
737
|
+
weak_map_initialize(int argc, VALUE *argv, VALUE self)
|
|
738
|
+
{
|
|
739
|
+
VALUE mapping = Qnil;
|
|
740
|
+
VALUE keywords = Qnil;
|
|
741
|
+
ID keyword_ids[] = {
|
|
742
|
+
rb_intern("compare_by_identity"),
|
|
743
|
+
rb_intern("compare_keys_by_identity"),
|
|
744
|
+
rb_intern("compare_values_by_identity")
|
|
745
|
+
};
|
|
746
|
+
VALUE keyword_values[3] = {Qundef, Qundef, Qundef};
|
|
747
|
+
weak_map_t *map;
|
|
748
|
+
|
|
749
|
+
rb_scan_args(argc, argv, "01:", &mapping, &keywords);
|
|
750
|
+
if (!NIL_P(keywords)) rb_get_kwargs(keywords, keyword_ids, 0, 3, keyword_values);
|
|
751
|
+
TypedData_Get_Struct(self, weak_map_t, &weak_map_type, map);
|
|
752
|
+
if (map->initialized) rb_raise(rb_eRuntimeError, "weak map is already initialized");
|
|
753
|
+
|
|
754
|
+
bool common = keyword_values[0] == Qundef
|
|
755
|
+
? false
|
|
756
|
+
: containers_strict_bool(keyword_values[0], "compare_by_identity");
|
|
757
|
+
map->compare_keys_by_identity = keyword_values[1] == Qundef
|
|
758
|
+
? common
|
|
759
|
+
: containers_strict_bool(keyword_values[1], "compare_keys_by_identity");
|
|
760
|
+
map->compare_values_by_identity = keyword_values[2] == Qundef
|
|
761
|
+
? common
|
|
762
|
+
: containers_strict_bool(keyword_values[2], "compare_values_by_identity");
|
|
763
|
+
map->capacity = WEAK_MAP_INITIAL_CAPACITY;
|
|
764
|
+
map->slots = weak_map_allocate_slots(map->capacity);
|
|
765
|
+
|
|
766
|
+
rb_gc_declare_weak_references(self);
|
|
767
|
+
if (!NIL_P(mapping)) {
|
|
768
|
+
Check_Type(mapping, T_HASH);
|
|
769
|
+
weak_map_init_context_t context = {.self = self, .map = map};
|
|
770
|
+
rb_hash_foreach(mapping, weak_map_initialize_entry, (VALUE)&context);
|
|
771
|
+
}
|
|
772
|
+
map->initialized = true;
|
|
773
|
+
containers_publish_native_with_references(self, weak_map_validate_references);
|
|
774
|
+
return self;
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
static VALUE
|
|
778
|
+
weak_map_get(VALUE self, VALUE key)
|
|
779
|
+
{
|
|
780
|
+
weak_map_t *map = get_weak_map(self);
|
|
781
|
+
key = weak_map_prepare_key(map, key);
|
|
782
|
+
st_index_t hash = weak_map_key_hash(map, key);
|
|
783
|
+
bool found;
|
|
784
|
+
VALUE result = Qnil;
|
|
785
|
+
weak_map_execution_context_t execution;
|
|
786
|
+
weak_map_lock_state(map, &execution);
|
|
787
|
+
size_t index = weak_map_find_slot(map, key, hash, &found, &execution);
|
|
788
|
+
if (found) result = map->slots[index].value;
|
|
789
|
+
pthread_mutex_unlock(&map->lock);
|
|
790
|
+
return result;
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
static VALUE
|
|
794
|
+
weak_map_check_mutation(VALUE self)
|
|
795
|
+
{
|
|
796
|
+
rb_check_frozen(self);
|
|
797
|
+
return self;
|
|
798
|
+
}
|
|
799
|
+
|
|
800
|
+
static VALUE
|
|
801
|
+
weak_map_prepare_mutation_key(VALUE self, VALUE key)
|
|
802
|
+
{
|
|
803
|
+
rb_check_frozen(self);
|
|
804
|
+
key = weak_map_normalize_key(get_weak_map(self), key);
|
|
805
|
+
if (!rb_ractor_shareable_p(key)) {
|
|
806
|
+
rb_raise(eIsolationError, "key must be Ractor-shareable");
|
|
807
|
+
}
|
|
808
|
+
return key;
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
static VALUE
|
|
812
|
+
weak_map_fetch(int argc, VALUE *argv, VALUE self)
|
|
813
|
+
{
|
|
814
|
+
VALUE key;
|
|
815
|
+
VALUE default_value;
|
|
816
|
+
rb_scan_args(argc, argv, "11", &key, &default_value);
|
|
817
|
+
|
|
818
|
+
bool default_given = argc == 2;
|
|
819
|
+
bool block_given = rb_block_given_p();
|
|
820
|
+
if (block_given && default_given) {
|
|
821
|
+
rb_warn("block supersedes default value argument");
|
|
822
|
+
}
|
|
823
|
+
|
|
824
|
+
VALUE original_key = key;
|
|
825
|
+
weak_map_t *map = get_weak_map(self);
|
|
826
|
+
key = weak_map_prepare_key(map, key);
|
|
827
|
+
st_index_t hash = weak_map_key_hash(map, key);
|
|
828
|
+
bool found;
|
|
829
|
+
VALUE result = Qnil;
|
|
830
|
+
weak_map_execution_context_t execution;
|
|
831
|
+
weak_map_lock_state(map, &execution);
|
|
832
|
+
size_t index = weak_map_find_slot(map, key, hash, &found, &execution);
|
|
833
|
+
if (found) result = map->slots[index].value;
|
|
834
|
+
pthread_mutex_unlock(&map->lock);
|
|
835
|
+
|
|
836
|
+
if (found) return result;
|
|
837
|
+
if (block_given) return rb_yield(original_key);
|
|
838
|
+
if (default_given) return default_value;
|
|
839
|
+
containers_raise_key_error(self, original_key);
|
|
840
|
+
return Qnil;
|
|
841
|
+
}
|
|
842
|
+
|
|
843
|
+
static VALUE
|
|
844
|
+
weak_map_set(VALUE self, VALUE key, VALUE value)
|
|
845
|
+
{
|
|
846
|
+
rb_check_frozen(self);
|
|
847
|
+
weak_map_t *map = get_weak_map(self);
|
|
848
|
+
key = weak_map_prepare_key(map, key);
|
|
849
|
+
containers_check_shareable(value);
|
|
850
|
+
st_index_t hash = weak_map_key_hash(map, key);
|
|
851
|
+
rb_check_frozen(self);
|
|
852
|
+
weak_map_execution_context_t execution;
|
|
853
|
+
weak_map_lock_for_key(map, key, hash, &execution);
|
|
854
|
+
weak_map_store_locked(self, map, key, value, hash, &execution);
|
|
855
|
+
pthread_mutex_unlock(&map->lock);
|
|
856
|
+
return value;
|
|
857
|
+
}
|
|
858
|
+
|
|
859
|
+
static VALUE
|
|
860
|
+
weak_map_key_p(VALUE self, VALUE key)
|
|
861
|
+
{
|
|
862
|
+
weak_map_t *map = get_weak_map(self);
|
|
863
|
+
key = weak_map_prepare_key(map, key);
|
|
864
|
+
st_index_t hash = weak_map_key_hash(map, key);
|
|
865
|
+
bool found;
|
|
866
|
+
weak_map_execution_context_t execution;
|
|
867
|
+
weak_map_lock_state(map, &execution);
|
|
868
|
+
(void)weak_map_find_slot(map, key, hash, &found, &execution);
|
|
869
|
+
pthread_mutex_unlock(&map->lock);
|
|
870
|
+
return found ? Qtrue : Qfalse;
|
|
871
|
+
}
|
|
872
|
+
|
|
873
|
+
static VALUE
|
|
874
|
+
weak_map_getkey(VALUE self, VALUE key)
|
|
875
|
+
{
|
|
876
|
+
weak_map_t *map = get_weak_map(self);
|
|
877
|
+
key = weak_map_prepare_key(map, key);
|
|
878
|
+
st_index_t hash = weak_map_key_hash(map, key);
|
|
879
|
+
bool found;
|
|
880
|
+
VALUE result = Qnil;
|
|
881
|
+
weak_map_execution_context_t execution;
|
|
882
|
+
weak_map_lock_state(map, &execution);
|
|
883
|
+
size_t index = weak_map_find_slot(map, key, hash, &found, &execution);
|
|
884
|
+
if (found) result = map->compare_keys_by_identity ? key : map->slots[index].key;
|
|
885
|
+
pthread_mutex_unlock(&map->lock);
|
|
886
|
+
return result;
|
|
887
|
+
}
|
|
888
|
+
|
|
889
|
+
static VALUE
|
|
890
|
+
weak_map_delete(VALUE self, VALUE key)
|
|
891
|
+
{
|
|
892
|
+
rb_check_frozen(self);
|
|
893
|
+
weak_map_t *map = get_weak_map(self);
|
|
894
|
+
key = weak_map_prepare_key(map, key);
|
|
895
|
+
st_index_t hash = weak_map_key_hash(map, key);
|
|
896
|
+
rb_check_frozen(self);
|
|
897
|
+
bool found;
|
|
898
|
+
VALUE result = Qnil;
|
|
899
|
+
weak_map_execution_context_t execution;
|
|
900
|
+
weak_map_lock_for_key(map, key, hash, &execution);
|
|
901
|
+
size_t index = weak_map_find_slot(map, key, hash, &found, &execution);
|
|
902
|
+
if (RB_OBJ_FROZEN(self)) {
|
|
903
|
+
pthread_mutex_unlock(&map->lock);
|
|
904
|
+
rb_check_frozen(self);
|
|
905
|
+
}
|
|
906
|
+
if (found) {
|
|
907
|
+
weak_map_slot_t *slot = &map->slots[index];
|
|
908
|
+
result = slot->value;
|
|
909
|
+
slot->key = Qnil;
|
|
910
|
+
slot->value = Qnil;
|
|
911
|
+
slot->state = WEAK_MAP_TOMBSTONE;
|
|
912
|
+
map->size--;
|
|
913
|
+
map->tombstones++;
|
|
914
|
+
weak_map_notify_waiters_locked(map);
|
|
915
|
+
}
|
|
916
|
+
pthread_mutex_unlock(&map->lock);
|
|
917
|
+
return result;
|
|
918
|
+
}
|
|
919
|
+
|
|
920
|
+
static VALUE
|
|
921
|
+
weak_map_clear(VALUE self)
|
|
922
|
+
{
|
|
923
|
+
rb_check_frozen(self);
|
|
924
|
+
weak_map_t *map = get_weak_map(self);
|
|
925
|
+
weak_map_execution_context_t execution;
|
|
926
|
+
weak_map_lock_state(map, &execution);
|
|
927
|
+
for (weak_map_reservation_t *entry = map->reservations; entry; entry = entry->next) {
|
|
928
|
+
entry->invalidated = true;
|
|
929
|
+
}
|
|
930
|
+
for (size_t index = 0; index < map->capacity; index++) {
|
|
931
|
+
weak_map_slot_t *slot = &map->slots[index];
|
|
932
|
+
slot->key = Qnil;
|
|
933
|
+
slot->value = Qnil;
|
|
934
|
+
slot->hash = 0;
|
|
935
|
+
slot->state = WEAK_MAP_EMPTY;
|
|
936
|
+
}
|
|
937
|
+
map->iteration_epoch++;
|
|
938
|
+
map->size = 0;
|
|
939
|
+
map->tombstones = 0;
|
|
940
|
+
weak_map_notify_waiters_locked(map);
|
|
941
|
+
pthread_mutex_unlock(&map->lock);
|
|
942
|
+
return self;
|
|
943
|
+
}
|
|
944
|
+
|
|
945
|
+
typedef struct {
|
|
946
|
+
VALUE self;
|
|
947
|
+
weak_map_t *map;
|
|
948
|
+
VALUE key;
|
|
949
|
+
VALUE current;
|
|
950
|
+
VALUE argument;
|
|
951
|
+
VALUE replacement;
|
|
952
|
+
st_index_t hash;
|
|
953
|
+
bool identity;
|
|
954
|
+
bool complete;
|
|
955
|
+
bool present;
|
|
956
|
+
weak_map_reservation_t reservation;
|
|
957
|
+
} weak_map_operation_t;
|
|
958
|
+
|
|
959
|
+
static VALUE
|
|
960
|
+
weak_map_operation_cleanup(VALUE opaque)
|
|
961
|
+
{
|
|
962
|
+
weak_map_operation_t *operation = (weak_map_operation_t *)opaque;
|
|
963
|
+
if (!operation->complete) {
|
|
964
|
+
weak_map_execution_context_t execution;
|
|
965
|
+
weak_map_lock_state(operation->map, &execution);
|
|
966
|
+
weak_map_finish_reservation_locked(operation->map, &operation->reservation);
|
|
967
|
+
weak_map_notify_waiters_locked(operation->map);
|
|
968
|
+
pthread_mutex_unlock(&operation->map->lock);
|
|
969
|
+
}
|
|
970
|
+
return Qnil;
|
|
971
|
+
}
|
|
972
|
+
|
|
973
|
+
static VALUE
|
|
974
|
+
weak_map_store_body(VALUE opaque)
|
|
975
|
+
{
|
|
976
|
+
weak_map_operation_t *operation = (weak_map_operation_t *)opaque;
|
|
977
|
+
VALUE result = rb_yield_values(0);
|
|
978
|
+
containers_check_shareable(result);
|
|
979
|
+
rb_check_frozen(operation->self);
|
|
980
|
+
weak_map_execution_context_t execution;
|
|
981
|
+
weak_map_lock_state(operation->map, &execution);
|
|
982
|
+
bool stored = !operation->reservation.invalidated;
|
|
983
|
+
if (stored) {
|
|
984
|
+
weak_map_store_locked(
|
|
985
|
+
operation->self,
|
|
986
|
+
operation->map,
|
|
987
|
+
operation->key,
|
|
988
|
+
result,
|
|
989
|
+
operation->hash,
|
|
990
|
+
&execution
|
|
991
|
+
);
|
|
992
|
+
}
|
|
993
|
+
weak_map_finish_reservation_locked(operation->map, &operation->reservation);
|
|
994
|
+
pthread_mutex_unlock(&operation->map->lock);
|
|
995
|
+
operation->complete = true;
|
|
996
|
+
return stored ? result : Qnil;
|
|
997
|
+
}
|
|
998
|
+
|
|
999
|
+
static VALUE
|
|
1000
|
+
weak_map_store_if_absent(int argc, VALUE *argv, VALUE self)
|
|
1001
|
+
{
|
|
1002
|
+
rb_check_frozen(self);
|
|
1003
|
+
VALUE key;
|
|
1004
|
+
VALUE keywords = Qnil;
|
|
1005
|
+
VALUE keyword_values[1] = {Qundef};
|
|
1006
|
+
ID keyword_ids[] = {rb_intern("timeout")};
|
|
1007
|
+
rb_scan_args(argc, argv, "1:", &key, &keywords);
|
|
1008
|
+
if (!NIL_P(keywords)) rb_get_kwargs(keywords, keyword_ids, 0, 1, keyword_values);
|
|
1009
|
+
|
|
1010
|
+
weak_map_t *map = get_weak_map(self);
|
|
1011
|
+
key = weak_map_prepare_key(map, key);
|
|
1012
|
+
weak_map_operation_t operation = {
|
|
1013
|
+
.self = self,
|
|
1014
|
+
.map = map,
|
|
1015
|
+
.key = key,
|
|
1016
|
+
.complete = false,
|
|
1017
|
+
};
|
|
1018
|
+
rb_need_block();
|
|
1019
|
+
operation.hash = weak_map_key_hash(map, key);
|
|
1020
|
+
rb_check_frozen(self);
|
|
1021
|
+
weak_map_timeout_t timeout = weak_map_parse_timeout(
|
|
1022
|
+
keyword_values[0] == Qundef ? Qnil : keyword_values[0]
|
|
1023
|
+
);
|
|
1024
|
+
|
|
1025
|
+
weak_map_execution_context_t execution;
|
|
1026
|
+
if (!weak_map_lock_for_key_with_timeout(map, key, operation.hash, &timeout, &execution)) return Qnil;
|
|
1027
|
+
bool found;
|
|
1028
|
+
size_t index = weak_map_find_slot(map, key, operation.hash, &found, &execution);
|
|
1029
|
+
if (RB_OBJ_FROZEN(self)) {
|
|
1030
|
+
pthread_mutex_unlock(&map->lock);
|
|
1031
|
+
rb_check_frozen(self);
|
|
1032
|
+
}
|
|
1033
|
+
if (found) {
|
|
1034
|
+
VALUE value = map->slots[index].value;
|
|
1035
|
+
pthread_mutex_unlock(&map->lock);
|
|
1036
|
+
return value;
|
|
1037
|
+
}
|
|
1038
|
+
operation.key = found ? map->slots[index].key : key;
|
|
1039
|
+
weak_map_begin_reservation_locked(map, &operation.reservation, operation.key,
|
|
1040
|
+
operation.current, operation.hash, &execution);
|
|
1041
|
+
pthread_mutex_unlock(&map->lock);
|
|
1042
|
+
return rb_ensure(weak_map_store_body, (VALUE)&operation, weak_map_operation_cleanup, (VALUE)&operation);
|
|
1043
|
+
}
|
|
1044
|
+
|
|
1045
|
+
static VALUE
|
|
1046
|
+
weak_map_cas_body(VALUE opaque)
|
|
1047
|
+
{
|
|
1048
|
+
weak_map_operation_t *operation = (weak_map_operation_t *)opaque;
|
|
1049
|
+
bool matches = operation->identity
|
|
1050
|
+
? operation->current == operation->argument
|
|
1051
|
+
: RTEST(rb_equal(operation->current, operation->argument));
|
|
1052
|
+
rb_check_frozen(operation->self);
|
|
1053
|
+
|
|
1054
|
+
weak_map_execution_context_t execution;
|
|
1055
|
+
weak_map_lock_state(operation->map, &execution);
|
|
1056
|
+
matches = matches && !operation->reservation.invalidated;
|
|
1057
|
+
if (matches) {
|
|
1058
|
+
weak_map_store_locked(
|
|
1059
|
+
operation->self,
|
|
1060
|
+
operation->map,
|
|
1061
|
+
operation->key,
|
|
1062
|
+
operation->replacement,
|
|
1063
|
+
operation->hash,
|
|
1064
|
+
&execution
|
|
1065
|
+
);
|
|
1066
|
+
}
|
|
1067
|
+
weak_map_finish_reservation_locked(operation->map, &operation->reservation);
|
|
1068
|
+
if (!matches) weak_map_notify_waiters_locked(operation->map);
|
|
1069
|
+
pthread_mutex_unlock(&operation->map->lock);
|
|
1070
|
+
operation->complete = true;
|
|
1071
|
+
return matches ? Qtrue : Qfalse;
|
|
1072
|
+
}
|
|
1073
|
+
|
|
1074
|
+
static VALUE
|
|
1075
|
+
weak_map_compare_and_set(int argc, VALUE *argv, VALUE self)
|
|
1076
|
+
{
|
|
1077
|
+
rb_check_frozen(self);
|
|
1078
|
+
VALUE key;
|
|
1079
|
+
VALUE expected;
|
|
1080
|
+
VALUE replacement;
|
|
1081
|
+
VALUE keywords = Qnil;
|
|
1082
|
+
VALUE keyword_values[1] = {Qundef};
|
|
1083
|
+
ID keyword_ids[] = {rb_intern("timeout")};
|
|
1084
|
+
rb_scan_args(argc, argv, "3:", &key, &expected, &replacement, &keywords);
|
|
1085
|
+
if (!NIL_P(keywords)) rb_get_kwargs(keywords, keyword_ids, 0, 1, keyword_values);
|
|
1086
|
+
|
|
1087
|
+
weak_map_t *map = get_weak_map(self);
|
|
1088
|
+
key = weak_map_prepare_key(map, key);
|
|
1089
|
+
weak_map_operation_t operation = {
|
|
1090
|
+
.self = self,
|
|
1091
|
+
.map = map,
|
|
1092
|
+
.key = key,
|
|
1093
|
+
.argument = expected,
|
|
1094
|
+
.replacement = replacement,
|
|
1095
|
+
.identity = map->compare_values_by_identity,
|
|
1096
|
+
.complete = false,
|
|
1097
|
+
};
|
|
1098
|
+
containers_check_shareable(expected);
|
|
1099
|
+
containers_check_shareable(replacement);
|
|
1100
|
+
operation.hash = weak_map_key_hash(map, key);
|
|
1101
|
+
rb_check_frozen(self);
|
|
1102
|
+
weak_map_timeout_t timeout = weak_map_parse_timeout(
|
|
1103
|
+
keyword_values[0] == Qundef ? Qnil : keyword_values[0]
|
|
1104
|
+
);
|
|
1105
|
+
|
|
1106
|
+
weak_map_execution_context_t execution;
|
|
1107
|
+
if (!weak_map_lock_for_key_with_timeout(map, key, operation.hash, &timeout, &execution)) return Qfalse;
|
|
1108
|
+
bool found;
|
|
1109
|
+
size_t index = weak_map_find_slot(map, key, operation.hash, &found, &execution);
|
|
1110
|
+
if (RB_OBJ_FROZEN(self)) {
|
|
1111
|
+
pthread_mutex_unlock(&map->lock);
|
|
1112
|
+
rb_check_frozen(self);
|
|
1113
|
+
}
|
|
1114
|
+
if (!found) {
|
|
1115
|
+
pthread_mutex_unlock(&map->lock);
|
|
1116
|
+
return Qfalse;
|
|
1117
|
+
}
|
|
1118
|
+
operation.current = map->slots[index].value;
|
|
1119
|
+
operation.key = found ? map->slots[index].key : key;
|
|
1120
|
+
weak_map_begin_reservation_locked(map, &operation.reservation, operation.key,
|
|
1121
|
+
operation.current, operation.hash, &execution);
|
|
1122
|
+
pthread_mutex_unlock(&map->lock);
|
|
1123
|
+
return rb_ensure(weak_map_cas_body, (VALUE)&operation, weak_map_operation_cleanup, (VALUE)&operation);
|
|
1124
|
+
}
|
|
1125
|
+
|
|
1126
|
+
/* Decisions are interpreted before value validation. KEEP never writes storage. */
|
|
1127
|
+
static VALUE
|
|
1128
|
+
weak_map_modify_body(VALUE opaque)
|
|
1129
|
+
{
|
|
1130
|
+
weak_map_operation_t *operation = (weak_map_operation_t *)opaque;
|
|
1131
|
+
VALUE value = rb_yield_values(2, operation->present ? Qtrue : Qfalse, operation->current);
|
|
1132
|
+
bool remove = value == weak_map_delete_token;
|
|
1133
|
+
bool store = !remove && value != weak_map_keep;
|
|
1134
|
+
if (store) containers_check_shareable(value);
|
|
1135
|
+
rb_check_frozen(operation->self);
|
|
1136
|
+
weak_map_execution_context_t execution;
|
|
1137
|
+
weak_map_lock_state(operation->map, &execution);
|
|
1138
|
+
bool changed = false;
|
|
1139
|
+
if (store && !operation->reservation.invalidated) {
|
|
1140
|
+
weak_map_store_locked(operation->self, operation->map, operation->key,
|
|
1141
|
+
value, operation->hash, &execution);
|
|
1142
|
+
changed = true;
|
|
1143
|
+
} else if (remove && !operation->reservation.invalidated) {
|
|
1144
|
+
bool found;
|
|
1145
|
+
size_t index = weak_map_find_slot(operation->map, operation->key,
|
|
1146
|
+
operation->hash, &found, &execution);
|
|
1147
|
+
if (RB_OBJ_FROZEN(operation->self)) {
|
|
1148
|
+
pthread_mutex_unlock(&operation->map->lock);
|
|
1149
|
+
rb_check_frozen(operation->self);
|
|
1150
|
+
}
|
|
1151
|
+
if (found) {
|
|
1152
|
+
weak_map_slot_t *slot = &operation->map->slots[index];
|
|
1153
|
+
slot->key = Qnil;
|
|
1154
|
+
slot->value = Qnil;
|
|
1155
|
+
slot->state = WEAK_MAP_TOMBSTONE;
|
|
1156
|
+
operation->map->size--;
|
|
1157
|
+
operation->map->tombstones++;
|
|
1158
|
+
weak_map_notify_waiters_locked(operation->map);
|
|
1159
|
+
changed = true;
|
|
1160
|
+
}
|
|
1161
|
+
}
|
|
1162
|
+
operation->complete = true;
|
|
1163
|
+
weak_map_finish_reservation_locked(operation->map, &operation->reservation);
|
|
1164
|
+
pthread_mutex_unlock(&operation->map->lock);
|
|
1165
|
+
return changed ? Qtrue : Qfalse;
|
|
1166
|
+
}
|
|
1167
|
+
|
|
1168
|
+
static VALUE
|
|
1169
|
+
weak_map_modify(VALUE self, VALUE key)
|
|
1170
|
+
{
|
|
1171
|
+
rb_check_frozen(self);
|
|
1172
|
+
rb_need_block();
|
|
1173
|
+
weak_map_t *map = get_weak_map(self);
|
|
1174
|
+
key = weak_map_prepare_key(map, key);
|
|
1175
|
+
weak_map_operation_t operation = {
|
|
1176
|
+
.self = self,
|
|
1177
|
+
.map = map,
|
|
1178
|
+
.key = key,
|
|
1179
|
+
.complete = false,
|
|
1180
|
+
};
|
|
1181
|
+
operation.hash = weak_map_key_hash(map, key);
|
|
1182
|
+
rb_check_frozen(self);
|
|
1183
|
+
weak_map_execution_context_t execution;
|
|
1184
|
+
weak_map_lock_for_key(map, key, operation.hash, &execution);
|
|
1185
|
+
bool found;
|
|
1186
|
+
size_t index = weak_map_find_slot(map, key, operation.hash, &found, &execution);
|
|
1187
|
+
if (RB_OBJ_FROZEN(self)) {
|
|
1188
|
+
pthread_mutex_unlock(&map->lock);
|
|
1189
|
+
rb_check_frozen(self);
|
|
1190
|
+
}
|
|
1191
|
+
operation.present = found;
|
|
1192
|
+
operation.current = found ? map->slots[index].value : Qnil;
|
|
1193
|
+
operation.key = found ? map->slots[index].key : key;
|
|
1194
|
+
weak_map_begin_reservation_locked(map, &operation.reservation, operation.key,
|
|
1195
|
+
operation.current, operation.hash, &execution);
|
|
1196
|
+
pthread_mutex_unlock(&map->lock);
|
|
1197
|
+
return rb_ensure(weak_map_modify_body, (VALUE)&operation, weak_map_operation_cleanup, (VALUE)&operation);
|
|
1198
|
+
}
|
|
1199
|
+
|
|
1200
|
+
static VALUE
|
|
1201
|
+
weak_map_upsert_body(VALUE opaque)
|
|
1202
|
+
{
|
|
1203
|
+
weak_map_operation_t *operation = (weak_map_operation_t *)opaque;
|
|
1204
|
+
VALUE result = rb_yield(operation->current);
|
|
1205
|
+
containers_check_shareable(result);
|
|
1206
|
+
rb_check_frozen(operation->self);
|
|
1207
|
+
weak_map_execution_context_t execution;
|
|
1208
|
+
weak_map_lock_state(operation->map, &execution);
|
|
1209
|
+
bool stored = !operation->reservation.invalidated;
|
|
1210
|
+
if (stored) {
|
|
1211
|
+
weak_map_store_locked(
|
|
1212
|
+
operation->self,
|
|
1213
|
+
operation->map,
|
|
1214
|
+
operation->key,
|
|
1215
|
+
result,
|
|
1216
|
+
operation->hash,
|
|
1217
|
+
&execution
|
|
1218
|
+
);
|
|
1219
|
+
}
|
|
1220
|
+
weak_map_finish_reservation_locked(operation->map, &operation->reservation);
|
|
1221
|
+
pthread_mutex_unlock(&operation->map->lock);
|
|
1222
|
+
operation->complete = true;
|
|
1223
|
+
return stored ? result : Qnil;
|
|
1224
|
+
}
|
|
1225
|
+
|
|
1226
|
+
static VALUE
|
|
1227
|
+
weak_map_upsert(int argc, VALUE *argv, VALUE self)
|
|
1228
|
+
{
|
|
1229
|
+
rb_check_frozen(self);
|
|
1230
|
+
VALUE key;
|
|
1231
|
+
VALUE initial;
|
|
1232
|
+
VALUE keywords = Qnil;
|
|
1233
|
+
VALUE keyword_values[1] = {Qundef};
|
|
1234
|
+
ID keyword_ids[] = {rb_intern("timeout")};
|
|
1235
|
+
rb_scan_args(argc, argv, "2:", &key, &initial, &keywords);
|
|
1236
|
+
if (!NIL_P(keywords)) rb_get_kwargs(keywords, keyword_ids, 0, 1, keyword_values);
|
|
1237
|
+
|
|
1238
|
+
weak_map_t *map = get_weak_map(self);
|
|
1239
|
+
key = weak_map_prepare_key(map, key);
|
|
1240
|
+
weak_map_operation_t operation = {
|
|
1241
|
+
.self = self,
|
|
1242
|
+
.map = map,
|
|
1243
|
+
.key = key,
|
|
1244
|
+
.complete = false,
|
|
1245
|
+
};
|
|
1246
|
+
containers_check_shareable(initial);
|
|
1247
|
+
rb_need_block();
|
|
1248
|
+
operation.hash = weak_map_key_hash(map, key);
|
|
1249
|
+
rb_check_frozen(self);
|
|
1250
|
+
weak_map_timeout_t timeout = weak_map_parse_timeout(
|
|
1251
|
+
keyword_values[0] == Qundef ? Qnil : keyword_values[0]
|
|
1252
|
+
);
|
|
1253
|
+
|
|
1254
|
+
weak_map_execution_context_t execution;
|
|
1255
|
+
if (!weak_map_lock_for_key_with_timeout(map, key, operation.hash, &timeout, &execution)) return Qnil;
|
|
1256
|
+
bool found;
|
|
1257
|
+
size_t index = weak_map_find_slot(map, key, operation.hash, &found, &execution);
|
|
1258
|
+
if (RB_OBJ_FROZEN(self)) {
|
|
1259
|
+
pthread_mutex_unlock(&map->lock);
|
|
1260
|
+
rb_check_frozen(self);
|
|
1261
|
+
}
|
|
1262
|
+
if (!found) {
|
|
1263
|
+
weak_map_store_locked(self, map, key, initial, operation.hash, &execution);
|
|
1264
|
+
pthread_mutex_unlock(&map->lock);
|
|
1265
|
+
return initial;
|
|
1266
|
+
}
|
|
1267
|
+
operation.current = map->slots[index].value;
|
|
1268
|
+
operation.key = found ? map->slots[index].key : key;
|
|
1269
|
+
weak_map_begin_reservation_locked(map, &operation.reservation, operation.key,
|
|
1270
|
+
operation.current, operation.hash, &execution);
|
|
1271
|
+
pthread_mutex_unlock(&map->lock);
|
|
1272
|
+
return rb_ensure(weak_map_upsert_body, (VALUE)&operation, weak_map_operation_cleanup, (VALUE)&operation);
|
|
1273
|
+
}
|
|
1274
|
+
|
|
1275
|
+
static VALUE
|
|
1276
|
+
weak_map_compare_keys_by_identity_p(VALUE self)
|
|
1277
|
+
{
|
|
1278
|
+
return get_weak_map(self)->compare_keys_by_identity ? Qtrue : Qfalse;
|
|
1279
|
+
}
|
|
1280
|
+
|
|
1281
|
+
static VALUE
|
|
1282
|
+
weak_map_compare_values_by_identity_p(VALUE self)
|
|
1283
|
+
{
|
|
1284
|
+
return get_weak_map(self)->compare_values_by_identity ? Qtrue : Qfalse;
|
|
1285
|
+
}
|
|
1286
|
+
|
|
1287
|
+
static VALUE
|
|
1288
|
+
weak_map_weak_keys_p(VALUE self)
|
|
1289
|
+
{
|
|
1290
|
+
return get_weak_map(self)->weak_keys ? Qtrue : Qfalse;
|
|
1291
|
+
}
|
|
1292
|
+
|
|
1293
|
+
static VALUE
|
|
1294
|
+
weak_map_weak_values_p(VALUE self)
|
|
1295
|
+
{
|
|
1296
|
+
return get_weak_map(self)->weak_values ? Qtrue : Qfalse;
|
|
1297
|
+
}
|
|
1298
|
+
|
|
1299
|
+
static VALUE
|
|
1300
|
+
weak_map_size(VALUE self)
|
|
1301
|
+
{
|
|
1302
|
+
weak_map_t *map = get_weak_map(self);
|
|
1303
|
+
size_t size;
|
|
1304
|
+
weak_map_execution_context_t execution;
|
|
1305
|
+
weak_map_lock_state(map, &execution);
|
|
1306
|
+
size = map->size;
|
|
1307
|
+
pthread_mutex_unlock(&map->lock);
|
|
1308
|
+
return SIZET2NUM(size);
|
|
1309
|
+
}
|
|
1310
|
+
|
|
1311
|
+
static VALUE
|
|
1312
|
+
weak_map_enumerator_size(VALUE self, VALUE arguments, VALUE enumerator)
|
|
1313
|
+
{
|
|
1314
|
+
(void)arguments;
|
|
1315
|
+
(void)enumerator;
|
|
1316
|
+
return weak_map_size(self);
|
|
1317
|
+
}
|
|
1318
|
+
|
|
1319
|
+
static VALUE
|
|
1320
|
+
weak_map_entries_snapshot(weak_map_t *map)
|
|
1321
|
+
{
|
|
1322
|
+
for (;;) {
|
|
1323
|
+
size_t capacity;
|
|
1324
|
+
weak_map_execution_context_t execution;
|
|
1325
|
+
weak_map_lock_state(map, &execution);
|
|
1326
|
+
capacity = map->size;
|
|
1327
|
+
pthread_mutex_unlock(&map->lock);
|
|
1328
|
+
|
|
1329
|
+
VALUE entries = rb_ary_new_capa((long)(capacity * 2));
|
|
1330
|
+
rb_ary_resize(entries, (long)(capacity * 2));
|
|
1331
|
+
weak_map_lock_state(map, &execution);
|
|
1332
|
+
if (map->size > capacity) {
|
|
1333
|
+
pthread_mutex_unlock(&map->lock);
|
|
1334
|
+
continue;
|
|
1335
|
+
}
|
|
1336
|
+
VALUE *entry_slots = RARRAY_PTR(entries);
|
|
1337
|
+
long entry_count = 0;
|
|
1338
|
+
for (size_t index = 0; index < map->capacity; index++) {
|
|
1339
|
+
weak_map_slot_t *slot = &map->slots[index];
|
|
1340
|
+
if (slot->state != WEAK_MAP_OCCUPIED) continue;
|
|
1341
|
+
RB_OBJ_WRITE(entries, &entry_slots[entry_count++], slot->key);
|
|
1342
|
+
RB_OBJ_WRITE(entries, &entry_slots[entry_count++], slot->value);
|
|
1343
|
+
}
|
|
1344
|
+
pthread_mutex_unlock(&map->lock);
|
|
1345
|
+
rb_ary_resize(entries, entry_count);
|
|
1346
|
+
return entries;
|
|
1347
|
+
}
|
|
1348
|
+
}
|
|
1349
|
+
|
|
1350
|
+
static VALUE
|
|
1351
|
+
weak_map_keys(VALUE self)
|
|
1352
|
+
{
|
|
1353
|
+
VALUE entries = weak_map_entries_snapshot(get_weak_map(self));
|
|
1354
|
+
long entry_count = RARRAY_LEN(entries);
|
|
1355
|
+
VALUE keys = rb_ary_new_capa(entry_count / 2);
|
|
1356
|
+
for (long index = 0; index < entry_count; index += 2) {
|
|
1357
|
+
rb_ary_push(keys, RARRAY_AREF(entries, index));
|
|
1358
|
+
}
|
|
1359
|
+
RB_GC_GUARD(entries);
|
|
1360
|
+
return keys;
|
|
1361
|
+
}
|
|
1362
|
+
|
|
1363
|
+
static VALUE
|
|
1364
|
+
weak_map_each(VALUE self)
|
|
1365
|
+
{
|
|
1366
|
+
RETURN_SIZED_ENUMERATOR(self, 0, NULL, weak_map_enumerator_size);
|
|
1367
|
+
VALUE entries = weak_map_entries_snapshot(get_weak_map(self));
|
|
1368
|
+
long entry_count = RARRAY_LEN(entries);
|
|
1369
|
+
for (long index = 0; index < entry_count; index += 2) {
|
|
1370
|
+
rb_yield(rb_assoc_new(RARRAY_AREF(entries, index), RARRAY_AREF(entries, index + 1)));
|
|
1371
|
+
}
|
|
1372
|
+
RB_GC_GUARD(entries);
|
|
1373
|
+
return self;
|
|
1374
|
+
}
|
|
1375
|
+
|
|
1376
|
+
/* A bounded live traversal. No native lock is held across a Ruby yield.
|
|
1377
|
+
* Insertions, rehashing and clearing invalidate the cursor. Deletions and
|
|
1378
|
+
* value replacement are permitted while the table layout stays unchanged. */
|
|
1379
|
+
static VALUE
|
|
1380
|
+
weak_map_each_live(VALUE self)
|
|
1381
|
+
{
|
|
1382
|
+
RETURN_SIZED_ENUMERATOR(self, 0, NULL, weak_map_enumerator_size);
|
|
1383
|
+
weak_map_t *map = get_weak_map(self);
|
|
1384
|
+
weak_map_execution_context_t execution;
|
|
1385
|
+
size_t cursor = 0;
|
|
1386
|
+
uint64_t epoch = 0;
|
|
1387
|
+
bool started = false;
|
|
1388
|
+
const long batch_slots = 64;
|
|
1389
|
+
VALUE batch = rb_ary_new_capa(batch_slots);
|
|
1390
|
+
rb_ary_resize(batch, batch_slots);
|
|
1391
|
+
|
|
1392
|
+
for (;;) {
|
|
1393
|
+
VALUE *storage = RARRAY_PTR(batch);
|
|
1394
|
+
for (long index = 0; index < batch_slots; index++) {
|
|
1395
|
+
RB_OBJ_WRITE(batch, &storage[index], Qnil);
|
|
1396
|
+
}
|
|
1397
|
+
weak_map_lock_state(map, &execution);
|
|
1398
|
+
if (started && epoch != map->iteration_epoch) {
|
|
1399
|
+
pthread_mutex_unlock(&map->lock);
|
|
1400
|
+
rb_raise(rb_eRuntimeError, "map structurally changed during live iteration");
|
|
1401
|
+
}
|
|
1402
|
+
epoch = map->iteration_epoch;
|
|
1403
|
+
started = true;
|
|
1404
|
+
long length = 0;
|
|
1405
|
+
while (cursor < map->capacity && length < batch_slots) {
|
|
1406
|
+
weak_map_slot_t *slot = &map->slots[cursor++];
|
|
1407
|
+
if (slot->state != WEAK_MAP_OCCUPIED) continue;
|
|
1408
|
+
RB_OBJ_WRITE(batch, &storage[length++], slot->key);
|
|
1409
|
+
RB_OBJ_WRITE(batch, &storage[length++], slot->value);
|
|
1410
|
+
}
|
|
1411
|
+
pthread_mutex_unlock(&map->lock);
|
|
1412
|
+
if (length == 0) break;
|
|
1413
|
+
for (long index = 0; index < length; index += 2) {
|
|
1414
|
+
rb_yield(rb_assoc_new(RARRAY_AREF(batch, index), RARRAY_AREF(batch, index + 1)));
|
|
1415
|
+
}
|
|
1416
|
+
}
|
|
1417
|
+
RB_GC_GUARD(batch);
|
|
1418
|
+
return self;
|
|
1419
|
+
}
|
|
1420
|
+
|
|
1421
|
+
static VALUE
|
|
1422
|
+
weak_map_each_key(VALUE self)
|
|
1423
|
+
{
|
|
1424
|
+
RETURN_SIZED_ENUMERATOR(self, 0, NULL, weak_map_enumerator_size);
|
|
1425
|
+
VALUE entries = weak_map_entries_snapshot(get_weak_map(self));
|
|
1426
|
+
long entry_count = RARRAY_LEN(entries);
|
|
1427
|
+
for (long index = 0; index < entry_count; index += 2) {
|
|
1428
|
+
rb_yield(RARRAY_AREF(entries, index));
|
|
1429
|
+
}
|
|
1430
|
+
RB_GC_GUARD(entries);
|
|
1431
|
+
return self;
|
|
1432
|
+
}
|
|
1433
|
+
|
|
1434
|
+
static VALUE
|
|
1435
|
+
weak_map_each_value(VALUE self)
|
|
1436
|
+
{
|
|
1437
|
+
RETURN_SIZED_ENUMERATOR(self, 0, NULL, weak_map_enumerator_size);
|
|
1438
|
+
VALUE entries = weak_map_entries_snapshot(get_weak_map(self));
|
|
1439
|
+
long entry_count = RARRAY_LEN(entries);
|
|
1440
|
+
for (long index = 1; index < entry_count; index += 2) {
|
|
1441
|
+
rb_yield(RARRAY_AREF(entries, index));
|
|
1442
|
+
}
|
|
1443
|
+
RB_GC_GUARD(entries);
|
|
1444
|
+
return self;
|
|
1445
|
+
}
|
|
1446
|
+
|
|
1447
|
+
static VALUE
|
|
1448
|
+
weak_map_get_with_timeout(int argc, VALUE *argv, VALUE self)
|
|
1449
|
+
{
|
|
1450
|
+
VALUE key;
|
|
1451
|
+
VALUE keywords = Qnil;
|
|
1452
|
+
VALUE keyword_values[1] = {Qundef};
|
|
1453
|
+
ID keyword_ids[] = {rb_intern("timeout")};
|
|
1454
|
+
rb_scan_args(argc, argv, "1:", &key, &keywords);
|
|
1455
|
+
if (!NIL_P(keywords)) rb_get_kwargs(keywords, keyword_ids, 0, 1, keyword_values);
|
|
1456
|
+
|
|
1457
|
+
weak_map_t *map = get_weak_map(self);
|
|
1458
|
+
key = weak_map_prepare_key(map, key);
|
|
1459
|
+
st_index_t hash = weak_map_key_hash(map, key);
|
|
1460
|
+
weak_map_timeout_t timeout = weak_map_parse_timeout(
|
|
1461
|
+
keyword_values[0] == Qundef ? Qnil : keyword_values[0]
|
|
1462
|
+
);
|
|
1463
|
+
weak_map_execution_context_t execution;
|
|
1464
|
+
if (!weak_map_lock_for_key_with_timeout(map, key, hash, &timeout, &execution)) {
|
|
1465
|
+
return weak_map_timeout_result();
|
|
1466
|
+
}
|
|
1467
|
+
|
|
1468
|
+
bool found;
|
|
1469
|
+
size_t index = weak_map_find_slot(map, key, hash, &found, &execution);
|
|
1470
|
+
VALUE result = found ? map->slots[index].value : Qnil;
|
|
1471
|
+
pthread_mutex_unlock(&map->lock);
|
|
1472
|
+
return result;
|
|
1473
|
+
}
|
|
1474
|
+
|
|
1475
|
+
static VALUE
|
|
1476
|
+
weak_map_store_with_timeout(int argc, VALUE *argv, VALUE self)
|
|
1477
|
+
{
|
|
1478
|
+
rb_check_frozen(self);
|
|
1479
|
+
VALUE key;
|
|
1480
|
+
VALUE value;
|
|
1481
|
+
VALUE keywords = Qnil;
|
|
1482
|
+
VALUE keyword_values[1] = {Qundef};
|
|
1483
|
+
ID keyword_ids[] = {rb_intern("timeout")};
|
|
1484
|
+
rb_scan_args(argc, argv, "2:", &key, &value, &keywords);
|
|
1485
|
+
if (!NIL_P(keywords)) rb_get_kwargs(keywords, keyword_ids, 0, 1, keyword_values);
|
|
1486
|
+
|
|
1487
|
+
weak_map_t *map = get_weak_map(self);
|
|
1488
|
+
key = weak_map_prepare_key(map, key);
|
|
1489
|
+
containers_check_shareable(value);
|
|
1490
|
+
st_index_t hash = weak_map_key_hash(map, key);
|
|
1491
|
+
rb_check_frozen(self);
|
|
1492
|
+
weak_map_timeout_t timeout = weak_map_parse_timeout(
|
|
1493
|
+
keyword_values[0] == Qundef ? Qnil : keyword_values[0]
|
|
1494
|
+
);
|
|
1495
|
+
weak_map_execution_context_t execution;
|
|
1496
|
+
if (!weak_map_lock_for_key_with_timeout(map, key, hash, &timeout, &execution)) {
|
|
1497
|
+
return weak_map_timeout_result();
|
|
1498
|
+
}
|
|
1499
|
+
weak_map_store_locked(self, map, key, value, hash, &execution);
|
|
1500
|
+
pthread_mutex_unlock(&map->lock);
|
|
1501
|
+
return value;
|
|
1502
|
+
}
|
|
1503
|
+
|
|
1504
|
+
static VALUE
|
|
1505
|
+
weak_map_swap(int argc, VALUE *argv, VALUE self)
|
|
1506
|
+
{
|
|
1507
|
+
rb_check_frozen(self);
|
|
1508
|
+
VALUE key;
|
|
1509
|
+
VALUE replacement;
|
|
1510
|
+
VALUE keywords = Qnil;
|
|
1511
|
+
VALUE keyword_values[1] = {Qundef};
|
|
1512
|
+
ID keyword_ids[] = {rb_intern("timeout")};
|
|
1513
|
+
rb_scan_args(argc, argv, "2:", &key, &replacement, &keywords);
|
|
1514
|
+
if (!NIL_P(keywords)) rb_get_kwargs(keywords, keyword_ids, 0, 1, keyword_values);
|
|
1515
|
+
|
|
1516
|
+
weak_map_t *map = get_weak_map(self);
|
|
1517
|
+
key = weak_map_prepare_key(map, key);
|
|
1518
|
+
containers_check_shareable(replacement);
|
|
1519
|
+
st_index_t hash = weak_map_key_hash(map, key);
|
|
1520
|
+
rb_check_frozen(self);
|
|
1521
|
+
weak_map_timeout_t timeout = weak_map_parse_timeout(
|
|
1522
|
+
keyword_values[0] == Qundef ? Qnil : keyword_values[0]
|
|
1523
|
+
);
|
|
1524
|
+
weak_map_execution_context_t execution;
|
|
1525
|
+
if (!weak_map_lock_for_key_with_timeout(map, key, hash, &timeout, &execution)) {
|
|
1526
|
+
return weak_map_timeout_result();
|
|
1527
|
+
}
|
|
1528
|
+
|
|
1529
|
+
bool found;
|
|
1530
|
+
size_t index = weak_map_find_slot(map, key, hash, &found, &execution);
|
|
1531
|
+
if (RB_OBJ_FROZEN(self)) {
|
|
1532
|
+
pthread_mutex_unlock(&map->lock);
|
|
1533
|
+
rb_check_frozen(self);
|
|
1534
|
+
}
|
|
1535
|
+
VALUE previous = found ? map->slots[index].value : Qnil;
|
|
1536
|
+
weak_map_store_locked(self, map, key, replacement, hash, &execution);
|
|
1537
|
+
pthread_mutex_unlock(&map->lock);
|
|
1538
|
+
return previous;
|
|
1539
|
+
}
|
|
1540
|
+
|
|
1541
|
+
static bool
|
|
1542
|
+
weak_map_values_equal(weak_map_t *map, VALUE left, VALUE right)
|
|
1543
|
+
{
|
|
1544
|
+
return map->compare_values_by_identity ? left == right : RTEST(rb_equal(left, right));
|
|
1545
|
+
}
|
|
1546
|
+
|
|
1547
|
+
static VALUE
|
|
1548
|
+
weak_map_wait_for_value(int argc, VALUE *argv, VALUE self, bool non_nil)
|
|
1549
|
+
{
|
|
1550
|
+
VALUE key;
|
|
1551
|
+
VALUE expected = Qnil;
|
|
1552
|
+
VALUE keywords = Qnil;
|
|
1553
|
+
VALUE keyword_values[1] = {Qundef};
|
|
1554
|
+
ID keyword_ids[] = {rb_intern("timeout")};
|
|
1555
|
+
if (non_nil) rb_scan_args(argc, argv, "1:", &key, &keywords);
|
|
1556
|
+
else rb_scan_args(argc, argv, "2:", &key, &expected, &keywords);
|
|
1557
|
+
if (!NIL_P(keywords)) rb_get_kwargs(keywords, keyword_ids, 0, 1, keyword_values);
|
|
1558
|
+
|
|
1559
|
+
weak_map_t *map = get_weak_map(self);
|
|
1560
|
+
key = weak_map_prepare_key(map, key);
|
|
1561
|
+
if (!non_nil) containers_check_shareable(expected);
|
|
1562
|
+
st_index_t hash = weak_map_key_hash(map, key);
|
|
1563
|
+
weak_map_timeout_t timeout = weak_map_parse_timeout(
|
|
1564
|
+
keyword_values[0] == Qundef ? Qnil : keyword_values[0]
|
|
1565
|
+
);
|
|
1566
|
+
|
|
1567
|
+
for (;;) {
|
|
1568
|
+
bool found;
|
|
1569
|
+
VALUE current = Qnil;
|
|
1570
|
+
uint64_t generation;
|
|
1571
|
+
weak_map_execution_context_t execution;
|
|
1572
|
+
weak_map_lock_state(map, &execution);
|
|
1573
|
+
weak_map_reservation_t *entry = weak_map_find_reservation_locked(map, key, hash, &execution);
|
|
1574
|
+
size_t index = weak_map_find_slot(map, key, hash, &found, &execution);
|
|
1575
|
+
if (found) current = map->slots[index].value;
|
|
1576
|
+
generation = map->generation;
|
|
1577
|
+
pthread_mutex_unlock(&map->lock);
|
|
1578
|
+
|
|
1579
|
+
bool ready = non_nil ? !NIL_P(current) : !weak_map_values_equal(map, current, expected);
|
|
1580
|
+
if (ready) return current;
|
|
1581
|
+
|
|
1582
|
+
weak_map_lock_state(map, &execution);
|
|
1583
|
+
if (map->generation != generation) {
|
|
1584
|
+
pthread_mutex_unlock(&map->lock);
|
|
1585
|
+
continue;
|
|
1586
|
+
}
|
|
1587
|
+
if (entry) weak_map_check_reservation_wait_locked(map, entry, &execution);
|
|
1588
|
+
pthread_mutex_unlock(&map->lock);
|
|
1589
|
+
if (!weak_map_wait_once(map, generation, &timeout)) return weak_map_timeout_result();
|
|
1590
|
+
}
|
|
1591
|
+
}
|
|
1592
|
+
|
|
1593
|
+
static VALUE
|
|
1594
|
+
weak_map_wait_until_changed(int argc, VALUE *argv, VALUE self)
|
|
1595
|
+
{
|
|
1596
|
+
return weak_map_wait_for_value(argc, argv, self, false);
|
|
1597
|
+
}
|
|
1598
|
+
|
|
1599
|
+
static VALUE
|
|
1600
|
+
weak_map_wait_until_non_nil(int argc, VALUE *argv, VALUE self)
|
|
1601
|
+
{
|
|
1602
|
+
return weak_map_wait_for_value(argc, argv, self, true);
|
|
1603
|
+
}
|
|
1604
|
+
|
|
1605
|
+
static VALUE
|
|
1606
|
+
weak_map_update(int argc, VALUE *argv, VALUE self)
|
|
1607
|
+
{
|
|
1608
|
+
rb_check_frozen(self);
|
|
1609
|
+
VALUE key;
|
|
1610
|
+
VALUE keywords = Qnil;
|
|
1611
|
+
VALUE keyword_values[1] = {Qundef};
|
|
1612
|
+
ID keyword_ids[] = {rb_intern("timeout")};
|
|
1613
|
+
rb_scan_args(argc, argv, "1:", &key, &keywords);
|
|
1614
|
+
if (!NIL_P(keywords)) rb_get_kwargs(keywords, keyword_ids, 0, 1, keyword_values);
|
|
1615
|
+
|
|
1616
|
+
weak_map_t *map = get_weak_map(self);
|
|
1617
|
+
key = weak_map_prepare_key(map, key);
|
|
1618
|
+
weak_map_operation_t operation = {
|
|
1619
|
+
.self = self,
|
|
1620
|
+
.map = map,
|
|
1621
|
+
.key = key,
|
|
1622
|
+
.complete = false,
|
|
1623
|
+
};
|
|
1624
|
+
rb_need_block();
|
|
1625
|
+
operation.hash = weak_map_key_hash(map, key);
|
|
1626
|
+
rb_check_frozen(self);
|
|
1627
|
+
weak_map_timeout_t timeout = weak_map_parse_timeout(
|
|
1628
|
+
keyword_values[0] == Qundef ? Qnil : keyword_values[0]
|
|
1629
|
+
);
|
|
1630
|
+
|
|
1631
|
+
weak_map_execution_context_t execution;
|
|
1632
|
+
if (!weak_map_lock_for_key_with_timeout(map, key, operation.hash, &timeout, &execution)) return Qnil;
|
|
1633
|
+
bool found;
|
|
1634
|
+
size_t index = weak_map_find_slot(map, key, operation.hash, &found, &execution);
|
|
1635
|
+
if (RB_OBJ_FROZEN(self)) {
|
|
1636
|
+
pthread_mutex_unlock(&map->lock);
|
|
1637
|
+
rb_check_frozen(self);
|
|
1638
|
+
}
|
|
1639
|
+
operation.current = found ? map->slots[index].value : Qnil;
|
|
1640
|
+
operation.key = found ? map->slots[index].key : key;
|
|
1641
|
+
weak_map_begin_reservation_locked(map, &operation.reservation, operation.key,
|
|
1642
|
+
operation.current, operation.hash, &execution);
|
|
1643
|
+
pthread_mutex_unlock(&map->lock);
|
|
1644
|
+
return rb_ensure(
|
|
1645
|
+
weak_map_upsert_body,
|
|
1646
|
+
(VALUE)&operation,
|
|
1647
|
+
weak_map_operation_cleanup,
|
|
1648
|
+
(VALUE)&operation
|
|
1649
|
+
);
|
|
1650
|
+
}
|
|
1651
|
+
|
|
1652
|
+
static void
|
|
1653
|
+
define_weak_map_methods(VALUE klass)
|
|
1654
|
+
{
|
|
1655
|
+
rb_define_alloc_func(klass, weak_map_allocate);
|
|
1656
|
+
rb_define_method(klass, "initialize", weak_map_initialize, -1);
|
|
1657
|
+
rb_define_method(klass, "[]", weak_map_get, 1);
|
|
1658
|
+
rb_define_method(klass, "[]=", weak_map_set, 2);
|
|
1659
|
+
rb_define_method(klass, "check_mutation", weak_map_check_mutation, 0);
|
|
1660
|
+
rb_define_method(klass, "prepare_mutation_key", weak_map_prepare_mutation_key, 1);
|
|
1661
|
+
rb_define_method(klass, "fetch", weak_map_fetch, -1);
|
|
1662
|
+
rb_define_method(klass, "get", weak_map_get_with_timeout, -1);
|
|
1663
|
+
rb_define_method(klass, "store", weak_map_store_with_timeout, -1);
|
|
1664
|
+
rb_define_method(klass, "swap", weak_map_swap, -1);
|
|
1665
|
+
rb_define_method(klass, "update", weak_map_update, -1);
|
|
1666
|
+
rb_define_method(klass, "modify", weak_map_modify, 1);
|
|
1667
|
+
rb_define_method(klass, "wait_until_changed", weak_map_wait_until_changed, -1);
|
|
1668
|
+
rb_define_method(klass, "wait_until_non_nil", weak_map_wait_until_non_nil, -1);
|
|
1669
|
+
rb_define_method(klass, "store_if_absent", weak_map_store_if_absent, -1);
|
|
1670
|
+
rb_define_method(klass, "key?", weak_map_key_p, 1);
|
|
1671
|
+
rb_define_method(klass, "delete", weak_map_delete, 1);
|
|
1672
|
+
rb_define_method(klass, "clear", weak_map_clear, 0);
|
|
1673
|
+
rb_define_method(klass, "compare_and_set", weak_map_compare_and_set, -1);
|
|
1674
|
+
rb_define_method(klass, "upsert", weak_map_upsert, -1);
|
|
1675
|
+
rb_define_method(klass, "compare_keys_by_identity?", weak_map_compare_keys_by_identity_p, 0);
|
|
1676
|
+
rb_define_method(klass, "compare_values_by_identity?", weak_map_compare_values_by_identity_p, 0);
|
|
1677
|
+
rb_define_method(klass, "weak_keys?", weak_map_weak_keys_p, 0);
|
|
1678
|
+
rb_define_method(klass, "weak_values?", weak_map_weak_values_p, 0);
|
|
1679
|
+
rb_define_method(klass, "getkey", weak_map_getkey, 1);
|
|
1680
|
+
rb_define_method(klass, "size", weak_map_size, 0);
|
|
1681
|
+
rb_define_method(klass, "keys", weak_map_keys, 0);
|
|
1682
|
+
rb_define_method(klass, "each", weak_map_each, 0);
|
|
1683
|
+
rb_define_method(klass, "each_live", weak_map_each_live, 0);
|
|
1684
|
+
rb_define_method(klass, "each_pair", weak_map_each, 0);
|
|
1685
|
+
rb_define_method(klass, "each_key", weak_map_each_key, 0);
|
|
1686
|
+
rb_define_method(klass, "each_value", weak_map_each_value, 0);
|
|
1687
|
+
}
|
|
1688
|
+
|
|
1689
|
+
void
|
|
1690
|
+
containers_init_weak_maps(VALUE namespace)
|
|
1691
|
+
{
|
|
1692
|
+
eIsolationError = rb_const_get(rb_cRactor, rb_intern("IsolationError"));
|
|
1693
|
+
weak_map_keep = rb_const_get(namespace, rb_intern("MAP_KEEP"));
|
|
1694
|
+
weak_map_delete_token = rb_const_get(namespace, rb_intern("MAP_DELETE"));
|
|
1695
|
+
rb_global_variable(&weak_map_keep);
|
|
1696
|
+
rb_global_variable(&weak_map_delete_token);
|
|
1697
|
+
cWeakKeyMap = rb_define_class_under(namespace, "WeakKeyMap", rb_cObject);
|
|
1698
|
+
cWeakValueMap = rb_define_class_under(namespace, "WeakValueMap", rb_cObject);
|
|
1699
|
+
cWeakMap = rb_define_class_under(namespace, "WeakMap", rb_cObject);
|
|
1700
|
+
define_weak_map_methods(cWeakKeyMap);
|
|
1701
|
+
define_weak_map_methods(cWeakValueMap);
|
|
1702
|
+
define_weak_map_methods(cWeakMap);
|
|
1703
|
+
rb_define_const(namespace, "NATIVE_WEAK_MAPS", Qtrue);
|
|
1704
|
+
}
|
|
1705
|
+
|
|
1706
|
+
#else
|
|
1707
|
+
|
|
1708
|
+
void
|
|
1709
|
+
containers_init_weak_maps(VALUE namespace)
|
|
1710
|
+
{
|
|
1711
|
+
(void)namespace;
|
|
1712
|
+
}
|
|
1713
|
+
|
|
1714
|
+
#endif
|