farce 0.0.1.alpha2-x86_64-linux-gnu
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/CODE_OF_CONDUCT.md +26 -0
- data/CONTRIBUTING.md +71 -0
- data/MIT-LICENSE +20 -0
- data/README.md +1524 -0
- data/SECURITY.md +10 -0
- data/docs/benchmarks.md +185 -0
- data/docs/gems/dry-types.md +290 -0
- data/docs/gems/msgpack.md +70 -0
- data/docs/gems/ractor-shim.md +63 -0
- data/docs/modes.md +628 -0
- data/docs/scopes.md +649 -0
- data/docs/variants.md +346 -0
- data/ext/ext_helper.rb +20 -0
- data/ext/farce/README.md +21 -0
- data/ext/farce/atom.c +1023 -0
- data/ext/farce/bounded_map.c +1683 -0
- data/ext/farce/containers.h +77 -0
- data/ext/farce/counter.c +399 -0
- data/ext/farce/darwin.c +100 -0
- data/ext/farce/depend +12 -0
- data/ext/farce/dict.c +1523 -0
- data/ext/farce/dict.h +152 -0
- data/ext/farce/drivers.c +237 -0
- data/ext/farce/exchanger.c +299 -0
- data/ext/farce/extconf.rb +99 -0
- data/ext/farce/farce.c +359 -0
- data/ext/farce/flag.c +288 -0
- data/ext/farce/io.c +338 -0
- data/ext/farce/lock.c +510 -0
- data/ext/farce/map.c +2240 -0
- data/ext/farce/priority_queue.c +2056 -0
- data/ext/farce/queue.c +1059 -0
- data/ext/farce/reactor.c +820 -0
- data/ext/farce/reactor.h +103 -0
- data/ext/farce/shareable.h +31 -0
- data/ext/farce/signal.c +350 -0
- data/ext/farce/transaction.c +354 -0
- data/ext/farce/transaction.h +40 -0
- data/ext/farce/tree_map.c +1953 -0
- data/ext/farce/trie.c +2020 -0
- data/ext/farce/unshareable.c +155 -0
- data/ext/farce/unshared_io_pool.h +234 -0
- data/ext/farce/unshared_signal.c +263 -0
- data/ext/farce/unshared_wait.h +193 -0
- data/ext/farce/unsupported.c +6 -0
- data/ext/farce/vector.c +1195 -0
- data/ext/farce/weak_map.c +1714 -0
- data/ext/java/org/farce/BoundedMap.java +394 -0
- data/ext/java/org/farce/FiberScheduler.java +141 -0
- data/ext/java/org/farce/PriorityKey.java +45 -0
- data/ext/java/org/farce/PriorityQueue.java +373 -0
- data/ext/java/org/farce/QueueSignal.java +28 -0
- data/ext/rebind/README.md +14 -0
- data/ext/rebind/extconf.rb +10 -0
- data/ext/rebind/rebind.c +186 -0
- data/lib/farce/_yard/internal.rb +13 -0
- data/lib/farce/_yard/macros.rb +61 -0
- data/lib/farce/_yard/ractor.rb +46 -0
- data/lib/farce/abstract/atom.rb +186 -0
- data/lib/farce/abstract/bounded_map.rb +232 -0
- data/lib/farce/abstract/collection.rb +151 -0
- data/lib/farce/abstract/concurrent_map.rb +381 -0
- data/lib/farce/abstract/counter.rb +193 -0
- data/lib/farce/abstract/duplicable_map.rb +229 -0
- data/lib/farce/abstract/exchanger.rb +26 -0
- data/lib/farce/abstract/flag.rb +71 -0
- data/lib/farce/abstract/lazy.rb +115 -0
- data/lib/farce/abstract/lease.rb +104 -0
- data/lib/farce/abstract/lease_map.rb +261 -0
- data/lib/farce/abstract/lease_pool.rb +91 -0
- data/lib/farce/abstract/lfu_map.rb +20 -0
- data/lib/farce/abstract/lru_map.rb +25 -0
- data/lib/farce/abstract/map.rb +345 -0
- data/lib/farce/abstract/molecule.rb +245 -0
- data/lib/farce/abstract/port.rb +74 -0
- data/lib/farce/abstract/priority_queue.rb +117 -0
- data/lib/farce/abstract/queue.rb +269 -0
- data/lib/farce/abstract/scheduler.rb +111 -0
- data/lib/farce/abstract/set.rb +910 -0
- data/lib/farce/abstract/sorted_set.rb +172 -0
- data/lib/farce/abstract/timer_queue.rb +136 -0
- data/lib/farce/abstract/tree_map.rb +269 -0
- data/lib/farce/abstract/value.rb +68 -0
- data/lib/farce/abstract/vector.rb +1126 -0
- data/lib/farce/abstract/weak_atom.rb +32 -0
- data/lib/farce/abstract/weak_key_map.rb +12 -0
- data/lib/farce/abstract/weak_map.rb +13 -0
- data/lib/farce/abstract/weak_set.rb +12 -0
- data/lib/farce/abstract/weak_value_map.rb +12 -0
- data/lib/farce/abstract.rb +17 -0
- data/lib/farce/atom.rb +293 -0
- data/lib/farce/class_mirror.rb +87 -0
- data/lib/farce/clock.rb +121 -0
- data/lib/farce/config.rb +229 -0
- data/lib/farce/counter.rb +71 -0
- data/lib/farce/deduper.rb +122 -0
- data/lib/farce/engine/jruby/bounded_map.rb +314 -0
- data/lib/farce/engine/jruby/fiber_scheduler.jar +0 -0
- data/lib/farce/engine/jruby/fiber_scheduler.rb +119 -0
- data/lib/farce/engine/jruby/lease_waiting.rb +19 -0
- data/lib/farce/engine/jruby/map.rb +505 -0
- data/lib/farce/engine/jruby/mutable_numeric_copy.rb +42 -0
- data/lib/farce/engine/jruby/signal.rb +147 -0
- data/lib/farce/engine/jruby.rb +63 -0
- data/lib/farce/engine/jvm/concurrent_weak_registry.rb +55 -0
- data/lib/farce/engine/jvm/counter.rb +102 -0
- data/lib/farce/engine/jvm/extension.rb +32 -0
- data/lib/farce/engine/jvm/farce.jar +0 -0
- data/lib/farce/engine/jvm/flag.rb +79 -0
- data/lib/farce/engine/jvm/priority_queue.rb +217 -0
- data/lib/farce/engine/jvm/tree_map.rb +350 -0
- data/lib/farce/engine/jvm/types.rb +180 -0
- data/lib/farce/engine/jvm.rb +19 -0
- data/lib/farce/engine/ruby/3.4/farce.so +0 -0
- data/lib/farce/engine/ruby/3.4/fiber_scheduler.rb +20 -0
- data/lib/farce/engine/ruby/3.4/port.rb +186 -0
- data/lib/farce/engine/ruby/3.4/ractor_methods.rb +26 -0
- data/lib/farce/engine/ruby/3.4/ractor_selector.rb +92 -0
- data/lib/farce/engine/ruby/3.4/rebind.so +0 -0
- data/lib/farce/engine/ruby/3.4/vault.rb +56 -0
- data/lib/farce/engine/ruby/4.0/farce.so +0 -0
- data/lib/farce/engine/ruby/4.0/port.rb +19 -0
- data/lib/farce/engine/ruby/4.0/ractor_methods.rb +20 -0
- data/lib/farce/engine/ruby/4.0/ractor_selector.rb +108 -0
- data/lib/farce/engine/ruby/4.0/rebind.so +0 -0
- data/lib/farce/engine/ruby/4.0/vault.rb +74 -0
- data/lib/farce/engine/ruby/4.1/port.rb +21 -0
- data/lib/farce/engine/ruby/4.1/ractor_methods.rb +22 -0
- data/lib/farce/engine/ruby/4.1/ractor_selector.rb +25 -0
- data/lib/farce/engine/ruby/4.1/vault.rb +5 -0
- data/lib/farce/engine/ruby/fiber_scheduler.rb +32 -0
- data/lib/farce/engine/ruby/key_lock_map.rb +28 -0
- data/lib/farce/engine/ruby/shared/lease.rb +26 -0
- data/lib/farce/engine/ruby/shared/lease_pool.rb +24 -0
- data/lib/farce/engine/ruby/shared/main_scheduler.rb +9 -0
- data/lib/farce/engine/ruby/shared/parallel_scheduler.rb +9 -0
- data/lib/farce/engine/ruby/shared/proxy_owner.rb +31 -0
- data/lib/farce/engine/ruby/shared/ractor_methods.rb +34 -0
- data/lib/farce/engine/ruby/shared/ractor_selector.rb +328 -0
- data/lib/farce/engine/ruby/shared/strict_map.rb +13 -0
- data/lib/farce/engine/ruby/shared/unshared_vector.rb +14 -0
- data/lib/farce/engine/ruby/shared/vault.rb +144 -0
- data/lib/farce/engine/ruby/shared/vault_weak_map.rb +336 -0
- data/lib/farce/engine/ruby/shared/weak_atom.rb +54 -0
- data/lib/farce/engine/ruby/shared/weak_map.rb +413 -0
- data/lib/farce/engine/ruby.rb +130 -0
- data/lib/farce/engine/shared/atom.rb +10 -0
- data/lib/farce/engine/shared/exchanger.rb +130 -0
- data/lib/farce/engine/shared/identity_key.rb +22 -0
- data/lib/farce/engine/shared/lease.rb +10 -0
- data/lib/farce/engine/shared/lease_pool.rb +10 -0
- data/lib/farce/engine/shared/main_scheduler.rb +20 -0
- data/lib/farce/engine/shared/map_key_coordination.rb +226 -0
- data/lib/farce/engine/shared/parallel_scheduler.rb +9 -0
- data/lib/farce/engine/shared/port.rb +44 -0
- data/lib/farce/engine/shared/portable_bounded_map.rb +619 -0
- data/lib/farce/engine/shared/proxy_owner.rb +43 -0
- data/lib/farce/engine/shared/queue.rb +233 -0
- data/lib/farce/engine/shared/ractor_methods.rb +73 -0
- data/lib/farce/engine/shared/rebindable.rb +15 -0
- data/lib/farce/engine/shared/strict_atom.rb +73 -0
- data/lib/farce/engine/shared/strict_map.rb +117 -0
- data/lib/farce/engine/shared/strict_queue_values.rb +27 -0
- data/lib/farce/engine/shared/strict_tree_map.rb +56 -0
- data/lib/farce/engine/shared/transaction_map_backend.rb +20 -0
- data/lib/farce/engine/shared/trie.rb +317 -0
- data/lib/farce/engine/shared/trie_builder.rb +199 -0
- data/lib/farce/engine/shared/unshareable.rb +14 -0
- data/lib/farce/engine/shared/unshared_atom.rb +256 -0
- data/lib/farce/engine/shared/unshared_priority_queue.rb +9 -0
- data/lib/farce/engine/shared/unshared_queue.rb +18 -0
- data/lib/farce/engine/shared/unshared_signal.rb +21 -0
- data/lib/farce/engine/shared/unshared_vector.rb +377 -0
- data/lib/farce/engine/shared/unshared_weak_atom.rb +30 -0
- data/lib/farce/engine/shared/unshared_weak_map.rb +445 -0
- data/lib/farce/engine/shared/vault.rb +31 -0
- data/lib/farce/engine/shared/vector.rb +83 -0
- data/lib/farce/engine/shared/weak_atom/base.rb +189 -0
- data/lib/farce/engine/shared/weak_atom.rb +20 -0
- data/lib/farce/engine/shared/weak_map/cell.rb +99 -0
- data/lib/farce/engine/shared/weak_map/index.rb +249 -0
- data/lib/farce/engine/shared/weak_map/lock.rb +80 -0
- data/lib/farce/engine/shared/weak_map/reference.rb +33 -0
- data/lib/farce/engine/shared.rb +63 -0
- data/lib/farce/engine/truffleruby/fiber_scheduler.rb +13 -0
- data/lib/farce/engine/truffleruby/lock.rb +121 -0
- data/lib/farce/engine/truffleruby/map.rb +656 -0
- data/lib/farce/engine/truffleruby/native/counter.rb +105 -0
- data/lib/farce/engine/truffleruby/native/flag.rb +77 -0
- data/lib/farce/engine/truffleruby/native/ordered_array_support.rb +67 -0
- data/lib/farce/engine/truffleruby/native/priority_queue.rb +388 -0
- data/lib/farce/engine/truffleruby/native/tree_map.rb +51 -0
- data/lib/farce/engine/truffleruby/native/unsafe_tree_map.rb +350 -0
- data/lib/farce/engine/truffleruby/signal.rb +77 -0
- data/lib/farce/engine/truffleruby.rb +148 -0
- data/lib/farce/envelope.rb +335 -0
- data/lib/farce/error.rb +35 -0
- data/lib/farce/exchanger.rb +41 -0
- data/lib/farce/flag.rb +27 -0
- data/lib/farce/integrations/active_support/blank.rb +68 -0
- data/lib/farce/integrations/active_support/clock.rb +17 -0
- data/lib/farce/integrations/active_support/duplicable.rb +78 -0
- data/lib/farce/integrations/active_support/map.rb +175 -0
- data/lib/farce/integrations/active_support/set.rb +60 -0
- data/lib/farce/integrations/active_support/value_serialization.rb +17 -0
- data/lib/farce/integrations/active_support/vector.rb +231 -0
- data/lib/farce/integrations/active_support.rb +9 -0
- data/lib/farce/integrations/activesupport.rb +5 -0
- data/lib/farce/integrations/bson.rb +108 -0
- data/lib/farce/integrations/cbor.rb +51 -0
- data/lib/farce/integrations/concurrent.rb +112 -0
- data/lib/farce/integrations/dry-types.rb +5 -0
- data/lib/farce/integrations/dry_types.rb +694 -0
- data/lib/farce/integrations/json.rb +7 -0
- data/lib/farce/integrations/msgpack.rb +112 -0
- data/lib/farce/integrations/oj.rb +69 -0
- data/lib/farce/integrations/psych.rb +191 -0
- data/lib/farce/integrations/ractor-sharing.rb +5 -0
- data/lib/farce/integrations/ractor-tmvar.rb +5 -0
- data/lib/farce/integrations/ractor_sharing.rb +172 -0
- data/lib/farce/integrations/ractor_tmvar.rb +64 -0
- data/lib/farce/integrations/shared/to_json.rb +42 -0
- data/lib/farce/integrations/sorted_set.rb +11 -0
- data/lib/farce/integrations/weakref.rb +38 -0
- data/lib/farce/integrations/yajl.rb +10 -0
- data/lib/farce/integrations.rb +156 -0
- data/lib/farce/internal/_frozen_config.rb +11 -0
- data/lib/farce/internal/autoloads.rb +47 -0
- data/lib/farce/internal/blocking_priority_queue.rb +122 -0
- data/lib/farce/internal/converter.rb +106 -0
- data/lib/farce/internal/copyable.rb +31 -0
- data/lib/farce/internal/delegation.rb +20 -0
- data/lib/farce/internal/external_transaction.rb +59 -0
- data/lib/farce/internal/fake_ractor.rb +179 -0
- data/lib/farce/internal/freeze.rb +118 -0
- data/lib/farce/internal/inspect.rb +157 -0
- data/lib/farce/internal/key_lock_map.rb +29 -0
- data/lib/farce/internal/key_normalizer.rb +289 -0
- data/lib/farce/internal/lease_initialization.rb +115 -0
- data/lib/farce/internal/lease_map.rb +441 -0
- data/lib/farce/internal/lease_pool_state.rb +228 -0
- data/lib/farce/internal/lease_state.rb +342 -0
- data/lib/farce/internal/lease_waiting.rb +13 -0
- data/lib/farce/internal/managed_queue.rb +26 -0
- data/lib/farce/internal/map_value_modes.rb +227 -0
- data/lib/farce/internal/marshal_support.rb +227 -0
- data/lib/farce/internal/mixin.rb +20 -0
- data/lib/farce/internal/mutable_ordered_key_lock_map.rb +63 -0
- data/lib/farce/internal/noncopyable.rb +17 -0
- data/lib/farce/internal/ordered_key_lock_map.rb +69 -0
- data/lib/farce/internal/pool_supervisor.rb +41 -0
- data/lib/farce/internal/pool_worker.rb +61 -0
- data/lib/farce/internal/portable_transaction/reservation_entry.rb +29 -0
- data/lib/farce/internal/portable_transaction/strong_map_entry.rb +137 -0
- data/lib/farce/internal/portable_transaction/strong_map_size_entry.rb +44 -0
- data/lib/farce/internal/portable_transaction/tree_entry.rb +64 -0
- data/lib/farce/internal/portable_transaction.rb +164 -0
- data/lib/farce/internal/proxy_owner_notifications.rb +29 -0
- data/lib/farce/internal/reservation_waiting.rb +24 -0
- data/lib/farce/internal/scheduled_task.rb +48 -0
- data/lib/farce/internal/scheduler_io.rb +171 -0
- data/lib/farce/internal/scheduler_lifecycle.rb +314 -0
- data/lib/farce/internal/select_scheduler.rb +235 -0
- data/lib/farce/internal/storage.rb +162 -0
- data/lib/farce/internal/strict_lease.rb +30 -0
- data/lib/farce/internal/strict_lease_map.rb +20 -0
- data/lib/farce/internal/strict_lease_pool.rb +29 -0
- data/lib/farce/internal/thread_pool.rb +166 -0
- data/lib/farce/internal/transaction_conflict.rb +9 -0
- data/lib/farce/internal/transaction_freeze_guard.rb +30 -0
- data/lib/farce/internal/transaction_map_snapshot.rb +123 -0
- data/lib/farce/internal/undefined.rb +22 -0
- data/lib/farce/internal/unshared_lease.rb +29 -0
- data/lib/farce/internal/unshared_lease_pool.rb +34 -0
- data/lib/farce/internal/unshared_queue_waiting.rb +28 -0
- data/lib/farce/internal/value_serialization.rb +13 -0
- data/lib/farce/internal/weak_map_value_modes.rb +58 -0
- data/lib/farce/internal/weak_mode_manager.rb +26 -0
- data/lib/farce/internal.rb +129 -0
- data/lib/farce/lazy.rb +100 -0
- data/lib/farce/lazy_ref.rb +40 -0
- data/lib/farce/lease.rb +28 -0
- data/lib/farce/lease_map.rb +34 -0
- data/lib/farce/lease_pool.rb +29 -0
- data/lib/farce/lfu_map.rb +53 -0
- data/lib/farce/local/atom.rb +23 -0
- data/lib/farce/local/counter.rb +88 -0
- data/lib/farce/local/flag.rb +65 -0
- data/lib/farce/local/lazy.rb +38 -0
- data/lib/farce/local/lazy_ref.rb +36 -0
- data/lib/farce/local/lease.rb +77 -0
- data/lib/farce/local/lease_map.rb +80 -0
- data/lib/farce/local/lease_pool.rb +38 -0
- data/lib/farce/local/lfu_map.rb +55 -0
- data/lib/farce/local/lru_map.rb +62 -0
- data/lib/farce/local/map.rb +47 -0
- data/lib/farce/local/molecule.rb +45 -0
- data/lib/farce/local/priority_queue.rb +53 -0
- data/lib/farce/local/queue.rb +32 -0
- data/lib/farce/local/scoped.rb +166 -0
- data/lib/farce/local/set.rb +18 -0
- data/lib/farce/local/sorted_set.rb +50 -0
- data/lib/farce/local/timer_queue.rb +41 -0
- data/lib/farce/local/tree_map.rb +47 -0
- data/lib/farce/local/vector.rb +30 -0
- data/lib/farce/local/weak_atom.rb +23 -0
- data/lib/farce/local/weak_key_map.rb +39 -0
- data/lib/farce/local/weak_map.rb +39 -0
- data/lib/farce/local/weak_set.rb +18 -0
- data/lib/farce/local/weak_value_map.rb +39 -0
- data/lib/farce/local.rb +35 -0
- data/lib/farce/lock.rb +54 -0
- data/lib/farce/lru_map.rb +63 -0
- data/lib/farce/map.rb +58 -0
- data/lib/farce/mode_manager.rb +142 -0
- data/lib/farce/molecule.rb +60 -0
- data/lib/farce/mutable.rb +171 -0
- data/lib/farce/pool.rb +332 -0
- data/lib/farce/port.rb +134 -0
- data/lib/farce/priority_queue.rb +70 -0
- data/lib/farce/proxy/register.rb +101 -0
- data/lib/farce/proxy/supervisor.rb +137 -0
- data/lib/farce/proxy/wrapper.rb +49 -0
- data/lib/farce/proxy.rb +291 -0
- data/lib/farce/queue.rb +60 -0
- data/lib/farce/ractor.rb +269 -0
- data/lib/farce/read_write_lock.rb +256 -0
- data/lib/farce/reference.rb +158 -0
- data/lib/farce/resolv/dns.rb +42 -0
- data/lib/farce/resolv.rb +85 -0
- data/lib/farce/scheduler.rb +536 -0
- data/lib/farce/set.rb +39 -0
- data/lib/farce/shareable.rb +150 -0
- data/lib/farce/signal.rb +97 -0
- data/lib/farce/sorted_set.rb +36 -0
- data/lib/farce/strict/atom.rb +34 -0
- data/lib/farce/strict/counter.rb +10 -0
- data/lib/farce/strict/exchanger.rb +25 -0
- data/lib/farce/strict/flag.rb +10 -0
- data/lib/farce/strict/lazy.rb +26 -0
- data/lib/farce/strict/lazy_ref.rb +26 -0
- data/lib/farce/strict/lease.rb +27 -0
- data/lib/farce/strict/lease_map.rb +33 -0
- data/lib/farce/strict/lease_pool.rb +28 -0
- data/lib/farce/strict/lfu_map.rb +27 -0
- data/lib/farce/strict/lru_map.rb +28 -0
- data/lib/farce/strict/map.rb +54 -0
- data/lib/farce/strict/molecule.rb +16 -0
- data/lib/farce/strict/port.rb +40 -0
- data/lib/farce/strict/priority_queue.rb +20 -0
- data/lib/farce/strict/queue.rb +17 -0
- data/lib/farce/strict/set.rb +14 -0
- data/lib/farce/strict/sorted_set.rb +19 -0
- data/lib/farce/strict/timer_queue.rb +13 -0
- data/lib/farce/strict/tree_map.rb +27 -0
- data/lib/farce/strict/vector.rb +24 -0
- data/lib/farce/strict/weak_atom.rb +31 -0
- data/lib/farce/strict/weak_key_map.rb +49 -0
- data/lib/farce/strict/weak_map.rb +49 -0
- data/lib/farce/strict/weak_set.rb +14 -0
- data/lib/farce/strict/weak_value_map.rb +48 -0
- data/lib/farce/strict.rb +28 -0
- data/lib/farce/system.rb +40 -0
- data/lib/farce/thread_scheduler.rb +70 -0
- data/lib/farce/timer_queue.rb +66 -0
- data/lib/farce/transaction/atom.rb +73 -0
- data/lib/farce/transaction/map.rb +25 -0
- data/lib/farce/transaction/map_operations.rb +143 -0
- data/lib/farce/transaction/molecule.rb +90 -0
- data/lib/farce/transaction/mutable.rb +61 -0
- data/lib/farce/transaction/set.rb +17 -0
- data/lib/farce/transaction/set_operations.rb +48 -0
- data/lib/farce/transaction/sorted_set.rb +18 -0
- data/lib/farce/transaction/tree_map.rb +78 -0
- data/lib/farce/transaction/vector.rb +139 -0
- data/lib/farce/transaction/wrapper.rb +202 -0
- data/lib/farce/transaction.rb +343 -0
- data/lib/farce/tree_map.rb +56 -0
- data/lib/farce/unsafe/lfu_map.rb +36 -0
- data/lib/farce/unsafe/lru_map.rb +36 -0
- data/lib/farce/unsafe/tree_map.rb +21 -0
- data/lib/farce/unsafe.rb +29 -0
- data/lib/farce/unshareable.rb +67 -0
- data/lib/farce/unshared/atom.rb +24 -0
- data/lib/farce/unshared/counter.rb +10 -0
- data/lib/farce/unshared/flag.rb +10 -0
- data/lib/farce/unshared/lazy.rb +38 -0
- data/lib/farce/unshared/lazy_ref.rb +26 -0
- data/lib/farce/unshared/lease.rb +27 -0
- data/lib/farce/unshared/lease_map.rb +26 -0
- data/lib/farce/unshared/lease_pool.rb +25 -0
- data/lib/farce/unshared/lfu_map.rb +20 -0
- data/lib/farce/unshared/lru_map.rb +20 -0
- data/lib/farce/unshared/map.rb +49 -0
- data/lib/farce/unshared/molecule.rb +16 -0
- data/lib/farce/unshared/priority_queue.rb +16 -0
- data/lib/farce/unshared/queue.rb +27 -0
- data/lib/farce/unshared/set.rb +14 -0
- data/lib/farce/unshared/sorted_set.rb +28 -0
- data/lib/farce/unshared/timer_queue.rb +16 -0
- data/lib/farce/unshared/tree_map.rb +21 -0
- data/lib/farce/unshared/vector.rb +19 -0
- data/lib/farce/unshared/weak_atom.rb +31 -0
- data/lib/farce/unshared/weak_key_map.rb +44 -0
- data/lib/farce/unshared/weak_map.rb +44 -0
- data/lib/farce/unshared/weak_set.rb +14 -0
- data/lib/farce/unshared/weak_value_map.rb +43 -0
- data/lib/farce/unshared.rb +28 -0
- data/lib/farce/vector.rb +228 -0
- data/lib/farce/version.rb +8 -0
- data/lib/farce/walker/definitions.rb +186 -0
- data/lib/farce/walker/modification.rb +355 -0
- data/lib/farce/walker.rb +319 -0
- data/lib/farce/weak_atom.rb +121 -0
- data/lib/farce/weak_key_map.rb +31 -0
- data/lib/farce/weak_map.rb +33 -0
- data/lib/farce/weak_ref.rb +67 -0
- data/lib/farce/weak_set.rb +82 -0
- data/lib/farce/weak_value.rb +195 -0
- data/lib/farce/weak_value_map.rb +32 -0
- data/lib/farce.rb +519 -0
- metadata +468 -0
data/ext/farce/atom.c
ADDED
|
@@ -0,0 +1,1023 @@
|
|
|
1
|
+
#include "containers.h"
|
|
2
|
+
#include "transaction.h"
|
|
3
|
+
#include "ruby/fiber/scheduler.h"
|
|
4
|
+
#include "ruby/io.h"
|
|
5
|
+
|
|
6
|
+
#include <errno.h>
|
|
7
|
+
#include <fcntl.h>
|
|
8
|
+
#include <math.h>
|
|
9
|
+
#include <stdint.h>
|
|
10
|
+
#include <time.h>
|
|
11
|
+
#include <unistd.h>
|
|
12
|
+
|
|
13
|
+
static VALUE cAtom;
|
|
14
|
+
#ifdef RC_HAVE_NATIVE_WEAK_REFERENCES
|
|
15
|
+
static VALUE cWeakAtom;
|
|
16
|
+
static VALUE cUnsharedWeakAtom;
|
|
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
|
+
#endif
|
|
23
|
+
|
|
24
|
+
typedef struct atom_waiter atom_waiter_t;
|
|
25
|
+
|
|
26
|
+
struct atom_waiter {
|
|
27
|
+
int read_fd;
|
|
28
|
+
int write_fd;
|
|
29
|
+
bool notified;
|
|
30
|
+
atom_waiter_t *next;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
typedef struct {
|
|
34
|
+
pthread_mutex_t lock;
|
|
35
|
+
VALUE value;
|
|
36
|
+
atom_waiter_t *waiters;
|
|
37
|
+
uint64_t version;
|
|
38
|
+
bool compare_by_identity;
|
|
39
|
+
uint8_t updating;
|
|
40
|
+
VALUE updating_fiber;
|
|
41
|
+
VALUE updating_thread;
|
|
42
|
+
bool weak;
|
|
43
|
+
bool unshared;
|
|
44
|
+
bool initialized;
|
|
45
|
+
} atom_t;
|
|
46
|
+
|
|
47
|
+
typedef struct {
|
|
48
|
+
bool finite;
|
|
49
|
+
bool collected;
|
|
50
|
+
double deadline;
|
|
51
|
+
} atom_timeout_t;
|
|
52
|
+
|
|
53
|
+
typedef struct {
|
|
54
|
+
VALUE fiber;
|
|
55
|
+
VALUE thread;
|
|
56
|
+
VALUE scheduler;
|
|
57
|
+
} atom_execution_context_t;
|
|
58
|
+
|
|
59
|
+
static atom_execution_context_t
|
|
60
|
+
atom_current_execution_context(void)
|
|
61
|
+
{
|
|
62
|
+
atom_execution_context_t context = {
|
|
63
|
+
.fiber = rb_fiber_current(),
|
|
64
|
+
.thread = rb_thread_current(),
|
|
65
|
+
.scheduler = rb_fiber_scheduler_current(),
|
|
66
|
+
};
|
|
67
|
+
return context;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
static bool atom_wait_once(atom_t *atom, atom_timeout_t *timeout);
|
|
71
|
+
|
|
72
|
+
static double
|
|
73
|
+
atom_monotonic_now(void)
|
|
74
|
+
{
|
|
75
|
+
struct timespec now;
|
|
76
|
+
#ifdef CLOCK_MONOTONIC
|
|
77
|
+
if (clock_gettime(CLOCK_MONOTONIC, &now) == 0) {
|
|
78
|
+
return (double)now.tv_sec + (double)now.tv_nsec / 1000000000.0;
|
|
79
|
+
}
|
|
80
|
+
#endif
|
|
81
|
+
struct timeval fallback;
|
|
82
|
+
gettimeofday(&fallback, NULL);
|
|
83
|
+
return (double)fallback.tv_sec + (double)fallback.tv_usec / 1000000.0;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
static atom_timeout_t
|
|
87
|
+
atom_parse_timeout(VALUE timeout)
|
|
88
|
+
{
|
|
89
|
+
atom_timeout_t parsed = {.finite = false, .deadline = 0};
|
|
90
|
+
if (NIL_P(timeout)) return parsed;
|
|
91
|
+
double seconds = NUM2DBL(timeout);
|
|
92
|
+
if (!isfinite(seconds) || seconds < 0) {
|
|
93
|
+
rb_raise(rb_eArgError, "timeout must be a finite, non-negative number or nil");
|
|
94
|
+
}
|
|
95
|
+
parsed.finite = true;
|
|
96
|
+
parsed.deadline = atom_monotonic_now() + seconds;
|
|
97
|
+
return parsed;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
static void
|
|
101
|
+
atom_set_fd_flags(int fd)
|
|
102
|
+
{
|
|
103
|
+
#ifdef _WIN32
|
|
104
|
+
(void)fd;
|
|
105
|
+
#else
|
|
106
|
+
int flags = fcntl(fd, F_GETFL, 0);
|
|
107
|
+
if (flags >= 0) (void)fcntl(fd, F_SETFL, flags | O_NONBLOCK);
|
|
108
|
+
flags = fcntl(fd, F_GETFD, 0);
|
|
109
|
+
if (flags >= 0) (void)fcntl(fd, F_SETFD, flags | FD_CLOEXEC);
|
|
110
|
+
#endif
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/* Called with atom->lock held. Every waiter owns a separate descriptor, so a
|
|
114
|
+
* single state transition can wake all waiters without consuming a shared
|
|
115
|
+
* notification. */
|
|
116
|
+
static void
|
|
117
|
+
atom_notify_waiters(atom_t *atom)
|
|
118
|
+
{
|
|
119
|
+
unsigned char byte = 1;
|
|
120
|
+
for (atom_waiter_t *waiter = atom->waiters; waiter; waiter = waiter->next) {
|
|
121
|
+
if (waiter->notified) continue;
|
|
122
|
+
ssize_t result;
|
|
123
|
+
do {
|
|
124
|
+
result = write(waiter->write_fd, &byte, 1);
|
|
125
|
+
} while (result < 0 && errno == EINTR);
|
|
126
|
+
if (result == 1 || (result < 0 && (errno == EAGAIN || errno == EWOULDBLOCK))) {
|
|
127
|
+
waiter->notified = true;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
static void
|
|
133
|
+
atom_changed(atom_t *atom)
|
|
134
|
+
{
|
|
135
|
+
atom->version++;
|
|
136
|
+
atom_notify_waiters(atom);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
static void
|
|
140
|
+
atom_finished_update(atom_t *atom, bool changed)
|
|
141
|
+
{
|
|
142
|
+
if (changed) atom->version++;
|
|
143
|
+
atom->updating = false;
|
|
144
|
+
atom->updating_fiber = Qnil;
|
|
145
|
+
atom->updating_thread = Qnil;
|
|
146
|
+
atom_notify_waiters(atom);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
static void
|
|
150
|
+
atom_mark(void *pointer)
|
|
151
|
+
{
|
|
152
|
+
atom_t *atom = pointer;
|
|
153
|
+
if (!atom->weak) rb_gc_mark_movable(atom->value);
|
|
154
|
+
rb_gc_mark_movable(atom->updating_fiber);
|
|
155
|
+
rb_gc_mark_movable(atom->updating_thread);
|
|
156
|
+
}
|
|
157
|
+
static void
|
|
158
|
+
atom_compact(void *pointer)
|
|
159
|
+
{
|
|
160
|
+
atom_t *atom = pointer;
|
|
161
|
+
atom->value = rb_gc_location(atom->value);
|
|
162
|
+
atom->updating_fiber = rb_gc_location(atom->updating_fiber);
|
|
163
|
+
atom->updating_thread = rb_gc_location(atom->updating_thread);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
#ifdef RC_HAVE_NATIVE_WEAK_REFERENCES
|
|
167
|
+
/* The VM invokes this after marking while mutators are stopped. Taking the atom
|
|
168
|
+
* mutex here could deadlock if GC stopped a mutator while it owned the mutex. */
|
|
169
|
+
static void
|
|
170
|
+
atom_handle_weak_references(void *pointer)
|
|
171
|
+
{
|
|
172
|
+
atom_t *atom = pointer;
|
|
173
|
+
if (!atom->weak || NIL_P(atom->value)) return;
|
|
174
|
+
if (rb_gc_handle_weak_references_alive_p(atom->value)) return;
|
|
175
|
+
|
|
176
|
+
atom->value = Qnil;
|
|
177
|
+
atom_changed(atom);
|
|
178
|
+
}
|
|
179
|
+
#endif
|
|
180
|
+
|
|
181
|
+
static void
|
|
182
|
+
atom_free(void *pointer)
|
|
183
|
+
{
|
|
184
|
+
atom_t *atom = pointer;
|
|
185
|
+
pthread_mutex_destroy(&atom->lock);
|
|
186
|
+
ruby_xfree(atom);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
static size_t
|
|
190
|
+
atom_memsize(const void *pointer)
|
|
191
|
+
{
|
|
192
|
+
return pointer ? sizeof(atom_t) : 0;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
static const rb_data_type_t atom_type = {
|
|
196
|
+
.wrap_struct_name = "Ractor::Containers::Atom",
|
|
197
|
+
.function = {
|
|
198
|
+
.dmark = atom_mark,
|
|
199
|
+
.dfree = atom_free,
|
|
200
|
+
.dsize = atom_memsize,
|
|
201
|
+
.dcompact = atom_compact,
|
|
202
|
+
#ifdef RC_HAVE_NATIVE_WEAK_REFERENCES
|
|
203
|
+
.handle_weak_references = atom_handle_weak_references,
|
|
204
|
+
#endif
|
|
205
|
+
},
|
|
206
|
+
.flags = RUBY_TYPED_FROZEN_SHAREABLE,
|
|
207
|
+
};
|
|
208
|
+
|
|
209
|
+
static void
|
|
210
|
+
atom_validate_references(VALUE self)
|
|
211
|
+
{
|
|
212
|
+
atom_t *atom;
|
|
213
|
+
TypedData_Get_Struct(self, atom_t, &atom_type, atom);
|
|
214
|
+
containers_check_shareable(atom->value);
|
|
215
|
+
containers_check_shareable(atom->updating_fiber);
|
|
216
|
+
containers_check_shareable(atom->updating_thread);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
#ifdef RC_HAVE_NATIVE_WEAK_REFERENCES
|
|
220
|
+
/* A separate type keeps mutable referents local even if the wrapper is frozen. */
|
|
221
|
+
static const rb_data_type_t unshared_weak_atom_type = {
|
|
222
|
+
.wrap_struct_name = "Farce::Internal::UnsharedWeakAtom",
|
|
223
|
+
.function = {
|
|
224
|
+
.dmark = atom_mark,
|
|
225
|
+
.dfree = atom_free,
|
|
226
|
+
.dsize = atom_memsize,
|
|
227
|
+
.dcompact = atom_compact,
|
|
228
|
+
.handle_weak_references = atom_handle_weak_references,
|
|
229
|
+
},
|
|
230
|
+
.parent = &atom_type,
|
|
231
|
+
};
|
|
232
|
+
#endif
|
|
233
|
+
|
|
234
|
+
static VALUE
|
|
235
|
+
atom_allocate(VALUE klass)
|
|
236
|
+
{
|
|
237
|
+
atom_t *atom;
|
|
238
|
+
const rb_data_type_t *type = &atom_type;
|
|
239
|
+
bool unshared = false;
|
|
240
|
+
#ifdef RC_HAVE_NATIVE_WEAK_REFERENCES
|
|
241
|
+
unshared = cUnsharedWeakAtom &&
|
|
242
|
+
(klass == cUnsharedWeakAtom || rb_class_inherited_p(klass, cUnsharedWeakAtom) == Qtrue);
|
|
243
|
+
if (unshared) type = &unshared_weak_atom_type;
|
|
244
|
+
#endif
|
|
245
|
+
VALUE object = TypedData_Make_Struct(klass, atom_t, type, atom);
|
|
246
|
+
atom->unshared = unshared;
|
|
247
|
+
pthread_mutex_init(&atom->lock, NULL);
|
|
248
|
+
atom->value = Qnil;
|
|
249
|
+
atom->waiters = NULL;
|
|
250
|
+
atom->version = 0;
|
|
251
|
+
atom->compare_by_identity = false;
|
|
252
|
+
atom->updating = false;
|
|
253
|
+
atom->updating_fiber = Qnil;
|
|
254
|
+
atom->updating_thread = Qnil;
|
|
255
|
+
#ifdef RC_HAVE_NATIVE_WEAK_REFERENCES
|
|
256
|
+
atom->weak = unshared || klass == cWeakAtom ||
|
|
257
|
+
(cWeakAtom && rb_class_inherited_p(klass, cWeakAtom) == Qtrue);
|
|
258
|
+
#else
|
|
259
|
+
atom->weak = false;
|
|
260
|
+
#endif
|
|
261
|
+
atom->initialized = false;
|
|
262
|
+
return object;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
static atom_t *
|
|
266
|
+
get_atom(VALUE self)
|
|
267
|
+
{
|
|
268
|
+
atom_t *atom;
|
|
269
|
+
TypedData_Get_Struct(self, atom_t, &atom_type, atom);
|
|
270
|
+
if (!atom->initialized) rb_raise(rb_eRuntimeError, "uninitialized Atom");
|
|
271
|
+
return atom;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
static void
|
|
275
|
+
atom_check_value(atom_t *atom, VALUE value)
|
|
276
|
+
{
|
|
277
|
+
if (!atom->unshared) containers_check_shareable(value);
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
/* Called with atom->lock held. Raising releases the short native mutex so the
|
|
281
|
+
* reservation owner's ensure handler can clear it normally. */
|
|
282
|
+
static void
|
|
283
|
+
atom_check_update_wait_locked(atom_t *atom, const atom_execution_context_t *context)
|
|
284
|
+
{
|
|
285
|
+
if (atom->updating_fiber == context->fiber) {
|
|
286
|
+
pthread_mutex_unlock(&atom->lock);
|
|
287
|
+
rb_raise(rb_eThreadError, "deadlock; recursive atom access during an update");
|
|
288
|
+
}
|
|
289
|
+
if (atom->updating_thread == context->thread && NIL_P(context->scheduler)) {
|
|
290
|
+
pthread_mutex_unlock(&atom->lock);
|
|
291
|
+
rb_raise(
|
|
292
|
+
rb_eThreadError,
|
|
293
|
+
"deadlock; atom update is owned by another unscheduled fiber"
|
|
294
|
+
);
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
static void
|
|
299
|
+
atom_begin_update_locked(atom_t *atom, const atom_execution_context_t *context)
|
|
300
|
+
{
|
|
301
|
+
atom->updating = true;
|
|
302
|
+
atom->updating_fiber = context->fiber;
|
|
303
|
+
atom->updating_thread = context->thread;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
static void
|
|
307
|
+
atom_lock_for_update(atom_t *atom, const atom_execution_context_t *context)
|
|
308
|
+
{
|
|
309
|
+
atom_timeout_t timeout = {.finite = false, .deadline = 0};
|
|
310
|
+
|
|
311
|
+
for (;;) {
|
|
312
|
+
pthread_mutex_lock(&atom->lock);
|
|
313
|
+
if (!atom->updating) return;
|
|
314
|
+
atom_check_update_wait_locked(atom, context);
|
|
315
|
+
(void)atom_wait_once(atom, &timeout);
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
static VALUE
|
|
320
|
+
atom_extract_timeout(int argc, VALUE *argv, const char *format, VALUE *argument)
|
|
321
|
+
{
|
|
322
|
+
VALUE keywords = Qnil;
|
|
323
|
+
VALUE timeout = Qnil;
|
|
324
|
+
ID keyword_ids[] = {rb_intern("timeout")};
|
|
325
|
+
VALUE keyword_values[1];
|
|
326
|
+
|
|
327
|
+
if (argument) rb_scan_args(argc, argv, format, argument, &keywords);
|
|
328
|
+
else rb_scan_args(argc, argv, format, &keywords);
|
|
329
|
+
if (!NIL_P(keywords)) {
|
|
330
|
+
rb_get_kwargs(keywords, keyword_ids, 0, 1, keyword_values);
|
|
331
|
+
if (keyword_values[0] != Qundef) timeout = keyword_values[0];
|
|
332
|
+
}
|
|
333
|
+
return timeout;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
typedef struct {
|
|
337
|
+
atom_t *atom;
|
|
338
|
+
atom_waiter_t waiter;
|
|
339
|
+
atom_timeout_t *timeout;
|
|
340
|
+
} atom_wait_context_t;
|
|
341
|
+
|
|
342
|
+
static bool
|
|
343
|
+
atom_wait_for_descriptor(int fd, atom_timeout_t *timeout)
|
|
344
|
+
{
|
|
345
|
+
VALUE wait_timeout = Qnil;
|
|
346
|
+
if (timeout->finite) {
|
|
347
|
+
double remaining = timeout->deadline - atom_monotonic_now();
|
|
348
|
+
if (remaining <= 0) return false;
|
|
349
|
+
wait_timeout = DBL2NUM(remaining);
|
|
350
|
+
}
|
|
351
|
+
return containers_wait_for_readable(fd, wait_timeout);
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
static VALUE
|
|
355
|
+
atom_wait_body(VALUE opaque)
|
|
356
|
+
{
|
|
357
|
+
atom_wait_context_t *context = (atom_wait_context_t *)opaque;
|
|
358
|
+
return atom_wait_for_descriptor(context->waiter.read_fd, context->timeout) ? Qtrue : Qfalse;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
static VALUE
|
|
362
|
+
atom_wait_cleanup(VALUE opaque)
|
|
363
|
+
{
|
|
364
|
+
atom_wait_context_t *context = (atom_wait_context_t *)opaque;
|
|
365
|
+
atom_waiter_t **link;
|
|
366
|
+
|
|
367
|
+
pthread_mutex_lock(&context->atom->lock);
|
|
368
|
+
for (link = &context->atom->waiters; *link; link = &(*link)->next) {
|
|
369
|
+
if (*link == &context->waiter) {
|
|
370
|
+
*link = context->waiter.next;
|
|
371
|
+
break;
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
pthread_mutex_unlock(&context->atom->lock);
|
|
375
|
+
close(context->waiter.read_fd);
|
|
376
|
+
close(context->waiter.write_fd);
|
|
377
|
+
return Qnil;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
/* Called with atom->lock held and always returns with it released. */
|
|
381
|
+
static bool
|
|
382
|
+
atom_wait_once(atom_t *atom, atom_timeout_t *timeout)
|
|
383
|
+
{
|
|
384
|
+
int descriptors[2];
|
|
385
|
+
if (pipe(descriptors) != 0) {
|
|
386
|
+
int error = errno;
|
|
387
|
+
pthread_mutex_unlock(&atom->lock);
|
|
388
|
+
double seconds = timeout->finite ? timeout->deadline - atom_monotonic_now() : 0;
|
|
389
|
+
if ((error == EMFILE || error == ENFILE) && timeout->finite && seconds <= 0) return false;
|
|
390
|
+
VALUE remaining = timeout->finite ? DBL2NUM(seconds) : Qnil;
|
|
391
|
+
containers_wait_without_descriptor(error, "pipe", &timeout->collected, remaining);
|
|
392
|
+
return true;
|
|
393
|
+
}
|
|
394
|
+
atom_set_fd_flags(descriptors[0]);
|
|
395
|
+
atom_set_fd_flags(descriptors[1]);
|
|
396
|
+
|
|
397
|
+
atom_wait_context_t context = {
|
|
398
|
+
.atom = atom,
|
|
399
|
+
.waiter = {
|
|
400
|
+
.read_fd = descriptors[0],
|
|
401
|
+
.write_fd = descriptors[1],
|
|
402
|
+
.notified = false,
|
|
403
|
+
.next = atom->waiters,
|
|
404
|
+
},
|
|
405
|
+
.timeout = timeout,
|
|
406
|
+
};
|
|
407
|
+
atom->waiters = &context.waiter;
|
|
408
|
+
pthread_mutex_unlock(&atom->lock);
|
|
409
|
+
return RTEST(rb_ensure(atom_wait_body, (VALUE)&context, atom_wait_cleanup, (VALUE)&context));
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
/* Returns with atom->lock held on success and released on timeout. */
|
|
413
|
+
static bool
|
|
414
|
+
atom_lock_for_update_with_timeout(
|
|
415
|
+
atom_t *atom,
|
|
416
|
+
atom_timeout_t *timeout,
|
|
417
|
+
const atom_execution_context_t *context
|
|
418
|
+
)
|
|
419
|
+
{
|
|
420
|
+
for (;;) {
|
|
421
|
+
pthread_mutex_lock(&atom->lock);
|
|
422
|
+
if (!atom->updating) return true;
|
|
423
|
+
atom_check_update_wait_locked(atom, context);
|
|
424
|
+
if (!atom_wait_once(atom, timeout)) return false;
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
/* Returns with the state mutex held. Ordinary updates still permit value
|
|
429
|
+
* reads. Only the external commit reservation excludes them. */
|
|
430
|
+
static bool
|
|
431
|
+
atom_lock_for_read(atom_t *atom, atom_timeout_t *timeout)
|
|
432
|
+
{
|
|
433
|
+
for (;;) {
|
|
434
|
+
pthread_mutex_lock(&atom->lock);
|
|
435
|
+
if (atom->updating != FARCE_TRANSACTION_RESERVED) return true;
|
|
436
|
+
pthread_mutex_unlock(&atom->lock);
|
|
437
|
+
atom_execution_context_t context = atom_current_execution_context();
|
|
438
|
+
pthread_mutex_lock(&atom->lock);
|
|
439
|
+
if (atom->updating != FARCE_TRANSACTION_RESERVED) return true;
|
|
440
|
+
atom_check_update_wait_locked(atom, &context);
|
|
441
|
+
if (!atom_wait_once(atom, timeout) && timeout->finite) return false;
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
static VALUE
|
|
446
|
+
atom_reserved_value(VALUE self)
|
|
447
|
+
{
|
|
448
|
+
atom_t *atom = get_atom(self);
|
|
449
|
+
atom_timeout_t timeout = {.finite = false, .deadline = 0};
|
|
450
|
+
(void)atom_lock_for_read(atom, &timeout);
|
|
451
|
+
VALUE value = atom->value;
|
|
452
|
+
pthread_mutex_unlock(&atom->lock);
|
|
453
|
+
return value;
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
static VALUE
|
|
457
|
+
atom_freeze(VALUE self)
|
|
458
|
+
{
|
|
459
|
+
atom_t *atom;
|
|
460
|
+
TypedData_Get_Struct(self, atom_t, &atom_type, atom);
|
|
461
|
+
atom_timeout_t timeout = {.finite = false, .deadline = 0};
|
|
462
|
+
(void)atom_lock_for_read(atom, &timeout);
|
|
463
|
+
rb_obj_freeze(self);
|
|
464
|
+
pthread_mutex_unlock(&atom->lock);
|
|
465
|
+
return self;
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
static VALUE
|
|
469
|
+
atom_timeout_result(void)
|
|
470
|
+
{
|
|
471
|
+
return rb_block_given_p() ? rb_yield_values(0) : Qnil;
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
static VALUE
|
|
475
|
+
atom_initialize(int argc, VALUE *argv, VALUE self)
|
|
476
|
+
{
|
|
477
|
+
VALUE value = Qnil;
|
|
478
|
+
VALUE keywords = Qnil;
|
|
479
|
+
VALUE identity = Qundef;
|
|
480
|
+
ID keyword_ids[] = {rb_intern("compare_by_identity")};
|
|
481
|
+
VALUE keyword_values[1];
|
|
482
|
+
atom_t *atom;
|
|
483
|
+
|
|
484
|
+
rb_check_frozen(self);
|
|
485
|
+
rb_scan_args(argc, argv, "01:", &value, &keywords);
|
|
486
|
+
if (!NIL_P(keywords)) {
|
|
487
|
+
rb_get_kwargs(keywords, keyword_ids, 0, 1, keyword_values);
|
|
488
|
+
identity = keyword_values[0];
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
TypedData_Get_Struct(self, atom_t, &atom_type, atom);
|
|
492
|
+
if (atom->initialized) rb_raise(rb_eRuntimeError, "Atom is already initialized");
|
|
493
|
+
atom_check_value(atom, value);
|
|
494
|
+
atom->value = value;
|
|
495
|
+
atom->compare_by_identity = identity == Qundef ? false : containers_strict_bool(identity, "compare_by_identity");
|
|
496
|
+
#ifdef RC_HAVE_NATIVE_WEAK_REFERENCES
|
|
497
|
+
if (atom->weak) rb_gc_declare_weak_references(self);
|
|
498
|
+
#endif
|
|
499
|
+
atom->initialized = true;
|
|
500
|
+
if (atom->unshared) return self;
|
|
501
|
+
return containers_publish_native_with_references(self, atom_validate_references);
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
static VALUE
|
|
505
|
+
atom_value(VALUE self)
|
|
506
|
+
{
|
|
507
|
+
atom_t *atom = get_atom(self);
|
|
508
|
+
VALUE value;
|
|
509
|
+
pthread_mutex_lock(&atom->lock);
|
|
510
|
+
value = atom->value;
|
|
511
|
+
pthread_mutex_unlock(&atom->lock);
|
|
512
|
+
return value;
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
static VALUE
|
|
516
|
+
atom_set_value(VALUE self, VALUE value)
|
|
517
|
+
{
|
|
518
|
+
atom_t *atom = get_atom(self);
|
|
519
|
+
rb_check_frozen(self);
|
|
520
|
+
atom_check_value(atom, value);
|
|
521
|
+
atom_execution_context_t execution = atom_current_execution_context();
|
|
522
|
+
atom_lock_for_update(atom, &execution);
|
|
523
|
+
atom->value = value;
|
|
524
|
+
atom_changed(atom);
|
|
525
|
+
pthread_mutex_unlock(&atom->lock);
|
|
526
|
+
return value;
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
static VALUE
|
|
530
|
+
atom_get(int argc, VALUE *argv, VALUE self)
|
|
531
|
+
{
|
|
532
|
+
atom_t *atom = get_atom(self);
|
|
533
|
+
atom_timeout_t timeout = atom_parse_timeout(atom_extract_timeout(argc, argv, "0:", NULL));
|
|
534
|
+
atom_execution_context_t execution = atom_current_execution_context();
|
|
535
|
+
|
|
536
|
+
for (;;) {
|
|
537
|
+
pthread_mutex_lock(&atom->lock);
|
|
538
|
+
if (!atom->updating) {
|
|
539
|
+
VALUE value = atom->value;
|
|
540
|
+
pthread_mutex_unlock(&atom->lock);
|
|
541
|
+
return value;
|
|
542
|
+
}
|
|
543
|
+
atom_check_update_wait_locked(atom, &execution);
|
|
544
|
+
if (!atom_wait_once(atom, &timeout)) return atom_timeout_result();
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
static VALUE
|
|
549
|
+
atom_store(int argc, VALUE *argv, VALUE self)
|
|
550
|
+
{
|
|
551
|
+
VALUE value;
|
|
552
|
+
atom_t *atom = get_atom(self);
|
|
553
|
+
rb_check_frozen(self);
|
|
554
|
+
VALUE timeout_value = atom_extract_timeout(argc, argv, "1:", &value);
|
|
555
|
+
atom_check_value(atom, value);
|
|
556
|
+
atom_timeout_t timeout = atom_parse_timeout(timeout_value);
|
|
557
|
+
rb_check_frozen(self);
|
|
558
|
+
atom_execution_context_t execution = atom_current_execution_context();
|
|
559
|
+
|
|
560
|
+
for (;;) {
|
|
561
|
+
pthread_mutex_lock(&atom->lock);
|
|
562
|
+
if (!atom->updating) {
|
|
563
|
+
atom->value = value;
|
|
564
|
+
atom_changed(atom);
|
|
565
|
+
pthread_mutex_unlock(&atom->lock);
|
|
566
|
+
return value;
|
|
567
|
+
}
|
|
568
|
+
atom_check_update_wait_locked(atom, &execution);
|
|
569
|
+
if (!atom_wait_once(atom, &timeout)) return atom_timeout_result();
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
static VALUE
|
|
574
|
+
atom_swap(int argc, VALUE *argv, VALUE self)
|
|
575
|
+
{
|
|
576
|
+
VALUE value;
|
|
577
|
+
atom_t *atom = get_atom(self);
|
|
578
|
+
rb_check_frozen(self);
|
|
579
|
+
VALUE timeout_value = atom_extract_timeout(argc, argv, "1:", &value);
|
|
580
|
+
atom_check_value(atom, value);
|
|
581
|
+
atom_timeout_t timeout = atom_parse_timeout(timeout_value);
|
|
582
|
+
rb_check_frozen(self);
|
|
583
|
+
atom_execution_context_t execution = atom_current_execution_context();
|
|
584
|
+
|
|
585
|
+
for (;;) {
|
|
586
|
+
pthread_mutex_lock(&atom->lock);
|
|
587
|
+
if (!atom->updating) {
|
|
588
|
+
VALUE previous = atom->value;
|
|
589
|
+
atom->value = value;
|
|
590
|
+
atom_changed(atom);
|
|
591
|
+
pthread_mutex_unlock(&atom->lock);
|
|
592
|
+
return previous;
|
|
593
|
+
}
|
|
594
|
+
atom_check_update_wait_locked(atom, &execution);
|
|
595
|
+
if (!atom_wait_once(atom, &timeout)) return atom_timeout_result();
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
typedef struct {
|
|
600
|
+
VALUE self;
|
|
601
|
+
atom_t *atom;
|
|
602
|
+
atom_execution_context_t execution;
|
|
603
|
+
VALUE current;
|
|
604
|
+
VALUE argument;
|
|
605
|
+
bool identity;
|
|
606
|
+
bool complete;
|
|
607
|
+
} atom_operation_t;
|
|
608
|
+
|
|
609
|
+
static VALUE
|
|
610
|
+
atom_operation_cleanup(VALUE opaque)
|
|
611
|
+
{
|
|
612
|
+
atom_operation_t *operation = (atom_operation_t *)opaque;
|
|
613
|
+
if (!operation->complete) {
|
|
614
|
+
pthread_mutex_lock(&operation->atom->lock);
|
|
615
|
+
atom_finished_update(operation->atom, false);
|
|
616
|
+
pthread_mutex_unlock(&operation->atom->lock);
|
|
617
|
+
}
|
|
618
|
+
return Qnil;
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
static VALUE
|
|
622
|
+
atom_store_body(VALUE opaque)
|
|
623
|
+
{
|
|
624
|
+
atom_operation_t *operation = (atom_operation_t *)opaque;
|
|
625
|
+
VALUE result = rb_yield_values(0);
|
|
626
|
+
rb_check_frozen(operation->self);
|
|
627
|
+
atom_check_value(operation->atom, result);
|
|
628
|
+
pthread_mutex_lock(&operation->atom->lock);
|
|
629
|
+
operation->atom->value = result;
|
|
630
|
+
atom_finished_update(operation->atom, true);
|
|
631
|
+
pthread_mutex_unlock(&operation->atom->lock);
|
|
632
|
+
operation->complete = true;
|
|
633
|
+
return result;
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
static VALUE
|
|
637
|
+
atom_store_if_absent(int argc, VALUE *argv, VALUE self)
|
|
638
|
+
{
|
|
639
|
+
atom_t *atom = get_atom(self);
|
|
640
|
+
atom_operation_t operation = {
|
|
641
|
+
.self = self,
|
|
642
|
+
.atom = atom,
|
|
643
|
+
.execution = atom_current_execution_context(),
|
|
644
|
+
.complete = false,
|
|
645
|
+
};
|
|
646
|
+
rb_check_frozen(self);
|
|
647
|
+
atom_timeout_t timeout = atom_parse_timeout(atom_extract_timeout(argc, argv, "0:", NULL));
|
|
648
|
+
rb_check_frozen(self);
|
|
649
|
+
|
|
650
|
+
rb_need_block();
|
|
651
|
+
if (!atom_lock_for_update_with_timeout(atom, &timeout, &operation.execution)) return Qnil;
|
|
652
|
+
if (!NIL_P(atom->value)) {
|
|
653
|
+
VALUE value = atom->value;
|
|
654
|
+
pthread_mutex_unlock(&atom->lock);
|
|
655
|
+
return value;
|
|
656
|
+
}
|
|
657
|
+
atom_begin_update_locked(atom, &operation.execution);
|
|
658
|
+
pthread_mutex_unlock(&atom->lock);
|
|
659
|
+
return rb_ensure(atom_store_body, (VALUE)&operation, atom_operation_cleanup, (VALUE)&operation);
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
typedef struct {
|
|
663
|
+
VALUE self;
|
|
664
|
+
atom_t *atom;
|
|
665
|
+
atom_execution_context_t execution;
|
|
666
|
+
VALUE current;
|
|
667
|
+
VALUE expected;
|
|
668
|
+
VALUE replacement;
|
|
669
|
+
bool identity;
|
|
670
|
+
bool complete;
|
|
671
|
+
} atom_cas_t;
|
|
672
|
+
|
|
673
|
+
static VALUE
|
|
674
|
+
atom_cas_cleanup(VALUE opaque)
|
|
675
|
+
{
|
|
676
|
+
atom_cas_t *operation = (atom_cas_t *)opaque;
|
|
677
|
+
if (!operation->complete) {
|
|
678
|
+
pthread_mutex_lock(&operation->atom->lock);
|
|
679
|
+
atom_finished_update(operation->atom, false);
|
|
680
|
+
pthread_mutex_unlock(&operation->atom->lock);
|
|
681
|
+
}
|
|
682
|
+
return Qnil;
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
static VALUE
|
|
686
|
+
atom_cas_body(VALUE opaque)
|
|
687
|
+
{
|
|
688
|
+
atom_cas_t *operation = (atom_cas_t *)opaque;
|
|
689
|
+
bool matches = operation->identity
|
|
690
|
+
? operation->current == operation->expected
|
|
691
|
+
: RTEST(rb_equal(operation->current, operation->expected));
|
|
692
|
+
rb_check_frozen(operation->self);
|
|
693
|
+
|
|
694
|
+
pthread_mutex_lock(&operation->atom->lock);
|
|
695
|
+
if (matches) operation->atom->value = operation->replacement;
|
|
696
|
+
atom_finished_update(operation->atom, matches);
|
|
697
|
+
pthread_mutex_unlock(&operation->atom->lock);
|
|
698
|
+
operation->complete = true;
|
|
699
|
+
return matches ? Qtrue : Qfalse;
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
static VALUE
|
|
703
|
+
atom_compare_and_set(int argc, VALUE *argv, VALUE self)
|
|
704
|
+
{
|
|
705
|
+
VALUE expected;
|
|
706
|
+
VALUE replacement;
|
|
707
|
+
VALUE keywords = Qnil;
|
|
708
|
+
VALUE keyword_values[1] = {Qundef};
|
|
709
|
+
ID keyword_ids[] = {rb_intern("timeout")};
|
|
710
|
+
rb_scan_args(argc, argv, "2:", &expected, &replacement, &keywords);
|
|
711
|
+
if (!NIL_P(keywords)) rb_get_kwargs(keywords, keyword_ids, 0, 1, keyword_values);
|
|
712
|
+
|
|
713
|
+
atom_t *atom = get_atom(self);
|
|
714
|
+
atom_cas_t operation;
|
|
715
|
+
rb_check_frozen(self);
|
|
716
|
+
atom_check_value(atom, expected);
|
|
717
|
+
atom_check_value(atom, replacement);
|
|
718
|
+
atom_timeout_t timeout = atom_parse_timeout(
|
|
719
|
+
keyword_values[0] == Qundef ? Qnil : keyword_values[0]
|
|
720
|
+
);
|
|
721
|
+
rb_check_frozen(self);
|
|
722
|
+
atom_execution_context_t execution = atom_current_execution_context();
|
|
723
|
+
|
|
724
|
+
if (!atom_lock_for_update_with_timeout(atom, &timeout, &execution)) return Qfalse;
|
|
725
|
+
operation.atom = atom;
|
|
726
|
+
operation.self = self;
|
|
727
|
+
operation.execution = execution;
|
|
728
|
+
operation.current = atom->value;
|
|
729
|
+
operation.expected = expected;
|
|
730
|
+
operation.replacement = replacement;
|
|
731
|
+
operation.identity = atom->compare_by_identity;
|
|
732
|
+
operation.complete = false;
|
|
733
|
+
atom_begin_update_locked(atom, &operation.execution);
|
|
734
|
+
pthread_mutex_unlock(&atom->lock);
|
|
735
|
+
|
|
736
|
+
return rb_ensure(atom_cas_body, (VALUE)&operation, atom_cas_cleanup, (VALUE)&operation);
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
static VALUE
|
|
740
|
+
atom_update_body(VALUE opaque)
|
|
741
|
+
{
|
|
742
|
+
atom_operation_t *operation = (atom_operation_t *)opaque;
|
|
743
|
+
VALUE result = rb_yield(operation->current);
|
|
744
|
+
rb_check_frozen(operation->self);
|
|
745
|
+
atom_check_value(operation->atom, result);
|
|
746
|
+
pthread_mutex_lock(&operation->atom->lock);
|
|
747
|
+
operation->atom->value = result;
|
|
748
|
+
atom_finished_update(operation->atom, true);
|
|
749
|
+
pthread_mutex_unlock(&operation->atom->lock);
|
|
750
|
+
operation->complete = true;
|
|
751
|
+
return result;
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
static VALUE
|
|
755
|
+
atom_update(int argc, VALUE *argv, VALUE self)
|
|
756
|
+
{
|
|
757
|
+
atom_t *atom = get_atom(self);
|
|
758
|
+
atom_operation_t operation = {
|
|
759
|
+
.self = self,
|
|
760
|
+
.atom = atom,
|
|
761
|
+
.execution = atom_current_execution_context(),
|
|
762
|
+
.complete = false,
|
|
763
|
+
};
|
|
764
|
+
rb_check_frozen(self);
|
|
765
|
+
atom_timeout_t timeout = atom_parse_timeout(atom_extract_timeout(argc, argv, "0:", NULL));
|
|
766
|
+
rb_check_frozen(self);
|
|
767
|
+
rb_need_block();
|
|
768
|
+
|
|
769
|
+
if (!atom_lock_for_update_with_timeout(atom, &timeout, &operation.execution)) return Qnil;
|
|
770
|
+
operation.current = atom->value;
|
|
771
|
+
atom_begin_update_locked(atom, &operation.execution);
|
|
772
|
+
pthread_mutex_unlock(&atom->lock);
|
|
773
|
+
|
|
774
|
+
return rb_ensure(atom_update_body, (VALUE)&operation, atom_operation_cleanup, (VALUE)&operation);
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
static VALUE
|
|
778
|
+
atom_upsert(int argc, VALUE *argv, VALUE self)
|
|
779
|
+
{
|
|
780
|
+
VALUE initial;
|
|
781
|
+
atom_t *atom = get_atom(self);
|
|
782
|
+
atom_operation_t operation = {
|
|
783
|
+
.self = self,
|
|
784
|
+
.atom = atom,
|
|
785
|
+
.execution = atom_current_execution_context(),
|
|
786
|
+
.complete = false,
|
|
787
|
+
};
|
|
788
|
+
rb_check_frozen(self);
|
|
789
|
+
atom_timeout_t timeout = atom_parse_timeout(atom_extract_timeout(argc, argv, "1:", &initial));
|
|
790
|
+
rb_check_frozen(self);
|
|
791
|
+
atom_check_value(atom, initial);
|
|
792
|
+
rb_need_block();
|
|
793
|
+
|
|
794
|
+
if (!atom_lock_for_update_with_timeout(atom, &timeout, &operation.execution)) return Qnil;
|
|
795
|
+
if (NIL_P(atom->value)) {
|
|
796
|
+
atom->value = initial;
|
|
797
|
+
atom_changed(atom);
|
|
798
|
+
pthread_mutex_unlock(&atom->lock);
|
|
799
|
+
return initial;
|
|
800
|
+
}
|
|
801
|
+
operation.current = atom->value;
|
|
802
|
+
atom_begin_update_locked(atom, &operation.execution);
|
|
803
|
+
pthread_mutex_unlock(&atom->lock);
|
|
804
|
+
|
|
805
|
+
return rb_ensure(atom_update_body, (VALUE)&operation, atom_operation_cleanup, (VALUE)&operation);
|
|
806
|
+
}
|
|
807
|
+
|
|
808
|
+
static bool
|
|
809
|
+
atom_values_equal(atom_t *atom, VALUE left, VALUE right)
|
|
810
|
+
{
|
|
811
|
+
return atom->compare_by_identity ? left == right : RTEST(rb_equal(left, right));
|
|
812
|
+
}
|
|
813
|
+
|
|
814
|
+
static VALUE
|
|
815
|
+
atom_wait_until_changed(int argc, VALUE *argv, VALUE self)
|
|
816
|
+
{
|
|
817
|
+
VALUE expected;
|
|
818
|
+
atom_t *atom = get_atom(self);
|
|
819
|
+
VALUE timeout_value = atom_extract_timeout(argc, argv, "1:", &expected);
|
|
820
|
+
atom_check_value(atom, expected);
|
|
821
|
+
atom_timeout_t timeout = atom_parse_timeout(timeout_value);
|
|
822
|
+
atom_execution_context_t execution = atom_current_execution_context();
|
|
823
|
+
|
|
824
|
+
for (;;) {
|
|
825
|
+
VALUE current;
|
|
826
|
+
uint64_t version;
|
|
827
|
+
pthread_mutex_lock(&atom->lock);
|
|
828
|
+
current = atom->value;
|
|
829
|
+
version = atom->version;
|
|
830
|
+
pthread_mutex_unlock(&atom->lock);
|
|
831
|
+
|
|
832
|
+
if (!atom_values_equal(atom, current, expected)) return current;
|
|
833
|
+
|
|
834
|
+
pthread_mutex_lock(&atom->lock);
|
|
835
|
+
if (atom->version != version) {
|
|
836
|
+
pthread_mutex_unlock(&atom->lock);
|
|
837
|
+
continue;
|
|
838
|
+
}
|
|
839
|
+
if (atom->updating) atom_check_update_wait_locked(atom, &execution);
|
|
840
|
+
if (!atom_wait_once(atom, &timeout)) return atom_timeout_result();
|
|
841
|
+
}
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
static VALUE
|
|
845
|
+
atom_wait_until_changed_reserved(int argc, VALUE *argv, VALUE self)
|
|
846
|
+
{
|
|
847
|
+
VALUE expected;
|
|
848
|
+
atom_t *atom = get_atom(self);
|
|
849
|
+
VALUE timeout_value = atom_extract_timeout(argc, argv, "1:", &expected);
|
|
850
|
+
atom_check_value(atom, expected);
|
|
851
|
+
atom_timeout_t timeout = atom_parse_timeout(timeout_value);
|
|
852
|
+
atom_execution_context_t execution = atom_current_execution_context();
|
|
853
|
+
|
|
854
|
+
for (;;) {
|
|
855
|
+
VALUE current;
|
|
856
|
+
uint64_t version;
|
|
857
|
+
if (!atom_lock_for_read(atom, &timeout)) return atom_timeout_result();
|
|
858
|
+
current = atom->value;
|
|
859
|
+
version = atom->version;
|
|
860
|
+
pthread_mutex_unlock(&atom->lock);
|
|
861
|
+
|
|
862
|
+
if (!atom_values_equal(atom, current, expected)) return current;
|
|
863
|
+
|
|
864
|
+
if (!atom_lock_for_read(atom, &timeout)) return atom_timeout_result();
|
|
865
|
+
if (atom->version != version) {
|
|
866
|
+
pthread_mutex_unlock(&atom->lock);
|
|
867
|
+
continue;
|
|
868
|
+
}
|
|
869
|
+
if (atom->updating) atom_check_update_wait_locked(atom, &execution);
|
|
870
|
+
if (!atom_wait_once(atom, &timeout)) return atom_timeout_result();
|
|
871
|
+
}
|
|
872
|
+
}
|
|
873
|
+
|
|
874
|
+
static VALUE
|
|
875
|
+
atom_wait_until_non_nil(int argc, VALUE *argv, VALUE self)
|
|
876
|
+
{
|
|
877
|
+
atom_t *atom = get_atom(self);
|
|
878
|
+
atom_timeout_t timeout = atom_parse_timeout(atom_extract_timeout(argc, argv, "0:", NULL));
|
|
879
|
+
atom_execution_context_t execution = atom_current_execution_context();
|
|
880
|
+
|
|
881
|
+
for (;;) {
|
|
882
|
+
pthread_mutex_lock(&atom->lock);
|
|
883
|
+
if (!NIL_P(atom->value)) {
|
|
884
|
+
VALUE current = atom->value;
|
|
885
|
+
pthread_mutex_unlock(&atom->lock);
|
|
886
|
+
return current;
|
|
887
|
+
}
|
|
888
|
+
if (atom->updating) atom_check_update_wait_locked(atom, &execution);
|
|
889
|
+
if (!atom_wait_once(atom, &timeout)) return atom_timeout_result();
|
|
890
|
+
}
|
|
891
|
+
}
|
|
892
|
+
|
|
893
|
+
static VALUE
|
|
894
|
+
atom_wait_until_non_nil_reserved(int argc, VALUE *argv, VALUE self)
|
|
895
|
+
{
|
|
896
|
+
atom_t *atom = get_atom(self);
|
|
897
|
+
atom_timeout_t timeout = atom_parse_timeout(atom_extract_timeout(argc, argv, "0:", NULL));
|
|
898
|
+
atom_execution_context_t execution = atom_current_execution_context();
|
|
899
|
+
|
|
900
|
+
for (;;) {
|
|
901
|
+
if (!atom_lock_for_read(atom, &timeout)) return atom_timeout_result();
|
|
902
|
+
if (!NIL_P(atom->value)) {
|
|
903
|
+
VALUE current = atom->value;
|
|
904
|
+
pthread_mutex_unlock(&atom->lock);
|
|
905
|
+
return current;
|
|
906
|
+
}
|
|
907
|
+
if (atom->updating) atom_check_update_wait_locked(atom, &execution);
|
|
908
|
+
if (!atom_wait_once(atom, &timeout)) return atom_timeout_result();
|
|
909
|
+
}
|
|
910
|
+
}
|
|
911
|
+
|
|
912
|
+
static VALUE
|
|
913
|
+
atom_compare_by_identity_p(VALUE self)
|
|
914
|
+
{
|
|
915
|
+
return get_atom(self)->compare_by_identity ? Qtrue : Qfalse;
|
|
916
|
+
}
|
|
917
|
+
|
|
918
|
+
static bool
|
|
919
|
+
atom_transaction_valid(farce_transaction_entry_t *entry)
|
|
920
|
+
{
|
|
921
|
+
atom_t *atom = entry->source_data;
|
|
922
|
+
return atom->version == entry->version && !atom->updating;
|
|
923
|
+
}
|
|
924
|
+
|
|
925
|
+
static void
|
|
926
|
+
atom_transaction_apply(farce_transaction_entry_t *entry)
|
|
927
|
+
{
|
|
928
|
+
atom_t *atom = entry->source_data;
|
|
929
|
+
atom_t *working = entry->working_data;
|
|
930
|
+
atom->value = working->value;
|
|
931
|
+
}
|
|
932
|
+
|
|
933
|
+
static void
|
|
934
|
+
atom_transaction_notify(farce_transaction_entry_t *entry)
|
|
935
|
+
{
|
|
936
|
+
atom_changed(entry->source_data);
|
|
937
|
+
}
|
|
938
|
+
|
|
939
|
+
static void
|
|
940
|
+
atom_transaction_reserve(farce_transaction_entry_t *entry, VALUE fiber, VALUE thread)
|
|
941
|
+
{
|
|
942
|
+
atom_t *atom = entry->source_data;
|
|
943
|
+
atom->updating = FARCE_TRANSACTION_RESERVED;
|
|
944
|
+
atom->updating_fiber = fiber;
|
|
945
|
+
atom->updating_thread = thread;
|
|
946
|
+
}
|
|
947
|
+
|
|
948
|
+
static void
|
|
949
|
+
atom_transaction_release(farce_transaction_entry_t *entry)
|
|
950
|
+
{
|
|
951
|
+
atom_finished_update(entry->source_data, false);
|
|
952
|
+
}
|
|
953
|
+
|
|
954
|
+
static const farce_transaction_ops_t atom_transaction_ops = {
|
|
955
|
+
atom_transaction_valid, atom_transaction_apply, atom_transaction_notify,
|
|
956
|
+
atom_transaction_reserve, atom_transaction_release,
|
|
957
|
+
};
|
|
958
|
+
|
|
959
|
+
static VALUE
|
|
960
|
+
atom_transaction_snapshot(VALUE self)
|
|
961
|
+
{
|
|
962
|
+
atom_t *source = get_atom(self);
|
|
963
|
+
if (source->weak) rb_raise(rb_eTypeError, "weak atoms do not support transactions");
|
|
964
|
+
VALUE working = atom_allocate(cAtom);
|
|
965
|
+
atom_t *copy;
|
|
966
|
+
TypedData_Get_Struct(working, atom_t, &atom_type, copy);
|
|
967
|
+
farce_transaction_entry_t *entry;
|
|
968
|
+
VALUE result = farce_transaction_entry_new(
|
|
969
|
+
self, working, source, copy, &source->lock, &atom_transaction_ops, &entry
|
|
970
|
+
);
|
|
971
|
+
pthread_mutex_lock(&source->lock);
|
|
972
|
+
copy->value = source->value;
|
|
973
|
+
copy->compare_by_identity = source->compare_by_identity;
|
|
974
|
+
copy->unshared = source->unshared;
|
|
975
|
+
copy->initialized = true;
|
|
976
|
+
entry->version = source->version;
|
|
977
|
+
pthread_mutex_unlock(&source->lock);
|
|
978
|
+
return result;
|
|
979
|
+
}
|
|
980
|
+
|
|
981
|
+
static void
|
|
982
|
+
define_atom_methods(VALUE klass, bool reserved_reads)
|
|
983
|
+
{
|
|
984
|
+
rb_define_alloc_func(klass, atom_allocate);
|
|
985
|
+
rb_define_method(klass, "initialize", atom_initialize, -1);
|
|
986
|
+
rb_define_method(klass, "value", reserved_reads ? atom_reserved_value : atom_value, 0);
|
|
987
|
+
rb_define_method(klass, "value=", atom_set_value, 1);
|
|
988
|
+
rb_define_method(klass, "get", atom_get, -1);
|
|
989
|
+
rb_define_method(klass, "store", atom_store, -1);
|
|
990
|
+
rb_define_method(klass, "swap", atom_swap, -1);
|
|
991
|
+
rb_define_method(klass, "store_if_absent", atom_store_if_absent, -1);
|
|
992
|
+
rb_define_method(klass, "transaction_snapshot", atom_transaction_snapshot, 0);
|
|
993
|
+
rb_define_method(klass, "compare_and_set", atom_compare_and_set, -1);
|
|
994
|
+
rb_define_method(klass, "update", atom_update, -1);
|
|
995
|
+
rb_define_method(klass, "upsert", atom_upsert, -1);
|
|
996
|
+
rb_define_method(klass, "wait_until_changed", reserved_reads ? atom_wait_until_changed_reserved : atom_wait_until_changed, -1);
|
|
997
|
+
rb_define_method(klass, "wait_until_non_nil", reserved_reads ? atom_wait_until_non_nil_reserved : atom_wait_until_non_nil, -1);
|
|
998
|
+
rb_define_method(klass, "compare_by_identity?", atom_compare_by_identity_p, 0);
|
|
999
|
+
}
|
|
1000
|
+
|
|
1001
|
+
#ifdef RC_HAVE_NATIVE_WEAK_REFERENCES
|
|
1002
|
+
static VALUE
|
|
1003
|
+
unshared_weak_atom_initialize_copy(VALUE self, VALUE other)
|
|
1004
|
+
{
|
|
1005
|
+
rb_raise(rb_eTypeError, "cannot copy UnsharedWeakAtom");
|
|
1006
|
+
}
|
|
1007
|
+
#endif
|
|
1008
|
+
|
|
1009
|
+
|
|
1010
|
+
void
|
|
1011
|
+
containers_init_atom(VALUE namespace)
|
|
1012
|
+
{
|
|
1013
|
+
cAtom = rb_define_class_under(namespace, "Atom", rb_cObject);
|
|
1014
|
+
define_atom_methods(cAtom, true);
|
|
1015
|
+
rb_define_method(cAtom, "freeze", atom_freeze, 0);
|
|
1016
|
+
#ifdef RC_HAVE_NATIVE_WEAK_REFERENCES
|
|
1017
|
+
cWeakAtom = rb_define_class_under(namespace, "WeakAtom", rb_cObject);
|
|
1018
|
+
define_atom_methods(cWeakAtom, false);
|
|
1019
|
+
cUnsharedWeakAtom = rb_define_class_under(namespace, "UnsharedWeakAtom", rb_cObject);
|
|
1020
|
+
define_atom_methods(cUnsharedWeakAtom, false);
|
|
1021
|
+
rb_define_private_method(cUnsharedWeakAtom, "initialize_copy", unshared_weak_atom_initialize_copy, 1);
|
|
1022
|
+
#endif
|
|
1023
|
+
}
|