farce 0.0.1.alpha2-java
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/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 +420 -0
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# shareable_constant_value: literal
|
|
3
|
+
# warn_indent: true
|
|
4
|
+
|
|
5
|
+
module Farce
|
|
6
|
+
module Abstract
|
|
7
|
+
# @abstract Common superclass for weak atomic references.
|
|
8
|
+
#
|
|
9
|
+
# A weak atom does not keep its current value alive. Its value becomes `nil`
|
|
10
|
+
# after garbage collection when no strong references remain. Collection
|
|
11
|
+
# timing is controlled by the garbage collector. In-flight operations may
|
|
12
|
+
# temporarily retain values they observe. Updates are serialized even when
|
|
13
|
+
# collection changes the observed value.
|
|
14
|
+
class WeakAtom < Atom
|
|
15
|
+
# @param value [BasicObject, nil] the initial value
|
|
16
|
+
# @param compare_by_identity [Boolean] whether comparisons use object identity instead of equality
|
|
17
|
+
def initialize(value = nil, compare_by_identity: false)
|
|
18
|
+
@atom = internal_atom_class.new(value, compare_by_identity:)
|
|
19
|
+
super()
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
private
|
|
23
|
+
|
|
24
|
+
# @abstract
|
|
25
|
+
# simplecov:disable
|
|
26
|
+
def internal_atom_class
|
|
27
|
+
raise NoMethodError, "Farce::Abstract::WeakAtom should not be instantiated directly. Use a subclass instead."
|
|
28
|
+
end
|
|
29
|
+
# simplecov:enable
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# shareable_constant_value: literal
|
|
3
|
+
# warn_indent: true
|
|
4
|
+
|
|
5
|
+
module Farce
|
|
6
|
+
module Abstract
|
|
7
|
+
# @abstract Common superclass for concurrent maps that retain keys weakly.
|
|
8
|
+
class WeakKeyMap < ConcurrentMap
|
|
9
|
+
def weak_keys? = true
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# shareable_constant_value: literal
|
|
3
|
+
# warn_indent: true
|
|
4
|
+
|
|
5
|
+
module Farce
|
|
6
|
+
module Abstract
|
|
7
|
+
# @abstract Common superclass for concurrent maps that retain keys and values weakly.
|
|
8
|
+
class WeakMap < ConcurrentMap
|
|
9
|
+
def weak_keys? = true
|
|
10
|
+
def weak_values? = true
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# shareable_constant_value: literal
|
|
3
|
+
# warn_indent: true
|
|
4
|
+
|
|
5
|
+
module Farce
|
|
6
|
+
module Abstract
|
|
7
|
+
# @abstract A set whose elements are retained weakly.
|
|
8
|
+
# Heap elements can disappear once no other strong reference remains.
|
|
9
|
+
class WeakSet < Set
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
end
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# shareable_constant_value: literal
|
|
3
|
+
# warn_indent: true
|
|
4
|
+
|
|
5
|
+
module Farce
|
|
6
|
+
module Abstract
|
|
7
|
+
# @abstract Common superclass for concurrent maps that retain values weakly.
|
|
8
|
+
class WeakValueMap < ConcurrentMap
|
|
9
|
+
def weak_values? = true
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# shareable_constant_value: literal
|
|
3
|
+
# warn_indent: true
|
|
4
|
+
|
|
5
|
+
module Farce
|
|
6
|
+
# Module holding abstract versions of classes that have multiple public implementations.
|
|
7
|
+
#
|
|
8
|
+
# Subclasses of these can be found under the following namespaces:
|
|
9
|
+
# * {Farce} - for Ractor-shareable implementations
|
|
10
|
+
# * {Farce::Local} - implementations scoped by Ractor/Thread/Fiber/etc
|
|
11
|
+
# * {Farce::Strict} - for Ractor-shareable implementations accepting only shareable values
|
|
12
|
+
# * {Farce::Unshared} - for Ractor-unshareable implementations
|
|
13
|
+
# * {Farce::Unsafe} – for implementations that are not thread-safe
|
|
14
|
+
module Abstract
|
|
15
|
+
include Internal::Autoloads
|
|
16
|
+
end
|
|
17
|
+
end
|
data/lib/farce/atom.rb
ADDED
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# shareable_constant_value: literal
|
|
3
|
+
# warn_indent: true
|
|
4
|
+
|
|
5
|
+
module Farce
|
|
6
|
+
# A Ractor-shareable atomic reference that strongly retains its current value and can hold non-shareable values.
|
|
7
|
+
#
|
|
8
|
+
# Values are transferred according to the atom's default {#mode}, or an operation-specific mode where supported.
|
|
9
|
+
# Values returned by the atom are automatically unwrapped. Updates are serialized, and comparison operations compare
|
|
10
|
+
# the stored values without claiming or opening their envelopes when possible.
|
|
11
|
+
#
|
|
12
|
+
# @example Creating a new atom
|
|
13
|
+
# atom = Farce::Atom.new("initial value")
|
|
14
|
+
# atom.value # => "initial value"
|
|
15
|
+
#
|
|
16
|
+
# # atoms are ractor-shareable
|
|
17
|
+
# Ractor.new(atom) do |atom|
|
|
18
|
+
# atom.update { |current| current + " updated" } # => "initial value updated"
|
|
19
|
+
# end
|
|
20
|
+
#
|
|
21
|
+
# # waits for the other ractor to perform its update
|
|
22
|
+
# atom.wait_until_changed "initial value"
|
|
23
|
+
#
|
|
24
|
+
# @example Updating a counter atomically
|
|
25
|
+
# # Note: Farce::Counter would have better performance
|
|
26
|
+
# counter = Farce::Atom.new(0)
|
|
27
|
+
# counter.update { |count| count + 1 } # => 1
|
|
28
|
+
# counter.compare_and_set(1, 2) # => true
|
|
29
|
+
# counter.value # => 2
|
|
30
|
+
#
|
|
31
|
+
# @example Automatically making values shareable
|
|
32
|
+
# atom = Farce::Atom.new([], mode: :make_shareable)
|
|
33
|
+
# atom.update { |items| items + [:job] } # => [:job]
|
|
34
|
+
# atom.value # => [:job]
|
|
35
|
+
# Ractor.shareable?(atom.value) # => true
|
|
36
|
+
class Atom < Farce::Abstract::Atom
|
|
37
|
+
include Shareable::Delegated
|
|
38
|
+
|
|
39
|
+
NIL_VALUE = Object.new.freeze
|
|
40
|
+
TIMED_OUT = Object.new.freeze
|
|
41
|
+
private_constant :NIL_VALUE, :TIMED_OUT
|
|
42
|
+
|
|
43
|
+
# @!macro modes
|
|
44
|
+
# @param value [BasicObject, nil] the initial value
|
|
45
|
+
# @param compare_by_identity [Boolean] whether comparisons use object identity instead of equality
|
|
46
|
+
# @param mode [Symbol] the default mode used to transfer values between Ractors
|
|
47
|
+
def initialize(value = nil, compare_by_identity: false, mode: :copy)
|
|
48
|
+
unless compare_by_identity == true || compare_by_identity == false
|
|
49
|
+
raise ArgumentError, "compare_by_identity must be a boolean"
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
@manager = ModeManager.new(mode:)
|
|
53
|
+
@compare_by_identity = compare_by_identity
|
|
54
|
+
@atom = Internal::Atom.new(wrap_value(value), compare_by_identity: true)
|
|
55
|
+
super()
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# @api private
|
|
59
|
+
def marshal_dump
|
|
60
|
+
stored = @atom.value
|
|
61
|
+
stored = nil if NIL_VALUE.equal?(stored)
|
|
62
|
+
[1, Internal::MarshalSupport.value(stored), @manager, compare_by_identity?, frozen?]
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# @api private
|
|
66
|
+
def marshal_load(data)
|
|
67
|
+
stored, @manager, identity, frozen = Internal::MarshalSupport.payload(data, 4)
|
|
68
|
+
@compare_by_identity = identity
|
|
69
|
+
@atom = Internal::Atom.new(NIL_VALUE, compare_by_identity: true)
|
|
70
|
+
publish_shareable
|
|
71
|
+
value = Internal::MarshalSupport.restore_value(stored)
|
|
72
|
+
@atom.store(nil.equal?(value) ? NIL_VALUE : value)
|
|
73
|
+
Internal::MarshalSupport.freeze(self, frozen)
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# @api private
|
|
77
|
+
def transaction_wrapper(transaction)
|
|
78
|
+
Transaction::Atom.new(transaction, self, @atom, manager: @manager, nil_value: NIL_VALUE)
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# The default mode used to transfer values between Ractors.
|
|
82
|
+
# @return [Symbol]
|
|
83
|
+
def mode = @manager.mode
|
|
84
|
+
|
|
85
|
+
# Whether comparisons use object identity instead of equality.
|
|
86
|
+
# @return [Boolean]
|
|
87
|
+
def compare_by_identity? = @compare_by_identity
|
|
88
|
+
|
|
89
|
+
# Return the current value without waiting for an update in progress.
|
|
90
|
+
# @return [BasicObject, nil] the current value
|
|
91
|
+
def value = unwrap_value(@atom.value)
|
|
92
|
+
|
|
93
|
+
# Store a value using the default mode.
|
|
94
|
+
# @param new_value [BasicObject, nil] the new value
|
|
95
|
+
# @return [BasicObject, nil] the new value
|
|
96
|
+
def value=(new_value)
|
|
97
|
+
store(new_value)
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
# Return the current value, waiting for any update in progress.
|
|
101
|
+
# @param timeout [Numeric, nil] the maximum number of seconds to wait
|
|
102
|
+
# @yield called when the timeout expires
|
|
103
|
+
# @return [BasicObject, nil] the current value or the fallback result
|
|
104
|
+
def get(timeout: nil, &) = unwrap_result(@atom.get(timeout:) { TIMED_OUT }, &)
|
|
105
|
+
|
|
106
|
+
# Store a new value, waiting for any update in progress.
|
|
107
|
+
# @!macro modes
|
|
108
|
+
# @param new_value [BasicObject, nil] the new value
|
|
109
|
+
# @param mode [Symbol, nil] the transfer mode, or nil to use the atom's default mode
|
|
110
|
+
# @param timeout [Numeric, nil] the maximum number of seconds to wait
|
|
111
|
+
# @yield called when the timeout expires
|
|
112
|
+
# @return [BasicObject, nil] the stored value or the fallback result
|
|
113
|
+
def store(new_value, mode: nil, timeout: nil, &)
|
|
114
|
+
check_frozen!
|
|
115
|
+
new_value = wrap_value(new_value, mode:)
|
|
116
|
+
unwrap_result(@atom.store(new_value, timeout:) { TIMED_OUT }, &)
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
# Replace the current value and return the previous value.
|
|
120
|
+
# @!macro modes
|
|
121
|
+
# @param new_value [BasicObject, nil] the new value
|
|
122
|
+
# @param mode [Symbol, nil] the transfer mode, or nil to use the atom's default mode
|
|
123
|
+
# @param timeout [Numeric, nil] the maximum number of seconds to wait
|
|
124
|
+
# @yield called when the timeout expires
|
|
125
|
+
# @return [BasicObject, nil] the previous value or the fallback result
|
|
126
|
+
def swap(new_value, mode: nil, timeout: nil, &)
|
|
127
|
+
check_frozen!
|
|
128
|
+
new_value = wrap_value(new_value, mode:)
|
|
129
|
+
unwrap_result(@atom.swap(new_value, timeout:) { TIMED_OUT }, &)
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
# Compute and store a value if the current value is nil.
|
|
133
|
+
# @!macro modes
|
|
134
|
+
# @param mode [Symbol, nil] the transfer mode, or nil to use the atom's default mode
|
|
135
|
+
# @param timeout [Numeric, nil] the maximum number of seconds to wait
|
|
136
|
+
# @yield computes the value to store when the current value is nil
|
|
137
|
+
# @yieldreturn [BasicObject, nil] the value to store
|
|
138
|
+
# @return [BasicObject, nil] the current or newly stored value, or nil when the timeout expires
|
|
139
|
+
def store_if_absent(mode: nil, timeout: nil)
|
|
140
|
+
raise LocalJumpError, "no block given" unless block_given?
|
|
141
|
+
check_frozen!
|
|
142
|
+
|
|
143
|
+
result = @atom.update(timeout:) do |current|
|
|
144
|
+
next current unless NIL_VALUE.equal?(current)
|
|
145
|
+
|
|
146
|
+
value = yield
|
|
147
|
+
check_frozen!
|
|
148
|
+
wrap_value(value, mode:)
|
|
149
|
+
end
|
|
150
|
+
unwrap_value(result)
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
# Atomically replace the current value if it matches the expected value.
|
|
154
|
+
# @!macro modes
|
|
155
|
+
# @param expected [BasicObject, nil] the value to compare with the current value
|
|
156
|
+
# @param new_value [BasicObject, nil] the replacement value
|
|
157
|
+
# @param mode [Symbol, nil] the replacement's transfer mode, or nil to use the atom's default mode
|
|
158
|
+
# @param timeout [Numeric, nil] the maximum number of seconds to wait
|
|
159
|
+
# @return [Boolean] whether the value was replaced
|
|
160
|
+
def compare_and_set(expected, new_value, mode: nil, timeout: nil)
|
|
161
|
+
check_frozen!
|
|
162
|
+
expected = wrap_comparison(expected)
|
|
163
|
+
matched = false
|
|
164
|
+
|
|
165
|
+
@atom.update(timeout:) do |current|
|
|
166
|
+
equal = values_equal?(current, expected)
|
|
167
|
+
check_frozen!
|
|
168
|
+
next current unless equal
|
|
169
|
+
|
|
170
|
+
matched = true
|
|
171
|
+
wrap_value(new_value, mode:)
|
|
172
|
+
end
|
|
173
|
+
matched
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
# Atomically replace the current value with the result of a block.
|
|
177
|
+
# @!macro modes
|
|
178
|
+
# @param mode [Symbol, nil] the result's transfer mode, or nil to use the atom's default mode
|
|
179
|
+
# @param timeout [Numeric, nil] the maximum number of seconds to wait
|
|
180
|
+
# @yield receives the current value and computes its replacement
|
|
181
|
+
# @yieldparam current [BasicObject, nil] the current value
|
|
182
|
+
# @yieldreturn [BasicObject, nil] the replacement value
|
|
183
|
+
# @return [BasicObject, nil] the replacement value, or nil when the timeout expires
|
|
184
|
+
def update(mode: nil, timeout: nil)
|
|
185
|
+
raise LocalJumpError, "no block given" unless block_given?
|
|
186
|
+
check_frozen!
|
|
187
|
+
result = @atom.update(timeout:) do |current|
|
|
188
|
+
value = yield(unwrap_value(current))
|
|
189
|
+
check_frozen!
|
|
190
|
+
wrap_value(value, mode:)
|
|
191
|
+
end
|
|
192
|
+
unwrap_value(result)
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
# Store an initial value if the current value is nil, otherwise replace it with the result of a block.
|
|
196
|
+
# @!macro modes
|
|
197
|
+
# @param initial_value [BasicObject, nil] the value to store when the current value is nil
|
|
198
|
+
# @param mode [Symbol, nil] the transfer mode, or nil to use the atom's default mode
|
|
199
|
+
# @param timeout [Numeric, nil] the maximum number of seconds to wait
|
|
200
|
+
# @yield receives a non-nil current value and computes its replacement
|
|
201
|
+
# @yieldparam current [BasicObject] the current value
|
|
202
|
+
# @yieldreturn [BasicObject, nil] the replacement value
|
|
203
|
+
# @return [BasicObject, nil] the current replacement or initial value, or nil when the timeout expires
|
|
204
|
+
def upsert(initial_value, mode: nil, timeout: nil)
|
|
205
|
+
raise LocalJumpError, "no block given" unless block_given?
|
|
206
|
+
check_frozen!
|
|
207
|
+
|
|
208
|
+
result = @atom.update(timeout:) do |current|
|
|
209
|
+
if NIL_VALUE.equal?(current)
|
|
210
|
+
wrap_value(initial_value, mode:)
|
|
211
|
+
else
|
|
212
|
+
value = yield(unwrap_value(current))
|
|
213
|
+
check_frozen!
|
|
214
|
+
wrap_value(value, mode:)
|
|
215
|
+
end
|
|
216
|
+
end
|
|
217
|
+
unwrap_value(result)
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
# Wait until the current value no longer matches an expected value.
|
|
221
|
+
# @param expected [BasicObject, nil] the value to compare with the current value
|
|
222
|
+
# @param timeout [Numeric, nil] the maximum number of seconds to wait
|
|
223
|
+
# @yield called when the timeout expires
|
|
224
|
+
# @return [BasicObject, nil] the changed value or the fallback result
|
|
225
|
+
def wait_until_changed(expected, timeout: nil, &)
|
|
226
|
+
expected = wrap_comparison(expected)
|
|
227
|
+
deadline = Internal.timeout_deadline(timeout)
|
|
228
|
+
|
|
229
|
+
while true
|
|
230
|
+
current = @atom.get(timeout: Internal.remaining_timeout(deadline)) { TIMED_OUT }
|
|
231
|
+
return unwrap_result(current, &) if TIMED_OUT.equal?(current)
|
|
232
|
+
return unwrap_value(current) unless values_equal?(current, expected)
|
|
233
|
+
|
|
234
|
+
result = @atom.wait_until_changed(current, timeout: Internal.remaining_timeout(deadline)) { TIMED_OUT }
|
|
235
|
+
return unwrap_result(result, &) if TIMED_OUT.equal?(result)
|
|
236
|
+
end
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
# Wait until the current value is not nil.
|
|
240
|
+
# @param timeout [Numeric, nil] the maximum number of seconds to wait
|
|
241
|
+
# @yield called when the timeout expires
|
|
242
|
+
# @return [BasicObject, nil] the non-nil value or the fallback result
|
|
243
|
+
def wait_until_non_nil(timeout: nil, &) = wait_until_changed(nil, timeout:, &)
|
|
244
|
+
|
|
245
|
+
# @api private
|
|
246
|
+
def inspect_with(inspector)
|
|
247
|
+
super do
|
|
248
|
+
yield if block_given?
|
|
249
|
+
inspector.attribute(:mode, mode)
|
|
250
|
+
end
|
|
251
|
+
end
|
|
252
|
+
|
|
253
|
+
private
|
|
254
|
+
|
|
255
|
+
def freeze_backend = @atom
|
|
256
|
+
|
|
257
|
+
def wrap_value(value, mode: nil)
|
|
258
|
+
value.nil? ? NIL_VALUE : @manager.wrap(value, mode:)
|
|
259
|
+
end
|
|
260
|
+
|
|
261
|
+
def wrap_comparison(value)
|
|
262
|
+
mode = compare_by_identity? ? :local : :copy
|
|
263
|
+
wrap_value(value, mode:)
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
def unwrap_value(value)
|
|
267
|
+
return if NIL_VALUE.equal?(value)
|
|
268
|
+
|
|
269
|
+
@manager.unwrap(value)
|
|
270
|
+
end
|
|
271
|
+
|
|
272
|
+
def inspect_value(inspector, prefix = "value=")
|
|
273
|
+
value = @atom.value
|
|
274
|
+
value = nil if NIL_VALUE.equal?(value)
|
|
275
|
+
inspector.breakable if prefix
|
|
276
|
+
|
|
277
|
+
if @manager.managed_envelope?(value)
|
|
278
|
+
inspector.from_envelope(value, prefix)
|
|
279
|
+
else
|
|
280
|
+
inspector.text(prefix) if prefix
|
|
281
|
+
inspector.object(value)
|
|
282
|
+
end
|
|
283
|
+
end
|
|
284
|
+
|
|
285
|
+
def unwrap_result(result)
|
|
286
|
+
return unwrap_value(result) unless TIMED_OUT.equal?(result)
|
|
287
|
+
|
|
288
|
+
yield if block_given?
|
|
289
|
+
end
|
|
290
|
+
|
|
291
|
+
def values_equal?(left, right) = @manager.same_value?(left, right, identity: compare_by_identity?)
|
|
292
|
+
end
|
|
293
|
+
end
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# shareable_constant_value: literal
|
|
3
|
+
# warn_indent: true
|
|
4
|
+
|
|
5
|
+
module Farce
|
|
6
|
+
# Map Ruby classes to a parallel inheritance hierarchy.
|
|
7
|
+
# Undefined classes use the nearest defined ancestor's mirror.
|
|
8
|
+
# An optional block turns each lookup into a cached value for that source class.
|
|
9
|
+
#
|
|
10
|
+
# @example Specialize behavior with ordinary method inheritance
|
|
11
|
+
# mirror = Farce::ClassMirror.new
|
|
12
|
+
# mirror.define(Object) { def label = "object" }
|
|
13
|
+
# mirror.define(Array) { def label = "array of #{super}" }
|
|
14
|
+
# mirror[Array].new.label # => "array of object"
|
|
15
|
+
class ClassMirror
|
|
16
|
+
include Farce::Shareable::Tracked
|
|
17
|
+
|
|
18
|
+
# @param base_class [Class] the mirror for BasicObject
|
|
19
|
+
# @yieldparam mirror [Class] the nearest defined mirror class
|
|
20
|
+
# @yieldparam klass [Class] the class passed to {#[]}
|
|
21
|
+
# @yieldreturn [Object] a Ractor-shareable value to cache for klass
|
|
22
|
+
# @note The block must be convertible to a shareable proc.
|
|
23
|
+
def initialize(base_class = Class.new, &)
|
|
24
|
+
raise TypeError, "Class expected, got #{base_class.class}" unless base_class.is_a?(Class)
|
|
25
|
+
@mapping = Strict::Map.new({ BasicObject => base_class })
|
|
26
|
+
@cache = Strict::WeakKeyMap.new
|
|
27
|
+
@callback = block_given? ? Ractor.shareable_proc(&) : nil
|
|
28
|
+
super()
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# @api private
|
|
32
|
+
def marshal_dump
|
|
33
|
+
raise TypeError, "class mirror callbacks cannot be marshaled" if @callback
|
|
34
|
+
[1, @mapping.to_h, frozen?]
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# @api private
|
|
38
|
+
def marshal_load(data)
|
|
39
|
+
mapping, frozen = Internal::MarshalSupport.payload(data, 2)
|
|
40
|
+
@mapping = Strict::Map.new(mapping)
|
|
41
|
+
@cache = Strict::WeakKeyMap.new
|
|
42
|
+
@callback = nil
|
|
43
|
+
@farce_freeze_state = Internal::Flag.new(false)
|
|
44
|
+
publish_shareable
|
|
45
|
+
Internal::MarshalSupport.freeze(self, frozen)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# Look up a mirror or the cached result of the constructor block.
|
|
49
|
+
# @param klass [Class] the source class
|
|
50
|
+
# @return [Object] the mirror class or the block result
|
|
51
|
+
# @raise [TypeError] if klass is not a Class
|
|
52
|
+
def [](klass)
|
|
53
|
+
raise TypeError, "Class expected, got #{klass.class}" unless klass.is_a?(Class)
|
|
54
|
+
@cache.store_if_absent(klass) do
|
|
55
|
+
result = lookup(klass)
|
|
56
|
+
@callback ? @callback.call(result, klass) : result
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Define or extend a mirror class from the main Ractor.
|
|
61
|
+
# Missing ancestors are defined automatically. The block defines methods
|
|
62
|
+
# in an included module, so `super` can call earlier definitions.
|
|
63
|
+
# Existing lookup results are invalidated after each definition.
|
|
64
|
+
# @param klass [Class] the source class
|
|
65
|
+
# @yield evaluated as a module body
|
|
66
|
+
# @return [Class] the mirror class
|
|
67
|
+
# @raise [Ractor::IsolationError] outside the main Ractor
|
|
68
|
+
# @raise [TypeError] if klass is not a Class
|
|
69
|
+
def define(klass, &)
|
|
70
|
+
check_frozen!
|
|
71
|
+
raise Ractor::IsolationError, "must be called from main Ractor" unless Ractor.main?
|
|
72
|
+
raise TypeError, "Class expected, got #{klass.class}" unless klass.is_a?(Class)
|
|
73
|
+
mixin = Module.new(&) if block_given?
|
|
74
|
+
mirror = @mapping.store_if_absent(klass) { Class.new(define(klass.superclass)) }
|
|
75
|
+
mirror.include(mixin) if mixin
|
|
76
|
+
@cache.clear
|
|
77
|
+
mirror
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
private
|
|
81
|
+
|
|
82
|
+
def lookup(klass)
|
|
83
|
+
raise TypeError, "Class expected, got #{klass.class}" unless klass.is_a?(Class)
|
|
84
|
+
@mapping.fetch(klass) { lookup(klass.superclass) }
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
data/lib/farce/clock.rb
ADDED
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# shareable_constant_value: literal
|
|
3
|
+
# warn_indent: true
|
|
4
|
+
|
|
5
|
+
module Farce
|
|
6
|
+
# Wrapper module for clock-related functionality.
|
|
7
|
+
# Converts everything to a monotonic clock time in seconds, from when `farce/clock` was loaded.
|
|
8
|
+
# @see Farce.clock
|
|
9
|
+
module Clock
|
|
10
|
+
extend self
|
|
11
|
+
|
|
12
|
+
CLOCK_MONOTONIC = Process::CLOCK_MONOTONIC
|
|
13
|
+
CUTOFF_CLOCK = 600 # 10 minutes
|
|
14
|
+
CUTOFF_TIME = 1_000_000_000 # September 9, 2001 or 31-ish years ¯\_(ツ)_/¯
|
|
15
|
+
MONOTONIC = Process.clock_gettime(CLOCK_MONOTONIC)
|
|
16
|
+
REAL_TIME = Process.clock_gettime(Process::CLOCK_REALTIME)
|
|
17
|
+
TIME_DIFF = REAL_TIME - MONOTONIC
|
|
18
|
+
private_constant :CLOCK_MONOTONIC, :CUTOFF_CLOCK, :CUTOFF_TIME, :MONOTONIC, :REAL_TIME, :TIME_DIFF
|
|
19
|
+
|
|
20
|
+
# Converts the given value to a monotonic clock time in seconds, assuming that it already represents a clock time.
|
|
21
|
+
# Returns the current clock time if no value is given.
|
|
22
|
+
#
|
|
23
|
+
# Same as calling {#parse parse(clock: value)}
|
|
24
|
+
#
|
|
25
|
+
# @param value [nil, #to_f] the value to convert to clock time
|
|
26
|
+
# @raise [ArgumentError] if the resulting clock time is NaN
|
|
27
|
+
# @return [Float] the clock time in seconds
|
|
28
|
+
def clock(value) = validate_timestamp(value ? value.to_f : current)
|
|
29
|
+
|
|
30
|
+
# Returns the current monotonic clock time in seconds.
|
|
31
|
+
# @return [Float] the clock time in seconds
|
|
32
|
+
def current = Process.clock_gettime(CLOCK_MONOTONIC) - MONOTONIC
|
|
33
|
+
|
|
34
|
+
# Converts the given value to a monotonic clock time in seconds.
|
|
35
|
+
#
|
|
36
|
+
# @param value [nil, Numeric, Time, ActiveSupport::Duration, Hash]
|
|
37
|
+
# The value to convert to clock time
|
|
38
|
+
#
|
|
39
|
+
# If the value is `nil`, it is treated as the current time.
|
|
40
|
+
#
|
|
41
|
+
# ActiveSupport durations are relative offsets when the ActiveSupport integration is loaded.
|
|
42
|
+
#
|
|
43
|
+
# Other numeric values use the following cutoffs:
|
|
44
|
+
# * Up to 600 (10 minutes): Offsets from the current time.
|
|
45
|
+
# * Greater than 600, up to one billion: Clock times (like the value returned by {#current}).
|
|
46
|
+
# * Greater than one billion: UNIX timestamps (like the value returned by `Time.now.to_f`).
|
|
47
|
+
#
|
|
48
|
+
# Hashes may contain up to one key-value pair, with the key being one of the following:
|
|
49
|
+
# * `:at`, `:time`, or `:timeout_at` for {#time fixed times}
|
|
50
|
+
# * `:delay`, `:in`, `:offset`, `:timeout`, or `:wait` for {#offset relative offsets}
|
|
51
|
+
# * `:clock` for {#clock clock times}
|
|
52
|
+
#
|
|
53
|
+
# @raise [ArgumentError] if the resulting clock time is NaN
|
|
54
|
+
# @return [Float] the clock time in seconds
|
|
55
|
+
def parse(value = nil)
|
|
56
|
+
case value
|
|
57
|
+
when nil, UNDEFINED then return current
|
|
58
|
+
when Float, Integer then return value > CUTOFF_CLOCK ? time(value) : offset(value)
|
|
59
|
+
when Time then return time(value)
|
|
60
|
+
when Hash
|
|
61
|
+
case value.size
|
|
62
|
+
when 0 then return current
|
|
63
|
+
when 1
|
|
64
|
+
return time(value[:at]) if value.key?(:at)
|
|
65
|
+
method = argument = nil
|
|
66
|
+
value.each_pair { |key, option| method, argument = key, option }
|
|
67
|
+
return public_send(method, argument)
|
|
68
|
+
end
|
|
69
|
+
else
|
|
70
|
+
return at(value.to_time) if value.respond_to?(:to_time)
|
|
71
|
+
return parse(value.to_f) if value.is_a?(Numeric)
|
|
72
|
+
end
|
|
73
|
+
raise TypeError, "Cannot convert #{value.inspect} to clock time"
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# Converts the given value to a monotonic clock time in seconds.
|
|
77
|
+
# The value is assumed to be a fixed point in time (independent of the current time).
|
|
78
|
+
#
|
|
79
|
+
# If the value is numeric and greater than one billion, it is treated as a UNIX timestamp.
|
|
80
|
+
# Meaning that you can't pass a clock time greater than 31 years, or a timestamp before September 9, 2001.
|
|
81
|
+
#
|
|
82
|
+
# @param value [Numeric, Time] the value to convert to clock time
|
|
83
|
+
# @raise [ArgumentError] if the resulting clock time is NaN
|
|
84
|
+
# @return [Float] the clock time in seconds
|
|
85
|
+
def time(value)
|
|
86
|
+
case value
|
|
87
|
+
when Float, Integer then timestamp = value > CUTOFF_TIME ? value - REAL_TIME : Float(value)
|
|
88
|
+
when Time then timestamp = value.to_f - REAL_TIME
|
|
89
|
+
when Numeric then return time(value.to_f)
|
|
90
|
+
else raise TypeError, "Cannot convert #{value.class} to clock time"
|
|
91
|
+
end
|
|
92
|
+
validate_timestamp(timestamp)
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
# Converts the given value to a monotonic clock time in seconds.
|
|
96
|
+
# The value is assumed to be a relative offset from the current time.
|
|
97
|
+
#
|
|
98
|
+
# @param value [Numeric] the value to convert to clock time
|
|
99
|
+
# @raise [ArgumentError] if the resulting clock time is NaN
|
|
100
|
+
# @return [Float] the clock time in seconds
|
|
101
|
+
def offset(value)
|
|
102
|
+
return validate_timestamp(current + value.to_f) if value.is_a?(Numeric)
|
|
103
|
+
raise TypeError, "Cannot convert #{value.class} to clock time"
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
alias now current
|
|
107
|
+
alias at time
|
|
108
|
+
alias timeout_at time
|
|
109
|
+
alias delay offset
|
|
110
|
+
alias in offset
|
|
111
|
+
alias timeout offset
|
|
112
|
+
alias wait offset
|
|
113
|
+
|
|
114
|
+
private
|
|
115
|
+
|
|
116
|
+
def validate_timestamp(timestamp)
|
|
117
|
+
raise ArgumentError, "timestamp must not be NaN" if timestamp.nan?
|
|
118
|
+
timestamp
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
end
|