farce 0.0.1.alpha2-x86-linux-gnu
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/CODE_OF_CONDUCT.md +26 -0
- data/CONTRIBUTING.md +71 -0
- data/MIT-LICENSE +20 -0
- data/README.md +1524 -0
- data/SECURITY.md +10 -0
- data/docs/benchmarks.md +185 -0
- data/docs/gems/dry-types.md +290 -0
- data/docs/gems/msgpack.md +70 -0
- data/docs/gems/ractor-shim.md +63 -0
- data/docs/modes.md +628 -0
- data/docs/scopes.md +649 -0
- data/docs/variants.md +346 -0
- data/ext/ext_helper.rb +20 -0
- data/ext/farce/README.md +21 -0
- data/ext/farce/atom.c +1023 -0
- data/ext/farce/bounded_map.c +1683 -0
- data/ext/farce/containers.h +77 -0
- data/ext/farce/counter.c +399 -0
- data/ext/farce/darwin.c +100 -0
- data/ext/farce/depend +12 -0
- data/ext/farce/dict.c +1523 -0
- data/ext/farce/dict.h +152 -0
- data/ext/farce/drivers.c +237 -0
- data/ext/farce/exchanger.c +299 -0
- data/ext/farce/extconf.rb +99 -0
- data/ext/farce/farce.c +359 -0
- data/ext/farce/flag.c +288 -0
- data/ext/farce/io.c +338 -0
- data/ext/farce/lock.c +510 -0
- data/ext/farce/map.c +2240 -0
- data/ext/farce/priority_queue.c +2056 -0
- data/ext/farce/queue.c +1059 -0
- data/ext/farce/reactor.c +820 -0
- data/ext/farce/reactor.h +103 -0
- data/ext/farce/shareable.h +31 -0
- data/ext/farce/signal.c +350 -0
- data/ext/farce/transaction.c +354 -0
- data/ext/farce/transaction.h +40 -0
- data/ext/farce/tree_map.c +1953 -0
- data/ext/farce/trie.c +2020 -0
- data/ext/farce/unshareable.c +155 -0
- data/ext/farce/unshared_io_pool.h +234 -0
- data/ext/farce/unshared_signal.c +263 -0
- data/ext/farce/unshared_wait.h +193 -0
- data/ext/farce/unsupported.c +6 -0
- data/ext/farce/vector.c +1195 -0
- data/ext/farce/weak_map.c +1714 -0
- data/ext/java/org/farce/BoundedMap.java +394 -0
- data/ext/java/org/farce/FiberScheduler.java +141 -0
- data/ext/java/org/farce/PriorityKey.java +45 -0
- data/ext/java/org/farce/PriorityQueue.java +373 -0
- data/ext/java/org/farce/QueueSignal.java +28 -0
- data/ext/rebind/README.md +14 -0
- data/ext/rebind/extconf.rb +10 -0
- data/ext/rebind/rebind.c +186 -0
- data/lib/farce/_yard/internal.rb +13 -0
- data/lib/farce/_yard/macros.rb +61 -0
- data/lib/farce/_yard/ractor.rb +46 -0
- data/lib/farce/abstract/atom.rb +186 -0
- data/lib/farce/abstract/bounded_map.rb +232 -0
- data/lib/farce/abstract/collection.rb +151 -0
- data/lib/farce/abstract/concurrent_map.rb +381 -0
- data/lib/farce/abstract/counter.rb +193 -0
- data/lib/farce/abstract/duplicable_map.rb +229 -0
- data/lib/farce/abstract/exchanger.rb +26 -0
- data/lib/farce/abstract/flag.rb +71 -0
- data/lib/farce/abstract/lazy.rb +115 -0
- data/lib/farce/abstract/lease.rb +104 -0
- data/lib/farce/abstract/lease_map.rb +261 -0
- data/lib/farce/abstract/lease_pool.rb +91 -0
- data/lib/farce/abstract/lfu_map.rb +20 -0
- data/lib/farce/abstract/lru_map.rb +25 -0
- data/lib/farce/abstract/map.rb +345 -0
- data/lib/farce/abstract/molecule.rb +245 -0
- data/lib/farce/abstract/port.rb +74 -0
- data/lib/farce/abstract/priority_queue.rb +117 -0
- data/lib/farce/abstract/queue.rb +269 -0
- data/lib/farce/abstract/scheduler.rb +111 -0
- data/lib/farce/abstract/set.rb +910 -0
- data/lib/farce/abstract/sorted_set.rb +172 -0
- data/lib/farce/abstract/timer_queue.rb +136 -0
- data/lib/farce/abstract/tree_map.rb +269 -0
- data/lib/farce/abstract/value.rb +68 -0
- data/lib/farce/abstract/vector.rb +1126 -0
- data/lib/farce/abstract/weak_atom.rb +32 -0
- data/lib/farce/abstract/weak_key_map.rb +12 -0
- data/lib/farce/abstract/weak_map.rb +13 -0
- data/lib/farce/abstract/weak_set.rb +12 -0
- data/lib/farce/abstract/weak_value_map.rb +12 -0
- data/lib/farce/abstract.rb +17 -0
- data/lib/farce/atom.rb +293 -0
- data/lib/farce/class_mirror.rb +87 -0
- data/lib/farce/clock.rb +121 -0
- data/lib/farce/config.rb +229 -0
- data/lib/farce/counter.rb +71 -0
- data/lib/farce/deduper.rb +122 -0
- data/lib/farce/engine/jruby/bounded_map.rb +314 -0
- data/lib/farce/engine/jruby/fiber_scheduler.jar +0 -0
- data/lib/farce/engine/jruby/fiber_scheduler.rb +119 -0
- data/lib/farce/engine/jruby/lease_waiting.rb +19 -0
- data/lib/farce/engine/jruby/map.rb +505 -0
- data/lib/farce/engine/jruby/mutable_numeric_copy.rb +42 -0
- data/lib/farce/engine/jruby/signal.rb +147 -0
- data/lib/farce/engine/jruby.rb +63 -0
- data/lib/farce/engine/jvm/concurrent_weak_registry.rb +55 -0
- data/lib/farce/engine/jvm/counter.rb +102 -0
- data/lib/farce/engine/jvm/extension.rb +32 -0
- data/lib/farce/engine/jvm/farce.jar +0 -0
- data/lib/farce/engine/jvm/flag.rb +79 -0
- data/lib/farce/engine/jvm/priority_queue.rb +217 -0
- data/lib/farce/engine/jvm/tree_map.rb +350 -0
- data/lib/farce/engine/jvm/types.rb +180 -0
- data/lib/farce/engine/jvm.rb +19 -0
- data/lib/farce/engine/ruby/3.4/farce.so +0 -0
- data/lib/farce/engine/ruby/3.4/fiber_scheduler.rb +20 -0
- data/lib/farce/engine/ruby/3.4/port.rb +186 -0
- data/lib/farce/engine/ruby/3.4/ractor_methods.rb +26 -0
- data/lib/farce/engine/ruby/3.4/ractor_selector.rb +92 -0
- data/lib/farce/engine/ruby/3.4/rebind.so +0 -0
- data/lib/farce/engine/ruby/3.4/vault.rb +56 -0
- data/lib/farce/engine/ruby/4.0/farce.so +0 -0
- data/lib/farce/engine/ruby/4.0/port.rb +19 -0
- data/lib/farce/engine/ruby/4.0/ractor_methods.rb +20 -0
- data/lib/farce/engine/ruby/4.0/ractor_selector.rb +108 -0
- data/lib/farce/engine/ruby/4.0/rebind.so +0 -0
- data/lib/farce/engine/ruby/4.0/vault.rb +74 -0
- data/lib/farce/engine/ruby/4.1/port.rb +21 -0
- data/lib/farce/engine/ruby/4.1/ractor_methods.rb +22 -0
- data/lib/farce/engine/ruby/4.1/ractor_selector.rb +25 -0
- data/lib/farce/engine/ruby/4.1/vault.rb +5 -0
- data/lib/farce/engine/ruby/fiber_scheduler.rb +32 -0
- data/lib/farce/engine/ruby/key_lock_map.rb +28 -0
- data/lib/farce/engine/ruby/shared/lease.rb +26 -0
- data/lib/farce/engine/ruby/shared/lease_pool.rb +24 -0
- data/lib/farce/engine/ruby/shared/main_scheduler.rb +9 -0
- data/lib/farce/engine/ruby/shared/parallel_scheduler.rb +9 -0
- data/lib/farce/engine/ruby/shared/proxy_owner.rb +31 -0
- data/lib/farce/engine/ruby/shared/ractor_methods.rb +34 -0
- data/lib/farce/engine/ruby/shared/ractor_selector.rb +328 -0
- data/lib/farce/engine/ruby/shared/strict_map.rb +13 -0
- data/lib/farce/engine/ruby/shared/unshared_vector.rb +14 -0
- data/lib/farce/engine/ruby/shared/vault.rb +144 -0
- data/lib/farce/engine/ruby/shared/vault_weak_map.rb +336 -0
- data/lib/farce/engine/ruby/shared/weak_atom.rb +54 -0
- data/lib/farce/engine/ruby/shared/weak_map.rb +413 -0
- data/lib/farce/engine/ruby.rb +130 -0
- data/lib/farce/engine/shared/atom.rb +10 -0
- data/lib/farce/engine/shared/exchanger.rb +130 -0
- data/lib/farce/engine/shared/identity_key.rb +22 -0
- data/lib/farce/engine/shared/lease.rb +10 -0
- data/lib/farce/engine/shared/lease_pool.rb +10 -0
- data/lib/farce/engine/shared/main_scheduler.rb +20 -0
- data/lib/farce/engine/shared/map_key_coordination.rb +226 -0
- data/lib/farce/engine/shared/parallel_scheduler.rb +9 -0
- data/lib/farce/engine/shared/port.rb +44 -0
- data/lib/farce/engine/shared/portable_bounded_map.rb +619 -0
- data/lib/farce/engine/shared/proxy_owner.rb +43 -0
- data/lib/farce/engine/shared/queue.rb +233 -0
- data/lib/farce/engine/shared/ractor_methods.rb +73 -0
- data/lib/farce/engine/shared/rebindable.rb +15 -0
- data/lib/farce/engine/shared/strict_atom.rb +73 -0
- data/lib/farce/engine/shared/strict_map.rb +117 -0
- data/lib/farce/engine/shared/strict_queue_values.rb +27 -0
- data/lib/farce/engine/shared/strict_tree_map.rb +56 -0
- data/lib/farce/engine/shared/transaction_map_backend.rb +20 -0
- data/lib/farce/engine/shared/trie.rb +317 -0
- data/lib/farce/engine/shared/trie_builder.rb +199 -0
- data/lib/farce/engine/shared/unshareable.rb +14 -0
- data/lib/farce/engine/shared/unshared_atom.rb +256 -0
- data/lib/farce/engine/shared/unshared_priority_queue.rb +9 -0
- data/lib/farce/engine/shared/unshared_queue.rb +18 -0
- data/lib/farce/engine/shared/unshared_signal.rb +21 -0
- data/lib/farce/engine/shared/unshared_vector.rb +377 -0
- data/lib/farce/engine/shared/unshared_weak_atom.rb +30 -0
- data/lib/farce/engine/shared/unshared_weak_map.rb +445 -0
- data/lib/farce/engine/shared/vault.rb +31 -0
- data/lib/farce/engine/shared/vector.rb +83 -0
- data/lib/farce/engine/shared/weak_atom/base.rb +189 -0
- data/lib/farce/engine/shared/weak_atom.rb +20 -0
- data/lib/farce/engine/shared/weak_map/cell.rb +99 -0
- data/lib/farce/engine/shared/weak_map/index.rb +249 -0
- data/lib/farce/engine/shared/weak_map/lock.rb +80 -0
- data/lib/farce/engine/shared/weak_map/reference.rb +33 -0
- data/lib/farce/engine/shared.rb +63 -0
- data/lib/farce/engine/truffleruby/fiber_scheduler.rb +13 -0
- data/lib/farce/engine/truffleruby/lock.rb +121 -0
- data/lib/farce/engine/truffleruby/map.rb +656 -0
- data/lib/farce/engine/truffleruby/native/counter.rb +105 -0
- data/lib/farce/engine/truffleruby/native/flag.rb +77 -0
- data/lib/farce/engine/truffleruby/native/ordered_array_support.rb +67 -0
- data/lib/farce/engine/truffleruby/native/priority_queue.rb +388 -0
- data/lib/farce/engine/truffleruby/native/tree_map.rb +51 -0
- data/lib/farce/engine/truffleruby/native/unsafe_tree_map.rb +350 -0
- data/lib/farce/engine/truffleruby/signal.rb +77 -0
- data/lib/farce/engine/truffleruby.rb +148 -0
- data/lib/farce/envelope.rb +335 -0
- data/lib/farce/error.rb +35 -0
- data/lib/farce/exchanger.rb +41 -0
- data/lib/farce/flag.rb +27 -0
- data/lib/farce/integrations/active_support/blank.rb +68 -0
- data/lib/farce/integrations/active_support/clock.rb +17 -0
- data/lib/farce/integrations/active_support/duplicable.rb +78 -0
- data/lib/farce/integrations/active_support/map.rb +175 -0
- data/lib/farce/integrations/active_support/set.rb +60 -0
- data/lib/farce/integrations/active_support/value_serialization.rb +17 -0
- data/lib/farce/integrations/active_support/vector.rb +231 -0
- data/lib/farce/integrations/active_support.rb +9 -0
- data/lib/farce/integrations/activesupport.rb +5 -0
- data/lib/farce/integrations/bson.rb +108 -0
- data/lib/farce/integrations/cbor.rb +51 -0
- data/lib/farce/integrations/concurrent.rb +112 -0
- data/lib/farce/integrations/dry-types.rb +5 -0
- data/lib/farce/integrations/dry_types.rb +694 -0
- data/lib/farce/integrations/json.rb +7 -0
- data/lib/farce/integrations/msgpack.rb +112 -0
- data/lib/farce/integrations/oj.rb +69 -0
- data/lib/farce/integrations/psych.rb +191 -0
- data/lib/farce/integrations/ractor-sharing.rb +5 -0
- data/lib/farce/integrations/ractor-tmvar.rb +5 -0
- data/lib/farce/integrations/ractor_sharing.rb +172 -0
- data/lib/farce/integrations/ractor_tmvar.rb +64 -0
- data/lib/farce/integrations/shared/to_json.rb +42 -0
- data/lib/farce/integrations/sorted_set.rb +11 -0
- data/lib/farce/integrations/weakref.rb +38 -0
- data/lib/farce/integrations/yajl.rb +10 -0
- data/lib/farce/integrations.rb +156 -0
- data/lib/farce/internal/_frozen_config.rb +11 -0
- data/lib/farce/internal/autoloads.rb +47 -0
- data/lib/farce/internal/blocking_priority_queue.rb +122 -0
- data/lib/farce/internal/converter.rb +106 -0
- data/lib/farce/internal/copyable.rb +31 -0
- data/lib/farce/internal/delegation.rb +20 -0
- data/lib/farce/internal/external_transaction.rb +59 -0
- data/lib/farce/internal/fake_ractor.rb +179 -0
- data/lib/farce/internal/freeze.rb +118 -0
- data/lib/farce/internal/inspect.rb +157 -0
- data/lib/farce/internal/key_lock_map.rb +29 -0
- data/lib/farce/internal/key_normalizer.rb +289 -0
- data/lib/farce/internal/lease_initialization.rb +115 -0
- data/lib/farce/internal/lease_map.rb +441 -0
- data/lib/farce/internal/lease_pool_state.rb +228 -0
- data/lib/farce/internal/lease_state.rb +342 -0
- data/lib/farce/internal/lease_waiting.rb +13 -0
- data/lib/farce/internal/managed_queue.rb +26 -0
- data/lib/farce/internal/map_value_modes.rb +227 -0
- data/lib/farce/internal/marshal_support.rb +227 -0
- data/lib/farce/internal/mixin.rb +20 -0
- data/lib/farce/internal/mutable_ordered_key_lock_map.rb +63 -0
- data/lib/farce/internal/noncopyable.rb +17 -0
- data/lib/farce/internal/ordered_key_lock_map.rb +69 -0
- data/lib/farce/internal/pool_supervisor.rb +41 -0
- data/lib/farce/internal/pool_worker.rb +61 -0
- data/lib/farce/internal/portable_transaction/reservation_entry.rb +29 -0
- data/lib/farce/internal/portable_transaction/strong_map_entry.rb +137 -0
- data/lib/farce/internal/portable_transaction/strong_map_size_entry.rb +44 -0
- data/lib/farce/internal/portable_transaction/tree_entry.rb +64 -0
- data/lib/farce/internal/portable_transaction.rb +164 -0
- data/lib/farce/internal/proxy_owner_notifications.rb +29 -0
- data/lib/farce/internal/reservation_waiting.rb +24 -0
- data/lib/farce/internal/scheduled_task.rb +48 -0
- data/lib/farce/internal/scheduler_io.rb +171 -0
- data/lib/farce/internal/scheduler_lifecycle.rb +314 -0
- data/lib/farce/internal/select_scheduler.rb +235 -0
- data/lib/farce/internal/storage.rb +162 -0
- data/lib/farce/internal/strict_lease.rb +30 -0
- data/lib/farce/internal/strict_lease_map.rb +20 -0
- data/lib/farce/internal/strict_lease_pool.rb +29 -0
- data/lib/farce/internal/thread_pool.rb +166 -0
- data/lib/farce/internal/transaction_conflict.rb +9 -0
- data/lib/farce/internal/transaction_freeze_guard.rb +30 -0
- data/lib/farce/internal/transaction_map_snapshot.rb +123 -0
- data/lib/farce/internal/undefined.rb +22 -0
- data/lib/farce/internal/unshared_lease.rb +29 -0
- data/lib/farce/internal/unshared_lease_pool.rb +34 -0
- data/lib/farce/internal/unshared_queue_waiting.rb +28 -0
- data/lib/farce/internal/value_serialization.rb +13 -0
- data/lib/farce/internal/weak_map_value_modes.rb +58 -0
- data/lib/farce/internal/weak_mode_manager.rb +26 -0
- data/lib/farce/internal.rb +129 -0
- data/lib/farce/lazy.rb +100 -0
- data/lib/farce/lazy_ref.rb +40 -0
- data/lib/farce/lease.rb +28 -0
- data/lib/farce/lease_map.rb +34 -0
- data/lib/farce/lease_pool.rb +29 -0
- data/lib/farce/lfu_map.rb +53 -0
- data/lib/farce/local/atom.rb +23 -0
- data/lib/farce/local/counter.rb +88 -0
- data/lib/farce/local/flag.rb +65 -0
- data/lib/farce/local/lazy.rb +38 -0
- data/lib/farce/local/lazy_ref.rb +36 -0
- data/lib/farce/local/lease.rb +77 -0
- data/lib/farce/local/lease_map.rb +80 -0
- data/lib/farce/local/lease_pool.rb +38 -0
- data/lib/farce/local/lfu_map.rb +55 -0
- data/lib/farce/local/lru_map.rb +62 -0
- data/lib/farce/local/map.rb +47 -0
- data/lib/farce/local/molecule.rb +45 -0
- data/lib/farce/local/priority_queue.rb +53 -0
- data/lib/farce/local/queue.rb +32 -0
- data/lib/farce/local/scoped.rb +166 -0
- data/lib/farce/local/set.rb +18 -0
- data/lib/farce/local/sorted_set.rb +50 -0
- data/lib/farce/local/timer_queue.rb +41 -0
- data/lib/farce/local/tree_map.rb +47 -0
- data/lib/farce/local/vector.rb +30 -0
- data/lib/farce/local/weak_atom.rb +23 -0
- data/lib/farce/local/weak_key_map.rb +39 -0
- data/lib/farce/local/weak_map.rb +39 -0
- data/lib/farce/local/weak_set.rb +18 -0
- data/lib/farce/local/weak_value_map.rb +39 -0
- data/lib/farce/local.rb +35 -0
- data/lib/farce/lock.rb +54 -0
- data/lib/farce/lru_map.rb +63 -0
- data/lib/farce/map.rb +58 -0
- data/lib/farce/mode_manager.rb +142 -0
- data/lib/farce/molecule.rb +60 -0
- data/lib/farce/mutable.rb +171 -0
- data/lib/farce/pool.rb +332 -0
- data/lib/farce/port.rb +134 -0
- data/lib/farce/priority_queue.rb +70 -0
- data/lib/farce/proxy/register.rb +101 -0
- data/lib/farce/proxy/supervisor.rb +137 -0
- data/lib/farce/proxy/wrapper.rb +49 -0
- data/lib/farce/proxy.rb +291 -0
- data/lib/farce/queue.rb +60 -0
- data/lib/farce/ractor.rb +269 -0
- data/lib/farce/read_write_lock.rb +256 -0
- data/lib/farce/reference.rb +158 -0
- data/lib/farce/resolv/dns.rb +42 -0
- data/lib/farce/resolv.rb +85 -0
- data/lib/farce/scheduler.rb +536 -0
- data/lib/farce/set.rb +39 -0
- data/lib/farce/shareable.rb +150 -0
- data/lib/farce/signal.rb +97 -0
- data/lib/farce/sorted_set.rb +36 -0
- data/lib/farce/strict/atom.rb +34 -0
- data/lib/farce/strict/counter.rb +10 -0
- data/lib/farce/strict/exchanger.rb +25 -0
- data/lib/farce/strict/flag.rb +10 -0
- data/lib/farce/strict/lazy.rb +26 -0
- data/lib/farce/strict/lazy_ref.rb +26 -0
- data/lib/farce/strict/lease.rb +27 -0
- data/lib/farce/strict/lease_map.rb +33 -0
- data/lib/farce/strict/lease_pool.rb +28 -0
- data/lib/farce/strict/lfu_map.rb +27 -0
- data/lib/farce/strict/lru_map.rb +28 -0
- data/lib/farce/strict/map.rb +54 -0
- data/lib/farce/strict/molecule.rb +16 -0
- data/lib/farce/strict/port.rb +40 -0
- data/lib/farce/strict/priority_queue.rb +20 -0
- data/lib/farce/strict/queue.rb +17 -0
- data/lib/farce/strict/set.rb +14 -0
- data/lib/farce/strict/sorted_set.rb +19 -0
- data/lib/farce/strict/timer_queue.rb +13 -0
- data/lib/farce/strict/tree_map.rb +27 -0
- data/lib/farce/strict/vector.rb +24 -0
- data/lib/farce/strict/weak_atom.rb +31 -0
- data/lib/farce/strict/weak_key_map.rb +49 -0
- data/lib/farce/strict/weak_map.rb +49 -0
- data/lib/farce/strict/weak_set.rb +14 -0
- data/lib/farce/strict/weak_value_map.rb +48 -0
- data/lib/farce/strict.rb +28 -0
- data/lib/farce/system.rb +40 -0
- data/lib/farce/thread_scheduler.rb +70 -0
- data/lib/farce/timer_queue.rb +66 -0
- data/lib/farce/transaction/atom.rb +73 -0
- data/lib/farce/transaction/map.rb +25 -0
- data/lib/farce/transaction/map_operations.rb +143 -0
- data/lib/farce/transaction/molecule.rb +90 -0
- data/lib/farce/transaction/mutable.rb +61 -0
- data/lib/farce/transaction/set.rb +17 -0
- data/lib/farce/transaction/set_operations.rb +48 -0
- data/lib/farce/transaction/sorted_set.rb +18 -0
- data/lib/farce/transaction/tree_map.rb +78 -0
- data/lib/farce/transaction/vector.rb +139 -0
- data/lib/farce/transaction/wrapper.rb +202 -0
- data/lib/farce/transaction.rb +343 -0
- data/lib/farce/tree_map.rb +56 -0
- data/lib/farce/unsafe/lfu_map.rb +36 -0
- data/lib/farce/unsafe/lru_map.rb +36 -0
- data/lib/farce/unsafe/tree_map.rb +21 -0
- data/lib/farce/unsafe.rb +29 -0
- data/lib/farce/unshareable.rb +67 -0
- data/lib/farce/unshared/atom.rb +24 -0
- data/lib/farce/unshared/counter.rb +10 -0
- data/lib/farce/unshared/flag.rb +10 -0
- data/lib/farce/unshared/lazy.rb +38 -0
- data/lib/farce/unshared/lazy_ref.rb +26 -0
- data/lib/farce/unshared/lease.rb +27 -0
- data/lib/farce/unshared/lease_map.rb +26 -0
- data/lib/farce/unshared/lease_pool.rb +25 -0
- data/lib/farce/unshared/lfu_map.rb +20 -0
- data/lib/farce/unshared/lru_map.rb +20 -0
- data/lib/farce/unshared/map.rb +49 -0
- data/lib/farce/unshared/molecule.rb +16 -0
- data/lib/farce/unshared/priority_queue.rb +16 -0
- data/lib/farce/unshared/queue.rb +27 -0
- data/lib/farce/unshared/set.rb +14 -0
- data/lib/farce/unshared/sorted_set.rb +28 -0
- data/lib/farce/unshared/timer_queue.rb +16 -0
- data/lib/farce/unshared/tree_map.rb +21 -0
- data/lib/farce/unshared/vector.rb +19 -0
- data/lib/farce/unshared/weak_atom.rb +31 -0
- data/lib/farce/unshared/weak_key_map.rb +44 -0
- data/lib/farce/unshared/weak_map.rb +44 -0
- data/lib/farce/unshared/weak_set.rb +14 -0
- data/lib/farce/unshared/weak_value_map.rb +43 -0
- data/lib/farce/unshared.rb +28 -0
- data/lib/farce/vector.rb +228 -0
- data/lib/farce/version.rb +8 -0
- data/lib/farce/walker/definitions.rb +186 -0
- data/lib/farce/walker/modification.rb +355 -0
- data/lib/farce/walker.rb +319 -0
- data/lib/farce/weak_atom.rb +121 -0
- data/lib/farce/weak_key_map.rb +31 -0
- data/lib/farce/weak_map.rb +33 -0
- data/lib/farce/weak_ref.rb +67 -0
- data/lib/farce/weak_set.rb +82 -0
- data/lib/farce/weak_value.rb +195 -0
- data/lib/farce/weak_value_map.rb +32 -0
- data/lib/farce.rb +519 -0
- metadata +468 -0
|
@@ -0,0 +1,394 @@
|
|
|
1
|
+
package org.farce;
|
|
2
|
+
|
|
3
|
+
import java.math.BigInteger;
|
|
4
|
+
import java.util.function.BiPredicate;
|
|
5
|
+
|
|
6
|
+
/** Hash-indexed LRU/LFU storage with opaque boxed Ruby keys and values. */
|
|
7
|
+
public final class BoundedMap {
|
|
8
|
+
private static final int INITIAL_TABLE_SIZE = 16;
|
|
9
|
+
private static final Object TOMBSTONE = new Object();
|
|
10
|
+
|
|
11
|
+
// Overflow tests compile a temporary source copy with this constant changed
|
|
12
|
+
// to Long.MAX_VALUE - 1, then restore the production jar built from one.
|
|
13
|
+
private static final long INITIAL_FREQUENCY = 1L;
|
|
14
|
+
private static final BigInteger LONG_MAX = BigInteger.valueOf(Long.MAX_VALUE);
|
|
15
|
+
private static final BigInteger ONE = BigInteger.ONE;
|
|
16
|
+
|
|
17
|
+
private static class Entry {
|
|
18
|
+
final Object key;
|
|
19
|
+
final int hash;
|
|
20
|
+
Object value;
|
|
21
|
+
int slot;
|
|
22
|
+
Entry previous;
|
|
23
|
+
Entry following;
|
|
24
|
+
|
|
25
|
+
Entry(Object key, Object value, int hash) {
|
|
26
|
+
this.key = key;
|
|
27
|
+
this.value = value;
|
|
28
|
+
this.hash = hash;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
private static final class LFUEntry extends Entry {
|
|
33
|
+
Bucket bucket;
|
|
34
|
+
|
|
35
|
+
LFUEntry(Object key, Object value, int hash) { super(key, value, hash); }
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
private static final class Bucket {
|
|
39
|
+
long frequency;
|
|
40
|
+
BigInteger bigFrequency;
|
|
41
|
+
Bucket previous;
|
|
42
|
+
Bucket following;
|
|
43
|
+
Entry leastRecent;
|
|
44
|
+
Entry mostRecent;
|
|
45
|
+
|
|
46
|
+
Bucket(long frequency, BigInteger bigFrequency) {
|
|
47
|
+
this.frequency = frequency;
|
|
48
|
+
this.bigFrequency = bigFrequency;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
private final boolean lfu;
|
|
53
|
+
private final BiPredicate<Object, Object> equal;
|
|
54
|
+
private Object[] table = new Object[INITIAL_TABLE_SIZE];
|
|
55
|
+
private int size;
|
|
56
|
+
private int used;
|
|
57
|
+
private long maxSize;
|
|
58
|
+
private Entry leastRecent;
|
|
59
|
+
private Entry mostRecent;
|
|
60
|
+
private Bucket leastFrequency;
|
|
61
|
+
private Bucket mostFrequency;
|
|
62
|
+
|
|
63
|
+
public BoundedMap(boolean lfu, long maxSize, BiPredicate<Object, Object> equal) {
|
|
64
|
+
if (maxSize < 0) throw new IllegalArgumentException("maxSize must be non-negative");
|
|
65
|
+
this.lfu = lfu;
|
|
66
|
+
this.maxSize = maxSize;
|
|
67
|
+
this.equal = equal;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
public Object get(Object key, int hash) {
|
|
71
|
+
Entry entry = find(key, hash);
|
|
72
|
+
if (entry == null) return null;
|
|
73
|
+
if (lfu) promoteLFU(entry);
|
|
74
|
+
else promoteLRU(entry);
|
|
75
|
+
return entry.value;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
public Object observe(Object key, int hash) {
|
|
79
|
+
Entry entry = find(key, hash);
|
|
80
|
+
return entry == null ? null : entry.value;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
public Object observeKey(Object key, int hash) {
|
|
84
|
+
Entry entry = find(key, hash);
|
|
85
|
+
return entry == null ? null : entry.key;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
public Object put(Object key, Object value, int hash) {
|
|
89
|
+
Entry existing = find(key, hash);
|
|
90
|
+
if (existing != null) {
|
|
91
|
+
if (lfu) promoteLFU(existing);
|
|
92
|
+
else promoteLRU(existing);
|
|
93
|
+
existing.value = value;
|
|
94
|
+
return value;
|
|
95
|
+
}
|
|
96
|
+
if (maxSize == 0) return value;
|
|
97
|
+
|
|
98
|
+
prepareTableForInsert();
|
|
99
|
+
Entry inserted = lfu ? new LFUEntry(key, value, hash) : new Entry(key, value, hash);
|
|
100
|
+
Bucket initial = null;
|
|
101
|
+
if (lfu && (leastFrequency == null || !hasInitialFrequency(leastFrequency))) {
|
|
102
|
+
initial = new Bucket(INITIAL_FREQUENCY, null);
|
|
103
|
+
}
|
|
104
|
+
Entry victim = size >= maxSize ? victim() : null;
|
|
105
|
+
|
|
106
|
+
int slot = emptySlot(hash);
|
|
107
|
+
if (table[slot] == null) used++;
|
|
108
|
+
table[slot] = inserted;
|
|
109
|
+
inserted.slot = slot;
|
|
110
|
+
size++;
|
|
111
|
+
if (lfu) {
|
|
112
|
+
if (initial != null) linkBucketBefore(initial, leastFrequency);
|
|
113
|
+
appendToBucket(initial == null ? leastFrequency : initial, inserted);
|
|
114
|
+
} else {
|
|
115
|
+
appendLRU(inserted);
|
|
116
|
+
}
|
|
117
|
+
if (victim != null) removeKnown(victim);
|
|
118
|
+
return value;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
public Object delete(Object key, int hash) {
|
|
122
|
+
Entry entry = find(key, hash);
|
|
123
|
+
if (entry == null) return null;
|
|
124
|
+
Object value = entry.value;
|
|
125
|
+
removeKnown(entry);
|
|
126
|
+
return value;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
public Object[] shift() {
|
|
130
|
+
Entry entry = victim();
|
|
131
|
+
if (entry == null) return null;
|
|
132
|
+
Object[] pair = new Object[] { entry.key, entry.value };
|
|
133
|
+
removeKnown(entry);
|
|
134
|
+
return pair;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
public long prune(long target) {
|
|
138
|
+
long removed = 0;
|
|
139
|
+
while (size > target) {
|
|
140
|
+
removeKnown(victim());
|
|
141
|
+
removed++;
|
|
142
|
+
}
|
|
143
|
+
return removed;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
public void setMaxSize(long limit) {
|
|
147
|
+
if (limit < 0) throw new IllegalArgumentException("limit must be non-negative");
|
|
148
|
+
while (size > limit) removeKnown(victim());
|
|
149
|
+
maxSize = limit;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
public long getMaxSize() { return maxSize; }
|
|
153
|
+
public long size() { return size; }
|
|
154
|
+
public boolean isEmpty() { return size == 0; }
|
|
155
|
+
|
|
156
|
+
public void clear() {
|
|
157
|
+
Object[] replacement = new Object[INITIAL_TABLE_SIZE];
|
|
158
|
+
table = replacement;
|
|
159
|
+
size = 0;
|
|
160
|
+
used = 0;
|
|
161
|
+
leastRecent = mostRecent = null;
|
|
162
|
+
leastFrequency = mostFrequency = null;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
public Object[] snapshot() {
|
|
166
|
+
if (size > Integer.MAX_VALUE / 2)
|
|
167
|
+
throw new OutOfMemoryError("bounded map snapshot is too large");
|
|
168
|
+
Object[] result = new Object[size * 2];
|
|
169
|
+
int offset = 0;
|
|
170
|
+
if (lfu) {
|
|
171
|
+
for (Bucket bucket = leastFrequency; bucket != null; bucket = bucket.following) {
|
|
172
|
+
for (Entry entry = bucket.leastRecent; entry != null; entry = entry.following) {
|
|
173
|
+
result[offset++] = entry.key;
|
|
174
|
+
result[offset++] = entry.value;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
} else {
|
|
178
|
+
for (Entry entry = leastRecent; entry != null; entry = entry.following) {
|
|
179
|
+
result[offset++] = entry.key;
|
|
180
|
+
result[offset++] = entry.value;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
return result;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/** Copy storage and eviction history without invoking Ruby key callbacks. */
|
|
187
|
+
public BoundedMap copy() {
|
|
188
|
+
BoundedMap copy = new BoundedMap(lfu, maxSize, equal);
|
|
189
|
+
copy.table = new Object[table.length];
|
|
190
|
+
if (lfu) {
|
|
191
|
+
for (Bucket sourceBucket = leastFrequency; sourceBucket != null; sourceBucket = sourceBucket.following) {
|
|
192
|
+
Bucket bucket = new Bucket(sourceBucket.frequency, sourceBucket.bigFrequency);
|
|
193
|
+
copy.linkBucketBefore(bucket, null);
|
|
194
|
+
for (Entry source = sourceBucket.leastRecent; source != null; source = source.following) {
|
|
195
|
+
Entry entry = new LFUEntry(source.key, source.value, source.hash);
|
|
196
|
+
copy.insertCopiedEntry(entry);
|
|
197
|
+
copy.appendToBucket(bucket, entry);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
} else {
|
|
201
|
+
for (Entry source = leastRecent; source != null; source = source.following) {
|
|
202
|
+
Entry entry = new Entry(source.key, source.value, source.hash);
|
|
203
|
+
copy.insertCopiedEntry(entry);
|
|
204
|
+
copy.appendLRU(entry);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
return copy;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
private void insertCopiedEntry(Entry entry) {
|
|
211
|
+
int slot = emptySlot(entry.hash);
|
|
212
|
+
table[slot] = entry;
|
|
213
|
+
entry.slot = slot;
|
|
214
|
+
size++;
|
|
215
|
+
used++;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
private Entry victim() {
|
|
219
|
+
return lfu ? (leastFrequency == null ? null : leastFrequency.leastRecent) : leastRecent;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
private Entry find(Object key, int hash) {
|
|
223
|
+
int mask = table.length - 1;
|
|
224
|
+
int slot = spread(hash) & mask;
|
|
225
|
+
while (true) {
|
|
226
|
+
Object item = table[slot];
|
|
227
|
+
if (item == null) return null;
|
|
228
|
+
if (item != TOMBSTONE) {
|
|
229
|
+
Entry entry = (Entry)item;
|
|
230
|
+
if (entry.hash == hash && equal.test(entry.key, key)) return entry;
|
|
231
|
+
}
|
|
232
|
+
slot = (slot + 1) & mask;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
private int emptySlot(int hash) {
|
|
237
|
+
int mask = table.length - 1;
|
|
238
|
+
int slot = spread(hash) & mask;
|
|
239
|
+
int tombstone = -1;
|
|
240
|
+
while (true) {
|
|
241
|
+
Object item = table[slot];
|
|
242
|
+
if (item == null) return tombstone < 0 ? slot : tombstone;
|
|
243
|
+
if (item == TOMBSTONE && tombstone < 0) tombstone = slot;
|
|
244
|
+
slot = (slot + 1) & mask;
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
private static int spread(int hash) { return hash ^ (hash >>> 16); }
|
|
249
|
+
|
|
250
|
+
private void prepareTableForInsert() {
|
|
251
|
+
int threshold = table.length - table.length / 3;
|
|
252
|
+
if (used + 1 <= threshold) return;
|
|
253
|
+
|
|
254
|
+
int desired = size + 1 <= threshold ? table.length : checkedDouble(table.length);
|
|
255
|
+
rebuild(desired);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
private static int checkedDouble(int length) {
|
|
259
|
+
if (length >= (1 << 30)) throw new OutOfMemoryError("bounded map index is too large");
|
|
260
|
+
return length << 1;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
private void rebuild(int capacity) {
|
|
264
|
+
Object[] replacement = new Object[capacity];
|
|
265
|
+
for (Object item : table) {
|
|
266
|
+
if (item == null || item == TOMBSTONE) continue;
|
|
267
|
+
Entry entry = (Entry)item;
|
|
268
|
+
int mask = replacement.length - 1;
|
|
269
|
+
int slot = spread(entry.hash) & mask;
|
|
270
|
+
while (replacement[slot] != null) slot = (slot + 1) & mask;
|
|
271
|
+
replacement[slot] = entry;
|
|
272
|
+
entry.slot = slot;
|
|
273
|
+
}
|
|
274
|
+
table = replacement;
|
|
275
|
+
used = size;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
private void removeKnown(Entry entry) {
|
|
279
|
+
table[entry.slot] = TOMBSTONE;
|
|
280
|
+
size--;
|
|
281
|
+
if (lfu) unlinkLFU(entry);
|
|
282
|
+
else unlinkLRU(entry);
|
|
283
|
+
entry.previous = entry.following = null;
|
|
284
|
+
if (lfu) ((LFUEntry)entry).bucket = null;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
private void appendLRU(Entry entry) {
|
|
288
|
+
entry.previous = mostRecent;
|
|
289
|
+
if (mostRecent == null) leastRecent = entry;
|
|
290
|
+
else mostRecent.following = entry;
|
|
291
|
+
mostRecent = entry;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
private void unlinkLRU(Entry entry) {
|
|
295
|
+
if (entry.previous == null) leastRecent = entry.following;
|
|
296
|
+
else entry.previous.following = entry.following;
|
|
297
|
+
if (entry.following == null) mostRecent = entry.previous;
|
|
298
|
+
else entry.following.previous = entry.previous;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
private void promoteLRU(Entry entry) {
|
|
302
|
+
if (entry == mostRecent) return;
|
|
303
|
+
unlinkLRU(entry);
|
|
304
|
+
entry.previous = entry.following = null;
|
|
305
|
+
appendLRU(entry);
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
private static boolean hasInitialFrequency(Bucket bucket) {
|
|
309
|
+
return bucket.bigFrequency == null && bucket.frequency == INITIAL_FREQUENCY;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
private static boolean sameFrequency(Bucket bucket, long frequency, BigInteger bigFrequency) {
|
|
313
|
+
if (bucket.bigFrequency == null || bigFrequency == null)
|
|
314
|
+
return bucket.bigFrequency == null && bigFrequency == null && bucket.frequency == frequency;
|
|
315
|
+
return bucket.bigFrequency.equals(bigFrequency);
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
private void promoteLFU(Entry entry) {
|
|
319
|
+
Bucket current = ((LFUEntry)entry).bucket;
|
|
320
|
+
Bucket following = current.following;
|
|
321
|
+
long frequency = 0;
|
|
322
|
+
BigInteger bigFrequency = null;
|
|
323
|
+
if (current.bigFrequency != null) bigFrequency = current.bigFrequency.add(ONE);
|
|
324
|
+
else if (current.frequency == Long.MAX_VALUE) bigFrequency = LONG_MAX.add(ONE);
|
|
325
|
+
else frequency = current.frequency + 1;
|
|
326
|
+
|
|
327
|
+
if (following != null && sameFrequency(following, frequency, bigFrequency)) {
|
|
328
|
+
unlinkFromBucket(current, entry);
|
|
329
|
+
appendToBucket(following, entry);
|
|
330
|
+
return;
|
|
331
|
+
}
|
|
332
|
+
if (current.leastRecent == entry && current.mostRecent == entry) {
|
|
333
|
+
current.frequency = frequency;
|
|
334
|
+
current.bigFrequency = bigFrequency;
|
|
335
|
+
return;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
Bucket destination = new Bucket(frequency, bigFrequency);
|
|
339
|
+
linkBucketAfter(destination, current);
|
|
340
|
+
unlinkFromBucket(current, entry);
|
|
341
|
+
appendToBucket(destination, entry);
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
private void appendToBucket(Bucket bucket, Entry entry) {
|
|
345
|
+
((LFUEntry)entry).bucket = bucket;
|
|
346
|
+
entry.previous = bucket.mostRecent;
|
|
347
|
+
entry.following = null;
|
|
348
|
+
if (bucket.mostRecent == null) bucket.leastRecent = entry;
|
|
349
|
+
else bucket.mostRecent.following = entry;
|
|
350
|
+
bucket.mostRecent = entry;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
private void unlinkLFU(Entry entry) { unlinkFromBucket(((LFUEntry)entry).bucket, entry); }
|
|
354
|
+
|
|
355
|
+
private void unlinkFromBucket(Bucket bucket, Entry entry) {
|
|
356
|
+
if (entry.previous == null) bucket.leastRecent = entry.following;
|
|
357
|
+
else entry.previous.following = entry.following;
|
|
358
|
+
if (entry.following == null) bucket.mostRecent = entry.previous;
|
|
359
|
+
else entry.following.previous = entry.previous;
|
|
360
|
+
entry.previous = entry.following = null;
|
|
361
|
+
if (bucket.leastRecent == null) unlinkBucket(bucket);
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
private void linkBucketBefore(Bucket bucket, Bucket following) {
|
|
365
|
+
bucket.following = following;
|
|
366
|
+
if (following == null) {
|
|
367
|
+
bucket.previous = mostFrequency;
|
|
368
|
+
if (mostFrequency == null) leastFrequency = bucket;
|
|
369
|
+
else mostFrequency.following = bucket;
|
|
370
|
+
mostFrequency = bucket;
|
|
371
|
+
} else {
|
|
372
|
+
bucket.previous = following.previous;
|
|
373
|
+
if (following.previous == null) leastFrequency = bucket;
|
|
374
|
+
else following.previous.following = bucket;
|
|
375
|
+
following.previous = bucket;
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
private void linkBucketAfter(Bucket bucket, Bucket previous) {
|
|
380
|
+
bucket.previous = previous;
|
|
381
|
+
bucket.following = previous.following;
|
|
382
|
+
if (previous.following == null) mostFrequency = bucket;
|
|
383
|
+
else previous.following.previous = bucket;
|
|
384
|
+
previous.following = bucket;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
private void unlinkBucket(Bucket bucket) {
|
|
388
|
+
if (bucket.previous == null) leastFrequency = bucket.following;
|
|
389
|
+
else bucket.previous.following = bucket.following;
|
|
390
|
+
if (bucket.following == null) mostFrequency = bucket.previous;
|
|
391
|
+
else bucket.following.previous = bucket.previous;
|
|
392
|
+
bucket.previous = bucket.following = null;
|
|
393
|
+
}
|
|
394
|
+
}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
package org.farce;
|
|
2
|
+
|
|
3
|
+
import java.io.IOException;
|
|
4
|
+
import java.nio.channels.SelectableChannel;
|
|
5
|
+
import java.nio.channels.SelectionKey;
|
|
6
|
+
import java.nio.channels.Selector;
|
|
7
|
+
import java.util.HashMap;
|
|
8
|
+
import java.util.Map;
|
|
9
|
+
import org.jruby.Ruby;
|
|
10
|
+
import org.jruby.RubyArray;
|
|
11
|
+
import org.jruby.RubyClass;
|
|
12
|
+
import org.jruby.RubyIO;
|
|
13
|
+
import org.jruby.RubyIOBuffer;
|
|
14
|
+
import java.nio.ByteBuffer;
|
|
15
|
+
import java.lang.reflect.Field;
|
|
16
|
+
import org.jruby.RubyModule;
|
|
17
|
+
import org.jruby.RubyObject;
|
|
18
|
+
import org.jruby.RubyThread;
|
|
19
|
+
import org.jruby.anno.JRubyMethod;
|
|
20
|
+
import org.jruby.runtime.ThreadContext;
|
|
21
|
+
import org.jruby.runtime.builtin.IRubyObject;
|
|
22
|
+
import org.jruby.util.io.SelectExecutor;
|
|
23
|
+
|
|
24
|
+
/** The public Ruby subclass itself owns this Java state. Channels remain Ruby-owned. */
|
|
25
|
+
public final class FiberScheduler extends RubyObject {
|
|
26
|
+
private Selector selector;
|
|
27
|
+
private RubyThread owner;
|
|
28
|
+
private long compatibilityPolls;
|
|
29
|
+
|
|
30
|
+
public FiberScheduler(Ruby runtime, RubyClass klass) { super(runtime, klass); }
|
|
31
|
+
|
|
32
|
+
public static void load(Ruby runtime) {
|
|
33
|
+
RubyModule internal = (RubyModule) runtime.getModule("Farce").getConstantAt("Internal");
|
|
34
|
+
RubyClass klass = internal.defineClassUnder("FiberScheduler", runtime.getObject(), FiberScheduler::new);
|
|
35
|
+
klass.defineAnnotatedMethods(FiberScheduler.class);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
@JRubyMethod(name = "nio_initialize")
|
|
39
|
+
public IRubyObject initializeNio(ThreadContext context) {
|
|
40
|
+
if (owner != null) throw context.runtime.newRuntimeError("scheduler already initialized");
|
|
41
|
+
owner = context.getThread().getFiberCurrentThread();
|
|
42
|
+
try { selector = Selector.open(); }
|
|
43
|
+
catch (IOException error) { throw context.runtime.newIOError(error.toString()); }
|
|
44
|
+
return context.nil;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
private void checkOwner(ThreadContext context) {
|
|
48
|
+
if (context.getThread().getFiberCurrentThread() != owner)
|
|
49
|
+
throw context.runtime.newThreadError("scheduler belongs to another thread");
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
private IRubyObject snapshot(ThreadContext context, RubyArray groups, Long milliseconds) {
|
|
53
|
+
// This primitive is below JRuby's scheduler dispatch and preserves Ruby IO buffers.
|
|
54
|
+
return new SelectExecutor(groups.entry(0), groups.entry(1), groups.entry(2), milliseconds).go(context);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
@JRubyMethod(name = "nio_select", required = 2)
|
|
58
|
+
public IRubyObject select(ThreadContext context, IRubyObject groupValue, IRubyObject timeout) {
|
|
59
|
+
checkOwner(context);
|
|
60
|
+
if (selector == null) throw context.runtime.newIOError("scheduler is closed");
|
|
61
|
+
RubyArray groups = (RubyArray) groupValue;
|
|
62
|
+
Long milliseconds = timeout.isNil() ? null : Math.max(0, (long)Math.ceil(timeout.convertToFloat().getDoubleValue() * 1000));
|
|
63
|
+
IRubyObject immediate = snapshot(context, groups, 0L);
|
|
64
|
+
if (!immediate.isNil() || (milliseconds != null && milliseconds == 0)) return immediate;
|
|
65
|
+
HashMap<SelectableChannel, Integer> interests = new HashMap<>();
|
|
66
|
+
boolean compatible = ((RubyArray)groups.entry(2)).isEmpty();
|
|
67
|
+
for (int i = 0; i < 2; i++) {
|
|
68
|
+
RubyArray list = (RubyArray)groups.entry(i);
|
|
69
|
+
for (Object object : list.toJavaArray()) {
|
|
70
|
+
RubyIO io = RubyIO.convertToIO(context, (IRubyObject)object);
|
|
71
|
+
SelectableChannel channel = io.getOpenFileChecked().fd().chSelect;
|
|
72
|
+
if (channel == null || channel.provider() != selector.provider()) { compatible = false; continue; }
|
|
73
|
+
int ops = i == 0 ? SelectionKey.OP_READ : SelectionKey.OP_WRITE;
|
|
74
|
+
if ((channel.validOps() & ops) == 0) { compatible = false; continue; }
|
|
75
|
+
interests.merge(channel, ops, (a, b) -> a | b);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
if (!compatible) {
|
|
79
|
+
compatibilityPolls++;
|
|
80
|
+
return snapshot(context, groups, milliseconds);
|
|
81
|
+
}
|
|
82
|
+
try {
|
|
83
|
+
for (SelectionKey key : selector.keys()) if (!interests.containsKey(key.channel())) key.cancel();
|
|
84
|
+
selector.selectNow(); // acknowledge cancelled keys before re-registering
|
|
85
|
+
for (Map.Entry<SelectableChannel, Integer> entry : interests.entrySet()) {
|
|
86
|
+
SelectableChannel channel = entry.getKey();
|
|
87
|
+
channel.configureBlocking(false);
|
|
88
|
+
SelectionKey key = channel.keyFor(selector);
|
|
89
|
+
if (key == null) channel.register(selector, entry.getValue());
|
|
90
|
+
else key.interestOps(entry.getValue());
|
|
91
|
+
}
|
|
92
|
+
if (milliseconds == null) selector.select();
|
|
93
|
+
else selector.select(milliseconds);
|
|
94
|
+
selector.selectedKeys().clear();
|
|
95
|
+
return snapshot(context, groups, 0L);
|
|
96
|
+
} catch (IOException error) {
|
|
97
|
+
throw context.runtime.newIOError(error.toString());
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
@JRubyMethod(name = "nio_wakeup")
|
|
102
|
+
public IRubyObject wakeup(ThreadContext context) {
|
|
103
|
+
Selector current = selector;
|
|
104
|
+
if (current != null) current.wakeup();
|
|
105
|
+
return context.nil;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
@JRubyMethod(name = "nio_destroy")
|
|
109
|
+
public IRubyObject destroy(ThreadContext context) {
|
|
110
|
+
if (owner != null) checkOwner(context);
|
|
111
|
+
Selector current = selector;
|
|
112
|
+
selector = null;
|
|
113
|
+
if (current != null) try { current.close(); }
|
|
114
|
+
catch (IOException error) { throw context.runtime.newIOError(error.toString()); }
|
|
115
|
+
return context.nil;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Some runtime-created hook buffers report size zero despite having storage.
|
|
119
|
+
// Inspect the buffer itself so the workaround also covers patched releases.
|
|
120
|
+
// Leave normal and genuinely empty buffers alone. For a malformed buffer,
|
|
121
|
+
// expose only the backing ByteBuffer's remaining range without changing it.
|
|
122
|
+
@JRubyMethod(name = "nio_hook_buffer", required = 1)
|
|
123
|
+
public IRubyObject hookBuffer(ThreadContext context, IRubyObject value) {
|
|
124
|
+
RubyIOBuffer buffer = (RubyIOBuffer)value;
|
|
125
|
+
if (!buffer.locked_p(context).isTrue()) return value;
|
|
126
|
+
if (buffer.size(context).convertToInteger().getLongValue() != 0) return value;
|
|
127
|
+
try {
|
|
128
|
+
Field field = RubyIOBuffer.class.getDeclaredField("base");
|
|
129
|
+
if (!field.trySetAccessible()) throw context.runtime.newRuntimeError("Farce cannot access the JRuby hook buffer layout");
|
|
130
|
+
ByteBuffer base = (ByteBuffer)field.get(buffer);
|
|
131
|
+
if (base == null || !base.hasRemaining()) return value;
|
|
132
|
+
int flags = buffer.readonly_p(context).isTrue() ? RubyIOBuffer.READONLY : 0;
|
|
133
|
+
return RubyIOBuffer.newBuffer(context, base.slice(), base.remaining(), flags);
|
|
134
|
+
} catch (ReflectiveOperationException error) {
|
|
135
|
+
throw context.runtime.newRuntimeError("Farce cannot adapt the JRuby hook buffer layout: " + error);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
@JRubyMethod(name = "compatibility_polls")
|
|
140
|
+
public IRubyObject compatibilityPolls(ThreadContext context) { return context.runtime.newFixnum(compatibilityPolls); }
|
|
141
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
package org.farce;
|
|
2
|
+
|
|
3
|
+
import java.util.Comparator;
|
|
4
|
+
|
|
5
|
+
/** Keeps ordinary float comparisons inside the JVM's TreeMap traversal. */
|
|
6
|
+
public final class PriorityKey {
|
|
7
|
+
// An opaque Ruby wrapper preserves String subclasses and comparison state
|
|
8
|
+
// across both JRuby's and TruffleRuby's automatic Java conversions.
|
|
9
|
+
private final Object rubyKey;
|
|
10
|
+
private final boolean floating;
|
|
11
|
+
private final double number;
|
|
12
|
+
private final boolean snapshot;
|
|
13
|
+
|
|
14
|
+
public PriorityKey(Object rubyKey, boolean floating, double number) {
|
|
15
|
+
this(rubyKey, floating, number, false);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
public PriorityKey(Object rubyKey, boolean floating, double number, boolean snapshot) {
|
|
19
|
+
this.rubyKey = rubyKey;
|
|
20
|
+
this.floating = floating;
|
|
21
|
+
this.number = number;
|
|
22
|
+
this.snapshot = snapshot;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
public boolean requiresSnapshot() { return snapshot; }
|
|
26
|
+
|
|
27
|
+
public Object getRubyKey() {
|
|
28
|
+
return rubyKey;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
public static Comparator<PriorityKey> comparator(Comparator<Object> fallback) {
|
|
32
|
+
return (left, right) -> {
|
|
33
|
+
// TreeMap checks its first key against itself. Ruby containers do
|
|
34
|
+
// not invoke the priority's comparator for that insertion.
|
|
35
|
+
if (left == right) return 0;
|
|
36
|
+
if (left.floating && right.floating &&
|
|
37
|
+
!Double.isNaN(left.number) && !Double.isNaN(right.number)) {
|
|
38
|
+
// Double.compare distinguishes signed zero; Ruby does not.
|
|
39
|
+
return left.number < right.number ? -1 : left.number > right.number ? 1 : 0;
|
|
40
|
+
}
|
|
41
|
+
// Preserve Ruby comparison failures, coercion, and mixed priorities.
|
|
42
|
+
return fallback.compare(left.rubyKey, right.rubyKey);
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
}
|