From d0dd1bbbadee21543e2f9fe3baf4be8520c72fce Mon Sep 17 00:00:00 2001 From: Nico Jensen Date: Tue, 12 Mar 2019 14:11:02 +0100 Subject: [PATCH] update bundle update --- Gemfile.lock | 2 +- vendor/bundle/cache/concurrent-ruby-1.1.5.gem | Bin 0 -> 356352 bytes .../gems/concurrent-ruby-1.1.5/CHANGELOG.md | 478 ++ .../bundle/gems/concurrent-ruby-1.1.5/Gemfile | 41 + .../gems/concurrent-ruby-1.1.5/LICENSE.md | 23 + .../gems/concurrent-ruby-1.1.5/README.md | 381 ++ .../gems/concurrent-ruby-1.1.5/Rakefile | 327 ++ .../ConcurrentRubyService.java | 17 + .../ext/AtomicReferenceLibrary.java | 175 + .../ext/JRubyMapBackendLibrary.java | 248 ++ .../ext/JavaAtomicBooleanLibrary.java | 93 + .../ext/JavaAtomicFixnumLibrary.java | 113 + .../ext/JavaSemaphoreLibrary.java | 159 + .../ext/SynchronizationLibrary.java | 307 ++ .../ext/jsr166e/ConcurrentHashMap.java | 31 + .../ext/jsr166e/ConcurrentHashMapV8.java | 3863 +++++++++++++++++ .../ext/jsr166e/LongAdder.java | 203 + .../ext/jsr166e/Striped64.java | 342 ++ .../jsr166e/nounsafe/ConcurrentHashMapV8.java | 3800 ++++++++++++++++ .../ext/jsr166e/nounsafe/LongAdder.java | 204 + .../ext/jsr166e/nounsafe/Striped64.java | 291 ++ .../ext/jsr166y/ThreadLocalRandom.java | 199 + .../lib/concurrent-ruby.rb | 1 + .../concurrent-ruby-1.1.5/lib/concurrent.rb | 134 + .../lib/concurrent/agent.rb | 587 +++ .../lib/concurrent/array.rb | 66 + .../lib/concurrent/async.rb | 459 ++ .../lib/concurrent/atom.rb | 222 + .../atomic/abstract_thread_local_var.rb | 66 + .../lib/concurrent/atomic/atomic_boolean.rb | 126 + .../lib/concurrent/atomic/atomic_fixnum.rb | 143 + .../atomic/atomic_markable_reference.rb | 164 + .../lib/concurrent/atomic/atomic_reference.rb | 204 + .../lib/concurrent/atomic/count_down_latch.rb | 100 + .../lib/concurrent/atomic/cyclic_barrier.rb | 128 + .../lib/concurrent/atomic/event.rb | 109 + .../atomic/java_count_down_latch.rb | 42 + .../atomic/java_thread_local_var.rb | 37 + .../concurrent/atomic/mutex_atomic_boolean.rb | 62 + .../concurrent/atomic/mutex_atomic_fixnum.rb | 75 + .../atomic/mutex_count_down_latch.rb | 44 + .../lib/concurrent/atomic/mutex_semaphore.rb | 115 + .../lib/concurrent/atomic/read_write_lock.rb | 254 ++ .../atomic/reentrant_read_write_lock.rb | 379 ++ .../atomic/ruby_thread_local_var.rb | 161 + .../lib/concurrent/atomic/semaphore.rb | 145 + .../lib/concurrent/atomic/thread_local_var.rb | 104 + .../atomic_reference/mutex_atomic.rb | 56 + .../atomic_reference/numeric_cas_wrapper.rb | 28 + .../lib/concurrent/atomics.rb | 10 + .../collection/copy_on_notify_observer_set.rb | 107 + .../collection/copy_on_write_observer_set.rb | 111 + .../java_non_concurrent_priority_queue.rb | 84 + .../concurrent/collection/lock_free_stack.rb | 158 + .../map/atomic_reference_map_backend.rb | 927 ++++ .../collection/map/mri_map_backend.rb | 66 + .../map/non_concurrent_map_backend.rb | 140 + .../map/synchronized_map_backend.rb | 82 + .../non_concurrent_priority_queue.rb | 143 + .../ruby_non_concurrent_priority_queue.rb | 150 + .../lib/concurrent/concern/deprecation.rb | 34 + .../lib/concurrent/concern/dereferenceable.rb | 73 + .../lib/concurrent/concern/logging.rb | 32 + .../lib/concurrent/concern/obligation.rb | 220 + .../lib/concurrent/concern/observable.rb | 110 + .../lib/concurrent/concurrent_ruby.jar | Bin 0 -> 137051 bytes .../lib/concurrent/configuration.rb | 184 + .../lib/concurrent/constants.rb | 8 + .../lib/concurrent/dataflow.rb | 81 + .../lib/concurrent/delay.rb | 199 + .../lib/concurrent/errors.rb | 69 + .../lib/concurrent/exchanger.rb | 352 ++ .../executor/abstract_executor_service.rb | 134 + .../concurrent/executor/cached_thread_pool.rb | 62 + .../concurrent/executor/executor_service.rb | 185 + .../concurrent/executor/fixed_thread_pool.rb | 206 + .../concurrent/executor/immediate_executor.rb | 66 + .../executor/indirect_immediate_executor.rb | 44 + .../executor/java_executor_service.rb | 91 + .../executor/java_single_thread_executor.rb | 29 + .../executor/java_thread_pool_executor.rb | 123 + .../executor/ruby_executor_service.rb | 78 + .../executor/ruby_single_thread_executor.rb | 22 + .../executor/ruby_thread_pool_executor.rb | 362 ++ .../concurrent/executor/safe_task_executor.rb | 35 + .../executor/serial_executor_service.rb | 34 + .../executor/serialized_execution.rb | 107 + .../serialized_execution_delegator.rb | 28 + .../executor/simple_executor_service.rb | 100 + .../executor/single_thread_executor.rb | 56 + .../executor/thread_pool_executor.rb | 87 + .../lib/concurrent/executor/timer_set.rb | 173 + .../lib/concurrent/executors.rb | 20 + .../lib/concurrent/future.rb | 141 + .../lib/concurrent/hash.rb | 59 + .../lib/concurrent/immutable_struct.rb | 93 + .../lib/concurrent/ivar.rb | 207 + .../lib/concurrent/map.rb | 337 ++ .../lib/concurrent/maybe.rb | 229 + .../lib/concurrent/mutable_struct.rb | 229 + .../lib/concurrent/mvar.rb | 242 ++ .../lib/concurrent/options.rb | 42 + .../lib/concurrent/promise.rb | 579 +++ .../lib/concurrent/promises.rb | 2167 +++++++++ .../lib/concurrent/re_include.rb | 58 + .../lib/concurrent/scheduled_task.rb | 318 ++ .../lib/concurrent/set.rb | 66 + .../lib/concurrent/settable_struct.rb | 129 + .../lib/concurrent/synchronization.rb | 30 + .../abstract_lockable_object.rb | 98 + .../synchronization/abstract_object.rb | 24 + .../synchronization/abstract_struct.rb | 160 + .../concurrent/synchronization/condition.rb | 60 + .../synchronization/jruby_lockable_object.rb | 13 + .../synchronization/jruby_object.rb | 45 + .../lib/concurrent/synchronization/lock.rb | 36 + .../synchronization/lockable_object.rb | 74 + .../concurrent/synchronization/mri_object.rb | 44 + .../synchronization/mutex_lockable_object.rb | 76 + .../lib/concurrent/synchronization/object.rb | 183 + .../synchronization/rbx_lockable_object.rb | 65 + .../concurrent/synchronization/rbx_object.rb | 49 + .../synchronization/truffleruby_object.rb | 47 + .../concurrent/synchronization/volatile.rb | 36 + .../thread_safe/synchronized_delegator.rb | 50 + .../lib/concurrent/thread_safe/util.rb | 16 + .../lib/concurrent/thread_safe/util/adder.rb | 74 + .../thread_safe/util/cheap_lockable.rb | 118 + .../thread_safe/util/data_structures.rb | 63 + .../thread_safe/util/power_of_two_tuple.rb | 38 + .../concurrent/thread_safe/util/striped64.rb | 246 ++ .../concurrent/thread_safe/util/volatile.rb | 75 + .../thread_safe/util/xor_shift_random.rb | 50 + .../lib/concurrent/timer_task.rb | 334 ++ .../lib/concurrent/tuple.rb | 86 + .../lib/concurrent/tvar.rb | 258 ++ .../lib/concurrent/utility/at_exit.rb | 97 + .../lib/concurrent/utility/engine.rb | 56 + .../lib/concurrent/utility/monotonic_time.rb | 58 + .../utility/native_extension_loader.rb | 79 + .../lib/concurrent/utility/native_integer.rb | 53 + .../concurrent/utility/processor_counter.rb | 158 + .../lib/concurrent/version.rb | 3 + .../concurrent-ruby-1.1.5.gemspec | 24 + 144 files changed, 28561 insertions(+), 1 deletion(-) create mode 100644 vendor/bundle/cache/concurrent-ruby-1.1.5.gem create mode 100644 vendor/bundle/gems/concurrent-ruby-1.1.5/CHANGELOG.md create mode 100644 vendor/bundle/gems/concurrent-ruby-1.1.5/Gemfile create mode 100644 vendor/bundle/gems/concurrent-ruby-1.1.5/LICENSE.md create mode 100644 vendor/bundle/gems/concurrent-ruby-1.1.5/README.md create mode 100644 vendor/bundle/gems/concurrent-ruby-1.1.5/Rakefile create mode 100644 vendor/bundle/gems/concurrent-ruby-1.1.5/ext/concurrent-ruby/ConcurrentRubyService.java create mode 100644 vendor/bundle/gems/concurrent-ruby-1.1.5/ext/concurrent-ruby/com/concurrent_ruby/ext/AtomicReferenceLibrary.java create mode 100644 vendor/bundle/gems/concurrent-ruby-1.1.5/ext/concurrent-ruby/com/concurrent_ruby/ext/JRubyMapBackendLibrary.java create mode 100644 vendor/bundle/gems/concurrent-ruby-1.1.5/ext/concurrent-ruby/com/concurrent_ruby/ext/JavaAtomicBooleanLibrary.java create mode 100644 vendor/bundle/gems/concurrent-ruby-1.1.5/ext/concurrent-ruby/com/concurrent_ruby/ext/JavaAtomicFixnumLibrary.java create mode 100644 vendor/bundle/gems/concurrent-ruby-1.1.5/ext/concurrent-ruby/com/concurrent_ruby/ext/JavaSemaphoreLibrary.java create mode 100644 vendor/bundle/gems/concurrent-ruby-1.1.5/ext/concurrent-ruby/com/concurrent_ruby/ext/SynchronizationLibrary.java create mode 100644 vendor/bundle/gems/concurrent-ruby-1.1.5/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/ConcurrentHashMap.java create mode 100644 vendor/bundle/gems/concurrent-ruby-1.1.5/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/ConcurrentHashMapV8.java create mode 100644 vendor/bundle/gems/concurrent-ruby-1.1.5/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/LongAdder.java create mode 100644 vendor/bundle/gems/concurrent-ruby-1.1.5/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/Striped64.java create mode 100644 vendor/bundle/gems/concurrent-ruby-1.1.5/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/nounsafe/ConcurrentHashMapV8.java create mode 100644 vendor/bundle/gems/concurrent-ruby-1.1.5/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/nounsafe/LongAdder.java create mode 100644 vendor/bundle/gems/concurrent-ruby-1.1.5/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/nounsafe/Striped64.java create mode 100644 vendor/bundle/gems/concurrent-ruby-1.1.5/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166y/ThreadLocalRandom.java create mode 100644 vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent-ruby.rb create mode 100644 vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent.rb create mode 100644 vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/agent.rb create mode 100644 vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/array.rb create mode 100644 vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/async.rb create mode 100644 vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/atom.rb create mode 100644 vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic/abstract_thread_local_var.rb create mode 100644 vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic/atomic_boolean.rb create mode 100644 vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic/atomic_fixnum.rb create mode 100644 vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic/atomic_markable_reference.rb create mode 100644 vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic/atomic_reference.rb create mode 100644 vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic/count_down_latch.rb create mode 100644 vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic/cyclic_barrier.rb create mode 100644 vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic/event.rb create mode 100644 vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic/java_count_down_latch.rb create mode 100644 vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic/java_thread_local_var.rb create mode 100644 vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic/mutex_atomic_boolean.rb create mode 100644 vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic/mutex_atomic_fixnum.rb create mode 100644 vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic/mutex_count_down_latch.rb create mode 100644 vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic/mutex_semaphore.rb create mode 100644 vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic/read_write_lock.rb create mode 100644 vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic/reentrant_read_write_lock.rb create mode 100644 vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic/ruby_thread_local_var.rb create mode 100644 vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic/semaphore.rb create mode 100644 vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic/thread_local_var.rb create mode 100644 vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic_reference/mutex_atomic.rb create mode 100644 vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic_reference/numeric_cas_wrapper.rb create mode 100644 vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/atomics.rb create mode 100644 vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/collection/copy_on_notify_observer_set.rb create mode 100644 vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/collection/copy_on_write_observer_set.rb create mode 100644 vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/collection/java_non_concurrent_priority_queue.rb create mode 100644 vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/collection/lock_free_stack.rb create mode 100644 vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/collection/map/atomic_reference_map_backend.rb create mode 100644 vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/collection/map/mri_map_backend.rb create mode 100644 vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/collection/map/non_concurrent_map_backend.rb create mode 100644 vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/collection/map/synchronized_map_backend.rb create mode 100644 vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/collection/non_concurrent_priority_queue.rb create mode 100644 vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/collection/ruby_non_concurrent_priority_queue.rb create mode 100644 vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/concern/deprecation.rb create mode 100644 vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/concern/dereferenceable.rb create mode 100644 vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/concern/logging.rb create mode 100644 vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/concern/obligation.rb create mode 100644 vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/concern/observable.rb create mode 100644 vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/concurrent_ruby.jar create mode 100644 vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/configuration.rb create mode 100644 vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/constants.rb create mode 100644 vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/dataflow.rb create mode 100644 vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/delay.rb create mode 100644 vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/errors.rb create mode 100644 vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/exchanger.rb create mode 100644 vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/executor/abstract_executor_service.rb create mode 100644 vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/executor/cached_thread_pool.rb create mode 100644 vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/executor/executor_service.rb create mode 100644 vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/executor/fixed_thread_pool.rb create mode 100644 vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/executor/immediate_executor.rb create mode 100644 vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/executor/indirect_immediate_executor.rb create mode 100644 vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/executor/java_executor_service.rb create mode 100644 vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/executor/java_single_thread_executor.rb create mode 100644 vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/executor/java_thread_pool_executor.rb create mode 100644 vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/executor/ruby_executor_service.rb create mode 100644 vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/executor/ruby_single_thread_executor.rb create mode 100644 vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/executor/ruby_thread_pool_executor.rb create mode 100644 vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/executor/safe_task_executor.rb create mode 100644 vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/executor/serial_executor_service.rb create mode 100644 vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/executor/serialized_execution.rb create mode 100644 vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/executor/serialized_execution_delegator.rb create mode 100644 vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/executor/simple_executor_service.rb create mode 100644 vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/executor/single_thread_executor.rb create mode 100644 vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/executor/thread_pool_executor.rb create mode 100644 vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/executor/timer_set.rb create mode 100644 vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/executors.rb create mode 100644 vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/future.rb create mode 100644 vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/hash.rb create mode 100644 vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/immutable_struct.rb create mode 100644 vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/ivar.rb create mode 100644 vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/map.rb create mode 100644 vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/maybe.rb create mode 100644 vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/mutable_struct.rb create mode 100644 vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/mvar.rb create mode 100644 vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/options.rb create mode 100644 vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/promise.rb create mode 100644 vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/promises.rb create mode 100644 vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/re_include.rb create mode 100644 vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/scheduled_task.rb create mode 100644 vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/set.rb create mode 100644 vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/settable_struct.rb create mode 100644 vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/synchronization.rb create mode 100644 vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/synchronization/abstract_lockable_object.rb create mode 100644 vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/synchronization/abstract_object.rb create mode 100644 vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/synchronization/abstract_struct.rb create mode 100644 vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/synchronization/condition.rb create mode 100644 vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/synchronization/jruby_lockable_object.rb create mode 100644 vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/synchronization/jruby_object.rb create mode 100644 vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/synchronization/lock.rb create mode 100644 vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/synchronization/lockable_object.rb create mode 100644 vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/synchronization/mri_object.rb create mode 100644 vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/synchronization/mutex_lockable_object.rb create mode 100644 vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/synchronization/object.rb create mode 100644 vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/synchronization/rbx_lockable_object.rb create mode 100644 vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/synchronization/rbx_object.rb create mode 100644 vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/synchronization/truffleruby_object.rb create mode 100644 vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/synchronization/volatile.rb create mode 100644 vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/thread_safe/synchronized_delegator.rb create mode 100644 vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/thread_safe/util.rb create mode 100644 vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/thread_safe/util/adder.rb create mode 100644 vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/thread_safe/util/cheap_lockable.rb create mode 100644 vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/thread_safe/util/data_structures.rb create mode 100644 vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/thread_safe/util/power_of_two_tuple.rb create mode 100644 vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/thread_safe/util/striped64.rb create mode 100644 vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/thread_safe/util/volatile.rb create mode 100644 vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/thread_safe/util/xor_shift_random.rb create mode 100644 vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/timer_task.rb create mode 100644 vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/tuple.rb create mode 100644 vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/tvar.rb create mode 100644 vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/utility/at_exit.rb create mode 100644 vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/utility/engine.rb create mode 100644 vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/utility/monotonic_time.rb create mode 100644 vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/utility/native_extension_loader.rb create mode 100644 vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/utility/native_integer.rb create mode 100644 vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/utility/processor_counter.rb create mode 100644 vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/version.rb create mode 100644 vendor/bundle/specifications/concurrent-ruby-1.1.5.gemspec diff --git a/Gemfile.lock b/Gemfile.lock index 918e5c7..0e3c232 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -4,7 +4,7 @@ GEM addressable (2.6.0) public_suffix (>= 2.0.2, < 4.0) colorator (1.1.0) - concurrent-ruby (1.1.4) + concurrent-ruby (1.1.5) em-websocket (0.5.1) eventmachine (>= 0.12.9) http_parser.rb (~> 0.6.0) diff --git a/vendor/bundle/cache/concurrent-ruby-1.1.5.gem b/vendor/bundle/cache/concurrent-ruby-1.1.5.gem new file mode 100644 index 0000000000000000000000000000000000000000..ce1284af9558b9e65cd09e5561830ca3a839bc5b GIT binary patch literal 356352 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

^q*DXCqf9NXX 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 u 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 literal 0 HcmV?d00001 diff --git a/vendor/bundle/gems/concurrent-ruby-1.1.5/CHANGELOG.md b/vendor/bundle/gems/concurrent-ruby-1.1.5/CHANGELOG.md new file mode 100644 index 0000000..f33cc76 --- /dev/null +++ b/vendor/bundle/gems/concurrent-ruby-1.1.5/CHANGELOG.md @@ -0,0 +1,478 @@ +## Current + +## Release v1.1.5, edge v0.5.0 (10 mar 2019) + +concurrent-ruby: + +* fix potential leak of context on JRuby and Java 7 + +concurrent-ruby-edge: + +* Add finalized Concurrent::Cancellation +* Add finalized Concurrent::Throttle +* Add finalized Concurrent::Promises::Channel +* Add new Concurrent::ErlangActor + +## Release v1.1.4 (14 Dec 2018) + +* (#780) Remove java_alias of 'submit' method of Runnable to let executor service work on java 11 +* (#776) Fix NameError on defining a struct with a name which is already taken in an ancestor + +## Release v1.1.3 (7 Nov 2018) + +* (#775) fix partial require of the gem (although not officially supported) + +## Release v1.1.2 (6 Nov 2018) + +* (#773) more defensive 1.9.3 support + +## Release v1.1.1, edge v0.4.1 (1 Nov 2018) + +* (#768) add support for 1.9.3 back + +## Release v1.1.0, edge v0.4.0 (31 OCt 2018) (yanked) + +* (#768) yanked because of issues with removed 1.9.3 support + +## Release v1.1.0.pre2, edge v0.4.0.pre2 (18 Sep 2018) + +concurrent-ruby: + +* fixed documentation and README links +* fix Set for TruffleRuby and Rubinius +* use properly supported TruffleRuby APIs + +concurrent-ruby-edge: + +* add Promises.zip_futures_over_on + +## Release v1.1.0.pre1, edge v0.4.0.pre1 (15 Aug 2018) + +concurrent-ruby: + +* requires at least Ruby 2.0 +* [Promises](http://ruby-concurrency.github.io/concurrent-ruby/1.1.0/Concurrent/Promises.html) + are moved from `concurrent-ruby-edge` to `concurrent-ruby` +* Add support for TruffleRuby + * (#734) Fix Array/Hash/Set construction broken on TruffleRuby + * AtomicReference fixed +* CI stabilization +* remove sharp dependency edge -> core +* remove warnings +* documentation updates +* Exchanger is no longer documented as edge since it was already available in + `concurrent-ruby` +* (#644) Fix Map#each and #each_pair not returning enumerator outside of MRI +* (#659) Edge promises fail during error handling +* (#741) Raise on recursive Delay#value call +* (#727) #717 fix global IO executor on JRuby +* (#740) Drop support for CRuby 1.9, JRuby 1.7, Rubinius. +* (#737) Move AtomicMarkableReference out of Edge +* (#708) Prefer platform specific memory barriers +* (#735) Fix wrong expected exception in channel spec assertion +* (#729) Allow executor option in `Promise#then` +* (#725) fix timeout check to use timeout_interval +* (#719) update engine detection +* (#660) Add specs for Promise#zip/Promise.zip ordering +* (#654) Promise.zip execution changes +* (#666) Add thread safe set implementation +* (#651) #699 #to_s, #inspect should not output negative object IDs. +* (#685) Avoid RSpec warnings about raise_error +* (#680) Avoid RSpec monkey patching, persist spec results locally, use RSpec + v3.7.0 +* (#665) Initialize the monitor for new subarrays on Rubinius +* (#661) Fix error handling in edge promises + +concurrent-ruby-edge: + +* (#659) Edge promises fail during error handling +* Edge files clearly separated in `lib-edge` +* added ReInclude + +## Release v1.0.5, edge v0.3.1 (26 Feb 2017) + +concurrent-ruby: + +* Documentation for Event and Semaphore +* Use Unsafe#fullFence and #loadFence directly since the shortcuts were removed in JRuby +* Do not depend on org.jruby.util.unsafe.UnsafeHolder + +concurrent-ruby-edge: + +* (#620) Actors on Pool raise an error +* (#624) Delayed promises did not interact correctly with flatting + * Fix arguments yielded by callback methods +* Overridable default executor in promises factory methods +* Asking actor to terminate will always resolve to `true` + +## Release v1.0.4, edge v0.3.0 (27 Dec 2016) + +concurrent-ruby: + +* Nothing + +concurrent-ruby-edge: + +* New promises' API renamed, lots of improvements, edge bumped to 0.3.0 + * **Incompatible** with previous 0.2.3 version + * see https://github.com/ruby-concurrency/concurrent-ruby/pull/522 + +## Release v1.0.3 (17 Dec 2016) + +* Trigger execution of flattened delayed futures +* Avoid forking for processor_count if possible +* Semaphore Mutex and JRuby parity +* Adds Map#each as alias to Map#each_pair +* Fix uninitialized instance variables +* Make Fixnum, Bignum merger ready +* Allows Promise#then to receive an executor +* TimerSet now survives a fork +* Reject promise on any exception +* Allow ThreadLocalVar to be initialized with a block +* Support Alpha with `Concurrent::processor_count` +* Fixes format-security error when compiling ruby_193_compatible.h +* Concurrent::Atom#swap fixed: reraise the exceptions from block + +## Release v1.0.2 (2 May 2016) + +* Fix bug with `Concurrent::Map` MRI backend `#inspect` method +* Fix bug with `Concurrent::Map` MRI backend using `Hash#value?` +* Improved documentation and examples +* Minor updates to Edge + +## Release v1.0.1 (27 February 2016) + +* Fix "uninitialized constant Concurrent::ReentrantReadWriteLock" error. +* Better handling of `autoload` vs. `require`. +* Improved API for Edge `Future` zipping. +* Fix reference leak in Edge `Future` constructor . +* Fix bug which prevented thread pools from surviving a `fork`. +* Fix bug in which `TimerTask` did not correctly specify all its dependencies. +* Improved support for JRuby+Truffle +* Improved error messages. +* Improved documentation. +* Updated README and CONTRIBUTING. + +## Release v1.0.0 (13 November 2015) + +* Rename `attr_volatile_with_cas` to `attr_atomic` +* Add `clear_each` to `LockFreeStack` +* Update `AtomicReference` documentation +* Further updates and improvements to the synchronization layer. +* Performance and memory usage performance with `Actor` logging. +* Fixed `ThreadPoolExecutor` task count methods. +* Improved `Async` performance for both short and long-lived objects. +* Fixed bug in `LockFreeLinkedSet`. +* Fixed bug in which `Agent#await` triggered a validation failure. +* Further `Channel` updates. +* Adopted a project Code of Conduct +* Cleared interpreter warnings +* Fixed bug in `ThreadPoolExecutor` task count methods +* Fixed bug in 'LockFreeLinkedSet' +* Improved Java extension loading +* Handle Exception children in Edge::Future +* Continued improvements to channel +* Removed interpreter warnings. +* Shared constants now in `lib/concurrent/constants.rb` +* Refactored many tests. +* Improved synchronization layer/memory model documentation. +* Bug fix in Edge `Future#flat` +* Brand new `Channel` implementation in Edge gem. +* Simplification of `RubySingleThreadExecutor` +* `Async` improvements + - Each object uses its own `SingleThreadExecutor` instead of the global thread pool. + - No longers supports executor injection + - Much better documentation +* `Atom` updates + - No longer `Dereferenceable` + - Now `Observable` + - Added a `#reset` method +* Brand new `Agent` API and implementation. Now functionally equivalent to Clojure. +* Continued improvements to the synchronization layer +* Merged in the `thread_safe` gem + - `Concurrent::Array` + - `Concurrent::Hash` + - `Concurrent::Map` (formerly ThreadSafe::Cache) + - `Concurrent::Tuple` +* Minor improvements to Concurrent::Map +* Complete rewrite of `Exchanger` +* Removed all deprecated code (classes, methods, constants, etc.) +* Updated Agent, MutexAtomic, and BufferedChannel to inherit from Synchronization::Object. +* Many improved tests +* Some internal reorganization + +## Release v0.9.1 (09 August 2015) + +* Fixed a Rubiniux bug in synchronization object +* Fixed all interpreter warnings (except circular references) +* Fixed require statements when requiring `Atom` alone +* Significantly improved `ThreadLocalVar` on non-JRuby platforms +* Fixed error handling in Edge `Concurrent.zip` +* `AtomicFixnum` methods `#increment` and `#decrement` now support optional delta +* New `AtomicFixnum#update` method +* Minor optimizations in `ReadWriteLock` +* New `ReentrantReadWriteLock` class +* `ThreadLocalVar#bind` method is now public +* Refactored many tests + +## Release v0.9.0 (10 July 2015) + +* Updated `AtomicReference` + - `AtomicReference#try_update` now simply returns instead of raising exception + - `AtomicReference#try_update!` was added to raise exceptions if an update + fails. Note: this is the same behavior as the old `try_update` +* Pure Java implementations of + - `AtomicBoolean` + - `AtomicFixnum` + - `Semaphore` +* Fixed bug when pruning Ruby thread pools +* Fixed bug in time calculations within `ScheduledTask` +* Default `count` in `CountDownLatch` to 1 +* Use monotonic clock for all timers via `Concurrent.monotonic_time` + - Use `Process.clock_gettime(Process::CLOCK_MONOTONIC)` when available + - Fallback to `java.lang.System.nanoTime()` on unsupported JRuby versions + - Pure Ruby implementation for everything else + - Effects `Concurrent.timer`, `Concurrent.timeout`, `TimerSet`, `TimerTask`, and `ScheduledTask` +* Deprecated all clock-time based timer scheduling + - Only support scheduling by delay + - Effects `Concurrent.timer`, `TimerSet`, and `ScheduledTask` +* Added new `ReadWriteLock` class +* Consistent `at_exit` behavior for Java and Ruby thread pools. +* Added `at_exit` handler to Ruby thread pools (already in Java thread pools) + - Ruby handler stores the object id and retrieves from `ObjectSpace` + - JRuby disables `ObjectSpace` by default so that handler stores the object reference +* Added a `:stop_on_exit` option to thread pools to enable/disable `at_exit` handler +* Updated thread pool docs to better explain shutting down thread pools +* Simpler `:executor` option syntax for all abstractions which support this option +* Added `Executor#auto_terminate?` predicate method (for thread pools) +* Added `at_exit` handler to `TimerSet` +* Simplified auto-termination of the global executors + - Can now disable auto-termination of global executors + - Added shutdown/kill/wait_for_termination variants for global executors +* Can now disable auto-termination for *all* executors (the nuclear option) +* Simplified auto-termination of the global executors +* Deprecated terms "task pool" and "operation pool" + - New terms are "io executor" and "fast executor" + - New functions added with new names + - Deprecation warnings added to functions referencing old names +* Moved all thread pool related functions from `Concurrent::Configuration` to `Concurrent` + - Old functions still exist with deprecation warnings + - New functions have updated names as appropriate +* All high-level abstractions default to the "io executor" +* Fixed bug in `Actor` causing it to prematurely warm global thread pools on gem load + - This also fixed a `RejectedExecutionError` bug when running with minitest/autorun via JRuby +* Moved global logger up to the `Concurrent` namespace and refactored the code +* Optimized the performance of `Delay` + - Fixed a bug in which no executor option on construction caused block execution on a global thread pool +* Numerous improvements and bug fixes to `TimerSet` +* Fixed deadlock of `Future` when the handler raises Exception +* Added shared specs for more classes +* New concurrency abstractions including: + - `Atom` + - `Maybe` + - `ImmutableStruct` + - `MutableStruct` + - `SettableStruct` +* Created an Edge gem for unstable abstractions including + - `Actor` + - `Agent` + - `Channel` + - `Exchanger` + - `LazyRegister` + - **new Future Framework** - unified + implementation of Futures and Promises which combines Features of previous `Future`, + `Promise`, `IVar`, `Event`, `Probe`, `dataflow`, `Delay`, `TimerTask` into single framework. It uses extensively + new synchronization layer to make all the paths **lock-free** with exception of blocking threads on `#wait`. + It offers better performance and does not block threads when not required. +* Actor framework changes: + - fixed reset loop in Pool + - Pool can use any actor as a worker, abstract worker class is no longer needed. + - Actor events not have format `[:event_name, *payload]` instead of just the Symbol. + - Actor now uses new Future/Promise Framework instead of `IVar` for better interoperability + - Behaviour definition array was simplified to `[BehaviourClass1, [BehaviourClass2, *initialization_args]]` + - Linking behavior responds to :linked message by returning array of linked actors + - Supervised behavior is removed in favour of just Linking + - RestartingContext is supervised by default now, `supervise: true` is not required any more + - Events can be private and public, so far only difference is that Linking will + pass to linked actors only public messages. Adding private :restarting and + :resetting events which are send before the actor restarts or resets allowing + to add callbacks to cleanup current child actors. + - Print also object_id in Reference to_s + - Add AbstractContext#default_executor to be able to override executor class wide + - Add basic IO example + - Documentation somewhat improved + - All messages should have same priority. It's now possible to send `actor << job1 << job2 << :terminate!` and + be sure that both jobs are processed first. +* Refactored `Channel` to use newer synchronization objects +* Added `#reset` and `#cancel` methods to `TimerSet` +* Added `#cancel` method to `Future` and `ScheduledTask` +* Refactored `TimerSet` to use `ScheduledTask` +* Updated `Async` with a factory that initializes the object +* Deprecated `Concurrent.timer` and `Concurrent.timeout` +* Reduced max threads on pure-Ruby thread pools (abends around 14751 threads) +* Moved many private/internal classes/modules into "namespace" modules +* Removed brute-force killing of threads in tests +* Fixed a thread pool bug when the operating system cannot allocate more threads + +## Release v0.8.0 (25 January 2015) + +* C extension for MRI have been extracted into the `concurrent-ruby-ext` companion gem. + Please see the README for more detail. +* Better variable isolation in `Promise` and `Future` via an `:args` option +* Continued to update intermittently failing tests + +## Release v0.7.2 (24 January 2015) + +* New `Semaphore` class based on [java.util.concurrent.Semaphore](http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/Semaphore.html) +* New `Promise.all?` and `Promise.any?` class methods +* Renamed `:overflow_policy` on thread pools to `:fallback_policy` +* Thread pools still accept the `:overflow_policy` option but display a warning +* Thread pools now implement `fallback_policy` behavior when not running (rather than universally rejecting tasks) +* Fixed minor `set_deref_options` constructor bug in `Promise` class +* Fixed minor `require` bug in `ThreadLocalVar` class +* Fixed race condition bug in `TimerSet` class +* Fixed race condition bug in `TimerSet` class +* Fixed signal bug in `TimerSet#post` method +* Numerous non-functional updates to clear warning when running in debug mode +* Fixed more intermittently failing tests +* Tests now run on new Travis build environment +* Multiple documentation updates + +## Release v0.7.1 (4 December 2014) + +Please see the [roadmap](https://github.com/ruby-concurrency/concurrent-ruby/issues/142) for more information on the next planned release. + +* Added `flat_map` method to `Promise` +* Added `zip` method to `Promise` +* Fixed bug with logging in `Actor` +* Improvements to `Promise` tests +* Removed actor-experimental warning +* Added an `IndirectImmediateExecutor` class +* Allow disabling auto termination of global executors +* Fix thread leaking in `ThreadLocalVar` (uses `Ref` gem on non-JRuby systems) +* Fix thread leaking when pruning pure-Ruby thread pools +* Prevent `Actor` from using an `ImmediateExecutor` (causes deadlock) +* Added missing synchronizations to `TimerSet` +* Fixed bug with return value of `Concurrent::Actor::Utils::Pool#ask` +* Fixed timing bug in `TimerTask` +* Fixed bug when creating a `JavaThreadPoolExecutor` with minimum pool size of zero +* Removed confusing warning when not using native extenstions +* Improved documentation + +## Release v0.7.0 (13 August 2014) + +* Merge the [atomic](https://github.com/ruby-concurrency/atomic) gem + - Pure Ruby `MutexAtomic` atomic reference class + - Platform native atomic reference classes `CAtomic`, `JavaAtomic`, and `RbxAtomic` + - Automated [build process](https://github.com/ruby-concurrency/rake-compiler-dev-box) + - Fat binary releases for [multiple platforms](https://rubygems.org/gems/concurrent-ruby/versions) including Windows (32/64), Linux (32/64), OS X (64-bit), Solaris (64-bit), and JRuby +* C native `CAtomicBoolean` +* C native `CAtomicFixnum` +* Refactored intermittently failing tests +* Added `dataflow!` and `dataflow_with!` methods to match `Future#value!` method +* Better handling of timeout in `Agent` +* Actor Improvements + - Fine-grained implementation using chain of behaviors. Each behavior is responsible for single aspect like: `Termination`, `Pausing`, `Linking`, `Supervising`, etc. Users can create custom Actors easily based on their needs. + - Supervision was added. `RestartingContext` will pause on error waiting on its supervisor to decide what to do next ( options are `:terminate!`, `:resume!`, `:reset!`, `:restart!`). Supervising behavior also supports strategies `:one_for_one` and `:one_for_all`. + - Linking was added to be able to monitor actor's events like: `:terminated`, `:paused`, `:restarted`, etc. + - Dead letter routing added. Rejected envelopes are collected in a configurable actor (default: `Concurrent::Actor.root.ask!(:dead_letter_routing)`) + - Old `Actor` class removed and replaced by new implementation previously called `Actress`. `Actress` was kept as an alias for `Actor` to keep compatibility. + - `Utils::Broadcast` actor which allows Publish–subscribe pattern. +* More executors for managing serialized operations + - `SerializedExecution` mixin module + - `SerializedExecutionDelegator` for serializing *any* executor +* Updated `Async` with serialized execution +* Updated `ImmediateExecutor` and `PerThreadExecutor` with full executor service lifecycle +* Added a `Delay` to root `Actress` initialization +* Minor bug fixes to thread pools +* Refactored many intermittently failing specs +* Removed Java interop warning `executor.rb:148 warning: ambiguous Java methods found, using submit(java.lang.Runnable)` +* Fixed minor bug in `RubyCachedThreadPool` overflow policy +* Updated tests to use [RSpec 3.0](http://myronmars.to/n/dev-blog/2014/05/notable-changes-in-rspec-3) +* Removed deprecated `Actor` class +* Better support for Rubinius + +## Release v0.6.1 (14 June 2014) + +* Many improvements to `Concurrent::Actress` +* Bug fixes to `Concurrent::RubyThreadPoolExecutor` +* Fixed several brittle tests +* Moved documentation to http://ruby-concurrency.github.io/concurrent-ruby/frames.html + +## Release v0.6.0 (25 May 2014) + +* Added `Concurrent::Observable` to encapsulate our thread safe observer sets +* Improvements to new `Channel` +* Major improvements to `CachedThreadPool` and `FixedThreadPool` +* Added `SingleThreadExecutor` +* Added `Current::timer` function +* Added `TimerSet` executor +* Added `AtomicBoolean` +* `ScheduledTask` refactoring +* Pure Ruby and JRuby-optimized `PriorityQueue` classes +* Updated `Agent` behavior to more closely match Clojure +* Observer sets support block callbacks to the `add_observer` method +* New algorithm for thread creation in `RubyThreadPoolExecutor` +* Minor API updates to `Event` +* Rewritten `TimerTask` now an `Executor` instead of a `Runnable` +* Fixed many brittle specs +* Renamed `FixedThreadPool` and `CachedThreadPool` to `RubyFixedThreadPool` and `RubyCachedThreadPool` +* Created JRuby optimized `JavaFixedThreadPool` and `JavaCachedThreadPool` +* Consolidated fixed thread pool tests into `spec/concurrent/fixed_thread_pool_shared.rb` and `spec/concurrent/cached_thread_pool_shared.rb` +* `FixedThreadPool` now subclasses `RubyFixedThreadPool` or `JavaFixedThreadPool` as appropriate +* `CachedThreadPool` now subclasses `RubyCachedThreadPool` or `JavaCachedThreadPool` as appropriate +* New `Delay` class +* `Concurrent::processor_count` helper function +* New `Async` module +* Renamed `NullThreadPool` to `PerThreadExecutor` +* Deprecated `Channel` (we are planning a new implementation based on [Go](http://golangtutorials.blogspot.com/2011/06/channels-in-go.html)) +* Added gem-level [configuration](http://robots.thoughtbot.com/mygem-configure-block) +* Deprecated `$GLOBAL_THREAD_POOL` in lieu of gem-level configuration +* Removed support for Ruby [1.9.2](https://www.ruby-lang.org/en/news/2013/12/17/maintenance-of-1-8-7-and-1-9-2/) +* New `RubyThreadPoolExecutor` and `JavaThreadPoolExecutor` classes +* All thread pools now extend the appropriate thread pool executor classes +* All thread pools now support `:overflow_policy` (based on Java's [reject policies](http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/ThreadPoolExecutor.html)) +* Deprecated `UsesGlobalThreadPool` in lieu of explicit `:executor` option (dependency injection) on `Future`, `Promise`, and `Agent` +* Added `Concurrent::dataflow_with(executor, *inputs)` method to support executor dependency injection for dataflow +* Software transactional memory with `TVar` and `Concurrent::atomically` +* First implementation of [new, high-performance](https://github.com/ruby-concurrency/concurrent-ruby/pull/49) `Channel` +* `Actor` is deprecated in favor of new experimental actor implementation [#73](https://github.com/ruby-concurrency/concurrent-ruby/pull/73). To avoid namespace collision it is living in `Actress` namespace until `Actor` is removed in next release. + +## Release v0.5.0 + +This is the most significant release of this gem since its inception. This release includes many improvements and optimizations. It also includes several bug fixes. The major areas of focus for this release were: + +* Stability improvements on Ruby versions with thread-level parallelism ([JRuby](http://jruby.org/) and [Rubinius](http://rubini.us/)) +* Creation of new low-level concurrency abstractions +* Internal refactoring to use the new low-level abstractions + +Most of these updates had no effect on the gem API. There are a few notable exceptions which were unavoidable. Please read the [release notes](API-Updates-in-v0.5.0) for more information. + +Specific changes include: + +* New class `IVar` +* New class `MVar` +* New class `ThreadLocalVar` +* New class `AtomicFixnum` +* New class method `dataflow` +* New class `Condition` +* New class `CountDownLatch` +* New class `DependencyCounter` +* New class `SafeTaskExecutor` +* New class `CopyOnNotifyObserverSet` +* New class `CopyOnWriteObserverSet` +* `Future` updated with `execute` API +* `ScheduledTask` updated with `execute` API +* New `Promise` API +* `Future` now extends `IVar` +* `Postable#post?` now returns an `IVar` +* Thread safety fixes to `Dereferenceable` +* Thread safety fixes to `Obligation` +* Thread safety fixes to `Supervisor` +* Thread safety fixes to `Event` +* Various other thread safety (race condition) fixes +* Refactored brittle tests +* Implemented pending tests +* Added JRuby and Rubinius as Travis CI build targets +* Added [CodeClimate](https://codeclimate.com/) code review +* Improved YARD documentation diff --git a/vendor/bundle/gems/concurrent-ruby-1.1.5/Gemfile b/vendor/bundle/gems/concurrent-ruby-1.1.5/Gemfile new file mode 100644 index 0000000..fe99e8b --- /dev/null +++ b/vendor/bundle/gems/concurrent-ruby-1.1.5/Gemfile @@ -0,0 +1,41 @@ +source 'https://rubygems.org' + +require File.join(File.dirname(__FILE__), 'lib/concurrent/version') +require File.join(File.dirname(__FILE__ ), 'lib-edge/concurrent/edge/version') + +no_path = ENV['NO_PATH'] +options = no_path ? {} : { path: '.' } + +gem 'concurrent-ruby', Concurrent::VERSION, options +gem 'concurrent-ruby-edge', Concurrent::EDGE_VERSION, options +gem 'concurrent-ruby-ext', Concurrent::VERSION, options.merge(platform: :mri) + +group :development do + gem 'rake', '~> 12.0' + gem 'rake-compiler', '~> 1.0', '>= 1.0.7' + gem 'rake-compiler-dock', '~> 0.7.0' + gem 'pry', '~> 0.11', platforms: :mri +end + +group :documentation, optional: true do + gem 'yard', '~> 0.9.0', require: false + gem 'redcarpet', '~> 3.0', platforms: :mri # understands github markdown + gem 'md-ruby-eval', '~> 0.6' +end + +group :testing do + gem 'rspec', '~> 3.7' + gem 'timecop', '~> 0.7.4' + gem 'sigdump', require: false +end + +# made opt-in since it will not install on jruby 1.7 +group :coverage, optional: !ENV['COVERAGE'] do + gem 'simplecov', '~> 0.16.0', require: false + gem 'coveralls', '~> 0.8.2', require: false +end + +group :benchmarks, optional: true do + gem 'benchmark-ips', '~> 2.7' + gem 'bench9000' +end diff --git a/vendor/bundle/gems/concurrent-ruby-1.1.5/LICENSE.md b/vendor/bundle/gems/concurrent-ruby-1.1.5/LICENSE.md new file mode 100644 index 0000000..eb46190 --- /dev/null +++ b/vendor/bundle/gems/concurrent-ruby-1.1.5/LICENSE.md @@ -0,0 +1,23 @@ +``` +Copyright (c) Jerry D'Antonio -- released under the MIT license. + +http://www.opensource.org/licenses/mit-license.php + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +``` diff --git a/vendor/bundle/gems/concurrent-ruby-1.1.5/README.md b/vendor/bundle/gems/concurrent-ruby-1.1.5/README.md new file mode 100644 index 0000000..17e06a2 --- /dev/null +++ b/vendor/bundle/gems/concurrent-ruby-1.1.5/README.md @@ -0,0 +1,381 @@ +# Concurrent Ruby + +[![Gem Version](https://badge.fury.io/rb/concurrent-ruby.svg)](http://badge.fury.io/rb/concurrent-ruby) +[![Build Status](https://travis-ci.org/ruby-concurrency/concurrent-ruby.svg?branch=master)](https://travis-ci.org/ruby-concurrency/concurrent-ruby) +[![Build status](https://ci.appveyor.com/api/projects/status/iq8aboyuu3etad4w?svg=true)](https://ci.appveyor.com/project/rubyconcurrency/concurrent-ruby) +[![License](https://img.shields.io/badge/license-MIT-green.svg)](http://opensource.org/licenses/MIT) +[![Gitter chat](https://img.shields.io/badge/IRC%20(gitter)-devs%20%26%20users-brightgreen.svg)](https://gitter.im/ruby-concurrency/concurrent-ruby) + +Modern concurrency tools for Ruby. Inspired by +[Erlang](http://www.erlang.org/doc/reference_manual/processes.html), +[Clojure](http://clojure.org/concurrent_programming), +[Scala](http://akka.io/), +[Haskell](http://www.haskell.org/haskellwiki/Applications_and_libraries/Concurrency_and_parallelism#Concurrent_Haskell), +[F#](http://blogs.msdn.com/b/dsyme/archive/2010/02/15/async-and-parallel-design-patterns-in-f-part-3-agents.aspx), +[C#](http://msdn.microsoft.com/en-us/library/vstudio/hh191443.aspx), +[Java](http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/package-summary.html), +and classic concurrency patterns. + + + +The design goals of this gem are: + +* Be an 'unopinionated' toolbox that provides useful utilities without debating which is better + or why +* Remain free of external gem dependencies +* Stay true to the spirit of the languages providing inspiration +* But implement in a way that makes sense for Ruby +* Keep the semantics as idiomatic Ruby as possible +* Support features that make sense in Ruby +* Exclude features that don't make sense in Ruby +* Be small, lean, and loosely coupled +* Thread-safety +* Backward compatibility + +## Contributing + +**This gem depends on +[contributions](https://github.com/ruby-concurrency/concurrent-ruby/graphs/contributors) and we +appreciate your help. Would you like to contribute? Great! Have a look at +[issues with `looking-for-contributor` label](https://github.com/ruby-concurrency/concurrent-ruby/issues?q=is%3Aissue+is%3Aopen+label%3Alooking-for-contributor).** And if you pick something up let us know on the issue. + +## Thread Safety + +*Concurrent Ruby makes one of the strongest thread safety guarantees of any Ruby concurrency +library, providing consistent behavior and guarantees on all four of the main Ruby interpreters +(MRI/CRuby, JRuby, Rubinius, TruffleRuby).* + +Every abstraction in this library is thread safe. Specific thread safety guarantees are documented +with each abstraction. + +It is critical to remember, however, that Ruby is a language of mutable references. *No* +concurrency library for Ruby can ever prevent the user from making thread safety mistakes (such as +sharing a mutable object between threads and modifying it on both threads) or from creating +deadlocks through incorrect use of locks. All the library can do is provide safe abstractions which +encourage safe practices. Concurrent Ruby provides more safe concurrency abstractions than any +other Ruby library, many of which support the mantra of +["Do not communicate by sharing memory; instead, share memory by communicating"](https://blog.golang.org/share-memory-by-communicating). +Concurrent Ruby is also the only Ruby library which provides a full suite of thread safe and +immutable variable types and data structures. + +We've also initiated discussion to document [memory model](docs-source/synchronization.md) of Ruby which +would provide consistent behaviour and guarantees on all four of the main Ruby interpreters +(MRI/CRuby, JRuby, Rubinius, TruffleRuby). + +## Features & Documentation + +**The primary site for documentation is the automatically generated +[API documentation](http://ruby-concurrency.github.io/concurrent-ruby/index.html) which is up to +date with latest release.** This readme matches the master so may contain new stuff not yet +released. + +We also have a [IRC (gitter)](https://gitter.im/ruby-concurrency/concurrent-ruby). + +### Versioning + +* `concurrent-ruby` uses [Semantic Versioning](http://semver.org/) +* `concurrent-ruby-ext` has always same version as `concurrent-ruby` +* `concurrent-ruby-edge` will always be 0.y.z therefore following + [point 4](http://semver.org/#spec-item-4) applies *"Major version zero + (0.y.z) is for initial development. Anything may change at any time. The + public API should not be considered stable."* However we additionally use + following rules: + * Minor version increment means incompatible changes were made + * Patch version increment means only compatible changes were made + + +#### General-purpose Concurrency Abstractions + +* [Async](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/Async.html): + A mixin module that provides simple asynchronous behavior to a class. Loosely based on Erlang's + [gen_server](http://www.erlang.org/doc/man/gen_server.html). +* [ScheduledTask](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/ScheduledTask.html): + Like a Future scheduled for a specific future time. +* [TimerTask](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/TimerTask.html): + A Thread that periodically wakes up to perform work at regular intervals. +* [Promises](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/Promises.html): + Unified implementation of futures and promises which combines features of previous `Future`, + `Promise`, `IVar`, `Event`, `dataflow`, `Delay`, and (partially) `TimerTask` into a single + framework. It extensively uses the new synchronization layer to make all the features + **non-blocking** and **lock-free**, with the exception of obviously blocking operations like + `#wait`, `#value`. It also offers better performance. + +#### Thread-safe Value Objects, Structures, and Collections + +Collection classes that were originally part of the (deprecated) `thread_safe` gem: + +* [Array](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/Array.html) A thread-safe + subclass of Ruby's standard [Array](http://ruby-doc.org/core-2.2.0/Array.html). +* [Hash](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/Hash.html) A thread-safe + subclass of Ruby's standard [Hash](http://ruby-doc.org/core-2.2.0/Hash.html). +* [Set](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/Set.html) A thread-safe + subclass of Ruby's standard [Set](http://ruby-doc.org/stdlib-2.4.0/libdoc/set/rdoc/Set.html). +* [Map](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/Map.html) A hash-like object + that should have much better performance characteristics, especially under high concurrency, + than `Concurrent::Hash`. +* [Tuple](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/Tuple.html) A fixed size + array with volatile (synchronized, thread safe) getters/setters. + +Value objects inspired by other languages: + +* [Maybe](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/Maybe.html) A thread-safe, + immutable object representing an optional value, based on + [Haskell Data.Maybe](https://hackage.haskell.org/package/base-4.2.0.1/docs/Data-Maybe.html). + +Structure classes derived from Ruby's [Struct](http://ruby-doc.org/core-2.2.0/Struct.html): + +* [ImmutableStruct](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/ImmutableStruct.html) + Immutable struct where values are set at construction and cannot be changed later. +* [MutableStruct](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/MutableStruct.html) + Synchronized, mutable struct where values can be safely changed at any time. +* [SettableStruct](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/SettableStruct.html) + Synchronized, write-once struct where values can be set at most once, either at construction + or any time thereafter. + +Thread-safe variables: + +* [Agent](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/Agent.html): A way to + manage shared, mutable, *asynchronous*, independent state. Based on Clojure's + [Agent](http://clojure.org/agents). +* [Atom](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/Atom.html): A way to manage + shared, mutable, *synchronous*, independent state. Based on Clojure's + [Atom](http://clojure.org/atoms). +* [AtomicBoolean](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/AtomicBoolean.html) + A boolean value that can be updated atomically. +* [AtomicFixnum](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/AtomicFixnum.html) + A numeric value that can be updated atomically. +* [AtomicReference](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/AtomicReference.html) + An object reference that may be updated atomically. +* [Exchanger](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/Exchanger.html) + A synchronization point at which threads can pair and swap elements within pairs. Based on + Java's [Exchanger](http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/Exchanger.html). +* [MVar](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/MVar.html) A synchronized + single element container. Based on Haskell's + [MVar](https://hackage.haskell.org/package/base-4.8.1.0/docs/Control-Concurrent-MVar.html) and + Scala's [MVar](http://docs.typelevel.org/api/scalaz/nightly/index.html#scalaz.concurrent.MVar$). +* [ThreadLocalVar](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/ThreadLocalVar.html) + A variable where the value is different for each thread. +* [TVar](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/TVar.html) A transactional + variable implementing software transactional memory (STM). Based on Clojure's + [Ref](http://clojure.org/refs). + +#### Java-inspired ThreadPools and Other Executors + +* See the [thread pool](http://ruby-concurrency.github.io/concurrent-ruby/master/file.thread_pools.html) + overview, which also contains a list of other Executors available. + +#### Thread Synchronization Classes and Algorithms + +* [CountDownLatch](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/CountDownLatch.html) + A synchronization object that allows one thread to wait on multiple other threads. +* [CyclicBarrier](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/CyclicBarrier.html) + A synchronization aid that allows a set of threads to all wait for each other to reach a common barrier point. +* [Event](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/Event.html) Old school + kernel-style event. +* [ReadWriteLock](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/ReadWriteLock.html) + A lock that supports multiple readers but only one writer. +* [ReentrantReadWriteLock](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/ReentrantReadWriteLock.html) + A read/write lock with reentrant and upgrade features. +* [Semaphore](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/Semaphore.html) + A counting-based locking mechanism that uses permits. +* [AtomicMarkableReference](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/AtomicMarkableReference.html) + +#### Deprecated + +Deprecated features are still available and bugs are being fixed, but new features will not be added. + +* ~~[Future](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/Future.html): + An asynchronous operation that produces a value.~~ Replaced by + [Promises](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/Promises.html). + * ~~[.dataflow](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent.html#dataflow-class_method): + Built on Futures, Dataflow allows you to create a task that will be scheduled when all of + its data dependencies are available.~~ Replaced by + [Promises](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/Promises.html). +* ~~[Promise](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/Promise.html): Similar + to Futures, with more features.~~ Replaced by + [Promises](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/Promises.html). +* ~~[Delay](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/Delay.html) Lazy evaluation + of a block yielding an immutable result. Based on Clojure's + [delay](https://clojuredocs.org/clojure.core/delay).~~ Replaced by + [Promises](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/Promises.html). +* ~~[IVar](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/IVar.html) Similar to a + "future" but can be manually assigned once, after which it becomes immutable.~~ Replaced by + [Promises](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/Promises.html). + +### Edge Features + +These are available in the `concurrent-ruby-edge` companion gem. + +These features are under active development and may change frequently. They are expected not to +keep backward compatibility (there may also lack tests and documentation). Semantic versions will +be obeyed though. Features developed in `concurrent-ruby-edge` are expected to move to +`concurrent-ruby` when final. + +* [Actor](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/Actor.html): Implements + the Actor Model, where concurrent actors exchange messages. + *Status: Partial documentation and tests; depends on new future/promise framework; stability is good.* +* [Channel](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/Channel.html): + Communicating Sequential Processes ([CSP](https://en.wikipedia.org/wiki/Communicating_sequential_processes)). + Functionally equivalent to Go [channels](https://tour.golang.org/concurrency/2) with additional + inspiration from Clojure [core.async](https://clojure.github.io/core.async/). + *Status: Partial documentation and tests.* +* [LazyRegister](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/LazyRegister.html) +* [LockFreeLinkedSet](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/Edge/LockFreeLinkedSet.html) + *Status: will be moved to core soon.* +* [LockFreeStack](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/LockFreeStack.html) + *Status: missing documentation and tests.* +* [Promises::Channel](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/Promises/Channel.html) + A first in first out channel that accepts messages with push family of methods and returns + messages with pop family of methods. + Pop and push operations can be represented as futures, see `#pop_op` and `#push_op`. + The capacity of the channel can be limited to support back pressure, use capacity option in `#initialize`. + `#pop` method blocks ans `#pop_op` returns pending future if there is no message in the channel. + If the capacity is limited the `#push` method blocks and `#push_op` returns pending future. +* [Cancellation](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/Cancellation.html) + The Cancellation abstraction provides cooperative cancellation. + + The standard methods `Thread#raise` of `Thread#kill` available in Ruby + are very dangerous (see linked the blog posts bellow). + Therefore concurrent-ruby provides an alternative. + + * + * + * + + It provides an object which represents a task which can be executed, + the task has to get the reference to the object and periodically cooperatively check that it is not cancelled. + Good practices to make tasks cancellable: + * check cancellation every cycle of a loop which does significant work, + * do all blocking actions in a loop with a timeout then on timeout check cancellation + and if ok block again with the timeout +* [Throttle](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/Throttle.html) + A tool managing concurrency level of tasks. +* [ErlangActor](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/ErlangActor.html) + Actor implementation which precisely matches Erlang actor behaviour. + Requires at least Ruby 2.1 otherwise it's not loaded. + +## Supported Ruby versions + +* MRI 2.0 and above +* JRuby 9000 +* TruffleRuby are supported. +* Any Ruby interpreter that is compliant with Ruby 2.0 or newer. + +Actually we still support mri 1.9.3 and jruby 1.7.27 but we are looking at ways how to drop the support. +Java 8 is preferred for JRuby but every Java version on which JRuby 9000 runs is supported. + +The legacy support for Rubinius is kept but it is no longer maintained, if you would like to help +please respond to [#739](https://github.com/ruby-concurrency/concurrent-ruby/issues/739). + +## Usage + +Everything within this gem can be loaded simply by requiring it: + +```ruby +require 'concurrent' +``` + +*Requiring only specific abstractions from Concurrent Ruby is not yet supported.* + +To use the tools in the Edge gem it must be required separately: + +```ruby +require 'concurrent-edge' +``` + +If the library does not behave as expected, `Concurrent.use_stdlib_logger(Logger::DEBUG)` could +help to reveal the problem. + +## Installation + +```shell +gem install concurrent-ruby +``` + +or add the following line to Gemfile: + +```ruby +gem 'concurrent-ruby', require: 'concurrent' +``` + +and run `bundle install` from your shell. + +### Edge Gem Installation + +The Edge gem must be installed separately from the core gem: + +```shell +gem install concurrent-ruby-edge +``` + +or add the following line to Gemfile: + +```ruby +gem 'concurrent-ruby-edge', require: 'concurrent-edge' +``` + +and run `bundle install` from your shell. + + +### C Extensions for MRI + +Potential performance improvements may be achieved under MRI by installing optional C extensions. +To minimise installation errors the C extensions are available in the `concurrent-ruby-ext` +extension gem. `concurrent-ruby` and `concurrent-ruby-ext` are always released together with same +version. Simply install the extension gem too: + +```ruby +gem install concurrent-ruby-ext +``` + +or add the following line to Gemfile: + +```ruby +gem 'concurrent-ruby-ext' +``` + +and run `bundle install` from your shell. + +In code it is only necessary to + +```ruby +require 'concurrent' +``` + +The `concurrent-ruby` gem will automatically detect the presence of the `concurrent-ruby-ext` gem +and load the appropriate C extensions. + +#### Note For gem developers + +No gems should depend on `concurrent-ruby-ext`. Doing so will force C extensions on your users. The +best practice is to depend on `concurrent-ruby` and let users to decide if they want C extensions. + +## Maintainers + +* [Petr Chalupa](https://github.com/pitr-ch) (lead maintainer, point-of-contact) +* [Jerry D'Antonio](https://github.com/jdantonio) (creator) +* [Chris Seaton](https://github.com/chrisseaton) + +### Special Thanks to + +* [Brian Durand](https://github.com/bdurand) for the `ref` gem +* [Charles Oliver Nutter](https://github.com/headius) for the `atomic` and `thread_safe` gems +* [thedarkone](https://github.com/thedarkone) for the `thread_safe` gem + +and to the past maintainers + +* [Michele Della Torre](https://github.com/mighe) +* [Paweł Obrok](https://github.com/obrok) +* [Lucas Allan](https://github.com/lucasallan) + +and to [Ruby Association](https://www.ruby.or.jp/en/) for sponsoring a project +["Enhancing Ruby’s concurrency tooling"](https://www.ruby.or.jp/en/news/20181106) in 2018. + +## License and Copyright + +*Concurrent Ruby* is free software released under the +[MIT License](http://www.opensource.org/licenses/MIT). + +The *Concurrent Ruby* [logo](https://raw.githubusercontent.com/ruby-concurrency/concurrent-ruby/master/docs-source/logo/concurrent-ruby-logo-300x300.png) was +designed by [David Jones](https://twitter.com/zombyboy). It is Copyright © 2014 +[Jerry D'Antonio](https://twitter.com/jerrydantonio). All Rights Reserved. diff --git a/vendor/bundle/gems/concurrent-ruby-1.1.5/Rakefile b/vendor/bundle/gems/concurrent-ruby-1.1.5/Rakefile new file mode 100644 index 0000000..c648d38 --- /dev/null +++ b/vendor/bundle/gems/concurrent-ruby-1.1.5/Rakefile @@ -0,0 +1,327 @@ +require_relative 'lib/concurrent/version' +require_relative 'lib/concurrent/utility/engine' + +if Concurrent.ruby_version :<, 2, 0, 0 + # @!visibility private + module Kernel + def __dir__ + File.dirname __FILE__ + end + end +end + +core_gemspec = Gem::Specification.load File.join(__dir__, 'concurrent-ruby.gemspec') +ext_gemspec = Gem::Specification.load File.join(__dir__, 'concurrent-ruby-ext.gemspec') +edge_gemspec = Gem::Specification.load File.join(__dir__, 'concurrent-ruby-edge.gemspec') + +require 'rake/javaextensiontask' + +Rake::JavaExtensionTask.new('concurrent_ruby', core_gemspec) do |ext| + ext.ext_dir = 'ext/concurrent-ruby' + ext.lib_dir = 'lib/concurrent' +end + +unless Concurrent.on_jruby? + require 'rake/extensiontask' + + Rake::ExtensionTask.new('concurrent_ruby_ext', ext_gemspec) do |ext| + ext.ext_dir = 'ext/concurrent-ruby-ext' + ext.lib_dir = 'lib/concurrent' + ext.source_pattern = '*.{c,h}' + + ext.cross_compile = true + ext.cross_platform = ['x86-mingw32', 'x64-mingw32'] + end +end + +require 'rake_compiler_dock' +namespace :repackage do + desc '* with Windows fat distributions' + task :all do + Dir.chdir(__dir__) do + # store gems in vendor cache for docker + sh 'bundle package' + + # build only the jar file not the whole gem for java platform, the jar is part the concurrent-ruby-x.y.z.gem + Rake::Task['lib/concurrent/concurrent_ruby.jar'].invoke + + # build all gem files + RakeCompilerDock.sh 'bundle install --local && bundle exec rake cross native package --trace' + end + end +end + +require 'rubygems' +require 'rubygems/package_task' + +Gem::PackageTask.new(core_gemspec) {} if core_gemspec +Gem::PackageTask.new(ext_gemspec) {} if ext_gemspec && !Concurrent.on_jruby? +Gem::PackageTask.new(edge_gemspec) {} if edge_gemspec + +CLEAN.include('lib/concurrent/2.*', 'lib/concurrent/*.jar') + +begin + require 'rspec' + require 'rspec/core/rake_task' + + RSpec::Core::RakeTask.new(:spec) + + namespace :spec do + desc '* Configured for ci' + RSpec::Core::RakeTask.new(:ci) do |t| + options = %w[ --color + --backtrace + --order defined + --format documentation + --tag ~notravis ] + t.rspec_opts = [*options].join(' ') + end + + desc '* test packaged and installed gems instead of local files' + task :installed do + Dir.chdir(__dir__) do + sh "gem install pkg/concurrent-ruby-#{Concurrent::VERSION}.gem" + sh "gem install pkg/concurrent-ruby-ext-#{Concurrent::VERSION}.gem" if Concurrent.on_cruby? + sh "gem install pkg/concurrent-ruby-edge-#{Concurrent::EDGE_VERSION}.gem" + ENV['NO_PATH'] = 'true' + sh 'bundle update' + sh 'bundle exec rake spec:ci' + end + end + end + + desc 'executed in CI' + task :ci => [:compile, 'spec:ci'] + + task :default => [:clobber, :compile, :spec] +rescue LoadError => e + puts 'RSpec is not installed, skipping test task definitions: ' + e.message +end + +current_yard_version_name = Concurrent::VERSION + +begin + require 'yard' + require 'md_ruby_eval' + require_relative 'support/yard_full_types' + + common_yard_options = ['--no-yardopts', + '--no-document', + '--no-private', + '--embed-mixins', + '--markup', 'markdown', + '--title', 'Concurrent Ruby', + '--template', 'default', + '--template-path', 'yard-template', + '--default-return', 'undocumented'] + + desc 'Generate YARD Documentation (signpost, master)' + task :yard => ['yard:signpost', 'yard:master'] + + namespace :yard do + + desc '* eval markdown files' + task :eval_md do + Dir.chdir File.join(__dir__, 'docs-source') do + sh 'bundle exec md-ruby-eval --auto' + end + end + + task :update_readme do + Dir.chdir __dir__ do + content = File.read(File.join('README.md')). + gsub(/\[([\w ]+)\]\(http:\/\/ruby-concurrency\.github\.io\/concurrent-ruby\/master\/.*\)/) do |_| + case $1 + when 'LockFreeLinkedSet' + "{Concurrent::Edge::#{$1} #{$1}}" + when '.dataflow' + '{Concurrent.dataflow Concurrent.dataflow}' + when 'thread pool' + '{file:thread_pools.md thread pool}' + else + "{Concurrent::#{$1} #{$1}}" + end + end + FileUtils.mkpath 'tmp' + File.write 'tmp/README.md', content + end + end + + define_yard_task = -> name do + output_dir = "docs/#{name}" + + removal_name = "remove.#{name}" + task removal_name do + Dir.chdir __dir__ do + FileUtils.rm_rf output_dir + end + end + + desc "* of #{name} into subdir #{name}" + YARD::Rake::YardocTask.new(name) do |yard| + yard.options.push( + '--output-dir', output_dir, + '--main', 'tmp/README.md', + *common_yard_options) + yard.files = ['./lib/**/*.rb', + './lib-edge/**/*.rb', + './ext/concurrent_ruby_ext/**/*.c', + '-', + 'docs-source/thread_pools.md', + 'docs-source/promises.out.md', + 'docs-source/medium-example.out.rb', + 'LICENSE.md', + 'CHANGELOG.md'] + end + Rake::Task[name].prerequisites.push removal_name, 'yard:eval_md', 'yard:update_readme' + end + + define_yard_task.call current_yard_version_name + define_yard_task.call 'master' + + desc "* signpost for versions" + YARD::Rake::YardocTask.new(:signpost) do |yard| + yard.options.push( + '--output-dir', 'docs', + '--main', 'docs-source/signpost.md', + *common_yard_options) + yard.files = ['no-lib'] + end + + define_uptodate_task = -> name do + namespace name do + desc "** ensure that #{name} generated documentation is matching the source code" + task :uptodate do + Dir.chdir(__dir__) do + begin + FileUtils.cp_r 'docs', 'docs-copy', verbose: true + Rake::Task["yard:#{name}"].invoke + sh 'diff -r docs/ docs-copy/' do |ok, res| + unless ok + begin + STDOUT.puts 'Command failed. Continue? (y/n)' + input = STDIN.gets.strip.downcase + end until %w(y n).include?(input) + exit 1 if input == 'n' + end + end + ensure + FileUtils.rm_rf 'docs-copy', verbose: true + end + end + end + end + end + + define_uptodate_task.call current_yard_version_name + define_uptodate_task.call 'master' + end + +rescue LoadError => e + puts 'YARD is not installed, skipping documentation task definitions: ' + e.message +end + +desc 'build, test, and publish the gem' +task :release => ['release:checks', 'release:build', 'release:test', 'release:publish'] + +namespace :release do + # Depends on environment of @pitr-ch + + mri_version = '2.5.1' + jruby_version = 'jruby-9.1.17.1' + + task :checks => "yard:#{current_yard_version_name}:uptodate" do + Dir.chdir(__dir__) do + sh 'test -z "$(git status --porcelain)"' do |ok, res| + unless ok + begin + STDOUT.puts 'Command failed. Continue? (y/n)' + input = STDIN.gets.strip.downcase + end until %w(y n).include?(input) + exit 1 if input == 'n' + end + end + sh 'git fetch' + sh 'test $(git show-ref --verify --hash refs/heads/master) = ' + + '$(git show-ref --verify --hash refs/remotes/origin/master)' do |ok, res| + unless ok + begin + STDOUT.puts 'Command failed. Continue? (y/n)' + input = STDIN.gets.strip.downcase + end until %w(y n).include?(input) + exit 1 if input == 'n' + end + end + end + end + + desc '* build all *.gem files necessary for release' + task :build => [:clobber, 'repackage:all'] + + desc '* test actual installed gems instead of cloned repository on MRI and JRuby' + task :test do + Dir.chdir(__dir__) do + old = ENV['RBENV_VERSION'] + + ENV['RBENV_VERSION'] = mri_version + sh 'rbenv version' + sh 'bundle exec rake spec:installed' + + ENV['RBENV_VERSION'] = jruby_version + sh 'rbenv version' + sh 'bundle exec rake spec:installed' + + puts 'Windows build is untested' + + ENV['RBENV_VERSION'] = old + end + end + + desc '* do all nested steps' + task :publish => ['publish:ask', 'publish:tag', 'publish:rubygems', 'publish:post_steps'] + + namespace :publish do + publish_edge = false + + task :ask do + begin + STDOUT.puts 'Do you want to publish anything? (y/n)' + input = STDIN.gets.strip.downcase + end until %w(y n).include?(input) + exit 1 if input == 'n' + begin + STDOUT.puts 'Do you want to publish edge? (y/n)' + input = STDIN.gets.strip.downcase + end until %w(y n).include?(input) + publish_edge = input == 'y' + end + + desc '** tag HEAD with current version and push to github' + task :tag do + Dir.chdir(__dir__) do + sh "git tag v#{Concurrent::VERSION}" + sh "git push origin v#{Concurrent::VERSION}" + sh "git tag edge-v#{Concurrent::EDGE_VERSION}" if publish_edge + sh "git push origin edge-v#{Concurrent::EDGE_VERSION}" if publish_edge + end + end + + desc '** push all *.gem files to rubygems' + task :rubygems do + Dir.chdir(__dir__) do + sh "gem push pkg/concurrent-ruby-#{Concurrent::VERSION}.gem" + sh "gem push pkg/concurrent-ruby-edge-#{Concurrent::EDGE_VERSION}.gem" if publish_edge + sh "gem push pkg/concurrent-ruby-ext-#{Concurrent::VERSION}.gem" + sh "gem push pkg/concurrent-ruby-ext-#{Concurrent::VERSION}-x64-mingw32.gem" + sh "gem push pkg/concurrent-ruby-ext-#{Concurrent::VERSION}-x86-mingw32.gem" + end + end + + desc '** print post release steps' + task :post_steps do + puts 'Manually: create a release on GitHub with relevant changelog part' + puts 'Manually: send email same as release with relevant changelog part' + puts 'Manually: tweet' + end + end +end diff --git a/vendor/bundle/gems/concurrent-ruby-1.1.5/ext/concurrent-ruby/ConcurrentRubyService.java b/vendor/bundle/gems/concurrent-ruby-1.1.5/ext/concurrent-ruby/ConcurrentRubyService.java new file mode 100644 index 0000000..fb6be96 --- /dev/null +++ b/vendor/bundle/gems/concurrent-ruby-1.1.5/ext/concurrent-ruby/ConcurrentRubyService.java @@ -0,0 +1,17 @@ +import org.jruby.Ruby; +import org.jruby.runtime.load.BasicLibraryService; + +import java.io.IOException; + +public class ConcurrentRubyService implements BasicLibraryService { + + public boolean basicLoad(final Ruby runtime) throws IOException { + new com.concurrent_ruby.ext.AtomicReferenceLibrary().load(runtime, false); + new com.concurrent_ruby.ext.JavaAtomicBooleanLibrary().load(runtime, false); + new com.concurrent_ruby.ext.JavaAtomicFixnumLibrary().load(runtime, false); + new com.concurrent_ruby.ext.JavaSemaphoreLibrary().load(runtime, false); + new com.concurrent_ruby.ext.SynchronizationLibrary().load(runtime, false); + new com.concurrent_ruby.ext.JRubyMapBackendLibrary().load(runtime, false); + return true; + } +} diff --git a/vendor/bundle/gems/concurrent-ruby-1.1.5/ext/concurrent-ruby/com/concurrent_ruby/ext/AtomicReferenceLibrary.java b/vendor/bundle/gems/concurrent-ruby-1.1.5/ext/concurrent-ruby/com/concurrent_ruby/ext/AtomicReferenceLibrary.java new file mode 100644 index 0000000..dfa9e77 --- /dev/null +++ b/vendor/bundle/gems/concurrent-ruby-1.1.5/ext/concurrent-ruby/com/concurrent_ruby/ext/AtomicReferenceLibrary.java @@ -0,0 +1,175 @@ +package com.concurrent_ruby.ext; + +import java.lang.reflect.Field; +import java.io.IOException; +import java.util.concurrent.atomic.AtomicReferenceFieldUpdater; +import org.jruby.Ruby; +import org.jruby.RubyClass; +import org.jruby.RubyModule; +import org.jruby.RubyNumeric; +import org.jruby.RubyObject; +import org.jruby.anno.JRubyClass; +import org.jruby.anno.JRubyMethod; +import org.jruby.runtime.ObjectAllocator; +import org.jruby.runtime.ThreadContext; +import org.jruby.runtime.builtin.IRubyObject; +import org.jruby.runtime.load.Library; + +/** + * This library adds an atomic reference type to JRuby for use in the atomic + * library. We do a native version to avoid the implicit value coercion that + * normally happens through JI. + * + * @author headius + */ +public class AtomicReferenceLibrary implements Library { + public void load(Ruby runtime, boolean wrap) throws IOException { + RubyModule concurrentMod = runtime.defineModule("Concurrent"); + RubyClass atomicCls = concurrentMod.defineClassUnder("JavaAtomicReference", runtime.getObject(), JRUBYREFERENCE_ALLOCATOR); + try { + sun.misc.Unsafe.class.getMethod("getAndSetObject", Object.class); + atomicCls.setAllocator(JRUBYREFERENCE8_ALLOCATOR); + } catch (Exception e) { + // leave it as Java 6/7 version + } + atomicCls.defineAnnotatedMethods(JRubyReference.class); + } + + private static final ObjectAllocator JRUBYREFERENCE_ALLOCATOR = new ObjectAllocator() { + public IRubyObject allocate(Ruby runtime, RubyClass klazz) { + return new JRubyReference(runtime, klazz); + } + }; + + private static final ObjectAllocator JRUBYREFERENCE8_ALLOCATOR = new ObjectAllocator() { + public IRubyObject allocate(Ruby runtime, RubyClass klazz) { + return new JRubyReference8(runtime, klazz); + } + }; + + @JRubyClass(name="JRubyReference", parent="Object") + public static class JRubyReference extends RubyObject { + volatile IRubyObject reference; + + static final sun.misc.Unsafe UNSAFE; + static final long referenceOffset; + + static { + try { + UNSAFE = UnsafeHolder.U; + Class k = JRubyReference.class; + referenceOffset = UNSAFE.objectFieldOffset(k.getDeclaredField("reference")); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + public JRubyReference(Ruby runtime, RubyClass klass) { + super(runtime, klass); + } + + @JRubyMethod + public IRubyObject initialize(ThreadContext context) { + UNSAFE.putObject(this, referenceOffset, context.nil); + return context.nil; + } + + @JRubyMethod + public IRubyObject initialize(ThreadContext context, IRubyObject value) { + UNSAFE.putObject(this, referenceOffset, value); + return context.nil; + } + + @JRubyMethod(name = {"get", "value"}) + public IRubyObject get() { + return reference; + } + + @JRubyMethod(name = {"set", "value="}) + public IRubyObject set(IRubyObject newValue) { + UNSAFE.putObjectVolatile(this, referenceOffset, newValue); + return newValue; + } + + @JRubyMethod(name = {"compare_and_set", "compare_and_swap"}) + public IRubyObject compare_and_set(ThreadContext context, IRubyObject expectedValue, IRubyObject newValue) { + Ruby runtime = context.runtime; + + if (expectedValue instanceof RubyNumeric) { + // numerics are not always idempotent in Ruby, so we need to do slower logic + return compareAndSetNumeric(context, expectedValue, newValue); + } + + return runtime.newBoolean(UNSAFE.compareAndSwapObject(this, referenceOffset, expectedValue, newValue)); + } + + @JRubyMethod(name = {"get_and_set", "swap"}) + public IRubyObject get_and_set(ThreadContext context, IRubyObject newValue) { + // less-efficient version for Java 6 and 7 + while (true) { + IRubyObject oldValue = get(); + if (UNSAFE.compareAndSwapObject(this, referenceOffset, oldValue, newValue)) { + return oldValue; + } + } + } + + private IRubyObject compareAndSetNumeric(ThreadContext context, IRubyObject expectedValue, IRubyObject newValue) { + Ruby runtime = context.runtime; + + // loop until: + // * reference CAS would succeed for same-valued objects + // * current and expected have different values as determined by #equals + while (true) { + IRubyObject current = reference; + + if (!(current instanceof RubyNumeric)) { + // old value is not numeric, CAS fails + return runtime.getFalse(); + } + + RubyNumeric currentNumber = (RubyNumeric)current; + if (!currentNumber.equals(expectedValue)) { + // current number does not equal expected, fail CAS + return runtime.getFalse(); + } + + // check that current has not changed, or else allow loop to repeat + boolean success = UNSAFE.compareAndSwapObject(this, referenceOffset, current, newValue); + if (success) { + // value is same and did not change in interim...success + return runtime.getTrue(); + } + } + } + } + + private static final class UnsafeHolder { + private UnsafeHolder(){} + + public static final sun.misc.Unsafe U = loadUnsafe(); + + private static sun.misc.Unsafe loadUnsafe() { + try { + Class unsafeClass = Class.forName("sun.misc.Unsafe"); + Field f = unsafeClass.getDeclaredField("theUnsafe"); + f.setAccessible(true); + return (sun.misc.Unsafe) f.get(null); + } catch (Exception e) { + return null; + } + } + } + + public static class JRubyReference8 extends JRubyReference { + public JRubyReference8(Ruby runtime, RubyClass klass) { + super(runtime, klass); + } + + @Override + public IRubyObject get_and_set(ThreadContext context, IRubyObject newValue) { + // efficient version for Java 8 + return (IRubyObject)UNSAFE.getAndSetObject(this, referenceOffset, newValue); + } + } +} diff --git a/vendor/bundle/gems/concurrent-ruby-1.1.5/ext/concurrent-ruby/com/concurrent_ruby/ext/JRubyMapBackendLibrary.java b/vendor/bundle/gems/concurrent-ruby-1.1.5/ext/concurrent-ruby/com/concurrent_ruby/ext/JRubyMapBackendLibrary.java new file mode 100644 index 0000000..a09f916 --- /dev/null +++ b/vendor/bundle/gems/concurrent-ruby-1.1.5/ext/concurrent-ruby/com/concurrent_ruby/ext/JRubyMapBackendLibrary.java @@ -0,0 +1,248 @@ +package com.concurrent_ruby.ext; + +import org.jruby.*; +import org.jruby.anno.JRubyClass; +import org.jruby.anno.JRubyMethod; +import com.concurrent_ruby.ext.jsr166e.ConcurrentHashMap; +import com.concurrent_ruby.ext.jsr166e.ConcurrentHashMapV8; +import com.concurrent_ruby.ext.jsr166e.nounsafe.*; +import org.jruby.runtime.Block; +import org.jruby.runtime.ObjectAllocator; +import org.jruby.runtime.ThreadContext; +import org.jruby.runtime.builtin.IRubyObject; +import org.jruby.runtime.load.Library; + +import java.io.IOException; +import java.util.Map; + +import static org.jruby.runtime.Visibility.PRIVATE; + +/** + * Native Java implementation to avoid the JI overhead. + * + * @author thedarkone + */ +public class JRubyMapBackendLibrary implements Library { + public void load(Ruby runtime, boolean wrap) throws IOException { + + RubyModule concurrentMod = runtime.defineModule("Concurrent"); + RubyModule thread_safeMod = concurrentMod.defineModuleUnder("Collection"); + RubyClass jrubyRefClass = thread_safeMod.defineClassUnder("JRubyMapBackend", runtime.getObject(), BACKEND_ALLOCATOR); + jrubyRefClass.setAllocator(BACKEND_ALLOCATOR); + jrubyRefClass.defineAnnotatedMethods(JRubyMapBackend.class); + } + + private static final ObjectAllocator BACKEND_ALLOCATOR = new ObjectAllocator() { + public IRubyObject allocate(Ruby runtime, RubyClass klazz) { + return new JRubyMapBackend(runtime, klazz); + } + }; + + @JRubyClass(name="JRubyMapBackend", parent="Object") + public static class JRubyMapBackend extends RubyObject { + // Defaults used by the CHM + static final int DEFAULT_INITIAL_CAPACITY = 16; + static final float DEFAULT_LOAD_FACTOR = 0.75f; + + public static final boolean CAN_USE_UNSAFE_CHM = canUseUnsafeCHM(); + + private ConcurrentHashMap map; + + private static ConcurrentHashMap newCHM(int initialCapacity, float loadFactor) { + if (CAN_USE_UNSAFE_CHM) { + return new ConcurrentHashMapV8(initialCapacity, loadFactor); + } else { + return new com.concurrent_ruby.ext.jsr166e.nounsafe.ConcurrentHashMapV8(initialCapacity, loadFactor); + } + } + + private static ConcurrentHashMap newCHM() { + return newCHM(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR); + } + + private static boolean canUseUnsafeCHM() { + try { + new com.concurrent_ruby.ext.jsr166e.ConcurrentHashMapV8(); // force class load and initialization + return true; + } catch (Throwable t) { // ensuring we really do catch everything + // Doug's Unsafe setup errors always have this "Could not ini.." message + if (isCausedBySecurityException(t)) { + return false; + } + throw (t instanceof RuntimeException ? (RuntimeException) t : new RuntimeException(t)); + } + } + + private static boolean isCausedBySecurityException(Throwable t) { + while (t != null) { + if ((t.getMessage() != null && t.getMessage().contains("Could not initialize intrinsics")) || t instanceof SecurityException) { + return true; + } + t = t.getCause(); + } + return false; + } + + public JRubyMapBackend(Ruby runtime, RubyClass klass) { + super(runtime, klass); + } + + @JRubyMethod + public IRubyObject initialize(ThreadContext context) { + map = newCHM(); + return context.getRuntime().getNil(); + } + + @JRubyMethod + public IRubyObject initialize(ThreadContext context, IRubyObject options) { + map = toCHM(context, options); + return context.getRuntime().getNil(); + } + + private ConcurrentHashMap toCHM(ThreadContext context, IRubyObject options) { + Ruby runtime = context.getRuntime(); + if (!options.isNil() && options.respondsTo("[]")) { + IRubyObject rInitialCapacity = options.callMethod(context, "[]", runtime.newSymbol("initial_capacity")); + IRubyObject rLoadFactor = options.callMethod(context, "[]", runtime.newSymbol("load_factor")); + int initialCapacity = !rInitialCapacity.isNil() ? RubyNumeric.num2int(rInitialCapacity.convertToInteger()) : DEFAULT_INITIAL_CAPACITY; + float loadFactor = !rLoadFactor.isNil() ? (float)RubyNumeric.num2dbl(rLoadFactor.convertToFloat()) : DEFAULT_LOAD_FACTOR; + return newCHM(initialCapacity, loadFactor); + } else { + return newCHM(); + } + } + + @JRubyMethod(name = "[]", required = 1) + public IRubyObject op_aref(ThreadContext context, IRubyObject key) { + IRubyObject value; + return ((value = map.get(key)) == null) ? context.getRuntime().getNil() : value; + } + + @JRubyMethod(name = {"[]="}, required = 2) + public IRubyObject op_aset(IRubyObject key, IRubyObject value) { + map.put(key, value); + return value; + } + + @JRubyMethod + public IRubyObject put_if_absent(IRubyObject key, IRubyObject value) { + IRubyObject result = map.putIfAbsent(key, value); + return result == null ? getRuntime().getNil() : result; + } + + @JRubyMethod + public IRubyObject compute_if_absent(final ThreadContext context, final IRubyObject key, final Block block) { + return map.computeIfAbsent(key, new ConcurrentHashMap.Fun() { + @Override + public IRubyObject apply(IRubyObject key) { + return block.yieldSpecific(context); + } + }); + } + + @JRubyMethod + public IRubyObject compute_if_present(final ThreadContext context, final IRubyObject key, final Block block) { + IRubyObject result = map.computeIfPresent(key, new ConcurrentHashMap.BiFun() { + @Override + public IRubyObject apply(IRubyObject key, IRubyObject oldValue) { + IRubyObject result = block.yieldSpecific(context, oldValue == null ? context.getRuntime().getNil() : oldValue); + return result.isNil() ? null : result; + } + }); + return result == null ? context.getRuntime().getNil() : result; + } + + @JRubyMethod + public IRubyObject compute(final ThreadContext context, final IRubyObject key, final Block block) { + IRubyObject result = map.compute(key, new ConcurrentHashMap.BiFun() { + @Override + public IRubyObject apply(IRubyObject key, IRubyObject oldValue) { + IRubyObject result = block.yieldSpecific(context, oldValue == null ? context.getRuntime().getNil() : oldValue); + return result.isNil() ? null : result; + } + }); + return result == null ? context.getRuntime().getNil() : result; + } + + @JRubyMethod + public IRubyObject merge_pair(final ThreadContext context, final IRubyObject key, final IRubyObject value, final Block block) { + IRubyObject result = map.merge(key, value, new ConcurrentHashMap.BiFun() { + @Override + public IRubyObject apply(IRubyObject oldValue, IRubyObject newValue) { + IRubyObject result = block.yieldSpecific(context, oldValue == null ? context.getRuntime().getNil() : oldValue); + return result.isNil() ? null : result; + } + }); + return result == null ? context.getRuntime().getNil() : result; + } + + @JRubyMethod + public RubyBoolean replace_pair(IRubyObject key, IRubyObject oldValue, IRubyObject newValue) { + return getRuntime().newBoolean(map.replace(key, oldValue, newValue)); + } + + @JRubyMethod(name = "key?", required = 1) + public RubyBoolean has_key_p(IRubyObject key) { + return map.containsKey(key) ? getRuntime().getTrue() : getRuntime().getFalse(); + } + + @JRubyMethod + public IRubyObject key(IRubyObject value) { + final IRubyObject key = map.findKey(value); + return key == null ? getRuntime().getNil() : key; + } + + @JRubyMethod + public IRubyObject replace_if_exists(IRubyObject key, IRubyObject value) { + IRubyObject result = map.replace(key, value); + return result == null ? getRuntime().getNil() : result; + } + + @JRubyMethod + public IRubyObject get_and_set(IRubyObject key, IRubyObject value) { + IRubyObject result = map.put(key, value); + return result == null ? getRuntime().getNil() : result; + } + + @JRubyMethod + public IRubyObject delete(IRubyObject key) { + IRubyObject result = map.remove(key); + return result == null ? getRuntime().getNil() : result; + } + + @JRubyMethod + public RubyBoolean delete_pair(IRubyObject key, IRubyObject value) { + return getRuntime().newBoolean(map.remove(key, value)); + } + + @JRubyMethod + public IRubyObject clear() { + map.clear(); + return this; + } + + @JRubyMethod + public IRubyObject each_pair(ThreadContext context, Block block) { + for (Map.Entry entry : map.entrySet()) { + block.yieldSpecific(context, entry.getKey(), entry.getValue()); + } + return this; + } + + @JRubyMethod + public RubyFixnum size(ThreadContext context) { + return context.getRuntime().newFixnum(map.size()); + } + + @JRubyMethod + public IRubyObject get_or_default(IRubyObject key, IRubyObject defaultValue) { + return map.getValueOrDefault(key, defaultValue); + } + + @JRubyMethod(visibility = PRIVATE) + public JRubyMapBackend initialize_copy(ThreadContext context, IRubyObject other) { + map = newCHM(); + return this; + } + } +} diff --git a/vendor/bundle/gems/concurrent-ruby-1.1.5/ext/concurrent-ruby/com/concurrent_ruby/ext/JavaAtomicBooleanLibrary.java b/vendor/bundle/gems/concurrent-ruby-1.1.5/ext/concurrent-ruby/com/concurrent_ruby/ext/JavaAtomicBooleanLibrary.java new file mode 100644 index 0000000..b566076 --- /dev/null +++ b/vendor/bundle/gems/concurrent-ruby-1.1.5/ext/concurrent-ruby/com/concurrent_ruby/ext/JavaAtomicBooleanLibrary.java @@ -0,0 +1,93 @@ +package com.concurrent_ruby.ext; + +import org.jruby.Ruby; +import org.jruby.RubyBoolean; +import org.jruby.RubyClass; +import org.jruby.RubyModule; +import org.jruby.RubyNil; +import org.jruby.RubyObject; +import org.jruby.anno.JRubyClass; +import org.jruby.anno.JRubyMethod; +import org.jruby.runtime.ObjectAllocator; +import org.jruby.runtime.ThreadContext; +import org.jruby.runtime.builtin.IRubyObject; +import org.jruby.runtime.load.Library; + +import java.io.IOException; +import java.util.concurrent.atomic.AtomicBoolean; + +public class JavaAtomicBooleanLibrary implements Library { + + public void load(Ruby runtime, boolean wrap) throws IOException { + RubyModule concurrentMod = runtime.defineModule("Concurrent"); + RubyClass atomicCls = concurrentMod.defineClassUnder("JavaAtomicBoolean", runtime.getObject(), JRUBYREFERENCE_ALLOCATOR); + atomicCls.defineAnnotatedMethods(JavaAtomicBoolean.class); + } + + private static final ObjectAllocator JRUBYREFERENCE_ALLOCATOR = new ObjectAllocator() { + public IRubyObject allocate(Ruby runtime, RubyClass klazz) { + return new JavaAtomicBoolean(runtime, klazz); + } + }; + + @JRubyClass(name = "JavaAtomicBoolean", parent = "Object") + public static class JavaAtomicBoolean extends RubyObject { + + private AtomicBoolean atomicBoolean; + + public JavaAtomicBoolean(Ruby runtime, RubyClass metaClass) { + super(runtime, metaClass); + } + + @JRubyMethod + public IRubyObject initialize(ThreadContext context, IRubyObject value) { + atomicBoolean = new AtomicBoolean(convertRubyBooleanToJavaBoolean(value)); + return context.nil; + } + + @JRubyMethod + public IRubyObject initialize(ThreadContext context) { + atomicBoolean = new AtomicBoolean(); + return context.nil; + } + + @JRubyMethod(name = "value") + public IRubyObject value() { + return getRuntime().newBoolean(atomicBoolean.get()); + } + + @JRubyMethod(name = "true?") + public IRubyObject isAtomicTrue() { + return getRuntime().newBoolean(atomicBoolean.get()); + } + + @JRubyMethod(name = "false?") + public IRubyObject isAtomicFalse() { + return getRuntime().newBoolean((atomicBoolean.get() == false)); + } + + @JRubyMethod(name = "value=") + public IRubyObject setAtomic(ThreadContext context, IRubyObject newValue) { + atomicBoolean.set(convertRubyBooleanToJavaBoolean(newValue)); + return context.nil; + } + + @JRubyMethod(name = "make_true") + public IRubyObject makeTrue() { + return getRuntime().newBoolean(atomicBoolean.compareAndSet(false, true)); + } + + @JRubyMethod(name = "make_false") + public IRubyObject makeFalse() { + return getRuntime().newBoolean(atomicBoolean.compareAndSet(true, false)); + } + + private boolean convertRubyBooleanToJavaBoolean(IRubyObject newValue) { + if (newValue instanceof RubyBoolean.False || newValue instanceof RubyNil) { + return false; + } else { + return true; + } + } + } +} diff --git a/vendor/bundle/gems/concurrent-ruby-1.1.5/ext/concurrent-ruby/com/concurrent_ruby/ext/JavaAtomicFixnumLibrary.java b/vendor/bundle/gems/concurrent-ruby-1.1.5/ext/concurrent-ruby/com/concurrent_ruby/ext/JavaAtomicFixnumLibrary.java new file mode 100644 index 0000000..672bfc0 --- /dev/null +++ b/vendor/bundle/gems/concurrent-ruby-1.1.5/ext/concurrent-ruby/com/concurrent_ruby/ext/JavaAtomicFixnumLibrary.java @@ -0,0 +1,113 @@ +package com.concurrent_ruby.ext; + +import java.io.IOException; +import java.util.concurrent.atomic.AtomicLong; +import org.jruby.Ruby; +import org.jruby.RubyClass; +import org.jruby.RubyFixnum; +import org.jruby.RubyModule; +import org.jruby.RubyObject; +import org.jruby.anno.JRubyClass; +import org.jruby.anno.JRubyMethod; +import org.jruby.runtime.ObjectAllocator; +import org.jruby.runtime.ThreadContext; +import org.jruby.runtime.builtin.IRubyObject; +import org.jruby.runtime.load.Library; +import org.jruby.runtime.Block; + +public class JavaAtomicFixnumLibrary implements Library { + + public void load(Ruby runtime, boolean wrap) throws IOException { + RubyModule concurrentMod = runtime.defineModule("Concurrent"); + RubyClass atomicCls = concurrentMod.defineClassUnder("JavaAtomicFixnum", runtime.getObject(), JRUBYREFERENCE_ALLOCATOR); + + atomicCls.defineAnnotatedMethods(JavaAtomicFixnum.class); + } + + private static final ObjectAllocator JRUBYREFERENCE_ALLOCATOR = new ObjectAllocator() { + public IRubyObject allocate(Ruby runtime, RubyClass klazz) { + return new JavaAtomicFixnum(runtime, klazz); + } + }; + + @JRubyClass(name = "JavaAtomicFixnum", parent = "Object") + public static class JavaAtomicFixnum extends RubyObject { + + private AtomicLong atomicLong; + + public JavaAtomicFixnum(Ruby runtime, RubyClass metaClass) { + super(runtime, metaClass); + } + + @JRubyMethod + public IRubyObject initialize(ThreadContext context) { + this.atomicLong = new AtomicLong(0); + return context.nil; + } + + @JRubyMethod + public IRubyObject initialize(ThreadContext context, IRubyObject value) { + this.atomicLong = new AtomicLong(rubyFixnumToLong(value)); + return context.nil; + } + + @JRubyMethod(name = "value") + public IRubyObject getValue() { + return getRuntime().newFixnum(atomicLong.get()); + } + + @JRubyMethod(name = "value=") + public IRubyObject setValue(ThreadContext context, IRubyObject newValue) { + atomicLong.set(rubyFixnumToLong(newValue)); + return context.nil; + } + + @JRubyMethod(name = {"increment", "up"}) + public IRubyObject increment() { + return getRuntime().newFixnum(atomicLong.incrementAndGet()); + } + + @JRubyMethod(name = {"increment", "up"}) + public IRubyObject increment(IRubyObject value) { + long delta = rubyFixnumToLong(value); + return getRuntime().newFixnum(atomicLong.addAndGet(delta)); + } + + @JRubyMethod(name = {"decrement", "down"}) + public IRubyObject decrement() { + return getRuntime().newFixnum(atomicLong.decrementAndGet()); + } + + @JRubyMethod(name = {"decrement", "down"}) + public IRubyObject decrement(IRubyObject value) { + long delta = rubyFixnumToLong(value); + return getRuntime().newFixnum(atomicLong.addAndGet(-delta)); + } + + @JRubyMethod(name = "compare_and_set") + public IRubyObject compareAndSet(ThreadContext context, IRubyObject expect, IRubyObject update) { + return getRuntime().newBoolean(atomicLong.compareAndSet(rubyFixnumToLong(expect), rubyFixnumToLong(update))); + } + + @JRubyMethod + public IRubyObject update(ThreadContext context, Block block) { + for (;;) { + long _oldValue = atomicLong.get(); + IRubyObject oldValue = getRuntime().newFixnum(_oldValue); + IRubyObject newValue = block.yield(context, oldValue); + if (atomicLong.compareAndSet(_oldValue, rubyFixnumToLong(newValue))) { + return newValue; + } + } + } + + private long rubyFixnumToLong(IRubyObject value) { + if (value instanceof RubyFixnum) { + RubyFixnum fixNum = (RubyFixnum) value; + return fixNum.getLongValue(); + } else { + throw getRuntime().newArgumentError("value must be a Fixnum"); + } + } + } +} diff --git a/vendor/bundle/gems/concurrent-ruby-1.1.5/ext/concurrent-ruby/com/concurrent_ruby/ext/JavaSemaphoreLibrary.java b/vendor/bundle/gems/concurrent-ruby-1.1.5/ext/concurrent-ruby/com/concurrent_ruby/ext/JavaSemaphoreLibrary.java new file mode 100644 index 0000000..a3e847d --- /dev/null +++ b/vendor/bundle/gems/concurrent-ruby-1.1.5/ext/concurrent-ruby/com/concurrent_ruby/ext/JavaSemaphoreLibrary.java @@ -0,0 +1,159 @@ +package com.concurrent_ruby.ext; + +import java.io.IOException; +import java.util.concurrent.Semaphore; +import org.jruby.Ruby; +import org.jruby.RubyClass; +import org.jruby.RubyFixnum; +import org.jruby.RubyModule; +import org.jruby.RubyNumeric; +import org.jruby.RubyObject; +import org.jruby.anno.JRubyClass; +import org.jruby.anno.JRubyMethod; +import org.jruby.runtime.ObjectAllocator; +import org.jruby.runtime.ThreadContext; +import org.jruby.runtime.builtin.IRubyObject; + +public class JavaSemaphoreLibrary { + + public void load(Ruby runtime, boolean wrap) throws IOException { + RubyModule concurrentMod = runtime.defineModule("Concurrent"); + RubyClass atomicCls = concurrentMod.defineClassUnder("JavaSemaphore", runtime.getObject(), JRUBYREFERENCE_ALLOCATOR); + + atomicCls.defineAnnotatedMethods(JavaSemaphore.class); + } + + private static final ObjectAllocator JRUBYREFERENCE_ALLOCATOR = new ObjectAllocator() { + public IRubyObject allocate(Ruby runtime, RubyClass klazz) { + return new JavaSemaphore(runtime, klazz); + } + }; + + @JRubyClass(name = "JavaSemaphore", parent = "Object") + public static class JavaSemaphore extends RubyObject { + + private JRubySemaphore semaphore; + + public JavaSemaphore(Ruby runtime, RubyClass metaClass) { + super(runtime, metaClass); + } + + @JRubyMethod + public IRubyObject initialize(ThreadContext context, IRubyObject value) { + this.semaphore = new JRubySemaphore(rubyFixnumInt(value, "count")); + return context.nil; + } + + @JRubyMethod + public IRubyObject acquire(ThreadContext context, IRubyObject value) throws InterruptedException { + this.semaphore.acquire(rubyFixnumToPositiveInt(value, "permits")); + return context.nil; + } + + @JRubyMethod(name = "available_permits") + public IRubyObject availablePermits(ThreadContext context) { + return getRuntime().newFixnum(this.semaphore.availablePermits()); + } + + @JRubyMethod(name = "drain_permits") + public IRubyObject drainPermits(ThreadContext context) { + return getRuntime().newFixnum(this.semaphore.drainPermits()); + } + + @JRubyMethod + public IRubyObject acquire(ThreadContext context) throws InterruptedException { + this.semaphore.acquire(1); + return context.nil; + } + + @JRubyMethod(name = "try_acquire") + public IRubyObject tryAcquire(ThreadContext context) throws InterruptedException { + return getRuntime().newBoolean(semaphore.tryAcquire(1)); + } + + @JRubyMethod(name = "try_acquire") + public IRubyObject tryAcquire(ThreadContext context, IRubyObject permits) throws InterruptedException { + return getRuntime().newBoolean(semaphore.tryAcquire(rubyFixnumToPositiveInt(permits, "permits"))); + } + + @JRubyMethod(name = "try_acquire") + public IRubyObject tryAcquire(ThreadContext context, IRubyObject permits, IRubyObject timeout) throws InterruptedException { + return getRuntime().newBoolean( + semaphore.tryAcquire( + rubyFixnumToPositiveInt(permits, "permits"), + rubyNumericToLong(timeout, "timeout"), + java.util.concurrent.TimeUnit.SECONDS) + ); + } + + @JRubyMethod + public IRubyObject release(ThreadContext context) { + this.semaphore.release(1); + return getRuntime().newBoolean(true); + } + + @JRubyMethod + public IRubyObject release(ThreadContext context, IRubyObject value) { + this.semaphore.release(rubyFixnumToPositiveInt(value, "permits")); + return getRuntime().newBoolean(true); + } + + @JRubyMethod(name = "reduce_permits") + public IRubyObject reducePermits(ThreadContext context, IRubyObject reduction) throws InterruptedException { + this.semaphore.publicReducePermits(rubyFixnumToNonNegativeInt(reduction, "reduction")); + return context.nil; + } + + private int rubyFixnumInt(IRubyObject value, String paramName) { + if (value instanceof RubyFixnum) { + RubyFixnum fixNum = (RubyFixnum) value; + return (int) fixNum.getLongValue(); + } else { + throw getRuntime().newArgumentError(paramName + " must be integer"); + } + } + + private int rubyFixnumToNonNegativeInt(IRubyObject value, String paramName) { + if (value instanceof RubyFixnum && ((RubyFixnum) value).getLongValue() >= 0) { + RubyFixnum fixNum = (RubyFixnum) value; + return (int) fixNum.getLongValue(); + } else { + throw getRuntime().newArgumentError(paramName + " must be a non-negative integer"); + } + } + + private int rubyFixnumToPositiveInt(IRubyObject value, String paramName) { + if (value instanceof RubyFixnum && ((RubyFixnum) value).getLongValue() > 0) { + RubyFixnum fixNum = (RubyFixnum) value; + return (int) fixNum.getLongValue(); + } else { + throw getRuntime().newArgumentError(paramName + " must be an integer greater than zero"); + } + } + + private long rubyNumericToLong(IRubyObject value, String paramName) { + if (value instanceof RubyNumeric && ((RubyNumeric) value).getDoubleValue() > 0) { + RubyNumeric fixNum = (RubyNumeric) value; + return fixNum.getLongValue(); + } else { + throw getRuntime().newArgumentError(paramName + " must be a float greater than zero"); + } + } + + class JRubySemaphore extends Semaphore { + + public JRubySemaphore(int permits) { + super(permits); + } + + public JRubySemaphore(int permits, boolean value) { + super(permits, value); + } + + public void publicReducePermits(int i) { + reducePermits(i); + } + + } + } +} diff --git a/vendor/bundle/gems/concurrent-ruby-1.1.5/ext/concurrent-ruby/com/concurrent_ruby/ext/SynchronizationLibrary.java b/vendor/bundle/gems/concurrent-ruby-1.1.5/ext/concurrent-ruby/com/concurrent_ruby/ext/SynchronizationLibrary.java new file mode 100644 index 0000000..bfcc0d0 --- /dev/null +++ b/vendor/bundle/gems/concurrent-ruby-1.1.5/ext/concurrent-ruby/com/concurrent_ruby/ext/SynchronizationLibrary.java @@ -0,0 +1,307 @@ +package com.concurrent_ruby.ext; + +import org.jruby.Ruby; +import org.jruby.RubyBasicObject; +import org.jruby.RubyClass; +import org.jruby.RubyModule; +import org.jruby.RubyObject; +import org.jruby.RubyThread; +import org.jruby.anno.JRubyClass; +import org.jruby.anno.JRubyMethod; +import org.jruby.runtime.Block; +import org.jruby.runtime.ObjectAllocator; +import org.jruby.runtime.ThreadContext; +import org.jruby.runtime.Visibility; +import org.jruby.runtime.builtin.IRubyObject; +import org.jruby.runtime.load.Library; +import sun.misc.Unsafe; + +import java.io.IOException; +import java.lang.reflect.Field; +import java.lang.reflect.Method; + +public class SynchronizationLibrary implements Library { + + private static final Unsafe UNSAFE = loadUnsafe(); + private static final boolean FULL_FENCE = supportsFences(); + + private static Unsafe loadUnsafe() { + try { + Class ncdfe = Class.forName("sun.misc.Unsafe"); + Field f = ncdfe.getDeclaredField("theUnsafe"); + f.setAccessible(true); + return (Unsafe) f.get((java.lang.Object) null); + } catch (Exception var2) { + return null; + } catch (NoClassDefFoundError var3) { + return null; + } + } + + private static boolean supportsFences() { + if (UNSAFE == null) { + return false; + } else { + try { + Method m = UNSAFE.getClass().getDeclaredMethod("fullFence", new Class[0]); + if (m != null) { + return true; + } + } catch (Exception var1) { + // nothing + } + + return false; + } + } + + private static final ObjectAllocator JRUBY_OBJECT_ALLOCATOR = new ObjectAllocator() { + public IRubyObject allocate(Ruby runtime, RubyClass klazz) { + return new JRubyObject(runtime, klazz); + } + }; + + private static final ObjectAllocator OBJECT_ALLOCATOR = new ObjectAllocator() { + public IRubyObject allocate(Ruby runtime, RubyClass klazz) { + return new Object(runtime, klazz); + } + }; + + private static final ObjectAllocator ABSTRACT_LOCKABLE_OBJECT_ALLOCATOR = new ObjectAllocator() { + public IRubyObject allocate(Ruby runtime, RubyClass klazz) { + return new AbstractLockableObject(runtime, klazz); + } + }; + + private static final ObjectAllocator JRUBY_LOCKABLE_OBJECT_ALLOCATOR = new ObjectAllocator() { + public IRubyObject allocate(Ruby runtime, RubyClass klazz) { + return new JRubyLockableObject(runtime, klazz); + } + }; + + public void load(Ruby runtime, boolean wrap) throws IOException { + RubyModule synchronizationModule = runtime. + defineModule("Concurrent"). + defineModuleUnder("Synchronization"); + + RubyModule jrubyAttrVolatileModule = synchronizationModule.defineModuleUnder("JRubyAttrVolatile"); + jrubyAttrVolatileModule.defineAnnotatedMethods(JRubyAttrVolatile.class); + + defineClass(runtime, synchronizationModule, "AbstractObject", "JRubyObject", + JRubyObject.class, JRUBY_OBJECT_ALLOCATOR); + + defineClass(runtime, synchronizationModule, "JRubyObject", "Object", + Object.class, OBJECT_ALLOCATOR); + + defineClass(runtime, synchronizationModule, "Object", "AbstractLockableObject", + AbstractLockableObject.class, ABSTRACT_LOCKABLE_OBJECT_ALLOCATOR); + + defineClass(runtime, synchronizationModule, "AbstractLockableObject", "JRubyLockableObject", + JRubyLockableObject.class, JRUBY_LOCKABLE_OBJECT_ALLOCATOR); + + defineClass(runtime, synchronizationModule, "Object", "JRuby", + JRuby.class, new ObjectAllocator() { + @Override + public IRubyObject allocate(Ruby runtime, RubyClass klazz) { + return new JRuby(runtime, klazz); + } + }); + } + + private RubyClass defineClass( + Ruby runtime, + RubyModule namespace, + String parentName, + String name, + Class javaImplementation, + ObjectAllocator allocator) { + final RubyClass parentClass = namespace.getClass(parentName); + + if (parentClass == null) { + System.out.println("not found " + parentName); + throw runtime.newRuntimeError(namespace.toString() + "::" + parentName + " is missing"); + } + + final RubyClass newClass = namespace.defineClassUnder(name, parentClass, allocator); + newClass.defineAnnotatedMethods(javaImplementation); + return newClass; + } + + // Facts: + // - all ivar reads are without any synchronisation of fences see + // https://github.com/jruby/jruby/blob/master/core/src/main/java/org/jruby/runtime/ivars/VariableAccessor.java#L110-110 + // - writes depend on UnsafeHolder.U, null -> SynchronizedVariableAccessor, !null -> StampedVariableAccessor + // SynchronizedVariableAccessor wraps with synchronized block, StampedVariableAccessor uses fullFence or + // volatilePut + // TODO (pitr 16-Sep-2015): what do we do in Java 9 ? + + // module JRubyAttrVolatile + public static class JRubyAttrVolatile { + + // volatile threadContext is used as a memory barrier per the JVM memory model happens-before semantic + // on volatile fields. any volatile field could have been used but using the thread context is an + // attempt to avoid code elimination. + private static volatile int volatileField; + + @JRubyMethod(name = "full_memory_barrier", visibility = Visibility.PUBLIC) + public static IRubyObject fullMemoryBarrier(ThreadContext context, IRubyObject self) { + // Prevent reordering of ivar writes with publication of this instance + if (!FULL_FENCE) { + // Assuming that following volatile read and write is not eliminated it simulates fullFence. + // If it's eliminated it'll cause problems only on non-x86 platforms. + // http://shipilev.net/blog/2014/jmm-pragmatics/#_happens_before_test_your_understanding + final int volatileRead = volatileField; + volatileField = context.getLine(); + } else { + UNSAFE.fullFence(); + } + return context.nil; + } + + @JRubyMethod(name = "instance_variable_get_volatile", visibility = Visibility.PUBLIC) + public static IRubyObject instanceVariableGetVolatile( + ThreadContext context, + IRubyObject self, + IRubyObject name) { + // Ensure we ses latest value with loadFence + if (!FULL_FENCE) { + // piggybacking on volatile read, simulating loadFence + final int volatileRead = volatileField; + return ((RubyBasicObject) self).instance_variable_get(context, name); + } else { + UNSAFE.loadFence(); + return ((RubyBasicObject) self).instance_variable_get(context, name); + } + } + + @JRubyMethod(name = "instance_variable_set_volatile", visibility = Visibility.PUBLIC) + public static IRubyObject InstanceVariableSetVolatile( + ThreadContext context, + IRubyObject self, + IRubyObject name, + IRubyObject value) { + // Ensure we make last update visible + if (!FULL_FENCE) { + // piggybacking on volatile write, simulating storeFence + final IRubyObject result = ((RubyBasicObject) self).instance_variable_set(name, value); + volatileField = context.getLine(); + return result; + } else { + // JRuby uses StampedVariableAccessor which calls fullFence + // so no additional steps needed. + // See https://github.com/jruby/jruby/blob/master/core/src/main/java/org/jruby/runtime/ivars/StampedVariableAccessor.java#L151-L159 + return ((RubyBasicObject) self).instance_variable_set(name, value); + } + } + } + + @JRubyClass(name = "JRubyObject", parent = "AbstractObject") + public static class JRubyObject extends RubyObject { + + public JRubyObject(Ruby runtime, RubyClass metaClass) { + super(runtime, metaClass); + } + } + + @JRubyClass(name = "Object", parent = "JRubyObject") + public static class Object extends JRubyObject { + + public Object(Ruby runtime, RubyClass metaClass) { + super(runtime, metaClass); + } + } + + @JRubyClass(name = "AbstractLockableObject", parent = "Object") + public static class AbstractLockableObject extends Object { + + public AbstractLockableObject(Ruby runtime, RubyClass metaClass) { + super(runtime, metaClass); + } + } + + @JRubyClass(name = "JRubyLockableObject", parent = "AbstractLockableObject") + public static class JRubyLockableObject extends JRubyObject { + + public JRubyLockableObject(Ruby runtime, RubyClass metaClass) { + super(runtime, metaClass); + } + + @JRubyMethod(name = "synchronize", visibility = Visibility.PROTECTED) + public IRubyObject rubySynchronize(ThreadContext context, Block block) { + synchronized (this) { + return block.yield(context, null); + } + } + + @JRubyMethod(name = "ns_wait", optional = 1, visibility = Visibility.PROTECTED) + public IRubyObject nsWait(ThreadContext context, IRubyObject[] args) { + Ruby runtime = context.runtime; + if (args.length > 1) { + throw runtime.newArgumentError(args.length, 1); + } + Double timeout = null; + if (args.length > 0 && !args[0].isNil()) { + timeout = args[0].convertToFloat().getDoubleValue(); + if (timeout < 0) { + throw runtime.newArgumentError("time interval must be positive"); + } + } + if (Thread.interrupted()) { + throw runtime.newConcurrencyError("thread interrupted"); + } + boolean success = false; + try { + success = context.getThread().wait_timeout(this, timeout); + } catch (InterruptedException ie) { + throw runtime.newConcurrencyError(ie.getLocalizedMessage()); + } finally { + // An interrupt or timeout may have caused us to miss + // a notify that we consumed, so do another notify in + // case someone else is available to pick it up. + if (!success) { + this.notify(); + } + } + return this; + } + + @JRubyMethod(name = "ns_signal", visibility = Visibility.PROTECTED) + public IRubyObject nsSignal(ThreadContext context) { + notify(); + return this; + } + + @JRubyMethod(name = "ns_broadcast", visibility = Visibility.PROTECTED) + public IRubyObject nsBroadcast(ThreadContext context) { + notifyAll(); + return this; + } + } + + @JRubyClass(name = "JRuby") + public static class JRuby extends RubyObject { + public JRuby(Ruby runtime, RubyClass metaClass) { + super(runtime, metaClass); + } + + @JRubyMethod(name = "sleep_interruptibly", visibility = Visibility.PUBLIC, module = true) + public static IRubyObject sleepInterruptibly(final ThreadContext context, IRubyObject receiver, final Block block) { + try { + context.getThread().executeBlockingTask(new RubyThread.BlockingTask() { + @Override + public void run() throws InterruptedException { + block.call(context); + } + + @Override + public void wakeup() { + context.getThread().getNativeThread().interrupt(); + } + }); + } catch (InterruptedException e) { + throw context.runtime.newThreadError("interrupted in Concurrent::Synchronization::JRuby.sleep_interruptibly"); + } + return context.nil; + } + } +} diff --git a/vendor/bundle/gems/concurrent-ruby-1.1.5/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/ConcurrentHashMap.java b/vendor/bundle/gems/concurrent-ruby-1.1.5/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/ConcurrentHashMap.java new file mode 100644 index 0000000..e11e15a --- /dev/null +++ b/vendor/bundle/gems/concurrent-ruby-1.1.5/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/ConcurrentHashMap.java @@ -0,0 +1,31 @@ +package com.concurrent_ruby.ext.jsr166e; + +import java.util.Map; +import java.util.Set; + +public interface ConcurrentHashMap { + /** Interface describing a function of one argument */ + public interface Fun { T apply(A a); } + /** Interface describing a function of two arguments */ + public interface BiFun { T apply(A a, B b); } + + public V get(K key); + public V put(K key, V value); + public V putIfAbsent(K key, V value); + public V computeIfAbsent(K key, Fun mf); + public V computeIfPresent(K key, BiFun mf); + public V compute(K key, BiFun mf); + public V merge(K key, V value, BiFun mf); + public boolean replace(K key, V oldVal, V newVal); + public V replace(K key, V value); + public boolean containsKey(K key); + public boolean remove(Object key, Object value); + public V remove(K key); + public void clear(); + public Set> entrySet(); + public int size(); + public V getValueOrDefault(Object key, V defaultValue); + + public boolean containsValue(V value); + public K findKey(V value); +} diff --git a/vendor/bundle/gems/concurrent-ruby-1.1.5/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/ConcurrentHashMapV8.java b/vendor/bundle/gems/concurrent-ruby-1.1.5/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/ConcurrentHashMapV8.java new file mode 100644 index 0000000..86aa4eb --- /dev/null +++ b/vendor/bundle/gems/concurrent-ruby-1.1.5/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/ConcurrentHashMapV8.java @@ -0,0 +1,3863 @@ +/* + * Written by Doug Lea with assistance from members of JCP JSR-166 + * Expert Group and released to the public domain, as explained at + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +// This is based on the 1.79 version. + +package com.concurrent_ruby.ext.jsr166e; + +import org.jruby.RubyClass; +import org.jruby.RubyNumeric; +import org.jruby.RubyObject; +import org.jruby.exceptions.RaiseException; +import com.concurrent_ruby.ext.jsr166y.ThreadLocalRandom; +import org.jruby.runtime.ThreadContext; +import org.jruby.runtime.builtin.IRubyObject; + +import java.util.Arrays; +import java.util.Map; +import java.util.Set; +import java.util.Collection; +import java.util.Hashtable; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Enumeration; +import java.util.ConcurrentModificationException; +import java.util.NoSuchElementException; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.locks.AbstractQueuedSynchronizer; + +import java.io.Serializable; + +/** + * A hash table supporting full concurrency of retrievals and + * high expected concurrency for updates. This class obeys the + * same functional specification as {@link java.util.Hashtable}, and + * includes versions of methods corresponding to each method of + * {@code Hashtable}. However, even though all operations are + * thread-safe, retrieval operations do not entail locking, + * and there is not any support for locking the entire table + * in a way that prevents all access. This class is fully + * interoperable with {@code Hashtable} in programs that rely on its + * thread safety but not on its synchronization details. + * + *

Retrieval operations (including {@code get}) generally do not + * block, so may overlap with update operations (including {@code put} + * and {@code remove}). Retrievals reflect the results of the most + * recently completed update operations holding upon their + * onset. (More formally, an update operation for a given key bears a + * happens-before relation with any (non-null) retrieval for + * that key reporting the updated value.) For aggregate operations + * such as {@code putAll} and {@code clear}, concurrent retrievals may + * reflect insertion or removal of only some entries. Similarly, + * Iterators and Enumerations return elements reflecting the state of + * the hash table at some point at or since the creation of the + * iterator/enumeration. They do not throw {@link + * ConcurrentModificationException}. However, iterators are designed + * to be used by only one thread at a time. Bear in mind that the + * results of aggregate status methods including {@code size}, {@code + * isEmpty}, and {@code containsValue} are typically useful only when + * a map is not undergoing concurrent updates in other threads. + * Otherwise the results of these methods reflect transient states + * that may be adequate for monitoring or estimation purposes, but not + * for program control. + * + *

The table is dynamically expanded when there are too many + * collisions (i.e., keys that have distinct hash codes but fall into + * the same slot modulo the table size), with the expected average + * effect of maintaining roughly two bins per mapping (corresponding + * to a 0.75 load factor threshold for resizing). There may be much + * variance around this average as mappings are added and removed, but + * overall, this maintains a commonly accepted time/space tradeoff for + * hash tables. However, resizing this or any other kind of hash + * table may be a relatively slow operation. When possible, it is a + * good idea to provide a size estimate as an optional {@code + * initialCapacity} constructor argument. An additional optional + * {@code loadFactor} constructor argument provides a further means of + * customizing initial table capacity by specifying the table density + * to be used in calculating the amount of space to allocate for the + * given number of elements. Also, for compatibility with previous + * versions of this class, constructors may optionally specify an + * expected {@code concurrencyLevel} as an additional hint for + * internal sizing. Note that using many keys with exactly the same + * {@code hashCode()} is a sure way to slow down performance of any + * hash table. + * + *

A {@link Set} projection of a ConcurrentHashMapV8 may be created + * (using {@link #newKeySet()} or {@link #newKeySet(int)}), or viewed + * (using {@link #keySet(Object)} when only keys are of interest, and the + * mapped values are (perhaps transiently) not used or all take the + * same mapping value. + * + *

A ConcurrentHashMapV8 can be used as scalable frequency map (a + * form of histogram or multiset) by using {@link LongAdder} values + * and initializing via {@link #computeIfAbsent}. For example, to add + * a count to a {@code ConcurrentHashMapV8 freqs}, you + * can use {@code freqs.computeIfAbsent(k -> new + * LongAdder()).increment();} + * + *

This class and its views and iterators implement all of the + * optional methods of the {@link Map} and {@link Iterator} + * interfaces. + * + *

Like {@link Hashtable} but unlike {@link HashMap}, this class + * does not allow {@code null} to be used as a key or value. + * + *

ConcurrentHashMapV8s support parallel operations using the {@link + * ForkJoinPool#commonPool}. (Tasks that may be used in other contexts + * are available in class {@link ForkJoinTasks}). These operations are + * designed to be safely, and often sensibly, applied even with maps + * that are being concurrently updated by other threads; for example, + * when computing a snapshot summary of the values in a shared + * registry. There are three kinds of operation, each with four + * forms, accepting functions with Keys, Values, Entries, and (Key, + * Value) arguments and/or return values. (The first three forms are + * also available via the {@link #keySet()}, {@link #values()} and + * {@link #entrySet()} views). Because the elements of a + * ConcurrentHashMapV8 are not ordered in any particular way, and may be + * processed in different orders in different parallel executions, the + * correctness of supplied functions should not depend on any + * ordering, or on any other objects or values that may transiently + * change while computation is in progress; and except for forEach + * actions, should ideally be side-effect-free. + * + *

+ * + *

The concurrency properties of bulk operations follow + * from those of ConcurrentHashMapV8: Any non-null result returned + * from {@code get(key)} and related access methods bears a + * happens-before relation with the associated insertion or + * update. The result of any bulk operation reflects the + * composition of these per-element relations (but is not + * necessarily atomic with respect to the map as a whole unless it + * is somehow known to be quiescent). Conversely, because keys + * and values in the map are never null, null serves as a reliable + * atomic indicator of the current lack of any result. To + * maintain this property, null serves as an implicit basis for + * all non-scalar reduction operations. For the double, long, and + * int versions, the basis should be one that, when combined with + * any other value, returns that other value (more formally, it + * should be the identity element for the reduction). Most common + * reductions have these properties; for example, computing a sum + * with basis 0 or a minimum with basis MAX_VALUE. + * + *

Search and transformation functions provided as arguments + * should similarly return null to indicate the lack of any result + * (in which case it is not used). In the case of mapped + * reductions, this also enables transformations to serve as + * filters, returning null (or, in the case of primitive + * specializations, the identity basis) if the element should not + * be combined. You can create compound transformations and + * filterings by composing them yourself under this "null means + * there is nothing there now" rule before using them in search or + * reduce operations. + * + *

Methods accepting and/or returning Entry arguments maintain + * key-value associations. They may be useful for example when + * finding the key for the greatest value. Note that "plain" Entry + * arguments can be supplied using {@code new + * AbstractMap.SimpleEntry(k,v)}. + * + *

Bulk operations may complete abruptly, throwing an + * exception encountered in the application of a supplied + * function. Bear in mind when handling such exceptions that other + * concurrently executing functions could also have thrown + * exceptions, or would have done so if the first exception had + * not occurred. + * + *

Parallel speedups for bulk operations compared to sequential + * processing are common but not guaranteed. Operations involving + * brief functions on small maps may execute more slowly than + * sequential loops if the underlying work to parallelize the + * computation is more expensive than the computation itself. + * Similarly, parallelization may not lead to much actual parallelism + * if all processors are busy performing unrelated tasks. + * + *

All arguments to all task methods must be non-null. + * + *

jsr166e note: During transition, this class + * uses nested functional interfaces with different names but the + * same forms as those expected for JDK8. + * + *

This class is a member of the + * + * Java Collections Framework. + * + * @since 1.5 + * @author Doug Lea + * @param the type of keys maintained by this map + * @param the type of mapped values + */ +public class ConcurrentHashMapV8 + implements ConcurrentMap, Serializable, ConcurrentHashMap { + private static final long serialVersionUID = 7249069246763182397L; + + /** + * A partitionable iterator. A Spliterator can be traversed + * directly, but can also be partitioned (before traversal) by + * creating another Spliterator that covers a non-overlapping + * portion of the elements, and so may be amenable to parallel + * execution. + * + *

This interface exports a subset of expected JDK8 + * functionality. + * + *

Sample usage: Here is one (of the several) ways to compute + * the sum of the values held in a map using the ForkJoin + * framework. As illustrated here, Spliterators are well suited to + * designs in which a task repeatedly splits off half its work + * into forked subtasks until small enough to process directly, + * and then joins these subtasks. Variants of this style can also + * be used in completion-based designs. + * + *

+     * {@code ConcurrentHashMapV8 m = ...
+     * // split as if have 8 * parallelism, for load balance
+     * int n = m.size();
+     * int p = aForkJoinPool.getParallelism() * 8;
+     * int split = (n < p)? n : p;
+     * long sum = aForkJoinPool.invoke(new SumValues(m.valueSpliterator(), split, null));
+     * // ...
+     * static class SumValues extends RecursiveTask {
+     *   final Spliterator s;
+     *   final int split;             // split while > 1
+     *   final SumValues nextJoin;    // records forked subtasks to join
+     *   SumValues(Spliterator s, int depth, SumValues nextJoin) {
+     *     this.s = s; this.depth = depth; this.nextJoin = nextJoin;
+     *   }
+     *   public Long compute() {
+     *     long sum = 0;
+     *     SumValues subtasks = null; // fork subtasks
+     *     for (int s = split >>> 1; s > 0; s >>>= 1)
+     *       (subtasks = new SumValues(s.split(), s, subtasks)).fork();
+     *     while (s.hasNext())        // directly process remaining elements
+     *       sum += s.next();
+     *     for (SumValues t = subtasks; t != null; t = t.nextJoin)
+     *       sum += t.join();         // collect subtask results
+     *     return sum;
+     *   }
+     * }
+     * }
+ */ + public static interface Spliterator extends Iterator { + /** + * Returns a Spliterator covering approximately half of the + * elements, guaranteed not to overlap with those subsequently + * returned by this Spliterator. After invoking this method, + * the current Spliterator will not produce any of + * the elements of the returned Spliterator, but the two + * Spliterators together will produce all of the elements that + * would have been produced by this Spliterator had this + * method not been called. The exact number of elements + * produced by the returned Spliterator is not guaranteed, and + * may be zero (i.e., with {@code hasNext()} reporting {@code + * false}) if this Spliterator cannot be further split. + * + * @return a Spliterator covering approximately half of the + * elements + * @throws IllegalStateException if this Spliterator has + * already commenced traversing elements + */ + Spliterator split(); + } + + + /* + * Overview: + * + * The primary design goal of this hash table is to maintain + * concurrent readability (typically method get(), but also + * iterators and related methods) while minimizing update + * contention. Secondary goals are to keep space consumption about + * the same or better than java.util.HashMap, and to support high + * initial insertion rates on an empty table by many threads. + * + * Each key-value mapping is held in a Node. Because Node fields + * can contain special values, they are defined using plain Object + * types. Similarly in turn, all internal methods that use them + * work off Object types. And similarly, so do the internal + * methods of auxiliary iterator and view classes. All public + * generic typed methods relay in/out of these internal methods, + * supplying null-checks and casts as needed. This also allows + * many of the public methods to be factored into a smaller number + * of internal methods (although sadly not so for the five + * variants of put-related operations). The validation-based + * approach explained below leads to a lot of code sprawl because + * retry-control precludes factoring into smaller methods. + * + * The table is lazily initialized to a power-of-two size upon the + * first insertion. Each bin in the table normally contains a + * list of Nodes (most often, the list has only zero or one Node). + * Table accesses require volatile/atomic reads, writes, and + * CASes. Because there is no other way to arrange this without + * adding further indirections, we use intrinsics + * (sun.misc.Unsafe) operations. The lists of nodes within bins + * are always accurately traversable under volatile reads, so long + * as lookups check hash code and non-nullness of value before + * checking key equality. + * + * We use the top two bits of Node hash fields for control + * purposes -- they are available anyway because of addressing + * constraints. As explained further below, these top bits are + * used as follows: + * 00 - Normal + * 01 - Locked + * 11 - Locked and may have a thread waiting for lock + * 10 - Node is a forwarding node + * + * The lower 30 bits of each Node's hash field contain a + * transformation of the key's hash code, except for forwarding + * nodes, for which the lower bits are zero (and so always have + * hash field == MOVED). + * + * Insertion (via put or its variants) of the first node in an + * empty bin is performed by just CASing it to the bin. This is + * by far the most common case for put operations under most + * key/hash distributions. Other update operations (insert, + * delete, and replace) require locks. We do not want to waste + * the space required to associate a distinct lock object with + * each bin, so instead use the first node of a bin list itself as + * a lock. Blocking support for these locks relies on the builtin + * "synchronized" monitors. However, we also need a tryLock + * construction, so we overlay these by using bits of the Node + * hash field for lock control (see above), and so normally use + * builtin monitors only for blocking and signalling using + * wait/notifyAll constructions. See Node.tryAwaitLock. + * + * Using the first node of a list as a lock does not by itself + * suffice though: When a node is locked, any update must first + * validate that it is still the first node after locking it, and + * retry if not. Because new nodes are always appended to lists, + * once a node is first in a bin, it remains first until deleted + * or the bin becomes invalidated (upon resizing). However, + * operations that only conditionally update may inspect nodes + * until the point of update. This is a converse of sorts to the + * lazy locking technique described by Herlihy & Shavit. + * + * The main disadvantage of per-bin locks is that other update + * operations on other nodes in a bin list protected by the same + * lock can stall, for example when user equals() or mapping + * functions take a long time. However, statistically, under + * random hash codes, this is not a common problem. Ideally, the + * frequency of nodes in bins follows a Poisson distribution + * (http://en.wikipedia.org/wiki/Poisson_distribution) with a + * parameter of about 0.5 on average, given the resizing threshold + * of 0.75, although with a large variance because of resizing + * granularity. Ignoring variance, the expected occurrences of + * list size k are (exp(-0.5) * pow(0.5, k) / factorial(k)). The + * first values are: + * + * 0: 0.60653066 + * 1: 0.30326533 + * 2: 0.07581633 + * 3: 0.01263606 + * 4: 0.00157952 + * 5: 0.00015795 + * 6: 0.00001316 + * 7: 0.00000094 + * 8: 0.00000006 + * more: less than 1 in ten million + * + * Lock contention probability for two threads accessing distinct + * elements is roughly 1 / (8 * #elements) under random hashes. + * + * Actual hash code distributions encountered in practice + * sometimes deviate significantly from uniform randomness. This + * includes the case when N > (1<<30), so some keys MUST collide. + * Similarly for dumb or hostile usages in which multiple keys are + * designed to have identical hash codes. Also, although we guard + * against the worst effects of this (see method spread), sets of + * hashes may differ only in bits that do not impact their bin + * index for a given power-of-two mask. So we use a secondary + * strategy that applies when the number of nodes in a bin exceeds + * a threshold, and at least one of the keys implements + * Comparable. These TreeBins use a balanced tree to hold nodes + * (a specialized form of red-black trees), bounding search time + * to O(log N). Each search step in a TreeBin is around twice as + * slow as in a regular list, but given that N cannot exceed + * (1<<64) (before running out of addresses) this bounds search + * steps, lock hold times, etc, to reasonable constants (roughly + * 100 nodes inspected per operation worst case) so long as keys + * are Comparable (which is very common -- String, Long, etc). + * TreeBin nodes (TreeNodes) also maintain the same "next" + * traversal pointers as regular nodes, so can be traversed in + * iterators in the same way. + * + * The table is resized when occupancy exceeds a percentage + * threshold (nominally, 0.75, but see below). Only a single + * thread performs the resize (using field "sizeCtl", to arrange + * exclusion), but the table otherwise remains usable for reads + * and updates. Resizing proceeds by transferring bins, one by + * one, from the table to the next table. Because we are using + * power-of-two expansion, the elements from each bin must either + * stay at same index, or move with a power of two offset. We + * eliminate unnecessary node creation by catching cases where old + * nodes can be reused because their next fields won't change. On + * average, only about one-sixth of them need cloning when a table + * doubles. The nodes they replace will be garbage collectable as + * soon as they are no longer referenced by any reader thread that + * may be in the midst of concurrently traversing table. Upon + * transfer, the old table bin contains only a special forwarding + * node (with hash field "MOVED") that contains the next table as + * its key. On encountering a forwarding node, access and update + * operations restart, using the new table. + * + * Each bin transfer requires its bin lock. However, unlike other + * cases, a transfer can skip a bin if it fails to acquire its + * lock, and revisit it later (unless it is a TreeBin). Method + * rebuild maintains a buffer of TRANSFER_BUFFER_SIZE bins that + * have been skipped because of failure to acquire a lock, and + * blocks only if none are available (i.e., only very rarely). + * The transfer operation must also ensure that all accessible + * bins in both the old and new table are usable by any traversal. + * When there are no lock acquisition failures, this is arranged + * simply by proceeding from the last bin (table.length - 1) up + * towards the first. Upon seeing a forwarding node, traversals + * (see class Iter) arrange to move to the new table + * without revisiting nodes. However, when any node is skipped + * during a transfer, all earlier table bins may have become + * visible, so are initialized with a reverse-forwarding node back + * to the old table until the new ones are established. (This + * sometimes requires transiently locking a forwarding node, which + * is possible under the above encoding.) These more expensive + * mechanics trigger only when necessary. + * + * The traversal scheme also applies to partial traversals of + * ranges of bins (via an alternate Traverser constructor) + * to support partitioned aggregate operations. Also, read-only + * operations give up if ever forwarded to a null table, which + * provides support for shutdown-style clearing, which is also not + * currently implemented. + * + * Lazy table initialization minimizes footprint until first use, + * and also avoids resizings when the first operation is from a + * putAll, constructor with map argument, or deserialization. + * These cases attempt to override the initial capacity settings, + * but harmlessly fail to take effect in cases of races. + * + * The element count is maintained using a LongAdder, which avoids + * contention on updates but can encounter cache thrashing if read + * too frequently during concurrent access. To avoid reading so + * often, resizing is attempted either when a bin lock is + * contended, or upon adding to a bin already holding two or more + * nodes (checked before adding in the xIfAbsent methods, after + * adding in others). Under uniform hash distributions, the + * probability of this occurring at threshold is around 13%, + * meaning that only about 1 in 8 puts check threshold (and after + * resizing, many fewer do so). But this approximation has high + * variance for small table sizes, so we check on any collision + * for sizes <= 64. The bulk putAll operation further reduces + * contention by only committing count updates upon these size + * checks. + * + * Maintaining API and serialization compatibility with previous + * versions of this class introduces several oddities. Mainly: We + * leave untouched but unused constructor arguments refering to + * concurrencyLevel. We accept a loadFactor constructor argument, + * but apply it only to initial table capacity (which is the only + * time that we can guarantee to honor it.) We also declare an + * unused "Segment" class that is instantiated in minimal form + * only when serializing. + */ + + /* ---------------- Constants -------------- */ + + /** + * The largest possible table capacity. This value must be + * exactly 1<<30 to stay within Java array allocation and indexing + * bounds for power of two table sizes, and is further required + * because the top two bits of 32bit hash fields are used for + * control purposes. + */ + private static final int MAXIMUM_CAPACITY = 1 << 30; + + /** + * The default initial table capacity. Must be a power of 2 + * (i.e., at least 1) and at most MAXIMUM_CAPACITY. + */ + private static final int DEFAULT_CAPACITY = 16; + + /** + * The largest possible (non-power of two) array size. + * Needed by toArray and related methods. + */ + static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8; + + /** + * The default concurrency level for this table. Unused but + * defined for compatibility with previous versions of this class. + */ + private static final int DEFAULT_CONCURRENCY_LEVEL = 16; + + /** + * The load factor for this table. Overrides of this value in + * constructors affect only the initial table capacity. The + * actual floating point value isn't normally used -- it is + * simpler to use expressions such as {@code n - (n >>> 2)} for + * the associated resizing threshold. + */ + private static final float LOAD_FACTOR = 0.75f; + + /** + * The buffer size for skipped bins during transfers. The + * value is arbitrary but should be large enough to avoid + * most locking stalls during resizes. + */ + private static final int TRANSFER_BUFFER_SIZE = 32; + + /** + * The bin count threshold for using a tree rather than list for a + * bin. The value reflects the approximate break-even point for + * using tree-based operations. + * Note that Doug's version defaults to 8, but when dealing with + * Ruby objects it is actually beneficial to avoid TreeNodes + * as long as possible as it usually means going into Ruby land. + */ + private static final int TREE_THRESHOLD = 16; + + /* + * Encodings for special uses of Node hash fields. See above for + * explanation. + */ + static final int MOVED = 0x80000000; // hash field for forwarding nodes + static final int LOCKED = 0x40000000; // set/tested only as a bit + static final int WAITING = 0xc0000000; // both bits set/tested together + static final int HASH_BITS = 0x3fffffff; // usable bits of normal node hash + + /* ---------------- Fields -------------- */ + + /** + * The array of bins. Lazily initialized upon first insertion. + * Size is always a power of two. Accessed directly by iterators. + */ + transient volatile Node[] table; + + /** + * The counter maintaining number of elements. + */ + private transient final LongAdder counter; + + /** + * Table initialization and resizing control. When negative, the + * table is being initialized or resized. Otherwise, when table is + * null, holds the initial table size to use upon creation, or 0 + * for default. After initialization, holds the next element count + * value upon which to resize the table. + */ + private transient volatile int sizeCtl; + + // views + private transient KeySetView keySet; + private transient ValuesView values; + private transient EntrySetView entrySet; + + /** For serialization compatibility. Null unless serialized; see below */ + private Segment[] segments; + + /* ---------------- Table element access -------------- */ + + /* + * Volatile access methods are used for table elements as well as + * elements of in-progress next table while resizing. Uses are + * null checked by callers, and implicitly bounds-checked, relying + * on the invariants that tab arrays have non-zero size, and all + * indices are masked with (tab.length - 1) which is never + * negative and always less than length. Note that, to be correct + * wrt arbitrary concurrency errors by users, bounds checks must + * operate on local variables, which accounts for some odd-looking + * inline assignments below. + */ + + static final Node tabAt(Node[] tab, int i) { // used by Iter + return (Node)UNSAFE.getObjectVolatile(tab, ((long)i< 1 ? 64 : 1; + + /** + * Spins a while if LOCKED bit set and this node is the first + * of its bin, and then sets WAITING bits on hash field and + * blocks (once) if they are still set. It is OK for this + * method to return even if lock is not available upon exit, + * which enables these simple single-wait mechanics. + * + * The corresponding signalling operation is performed within + * callers: Upon detecting that WAITING has been set when + * unlocking lock (via a failed CAS from non-waiting LOCKED + * state), unlockers acquire the sync lock and perform a + * notifyAll. + * + * The initial sanity check on tab and bounds is not currently + * necessary in the only usages of this method, but enables + * use in other future contexts. + */ + final void tryAwaitLock(Node[] tab, int i) { + if (tab != null && i >= 0 && i < tab.length) { // sanity check + int r = ThreadLocalRandom.current().nextInt(); // randomize spins + int spins = MAX_SPINS, h; + while (tabAt(tab, i) == this && ((h = hash) & LOCKED) != 0) { + if (spins >= 0) { + r ^= r << 1; r ^= r >>> 3; r ^= r << 10; // xorshift + if (r >= 0 && --spins == 0) + Thread.yield(); // yield before block + } + else if (casHash(h, h | WAITING)) { + synchronized (this) { + if (tabAt(tab, i) == this && + (hash & WAITING) == WAITING) { + try { + wait(); + } catch (InterruptedException ie) { + Thread.currentThread().interrupt(); + } + } + else + notifyAll(); // possibly won race vs signaller + } + break; + } + } + } + } + + // Unsafe mechanics for casHash + private static final sun.misc.Unsafe UNSAFE; + private static final long hashOffset; + + static { + try { + UNSAFE = getUnsafe(); + Class k = Node.class; + hashOffset = UNSAFE.objectFieldOffset + (k.getDeclaredField("hash")); + } catch (Exception e) { + throw new Error(e); + } + } + } + + /* ---------------- TreeBins -------------- */ + + /** + * Nodes for use in TreeBins + */ + static final class TreeNode extends Node { + TreeNode parent; // red-black tree links + TreeNode left; + TreeNode right; + TreeNode prev; // needed to unlink next upon deletion + boolean red; + + TreeNode(int hash, Object key, Object val, Node next, TreeNode parent) { + super(hash, key, val, next); + this.parent = parent; + } + } + + /** + * A specialized form of red-black tree for use in bins + * whose size exceeds a threshold. + * + * TreeBins use a special form of comparison for search and + * related operations (which is the main reason we cannot use + * existing collections such as TreeMaps). TreeBins contain + * Comparable elements, but may contain others, as well as + * elements that are Comparable but not necessarily Comparable + * for the same T, so we cannot invoke compareTo among them. To + * handle this, the tree is ordered primarily by hash value, then + * by getClass().getName() order, and then by Comparator order + * among elements of the same class. On lookup at a node, if + * elements are not comparable or compare as 0, both left and + * right children may need to be searched in the case of tied hash + * values. (This corresponds to the full list search that would be + * necessary if all elements were non-Comparable and had tied + * hashes.) The red-black balancing code is updated from + * pre-jdk-collections + * (http://gee.cs.oswego.edu/dl/classes/collections/RBCell.java) + * based in turn on Cormen, Leiserson, and Rivest "Introduction to + * Algorithms" (CLR). + * + * TreeBins also maintain a separate locking discipline than + * regular bins. Because they are forwarded via special MOVED + * nodes at bin heads (which can never change once established), + * we cannot use those nodes as locks. Instead, TreeBin + * extends AbstractQueuedSynchronizer to support a simple form of + * read-write lock. For update operations and table validation, + * the exclusive form of lock behaves in the same way as bin-head + * locks. However, lookups use shared read-lock mechanics to allow + * multiple readers in the absence of writers. Additionally, + * these lookups do not ever block: While the lock is not + * available, they proceed along the slow traversal path (via + * next-pointers) until the lock becomes available or the list is + * exhausted, whichever comes first. (These cases are not fast, + * but maximize aggregate expected throughput.) The AQS mechanics + * for doing this are straightforward. The lock state is held as + * AQS getState(). Read counts are negative; the write count (1) + * is positive. There are no signalling preferences among readers + * and writers. Since we don't need to export full Lock API, we + * just override the minimal AQS methods and use them directly. + */ + static final class TreeBin extends AbstractQueuedSynchronizer { + private static final long serialVersionUID = 2249069246763182397L; + transient TreeNode root; // root of tree + transient TreeNode first; // head of next-pointer list + + /* AQS overrides */ + public final boolean isHeldExclusively() { return getState() > 0; } + public final boolean tryAcquire(int ignore) { + if (compareAndSetState(0, 1)) { + setExclusiveOwnerThread(Thread.currentThread()); + return true; + } + return false; + } + public final boolean tryRelease(int ignore) { + setExclusiveOwnerThread(null); + setState(0); + return true; + } + public final int tryAcquireShared(int ignore) { + for (int c;;) { + if ((c = getState()) > 0) + return -1; + if (compareAndSetState(c, c -1)) + return 1; + } + } + public final boolean tryReleaseShared(int ignore) { + int c; + do {} while (!compareAndSetState(c = getState(), c + 1)); + return c == -1; + } + + /** From CLR */ + private void rotateLeft(TreeNode p) { + if (p != null) { + TreeNode r = p.right, pp, rl; + if ((rl = p.right = r.left) != null) + rl.parent = p; + if ((pp = r.parent = p.parent) == null) + root = r; + else if (pp.left == p) + pp.left = r; + else + pp.right = r; + r.left = p; + p.parent = r; + } + } + + /** From CLR */ + private void rotateRight(TreeNode p) { + if (p != null) { + TreeNode l = p.left, pp, lr; + if ((lr = p.left = l.right) != null) + lr.parent = p; + if ((pp = l.parent = p.parent) == null) + root = l; + else if (pp.right == p) + pp.right = l; + else + pp.left = l; + l.right = p; + p.parent = l; + } + } + + @SuppressWarnings("unchecked") final TreeNode getTreeNode + (int h, Object k, TreeNode p) { + return getTreeNode(h, (RubyObject)k, p); + } + + /** + * Returns the TreeNode (or null if not found) for the given key + * starting at given root. + */ + @SuppressWarnings("unchecked") final TreeNode getTreeNode + (int h, RubyObject k, TreeNode p) { + RubyClass c = k.getMetaClass(); boolean kNotComparable = !k.respondsTo("<=>"); + while (p != null) { + int dir, ph; RubyObject pk; RubyClass pc; + if ((ph = p.hash) == h) { + if ((pk = (RubyObject)p.key) == k || k.equals(pk)) + return p; + if (c != (pc = (RubyClass)pk.getMetaClass()) || + kNotComparable || + (dir = rubyCompare(k, pk)) == 0) { + dir = (c == pc) ? 0 : c.getName().compareTo(pc.getName()); + if (dir == 0) { // if still stuck, need to check both sides + TreeNode r = null, pl, pr; + // try to recurse on the right + if ((pr = p.right) != null && h >= pr.hash && (r = getTreeNode(h, k, pr)) != null) + return r; + // try to continue iterating on the left side + else if ((pl = p.left) != null && h <= pl.hash) + dir = -1; + else // no matching node found + return null; + } + } + } + else + dir = (h < ph) ? -1 : 1; + p = (dir > 0) ? p.right : p.left; + } + return null; + } + + int rubyCompare(RubyObject l, RubyObject r) { + ThreadContext context = l.getMetaClass().getRuntime().getCurrentContext(); + IRubyObject result; + try { + result = l.callMethod(context, "<=>", r); + } catch (RaiseException e) { + // handle objects "lying" about responding to <=>, ie: an Array containing non-comparable keys + if (context.runtime.getNoMethodError().isInstance(e.getException())) { + return 0; + } + throw e; + } + + return result.isNil() ? 0 : RubyNumeric.num2int(result.convertToInteger()); + } + + /** + * Wrapper for getTreeNode used by CHM.get. Tries to obtain + * read-lock to call getTreeNode, but during failure to get + * lock, searches along next links. + */ + final Object getValue(int h, Object k) { + Node r = null; + int c = getState(); // Must read lock state first + for (Node e = first; e != null; e = e.next) { + if (c <= 0 && compareAndSetState(c, c - 1)) { + try { + r = getTreeNode(h, k, root); + } finally { + releaseShared(0); + } + break; + } + else if ((e.hash & HASH_BITS) == h && k.equals(e.key)) { + r = e; + break; + } + else + c = getState(); + } + return r == null ? null : r.val; + } + + @SuppressWarnings("unchecked") final TreeNode putTreeNode + (int h, Object k, Object v) { + return putTreeNode(h, (RubyObject)k, v); + } + + /** + * Finds or adds a node. + * @return null if added + */ + @SuppressWarnings("unchecked") final TreeNode putTreeNode + (int h, RubyObject k, Object v) { + RubyClass c = k.getMetaClass(); + boolean kNotComparable = !k.respondsTo("<=>"); + TreeNode pp = root, p = null; + int dir = 0; + while (pp != null) { // find existing node or leaf to insert at + int ph; RubyObject pk; RubyClass pc; + p = pp; + if ((ph = p.hash) == h) { + if ((pk = (RubyObject)p.key) == k || k.equals(pk)) + return p; + if (c != (pc = pk.getMetaClass()) || + kNotComparable || + (dir = rubyCompare(k, pk)) == 0) { + dir = (c == pc) ? 0 : c.getName().compareTo(pc.getName()); + if (dir == 0) { // if still stuck, need to check both sides + TreeNode r = null, pr; + // try to recurse on the right + if ((pr = p.right) != null && h >= pr.hash && (r = getTreeNode(h, k, pr)) != null) + return r; + else // continue descending down the left subtree + dir = -1; + } + } + } + else + dir = (h < ph) ? -1 : 1; + pp = (dir > 0) ? p.right : p.left; + } + + TreeNode f = first; + TreeNode x = first = new TreeNode(h, (Object)k, v, f, p); + if (p == null) + root = x; + else { // attach and rebalance; adapted from CLR + TreeNode xp, xpp; + if (f != null) + f.prev = x; + if (dir <= 0) + p.left = x; + else + p.right = x; + x.red = true; + while (x != null && (xp = x.parent) != null && xp.red && + (xpp = xp.parent) != null) { + TreeNode xppl = xpp.left; + if (xp == xppl) { + TreeNode y = xpp.right; + if (y != null && y.red) { + y.red = false; + xp.red = false; + xpp.red = true; + x = xpp; + } + else { + if (x == xp.right) { + rotateLeft(x = xp); + xpp = (xp = x.parent) == null ? null : xp.parent; + } + if (xp != null) { + xp.red = false; + if (xpp != null) { + xpp.red = true; + rotateRight(xpp); + } + } + } + } + else { + TreeNode y = xppl; + if (y != null && y.red) { + y.red = false; + xp.red = false; + xpp.red = true; + x = xpp; + } + else { + if (x == xp.left) { + rotateRight(x = xp); + xpp = (xp = x.parent) == null ? null : xp.parent; + } + if (xp != null) { + xp.red = false; + if (xpp != null) { + xpp.red = true; + rotateLeft(xpp); + } + } + } + } + } + TreeNode r = root; + if (r != null && r.red) + r.red = false; + } + return null; + } + + /** + * Removes the given node, that must be present before this + * call. This is messier than typical red-black deletion code + * because we cannot swap the contents of an interior node + * with a leaf successor that is pinned by "next" pointers + * that are accessible independently of lock. So instead we + * swap the tree linkages. + */ + final void deleteTreeNode(TreeNode p) { + TreeNode next = (TreeNode)p.next; // unlink traversal pointers + TreeNode pred = p.prev; + if (pred == null) + first = next; + else + pred.next = next; + if (next != null) + next.prev = pred; + TreeNode replacement; + TreeNode pl = p.left; + TreeNode pr = p.right; + if (pl != null && pr != null) { + TreeNode s = pr, sl; + while ((sl = s.left) != null) // find successor + s = sl; + boolean c = s.red; s.red = p.red; p.red = c; // swap colors + TreeNode sr = s.right; + TreeNode pp = p.parent; + if (s == pr) { // p was s's direct parent + p.parent = s; + s.right = p; + } + else { + TreeNode sp = s.parent; + if ((p.parent = sp) != null) { + if (s == sp.left) + sp.left = p; + else + sp.right = p; + } + if ((s.right = pr) != null) + pr.parent = s; + } + p.left = null; + if ((p.right = sr) != null) + sr.parent = p; + if ((s.left = pl) != null) + pl.parent = s; + if ((s.parent = pp) == null) + root = s; + else if (p == pp.left) + pp.left = s; + else + pp.right = s; + replacement = sr; + } + else + replacement = (pl != null) ? pl : pr; + TreeNode pp = p.parent; + if (replacement == null) { + if (pp == null) { + root = null; + return; + } + replacement = p; + } + else { + replacement.parent = pp; + if (pp == null) + root = replacement; + else if (p == pp.left) + pp.left = replacement; + else + pp.right = replacement; + p.left = p.right = p.parent = null; + } + if (!p.red) { // rebalance, from CLR + TreeNode x = replacement; + while (x != null) { + TreeNode xp, xpl; + if (x.red || (xp = x.parent) == null) { + x.red = false; + break; + } + if (x == (xpl = xp.left)) { + TreeNode sib = xp.right; + if (sib != null && sib.red) { + sib.red = false; + xp.red = true; + rotateLeft(xp); + sib = (xp = x.parent) == null ? null : xp.right; + } + if (sib == null) + x = xp; + else { + TreeNode sl = sib.left, sr = sib.right; + if ((sr == null || !sr.red) && + (sl == null || !sl.red)) { + sib.red = true; + x = xp; + } + else { + if (sr == null || !sr.red) { + if (sl != null) + sl.red = false; + sib.red = true; + rotateRight(sib); + sib = (xp = x.parent) == null ? null : xp.right; + } + if (sib != null) { + sib.red = (xp == null) ? false : xp.red; + if ((sr = sib.right) != null) + sr.red = false; + } + if (xp != null) { + xp.red = false; + rotateLeft(xp); + } + x = root; + } + } + } + else { // symmetric + TreeNode sib = xpl; + if (sib != null && sib.red) { + sib.red = false; + xp.red = true; + rotateRight(xp); + sib = (xp = x.parent) == null ? null : xp.left; + } + if (sib == null) + x = xp; + else { + TreeNode sl = sib.left, sr = sib.right; + if ((sl == null || !sl.red) && + (sr == null || !sr.red)) { + sib.red = true; + x = xp; + } + else { + if (sl == null || !sl.red) { + if (sr != null) + sr.red = false; + sib.red = true; + rotateLeft(sib); + sib = (xp = x.parent) == null ? null : xp.left; + } + if (sib != null) { + sib.red = (xp == null) ? false : xp.red; + if ((sl = sib.left) != null) + sl.red = false; + } + if (xp != null) { + xp.red = false; + rotateRight(xp); + } + x = root; + } + } + } + } + } + if (p == replacement && (pp = p.parent) != null) { + if (p == pp.left) // detach pointers + pp.left = null; + else if (p == pp.right) + pp.right = null; + p.parent = null; + } + } + } + + /* ---------------- Collision reduction methods -------------- */ + + /** + * Spreads higher bits to lower, and also forces top 2 bits to 0. + * Because the table uses power-of-two masking, sets of hashes + * that vary only in bits above the current mask will always + * collide. (Among known examples are sets of Float keys holding + * consecutive whole numbers in small tables.) To counter this, + * we apply a transform that spreads the impact of higher bits + * downward. There is a tradeoff between speed, utility, and + * quality of bit-spreading. Because many common sets of hashes + * are already reasonably distributed across bits (so don't benefit + * from spreading), and because we use trees to handle large sets + * of collisions in bins, we don't need excessively high quality. + */ + private static final int spread(int h) { + h ^= (h >>> 18) ^ (h >>> 12); + return (h ^ (h >>> 10)) & HASH_BITS; + } + + /** + * Replaces a list bin with a tree bin. Call only when locked. + * Fails to replace if the given key is non-comparable or table + * is, or needs, resizing. + */ + private final void replaceWithTreeBin(Node[] tab, int index, Object key) { + if ((key instanceof Comparable) && + (tab.length >= MAXIMUM_CAPACITY || counter.sum() < (long)sizeCtl)) { + TreeBin t = new TreeBin(); + for (Node e = tabAt(tab, index); e != null; e = e.next) + t.putTreeNode(e.hash & HASH_BITS, e.key, e.val); + setTabAt(tab, index, new Node(MOVED, t, null, null)); + } + } + + /* ---------------- Internal access and update methods -------------- */ + + /** Implementation for get and containsKey */ + private final Object internalGet(Object k) { + int h = spread(k.hashCode()); + retry: for (Node[] tab = table; tab != null;) { + Node e, p; Object ek, ev; int eh; // locals to read fields once + for (e = tabAt(tab, (tab.length - 1) & h); e != null; e = e.next) { + if ((eh = e.hash) == MOVED) { + if ((ek = e.key) instanceof TreeBin) // search TreeBin + return ((TreeBin)ek).getValue(h, k); + else { // restart with new table + tab = (Node[])ek; + continue retry; + } + } + else if ((eh & HASH_BITS) == h && (ev = e.val) != null && + ((ek = e.key) == k || k.equals(ek))) + return ev; + } + break; + } + return null; + } + + /** + * Implementation for the four public remove/replace methods: + * Replaces node value with v, conditional upon match of cv if + * non-null. If resulting value is null, delete. + */ + private final Object internalReplace(Object k, Object v, Object cv) { + int h = spread(k.hashCode()); + Object oldVal = null; + for (Node[] tab = table;;) { + Node f; int i, fh; Object fk; + if (tab == null || + (f = tabAt(tab, i = (tab.length - 1) & h)) == null) + break; + else if ((fh = f.hash) == MOVED) { + if ((fk = f.key) instanceof TreeBin) { + TreeBin t = (TreeBin)fk; + boolean validated = false; + boolean deleted = false; + t.acquire(0); + try { + if (tabAt(tab, i) == f) { + validated = true; + TreeNode p = t.getTreeNode(h, k, t.root); + if (p != null) { + Object pv = p.val; + if (cv == null || cv == pv || cv.equals(pv)) { + oldVal = pv; + if ((p.val = v) == null) { + deleted = true; + t.deleteTreeNode(p); + } + } + } + } + } finally { + t.release(0); + } + if (validated) { + if (deleted) + counter.add(-1L); + break; + } + } + else + tab = (Node[])fk; + } + else if ((fh & HASH_BITS) != h && f.next == null) // precheck + break; // rules out possible existence + else if ((fh & LOCKED) != 0) { + checkForResize(); // try resizing if can't get lock + f.tryAwaitLock(tab, i); + } + else if (f.casHash(fh, fh | LOCKED)) { + boolean validated = false; + boolean deleted = false; + try { + if (tabAt(tab, i) == f) { + validated = true; + for (Node e = f, pred = null;;) { + Object ek, ev; + if ((e.hash & HASH_BITS) == h && + ((ev = e.val) != null) && + ((ek = e.key) == k || k.equals(ek))) { + if (cv == null || cv == ev || cv.equals(ev)) { + oldVal = ev; + if ((e.val = v) == null) { + deleted = true; + Node en = e.next; + if (pred != null) + pred.next = en; + else + setTabAt(tab, i, en); + } + } + break; + } + pred = e; + if ((e = e.next) == null) + break; + } + } + } finally { + if (!f.casHash(fh | LOCKED, fh)) { + f.hash = fh; + synchronized (f) { f.notifyAll(); }; + } + } + if (validated) { + if (deleted) + counter.add(-1L); + break; + } + } + } + return oldVal; + } + + /* + * Internal versions of the six insertion methods, each a + * little more complicated than the last. All have + * the same basic structure as the first (internalPut): + * 1. If table uninitialized, create + * 2. If bin empty, try to CAS new node + * 3. If bin stale, use new table + * 4. if bin converted to TreeBin, validate and relay to TreeBin methods + * 5. Lock and validate; if valid, scan and add or update + * + * The others interweave other checks and/or alternative actions: + * * Plain put checks for and performs resize after insertion. + * * putIfAbsent prescans for mapping without lock (and fails to add + * if present), which also makes pre-emptive resize checks worthwhile. + * * computeIfAbsent extends form used in putIfAbsent with additional + * mechanics to deal with, calls, potential exceptions and null + * returns from function call. + * * compute uses the same function-call mechanics, but without + * the prescans + * * merge acts as putIfAbsent in the absent case, but invokes the + * update function if present + * * putAll attempts to pre-allocate enough table space + * and more lazily performs count updates and checks. + * + * Someday when details settle down a bit more, it might be worth + * some factoring to reduce sprawl. + */ + + /** Implementation for put */ + private final Object internalPut(Object k, Object v) { + int h = spread(k.hashCode()); + int count = 0; + for (Node[] tab = table;;) { + int i; Node f; int fh; Object fk; + if (tab == null) + tab = initTable(); + else if ((f = tabAt(tab, i = (tab.length - 1) & h)) == null) { + if (casTabAt(tab, i, null, new Node(h, k, v, null))) + break; // no lock when adding to empty bin + } + else if ((fh = f.hash) == MOVED) { + if ((fk = f.key) instanceof TreeBin) { + TreeBin t = (TreeBin)fk; + Object oldVal = null; + t.acquire(0); + try { + if (tabAt(tab, i) == f) { + count = 2; + TreeNode p = t.putTreeNode(h, k, v); + if (p != null) { + oldVal = p.val; + p.val = v; + } + } + } finally { + t.release(0); + } + if (count != 0) { + if (oldVal != null) + return oldVal; + break; + } + } + else + tab = (Node[])fk; + } + else if ((fh & LOCKED) != 0) { + checkForResize(); + f.tryAwaitLock(tab, i); + } + else if (f.casHash(fh, fh | LOCKED)) { + Object oldVal = null; + try { // needed in case equals() throws + if (tabAt(tab, i) == f) { + count = 1; + for (Node e = f;; ++count) { + Object ek, ev; + if ((e.hash & HASH_BITS) == h && + (ev = e.val) != null && + ((ek = e.key) == k || k.equals(ek))) { + oldVal = ev; + e.val = v; + break; + } + Node last = e; + if ((e = e.next) == null) { + last.next = new Node(h, k, v, null); + if (count >= TREE_THRESHOLD) + replaceWithTreeBin(tab, i, k); + break; + } + } + } + } finally { // unlock and signal if needed + if (!f.casHash(fh | LOCKED, fh)) { + f.hash = fh; + synchronized (f) { f.notifyAll(); }; + } + } + if (count != 0) { + if (oldVal != null) + return oldVal; + if (tab.length <= 64) + count = 2; + break; + } + } + } + counter.add(1L); + if (count > 1) + checkForResize(); + return null; + } + + /** Implementation for putIfAbsent */ + private final Object internalPutIfAbsent(Object k, Object v) { + int h = spread(k.hashCode()); + int count = 0; + for (Node[] tab = table;;) { + int i; Node f; int fh; Object fk, fv; + if (tab == null) + tab = initTable(); + else if ((f = tabAt(tab, i = (tab.length - 1) & h)) == null) { + if (casTabAt(tab, i, null, new Node(h, k, v, null))) + break; + } + else if ((fh = f.hash) == MOVED) { + if ((fk = f.key) instanceof TreeBin) { + TreeBin t = (TreeBin)fk; + Object oldVal = null; + t.acquire(0); + try { + if (tabAt(tab, i) == f) { + count = 2; + TreeNode p = t.putTreeNode(h, k, v); + if (p != null) + oldVal = p.val; + } + } finally { + t.release(0); + } + if (count != 0) { + if (oldVal != null) + return oldVal; + break; + } + } + else + tab = (Node[])fk; + } + else if ((fh & HASH_BITS) == h && (fv = f.val) != null && + ((fk = f.key) == k || k.equals(fk))) + return fv; + else { + Node g = f.next; + if (g != null) { // at least 2 nodes -- search and maybe resize + for (Node e = g;;) { + Object ek, ev; + if ((e.hash & HASH_BITS) == h && (ev = e.val) != null && + ((ek = e.key) == k || k.equals(ek))) + return ev; + if ((e = e.next) == null) { + checkForResize(); + break; + } + } + } + if (((fh = f.hash) & LOCKED) != 0) { + checkForResize(); + f.tryAwaitLock(tab, i); + } + else if (tabAt(tab, i) == f && f.casHash(fh, fh | LOCKED)) { + Object oldVal = null; + try { + if (tabAt(tab, i) == f) { + count = 1; + for (Node e = f;; ++count) { + Object ek, ev; + if ((e.hash & HASH_BITS) == h && + (ev = e.val) != null && + ((ek = e.key) == k || k.equals(ek))) { + oldVal = ev; + break; + } + Node last = e; + if ((e = e.next) == null) { + last.next = new Node(h, k, v, null); + if (count >= TREE_THRESHOLD) + replaceWithTreeBin(tab, i, k); + break; + } + } + } + } finally { + if (!f.casHash(fh | LOCKED, fh)) { + f.hash = fh; + synchronized (f) { f.notifyAll(); }; + } + } + if (count != 0) { + if (oldVal != null) + return oldVal; + if (tab.length <= 64) + count = 2; + break; + } + } + } + } + counter.add(1L); + if (count > 1) + checkForResize(); + return null; + } + + /** Implementation for computeIfAbsent */ + private final Object internalComputeIfAbsent(K k, + Fun mf) { + int h = spread(k.hashCode()); + Object val = null; + int count = 0; + for (Node[] tab = table;;) { + Node f; int i, fh; Object fk, fv; + if (tab == null) + tab = initTable(); + else if ((f = tabAt(tab, i = (tab.length - 1) & h)) == null) { + Node node = new Node(fh = h | LOCKED, k, null, null); + if (casTabAt(tab, i, null, node)) { + count = 1; + try { + if ((val = mf.apply(k)) != null) + node.val = val; + } finally { + if (val == null) + setTabAt(tab, i, null); + if (!node.casHash(fh, h)) { + node.hash = h; + synchronized (node) { node.notifyAll(); }; + } + } + } + if (count != 0) + break; + } + else if ((fh = f.hash) == MOVED) { + if ((fk = f.key) instanceof TreeBin) { + TreeBin t = (TreeBin)fk; + boolean added = false; + t.acquire(0); + try { + if (tabAt(tab, i) == f) { + count = 1; + TreeNode p = t.getTreeNode(h, k, t.root); + if (p != null) + val = p.val; + else if ((val = mf.apply(k)) != null) { + added = true; + count = 2; + t.putTreeNode(h, k, val); + } + } + } finally { + t.release(0); + } + if (count != 0) { + if (!added) + return val; + break; + } + } + else + tab = (Node[])fk; + } + else if ((fh & HASH_BITS) == h && (fv = f.val) != null && + ((fk = f.key) == k || k.equals(fk))) + return fv; + else { + Node g = f.next; + if (g != null) { + for (Node e = g;;) { + Object ek, ev; + if ((e.hash & HASH_BITS) == h && (ev = e.val) != null && + ((ek = e.key) == k || k.equals(ek))) + return ev; + if ((e = e.next) == null) { + checkForResize(); + break; + } + } + } + if (((fh = f.hash) & LOCKED) != 0) { + checkForResize(); + f.tryAwaitLock(tab, i); + } + else if (tabAt(tab, i) == f && f.casHash(fh, fh | LOCKED)) { + boolean added = false; + try { + if (tabAt(tab, i) == f) { + count = 1; + for (Node e = f;; ++count) { + Object ek, ev; + if ((e.hash & HASH_BITS) == h && + (ev = e.val) != null && + ((ek = e.key) == k || k.equals(ek))) { + val = ev; + break; + } + Node last = e; + if ((e = e.next) == null) { + if ((val = mf.apply(k)) != null) { + added = true; + last.next = new Node(h, k, val, null); + if (count >= TREE_THRESHOLD) + replaceWithTreeBin(tab, i, k); + } + break; + } + } + } + } finally { + if (!f.casHash(fh | LOCKED, fh)) { + f.hash = fh; + synchronized (f) { f.notifyAll(); }; + } + } + if (count != 0) { + if (!added) + return val; + if (tab.length <= 64) + count = 2; + break; + } + } + } + } + if (val != null) { + counter.add(1L); + if (count > 1) + checkForResize(); + } + return val; + } + + /** Implementation for compute */ + @SuppressWarnings("unchecked") private final Object internalCompute + (K k, boolean onlyIfPresent, BiFun mf) { + int h = spread(k.hashCode()); + Object val = null; + int delta = 0; + int count = 0; + for (Node[] tab = table;;) { + Node f; int i, fh; Object fk; + if (tab == null) + tab = initTable(); + else if ((f = tabAt(tab, i = (tab.length - 1) & h)) == null) { + if (onlyIfPresent) + break; + Node node = new Node(fh = h | LOCKED, k, null, null); + if (casTabAt(tab, i, null, node)) { + try { + count = 1; + if ((val = mf.apply(k, null)) != null) { + node.val = val; + delta = 1; + } + } finally { + if (delta == 0) + setTabAt(tab, i, null); + if (!node.casHash(fh, h)) { + node.hash = h; + synchronized (node) { node.notifyAll(); }; + } + } + } + if (count != 0) + break; + } + else if ((fh = f.hash) == MOVED) { + if ((fk = f.key) instanceof TreeBin) { + TreeBin t = (TreeBin)fk; + t.acquire(0); + try { + if (tabAt(tab, i) == f) { + count = 1; + TreeNode p = t.getTreeNode(h, k, t.root); + Object pv; + if (p == null) { + if (onlyIfPresent) + break; + pv = null; + } else + pv = p.val; + if ((val = mf.apply(k, (V)pv)) != null) { + if (p != null) + p.val = val; + else { + count = 2; + delta = 1; + t.putTreeNode(h, k, val); + } + } + else if (p != null) { + delta = -1; + t.deleteTreeNode(p); + } + } + } finally { + t.release(0); + } + if (count != 0) + break; + } + else + tab = (Node[])fk; + } + else if ((fh & LOCKED) != 0) { + checkForResize(); + f.tryAwaitLock(tab, i); + } + else if (f.casHash(fh, fh | LOCKED)) { + try { + if (tabAt(tab, i) == f) { + count = 1; + for (Node e = f, pred = null;; ++count) { + Object ek, ev; + if ((e.hash & HASH_BITS) == h && + (ev = e.val) != null && + ((ek = e.key) == k || k.equals(ek))) { + val = mf.apply(k, (V)ev); + if (val != null) + e.val = val; + else { + delta = -1; + Node en = e.next; + if (pred != null) + pred.next = en; + else + setTabAt(tab, i, en); + } + break; + } + pred = e; + if ((e = e.next) == null) { + if (!onlyIfPresent && (val = mf.apply(k, null)) != null) { + pred.next = new Node(h, k, val, null); + delta = 1; + if (count >= TREE_THRESHOLD) + replaceWithTreeBin(tab, i, k); + } + break; + } + } + } + } finally { + if (!f.casHash(fh | LOCKED, fh)) { + f.hash = fh; + synchronized (f) { f.notifyAll(); }; + } + } + if (count != 0) { + if (tab.length <= 64) + count = 2; + break; + } + } + } + if (delta != 0) { + counter.add((long)delta); + if (count > 1) + checkForResize(); + } + return val; + } + + /** Implementation for merge */ + @SuppressWarnings("unchecked") private final Object internalMerge + (K k, V v, BiFun mf) { + int h = spread(k.hashCode()); + Object val = null; + int delta = 0; + int count = 0; + for (Node[] tab = table;;) { + int i; Node f; int fh; Object fk, fv; + if (tab == null) + tab = initTable(); + else if ((f = tabAt(tab, i = (tab.length - 1) & h)) == null) { + if (casTabAt(tab, i, null, new Node(h, k, v, null))) { + delta = 1; + val = v; + break; + } + } + else if ((fh = f.hash) == MOVED) { + if ((fk = f.key) instanceof TreeBin) { + TreeBin t = (TreeBin)fk; + t.acquire(0); + try { + if (tabAt(tab, i) == f) { + count = 1; + TreeNode p = t.getTreeNode(h, k, t.root); + val = (p == null) ? v : mf.apply((V)p.val, v); + if (val != null) { + if (p != null) + p.val = val; + else { + count = 2; + delta = 1; + t.putTreeNode(h, k, val); + } + } + else if (p != null) { + delta = -1; + t.deleteTreeNode(p); + } + } + } finally { + t.release(0); + } + if (count != 0) + break; + } + else + tab = (Node[])fk; + } + else if ((fh & LOCKED) != 0) { + checkForResize(); + f.tryAwaitLock(tab, i); + } + else if (f.casHash(fh, fh | LOCKED)) { + try { + if (tabAt(tab, i) == f) { + count = 1; + for (Node e = f, pred = null;; ++count) { + Object ek, ev; + if ((e.hash & HASH_BITS) == h && + (ev = e.val) != null && + ((ek = e.key) == k || k.equals(ek))) { + val = mf.apply((V)ev, v); + if (val != null) + e.val = val; + else { + delta = -1; + Node en = e.next; + if (pred != null) + pred.next = en; + else + setTabAt(tab, i, en); + } + break; + } + pred = e; + if ((e = e.next) == null) { + val = v; + pred.next = new Node(h, k, val, null); + delta = 1; + if (count >= TREE_THRESHOLD) + replaceWithTreeBin(tab, i, k); + break; + } + } + } + } finally { + if (!f.casHash(fh | LOCKED, fh)) { + f.hash = fh; + synchronized (f) { f.notifyAll(); }; + } + } + if (count != 0) { + if (tab.length <= 64) + count = 2; + break; + } + } + } + if (delta != 0) { + counter.add((long)delta); + if (count > 1) + checkForResize(); + } + return val; + } + + /** Implementation for putAll */ + private final void internalPutAll(Map m) { + tryPresize(m.size()); + long delta = 0L; // number of uncommitted additions + boolean npe = false; // to throw exception on exit for nulls + try { // to clean up counts on other exceptions + for (Map.Entry entry : m.entrySet()) { + Object k, v; + if (entry == null || (k = entry.getKey()) == null || + (v = entry.getValue()) == null) { + npe = true; + break; + } + int h = spread(k.hashCode()); + for (Node[] tab = table;;) { + int i; Node f; int fh; Object fk; + if (tab == null) + tab = initTable(); + else if ((f = tabAt(tab, i = (tab.length - 1) & h)) == null){ + if (casTabAt(tab, i, null, new Node(h, k, v, null))) { + ++delta; + break; + } + } + else if ((fh = f.hash) == MOVED) { + if ((fk = f.key) instanceof TreeBin) { + TreeBin t = (TreeBin)fk; + boolean validated = false; + t.acquire(0); + try { + if (tabAt(tab, i) == f) { + validated = true; + TreeNode p = t.getTreeNode(h, k, t.root); + if (p != null) + p.val = v; + else { + t.putTreeNode(h, k, v); + ++delta; + } + } + } finally { + t.release(0); + } + if (validated) + break; + } + else + tab = (Node[])fk; + } + else if ((fh & LOCKED) != 0) { + counter.add(delta); + delta = 0L; + checkForResize(); + f.tryAwaitLock(tab, i); + } + else if (f.casHash(fh, fh | LOCKED)) { + int count = 0; + try { + if (tabAt(tab, i) == f) { + count = 1; + for (Node e = f;; ++count) { + Object ek, ev; + if ((e.hash & HASH_BITS) == h && + (ev = e.val) != null && + ((ek = e.key) == k || k.equals(ek))) { + e.val = v; + break; + } + Node last = e; + if ((e = e.next) == null) { + ++delta; + last.next = new Node(h, k, v, null); + if (count >= TREE_THRESHOLD) + replaceWithTreeBin(tab, i, k); + break; + } + } + } + } finally { + if (!f.casHash(fh | LOCKED, fh)) { + f.hash = fh; + synchronized (f) { f.notifyAll(); }; + } + } + if (count != 0) { + if (count > 1) { + counter.add(delta); + delta = 0L; + checkForResize(); + } + break; + } + } + } + } + } finally { + if (delta != 0) + counter.add(delta); + } + if (npe) + throw new NullPointerException(); + } + + /* ---------------- Table Initialization and Resizing -------------- */ + + /** + * Returns a power of two table size for the given desired capacity. + * See Hackers Delight, sec 3.2 + */ + private static final int tableSizeFor(int c) { + int n = c - 1; + n |= n >>> 1; + n |= n >>> 2; + n |= n >>> 4; + n |= n >>> 8; + n |= n >>> 16; + return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1; + } + + /** + * Initializes table, using the size recorded in sizeCtl. + */ + private final Node[] initTable() { + Node[] tab; int sc; + while ((tab = table) == null) { + if ((sc = sizeCtl) < 0) + Thread.yield(); // lost initialization race; just spin + else if (UNSAFE.compareAndSwapInt(this, sizeCtlOffset, sc, -1)) { + try { + if ((tab = table) == null) { + int n = (sc > 0) ? sc : DEFAULT_CAPACITY; + tab = table = new Node[n]; + sc = n - (n >>> 2); + } + } finally { + sizeCtl = sc; + } + break; + } + } + return tab; + } + + /** + * If table is too small and not already resizing, creates next + * table and transfers bins. Rechecks occupancy after a transfer + * to see if another resize is already needed because resizings + * are lagging additions. + */ + private final void checkForResize() { + Node[] tab; int n, sc; + while ((tab = table) != null && + (n = tab.length) < MAXIMUM_CAPACITY && + (sc = sizeCtl) >= 0 && counter.sum() >= (long)sc && + UNSAFE.compareAndSwapInt(this, sizeCtlOffset, sc, -1)) { + try { + if (tab == table) { + table = rebuild(tab); + sc = (n << 1) - (n >>> 1); + } + } finally { + sizeCtl = sc; + } + } + } + + /** + * Tries to presize table to accommodate the given number of elements. + * + * @param size number of elements (doesn't need to be perfectly accurate) + */ + private final void tryPresize(int size) { + int c = (size >= (MAXIMUM_CAPACITY >>> 1)) ? MAXIMUM_CAPACITY : + tableSizeFor(size + (size >>> 1) + 1); + int sc; + while ((sc = sizeCtl) >= 0) { + Node[] tab = table; int n; + if (tab == null || (n = tab.length) == 0) { + n = (sc > c) ? sc : c; + if (UNSAFE.compareAndSwapInt(this, sizeCtlOffset, sc, -1)) { + try { + if (table == tab) { + table = new Node[n]; + sc = n - (n >>> 2); + } + } finally { + sizeCtl = sc; + } + } + } + else if (c <= sc || n >= MAXIMUM_CAPACITY) + break; + else if (UNSAFE.compareAndSwapInt(this, sizeCtlOffset, sc, -1)) { + try { + if (table == tab) { + table = rebuild(tab); + sc = (n << 1) - (n >>> 1); + } + } finally { + sizeCtl = sc; + } + } + } + } + + /* + * Moves and/or copies the nodes in each bin to new table. See + * above for explanation. + * + * @return the new table + */ + private static final Node[] rebuild(Node[] tab) { + int n = tab.length; + Node[] nextTab = new Node[n << 1]; + Node fwd = new Node(MOVED, nextTab, null, null); + int[] buffer = null; // holds bins to revisit; null until needed + Node rev = null; // reverse forwarder; null until needed + int nbuffered = 0; // the number of bins in buffer list + int bufferIndex = 0; // buffer index of current buffered bin + int bin = n - 1; // current non-buffered bin or -1 if none + + for (int i = bin;;) { // start upwards sweep + int fh; Node f; + if ((f = tabAt(tab, i)) == null) { + if (bin >= 0) { // Unbuffered; no lock needed (or available) + if (!casTabAt(tab, i, f, fwd)) + continue; + } + else { // transiently use a locked forwarding node + Node g = new Node(MOVED|LOCKED, nextTab, null, null); + if (!casTabAt(tab, i, f, g)) + continue; + setTabAt(nextTab, i, null); + setTabAt(nextTab, i + n, null); + setTabAt(tab, i, fwd); + if (!g.casHash(MOVED|LOCKED, MOVED)) { + g.hash = MOVED; + synchronized (g) { g.notifyAll(); } + } + } + } + else if ((fh = f.hash) == MOVED) { + Object fk = f.key; + if (fk instanceof TreeBin) { + TreeBin t = (TreeBin)fk; + boolean validated = false; + t.acquire(0); + try { + if (tabAt(tab, i) == f) { + validated = true; + splitTreeBin(nextTab, i, t); + setTabAt(tab, i, fwd); + } + } finally { + t.release(0); + } + if (!validated) + continue; + } + } + else if ((fh & LOCKED) == 0 && f.casHash(fh, fh|LOCKED)) { + boolean validated = false; + try { // split to lo and hi lists; copying as needed + if (tabAt(tab, i) == f) { + validated = true; + splitBin(nextTab, i, f); + setTabAt(tab, i, fwd); + } + } finally { + if (!f.casHash(fh | LOCKED, fh)) { + f.hash = fh; + synchronized (f) { f.notifyAll(); }; + } + } + if (!validated) + continue; + } + else { + if (buffer == null) // initialize buffer for revisits + buffer = new int[TRANSFER_BUFFER_SIZE]; + if (bin < 0 && bufferIndex > 0) { + int j = buffer[--bufferIndex]; + buffer[bufferIndex] = i; + i = j; // swap with another bin + continue; + } + if (bin < 0 || nbuffered >= TRANSFER_BUFFER_SIZE) { + f.tryAwaitLock(tab, i); + continue; // no other options -- block + } + if (rev == null) // initialize reverse-forwarder + rev = new Node(MOVED, tab, null, null); + if (tabAt(tab, i) != f || (f.hash & LOCKED) == 0) + continue; // recheck before adding to list + buffer[nbuffered++] = i; + setTabAt(nextTab, i, rev); // install place-holders + setTabAt(nextTab, i + n, rev); + } + + if (bin > 0) + i = --bin; + else if (buffer != null && nbuffered > 0) { + bin = -1; + i = buffer[bufferIndex = --nbuffered]; + } + else + return nextTab; + } + } + + /** + * Splits a normal bin with list headed by e into lo and hi parts; + * installs in given table. + */ + private static void splitBin(Node[] nextTab, int i, Node e) { + int bit = nextTab.length >>> 1; // bit to split on + int runBit = e.hash & bit; + Node lastRun = e, lo = null, hi = null; + for (Node p = e.next; p != null; p = p.next) { + int b = p.hash & bit; + if (b != runBit) { + runBit = b; + lastRun = p; + } + } + if (runBit == 0) + lo = lastRun; + else + hi = lastRun; + for (Node p = e; p != lastRun; p = p.next) { + int ph = p.hash & HASH_BITS; + Object pk = p.key, pv = p.val; + if ((ph & bit) == 0) + lo = new Node(ph, pk, pv, lo); + else + hi = new Node(ph, pk, pv, hi); + } + setTabAt(nextTab, i, lo); + setTabAt(nextTab, i + bit, hi); + } + + /** + * Splits a tree bin into lo and hi parts; installs in given table. + */ + private static void splitTreeBin(Node[] nextTab, int i, TreeBin t) { + int bit = nextTab.length >>> 1; + TreeBin lt = new TreeBin(); + TreeBin ht = new TreeBin(); + int lc = 0, hc = 0; + for (Node e = t.first; e != null; e = e.next) { + int h = e.hash & HASH_BITS; + Object k = e.key, v = e.val; + if ((h & bit) == 0) { + ++lc; + lt.putTreeNode(h, k, v); + } + else { + ++hc; + ht.putTreeNode(h, k, v); + } + } + Node ln, hn; // throw away trees if too small + if (lc <= (TREE_THRESHOLD >>> 1)) { + ln = null; + for (Node p = lt.first; p != null; p = p.next) + ln = new Node(p.hash, p.key, p.val, ln); + } + else + ln = new Node(MOVED, lt, null, null); + setTabAt(nextTab, i, ln); + if (hc <= (TREE_THRESHOLD >>> 1)) { + hn = null; + for (Node p = ht.first; p != null; p = p.next) + hn = new Node(p.hash, p.key, p.val, hn); + } + else + hn = new Node(MOVED, ht, null, null); + setTabAt(nextTab, i + bit, hn); + } + + /** + * Implementation for clear. Steps through each bin, removing all + * nodes. + */ + private final void internalClear() { + long delta = 0L; // negative number of deletions + int i = 0; + Node[] tab = table; + while (tab != null && i < tab.length) { + int fh; Object fk; + Node f = tabAt(tab, i); + if (f == null) + ++i; + else if ((fh = f.hash) == MOVED) { + if ((fk = f.key) instanceof TreeBin) { + TreeBin t = (TreeBin)fk; + t.acquire(0); + try { + if (tabAt(tab, i) == f) { + for (Node p = t.first; p != null; p = p.next) { + if (p.val != null) { // (currently always true) + p.val = null; + --delta; + } + } + t.first = null; + t.root = null; + ++i; + } + } finally { + t.release(0); + } + } + else + tab = (Node[])fk; + } + else if ((fh & LOCKED) != 0) { + counter.add(delta); // opportunistically update count + delta = 0L; + f.tryAwaitLock(tab, i); + } + else if (f.casHash(fh, fh | LOCKED)) { + try { + if (tabAt(tab, i) == f) { + for (Node e = f; e != null; e = e.next) { + if (e.val != null) { // (currently always true) + e.val = null; + --delta; + } + } + setTabAt(tab, i, null); + ++i; + } + } finally { + if (!f.casHash(fh | LOCKED, fh)) { + f.hash = fh; + synchronized (f) { f.notifyAll(); }; + } + } + } + } + if (delta != 0) + counter.add(delta); + } + + /* ----------------Table Traversal -------------- */ + + /** + * Encapsulates traversal for methods such as containsValue; also + * serves as a base class for other iterators and bulk tasks. + * + * At each step, the iterator snapshots the key ("nextKey") and + * value ("nextVal") of a valid node (i.e., one that, at point of + * snapshot, has a non-null user value). Because val fields can + * change (including to null, indicating deletion), field nextVal + * might not be accurate at point of use, but still maintains the + * weak consistency property of holding a value that was once + * valid. To support iterator.remove, the nextKey field is not + * updated (nulled out) when the iterator cannot advance. + * + * Internal traversals directly access these fields, as in: + * {@code while (it.advance() != null) { process(it.nextKey); }} + * + * Exported iterators must track whether the iterator has advanced + * (in hasNext vs next) (by setting/checking/nulling field + * nextVal), and then extract key, value, or key-value pairs as + * return values of next(). + * + * The iterator visits once each still-valid node that was + * reachable upon iterator construction. It might miss some that + * were added to a bin after the bin was visited, which is OK wrt + * consistency guarantees. Maintaining this property in the face + * of possible ongoing resizes requires a fair amount of + * bookkeeping state that is difficult to optimize away amidst + * volatile accesses. Even so, traversal maintains reasonable + * throughput. + * + * Normally, iteration proceeds bin-by-bin traversing lists. + * However, if the table has been resized, then all future steps + * must traverse both the bin at the current index as well as at + * (index + baseSize); and so on for further resizings. To + * paranoically cope with potential sharing by users of iterators + * across threads, iteration terminates if a bounds checks fails + * for a table read. + * + * This class extends ForkJoinTask to streamline parallel + * iteration in bulk operations (see BulkTask). This adds only an + * int of space overhead, which is close enough to negligible in + * cases where it is not needed to not worry about it. Because + * ForkJoinTask is Serializable, but iterators need not be, we + * need to add warning suppressions. + */ + @SuppressWarnings("serial") static class Traverser { + final ConcurrentHashMapV8 map; + Node next; // the next entry to use + K nextKey; // cached key field of next + V nextVal; // cached val field of next + Node[] tab; // current table; updated if resized + int index; // index of bin to use next + int baseIndex; // current index of initial table + int baseLimit; // index bound for initial table + int baseSize; // initial table size + + /** Creates iterator for all entries in the table. */ + Traverser(ConcurrentHashMapV8 map) { + this.map = map; + } + + /** Creates iterator for split() methods */ + Traverser(Traverser it) { + ConcurrentHashMapV8 m; Node[] t; + if ((m = this.map = it.map) == null) + t = null; + else if ((t = it.tab) == null && // force parent tab initialization + (t = it.tab = m.table) != null) + it.baseLimit = it.baseSize = t.length; + this.tab = t; + this.baseSize = it.baseSize; + it.baseLimit = this.index = this.baseIndex = + ((this.baseLimit = it.baseLimit) + it.baseIndex + 1) >>> 1; + } + + /** + * Advances next; returns nextVal or null if terminated. + * See above for explanation. + */ + final V advance() { + Node e = next; + V ev = null; + outer: do { + if (e != null) // advance past used/skipped node + e = e.next; + while (e == null) { // get to next non-null bin + ConcurrentHashMapV8 m; + Node[] t; int b, i, n; Object ek; // checks must use locals + if ((t = tab) != null) + n = t.length; + else if ((m = map) != null && (t = tab = m.table) != null) + n = baseLimit = baseSize = t.length; + else + break outer; + if ((b = baseIndex) >= baseLimit || + (i = index) < 0 || i >= n) + break outer; + if ((e = tabAt(t, i)) != null && e.hash == MOVED) { + if ((ek = e.key) instanceof TreeBin) + e = ((TreeBin)ek).first; + else { + tab = (Node[])ek; + continue; // restarts due to null val + } + } // visit upper slots if present + index = (i += baseSize) < n ? i : (baseIndex = b + 1); + } + nextKey = (K) e.key; + } while ((ev = (V) e.val) == null); // skip deleted or special nodes + next = e; + return nextVal = ev; + } + + public final void remove() { + Object k = nextKey; + if (k == null && (advance() == null || (k = nextKey) == null)) + throw new IllegalStateException(); + map.internalReplace(k, null, null); + } + + public final boolean hasNext() { + return nextVal != null || advance() != null; + } + + public final boolean hasMoreElements() { return hasNext(); } + public final void setRawResult(Object x) { } + public R getRawResult() { return null; } + public boolean exec() { return true; } + } + + /* ---------------- Public operations -------------- */ + + /** + * Creates a new, empty map with the default initial table size (16). + */ + public ConcurrentHashMapV8() { + this.counter = new LongAdder(); + } + + /** + * Creates a new, empty map with an initial table size + * accommodating the specified number of elements without the need + * to dynamically resize. + * + * @param initialCapacity The implementation performs internal + * sizing to accommodate this many elements. + * @throws IllegalArgumentException if the initial capacity of + * elements is negative + */ + public ConcurrentHashMapV8(int initialCapacity) { + if (initialCapacity < 0) + throw new IllegalArgumentException(); + int cap = ((initialCapacity >= (MAXIMUM_CAPACITY >>> 1)) ? + MAXIMUM_CAPACITY : + tableSizeFor(initialCapacity + (initialCapacity >>> 1) + 1)); + this.counter = new LongAdder(); + this.sizeCtl = cap; + } + + /** + * Creates a new map with the same mappings as the given map. + * + * @param m the map + */ + public ConcurrentHashMapV8(Map m) { + this.counter = new LongAdder(); + this.sizeCtl = DEFAULT_CAPACITY; + internalPutAll(m); + } + + /** + * Creates a new, empty map with an initial table size based on + * the given number of elements ({@code initialCapacity}) and + * initial table density ({@code loadFactor}). + * + * @param initialCapacity the initial capacity. The implementation + * performs internal sizing to accommodate this many elements, + * given the specified load factor. + * @param loadFactor the load factor (table density) for + * establishing the initial table size + * @throws IllegalArgumentException if the initial capacity of + * elements is negative or the load factor is nonpositive + * + * @since 1.6 + */ + public ConcurrentHashMapV8(int initialCapacity, float loadFactor) { + this(initialCapacity, loadFactor, 1); + } + + /** + * Creates a new, empty map with an initial table size based on + * the given number of elements ({@code initialCapacity}), table + * density ({@code loadFactor}), and number of concurrently + * updating threads ({@code concurrencyLevel}). + * + * @param initialCapacity the initial capacity. The implementation + * performs internal sizing to accommodate this many elements, + * given the specified load factor. + * @param loadFactor the load factor (table density) for + * establishing the initial table size + * @param concurrencyLevel the estimated number of concurrently + * updating threads. The implementation may use this value as + * a sizing hint. + * @throws IllegalArgumentException if the initial capacity is + * negative or the load factor or concurrencyLevel are + * nonpositive + */ + public ConcurrentHashMapV8(int initialCapacity, + float loadFactor, int concurrencyLevel) { + if (!(loadFactor > 0.0f) || initialCapacity < 0 || concurrencyLevel <= 0) + throw new IllegalArgumentException(); + if (initialCapacity < concurrencyLevel) // Use at least as many bins + initialCapacity = concurrencyLevel; // as estimated threads + long size = (long)(1.0 + (long)initialCapacity / loadFactor); + int cap = (size >= (long)MAXIMUM_CAPACITY) ? + MAXIMUM_CAPACITY : tableSizeFor((int)size); + this.counter = new LongAdder(); + this.sizeCtl = cap; + } + + /** + * Creates a new {@link Set} backed by a ConcurrentHashMapV8 + * from the given type to {@code Boolean.TRUE}. + * + * @return the new set + */ + public static KeySetView newKeySet() { + return new KeySetView(new ConcurrentHashMapV8(), + Boolean.TRUE); + } + + /** + * Creates a new {@link Set} backed by a ConcurrentHashMapV8 + * from the given type to {@code Boolean.TRUE}. + * + * @param initialCapacity The implementation performs internal + * sizing to accommodate this many elements. + * @throws IllegalArgumentException if the initial capacity of + * elements is negative + * @return the new set + */ + public static KeySetView newKeySet(int initialCapacity) { + return new KeySetView(new ConcurrentHashMapV8(initialCapacity), + Boolean.TRUE); + } + + /** + * {@inheritDoc} + */ + public boolean isEmpty() { + return counter.sum() <= 0L; // ignore transient negative values + } + + /** + * {@inheritDoc} + */ + public int size() { + long n = counter.sum(); + return ((n < 0L) ? 0 : + (n > (long)Integer.MAX_VALUE) ? Integer.MAX_VALUE : + (int)n); + } + + /** + * Returns the number of mappings. This method should be used + * instead of {@link #size} because a ConcurrentHashMapV8 may + * contain more mappings than can be represented as an int. The + * value returned is a snapshot; the actual count may differ if + * there are ongoing concurrent insertions or removals. + * + * @return the number of mappings + */ + public long mappingCount() { + long n = counter.sum(); + return (n < 0L) ? 0L : n; // ignore transient negative values + } + + /** + * Returns the value to which the specified key is mapped, + * or {@code null} if this map contains no mapping for the key. + * + *

More formally, if this map contains a mapping from a key + * {@code k} to a value {@code v} such that {@code key.equals(k)}, + * then this method returns {@code v}; otherwise it returns + * {@code null}. (There can be at most one such mapping.) + * + * @throws NullPointerException if the specified key is null + */ + @SuppressWarnings("unchecked") public V get(Object key) { + if (key == null) + throw new NullPointerException(); + return (V)internalGet(key); + } + + /** + * Returns the value to which the specified key is mapped, + * or the given defaultValue if this map contains no mapping for the key. + * + * @param key the key + * @param defaultValue the value to return if this map contains + * no mapping for the given key + * @return the mapping for the key, if present; else the defaultValue + * @throws NullPointerException if the specified key is null + */ + @SuppressWarnings("unchecked") public V getValueOrDefault(Object key, V defaultValue) { + if (key == null) + throw new NullPointerException(); + V v = (V) internalGet(key); + return v == null ? defaultValue : v; + } + + /** + * Tests if the specified object is a key in this table. + * + * @param key possible key + * @return {@code true} if and only if the specified object + * is a key in this table, as determined by the + * {@code equals} method; {@code false} otherwise + * @throws NullPointerException if the specified key is null + */ + public boolean containsKey(Object key) { + if (key == null) + throw new NullPointerException(); + return internalGet(key) != null; + } + + /** + * Returns {@code true} if this map maps one or more keys to the + * specified value. Note: This method may require a full traversal + * of the map, and is much slower than method {@code containsKey}. + * + * @param value value whose presence in this map is to be tested + * @return {@code true} if this map maps one or more keys to the + * specified value + * @throws NullPointerException if the specified value is null + */ + public boolean containsValue(Object value) { + if (value == null) + throw new NullPointerException(); + Object v; + Traverser it = new Traverser(this); + while ((v = it.advance()) != null) { + if (v == value || value.equals(v)) + return true; + } + return false; + } + + public K findKey(Object value) { + if (value == null) + throw new NullPointerException(); + Object v; + Traverser it = new Traverser(this); + while ((v = it.advance()) != null) { + if (v == value || value.equals(v)) + return it.nextKey; + } + return null; + } + + /** + * Legacy method testing if some key maps into the specified value + * in this table. This method is identical in functionality to + * {@link #containsValue}, and exists solely to ensure + * full compatibility with class {@link java.util.Hashtable}, + * which supported this method prior to introduction of the + * Java Collections framework. + * + * @param value a value to search for + * @return {@code true} if and only if some key maps to the + * {@code value} argument in this table as + * determined by the {@code equals} method; + * {@code false} otherwise + * @throws NullPointerException if the specified value is null + */ + public boolean contains(Object value) { + return containsValue(value); + } + + /** + * Maps the specified key to the specified value in this table. + * Neither the key nor the value can be null. + * + *

The value can be retrieved by calling the {@code get} method + * with a key that is equal to the original key. + * + * @param key key with which the specified value is to be associated + * @param value value to be associated with the specified key + * @return the previous value associated with {@code key}, or + * {@code null} if there was no mapping for {@code key} + * @throws NullPointerException if the specified key or value is null + */ + @SuppressWarnings("unchecked") public V put(K key, V value) { + if (key == null || value == null) + throw new NullPointerException(); + return (V)internalPut(key, value); + } + + /** + * {@inheritDoc} + * + * @return the previous value associated with the specified key, + * or {@code null} if there was no mapping for the key + * @throws NullPointerException if the specified key or value is null + */ + @SuppressWarnings("unchecked") public V putIfAbsent(K key, V value) { + if (key == null || value == null) + throw new NullPointerException(); + return (V)internalPutIfAbsent(key, value); + } + + /** + * Copies all of the mappings from the specified map to this one. + * These mappings replace any mappings that this map had for any of the + * keys currently in the specified map. + * + * @param m mappings to be stored in this map + */ + public void putAll(Map m) { + internalPutAll(m); + } + + /** + * If the specified key is not already associated with a value, + * computes its value using the given mappingFunction and enters + * it into the map unless null. This is equivalent to + *

 {@code
+     * if (map.containsKey(key))
+     *   return map.get(key);
+     * value = mappingFunction.apply(key);
+     * if (value != null)
+     *   map.put(key, value);
+     * return value;}
+ * + * except that the action is performed atomically. If the + * function returns {@code null} no mapping is recorded. If the + * function itself throws an (unchecked) exception, the exception + * is rethrown to its caller, and no mapping is recorded. Some + * attempted update operations on this map by other threads may be + * blocked while computation is in progress, so the computation + * should be short and simple, and must not attempt to update any + * other mappings of this Map. The most appropriate usage is to + * construct a new object serving as an initial mapped value, or + * memoized result, as in: + * + *
 {@code
+     * map.computeIfAbsent(key, new Fun() {
+     *   public V map(K k) { return new Value(f(k)); }});}
+ * + * @param key key with which the specified value is to be associated + * @param mappingFunction the function to compute a value + * @return the current (existing or computed) value associated with + * the specified key, or null if the computed value is null + * @throws NullPointerException if the specified key or mappingFunction + * is null + * @throws IllegalStateException if the computation detectably + * attempts a recursive update to this map that would + * otherwise never complete + * @throws RuntimeException or Error if the mappingFunction does so, + * in which case the mapping is left unestablished + */ + @SuppressWarnings("unchecked") public V computeIfAbsent + (K key, Fun mappingFunction) { + if (key == null || mappingFunction == null) + throw new NullPointerException(); + return (V)internalComputeIfAbsent(key, mappingFunction); + } + + /** + * If the given key is present, computes a new mapping value given a key and + * its current mapped value. This is equivalent to + *
 {@code
+     *   if (map.containsKey(key)) {
+     *     value = remappingFunction.apply(key, map.get(key));
+     *     if (value != null)
+     *       map.put(key, value);
+     *     else
+     *       map.remove(key);
+     *   }
+     * }
+ * + * except that the action is performed atomically. If the + * function returns {@code null}, the mapping is removed. If the + * function itself throws an (unchecked) exception, the exception + * is rethrown to its caller, and the current mapping is left + * unchanged. Some attempted update operations on this map by + * other threads may be blocked while computation is in progress, + * so the computation should be short and simple, and must not + * attempt to update any other mappings of this Map. For example, + * to either create or append new messages to a value mapping: + * + * @param key key with which the specified value is to be associated + * @param remappingFunction the function to compute a value + * @return the new value associated with the specified key, or null if none + * @throws NullPointerException if the specified key or remappingFunction + * is null + * @throws IllegalStateException if the computation detectably + * attempts a recursive update to this map that would + * otherwise never complete + * @throws RuntimeException or Error if the remappingFunction does so, + * in which case the mapping is unchanged + */ + @SuppressWarnings("unchecked") public V computeIfPresent + (K key, BiFun remappingFunction) { + if (key == null || remappingFunction == null) + throw new NullPointerException(); + return (V)internalCompute(key, true, remappingFunction); + } + + /** + * Computes a new mapping value given a key and + * its current mapped value (or {@code null} if there is no current + * mapping). This is equivalent to + *
 {@code
+     *   value = remappingFunction.apply(key, map.get(key));
+     *   if (value != null)
+     *     map.put(key, value);
+     *   else
+     *     map.remove(key);
+     * }
+ * + * except that the action is performed atomically. If the + * function returns {@code null}, the mapping is removed. If the + * function itself throws an (unchecked) exception, the exception + * is rethrown to its caller, and the current mapping is left + * unchanged. Some attempted update operations on this map by + * other threads may be blocked while computation is in progress, + * so the computation should be short and simple, and must not + * attempt to update any other mappings of this Map. For example, + * to either create or append new messages to a value mapping: + * + *
 {@code
+     * Map map = ...;
+     * final String msg = ...;
+     * map.compute(key, new BiFun() {
+     *   public String apply(Key k, String v) {
+     *    return (v == null) ? msg : v + msg;});}}
+ * + * @param key key with which the specified value is to be associated + * @param remappingFunction the function to compute a value + * @return the new value associated with the specified key, or null if none + * @throws NullPointerException if the specified key or remappingFunction + * is null + * @throws IllegalStateException if the computation detectably + * attempts a recursive update to this map that would + * otherwise never complete + * @throws RuntimeException or Error if the remappingFunction does so, + * in which case the mapping is unchanged + */ + @SuppressWarnings("unchecked") public V compute + (K key, BiFun remappingFunction) { + if (key == null || remappingFunction == null) + throw new NullPointerException(); + return (V)internalCompute(key, false, remappingFunction); + } + + /** + * If the specified key is not already associated + * with a value, associate it with the given value. + * Otherwise, replace the value with the results of + * the given remapping function. This is equivalent to: + *
 {@code
+     *   if (!map.containsKey(key))
+     *     map.put(value);
+     *   else {
+     *     newValue = remappingFunction.apply(map.get(key), value);
+     *     if (value != null)
+     *       map.put(key, value);
+     *     else
+     *       map.remove(key);
+     *   }
+     * }
+ * except that the action is performed atomically. If the + * function returns {@code null}, the mapping is removed. If the + * function itself throws an (unchecked) exception, the exception + * is rethrown to its caller, and the current mapping is left + * unchanged. Some attempted update operations on this map by + * other threads may be blocked while computation is in progress, + * so the computation should be short and simple, and must not + * attempt to update any other mappings of this Map. + */ + @SuppressWarnings("unchecked") public V merge + (K key, V value, BiFun remappingFunction) { + if (key == null || value == null || remappingFunction == null) + throw new NullPointerException(); + return (V)internalMerge(key, value, remappingFunction); + } + + /** + * Removes the key (and its corresponding value) from this map. + * This method does nothing if the key is not in the map. + * + * @param key the key that needs to be removed + * @return the previous value associated with {@code key}, or + * {@code null} if there was no mapping for {@code key} + * @throws NullPointerException if the specified key is null + */ + @SuppressWarnings("unchecked") public V remove(Object key) { + if (key == null) + throw new NullPointerException(); + return (V)internalReplace(key, null, null); + } + + /** + * {@inheritDoc} + * + * @throws NullPointerException if the specified key is null + */ + public boolean remove(Object key, Object value) { + if (key == null) + throw new NullPointerException(); + if (value == null) + return false; + return internalReplace(key, null, value) != null; + } + + /** + * {@inheritDoc} + * + * @throws NullPointerException if any of the arguments are null + */ + public boolean replace(K key, V oldValue, V newValue) { + if (key == null || oldValue == null || newValue == null) + throw new NullPointerException(); + return internalReplace(key, newValue, oldValue) != null; + } + + /** + * {@inheritDoc} + * + * @return the previous value associated with the specified key, + * or {@code null} if there was no mapping for the key + * @throws NullPointerException if the specified key or value is null + */ + @SuppressWarnings("unchecked") public V replace(K key, V value) { + if (key == null || value == null) + throw new NullPointerException(); + return (V)internalReplace(key, value, null); + } + + /** + * Removes all of the mappings from this map. + */ + public void clear() { + internalClear(); + } + + /** + * Returns a {@link Set} view of the keys contained in this map. + * The set is backed by the map, so changes to the map are + * reflected in the set, and vice-versa. + * + * @return the set view + */ + public KeySetView keySet() { + KeySetView ks = keySet; + return (ks != null) ? ks : (keySet = new KeySetView(this, null)); + } + + /** + * Returns a {@link Set} view of the keys in this map, using the + * given common mapped value for any additions (i.e., {@link + * Collection#add} and {@link Collection#addAll}). This is of + * course only appropriate if it is acceptable to use the same + * value for all additions from this view. + * + * @param mappedValue the mapped value to use for any + * additions. + * @return the set view + * @throws NullPointerException if the mappedValue is null + */ + public KeySetView keySet(V mappedValue) { + if (mappedValue == null) + throw new NullPointerException(); + return new KeySetView(this, mappedValue); + } + + /** + * Returns a {@link Collection} view of the values contained in this map. + * The collection is backed by the map, so changes to the map are + * reflected in the collection, and vice-versa. + */ + public ValuesView values() { + ValuesView vs = values; + return (vs != null) ? vs : (values = new ValuesView(this)); + } + + /** + * Returns a {@link Set} view of the mappings contained in this map. + * The set is backed by the map, so changes to the map are + * reflected in the set, and vice-versa. The set supports element + * removal, which removes the corresponding mapping from the map, + * via the {@code Iterator.remove}, {@code Set.remove}, + * {@code removeAll}, {@code retainAll}, and {@code clear} + * operations. It does not support the {@code add} or + * {@code addAll} operations. + * + *

The view's {@code iterator} is a "weakly consistent" iterator + * that will never throw {@link ConcurrentModificationException}, + * and guarantees to traverse elements as they existed upon + * construction of the iterator, and may (but is not guaranteed to) + * reflect any modifications subsequent to construction. + */ + public Set> entrySet() { + EntrySetView es = entrySet; + return (es != null) ? es : (entrySet = new EntrySetView(this)); + } + + /** + * Returns an enumeration of the keys in this table. + * + * @return an enumeration of the keys in this table + * @see #keySet() + */ + public Enumeration keys() { + return new KeyIterator(this); + } + + /** + * Returns an enumeration of the values in this table. + * + * @return an enumeration of the values in this table + * @see #values() + */ + public Enumeration elements() { + return new ValueIterator(this); + } + + /** + * Returns a partitionable iterator of the keys in this map. + * + * @return a partitionable iterator of the keys in this map + */ + public Spliterator keySpliterator() { + return new KeyIterator(this); + } + + /** + * Returns a partitionable iterator of the values in this map. + * + * @return a partitionable iterator of the values in this map + */ + public Spliterator valueSpliterator() { + return new ValueIterator(this); + } + + /** + * Returns a partitionable iterator of the entries in this map. + * + * @return a partitionable iterator of the entries in this map + */ + public Spliterator> entrySpliterator() { + return new EntryIterator(this); + } + + /** + * Returns the hash code value for this {@link Map}, i.e., + * the sum of, for each key-value pair in the map, + * {@code key.hashCode() ^ value.hashCode()}. + * + * @return the hash code value for this map + */ + public int hashCode() { + int h = 0; + Traverser it = new Traverser(this); + Object v; + while ((v = it.advance()) != null) { + h += it.nextKey.hashCode() ^ v.hashCode(); + } + return h; + } + + /** + * Returns a string representation of this map. The string + * representation consists of a list of key-value mappings (in no + * particular order) enclosed in braces ("{@code {}}"). Adjacent + * mappings are separated by the characters {@code ", "} (comma + * and space). Each key-value mapping is rendered as the key + * followed by an equals sign ("{@code =}") followed by the + * associated value. + * + * @return a string representation of this map + */ + public String toString() { + Traverser it = new Traverser(this); + StringBuilder sb = new StringBuilder(); + sb.append('{'); + Object v; + if ((v = it.advance()) != null) { + for (;;) { + Object k = it.nextKey; + sb.append(k == this ? "(this Map)" : k); + sb.append('='); + sb.append(v == this ? "(this Map)" : v); + if ((v = it.advance()) == null) + break; + sb.append(',').append(' '); + } + } + return sb.append('}').toString(); + } + + /** + * Compares the specified object with this map for equality. + * Returns {@code true} if the given object is a map with the same + * mappings as this map. This operation may return misleading + * results if either map is concurrently modified during execution + * of this method. + * + * @param o object to be compared for equality with this map + * @return {@code true} if the specified object is equal to this map + */ + public boolean equals(Object o) { + if (o != this) { + if (!(o instanceof Map)) + return false; + Map m = (Map) o; + Traverser it = new Traverser(this); + Object val; + while ((val = it.advance()) != null) { + Object v = m.get(it.nextKey); + if (v == null || (v != val && !v.equals(val))) + return false; + } + for (Map.Entry e : m.entrySet()) { + Object mk, mv, v; + if ((mk = e.getKey()) == null || + (mv = e.getValue()) == null || + (v = internalGet(mk)) == null || + (mv != v && !mv.equals(v))) + return false; + } + } + return true; + } + + /* ----------------Iterators -------------- */ + + @SuppressWarnings("serial") static final class KeyIterator extends Traverser + implements Spliterator, Enumeration { + KeyIterator(ConcurrentHashMapV8 map) { super(map); } + KeyIterator(Traverser it) { + super(it); + } + public KeyIterator split() { + if (nextKey != null) + throw new IllegalStateException(); + return new KeyIterator(this); + } + @SuppressWarnings("unchecked") public final K next() { + if (nextVal == null && advance() == null) + throw new NoSuchElementException(); + Object k = nextKey; + nextVal = null; + return (K) k; + } + + public final K nextElement() { return next(); } + } + + @SuppressWarnings("serial") static final class ValueIterator extends Traverser + implements Spliterator, Enumeration { + ValueIterator(ConcurrentHashMapV8 map) { super(map); } + ValueIterator(Traverser it) { + super(it); + } + public ValueIterator split() { + if (nextKey != null) + throw new IllegalStateException(); + return new ValueIterator(this); + } + + @SuppressWarnings("unchecked") public final V next() { + Object v; + if ((v = nextVal) == null && (v = advance()) == null) + throw new NoSuchElementException(); + nextVal = null; + return (V) v; + } + + public final V nextElement() { return next(); } + } + + @SuppressWarnings("serial") static final class EntryIterator extends Traverser + implements Spliterator> { + EntryIterator(ConcurrentHashMapV8 map) { super(map); } + EntryIterator(Traverser it) { + super(it); + } + public EntryIterator split() { + if (nextKey != null) + throw new IllegalStateException(); + return new EntryIterator(this); + } + + @SuppressWarnings("unchecked") public final Map.Entry next() { + Object v; + if ((v = nextVal) == null && (v = advance()) == null) + throw new NoSuchElementException(); + Object k = nextKey; + nextVal = null; + return new MapEntry((K)k, (V)v, map); + } + } + + /** + * Exported Entry for iterators + */ + static final class MapEntry implements Map.Entry { + final K key; // non-null + V val; // non-null + final ConcurrentHashMapV8 map; + MapEntry(K key, V val, ConcurrentHashMapV8 map) { + this.key = key; + this.val = val; + this.map = map; + } + public final K getKey() { return key; } + public final V getValue() { return val; } + public final int hashCode() { return key.hashCode() ^ val.hashCode(); } + public final String toString(){ return key + "=" + val; } + + public final boolean equals(Object o) { + Object k, v; Map.Entry e; + return ((o instanceof Map.Entry) && + (k = (e = (Map.Entry)o).getKey()) != null && + (v = e.getValue()) != null && + (k == key || k.equals(key)) && + (v == val || v.equals(val))); + } + + /** + * Sets our entry's value and writes through to the map. The + * value to return is somewhat arbitrary here. Since we do not + * necessarily track asynchronous changes, the most recent + * "previous" value could be different from what we return (or + * could even have been removed in which case the put will + * re-establish). We do not and cannot guarantee more. + */ + public final V setValue(V value) { + if (value == null) throw new NullPointerException(); + V v = val; + val = value; + map.put(key, value); + return v; + } + } + + /* ---------------- Serialization Support -------------- */ + + /** + * Stripped-down version of helper class used in previous version, + * declared for the sake of serialization compatibility + */ + static class Segment implements Serializable { + private static final long serialVersionUID = 2249069246763182397L; + final float loadFactor; + Segment(float lf) { this.loadFactor = lf; } + } + + /** + * Saves the state of the {@code ConcurrentHashMapV8} instance to a + * stream (i.e., serializes it). + * @param s the stream + * @serialData + * the key (Object) and value (Object) + * for each key-value mapping, followed by a null pair. + * The key-value mappings are emitted in no particular order. + */ + @SuppressWarnings("unchecked") private void writeObject(java.io.ObjectOutputStream s) + throws java.io.IOException { + if (segments == null) { // for serialization compatibility + segments = (Segment[]) + new Segment[DEFAULT_CONCURRENCY_LEVEL]; + for (int i = 0; i < segments.length; ++i) + segments[i] = new Segment(LOAD_FACTOR); + } + s.defaultWriteObject(); + Traverser it = new Traverser(this); + Object v; + while ((v = it.advance()) != null) { + s.writeObject(it.nextKey); + s.writeObject(v); + } + s.writeObject(null); + s.writeObject(null); + segments = null; // throw away + } + + /** + * Reconstitutes the instance from a stream (that is, deserializes it). + * @param s the stream + */ + @SuppressWarnings("unchecked") private void readObject(java.io.ObjectInputStream s) + throws java.io.IOException, ClassNotFoundException { + s.defaultReadObject(); + this.segments = null; // unneeded + // initialize transient final field + UNSAFE.putObjectVolatile(this, counterOffset, new LongAdder()); + + // Create all nodes, then place in table once size is known + long size = 0L; + Node p = null; + for (;;) { + K k = (K) s.readObject(); + V v = (V) s.readObject(); + if (k != null && v != null) { + int h = spread(k.hashCode()); + p = new Node(h, k, v, p); + ++size; + } + else + break; + } + if (p != null) { + boolean init = false; + int n; + if (size >= (long)(MAXIMUM_CAPACITY >>> 1)) + n = MAXIMUM_CAPACITY; + else { + int sz = (int)size; + n = tableSizeFor(sz + (sz >>> 1) + 1); + } + int sc = sizeCtl; + boolean collide = false; + if (n > sc && + UNSAFE.compareAndSwapInt(this, sizeCtlOffset, sc, -1)) { + try { + if (table == null) { + init = true; + Node[] tab = new Node[n]; + int mask = n - 1; + while (p != null) { + int j = p.hash & mask; + Node next = p.next; + Node q = p.next = tabAt(tab, j); + setTabAt(tab, j, p); + if (!collide && q != null && q.hash == p.hash) + collide = true; + p = next; + } + table = tab; + counter.add(size); + sc = n - (n >>> 2); + } + } finally { + sizeCtl = sc; + } + if (collide) { // rescan and convert to TreeBins + Node[] tab = table; + for (int i = 0; i < tab.length; ++i) { + int c = 0; + for (Node e = tabAt(tab, i); e != null; e = e.next) { + if (++c > TREE_THRESHOLD && + (e.key instanceof Comparable)) { + replaceWithTreeBin(tab, i, e.key); + break; + } + } + } + } + } + if (!init) { // Can only happen if unsafely published. + while (p != null) { + internalPut(p.key, p.val); + p = p.next; + } + } + } + } + + + // ------------------------------------------------------- + + // Sams + /** Interface describing a void action of one argument */ + public interface Action { void apply(A a); } + /** Interface describing a void action of two arguments */ + public interface BiAction { void apply(A a, B b); } + /** Interface describing a function of one argument */ + public interface Generator { T apply(); } + /** Interface describing a function mapping its argument to a double */ + public interface ObjectToDouble { double apply(A a); } + /** Interface describing a function mapping its argument to a long */ + public interface ObjectToLong { long apply(A a); } + /** Interface describing a function mapping its argument to an int */ + public interface ObjectToInt {int apply(A a); } + /** Interface describing a function mapping two arguments to a double */ + public interface ObjectByObjectToDouble { double apply(A a, B b); } + /** Interface describing a function mapping two arguments to a long */ + public interface ObjectByObjectToLong { long apply(A a, B b); } + /** Interface describing a function mapping two arguments to an int */ + public interface ObjectByObjectToInt {int apply(A a, B b); } + /** Interface describing a function mapping a double to a double */ + public interface DoubleToDouble { double apply(double a); } + /** Interface describing a function mapping a long to a long */ + public interface LongToLong { long apply(long a); } + /** Interface describing a function mapping an int to an int */ + public interface IntToInt { int apply(int a); } + /** Interface describing a function mapping two doubles to a double */ + public interface DoubleByDoubleToDouble { double apply(double a, double b); } + /** Interface describing a function mapping two longs to a long */ + public interface LongByLongToLong { long apply(long a, long b); } + /** Interface describing a function mapping two ints to an int */ + public interface IntByIntToInt { int apply(int a, int b); } + + + /* ----------------Views -------------- */ + + /** + * Base class for views. + */ + static abstract class CHMView { + final ConcurrentHashMapV8 map; + CHMView(ConcurrentHashMapV8 map) { this.map = map; } + + /** + * Returns the map backing this view. + * + * @return the map backing this view + */ + public ConcurrentHashMapV8 getMap() { return map; } + + public final int size() { return map.size(); } + public final boolean isEmpty() { return map.isEmpty(); } + public final void clear() { map.clear(); } + + // implementations below rely on concrete classes supplying these + abstract public Iterator iterator(); + abstract public boolean contains(Object o); + abstract public boolean remove(Object o); + + private static final String oomeMsg = "Required array size too large"; + + public final Object[] toArray() { + long sz = map.mappingCount(); + if (sz > (long)(MAX_ARRAY_SIZE)) + throw new OutOfMemoryError(oomeMsg); + int n = (int)sz; + Object[] r = new Object[n]; + int i = 0; + Iterator it = iterator(); + while (it.hasNext()) { + if (i == n) { + if (n >= MAX_ARRAY_SIZE) + throw new OutOfMemoryError(oomeMsg); + if (n >= MAX_ARRAY_SIZE - (MAX_ARRAY_SIZE >>> 1) - 1) + n = MAX_ARRAY_SIZE; + else + n += (n >>> 1) + 1; + r = Arrays.copyOf(r, n); + } + r[i++] = it.next(); + } + return (i == n) ? r : Arrays.copyOf(r, i); + } + + @SuppressWarnings("unchecked") public final T[] toArray(T[] a) { + long sz = map.mappingCount(); + if (sz > (long)(MAX_ARRAY_SIZE)) + throw new OutOfMemoryError(oomeMsg); + int m = (int)sz; + T[] r = (a.length >= m) ? a : + (T[])java.lang.reflect.Array + .newInstance(a.getClass().getComponentType(), m); + int n = r.length; + int i = 0; + Iterator it = iterator(); + while (it.hasNext()) { + if (i == n) { + if (n >= MAX_ARRAY_SIZE) + throw new OutOfMemoryError(oomeMsg); + if (n >= MAX_ARRAY_SIZE - (MAX_ARRAY_SIZE >>> 1) - 1) + n = MAX_ARRAY_SIZE; + else + n += (n >>> 1) + 1; + r = Arrays.copyOf(r, n); + } + r[i++] = (T)it.next(); + } + if (a == r && i < n) { + r[i] = null; // null-terminate + return r; + } + return (i == n) ? r : Arrays.copyOf(r, i); + } + + public final int hashCode() { + int h = 0; + for (Iterator it = iterator(); it.hasNext();) + h += it.next().hashCode(); + return h; + } + + public final String toString() { + StringBuilder sb = new StringBuilder(); + sb.append('['); + Iterator it = iterator(); + if (it.hasNext()) { + for (;;) { + Object e = it.next(); + sb.append(e == this ? "(this Collection)" : e); + if (!it.hasNext()) + break; + sb.append(',').append(' '); + } + } + return sb.append(']').toString(); + } + + public final boolean containsAll(Collection c) { + if (c != this) { + for (Iterator it = c.iterator(); it.hasNext();) { + Object e = it.next(); + if (e == null || !contains(e)) + return false; + } + } + return true; + } + + public final boolean removeAll(Collection c) { + boolean modified = false; + for (Iterator it = iterator(); it.hasNext();) { + if (c.contains(it.next())) { + it.remove(); + modified = true; + } + } + return modified; + } + + public final boolean retainAll(Collection c) { + boolean modified = false; + for (Iterator it = iterator(); it.hasNext();) { + if (!c.contains(it.next())) { + it.remove(); + modified = true; + } + } + return modified; + } + + } + + /** + * A view of a ConcurrentHashMapV8 as a {@link Set} of keys, in + * which additions may optionally be enabled by mapping to a + * common value. This class cannot be directly instantiated. See + * {@link #keySet}, {@link #keySet(Object)}, {@link #newKeySet()}, + * {@link #newKeySet(int)}. + */ + public static class KeySetView extends CHMView implements Set, java.io.Serializable { + private static final long serialVersionUID = 7249069246763182397L; + private final V value; + KeySetView(ConcurrentHashMapV8 map, V value) { // non-public + super(map); + this.value = value; + } + + /** + * Returns the default mapped value for additions, + * or {@code null} if additions are not supported. + * + * @return the default mapped value for additions, or {@code null} + * if not supported. + */ + public V getMappedValue() { return value; } + + // implement Set API + + public boolean contains(Object o) { return map.containsKey(o); } + public boolean remove(Object o) { return map.remove(o) != null; } + + /** + * Returns a "weakly consistent" iterator that will never + * throw {@link ConcurrentModificationException}, and + * guarantees to traverse elements as they existed upon + * construction of the iterator, and may (but is not + * guaranteed to) reflect any modifications subsequent to + * construction. + * + * @return an iterator over the keys of this map + */ + public Iterator iterator() { return new KeyIterator(map); } + public boolean add(K e) { + V v; + if ((v = value) == null) + throw new UnsupportedOperationException(); + if (e == null) + throw new NullPointerException(); + return map.internalPutIfAbsent(e, v) == null; + } + public boolean addAll(Collection c) { + boolean added = false; + V v; + if ((v = value) == null) + throw new UnsupportedOperationException(); + for (K e : c) { + if (e == null) + throw new NullPointerException(); + if (map.internalPutIfAbsent(e, v) == null) + added = true; + } + return added; + } + public boolean equals(Object o) { + Set c; + return ((o instanceof Set) && + ((c = (Set)o) == this || + (containsAll(c) && c.containsAll(this)))); + } + } + + /** + * A view of a ConcurrentHashMapV8 as a {@link Collection} of + * values, in which additions are disabled. This class cannot be + * directly instantiated. See {@link #values}, + * + *

The view's {@code iterator} is a "weakly consistent" iterator + * that will never throw {@link ConcurrentModificationException}, + * and guarantees to traverse elements as they existed upon + * construction of the iterator, and may (but is not guaranteed to) + * reflect any modifications subsequent to construction. + */ + public static final class ValuesView extends CHMView + implements Collection { + ValuesView(ConcurrentHashMapV8 map) { super(map); } + public final boolean contains(Object o) { return map.containsValue(o); } + public final boolean remove(Object o) { + if (o != null) { + Iterator it = new ValueIterator(map); + while (it.hasNext()) { + if (o.equals(it.next())) { + it.remove(); + return true; + } + } + } + return false; + } + + /** + * Returns a "weakly consistent" iterator that will never + * throw {@link ConcurrentModificationException}, and + * guarantees to traverse elements as they existed upon + * construction of the iterator, and may (but is not + * guaranteed to) reflect any modifications subsequent to + * construction. + * + * @return an iterator over the values of this map + */ + public final Iterator iterator() { + return new ValueIterator(map); + } + public final boolean add(V e) { + throw new UnsupportedOperationException(); + } + public final boolean addAll(Collection c) { + throw new UnsupportedOperationException(); + } + } + + /** + * A view of a ConcurrentHashMapV8 as a {@link Set} of (key, value) + * entries. This class cannot be directly instantiated. See + * {@link #entrySet}. + */ + public static final class EntrySetView extends CHMView + implements Set> { + EntrySetView(ConcurrentHashMapV8 map) { super(map); } + public final boolean contains(Object o) { + Object k, v, r; Map.Entry e; + return ((o instanceof Map.Entry) && + (k = (e = (Map.Entry)o).getKey()) != null && + (r = map.get(k)) != null && + (v = e.getValue()) != null && + (v == r || v.equals(r))); + } + public final boolean remove(Object o) { + Object k, v; Map.Entry e; + return ((o instanceof Map.Entry) && + (k = (e = (Map.Entry)o).getKey()) != null && + (v = e.getValue()) != null && + map.remove(k, v)); + } + + /** + * Returns a "weakly consistent" iterator that will never + * throw {@link ConcurrentModificationException}, and + * guarantees to traverse elements as they existed upon + * construction of the iterator, and may (but is not + * guaranteed to) reflect any modifications subsequent to + * construction. + * + * @return an iterator over the entries of this map + */ + public final Iterator> iterator() { + return new EntryIterator(map); + } + + public final boolean add(Entry e) { + K key = e.getKey(); + V value = e.getValue(); + if (key == null || value == null) + throw new NullPointerException(); + return map.internalPut(key, value) == null; + } + public final boolean addAll(Collection> c) { + boolean added = false; + for (Entry e : c) { + if (add(e)) + added = true; + } + return added; + } + public boolean equals(Object o) { + Set c; + return ((o instanceof Set) && + ((c = (Set)o) == this || + (containsAll(c) && c.containsAll(this)))); + } + } + + // Unsafe mechanics + private static final sun.misc.Unsafe UNSAFE; + private static final long counterOffset; + private static final long sizeCtlOffset; + private static final long ABASE; + private static final int ASHIFT; + + static { + int ss; + try { + UNSAFE = getUnsafe(); + Class k = ConcurrentHashMapV8.class; + counterOffset = UNSAFE.objectFieldOffset + (k.getDeclaredField("counter")); + sizeCtlOffset = UNSAFE.objectFieldOffset + (k.getDeclaredField("sizeCtl")); + Class sc = Node[].class; + ABASE = UNSAFE.arrayBaseOffset(sc); + ss = UNSAFE.arrayIndexScale(sc); + } catch (Exception e) { + throw new Error(e); + } + if ((ss & (ss-1)) != 0) + throw new Error("data type scale not a power of two"); + ASHIFT = 31 - Integer.numberOfLeadingZeros(ss); + } + + /** + * Returns a sun.misc.Unsafe. Suitable for use in a 3rd party package. + * Replace with a simple call to Unsafe.getUnsafe when integrating + * into a jdk. + * + * @return a sun.misc.Unsafe + */ + private static sun.misc.Unsafe getUnsafe() { + try { + return sun.misc.Unsafe.getUnsafe(); + } catch (SecurityException se) { + try { + return java.security.AccessController.doPrivileged + (new java.security + .PrivilegedExceptionAction() { + public sun.misc.Unsafe run() throws Exception { + java.lang.reflect.Field f = sun.misc + .Unsafe.class.getDeclaredField("theUnsafe"); + f.setAccessible(true); + return (sun.misc.Unsafe) f.get(null); + }}); + } catch (java.security.PrivilegedActionException e) { + throw new RuntimeException("Could not initialize intrinsics", + e.getCause()); + } + } + } +} diff --git a/vendor/bundle/gems/concurrent-ruby-1.1.5/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/LongAdder.java b/vendor/bundle/gems/concurrent-ruby-1.1.5/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/LongAdder.java new file mode 100644 index 0000000..47a923c --- /dev/null +++ b/vendor/bundle/gems/concurrent-ruby-1.1.5/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/LongAdder.java @@ -0,0 +1,203 @@ +/* + * Written by Doug Lea with assistance from members of JCP JSR-166 + * Expert Group and released to the public domain, as explained at + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +// This is based on 1.9 version. + +package com.concurrent_ruby.ext.jsr166e; +import java.util.concurrent.atomic.AtomicLong; +import java.io.IOException; +import java.io.Serializable; +import java.io.ObjectInputStream; + +/** + * One or more variables that together maintain an initially zero + * {@code long} sum. When updates (method {@link #add}) are contended + * across threads, the set of variables may grow dynamically to reduce + * contention. Method {@link #sum} (or, equivalently, {@link + * #longValue}) returns the current total combined across the + * variables maintaining the sum. + * + *

This class is usually preferable to {@link AtomicLong} when + * multiple threads update a common sum that is used for purposes such + * as collecting statistics, not for fine-grained synchronization + * control. Under low update contention, the two classes have similar + * characteristics. But under high contention, expected throughput of + * this class is significantly higher, at the expense of higher space + * consumption. + * + *

This class extends {@link Number}, but does not define + * methods such as {@code hashCode} and {@code compareTo} because + * instances are expected to be mutated, and so are not useful as + * collection keys. + * + *

jsr166e note: This class is targeted to be placed in + * java.util.concurrent.atomic. + * + * @since 1.8 + * @author Doug Lea + */ +public class LongAdder extends Striped64 implements Serializable { + private static final long serialVersionUID = 7249069246863182397L; + + /** + * Version of plus for use in retryUpdate + */ + final long fn(long v, long x) { return v + x; } + + /** + * Creates a new adder with initial sum of zero. + */ + public LongAdder() { + } + + /** + * Adds the given value. + * + * @param x the value to add + */ + public void add(long x) { + Cell[] as; long b, v; HashCode hc; Cell a; int n; + if ((as = cells) != null || !casBase(b = base, b + x)) { + boolean uncontended = true; + int h = (hc = threadHashCode.get()).code; + if (as == null || (n = as.length) < 1 || + (a = as[(n - 1) & h]) == null || + !(uncontended = a.cas(v = a.value, v + x))) + retryUpdate(x, hc, uncontended); + } + } + + /** + * Equivalent to {@code add(1)}. + */ + public void increment() { + add(1L); + } + + /** + * Equivalent to {@code add(-1)}. + */ + public void decrement() { + add(-1L); + } + + /** + * Returns the current sum. The returned value is NOT an + * atomic snapshot: Invocation in the absence of concurrent + * updates returns an accurate result, but concurrent updates that + * occur while the sum is being calculated might not be + * incorporated. + * + * @return the sum + */ + public long sum() { + long sum = base; + Cell[] as = cells; + if (as != null) { + int n = as.length; + for (int i = 0; i < n; ++i) { + Cell a = as[i]; + if (a != null) + sum += a.value; + } + } + return sum; + } + + /** + * Resets variables maintaining the sum to zero. This method may + * be a useful alternative to creating a new adder, but is only + * effective if there are no concurrent updates. Because this + * method is intrinsically racy, it should only be used when it is + * known that no threads are concurrently updating. + */ + public void reset() { + internalReset(0L); + } + + /** + * Equivalent in effect to {@link #sum} followed by {@link + * #reset}. This method may apply for example during quiescent + * points between multithreaded computations. If there are + * updates concurrent with this method, the returned value is + * not guaranteed to be the final value occurring before + * the reset. + * + * @return the sum + */ + public long sumThenReset() { + long sum = base; + Cell[] as = cells; + base = 0L; + if (as != null) { + int n = as.length; + for (int i = 0; i < n; ++i) { + Cell a = as[i]; + if (a != null) { + sum += a.value; + a.value = 0L; + } + } + } + return sum; + } + + /** + * Returns the String representation of the {@link #sum}. + * @return the String representation of the {@link #sum} + */ + public String toString() { + return Long.toString(sum()); + } + + /** + * Equivalent to {@link #sum}. + * + * @return the sum + */ + public long longValue() { + return sum(); + } + + /** + * Returns the {@link #sum} as an {@code int} after a narrowing + * primitive conversion. + */ + public int intValue() { + return (int)sum(); + } + + /** + * Returns the {@link #sum} as a {@code float} + * after a widening primitive conversion. + */ + public float floatValue() { + return (float)sum(); + } + + /** + * Returns the {@link #sum} as a {@code double} after a widening + * primitive conversion. + */ + public double doubleValue() { + return (double)sum(); + } + + private void writeObject(java.io.ObjectOutputStream s) + throws java.io.IOException { + s.defaultWriteObject(); + s.writeLong(sum()); + } + + private void readObject(ObjectInputStream s) + throws IOException, ClassNotFoundException { + s.defaultReadObject(); + busy = 0; + cells = null; + base = s.readLong(); + } + +} diff --git a/vendor/bundle/gems/concurrent-ruby-1.1.5/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/Striped64.java b/vendor/bundle/gems/concurrent-ruby-1.1.5/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/Striped64.java new file mode 100644 index 0000000..93a277f --- /dev/null +++ b/vendor/bundle/gems/concurrent-ruby-1.1.5/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/Striped64.java @@ -0,0 +1,342 @@ +/* + * Written by Doug Lea with assistance from members of JCP JSR-166 + * Expert Group and released to the public domain, as explained at + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +// This is based on 1.5 version. + +package com.concurrent_ruby.ext.jsr166e; +import java.util.Random; + +/** + * A package-local class holding common representation and mechanics + * for classes supporting dynamic striping on 64bit values. The class + * extends Number so that concrete subclasses must publicly do so. + */ +abstract class Striped64 extends Number { + /* + * This class maintains a lazily-initialized table of atomically + * updated variables, plus an extra "base" field. The table size + * is a power of two. Indexing uses masked per-thread hash codes. + * Nearly all declarations in this class are package-private, + * accessed directly by subclasses. + * + * Table entries are of class Cell; a variant of AtomicLong padded + * to reduce cache contention on most processors. Padding is + * overkill for most Atomics because they are usually irregularly + * scattered in memory and thus don't interfere much with each + * other. But Atomic objects residing in arrays will tend to be + * placed adjacent to each other, and so will most often share + * cache lines (with a huge negative performance impact) without + * this precaution. + * + * In part because Cells are relatively large, we avoid creating + * them until they are needed. When there is no contention, all + * updates are made to the base field. Upon first contention (a + * failed CAS on base update), the table is initialized to size 2. + * The table size is doubled upon further contention until + * reaching the nearest power of two greater than or equal to the + * number of CPUS. Table slots remain empty (null) until they are + * needed. + * + * A single spinlock ("busy") is used for initializing and + * resizing the table, as well as populating slots with new Cells. + * There is no need for a blocking lock: When the lock is not + * available, threads try other slots (or the base). During these + * retries, there is increased contention and reduced locality, + * which is still better than alternatives. + * + * Per-thread hash codes are initialized to random values. + * Contention and/or table collisions are indicated by failed + * CASes when performing an update operation (see method + * retryUpdate). Upon a collision, if the table size is less than + * the capacity, it is doubled in size unless some other thread + * holds the lock. If a hashed slot is empty, and lock is + * available, a new Cell is created. Otherwise, if the slot + * exists, a CAS is tried. Retries proceed by "double hashing", + * using a secondary hash (Marsaglia XorShift) to try to find a + * free slot. + * + * The table size is capped because, when there are more threads + * than CPUs, supposing that each thread were bound to a CPU, + * there would exist a perfect hash function mapping threads to + * slots that eliminates collisions. When we reach capacity, we + * search for this mapping by randomly varying the hash codes of + * colliding threads. Because search is random, and collisions + * only become known via CAS failures, convergence can be slow, + * and because threads are typically not bound to CPUS forever, + * may not occur at all. However, despite these limitations, + * observed contention rates are typically low in these cases. + * + * It is possible for a Cell to become unused when threads that + * once hashed to it terminate, as well as in the case where + * doubling the table causes no thread to hash to it under + * expanded mask. We do not try to detect or remove such cells, + * under the assumption that for long-running instances, observed + * contention levels will recur, so the cells will eventually be + * needed again; and for short-lived ones, it does not matter. + */ + + /** + * Padded variant of AtomicLong supporting only raw accesses plus CAS. + * The value field is placed between pads, hoping that the JVM doesn't + * reorder them. + * + * JVM intrinsics note: It would be possible to use a release-only + * form of CAS here, if it were provided. + */ + static final class Cell { + volatile long p0, p1, p2, p3, p4, p5, p6; + volatile long value; + volatile long q0, q1, q2, q3, q4, q5, q6; + Cell(long x) { value = x; } + + final boolean cas(long cmp, long val) { + return UNSAFE.compareAndSwapLong(this, valueOffset, cmp, val); + } + + // Unsafe mechanics + private static final sun.misc.Unsafe UNSAFE; + private static final long valueOffset; + static { + try { + UNSAFE = getUnsafe(); + Class ak = Cell.class; + valueOffset = UNSAFE.objectFieldOffset + (ak.getDeclaredField("value")); + } catch (Exception e) { + throw new Error(e); + } + } + + } + + /** + * Holder for the thread-local hash code. The code is initially + * random, but may be set to a different value upon collisions. + */ + static final class HashCode { + static final Random rng = new Random(); + int code; + HashCode() { + int h = rng.nextInt(); // Avoid zero to allow xorShift rehash + code = (h == 0) ? 1 : h; + } + } + + /** + * The corresponding ThreadLocal class + */ + static final class ThreadHashCode extends ThreadLocal { + public HashCode initialValue() { return new HashCode(); } + } + + /** + * Static per-thread hash codes. Shared across all instances to + * reduce ThreadLocal pollution and because adjustments due to + * collisions in one table are likely to be appropriate for + * others. + */ + static final ThreadHashCode threadHashCode = new ThreadHashCode(); + + /** Number of CPUS, to place bound on table size */ + static final int NCPU = Runtime.getRuntime().availableProcessors(); + + /** + * Table of cells. When non-null, size is a power of 2. + */ + transient volatile Cell[] cells; + + /** + * Base value, used mainly when there is no contention, but also as + * a fallback during table initialization races. Updated via CAS. + */ + transient volatile long base; + + /** + * Spinlock (locked via CAS) used when resizing and/or creating Cells. + */ + transient volatile int busy; + + /** + * Package-private default constructor + */ + Striped64() { + } + + /** + * CASes the base field. + */ + final boolean casBase(long cmp, long val) { + return UNSAFE.compareAndSwapLong(this, baseOffset, cmp, val); + } + + /** + * CASes the busy field from 0 to 1 to acquire lock. + */ + final boolean casBusy() { + return UNSAFE.compareAndSwapInt(this, busyOffset, 0, 1); + } + + /** + * Computes the function of current and new value. Subclasses + * should open-code this update function for most uses, but the + * virtualized form is needed within retryUpdate. + * + * @param currentValue the current value (of either base or a cell) + * @param newValue the argument from a user update call + * @return result of the update function + */ + abstract long fn(long currentValue, long newValue); + + /** + * Handles cases of updates involving initialization, resizing, + * creating new Cells, and/or contention. See above for + * explanation. This method suffers the usual non-modularity + * problems of optimistic retry code, relying on rechecked sets of + * reads. + * + * @param x the value + * @param hc the hash code holder + * @param wasUncontended false if CAS failed before call + */ + final void retryUpdate(long x, HashCode hc, boolean wasUncontended) { + int h = hc.code; + boolean collide = false; // True if last slot nonempty + for (;;) { + Cell[] as; Cell a; int n; long v; + if ((as = cells) != null && (n = as.length) > 0) { + if ((a = as[(n - 1) & h]) == null) { + if (busy == 0) { // Try to attach new Cell + Cell r = new Cell(x); // Optimistically create + if (busy == 0 && casBusy()) { + boolean created = false; + try { // Recheck under lock + Cell[] rs; int m, j; + if ((rs = cells) != null && + (m = rs.length) > 0 && + rs[j = (m - 1) & h] == null) { + rs[j] = r; + created = true; + } + } finally { + busy = 0; + } + if (created) + break; + continue; // Slot is now non-empty + } + } + collide = false; + } + else if (!wasUncontended) // CAS already known to fail + wasUncontended = true; // Continue after rehash + else if (a.cas(v = a.value, fn(v, x))) + break; + else if (n >= NCPU || cells != as) + collide = false; // At max size or stale + else if (!collide) + collide = true; + else if (busy == 0 && casBusy()) { + try { + if (cells == as) { // Expand table unless stale + Cell[] rs = new Cell[n << 1]; + for (int i = 0; i < n; ++i) + rs[i] = as[i]; + cells = rs; + } + } finally { + busy = 0; + } + collide = false; + continue; // Retry with expanded table + } + h ^= h << 13; // Rehash + h ^= h >>> 17; + h ^= h << 5; + } + else if (busy == 0 && cells == as && casBusy()) { + boolean init = false; + try { // Initialize table + if (cells == as) { + Cell[] rs = new Cell[2]; + rs[h & 1] = new Cell(x); + cells = rs; + init = true; + } + } finally { + busy = 0; + } + if (init) + break; + } + else if (casBase(v = base, fn(v, x))) + break; // Fall back on using base + } + hc.code = h; // Record index for next time + } + + + /** + * Sets base and all cells to the given value. + */ + final void internalReset(long initialValue) { + Cell[] as = cells; + base = initialValue; + if (as != null) { + int n = as.length; + for (int i = 0; i < n; ++i) { + Cell a = as[i]; + if (a != null) + a.value = initialValue; + } + } + } + + // Unsafe mechanics + private static final sun.misc.Unsafe UNSAFE; + private static final long baseOffset; + private static final long busyOffset; + static { + try { + UNSAFE = getUnsafe(); + Class sk = Striped64.class; + baseOffset = UNSAFE.objectFieldOffset + (sk.getDeclaredField("base")); + busyOffset = UNSAFE.objectFieldOffset + (sk.getDeclaredField("busy")); + } catch (Exception e) { + throw new Error(e); + } + } + + /** + * Returns a sun.misc.Unsafe. Suitable for use in a 3rd party package. + * Replace with a simple call to Unsafe.getUnsafe when integrating + * into a jdk. + * + * @return a sun.misc.Unsafe + */ + private static sun.misc.Unsafe getUnsafe() { + try { + return sun.misc.Unsafe.getUnsafe(); + } catch (SecurityException se) { + try { + return java.security.AccessController.doPrivileged + (new java.security + .PrivilegedExceptionAction() { + public sun.misc.Unsafe run() throws Exception { + java.lang.reflect.Field f = sun.misc + .Unsafe.class.getDeclaredField("theUnsafe"); + f.setAccessible(true); + return (sun.misc.Unsafe) f.get(null); + }}); + } catch (java.security.PrivilegedActionException e) { + throw new RuntimeException("Could not initialize intrinsics", + e.getCause()); + } + } + } + +} diff --git a/vendor/bundle/gems/concurrent-ruby-1.1.5/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/nounsafe/ConcurrentHashMapV8.java b/vendor/bundle/gems/concurrent-ruby-1.1.5/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/nounsafe/ConcurrentHashMapV8.java new file mode 100644 index 0000000..b7fc5a9 --- /dev/null +++ b/vendor/bundle/gems/concurrent-ruby-1.1.5/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/nounsafe/ConcurrentHashMapV8.java @@ -0,0 +1,3800 @@ +/* + * Written by Doug Lea with assistance from members of JCP JSR-166 + * Expert Group and released to the public domain, as explained at + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +// This is based on the 1.79 version. + +package com.concurrent_ruby.ext.jsr166e.nounsafe; + +import org.jruby.RubyClass; +import org.jruby.RubyNumeric; +import org.jruby.RubyObject; +import org.jruby.exceptions.RaiseException; +import com.concurrent_ruby.ext.jsr166e.ConcurrentHashMap; +import com.concurrent_ruby.ext.jsr166y.ThreadLocalRandom; +import org.jruby.runtime.ThreadContext; +import org.jruby.runtime.builtin.IRubyObject; + +import java.util.Arrays; +import java.util.Map; +import java.util.Set; +import java.util.Collection; +import java.util.Hashtable; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Enumeration; +import java.util.ConcurrentModificationException; +import java.util.NoSuchElementException; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.atomic.AtomicIntegerFieldUpdater; +import java.util.concurrent.atomic.AtomicReferenceArray; +import java.util.concurrent.locks.AbstractQueuedSynchronizer; + +import java.io.Serializable; + +/** + * A hash table supporting full concurrency of retrievals and + * high expected concurrency for updates. This class obeys the + * same functional specification as {@link java.util.Hashtable}, and + * includes versions of methods corresponding to each method of + * {@code Hashtable}. However, even though all operations are + * thread-safe, retrieval operations do not entail locking, + * and there is not any support for locking the entire table + * in a way that prevents all access. This class is fully + * interoperable with {@code Hashtable} in programs that rely on its + * thread safety but not on its synchronization details. + * + *

Retrieval operations (including {@code get}) generally do not + * block, so may overlap with update operations (including {@code put} + * and {@code remove}). Retrievals reflect the results of the most + * recently completed update operations holding upon their + * onset. (More formally, an update operation for a given key bears a + * happens-before relation with any (non-null) retrieval for + * that key reporting the updated value.) For aggregate operations + * such as {@code putAll} and {@code clear}, concurrent retrievals may + * reflect insertion or removal of only some entries. Similarly, + * Iterators and Enumerations return elements reflecting the state of + * the hash table at some point at or since the creation of the + * iterator/enumeration. They do not throw {@link + * ConcurrentModificationException}. However, iterators are designed + * to be used by only one thread at a time. Bear in mind that the + * results of aggregate status methods including {@code size}, {@code + * isEmpty}, and {@code containsValue} are typically useful only when + * a map is not undergoing concurrent updates in other threads. + * Otherwise the results of these methods reflect transient states + * that may be adequate for monitoring or estimation purposes, but not + * for program control. + * + *

The table is dynamically expanded when there are too many + * collisions (i.e., keys that have distinct hash codes but fall into + * the same slot modulo the table size), with the expected average + * effect of maintaining roughly two bins per mapping (corresponding + * to a 0.75 load factor threshold for resizing). There may be much + * variance around this average as mappings are added and removed, but + * overall, this maintains a commonly accepted time/space tradeoff for + * hash tables. However, resizing this or any other kind of hash + * table may be a relatively slow operation. When possible, it is a + * good idea to provide a size estimate as an optional {@code + * initialCapacity} constructor argument. An additional optional + * {@code loadFactor} constructor argument provides a further means of + * customizing initial table capacity by specifying the table density + * to be used in calculating the amount of space to allocate for the + * given number of elements. Also, for compatibility with previous + * versions of this class, constructors may optionally specify an + * expected {@code concurrencyLevel} as an additional hint for + * internal sizing. Note that using many keys with exactly the same + * {@code hashCode()} is a sure way to slow down performance of any + * hash table. + * + *

A {@link Set} projection of a ConcurrentHashMapV8 may be created + * (using {@link #newKeySet()} or {@link #newKeySet(int)}), or viewed + * (using {@link #keySet(Object)} when only keys are of interest, and the + * mapped values are (perhaps transiently) not used or all take the + * same mapping value. + * + *

A ConcurrentHashMapV8 can be used as scalable frequency map (a + * form of histogram or multiset) by using {@link LongAdder} values + * and initializing via {@link #computeIfAbsent}. For example, to add + * a count to a {@code ConcurrentHashMapV8 freqs}, you + * can use {@code freqs.computeIfAbsent(k -> new + * LongAdder()).increment();} + * + *

This class and its views and iterators implement all of the + * optional methods of the {@link Map} and {@link Iterator} + * interfaces. + * + *

Like {@link Hashtable} but unlike {@link HashMap}, this class + * does not allow {@code null} to be used as a key or value. + * + *

ConcurrentHashMapV8s support parallel operations using the {@link + * ForkJoinPool#commonPool}. (Tasks that may be used in other contexts + * are available in class {@link ForkJoinTasks}). These operations are + * designed to be safely, and often sensibly, applied even with maps + * that are being concurrently updated by other threads; for example, + * when computing a snapshot summary of the values in a shared + * registry. There are three kinds of operation, each with four + * forms, accepting functions with Keys, Values, Entries, and (Key, + * Value) arguments and/or return values. (The first three forms are + * also available via the {@link #keySet()}, {@link #values()} and + * {@link #entrySet()} views). Because the elements of a + * ConcurrentHashMapV8 are not ordered in any particular way, and may be + * processed in different orders in different parallel executions, the + * correctness of supplied functions should not depend on any + * ordering, or on any other objects or values that may transiently + * change while computation is in progress; and except for forEach + * actions, should ideally be side-effect-free. + * + *

+ * + *

The concurrency properties of bulk operations follow + * from those of ConcurrentHashMapV8: Any non-null result returned + * from {@code get(key)} and related access methods bears a + * happens-before relation with the associated insertion or + * update. The result of any bulk operation reflects the + * composition of these per-element relations (but is not + * necessarily atomic with respect to the map as a whole unless it + * is somehow known to be quiescent). Conversely, because keys + * and values in the map are never null, null serves as a reliable + * atomic indicator of the current lack of any result. To + * maintain this property, null serves as an implicit basis for + * all non-scalar reduction operations. For the double, long, and + * int versions, the basis should be one that, when combined with + * any other value, returns that other value (more formally, it + * should be the identity element for the reduction). Most common + * reductions have these properties; for example, computing a sum + * with basis 0 or a minimum with basis MAX_VALUE. + * + *

Search and transformation functions provided as arguments + * should similarly return null to indicate the lack of any result + * (in which case it is not used). In the case of mapped + * reductions, this also enables transformations to serve as + * filters, returning null (or, in the case of primitive + * specializations, the identity basis) if the element should not + * be combined. You can create compound transformations and + * filterings by composing them yourself under this "null means + * there is nothing there now" rule before using them in search or + * reduce operations. + * + *

Methods accepting and/or returning Entry arguments maintain + * key-value associations. They may be useful for example when + * finding the key for the greatest value. Note that "plain" Entry + * arguments can be supplied using {@code new + * AbstractMap.SimpleEntry(k,v)}. + * + *

Bulk operations may complete abruptly, throwing an + * exception encountered in the application of a supplied + * function. Bear in mind when handling such exceptions that other + * concurrently executing functions could also have thrown + * exceptions, or would have done so if the first exception had + * not occurred. + * + *

Parallel speedups for bulk operations compared to sequential + * processing are common but not guaranteed. Operations involving + * brief functions on small maps may execute more slowly than + * sequential loops if the underlying work to parallelize the + * computation is more expensive than the computation itself. + * Similarly, parallelization may not lead to much actual parallelism + * if all processors are busy performing unrelated tasks. + * + *

All arguments to all task methods must be non-null. + * + *

jsr166e note: During transition, this class + * uses nested functional interfaces with different names but the + * same forms as those expected for JDK8. + * + *

This class is a member of the + * + * Java Collections Framework. + * + * @since 1.5 + * @author Doug Lea + * @param the type of keys maintained by this map + * @param the type of mapped values + */ +public class ConcurrentHashMapV8 + implements ConcurrentMap, Serializable, ConcurrentHashMap { + private static final long serialVersionUID = 7249069246763182397L; + + /** + * A partitionable iterator. A Spliterator can be traversed + * directly, but can also be partitioned (before traversal) by + * creating another Spliterator that covers a non-overlapping + * portion of the elements, and so may be amenable to parallel + * execution. + * + *

This interface exports a subset of expected JDK8 + * functionality. + * + *

Sample usage: Here is one (of the several) ways to compute + * the sum of the values held in a map using the ForkJoin + * framework. As illustrated here, Spliterators are well suited to + * designs in which a task repeatedly splits off half its work + * into forked subtasks until small enough to process directly, + * and then joins these subtasks. Variants of this style can also + * be used in completion-based designs. + * + *

+     * {@code ConcurrentHashMapV8 m = ...
+     * // split as if have 8 * parallelism, for load balance
+     * int n = m.size();
+     * int p = aForkJoinPool.getParallelism() * 8;
+     * int split = (n < p)? n : p;
+     * long sum = aForkJoinPool.invoke(new SumValues(m.valueSpliterator(), split, null));
+     * // ...
+     * static class SumValues extends RecursiveTask {
+     *   final Spliterator s;
+     *   final int split;             // split while > 1
+     *   final SumValues nextJoin;    // records forked subtasks to join
+     *   SumValues(Spliterator s, int depth, SumValues nextJoin) {
+     *     this.s = s; this.depth = depth; this.nextJoin = nextJoin;
+     *   }
+     *   public Long compute() {
+     *     long sum = 0;
+     *     SumValues subtasks = null; // fork subtasks
+     *     for (int s = split >>> 1; s > 0; s >>>= 1)
+     *       (subtasks = new SumValues(s.split(), s, subtasks)).fork();
+     *     while (s.hasNext())        // directly process remaining elements
+     *       sum += s.next();
+     *     for (SumValues t = subtasks; t != null; t = t.nextJoin)
+     *       sum += t.join();         // collect subtask results
+     *     return sum;
+     *   }
+     * }
+     * }
+ */ + public static interface Spliterator extends Iterator { + /** + * Returns a Spliterator covering approximately half of the + * elements, guaranteed not to overlap with those subsequently + * returned by this Spliterator. After invoking this method, + * the current Spliterator will not produce any of + * the elements of the returned Spliterator, but the two + * Spliterators together will produce all of the elements that + * would have been produced by this Spliterator had this + * method not been called. The exact number of elements + * produced by the returned Spliterator is not guaranteed, and + * may be zero (i.e., with {@code hasNext()} reporting {@code + * false}) if this Spliterator cannot be further split. + * + * @return a Spliterator covering approximately half of the + * elements + * @throws IllegalStateException if this Spliterator has + * already commenced traversing elements + */ + Spliterator split(); + } + + + /* + * Overview: + * + * The primary design goal of this hash table is to maintain + * concurrent readability (typically method get(), but also + * iterators and related methods) while minimizing update + * contention. Secondary goals are to keep space consumption about + * the same or better than java.util.HashMap, and to support high + * initial insertion rates on an empty table by many threads. + * + * Each key-value mapping is held in a Node. Because Node fields + * can contain special values, they are defined using plain Object + * types. Similarly in turn, all internal methods that use them + * work off Object types. And similarly, so do the internal + * methods of auxiliary iterator and view classes. All public + * generic typed methods relay in/out of these internal methods, + * supplying null-checks and casts as needed. This also allows + * many of the public methods to be factored into a smaller number + * of internal methods (although sadly not so for the five + * variants of put-related operations). The validation-based + * approach explained below leads to a lot of code sprawl because + * retry-control precludes factoring into smaller methods. + * + * The table is lazily initialized to a power-of-two size upon the + * first insertion. Each bin in the table normally contains a + * list of Nodes (most often, the list has only zero or one Node). + * Table accesses require volatile/atomic reads, writes, and + * CASes. Because there is no other way to arrange this without + * adding further indirections, we use intrinsics + * (sun.misc.Unsafe) operations. The lists of nodes within bins + * are always accurately traversable under volatile reads, so long + * as lookups check hash code and non-nullness of value before + * checking key equality. + * + * We use the top two bits of Node hash fields for control + * purposes -- they are available anyway because of addressing + * constraints. As explained further below, these top bits are + * used as follows: + * 00 - Normal + * 01 - Locked + * 11 - Locked and may have a thread waiting for lock + * 10 - Node is a forwarding node + * + * The lower 30 bits of each Node's hash field contain a + * transformation of the key's hash code, except for forwarding + * nodes, for which the lower bits are zero (and so always have + * hash field == MOVED). + * + * Insertion (via put or its variants) of the first node in an + * empty bin is performed by just CASing it to the bin. This is + * by far the most common case for put operations under most + * key/hash distributions. Other update operations (insert, + * delete, and replace) require locks. We do not want to waste + * the space required to associate a distinct lock object with + * each bin, so instead use the first node of a bin list itself as + * a lock. Blocking support for these locks relies on the builtin + * "synchronized" monitors. However, we also need a tryLock + * construction, so we overlay these by using bits of the Node + * hash field for lock control (see above), and so normally use + * builtin monitors only for blocking and signalling using + * wait/notifyAll constructions. See Node.tryAwaitLock. + * + * Using the first node of a list as a lock does not by itself + * suffice though: When a node is locked, any update must first + * validate that it is still the first node after locking it, and + * retry if not. Because new nodes are always appended to lists, + * once a node is first in a bin, it remains first until deleted + * or the bin becomes invalidated (upon resizing). However, + * operations that only conditionally update may inspect nodes + * until the point of update. This is a converse of sorts to the + * lazy locking technique described by Herlihy & Shavit. + * + * The main disadvantage of per-bin locks is that other update + * operations on other nodes in a bin list protected by the same + * lock can stall, for example when user equals() or mapping + * functions take a long time. However, statistically, under + * random hash codes, this is not a common problem. Ideally, the + * frequency of nodes in bins follows a Poisson distribution + * (http://en.wikipedia.org/wiki/Poisson_distribution) with a + * parameter of about 0.5 on average, given the resizing threshold + * of 0.75, although with a large variance because of resizing + * granularity. Ignoring variance, the expected occurrences of + * list size k are (exp(-0.5) * pow(0.5, k) / factorial(k)). The + * first values are: + * + * 0: 0.60653066 + * 1: 0.30326533 + * 2: 0.07581633 + * 3: 0.01263606 + * 4: 0.00157952 + * 5: 0.00015795 + * 6: 0.00001316 + * 7: 0.00000094 + * 8: 0.00000006 + * more: less than 1 in ten million + * + * Lock contention probability for two threads accessing distinct + * elements is roughly 1 / (8 * #elements) under random hashes. + * + * Actual hash code distributions encountered in practice + * sometimes deviate significantly from uniform randomness. This + * includes the case when N > (1<<30), so some keys MUST collide. + * Similarly for dumb or hostile usages in which multiple keys are + * designed to have identical hash codes. Also, although we guard + * against the worst effects of this (see method spread), sets of + * hashes may differ only in bits that do not impact their bin + * index for a given power-of-two mask. So we use a secondary + * strategy that applies when the number of nodes in a bin exceeds + * a threshold, and at least one of the keys implements + * Comparable. These TreeBins use a balanced tree to hold nodes + * (a specialized form of red-black trees), bounding search time + * to O(log N). Each search step in a TreeBin is around twice as + * slow as in a regular list, but given that N cannot exceed + * (1<<64) (before running out of addresses) this bounds search + * steps, lock hold times, etc, to reasonable constants (roughly + * 100 nodes inspected per operation worst case) so long as keys + * are Comparable (which is very common -- String, Long, etc). + * TreeBin nodes (TreeNodes) also maintain the same "next" + * traversal pointers as regular nodes, so can be traversed in + * iterators in the same way. + * + * The table is resized when occupancy exceeds a percentage + * threshold (nominally, 0.75, but see below). Only a single + * thread performs the resize (using field "sizeCtl", to arrange + * exclusion), but the table otherwise remains usable for reads + * and updates. Resizing proceeds by transferring bins, one by + * one, from the table to the next table. Because we are using + * power-of-two expansion, the elements from each bin must either + * stay at same index, or move with a power of two offset. We + * eliminate unnecessary node creation by catching cases where old + * nodes can be reused because their next fields won't change. On + * average, only about one-sixth of them need cloning when a table + * doubles. The nodes they replace will be garbage collectable as + * soon as they are no longer referenced by any reader thread that + * may be in the midst of concurrently traversing table. Upon + * transfer, the old table bin contains only a special forwarding + * node (with hash field "MOVED") that contains the next table as + * its key. On encountering a forwarding node, access and update + * operations restart, using the new table. + * + * Each bin transfer requires its bin lock. However, unlike other + * cases, a transfer can skip a bin if it fails to acquire its + * lock, and revisit it later (unless it is a TreeBin). Method + * rebuild maintains a buffer of TRANSFER_BUFFER_SIZE bins that + * have been skipped because of failure to acquire a lock, and + * blocks only if none are available (i.e., only very rarely). + * The transfer operation must also ensure that all accessible + * bins in both the old and new table are usable by any traversal. + * When there are no lock acquisition failures, this is arranged + * simply by proceeding from the last bin (table.length - 1) up + * towards the first. Upon seeing a forwarding node, traversals + * (see class Iter) arrange to move to the new table + * without revisiting nodes. However, when any node is skipped + * during a transfer, all earlier table bins may have become + * visible, so are initialized with a reverse-forwarding node back + * to the old table until the new ones are established. (This + * sometimes requires transiently locking a forwarding node, which + * is possible under the above encoding.) These more expensive + * mechanics trigger only when necessary. + * + * The traversal scheme also applies to partial traversals of + * ranges of bins (via an alternate Traverser constructor) + * to support partitioned aggregate operations. Also, read-only + * operations give up if ever forwarded to a null table, which + * provides support for shutdown-style clearing, which is also not + * currently implemented. + * + * Lazy table initialization minimizes footprint until first use, + * and also avoids resizings when the first operation is from a + * putAll, constructor with map argument, or deserialization. + * These cases attempt to override the initial capacity settings, + * but harmlessly fail to take effect in cases of races. + * + * The element count is maintained using a LongAdder, which avoids + * contention on updates but can encounter cache thrashing if read + * too frequently during concurrent access. To avoid reading so + * often, resizing is attempted either when a bin lock is + * contended, or upon adding to a bin already holding two or more + * nodes (checked before adding in the xIfAbsent methods, after + * adding in others). Under uniform hash distributions, the + * probability of this occurring at threshold is around 13%, + * meaning that only about 1 in 8 puts check threshold (and after + * resizing, many fewer do so). But this approximation has high + * variance for small table sizes, so we check on any collision + * for sizes <= 64. The bulk putAll operation further reduces + * contention by only committing count updates upon these size + * checks. + * + * Maintaining API and serialization compatibility with previous + * versions of this class introduces several oddities. Mainly: We + * leave untouched but unused constructor arguments refering to + * concurrencyLevel. We accept a loadFactor constructor argument, + * but apply it only to initial table capacity (which is the only + * time that we can guarantee to honor it.) We also declare an + * unused "Segment" class that is instantiated in minimal form + * only when serializing. + */ + + /* ---------------- Constants -------------- */ + + /** + * The largest possible table capacity. This value must be + * exactly 1<<30 to stay within Java array allocation and indexing + * bounds for power of two table sizes, and is further required + * because the top two bits of 32bit hash fields are used for + * control purposes. + */ + private static final int MAXIMUM_CAPACITY = 1 << 30; + + /** + * The default initial table capacity. Must be a power of 2 + * (i.e., at least 1) and at most MAXIMUM_CAPACITY. + */ + private static final int DEFAULT_CAPACITY = 16; + + /** + * The largest possible (non-power of two) array size. + * Needed by toArray and related methods. + */ + static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8; + + /** + * The default concurrency level for this table. Unused but + * defined for compatibility with previous versions of this class. + */ + private static final int DEFAULT_CONCURRENCY_LEVEL = 16; + + /** + * The load factor for this table. Overrides of this value in + * constructors affect only the initial table capacity. The + * actual floating point value isn't normally used -- it is + * simpler to use expressions such as {@code n - (n >>> 2)} for + * the associated resizing threshold. + */ + private static final float LOAD_FACTOR = 0.75f; + + /** + * The buffer size for skipped bins during transfers. The + * value is arbitrary but should be large enough to avoid + * most locking stalls during resizes. + */ + private static final int TRANSFER_BUFFER_SIZE = 32; + + /** + * The bin count threshold for using a tree rather than list for a + * bin. The value reflects the approximate break-even point for + * using tree-based operations. + * Note that Doug's version defaults to 8, but when dealing with + * Ruby objects it is actually beneficial to avoid TreeNodes + * as long as possible as it usually means going into Ruby land. + */ + private static final int TREE_THRESHOLD = 16; + + /* + * Encodings for special uses of Node hash fields. See above for + * explanation. + */ + static final int MOVED = 0x80000000; // hash field for forwarding nodes + static final int LOCKED = 0x40000000; // set/tested only as a bit + static final int WAITING = 0xc0000000; // both bits set/tested together + static final int HASH_BITS = 0x3fffffff; // usable bits of normal node hash + + /* ---------------- Fields -------------- */ + + /** + * The array of bins. Lazily initialized upon first insertion. + * Size is always a power of two. Accessed directly by iterators. + */ + transient volatile AtomicReferenceArray table; + + /** + * The counter maintaining number of elements. + */ + private transient LongAdder counter; + + /** + * Table initialization and resizing control. When negative, the + * table is being initialized or resized. Otherwise, when table is + * null, holds the initial table size to use upon creation, or 0 + * for default. After initialization, holds the next element count + * value upon which to resize the table. + */ + private transient volatile int sizeCtl; + + // views + private transient KeySetView keySet; + private transient ValuesView values; + private transient EntrySetView entrySet; + + /** For serialization compatibility. Null unless serialized; see below */ + private Segment[] segments; + + static AtomicIntegerFieldUpdater SIZE_CTRL_UPDATER = AtomicIntegerFieldUpdater.newUpdater(ConcurrentHashMapV8.class, "sizeCtl"); + + /* ---------------- Table element access -------------- */ + + /* + * Volatile access methods are used for table elements as well as + * elements of in-progress next table while resizing. Uses are + * null checked by callers, and implicitly bounds-checked, relying + * on the invariants that tab arrays have non-zero size, and all + * indices are masked with (tab.length - 1) which is never + * negative and always less than length. Note that, to be correct + * wrt arbitrary concurrency errors by users, bounds checks must + * operate on local variables, which accounts for some odd-looking + * inline assignments below. + */ + + static final Node tabAt(AtomicReferenceArray tab, int i) { // used by Iter + return tab.get(i); + } + + private static final boolean casTabAt(AtomicReferenceArray tab, int i, Node c, Node v) { + return tab.compareAndSet(i, c, v); + } + + private static final void setTabAt(AtomicReferenceArray tab, int i, Node v) { + tab.set(i, v); + } + + /* ---------------- Nodes -------------- */ + + /** + * Key-value entry. Note that this is never exported out as a + * user-visible Map.Entry (see MapEntry below). Nodes with a hash + * field of MOVED are special, and do not contain user keys or + * values. Otherwise, keys are never null, and null val fields + * indicate that a node is in the process of being deleted or + * created. For purposes of read-only access, a key may be read + * before a val, but can only be used after checking val to be + * non-null. + */ + static class Node { + volatile int hash; + final Object key; + volatile Object val; + volatile Node next; + + static AtomicIntegerFieldUpdater HASH_UPDATER = AtomicIntegerFieldUpdater.newUpdater(Node.class, "hash"); + + Node(int hash, Object key, Object val, Node next) { + this.hash = hash; + this.key = key; + this.val = val; + this.next = next; + } + + /** CompareAndSet the hash field */ + final boolean casHash(int cmp, int val) { + return HASH_UPDATER.compareAndSet(this, cmp, val); + } + + /** The number of spins before blocking for a lock */ + static final int MAX_SPINS = + Runtime.getRuntime().availableProcessors() > 1 ? 64 : 1; + + /** + * Spins a while if LOCKED bit set and this node is the first + * of its bin, and then sets WAITING bits on hash field and + * blocks (once) if they are still set. It is OK for this + * method to return even if lock is not available upon exit, + * which enables these simple single-wait mechanics. + * + * The corresponding signalling operation is performed within + * callers: Upon detecting that WAITING has been set when + * unlocking lock (via a failed CAS from non-waiting LOCKED + * state), unlockers acquire the sync lock and perform a + * notifyAll. + * + * The initial sanity check on tab and bounds is not currently + * necessary in the only usages of this method, but enables + * use in other future contexts. + */ + final void tryAwaitLock(AtomicReferenceArray tab, int i) { + if (tab != null && i >= 0 && i < tab.length()) { // sanity check + int r = ThreadLocalRandom.current().nextInt(); // randomize spins + int spins = MAX_SPINS, h; + while (tabAt(tab, i) == this && ((h = hash) & LOCKED) != 0) { + if (spins >= 0) { + r ^= r << 1; r ^= r >>> 3; r ^= r << 10; // xorshift + if (r >= 0 && --spins == 0) + Thread.yield(); // yield before block + } + else if (casHash(h, h | WAITING)) { + synchronized (this) { + if (tabAt(tab, i) == this && + (hash & WAITING) == WAITING) { + try { + wait(); + } catch (InterruptedException ie) { + Thread.currentThread().interrupt(); + } + } + else + notifyAll(); // possibly won race vs signaller + } + break; + } + } + } + } + } + + /* ---------------- TreeBins -------------- */ + + /** + * Nodes for use in TreeBins + */ + static final class TreeNode extends Node { + TreeNode parent; // red-black tree links + TreeNode left; + TreeNode right; + TreeNode prev; // needed to unlink next upon deletion + boolean red; + + TreeNode(int hash, Object key, Object val, Node next, TreeNode parent) { + super(hash, key, val, next); + this.parent = parent; + } + } + + /** + * A specialized form of red-black tree for use in bins + * whose size exceeds a threshold. + * + * TreeBins use a special form of comparison for search and + * related operations (which is the main reason we cannot use + * existing collections such as TreeMaps). TreeBins contain + * Comparable elements, but may contain others, as well as + * elements that are Comparable but not necessarily Comparable + * for the same T, so we cannot invoke compareTo among them. To + * handle this, the tree is ordered primarily by hash value, then + * by getClass().getName() order, and then by Comparator order + * among elements of the same class. On lookup at a node, if + * elements are not comparable or compare as 0, both left and + * right children may need to be searched in the case of tied hash + * values. (This corresponds to the full list search that would be + * necessary if all elements were non-Comparable and had tied + * hashes.) The red-black balancing code is updated from + * pre-jdk-collections + * (http://gee.cs.oswego.edu/dl/classes/collections/RBCell.java) + * based in turn on Cormen, Leiserson, and Rivest "Introduction to + * Algorithms" (CLR). + * + * TreeBins also maintain a separate locking discipline than + * regular bins. Because they are forwarded via special MOVED + * nodes at bin heads (which can never change once established), + * we cannot use those nodes as locks. Instead, TreeBin + * extends AbstractQueuedSynchronizer to support a simple form of + * read-write lock. For update operations and table validation, + * the exclusive form of lock behaves in the same way as bin-head + * locks. However, lookups use shared read-lock mechanics to allow + * multiple readers in the absence of writers. Additionally, + * these lookups do not ever block: While the lock is not + * available, they proceed along the slow traversal path (via + * next-pointers) until the lock becomes available or the list is + * exhausted, whichever comes first. (These cases are not fast, + * but maximize aggregate expected throughput.) The AQS mechanics + * for doing this are straightforward. The lock state is held as + * AQS getState(). Read counts are negative; the write count (1) + * is positive. There are no signalling preferences among readers + * and writers. Since we don't need to export full Lock API, we + * just override the minimal AQS methods and use them directly. + */ + static final class TreeBin extends AbstractQueuedSynchronizer { + private static final long serialVersionUID = 2249069246763182397L; + transient TreeNode root; // root of tree + transient TreeNode first; // head of next-pointer list + + /* AQS overrides */ + public final boolean isHeldExclusively() { return getState() > 0; } + public final boolean tryAcquire(int ignore) { + if (compareAndSetState(0, 1)) { + setExclusiveOwnerThread(Thread.currentThread()); + return true; + } + return false; + } + public final boolean tryRelease(int ignore) { + setExclusiveOwnerThread(null); + setState(0); + return true; + } + public final int tryAcquireShared(int ignore) { + for (int c;;) { + if ((c = getState()) > 0) + return -1; + if (compareAndSetState(c, c -1)) + return 1; + } + } + public final boolean tryReleaseShared(int ignore) { + int c; + do {} while (!compareAndSetState(c = getState(), c + 1)); + return c == -1; + } + + /** From CLR */ + private void rotateLeft(TreeNode p) { + if (p != null) { + TreeNode r = p.right, pp, rl; + if ((rl = p.right = r.left) != null) + rl.parent = p; + if ((pp = r.parent = p.parent) == null) + root = r; + else if (pp.left == p) + pp.left = r; + else + pp.right = r; + r.left = p; + p.parent = r; + } + } + + /** From CLR */ + private void rotateRight(TreeNode p) { + if (p != null) { + TreeNode l = p.left, pp, lr; + if ((lr = p.left = l.right) != null) + lr.parent = p; + if ((pp = l.parent = p.parent) == null) + root = l; + else if (pp.right == p) + pp.right = l; + else + pp.left = l; + l.right = p; + p.parent = l; + } + } + + @SuppressWarnings("unchecked") final TreeNode getTreeNode + (int h, Object k, TreeNode p) { + return getTreeNode(h, (RubyObject)k, p); + } + + /** + * Returns the TreeNode (or null if not found) for the given key + * starting at given root. + */ + @SuppressWarnings("unchecked") final TreeNode getTreeNode + (int h, RubyObject k, TreeNode p) { + RubyClass c = k.getMetaClass(); boolean kNotComparable = !k.respondsTo("<=>"); + while (p != null) { + int dir, ph; RubyObject pk; RubyClass pc; + if ((ph = p.hash) == h) { + if ((pk = (RubyObject)p.key) == k || k.equals(pk)) + return p; + if (c != (pc = (RubyClass)pk.getMetaClass()) || + kNotComparable || + (dir = rubyCompare(k, pk)) == 0) { + dir = (c == pc) ? 0 : c.getName().compareTo(pc.getName()); + if (dir == 0) { // if still stuck, need to check both sides + TreeNode r = null, pl, pr; + // try to recurse on the right + if ((pr = p.right) != null && h >= pr.hash && (r = getTreeNode(h, k, pr)) != null) + return r; + // try to continue iterating on the left side + else if ((pl = p.left) != null && h <= pl.hash) + dir = -1; + else // no matching node found + return null; + } + } + } + else + dir = (h < ph) ? -1 : 1; + p = (dir > 0) ? p.right : p.left; + } + return null; + } + + int rubyCompare(RubyObject l, RubyObject r) { + ThreadContext context = l.getMetaClass().getRuntime().getCurrentContext(); + IRubyObject result; + try { + result = l.callMethod(context, "<=>", r); + } catch (RaiseException e) { + // handle objects "lying" about responding to <=>, ie: an Array containing non-comparable keys + if (context.runtime.getNoMethodError().isInstance(e.getException())) { + return 0; + } + throw e; + } + + return result.isNil() ? 0 : RubyNumeric.num2int(result.convertToInteger()); + } + + /** + * Wrapper for getTreeNode used by CHM.get. Tries to obtain + * read-lock to call getTreeNode, but during failure to get + * lock, searches along next links. + */ + final Object getValue(int h, Object k) { + Node r = null; + int c = getState(); // Must read lock state first + for (Node e = first; e != null; e = e.next) { + if (c <= 0 && compareAndSetState(c, c - 1)) { + try { + r = getTreeNode(h, k, root); + } finally { + releaseShared(0); + } + break; + } + else if ((e.hash & HASH_BITS) == h && k.equals(e.key)) { + r = e; + break; + } + else + c = getState(); + } + return r == null ? null : r.val; + } + + @SuppressWarnings("unchecked") final TreeNode putTreeNode + (int h, Object k, Object v) { + return putTreeNode(h, (RubyObject)k, v); + } + + /** + * Finds or adds a node. + * @return null if added + */ + @SuppressWarnings("unchecked") final TreeNode putTreeNode + (int h, RubyObject k, Object v) { + RubyClass c = k.getMetaClass(); + boolean kNotComparable = !k.respondsTo("<=>"); + TreeNode pp = root, p = null; + int dir = 0; + while (pp != null) { // find existing node or leaf to insert at + int ph; RubyObject pk; RubyClass pc; + p = pp; + if ((ph = p.hash) == h) { + if ((pk = (RubyObject)p.key) == k || k.equals(pk)) + return p; + if (c != (pc = pk.getMetaClass()) || + kNotComparable || + (dir = rubyCompare(k, pk)) == 0) { + dir = (c == pc) ? 0 : c.getName().compareTo(pc.getName()); + if (dir == 0) { // if still stuck, need to check both sides + TreeNode r = null, pr; + // try to recurse on the right + if ((pr = p.right) != null && h >= pr.hash && (r = getTreeNode(h, k, pr)) != null) + return r; + else // continue descending down the left subtree + dir = -1; + } + } + } + else + dir = (h < ph) ? -1 : 1; + pp = (dir > 0) ? p.right : p.left; + } + + TreeNode f = first; + TreeNode x = first = new TreeNode(h, (Object)k, v, f, p); + if (p == null) + root = x; + else { // attach and rebalance; adapted from CLR + TreeNode xp, xpp; + if (f != null) + f.prev = x; + if (dir <= 0) + p.left = x; + else + p.right = x; + x.red = true; + while (x != null && (xp = x.parent) != null && xp.red && + (xpp = xp.parent) != null) { + TreeNode xppl = xpp.left; + if (xp == xppl) { + TreeNode y = xpp.right; + if (y != null && y.red) { + y.red = false; + xp.red = false; + xpp.red = true; + x = xpp; + } + else { + if (x == xp.right) { + rotateLeft(x = xp); + xpp = (xp = x.parent) == null ? null : xp.parent; + } + if (xp != null) { + xp.red = false; + if (xpp != null) { + xpp.red = true; + rotateRight(xpp); + } + } + } + } + else { + TreeNode y = xppl; + if (y != null && y.red) { + y.red = false; + xp.red = false; + xpp.red = true; + x = xpp; + } + else { + if (x == xp.left) { + rotateRight(x = xp); + xpp = (xp = x.parent) == null ? null : xp.parent; + } + if (xp != null) { + xp.red = false; + if (xpp != null) { + xpp.red = true; + rotateLeft(xpp); + } + } + } + } + } + TreeNode r = root; + if (r != null && r.red) + r.red = false; + } + return null; + } + + /** + * Removes the given node, that must be present before this + * call. This is messier than typical red-black deletion code + * because we cannot swap the contents of an interior node + * with a leaf successor that is pinned by "next" pointers + * that are accessible independently of lock. So instead we + * swap the tree linkages. + */ + final void deleteTreeNode(TreeNode p) { + TreeNode next = (TreeNode)p.next; // unlink traversal pointers + TreeNode pred = p.prev; + if (pred == null) + first = next; + else + pred.next = next; + if (next != null) + next.prev = pred; + TreeNode replacement; + TreeNode pl = p.left; + TreeNode pr = p.right; + if (pl != null && pr != null) { + TreeNode s = pr, sl; + while ((sl = s.left) != null) // find successor + s = sl; + boolean c = s.red; s.red = p.red; p.red = c; // swap colors + TreeNode sr = s.right; + TreeNode pp = p.parent; + if (s == pr) { // p was s's direct parent + p.parent = s; + s.right = p; + } + else { + TreeNode sp = s.parent; + if ((p.parent = sp) != null) { + if (s == sp.left) + sp.left = p; + else + sp.right = p; + } + if ((s.right = pr) != null) + pr.parent = s; + } + p.left = null; + if ((p.right = sr) != null) + sr.parent = p; + if ((s.left = pl) != null) + pl.parent = s; + if ((s.parent = pp) == null) + root = s; + else if (p == pp.left) + pp.left = s; + else + pp.right = s; + replacement = sr; + } + else + replacement = (pl != null) ? pl : pr; + TreeNode pp = p.parent; + if (replacement == null) { + if (pp == null) { + root = null; + return; + } + replacement = p; + } + else { + replacement.parent = pp; + if (pp == null) + root = replacement; + else if (p == pp.left) + pp.left = replacement; + else + pp.right = replacement; + p.left = p.right = p.parent = null; + } + if (!p.red) { // rebalance, from CLR + TreeNode x = replacement; + while (x != null) { + TreeNode xp, xpl; + if (x.red || (xp = x.parent) == null) { + x.red = false; + break; + } + if (x == (xpl = xp.left)) { + TreeNode sib = xp.right; + if (sib != null && sib.red) { + sib.red = false; + xp.red = true; + rotateLeft(xp); + sib = (xp = x.parent) == null ? null : xp.right; + } + if (sib == null) + x = xp; + else { + TreeNode sl = sib.left, sr = sib.right; + if ((sr == null || !sr.red) && + (sl == null || !sl.red)) { + sib.red = true; + x = xp; + } + else { + if (sr == null || !sr.red) { + if (sl != null) + sl.red = false; + sib.red = true; + rotateRight(sib); + sib = (xp = x.parent) == null ? null : xp.right; + } + if (sib != null) { + sib.red = (xp == null) ? false : xp.red; + if ((sr = sib.right) != null) + sr.red = false; + } + if (xp != null) { + xp.red = false; + rotateLeft(xp); + } + x = root; + } + } + } + else { // symmetric + TreeNode sib = xpl; + if (sib != null && sib.red) { + sib.red = false; + xp.red = true; + rotateRight(xp); + sib = (xp = x.parent) == null ? null : xp.left; + } + if (sib == null) + x = xp; + else { + TreeNode sl = sib.left, sr = sib.right; + if ((sl == null || !sl.red) && + (sr == null || !sr.red)) { + sib.red = true; + x = xp; + } + else { + if (sl == null || !sl.red) { + if (sr != null) + sr.red = false; + sib.red = true; + rotateLeft(sib); + sib = (xp = x.parent) == null ? null : xp.left; + } + if (sib != null) { + sib.red = (xp == null) ? false : xp.red; + if ((sl = sib.left) != null) + sl.red = false; + } + if (xp != null) { + xp.red = false; + rotateRight(xp); + } + x = root; + } + } + } + } + } + if (p == replacement && (pp = p.parent) != null) { + if (p == pp.left) // detach pointers + pp.left = null; + else if (p == pp.right) + pp.right = null; + p.parent = null; + } + } + } + + /* ---------------- Collision reduction methods -------------- */ + + /** + * Spreads higher bits to lower, and also forces top 2 bits to 0. + * Because the table uses power-of-two masking, sets of hashes + * that vary only in bits above the current mask will always + * collide. (Among known examples are sets of Float keys holding + * consecutive whole numbers in small tables.) To counter this, + * we apply a transform that spreads the impact of higher bits + * downward. There is a tradeoff between speed, utility, and + * quality of bit-spreading. Because many common sets of hashes + * are already reasonably distributed across bits (so don't benefit + * from spreading), and because we use trees to handle large sets + * of collisions in bins, we don't need excessively high quality. + */ + private static final int spread(int h) { + h ^= (h >>> 18) ^ (h >>> 12); + return (h ^ (h >>> 10)) & HASH_BITS; + } + + /** + * Replaces a list bin with a tree bin. Call only when locked. + * Fails to replace if the given key is non-comparable or table + * is, or needs, resizing. + */ + private final void replaceWithTreeBin(AtomicReferenceArray tab, int index, Object key) { + if ((key instanceof Comparable) && + (tab.length() >= MAXIMUM_CAPACITY || counter.sum() < (long)sizeCtl)) { + TreeBin t = new TreeBin(); + for (Node e = tabAt(tab, index); e != null; e = e.next) + t.putTreeNode(e.hash & HASH_BITS, e.key, e.val); + setTabAt(tab, index, new Node(MOVED, t, null, null)); + } + } + + /* ---------------- Internal access and update methods -------------- */ + + /** Implementation for get and containsKey */ + private final Object internalGet(Object k) { + int h = spread(k.hashCode()); + retry: for (AtomicReferenceArray tab = table; tab != null;) { + Node e, p; Object ek, ev; int eh; // locals to read fields once + for (e = tabAt(tab, (tab.length() - 1) & h); e != null; e = e.next) { + if ((eh = e.hash) == MOVED) { + if ((ek = e.key) instanceof TreeBin) // search TreeBin + return ((TreeBin)ek).getValue(h, k); + else { // restart with new table + tab = (AtomicReferenceArray)ek; + continue retry; + } + } + else if ((eh & HASH_BITS) == h && (ev = e.val) != null && + ((ek = e.key) == k || k.equals(ek))) + return ev; + } + break; + } + return null; + } + + /** + * Implementation for the four public remove/replace methods: + * Replaces node value with v, conditional upon match of cv if + * non-null. If resulting value is null, delete. + */ + private final Object internalReplace(Object k, Object v, Object cv) { + int h = spread(k.hashCode()); + Object oldVal = null; + for (AtomicReferenceArray tab = table;;) { + Node f; int i, fh; Object fk; + if (tab == null || + (f = tabAt(tab, i = (tab.length() - 1) & h)) == null) + break; + else if ((fh = f.hash) == MOVED) { + if ((fk = f.key) instanceof TreeBin) { + TreeBin t = (TreeBin)fk; + boolean validated = false; + boolean deleted = false; + t.acquire(0); + try { + if (tabAt(tab, i) == f) { + validated = true; + TreeNode p = t.getTreeNode(h, k, t.root); + if (p != null) { + Object pv = p.val; + if (cv == null || cv == pv || cv.equals(pv)) { + oldVal = pv; + if ((p.val = v) == null) { + deleted = true; + t.deleteTreeNode(p); + } + } + } + } + } finally { + t.release(0); + } + if (validated) { + if (deleted) + counter.add(-1L); + break; + } + } + else + tab = (AtomicReferenceArray)fk; + } + else if ((fh & HASH_BITS) != h && f.next == null) // precheck + break; // rules out possible existence + else if ((fh & LOCKED) != 0) { + checkForResize(); // try resizing if can't get lock + f.tryAwaitLock(tab, i); + } + else if (f.casHash(fh, fh | LOCKED)) { + boolean validated = false; + boolean deleted = false; + try { + if (tabAt(tab, i) == f) { + validated = true; + for (Node e = f, pred = null;;) { + Object ek, ev; + if ((e.hash & HASH_BITS) == h && + ((ev = e.val) != null) && + ((ek = e.key) == k || k.equals(ek))) { + if (cv == null || cv == ev || cv.equals(ev)) { + oldVal = ev; + if ((e.val = v) == null) { + deleted = true; + Node en = e.next; + if (pred != null) + pred.next = en; + else + setTabAt(tab, i, en); + } + } + break; + } + pred = e; + if ((e = e.next) == null) + break; + } + } + } finally { + if (!f.casHash(fh | LOCKED, fh)) { + f.hash = fh; + synchronized (f) { f.notifyAll(); }; + } + } + if (validated) { + if (deleted) + counter.add(-1L); + break; + } + } + } + return oldVal; + } + + /* + * Internal versions of the six insertion methods, each a + * little more complicated than the last. All have + * the same basic structure as the first (internalPut): + * 1. If table uninitialized, create + * 2. If bin empty, try to CAS new node + * 3. If bin stale, use new table + * 4. if bin converted to TreeBin, validate and relay to TreeBin methods + * 5. Lock and validate; if valid, scan and add or update + * + * The others interweave other checks and/or alternative actions: + * * Plain put checks for and performs resize after insertion. + * * putIfAbsent prescans for mapping without lock (and fails to add + * if present), which also makes pre-emptive resize checks worthwhile. + * * computeIfAbsent extends form used in putIfAbsent with additional + * mechanics to deal with, calls, potential exceptions and null + * returns from function call. + * * compute uses the same function-call mechanics, but without + * the prescans + * * merge acts as putIfAbsent in the absent case, but invokes the + * update function if present + * * putAll attempts to pre-allocate enough table space + * and more lazily performs count updates and checks. + * + * Someday when details settle down a bit more, it might be worth + * some factoring to reduce sprawl. + */ + + /** Implementation for put */ + private final Object internalPut(Object k, Object v) { + int h = spread(k.hashCode()); + int count = 0; + for (AtomicReferenceArray tab = table;;) { + int i; Node f; int fh; Object fk; + if (tab == null) + tab = initTable(); + else if ((f = tabAt(tab, i = (tab.length() - 1) & h)) == null) { + if (casTabAt(tab, i, null, new Node(h, k, v, null))) + break; // no lock when adding to empty bin + } + else if ((fh = f.hash) == MOVED) { + if ((fk = f.key) instanceof TreeBin) { + TreeBin t = (TreeBin)fk; + Object oldVal = null; + t.acquire(0); + try { + if (tabAt(tab, i) == f) { + count = 2; + TreeNode p = t.putTreeNode(h, k, v); + if (p != null) { + oldVal = p.val; + p.val = v; + } + } + } finally { + t.release(0); + } + if (count != 0) { + if (oldVal != null) + return oldVal; + break; + } + } + else + tab = (AtomicReferenceArray)fk; + } + else if ((fh & LOCKED) != 0) { + checkForResize(); + f.tryAwaitLock(tab, i); + } + else if (f.casHash(fh, fh | LOCKED)) { + Object oldVal = null; + try { // needed in case equals() throws + if (tabAt(tab, i) == f) { + count = 1; + for (Node e = f;; ++count) { + Object ek, ev; + if ((e.hash & HASH_BITS) == h && + (ev = e.val) != null && + ((ek = e.key) == k || k.equals(ek))) { + oldVal = ev; + e.val = v; + break; + } + Node last = e; + if ((e = e.next) == null) { + last.next = new Node(h, k, v, null); + if (count >= TREE_THRESHOLD) + replaceWithTreeBin(tab, i, k); + break; + } + } + } + } finally { // unlock and signal if needed + if (!f.casHash(fh | LOCKED, fh)) { + f.hash = fh; + synchronized (f) { f.notifyAll(); }; + } + } + if (count != 0) { + if (oldVal != null) + return oldVal; + if (tab.length() <= 64) + count = 2; + break; + } + } + } + counter.add(1L); + if (count > 1) + checkForResize(); + return null; + } + + /** Implementation for putIfAbsent */ + private final Object internalPutIfAbsent(Object k, Object v) { + int h = spread(k.hashCode()); + int count = 0; + for (AtomicReferenceArray tab = table;;) { + int i; Node f; int fh; Object fk, fv; + if (tab == null) + tab = initTable(); + else if ((f = tabAt(tab, i = (tab.length() - 1) & h)) == null) { + if (casTabAt(tab, i, null, new Node(h, k, v, null))) + break; + } + else if ((fh = f.hash) == MOVED) { + if ((fk = f.key) instanceof TreeBin) { + TreeBin t = (TreeBin)fk; + Object oldVal = null; + t.acquire(0); + try { + if (tabAt(tab, i) == f) { + count = 2; + TreeNode p = t.putTreeNode(h, k, v); + if (p != null) + oldVal = p.val; + } + } finally { + t.release(0); + } + if (count != 0) { + if (oldVal != null) + return oldVal; + break; + } + } + else + tab = (AtomicReferenceArray)fk; + } + else if ((fh & HASH_BITS) == h && (fv = f.val) != null && + ((fk = f.key) == k || k.equals(fk))) + return fv; + else { + Node g = f.next; + if (g != null) { // at least 2 nodes -- search and maybe resize + for (Node e = g;;) { + Object ek, ev; + if ((e.hash & HASH_BITS) == h && (ev = e.val) != null && + ((ek = e.key) == k || k.equals(ek))) + return ev; + if ((e = e.next) == null) { + checkForResize(); + break; + } + } + } + if (((fh = f.hash) & LOCKED) != 0) { + checkForResize(); + f.tryAwaitLock(tab, i); + } + else if (tabAt(tab, i) == f && f.casHash(fh, fh | LOCKED)) { + Object oldVal = null; + try { + if (tabAt(tab, i) == f) { + count = 1; + for (Node e = f;; ++count) { + Object ek, ev; + if ((e.hash & HASH_BITS) == h && + (ev = e.val) != null && + ((ek = e.key) == k || k.equals(ek))) { + oldVal = ev; + break; + } + Node last = e; + if ((e = e.next) == null) { + last.next = new Node(h, k, v, null); + if (count >= TREE_THRESHOLD) + replaceWithTreeBin(tab, i, k); + break; + } + } + } + } finally { + if (!f.casHash(fh | LOCKED, fh)) { + f.hash = fh; + synchronized (f) { f.notifyAll(); }; + } + } + if (count != 0) { + if (oldVal != null) + return oldVal; + if (tab.length() <= 64) + count = 2; + break; + } + } + } + } + counter.add(1L); + if (count > 1) + checkForResize(); + return null; + } + + /** Implementation for computeIfAbsent */ + private final Object internalComputeIfAbsent(K k, + Fun mf) { + int h = spread(k.hashCode()); + Object val = null; + int count = 0; + for (AtomicReferenceArray tab = table;;) { + Node f; int i, fh; Object fk, fv; + if (tab == null) + tab = initTable(); + else if ((f = tabAt(tab, i = (tab.length() - 1) & h)) == null) { + Node node = new Node(fh = h | LOCKED, k, null, null); + if (casTabAt(tab, i, null, node)) { + count = 1; + try { + if ((val = mf.apply(k)) != null) + node.val = val; + } finally { + if (val == null) + setTabAt(tab, i, null); + if (!node.casHash(fh, h)) { + node.hash = h; + synchronized (node) { node.notifyAll(); }; + } + } + } + if (count != 0) + break; + } + else if ((fh = f.hash) == MOVED) { + if ((fk = f.key) instanceof TreeBin) { + TreeBin t = (TreeBin)fk; + boolean added = false; + t.acquire(0); + try { + if (tabAt(tab, i) == f) { + count = 1; + TreeNode p = t.getTreeNode(h, k, t.root); + if (p != null) + val = p.val; + else if ((val = mf.apply(k)) != null) { + added = true; + count = 2; + t.putTreeNode(h, k, val); + } + } + } finally { + t.release(0); + } + if (count != 0) { + if (!added) + return val; + break; + } + } + else + tab = (AtomicReferenceArray)fk; + } + else if ((fh & HASH_BITS) == h && (fv = f.val) != null && + ((fk = f.key) == k || k.equals(fk))) + return fv; + else { + Node g = f.next; + if (g != null) { + for (Node e = g;;) { + Object ek, ev; + if ((e.hash & HASH_BITS) == h && (ev = e.val) != null && + ((ek = e.key) == k || k.equals(ek))) + return ev; + if ((e = e.next) == null) { + checkForResize(); + break; + } + } + } + if (((fh = f.hash) & LOCKED) != 0) { + checkForResize(); + f.tryAwaitLock(tab, i); + } + else if (tabAt(tab, i) == f && f.casHash(fh, fh | LOCKED)) { + boolean added = false; + try { + if (tabAt(tab, i) == f) { + count = 1; + for (Node e = f;; ++count) { + Object ek, ev; + if ((e.hash & HASH_BITS) == h && + (ev = e.val) != null && + ((ek = e.key) == k || k.equals(ek))) { + val = ev; + break; + } + Node last = e; + if ((e = e.next) == null) { + if ((val = mf.apply(k)) != null) { + added = true; + last.next = new Node(h, k, val, null); + if (count >= TREE_THRESHOLD) + replaceWithTreeBin(tab, i, k); + } + break; + } + } + } + } finally { + if (!f.casHash(fh | LOCKED, fh)) { + f.hash = fh; + synchronized (f) { f.notifyAll(); }; + } + } + if (count != 0) { + if (!added) + return val; + if (tab.length() <= 64) + count = 2; + break; + } + } + } + } + if (val != null) { + counter.add(1L); + if (count > 1) + checkForResize(); + } + return val; + } + + /** Implementation for compute */ + @SuppressWarnings("unchecked") private final Object internalCompute + (K k, boolean onlyIfPresent, BiFun mf) { + int h = spread(k.hashCode()); + Object val = null; + int delta = 0; + int count = 0; + for (AtomicReferenceArray tab = table;;) { + Node f; int i, fh; Object fk; + if (tab == null) + tab = initTable(); + else if ((f = tabAt(tab, i = (tab.length() - 1) & h)) == null) { + if (onlyIfPresent) + break; + Node node = new Node(fh = h | LOCKED, k, null, null); + if (casTabAt(tab, i, null, node)) { + try { + count = 1; + if ((val = mf.apply(k, null)) != null) { + node.val = val; + delta = 1; + } + } finally { + if (delta == 0) + setTabAt(tab, i, null); + if (!node.casHash(fh, h)) { + node.hash = h; + synchronized (node) { node.notifyAll(); }; + } + } + } + if (count != 0) + break; + } + else if ((fh = f.hash) == MOVED) { + if ((fk = f.key) instanceof TreeBin) { + TreeBin t = (TreeBin)fk; + t.acquire(0); + try { + if (tabAt(tab, i) == f) { + count = 1; + TreeNode p = t.getTreeNode(h, k, t.root); + Object pv; + if (p == null) { + if (onlyIfPresent) + break; + pv = null; + } else + pv = p.val; + if ((val = mf.apply(k, (V)pv)) != null) { + if (p != null) + p.val = val; + else { + count = 2; + delta = 1; + t.putTreeNode(h, k, val); + } + } + else if (p != null) { + delta = -1; + t.deleteTreeNode(p); + } + } + } finally { + t.release(0); + } + if (count != 0) + break; + } + else + tab = (AtomicReferenceArray)fk; + } + else if ((fh & LOCKED) != 0) { + checkForResize(); + f.tryAwaitLock(tab, i); + } + else if (f.casHash(fh, fh | LOCKED)) { + try { + if (tabAt(tab, i) == f) { + count = 1; + for (Node e = f, pred = null;; ++count) { + Object ek, ev; + if ((e.hash & HASH_BITS) == h && + (ev = e.val) != null && + ((ek = e.key) == k || k.equals(ek))) { + val = mf.apply(k, (V)ev); + if (val != null) + e.val = val; + else { + delta = -1; + Node en = e.next; + if (pred != null) + pred.next = en; + else + setTabAt(tab, i, en); + } + break; + } + pred = e; + if ((e = e.next) == null) { + if (!onlyIfPresent && (val = mf.apply(k, null)) != null) { + pred.next = new Node(h, k, val, null); + delta = 1; + if (count >= TREE_THRESHOLD) + replaceWithTreeBin(tab, i, k); + } + break; + } + } + } + } finally { + if (!f.casHash(fh | LOCKED, fh)) { + f.hash = fh; + synchronized (f) { f.notifyAll(); }; + } + } + if (count != 0) { + if (tab.length() <= 64) + count = 2; + break; + } + } + } + if (delta != 0) { + counter.add((long)delta); + if (count > 1) + checkForResize(); + } + return val; + } + + /** Implementation for merge */ + @SuppressWarnings("unchecked") private final Object internalMerge + (K k, V v, BiFun mf) { + int h = spread(k.hashCode()); + Object val = null; + int delta = 0; + int count = 0; + for (AtomicReferenceArray tab = table;;) { + int i; Node f; int fh; Object fk, fv; + if (tab == null) + tab = initTable(); + else if ((f = tabAt(tab, i = (tab.length() - 1) & h)) == null) { + if (casTabAt(tab, i, null, new Node(h, k, v, null))) { + delta = 1; + val = v; + break; + } + } + else if ((fh = f.hash) == MOVED) { + if ((fk = f.key) instanceof TreeBin) { + TreeBin t = (TreeBin)fk; + t.acquire(0); + try { + if (tabAt(tab, i) == f) { + count = 1; + TreeNode p = t.getTreeNode(h, k, t.root); + val = (p == null) ? v : mf.apply((V)p.val, v); + if (val != null) { + if (p != null) + p.val = val; + else { + count = 2; + delta = 1; + t.putTreeNode(h, k, val); + } + } + else if (p != null) { + delta = -1; + t.deleteTreeNode(p); + } + } + } finally { + t.release(0); + } + if (count != 0) + break; + } + else + tab = (AtomicReferenceArray)fk; + } + else if ((fh & LOCKED) != 0) { + checkForResize(); + f.tryAwaitLock(tab, i); + } + else if (f.casHash(fh, fh | LOCKED)) { + try { + if (tabAt(tab, i) == f) { + count = 1; + for (Node e = f, pred = null;; ++count) { + Object ek, ev; + if ((e.hash & HASH_BITS) == h && + (ev = e.val) != null && + ((ek = e.key) == k || k.equals(ek))) { + val = mf.apply((V)ev, v); + if (val != null) + e.val = val; + else { + delta = -1; + Node en = e.next; + if (pred != null) + pred.next = en; + else + setTabAt(tab, i, en); + } + break; + } + pred = e; + if ((e = e.next) == null) { + val = v; + pred.next = new Node(h, k, val, null); + delta = 1; + if (count >= TREE_THRESHOLD) + replaceWithTreeBin(tab, i, k); + break; + } + } + } + } finally { + if (!f.casHash(fh | LOCKED, fh)) { + f.hash = fh; + synchronized (f) { f.notifyAll(); }; + } + } + if (count != 0) { + if (tab.length() <= 64) + count = 2; + break; + } + } + } + if (delta != 0) { + counter.add((long)delta); + if (count > 1) + checkForResize(); + } + return val; + } + + /** Implementation for putAll */ + private final void internalPutAll(Map m) { + tryPresize(m.size()); + long delta = 0L; // number of uncommitted additions + boolean npe = false; // to throw exception on exit for nulls + try { // to clean up counts on other exceptions + for (Map.Entry entry : m.entrySet()) { + Object k, v; + if (entry == null || (k = entry.getKey()) == null || + (v = entry.getValue()) == null) { + npe = true; + break; + } + int h = spread(k.hashCode()); + for (AtomicReferenceArray tab = table;;) { + int i; Node f; int fh; Object fk; + if (tab == null) + tab = initTable(); + else if ((f = tabAt(tab, i = (tab.length() - 1) & h)) == null){ + if (casTabAt(tab, i, null, new Node(h, k, v, null))) { + ++delta; + break; + } + } + else if ((fh = f.hash) == MOVED) { + if ((fk = f.key) instanceof TreeBin) { + TreeBin t = (TreeBin)fk; + boolean validated = false; + t.acquire(0); + try { + if (tabAt(tab, i) == f) { + validated = true; + TreeNode p = t.getTreeNode(h, k, t.root); + if (p != null) + p.val = v; + else { + t.putTreeNode(h, k, v); + ++delta; + } + } + } finally { + t.release(0); + } + if (validated) + break; + } + else + tab = (AtomicReferenceArray)fk; + } + else if ((fh & LOCKED) != 0) { + counter.add(delta); + delta = 0L; + checkForResize(); + f.tryAwaitLock(tab, i); + } + else if (f.casHash(fh, fh | LOCKED)) { + int count = 0; + try { + if (tabAt(tab, i) == f) { + count = 1; + for (Node e = f;; ++count) { + Object ek, ev; + if ((e.hash & HASH_BITS) == h && + (ev = e.val) != null && + ((ek = e.key) == k || k.equals(ek))) { + e.val = v; + break; + } + Node last = e; + if ((e = e.next) == null) { + ++delta; + last.next = new Node(h, k, v, null); + if (count >= TREE_THRESHOLD) + replaceWithTreeBin(tab, i, k); + break; + } + } + } + } finally { + if (!f.casHash(fh | LOCKED, fh)) { + f.hash = fh; + synchronized (f) { f.notifyAll(); }; + } + } + if (count != 0) { + if (count > 1) { + counter.add(delta); + delta = 0L; + checkForResize(); + } + break; + } + } + } + } + } finally { + if (delta != 0) + counter.add(delta); + } + if (npe) + throw new NullPointerException(); + } + + /* ---------------- Table Initialization and Resizing -------------- */ + + /** + * Returns a power of two table size for the given desired capacity. + * See Hackers Delight, sec 3.2 + */ + private static final int tableSizeFor(int c) { + int n = c - 1; + n |= n >>> 1; + n |= n >>> 2; + n |= n >>> 4; + n |= n >>> 8; + n |= n >>> 16; + return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1; + } + + /** + * Initializes table, using the size recorded in sizeCtl. + */ + private final AtomicReferenceArray initTable() { + AtomicReferenceArray tab; int sc; + while ((tab = table) == null) { + if ((sc = sizeCtl) < 0) + Thread.yield(); // lost initialization race; just spin + else if (SIZE_CTRL_UPDATER.compareAndSet(this, sc, -1)) { + try { + if ((tab = table) == null) { + int n = (sc > 0) ? sc : DEFAULT_CAPACITY; + tab = table = new AtomicReferenceArray(n); + sc = n - (n >>> 2); + } + } finally { + sizeCtl = sc; + } + break; + } + } + return tab; + } + + /** + * If table is too small and not already resizing, creates next + * table and transfers bins. Rechecks occupancy after a transfer + * to see if another resize is already needed because resizings + * are lagging additions. + */ + private final void checkForResize() { + AtomicReferenceArray tab; int n, sc; + while ((tab = table) != null && + (n = tab.length()) < MAXIMUM_CAPACITY && + (sc = sizeCtl) >= 0 && counter.sum() >= (long)sc && + SIZE_CTRL_UPDATER.compareAndSet(this, sc, -1)) { + try { + if (tab == table) { + table = rebuild(tab); + sc = (n << 1) - (n >>> 1); + } + } finally { + sizeCtl = sc; + } + } + } + + /** + * Tries to presize table to accommodate the given number of elements. + * + * @param size number of elements (doesn't need to be perfectly accurate) + */ + private final void tryPresize(int size) { + int c = (size >= (MAXIMUM_CAPACITY >>> 1)) ? MAXIMUM_CAPACITY : + tableSizeFor(size + (size >>> 1) + 1); + int sc; + while ((sc = sizeCtl) >= 0) { + AtomicReferenceArray tab = table; int n; + if (tab == null || (n = tab.length()) == 0) { + n = (sc > c) ? sc : c; + if (SIZE_CTRL_UPDATER.compareAndSet(this, sc, -1)) { + try { + if (table == tab) { + table = new AtomicReferenceArray(n); + sc = n - (n >>> 2); + } + } finally { + sizeCtl = sc; + } + } + } + else if (c <= sc || n >= MAXIMUM_CAPACITY) + break; + else if (SIZE_CTRL_UPDATER.compareAndSet(this, sc, -1)) { + try { + if (table == tab) { + table = rebuild(tab); + sc = (n << 1) - (n >>> 1); + } + } finally { + sizeCtl = sc; + } + } + } + } + + /* + * Moves and/or copies the nodes in each bin to new table. See + * above for explanation. + * + * @return the new table + */ + private static final AtomicReferenceArray rebuild(AtomicReferenceArray tab) { + int n = tab.length(); + AtomicReferenceArray nextTab = new AtomicReferenceArray(n << 1); + Node fwd = new Node(MOVED, nextTab, null, null); + int[] buffer = null; // holds bins to revisit; null until needed + Node rev = null; // reverse forwarder; null until needed + int nbuffered = 0; // the number of bins in buffer list + int bufferIndex = 0; // buffer index of current buffered bin + int bin = n - 1; // current non-buffered bin or -1 if none + + for (int i = bin;;) { // start upwards sweep + int fh; Node f; + if ((f = tabAt(tab, i)) == null) { + if (bin >= 0) { // Unbuffered; no lock needed (or available) + if (!casTabAt(tab, i, f, fwd)) + continue; + } + else { // transiently use a locked forwarding node + Node g = new Node(MOVED|LOCKED, nextTab, null, null); + if (!casTabAt(tab, i, f, g)) + continue; + setTabAt(nextTab, i, null); + setTabAt(nextTab, i + n, null); + setTabAt(tab, i, fwd); + if (!g.casHash(MOVED|LOCKED, MOVED)) { + g.hash = MOVED; + synchronized (g) { g.notifyAll(); } + } + } + } + else if ((fh = f.hash) == MOVED) { + Object fk = f.key; + if (fk instanceof TreeBin) { + TreeBin t = (TreeBin)fk; + boolean validated = false; + t.acquire(0); + try { + if (tabAt(tab, i) == f) { + validated = true; + splitTreeBin(nextTab, i, t); + setTabAt(tab, i, fwd); + } + } finally { + t.release(0); + } + if (!validated) + continue; + } + } + else if ((fh & LOCKED) == 0 && f.casHash(fh, fh|LOCKED)) { + boolean validated = false; + try { // split to lo and hi lists; copying as needed + if (tabAt(tab, i) == f) { + validated = true; + splitBin(nextTab, i, f); + setTabAt(tab, i, fwd); + } + } finally { + if (!f.casHash(fh | LOCKED, fh)) { + f.hash = fh; + synchronized (f) { f.notifyAll(); }; + } + } + if (!validated) + continue; + } + else { + if (buffer == null) // initialize buffer for revisits + buffer = new int[TRANSFER_BUFFER_SIZE]; + if (bin < 0 && bufferIndex > 0) { + int j = buffer[--bufferIndex]; + buffer[bufferIndex] = i; + i = j; // swap with another bin + continue; + } + if (bin < 0 || nbuffered >= TRANSFER_BUFFER_SIZE) { + f.tryAwaitLock(tab, i); + continue; // no other options -- block + } + if (rev == null) // initialize reverse-forwarder + rev = new Node(MOVED, tab, null, null); + if (tabAt(tab, i) != f || (f.hash & LOCKED) == 0) + continue; // recheck before adding to list + buffer[nbuffered++] = i; + setTabAt(nextTab, i, rev); // install place-holders + setTabAt(nextTab, i + n, rev); + } + + if (bin > 0) + i = --bin; + else if (buffer != null && nbuffered > 0) { + bin = -1; + i = buffer[bufferIndex = --nbuffered]; + } + else + return nextTab; + } + } + + /** + * Splits a normal bin with list headed by e into lo and hi parts; + * installs in given table. + */ + private static void splitBin(AtomicReferenceArray nextTab, int i, Node e) { + int bit = nextTab.length() >>> 1; // bit to split on + int runBit = e.hash & bit; + Node lastRun = e, lo = null, hi = null; + for (Node p = e.next; p != null; p = p.next) { + int b = p.hash & bit; + if (b != runBit) { + runBit = b; + lastRun = p; + } + } + if (runBit == 0) + lo = lastRun; + else + hi = lastRun; + for (Node p = e; p != lastRun; p = p.next) { + int ph = p.hash & HASH_BITS; + Object pk = p.key, pv = p.val; + if ((ph & bit) == 0) + lo = new Node(ph, pk, pv, lo); + else + hi = new Node(ph, pk, pv, hi); + } + setTabAt(nextTab, i, lo); + setTabAt(nextTab, i + bit, hi); + } + + /** + * Splits a tree bin into lo and hi parts; installs in given table. + */ + private static void splitTreeBin(AtomicReferenceArray nextTab, int i, TreeBin t) { + int bit = nextTab.length() >>> 1; + TreeBin lt = new TreeBin(); + TreeBin ht = new TreeBin(); + int lc = 0, hc = 0; + for (Node e = t.first; e != null; e = e.next) { + int h = e.hash & HASH_BITS; + Object k = e.key, v = e.val; + if ((h & bit) == 0) { + ++lc; + lt.putTreeNode(h, k, v); + } + else { + ++hc; + ht.putTreeNode(h, k, v); + } + } + Node ln, hn; // throw away trees if too small + if (lc <= (TREE_THRESHOLD >>> 1)) { + ln = null; + for (Node p = lt.first; p != null; p = p.next) + ln = new Node(p.hash, p.key, p.val, ln); + } + else + ln = new Node(MOVED, lt, null, null); + setTabAt(nextTab, i, ln); + if (hc <= (TREE_THRESHOLD >>> 1)) { + hn = null; + for (Node p = ht.first; p != null; p = p.next) + hn = new Node(p.hash, p.key, p.val, hn); + } + else + hn = new Node(MOVED, ht, null, null); + setTabAt(nextTab, i + bit, hn); + } + + /** + * Implementation for clear. Steps through each bin, removing all + * nodes. + */ + private final void internalClear() { + long delta = 0L; // negative number of deletions + int i = 0; + AtomicReferenceArray tab = table; + while (tab != null && i < tab.length()) { + int fh; Object fk; + Node f = tabAt(tab, i); + if (f == null) + ++i; + else if ((fh = f.hash) == MOVED) { + if ((fk = f.key) instanceof TreeBin) { + TreeBin t = (TreeBin)fk; + t.acquire(0); + try { + if (tabAt(tab, i) == f) { + for (Node p = t.first; p != null; p = p.next) { + if (p.val != null) { // (currently always true) + p.val = null; + --delta; + } + } + t.first = null; + t.root = null; + ++i; + } + } finally { + t.release(0); + } + } + else + tab = (AtomicReferenceArray)fk; + } + else if ((fh & LOCKED) != 0) { + counter.add(delta); // opportunistically update count + delta = 0L; + f.tryAwaitLock(tab, i); + } + else if (f.casHash(fh, fh | LOCKED)) { + try { + if (tabAt(tab, i) == f) { + for (Node e = f; e != null; e = e.next) { + if (e.val != null) { // (currently always true) + e.val = null; + --delta; + } + } + setTabAt(tab, i, null); + ++i; + } + } finally { + if (!f.casHash(fh | LOCKED, fh)) { + f.hash = fh; + synchronized (f) { f.notifyAll(); }; + } + } + } + } + if (delta != 0) + counter.add(delta); + } + + /* ----------------Table Traversal -------------- */ + + /** + * Encapsulates traversal for methods such as containsValue; also + * serves as a base class for other iterators and bulk tasks. + * + * At each step, the iterator snapshots the key ("nextKey") and + * value ("nextVal") of a valid node (i.e., one that, at point of + * snapshot, has a non-null user value). Because val fields can + * change (including to null, indicating deletion), field nextVal + * might not be accurate at point of use, but still maintains the + * weak consistency property of holding a value that was once + * valid. To support iterator.remove, the nextKey field is not + * updated (nulled out) when the iterator cannot advance. + * + * Internal traversals directly access these fields, as in: + * {@code while (it.advance() != null) { process(it.nextKey); }} + * + * Exported iterators must track whether the iterator has advanced + * (in hasNext vs next) (by setting/checking/nulling field + * nextVal), and then extract key, value, or key-value pairs as + * return values of next(). + * + * The iterator visits once each still-valid node that was + * reachable upon iterator construction. It might miss some that + * were added to a bin after the bin was visited, which is OK wrt + * consistency guarantees. Maintaining this property in the face + * of possible ongoing resizes requires a fair amount of + * bookkeeping state that is difficult to optimize away amidst + * volatile accesses. Even so, traversal maintains reasonable + * throughput. + * + * Normally, iteration proceeds bin-by-bin traversing lists. + * However, if the table has been resized, then all future steps + * must traverse both the bin at the current index as well as at + * (index + baseSize); and so on for further resizings. To + * paranoically cope with potential sharing by users of iterators + * across threads, iteration terminates if a bounds checks fails + * for a table read. + * + * This class extends ForkJoinTask to streamline parallel + * iteration in bulk operations (see BulkTask). This adds only an + * int of space overhead, which is close enough to negligible in + * cases where it is not needed to not worry about it. Because + * ForkJoinTask is Serializable, but iterators need not be, we + * need to add warning suppressions. + */ + @SuppressWarnings("serial") static class Traverser { + final ConcurrentHashMapV8 map; + Node next; // the next entry to use + K nextKey; // cached key field of next + V nextVal; // cached val field of next + AtomicReferenceArray tab; // current table; updated if resized + int index; // index of bin to use next + int baseIndex; // current index of initial table + int baseLimit; // index bound for initial table + int baseSize; // initial table size + + /** Creates iterator for all entries in the table. */ + Traverser(ConcurrentHashMapV8 map) { + this.map = map; + } + + /** Creates iterator for split() methods */ + Traverser(Traverser it) { + ConcurrentHashMapV8 m; AtomicReferenceArray t; + if ((m = this.map = it.map) == null) + t = null; + else if ((t = it.tab) == null && // force parent tab initialization + (t = it.tab = m.table) != null) + it.baseLimit = it.baseSize = t.length(); + this.tab = t; + this.baseSize = it.baseSize; + it.baseLimit = this.index = this.baseIndex = + ((this.baseLimit = it.baseLimit) + it.baseIndex + 1) >>> 1; + } + + /** + * Advances next; returns nextVal or null if terminated. + * See above for explanation. + */ + final V advance() { + Node e = next; + V ev = null; + outer: do { + if (e != null) // advance past used/skipped node + e = e.next; + while (e == null) { // get to next non-null bin + ConcurrentHashMapV8 m; + AtomicReferenceArray t; int b, i, n; Object ek; // checks must use locals + if ((t = tab) != null) + n = t.length(); + else if ((m = map) != null && (t = tab = m.table) != null) + n = baseLimit = baseSize = t.length(); + else + break outer; + if ((b = baseIndex) >= baseLimit || + (i = index) < 0 || i >= n) + break outer; + if ((e = tabAt(t, i)) != null && e.hash == MOVED) { + if ((ek = e.key) instanceof TreeBin) + e = ((TreeBin)ek).first; + else { + tab = (AtomicReferenceArray)ek; + continue; // restarts due to null val + } + } // visit upper slots if present + index = (i += baseSize) < n ? i : (baseIndex = b + 1); + } + nextKey = (K) e.key; + } while ((ev = (V) e.val) == null); // skip deleted or special nodes + next = e; + return nextVal = ev; + } + + public final void remove() { + Object k = nextKey; + if (k == null && (advance() == null || (k = nextKey) == null)) + throw new IllegalStateException(); + map.internalReplace(k, null, null); + } + + public final boolean hasNext() { + return nextVal != null || advance() != null; + } + + public final boolean hasMoreElements() { return hasNext(); } + public final void setRawResult(Object x) { } + public R getRawResult() { return null; } + public boolean exec() { return true; } + } + + /* ---------------- Public operations -------------- */ + + /** + * Creates a new, empty map with the default initial table size (16). + */ + public ConcurrentHashMapV8() { + this.counter = new LongAdder(); + } + + /** + * Creates a new, empty map with an initial table size + * accommodating the specified number of elements without the need + * to dynamically resize. + * + * @param initialCapacity The implementation performs internal + * sizing to accommodate this many elements. + * @throws IllegalArgumentException if the initial capacity of + * elements is negative + */ + public ConcurrentHashMapV8(int initialCapacity) { + if (initialCapacity < 0) + throw new IllegalArgumentException(); + int cap = ((initialCapacity >= (MAXIMUM_CAPACITY >>> 1)) ? + MAXIMUM_CAPACITY : + tableSizeFor(initialCapacity + (initialCapacity >>> 1) + 1)); + this.counter = new LongAdder(); + this.sizeCtl = cap; + } + + /** + * Creates a new map with the same mappings as the given map. + * + * @param m the map + */ + public ConcurrentHashMapV8(Map m) { + this.counter = new LongAdder(); + this.sizeCtl = DEFAULT_CAPACITY; + internalPutAll(m); + } + + /** + * Creates a new, empty map with an initial table size based on + * the given number of elements ({@code initialCapacity}) and + * initial table density ({@code loadFactor}). + * + * @param initialCapacity the initial capacity. The implementation + * performs internal sizing to accommodate this many elements, + * given the specified load factor. + * @param loadFactor the load factor (table density) for + * establishing the initial table size + * @throws IllegalArgumentException if the initial capacity of + * elements is negative or the load factor is nonpositive + * + * @since 1.6 + */ + public ConcurrentHashMapV8(int initialCapacity, float loadFactor) { + this(initialCapacity, loadFactor, 1); + } + + /** + * Creates a new, empty map with an initial table size based on + * the given number of elements ({@code initialCapacity}), table + * density ({@code loadFactor}), and number of concurrently + * updating threads ({@code concurrencyLevel}). + * + * @param initialCapacity the initial capacity. The implementation + * performs internal sizing to accommodate this many elements, + * given the specified load factor. + * @param loadFactor the load factor (table density) for + * establishing the initial table size + * @param concurrencyLevel the estimated number of concurrently + * updating threads. The implementation may use this value as + * a sizing hint. + * @throws IllegalArgumentException if the initial capacity is + * negative or the load factor or concurrencyLevel are + * nonpositive + */ + public ConcurrentHashMapV8(int initialCapacity, + float loadFactor, int concurrencyLevel) { + if (!(loadFactor > 0.0f) || initialCapacity < 0 || concurrencyLevel <= 0) + throw new IllegalArgumentException(); + if (initialCapacity < concurrencyLevel) // Use at least as many bins + initialCapacity = concurrencyLevel; // as estimated threads + long size = (long)(1.0 + (long)initialCapacity / loadFactor); + int cap = (size >= (long)MAXIMUM_CAPACITY) ? + MAXIMUM_CAPACITY : tableSizeFor((int)size); + this.counter = new LongAdder(); + this.sizeCtl = cap; + } + + /** + * Creates a new {@link Set} backed by a ConcurrentHashMapV8 + * from the given type to {@code Boolean.TRUE}. + * + * @return the new set + */ + public static KeySetView newKeySet() { + return new KeySetView(new ConcurrentHashMapV8(), + Boolean.TRUE); + } + + /** + * Creates a new {@link Set} backed by a ConcurrentHashMapV8 + * from the given type to {@code Boolean.TRUE}. + * + * @param initialCapacity The implementation performs internal + * sizing to accommodate this many elements. + * @throws IllegalArgumentException if the initial capacity of + * elements is negative + * @return the new set + */ + public static KeySetView newKeySet(int initialCapacity) { + return new KeySetView(new ConcurrentHashMapV8(initialCapacity), + Boolean.TRUE); + } + + /** + * {@inheritDoc} + */ + public boolean isEmpty() { + return counter.sum() <= 0L; // ignore transient negative values + } + + /** + * {@inheritDoc} + */ + public int size() { + long n = counter.sum(); + return ((n < 0L) ? 0 : + (n > (long)Integer.MAX_VALUE) ? Integer.MAX_VALUE : + (int)n); + } + + /** + * Returns the number of mappings. This method should be used + * instead of {@link #size} because a ConcurrentHashMapV8 may + * contain more mappings than can be represented as an int. The + * value returned is a snapshot; the actual count may differ if + * there are ongoing concurrent insertions or removals. + * + * @return the number of mappings + */ + public long mappingCount() { + long n = counter.sum(); + return (n < 0L) ? 0L : n; // ignore transient negative values + } + + /** + * Returns the value to which the specified key is mapped, + * or {@code null} if this map contains no mapping for the key. + * + *

More formally, if this map contains a mapping from a key + * {@code k} to a value {@code v} such that {@code key.equals(k)}, + * then this method returns {@code v}; otherwise it returns + * {@code null}. (There can be at most one such mapping.) + * + * @throws NullPointerException if the specified key is null + */ + @SuppressWarnings("unchecked") public V get(Object key) { + if (key == null) + throw new NullPointerException(); + return (V)internalGet(key); + } + + /** + * Returns the value to which the specified key is mapped, + * or the given defaultValue if this map contains no mapping for the key. + * + * @param key the key + * @param defaultValue the value to return if this map contains + * no mapping for the given key + * @return the mapping for the key, if present; else the defaultValue + * @throws NullPointerException if the specified key is null + */ + @SuppressWarnings("unchecked") public V getValueOrDefault(Object key, V defaultValue) { + if (key == null) + throw new NullPointerException(); + V v = (V) internalGet(key); + return v == null ? defaultValue : v; + } + + /** + * Tests if the specified object is a key in this table. + * + * @param key possible key + * @return {@code true} if and only if the specified object + * is a key in this table, as determined by the + * {@code equals} method; {@code false} otherwise + * @throws NullPointerException if the specified key is null + */ + public boolean containsKey(Object key) { + if (key == null) + throw new NullPointerException(); + return internalGet(key) != null; + } + + /** + * Returns {@code true} if this map maps one or more keys to the + * specified value. Note: This method may require a full traversal + * of the map, and is much slower than method {@code containsKey}. + * + * @param value value whose presence in this map is to be tested + * @return {@code true} if this map maps one or more keys to the + * specified value + * @throws NullPointerException if the specified value is null + */ + public boolean containsValue(Object value) { + if (value == null) + throw new NullPointerException(); + Object v; + Traverser it = new Traverser(this); + while ((v = it.advance()) != null) { + if (v == value || value.equals(v)) + return true; + } + return false; + } + + public K findKey(Object value) { + if (value == null) + throw new NullPointerException(); + Object v; + Traverser it = new Traverser(this); + while ((v = it.advance()) != null) { + if (v == value || value.equals(v)) + return it.nextKey; + } + return null; + } + + /** + * Legacy method testing if some key maps into the specified value + * in this table. This method is identical in functionality to + * {@link #containsValue}, and exists solely to ensure + * full compatibility with class {@link java.util.Hashtable}, + * which supported this method prior to introduction of the + * Java Collections framework. + * + * @param value a value to search for + * @return {@code true} if and only if some key maps to the + * {@code value} argument in this table as + * determined by the {@code equals} method; + * {@code false} otherwise + * @throws NullPointerException if the specified value is null + */ + public boolean contains(Object value) { + return containsValue(value); + } + + /** + * Maps the specified key to the specified value in this table. + * Neither the key nor the value can be null. + * + *

The value can be retrieved by calling the {@code get} method + * with a key that is equal to the original key. + * + * @param key key with which the specified value is to be associated + * @param value value to be associated with the specified key + * @return the previous value associated with {@code key}, or + * {@code null} if there was no mapping for {@code key} + * @throws NullPointerException if the specified key or value is null + */ + @SuppressWarnings("unchecked") public V put(K key, V value) { + if (key == null || value == null) + throw new NullPointerException(); + return (V)internalPut(key, value); + } + + /** + * {@inheritDoc} + * + * @return the previous value associated with the specified key, + * or {@code null} if there was no mapping for the key + * @throws NullPointerException if the specified key or value is null + */ + @SuppressWarnings("unchecked") public V putIfAbsent(K key, V value) { + if (key == null || value == null) + throw new NullPointerException(); + return (V)internalPutIfAbsent(key, value); + } + + /** + * Copies all of the mappings from the specified map to this one. + * These mappings replace any mappings that this map had for any of the + * keys currently in the specified map. + * + * @param m mappings to be stored in this map + */ + public void putAll(Map m) { + internalPutAll(m); + } + + /** + * If the specified key is not already associated with a value, + * computes its value using the given mappingFunction and enters + * it into the map unless null. This is equivalent to + *

 {@code
+     * if (map.containsKey(key))
+     *   return map.get(key);
+     * value = mappingFunction.apply(key);
+     * if (value != null)
+     *   map.put(key, value);
+     * return value;}
+ * + * except that the action is performed atomically. If the + * function returns {@code null} no mapping is recorded. If the + * function itself throws an (unchecked) exception, the exception + * is rethrown to its caller, and no mapping is recorded. Some + * attempted update operations on this map by other threads may be + * blocked while computation is in progress, so the computation + * should be short and simple, and must not attempt to update any + * other mappings of this Map. The most appropriate usage is to + * construct a new object serving as an initial mapped value, or + * memoized result, as in: + * + *
 {@code
+     * map.computeIfAbsent(key, new Fun() {
+     *   public V map(K k) { return new Value(f(k)); }});}
+ * + * @param key key with which the specified value is to be associated + * @param mappingFunction the function to compute a value + * @return the current (existing or computed) value associated with + * the specified key, or null if the computed value is null + * @throws NullPointerException if the specified key or mappingFunction + * is null + * @throws IllegalStateException if the computation detectably + * attempts a recursive update to this map that would + * otherwise never complete + * @throws RuntimeException or Error if the mappingFunction does so, + * in which case the mapping is left unestablished + */ + @SuppressWarnings("unchecked") public V computeIfAbsent + (K key, Fun mappingFunction) { + if (key == null || mappingFunction == null) + throw new NullPointerException(); + return (V)internalComputeIfAbsent(key, mappingFunction); + } + + /** + * If the given key is present, computes a new mapping value given a key and + * its current mapped value. This is equivalent to + *
 {@code
+     *   if (map.containsKey(key)) {
+     *     value = remappingFunction.apply(key, map.get(key));
+     *     if (value != null)
+     *       map.put(key, value);
+     *     else
+     *       map.remove(key);
+     *   }
+     * }
+ * + * except that the action is performed atomically. If the + * function returns {@code null}, the mapping is removed. If the + * function itself throws an (unchecked) exception, the exception + * is rethrown to its caller, and the current mapping is left + * unchanged. Some attempted update operations on this map by + * other threads may be blocked while computation is in progress, + * so the computation should be short and simple, and must not + * attempt to update any other mappings of this Map. For example, + * to either create or append new messages to a value mapping: + * + * @param key key with which the specified value is to be associated + * @param remappingFunction the function to compute a value + * @return the new value associated with the specified key, or null if none + * @throws NullPointerException if the specified key or remappingFunction + * is null + * @throws IllegalStateException if the computation detectably + * attempts a recursive update to this map that would + * otherwise never complete + * @throws RuntimeException or Error if the remappingFunction does so, + * in which case the mapping is unchanged + */ + @SuppressWarnings("unchecked") public V computeIfPresent + (K key, BiFun remappingFunction) { + if (key == null || remappingFunction == null) + throw new NullPointerException(); + return (V)internalCompute(key, true, remappingFunction); + } + + /** + * Computes a new mapping value given a key and + * its current mapped value (or {@code null} if there is no current + * mapping). This is equivalent to + *
 {@code
+     *   value = remappingFunction.apply(key, map.get(key));
+     *   if (value != null)
+     *     map.put(key, value);
+     *   else
+     *     map.remove(key);
+     * }
+ * + * except that the action is performed atomically. If the + * function returns {@code null}, the mapping is removed. If the + * function itself throws an (unchecked) exception, the exception + * is rethrown to its caller, and the current mapping is left + * unchanged. Some attempted update operations on this map by + * other threads may be blocked while computation is in progress, + * so the computation should be short and simple, and must not + * attempt to update any other mappings of this Map. For example, + * to either create or append new messages to a value mapping: + * + *
 {@code
+     * Map map = ...;
+     * final String msg = ...;
+     * map.compute(key, new BiFun() {
+     *   public String apply(Key k, String v) {
+     *    return (v == null) ? msg : v + msg;});}}
+ * + * @param key key with which the specified value is to be associated + * @param remappingFunction the function to compute a value + * @return the new value associated with the specified key, or null if none + * @throws NullPointerException if the specified key or remappingFunction + * is null + * @throws IllegalStateException if the computation detectably + * attempts a recursive update to this map that would + * otherwise never complete + * @throws RuntimeException or Error if the remappingFunction does so, + * in which case the mapping is unchanged + */ + @SuppressWarnings("unchecked") public V compute + (K key, BiFun remappingFunction) { + if (key == null || remappingFunction == null) + throw new NullPointerException(); + return (V)internalCompute(key, false, remappingFunction); + } + + /** + * If the specified key is not already associated + * with a value, associate it with the given value. + * Otherwise, replace the value with the results of + * the given remapping function. This is equivalent to: + *
 {@code
+     *   if (!map.containsKey(key))
+     *     map.put(value);
+     *   else {
+     *     newValue = remappingFunction.apply(map.get(key), value);
+     *     if (value != null)
+     *       map.put(key, value);
+     *     else
+     *       map.remove(key);
+     *   }
+     * }
+ * except that the action is performed atomically. If the + * function returns {@code null}, the mapping is removed. If the + * function itself throws an (unchecked) exception, the exception + * is rethrown to its caller, and the current mapping is left + * unchanged. Some attempted update operations on this map by + * other threads may be blocked while computation is in progress, + * so the computation should be short and simple, and must not + * attempt to update any other mappings of this Map. + */ + @SuppressWarnings("unchecked") public V merge + (K key, V value, BiFun remappingFunction) { + if (key == null || value == null || remappingFunction == null) + throw new NullPointerException(); + return (V)internalMerge(key, value, remappingFunction); + } + + /** + * Removes the key (and its corresponding value) from this map. + * This method does nothing if the key is not in the map. + * + * @param key the key that needs to be removed + * @return the previous value associated with {@code key}, or + * {@code null} if there was no mapping for {@code key} + * @throws NullPointerException if the specified key is null + */ + @SuppressWarnings("unchecked") public V remove(Object key) { + if (key == null) + throw new NullPointerException(); + return (V)internalReplace(key, null, null); + } + + /** + * {@inheritDoc} + * + * @throws NullPointerException if the specified key is null + */ + public boolean remove(Object key, Object value) { + if (key == null) + throw new NullPointerException(); + if (value == null) + return false; + return internalReplace(key, null, value) != null; + } + + /** + * {@inheritDoc} + * + * @throws NullPointerException if any of the arguments are null + */ + public boolean replace(K key, V oldValue, V newValue) { + if (key == null || oldValue == null || newValue == null) + throw new NullPointerException(); + return internalReplace(key, newValue, oldValue) != null; + } + + /** + * {@inheritDoc} + * + * @return the previous value associated with the specified key, + * or {@code null} if there was no mapping for the key + * @throws NullPointerException if the specified key or value is null + */ + @SuppressWarnings("unchecked") public V replace(K key, V value) { + if (key == null || value == null) + throw new NullPointerException(); + return (V)internalReplace(key, value, null); + } + + /** + * Removes all of the mappings from this map. + */ + public void clear() { + internalClear(); + } + + /** + * Returns a {@link Set} view of the keys contained in this map. + * The set is backed by the map, so changes to the map are + * reflected in the set, and vice-versa. + * + * @return the set view + */ + public KeySetView keySet() { + KeySetView ks = keySet; + return (ks != null) ? ks : (keySet = new KeySetView(this, null)); + } + + /** + * Returns a {@link Set} view of the keys in this map, using the + * given common mapped value for any additions (i.e., {@link + * Collection#add} and {@link Collection#addAll}). This is of + * course only appropriate if it is acceptable to use the same + * value for all additions from this view. + * + * @param mappedValue the mapped value to use for any + * additions. + * @return the set view + * @throws NullPointerException if the mappedValue is null + */ + public KeySetView keySet(V mappedValue) { + if (mappedValue == null) + throw new NullPointerException(); + return new KeySetView(this, mappedValue); + } + + /** + * Returns a {@link Collection} view of the values contained in this map. + * The collection is backed by the map, so changes to the map are + * reflected in the collection, and vice-versa. + */ + public ValuesView values() { + ValuesView vs = values; + return (vs != null) ? vs : (values = new ValuesView(this)); + } + + /** + * Returns a {@link Set} view of the mappings contained in this map. + * The set is backed by the map, so changes to the map are + * reflected in the set, and vice-versa. The set supports element + * removal, which removes the corresponding mapping from the map, + * via the {@code Iterator.remove}, {@code Set.remove}, + * {@code removeAll}, {@code retainAll}, and {@code clear} + * operations. It does not support the {@code add} or + * {@code addAll} operations. + * + *

The view's {@code iterator} is a "weakly consistent" iterator + * that will never throw {@link ConcurrentModificationException}, + * and guarantees to traverse elements as they existed upon + * construction of the iterator, and may (but is not guaranteed to) + * reflect any modifications subsequent to construction. + */ + public Set> entrySet() { + EntrySetView es = entrySet; + return (es != null) ? es : (entrySet = new EntrySetView(this)); + } + + /** + * Returns an enumeration of the keys in this table. + * + * @return an enumeration of the keys in this table + * @see #keySet() + */ + public Enumeration keys() { + return new KeyIterator(this); + } + + /** + * Returns an enumeration of the values in this table. + * + * @return an enumeration of the values in this table + * @see #values() + */ + public Enumeration elements() { + return new ValueIterator(this); + } + + /** + * Returns a partitionable iterator of the keys in this map. + * + * @return a partitionable iterator of the keys in this map + */ + public Spliterator keySpliterator() { + return new KeyIterator(this); + } + + /** + * Returns a partitionable iterator of the values in this map. + * + * @return a partitionable iterator of the values in this map + */ + public Spliterator valueSpliterator() { + return new ValueIterator(this); + } + + /** + * Returns a partitionable iterator of the entries in this map. + * + * @return a partitionable iterator of the entries in this map + */ + public Spliterator> entrySpliterator() { + return new EntryIterator(this); + } + + /** + * Returns the hash code value for this {@link Map}, i.e., + * the sum of, for each key-value pair in the map, + * {@code key.hashCode() ^ value.hashCode()}. + * + * @return the hash code value for this map + */ + public int hashCode() { + int h = 0; + Traverser it = new Traverser(this); + Object v; + while ((v = it.advance()) != null) { + h += it.nextKey.hashCode() ^ v.hashCode(); + } + return h; + } + + /** + * Returns a string representation of this map. The string + * representation consists of a list of key-value mappings (in no + * particular order) enclosed in braces ("{@code {}}"). Adjacent + * mappings are separated by the characters {@code ", "} (comma + * and space). Each key-value mapping is rendered as the key + * followed by an equals sign ("{@code =}") followed by the + * associated value. + * + * @return a string representation of this map + */ + public String toString() { + Traverser it = new Traverser(this); + StringBuilder sb = new StringBuilder(); + sb.append('{'); + Object v; + if ((v = it.advance()) != null) { + for (;;) { + Object k = it.nextKey; + sb.append(k == this ? "(this Map)" : k); + sb.append('='); + sb.append(v == this ? "(this Map)" : v); + if ((v = it.advance()) == null) + break; + sb.append(',').append(' '); + } + } + return sb.append('}').toString(); + } + + /** + * Compares the specified object with this map for equality. + * Returns {@code true} if the given object is a map with the same + * mappings as this map. This operation may return misleading + * results if either map is concurrently modified during execution + * of this method. + * + * @param o object to be compared for equality with this map + * @return {@code true} if the specified object is equal to this map + */ + public boolean equals(Object o) { + if (o != this) { + if (!(o instanceof Map)) + return false; + Map m = (Map) o; + Traverser it = new Traverser(this); + Object val; + while ((val = it.advance()) != null) { + Object v = m.get(it.nextKey); + if (v == null || (v != val && !v.equals(val))) + return false; + } + for (Map.Entry e : m.entrySet()) { + Object mk, mv, v; + if ((mk = e.getKey()) == null || + (mv = e.getValue()) == null || + (v = internalGet(mk)) == null || + (mv != v && !mv.equals(v))) + return false; + } + } + return true; + } + + /* ----------------Iterators -------------- */ + + @SuppressWarnings("serial") static final class KeyIterator extends Traverser + implements Spliterator, Enumeration { + KeyIterator(ConcurrentHashMapV8 map) { super(map); } + KeyIterator(Traverser it) { + super(it); + } + public KeyIterator split() { + if (nextKey != null) + throw new IllegalStateException(); + return new KeyIterator(this); + } + @SuppressWarnings("unchecked") public final K next() { + if (nextVal == null && advance() == null) + throw new NoSuchElementException(); + Object k = nextKey; + nextVal = null; + return (K) k; + } + + public final K nextElement() { return next(); } + } + + @SuppressWarnings("serial") static final class ValueIterator extends Traverser + implements Spliterator, Enumeration { + ValueIterator(ConcurrentHashMapV8 map) { super(map); } + ValueIterator(Traverser it) { + super(it); + } + public ValueIterator split() { + if (nextKey != null) + throw new IllegalStateException(); + return new ValueIterator(this); + } + + @SuppressWarnings("unchecked") public final V next() { + Object v; + if ((v = nextVal) == null && (v = advance()) == null) + throw new NoSuchElementException(); + nextVal = null; + return (V) v; + } + + public final V nextElement() { return next(); } + } + + @SuppressWarnings("serial") static final class EntryIterator extends Traverser + implements Spliterator> { + EntryIterator(ConcurrentHashMapV8 map) { super(map); } + EntryIterator(Traverser it) { + super(it); + } + public EntryIterator split() { + if (nextKey != null) + throw new IllegalStateException(); + return new EntryIterator(this); + } + + @SuppressWarnings("unchecked") public final Map.Entry next() { + Object v; + if ((v = nextVal) == null && (v = advance()) == null) + throw new NoSuchElementException(); + Object k = nextKey; + nextVal = null; + return new MapEntry((K)k, (V)v, map); + } + } + + /** + * Exported Entry for iterators + */ + static final class MapEntry implements Map.Entry { + final K key; // non-null + V val; // non-null + final ConcurrentHashMapV8 map; + MapEntry(K key, V val, ConcurrentHashMapV8 map) { + this.key = key; + this.val = val; + this.map = map; + } + public final K getKey() { return key; } + public final V getValue() { return val; } + public final int hashCode() { return key.hashCode() ^ val.hashCode(); } + public final String toString(){ return key + "=" + val; } + + public final boolean equals(Object o) { + Object k, v; Map.Entry e; + return ((o instanceof Map.Entry) && + (k = (e = (Map.Entry)o).getKey()) != null && + (v = e.getValue()) != null && + (k == key || k.equals(key)) && + (v == val || v.equals(val))); + } + + /** + * Sets our entry's value and writes through to the map. The + * value to return is somewhat arbitrary here. Since we do not + * necessarily track asynchronous changes, the most recent + * "previous" value could be different from what we return (or + * could even have been removed in which case the put will + * re-establish). We do not and cannot guarantee more. + */ + public final V setValue(V value) { + if (value == null) throw new NullPointerException(); + V v = val; + val = value; + map.put(key, value); + return v; + } + } + + /* ---------------- Serialization Support -------------- */ + + /** + * Stripped-down version of helper class used in previous version, + * declared for the sake of serialization compatibility + */ + static class Segment implements Serializable { + private static final long serialVersionUID = 2249069246763182397L; + final float loadFactor; + Segment(float lf) { this.loadFactor = lf; } + } + + /** + * Saves the state of the {@code ConcurrentHashMapV8} instance to a + * stream (i.e., serializes it). + * @param s the stream + * @serialData + * the key (Object) and value (Object) + * for each key-value mapping, followed by a null pair. + * The key-value mappings are emitted in no particular order. + */ + @SuppressWarnings("unchecked") private void writeObject(java.io.ObjectOutputStream s) + throws java.io.IOException { + if (segments == null) { // for serialization compatibility + segments = (Segment[]) + new Segment[DEFAULT_CONCURRENCY_LEVEL]; + for (int i = 0; i < segments.length; ++i) + segments[i] = new Segment(LOAD_FACTOR); + } + s.defaultWriteObject(); + Traverser it = new Traverser(this); + Object v; + while ((v = it.advance()) != null) { + s.writeObject(it.nextKey); + s.writeObject(v); + } + s.writeObject(null); + s.writeObject(null); + segments = null; // throw away + } + + /** + * Reconstitutes the instance from a stream (that is, deserializes it). + * @param s the stream + */ + @SuppressWarnings("unchecked") private void readObject(java.io.ObjectInputStream s) + throws java.io.IOException, ClassNotFoundException { + s.defaultReadObject(); + this.segments = null; // unneeded + // initialize transient final field + this.counter = new LongAdder(); + + // Create all nodes, then place in table once size is known + long size = 0L; + Node p = null; + for (;;) { + K k = (K) s.readObject(); + V v = (V) s.readObject(); + if (k != null && v != null) { + int h = spread(k.hashCode()); + p = new Node(h, k, v, p); + ++size; + } + else + break; + } + if (p != null) { + boolean init = false; + int n; + if (size >= (long)(MAXIMUM_CAPACITY >>> 1)) + n = MAXIMUM_CAPACITY; + else { + int sz = (int)size; + n = tableSizeFor(sz + (sz >>> 1) + 1); + } + int sc = sizeCtl; + boolean collide = false; + if (n > sc && + SIZE_CTRL_UPDATER.compareAndSet(this, sc, -1)) { + try { + if (table == null) { + init = true; + AtomicReferenceArray tab = new AtomicReferenceArray(n); + int mask = n - 1; + while (p != null) { + int j = p.hash & mask; + Node next = p.next; + Node q = p.next = tabAt(tab, j); + setTabAt(tab, j, p); + if (!collide && q != null && q.hash == p.hash) + collide = true; + p = next; + } + table = tab; + counter.add(size); + sc = n - (n >>> 2); + } + } finally { + sizeCtl = sc; + } + if (collide) { // rescan and convert to TreeBins + AtomicReferenceArray tab = table; + for (int i = 0; i < tab.length(); ++i) { + int c = 0; + for (Node e = tabAt(tab, i); e != null; e = e.next) { + if (++c > TREE_THRESHOLD && + (e.key instanceof Comparable)) { + replaceWithTreeBin(tab, i, e.key); + break; + } + } + } + } + } + if (!init) { // Can only happen if unsafely published. + while (p != null) { + internalPut(p.key, p.val); + p = p.next; + } + } + } + } + + + // ------------------------------------------------------- + + // Sams + /** Interface describing a void action of one argument */ + public interface Action { void apply(A a); } + /** Interface describing a void action of two arguments */ + public interface BiAction { void apply(A a, B b); } + /** Interface describing a function of one argument */ + public interface Generator { T apply(); } + /** Interface describing a function mapping its argument to a double */ + public interface ObjectToDouble { double apply(A a); } + /** Interface describing a function mapping its argument to a long */ + public interface ObjectToLong { long apply(A a); } + /** Interface describing a function mapping its argument to an int */ + public interface ObjectToInt {int apply(A a); } + /** Interface describing a function mapping two arguments to a double */ + public interface ObjectByObjectToDouble { double apply(A a, B b); } + /** Interface describing a function mapping two arguments to a long */ + public interface ObjectByObjectToLong { long apply(A a, B b); } + /** Interface describing a function mapping two arguments to an int */ + public interface ObjectByObjectToInt {int apply(A a, B b); } + /** Interface describing a function mapping a double to a double */ + public interface DoubleToDouble { double apply(double a); } + /** Interface describing a function mapping a long to a long */ + public interface LongToLong { long apply(long a); } + /** Interface describing a function mapping an int to an int */ + public interface IntToInt { int apply(int a); } + /** Interface describing a function mapping two doubles to a double */ + public interface DoubleByDoubleToDouble { double apply(double a, double b); } + /** Interface describing a function mapping two longs to a long */ + public interface LongByLongToLong { long apply(long a, long b); } + /** Interface describing a function mapping two ints to an int */ + public interface IntByIntToInt { int apply(int a, int b); } + + + /* ----------------Views -------------- */ + + /** + * Base class for views. + */ + static abstract class CHMView { + final ConcurrentHashMapV8 map; + CHMView(ConcurrentHashMapV8 map) { this.map = map; } + + /** + * Returns the map backing this view. + * + * @return the map backing this view + */ + public ConcurrentHashMapV8 getMap() { return map; } + + public final int size() { return map.size(); } + public final boolean isEmpty() { return map.isEmpty(); } + public final void clear() { map.clear(); } + + // implementations below rely on concrete classes supplying these + abstract public Iterator iterator(); + abstract public boolean contains(Object o); + abstract public boolean remove(Object o); + + private static final String oomeMsg = "Required array size too large"; + + public final Object[] toArray() { + long sz = map.mappingCount(); + if (sz > (long)(MAX_ARRAY_SIZE)) + throw new OutOfMemoryError(oomeMsg); + int n = (int)sz; + Object[] r = new Object[n]; + int i = 0; + Iterator it = iterator(); + while (it.hasNext()) { + if (i == n) { + if (n >= MAX_ARRAY_SIZE) + throw new OutOfMemoryError(oomeMsg); + if (n >= MAX_ARRAY_SIZE - (MAX_ARRAY_SIZE >>> 1) - 1) + n = MAX_ARRAY_SIZE; + else + n += (n >>> 1) + 1; + r = Arrays.copyOf(r, n); + } + r[i++] = it.next(); + } + return (i == n) ? r : Arrays.copyOf(r, i); + } + + @SuppressWarnings("unchecked") public final T[] toArray(T[] a) { + long sz = map.mappingCount(); + if (sz > (long)(MAX_ARRAY_SIZE)) + throw new OutOfMemoryError(oomeMsg); + int m = (int)sz; + T[] r = (a.length >= m) ? a : + (T[])java.lang.reflect.Array + .newInstance(a.getClass().getComponentType(), m); + int n = r.length; + int i = 0; + Iterator it = iterator(); + while (it.hasNext()) { + if (i == n) { + if (n >= MAX_ARRAY_SIZE) + throw new OutOfMemoryError(oomeMsg); + if (n >= MAX_ARRAY_SIZE - (MAX_ARRAY_SIZE >>> 1) - 1) + n = MAX_ARRAY_SIZE; + else + n += (n >>> 1) + 1; + r = Arrays.copyOf(r, n); + } + r[i++] = (T)it.next(); + } + if (a == r && i < n) { + r[i] = null; // null-terminate + return r; + } + return (i == n) ? r : Arrays.copyOf(r, i); + } + + public final int hashCode() { + int h = 0; + for (Iterator it = iterator(); it.hasNext();) + h += it.next().hashCode(); + return h; + } + + public final String toString() { + StringBuilder sb = new StringBuilder(); + sb.append('['); + Iterator it = iterator(); + if (it.hasNext()) { + for (;;) { + Object e = it.next(); + sb.append(e == this ? "(this Collection)" : e); + if (!it.hasNext()) + break; + sb.append(',').append(' '); + } + } + return sb.append(']').toString(); + } + + public final boolean containsAll(Collection c) { + if (c != this) { + for (Iterator it = c.iterator(); it.hasNext();) { + Object e = it.next(); + if (e == null || !contains(e)) + return false; + } + } + return true; + } + + public final boolean removeAll(Collection c) { + boolean modified = false; + for (Iterator it = iterator(); it.hasNext();) { + if (c.contains(it.next())) { + it.remove(); + modified = true; + } + } + return modified; + } + + public final boolean retainAll(Collection c) { + boolean modified = false; + for (Iterator it = iterator(); it.hasNext();) { + if (!c.contains(it.next())) { + it.remove(); + modified = true; + } + } + return modified; + } + + } + + /** + * A view of a ConcurrentHashMapV8 as a {@link Set} of keys, in + * which additions may optionally be enabled by mapping to a + * common value. This class cannot be directly instantiated. See + * {@link #keySet}, {@link #keySet(Object)}, {@link #newKeySet()}, + * {@link #newKeySet(int)}. + */ + public static class KeySetView extends CHMView implements Set, java.io.Serializable { + private static final long serialVersionUID = 7249069246763182397L; + private final V value; + KeySetView(ConcurrentHashMapV8 map, V value) { // non-public + super(map); + this.value = value; + } + + /** + * Returns the default mapped value for additions, + * or {@code null} if additions are not supported. + * + * @return the default mapped value for additions, or {@code null} + * if not supported. + */ + public V getMappedValue() { return value; } + + // implement Set API + + public boolean contains(Object o) { return map.containsKey(o); } + public boolean remove(Object o) { return map.remove(o) != null; } + + /** + * Returns a "weakly consistent" iterator that will never + * throw {@link ConcurrentModificationException}, and + * guarantees to traverse elements as they existed upon + * construction of the iterator, and may (but is not + * guaranteed to) reflect any modifications subsequent to + * construction. + * + * @return an iterator over the keys of this map + */ + public Iterator iterator() { return new KeyIterator(map); } + public boolean add(K e) { + V v; + if ((v = value) == null) + throw new UnsupportedOperationException(); + if (e == null) + throw new NullPointerException(); + return map.internalPutIfAbsent(e, v) == null; + } + public boolean addAll(Collection c) { + boolean added = false; + V v; + if ((v = value) == null) + throw new UnsupportedOperationException(); + for (K e : c) { + if (e == null) + throw new NullPointerException(); + if (map.internalPutIfAbsent(e, v) == null) + added = true; + } + return added; + } + public boolean equals(Object o) { + Set c; + return ((o instanceof Set) && + ((c = (Set)o) == this || + (containsAll(c) && c.containsAll(this)))); + } + } + + /** + * A view of a ConcurrentHashMapV8 as a {@link Collection} of + * values, in which additions are disabled. This class cannot be + * directly instantiated. See {@link #values}, + * + *

The view's {@code iterator} is a "weakly consistent" iterator + * that will never throw {@link ConcurrentModificationException}, + * and guarantees to traverse elements as they existed upon + * construction of the iterator, and may (but is not guaranteed to) + * reflect any modifications subsequent to construction. + */ + public static final class ValuesView extends CHMView + implements Collection { + ValuesView(ConcurrentHashMapV8 map) { super(map); } + public final boolean contains(Object o) { return map.containsValue(o); } + public final boolean remove(Object o) { + if (o != null) { + Iterator it = new ValueIterator(map); + while (it.hasNext()) { + if (o.equals(it.next())) { + it.remove(); + return true; + } + } + } + return false; + } + + /** + * Returns a "weakly consistent" iterator that will never + * throw {@link ConcurrentModificationException}, and + * guarantees to traverse elements as they existed upon + * construction of the iterator, and may (but is not + * guaranteed to) reflect any modifications subsequent to + * construction. + * + * @return an iterator over the values of this map + */ + public final Iterator iterator() { + return new ValueIterator(map); + } + public final boolean add(V e) { + throw new UnsupportedOperationException(); + } + public final boolean addAll(Collection c) { + throw new UnsupportedOperationException(); + } + } + + /** + * A view of a ConcurrentHashMapV8 as a {@link Set} of (key, value) + * entries. This class cannot be directly instantiated. See + * {@link #entrySet}. + */ + public static final class EntrySetView extends CHMView + implements Set> { + EntrySetView(ConcurrentHashMapV8 map) { super(map); } + public final boolean contains(Object o) { + Object k, v, r; Map.Entry e; + return ((o instanceof Map.Entry) && + (k = (e = (Map.Entry)o).getKey()) != null && + (r = map.get(k)) != null && + (v = e.getValue()) != null && + (v == r || v.equals(r))); + } + public final boolean remove(Object o) { + Object k, v; Map.Entry e; + return ((o instanceof Map.Entry) && + (k = (e = (Map.Entry)o).getKey()) != null && + (v = e.getValue()) != null && + map.remove(k, v)); + } + + /** + * Returns a "weakly consistent" iterator that will never + * throw {@link ConcurrentModificationException}, and + * guarantees to traverse elements as they existed upon + * construction of the iterator, and may (but is not + * guaranteed to) reflect any modifications subsequent to + * construction. + * + * @return an iterator over the entries of this map + */ + public final Iterator> iterator() { + return new EntryIterator(map); + } + + public final boolean add(Entry e) { + K key = e.getKey(); + V value = e.getValue(); + if (key == null || value == null) + throw new NullPointerException(); + return map.internalPut(key, value) == null; + } + public final boolean addAll(Collection> c) { + boolean added = false; + for (Entry e : c) { + if (add(e)) + added = true; + } + return added; + } + public boolean equals(Object o) { + Set c; + return ((o instanceof Set) && + ((c = (Set)o) == this || + (containsAll(c) && c.containsAll(this)))); + } + } +} diff --git a/vendor/bundle/gems/concurrent-ruby-1.1.5/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/nounsafe/LongAdder.java b/vendor/bundle/gems/concurrent-ruby-1.1.5/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/nounsafe/LongAdder.java new file mode 100644 index 0000000..ecf552a --- /dev/null +++ b/vendor/bundle/gems/concurrent-ruby-1.1.5/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/nounsafe/LongAdder.java @@ -0,0 +1,204 @@ +/* + * Written by Doug Lea with assistance from members of JCP JSR-166 + * Expert Group and released to the public domain, as explained at + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +// This is based on 1.9 version. + +package com.concurrent_ruby.ext.jsr166e.nounsafe; + +import java.util.concurrent.atomic.AtomicLong; +import java.io.IOException; +import java.io.Serializable; +import java.io.ObjectInputStream; + +/** + * One or more variables that together maintain an initially zero + * {@code long} sum. When updates (method {@link #add}) are contended + * across threads, the set of variables may grow dynamically to reduce + * contention. Method {@link #sum} (or, equivalently, {@link + * #longValue}) returns the current total combined across the + * variables maintaining the sum. + * + *

This class is usually preferable to {@link AtomicLong} when + * multiple threads update a common sum that is used for purposes such + * as collecting statistics, not for fine-grained synchronization + * control. Under low update contention, the two classes have similar + * characteristics. But under high contention, expected throughput of + * this class is significantly higher, at the expense of higher space + * consumption. + * + *

This class extends {@link Number}, but does not define + * methods such as {@code hashCode} and {@code compareTo} because + * instances are expected to be mutated, and so are not useful as + * collection keys. + * + *

jsr166e note: This class is targeted to be placed in + * java.util.concurrent.atomic. + * + * @since 1.8 + * @author Doug Lea + */ +public class LongAdder extends Striped64 implements Serializable { + private static final long serialVersionUID = 7249069246863182397L; + + /** + * Version of plus for use in retryUpdate + */ + final long fn(long v, long x) { return v + x; } + + /** + * Creates a new adder with initial sum of zero. + */ + public LongAdder() { + } + + /** + * Adds the given value. + * + * @param x the value to add + */ + public void add(long x) { + Cell[] as; long b, v; HashCode hc; Cell a; int n; + if ((as = cells) != null || !casBase(b = base, b + x)) { + boolean uncontended = true; + int h = (hc = threadHashCode.get()).code; + if (as == null || (n = as.length) < 1 || + (a = as[(n - 1) & h]) == null || + !(uncontended = a.cas(v = a.value, v + x))) + retryUpdate(x, hc, uncontended); + } + } + + /** + * Equivalent to {@code add(1)}. + */ + public void increment() { + add(1L); + } + + /** + * Equivalent to {@code add(-1)}. + */ + public void decrement() { + add(-1L); + } + + /** + * Returns the current sum. The returned value is NOT an + * atomic snapshot: Invocation in the absence of concurrent + * updates returns an accurate result, but concurrent updates that + * occur while the sum is being calculated might not be + * incorporated. + * + * @return the sum + */ + public long sum() { + long sum = base; + Cell[] as = cells; + if (as != null) { + int n = as.length; + for (int i = 0; i < n; ++i) { + Cell a = as[i]; + if (a != null) + sum += a.value; + } + } + return sum; + } + + /** + * Resets variables maintaining the sum to zero. This method may + * be a useful alternative to creating a new adder, but is only + * effective if there are no concurrent updates. Because this + * method is intrinsically racy, it should only be used when it is + * known that no threads are concurrently updating. + */ + public void reset() { + internalReset(0L); + } + + /** + * Equivalent in effect to {@link #sum} followed by {@link + * #reset}. This method may apply for example during quiescent + * points between multithreaded computations. If there are + * updates concurrent with this method, the returned value is + * not guaranteed to be the final value occurring before + * the reset. + * + * @return the sum + */ + public long sumThenReset() { + long sum = base; + Cell[] as = cells; + base = 0L; + if (as != null) { + int n = as.length; + for (int i = 0; i < n; ++i) { + Cell a = as[i]; + if (a != null) { + sum += a.value; + a.value = 0L; + } + } + } + return sum; + } + + /** + * Returns the String representation of the {@link #sum}. + * @return the String representation of the {@link #sum} + */ + public String toString() { + return Long.toString(sum()); + } + + /** + * Equivalent to {@link #sum}. + * + * @return the sum + */ + public long longValue() { + return sum(); + } + + /** + * Returns the {@link #sum} as an {@code int} after a narrowing + * primitive conversion. + */ + public int intValue() { + return (int)sum(); + } + + /** + * Returns the {@link #sum} as a {@code float} + * after a widening primitive conversion. + */ + public float floatValue() { + return (float)sum(); + } + + /** + * Returns the {@link #sum} as a {@code double} after a widening + * primitive conversion. + */ + public double doubleValue() { + return (double)sum(); + } + + private void writeObject(java.io.ObjectOutputStream s) + throws java.io.IOException { + s.defaultWriteObject(); + s.writeLong(sum()); + } + + private void readObject(ObjectInputStream s) + throws IOException, ClassNotFoundException { + s.defaultReadObject(); + busy = 0; + cells = null; + base = s.readLong(); + } + +} diff --git a/vendor/bundle/gems/concurrent-ruby-1.1.5/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/nounsafe/Striped64.java b/vendor/bundle/gems/concurrent-ruby-1.1.5/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/nounsafe/Striped64.java new file mode 100644 index 0000000..f521642 --- /dev/null +++ b/vendor/bundle/gems/concurrent-ruby-1.1.5/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/nounsafe/Striped64.java @@ -0,0 +1,291 @@ +/* + * Written by Doug Lea with assistance from members of JCP JSR-166 + * Expert Group and released to the public domain, as explained at + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +// This is based on 1.5 version. + +package com.concurrent_ruby.ext.jsr166e.nounsafe; + +import java.util.Random; +import java.util.concurrent.atomic.AtomicIntegerFieldUpdater; +import java.util.concurrent.atomic.AtomicLongFieldUpdater; + +/** + * A package-local class holding common representation and mechanics + * for classes supporting dynamic striping on 64bit values. The class + * extends Number so that concrete subclasses must publicly do so. + */ +abstract class Striped64 extends Number { + /* + * This class maintains a lazily-initialized table of atomically + * updated variables, plus an extra "base" field. The table size + * is a power of two. Indexing uses masked per-thread hash codes. + * Nearly all declarations in this class are package-private, + * accessed directly by subclasses. + * + * Table entries are of class Cell; a variant of AtomicLong padded + * to reduce cache contention on most processors. Padding is + * overkill for most Atomics because they are usually irregularly + * scattered in memory and thus don't interfere much with each + * other. But Atomic objects residing in arrays will tend to be + * placed adjacent to each other, and so will most often share + * cache lines (with a huge negative performance impact) without + * this precaution. + * + * In part because Cells are relatively large, we avoid creating + * them until they are needed. When there is no contention, all + * updates are made to the base field. Upon first contention (a + * failed CAS on base update), the table is initialized to size 2. + * The table size is doubled upon further contention until + * reaching the nearest power of two greater than or equal to the + * number of CPUS. Table slots remain empty (null) until they are + * needed. + * + * A single spinlock ("busy") is used for initializing and + * resizing the table, as well as populating slots with new Cells. + * There is no need for a blocking lock: When the lock is not + * available, threads try other slots (or the base). During these + * retries, there is increased contention and reduced locality, + * which is still better than alternatives. + * + * Per-thread hash codes are initialized to random values. + * Contention and/or table collisions are indicated by failed + * CASes when performing an update operation (see method + * retryUpdate). Upon a collision, if the table size is less than + * the capacity, it is doubled in size unless some other thread + * holds the lock. If a hashed slot is empty, and lock is + * available, a new Cell is created. Otherwise, if the slot + * exists, a CAS is tried. Retries proceed by "double hashing", + * using a secondary hash (Marsaglia XorShift) to try to find a + * free slot. + * + * The table size is capped because, when there are more threads + * than CPUs, supposing that each thread were bound to a CPU, + * there would exist a perfect hash function mapping threads to + * slots that eliminates collisions. When we reach capacity, we + * search for this mapping by randomly varying the hash codes of + * colliding threads. Because search is random, and collisions + * only become known via CAS failures, convergence can be slow, + * and because threads are typically not bound to CPUS forever, + * may not occur at all. However, despite these limitations, + * observed contention rates are typically low in these cases. + * + * It is possible for a Cell to become unused when threads that + * once hashed to it terminate, as well as in the case where + * doubling the table causes no thread to hash to it under + * expanded mask. We do not try to detect or remove such cells, + * under the assumption that for long-running instances, observed + * contention levels will recur, so the cells will eventually be + * needed again; and for short-lived ones, it does not matter. + */ + + /** + * Padded variant of AtomicLong supporting only raw accesses plus CAS. + * The value field is placed between pads, hoping that the JVM doesn't + * reorder them. + * + * JVM intrinsics note: It would be possible to use a release-only + * form of CAS here, if it were provided. + */ + static final class Cell { + volatile long p0, p1, p2, p3, p4, p5, p6; + volatile long value; + volatile long q0, q1, q2, q3, q4, q5, q6; + + static AtomicLongFieldUpdater VALUE_UPDATER = AtomicLongFieldUpdater.newUpdater(Cell.class, "value"); + + Cell(long x) { value = x; } + + final boolean cas(long cmp, long val) { + return VALUE_UPDATER.compareAndSet(this, cmp, val); + } + + } + + /** + * Holder for the thread-local hash code. The code is initially + * random, but may be set to a different value upon collisions. + */ + static final class HashCode { + static final Random rng = new Random(); + int code; + HashCode() { + int h = rng.nextInt(); // Avoid zero to allow xorShift rehash + code = (h == 0) ? 1 : h; + } + } + + /** + * The corresponding ThreadLocal class + */ + static final class ThreadHashCode extends ThreadLocal { + public HashCode initialValue() { return new HashCode(); } + } + + /** + * Static per-thread hash codes. Shared across all instances to + * reduce ThreadLocal pollution and because adjustments due to + * collisions in one table are likely to be appropriate for + * others. + */ + static final ThreadHashCode threadHashCode = new ThreadHashCode(); + + /** Number of CPUS, to place bound on table size */ + static final int NCPU = Runtime.getRuntime().availableProcessors(); + + /** + * Table of cells. When non-null, size is a power of 2. + */ + transient volatile Cell[] cells; + + /** + * Base value, used mainly when there is no contention, but also as + * a fallback during table initialization races. Updated via CAS. + */ + transient volatile long base; + + /** + * Spinlock (locked via CAS) used when resizing and/or creating Cells. + */ + transient volatile int busy; + + AtomicLongFieldUpdater BASE_UPDATER = AtomicLongFieldUpdater.newUpdater(Striped64.class, "base"); + AtomicIntegerFieldUpdater BUSY_UPDATER = AtomicIntegerFieldUpdater.newUpdater(Striped64.class, "busy"); + + /** + * Package-private default constructor + */ + Striped64() { + } + + /** + * CASes the base field. + */ + final boolean casBase(long cmp, long val) { + return BASE_UPDATER.compareAndSet(this, cmp, val); + } + + /** + * CASes the busy field from 0 to 1 to acquire lock. + */ + final boolean casBusy() { + return BUSY_UPDATER.compareAndSet(this, 0, 1); + } + + /** + * Computes the function of current and new value. Subclasses + * should open-code this update function for most uses, but the + * virtualized form is needed within retryUpdate. + * + * @param currentValue the current value (of either base or a cell) + * @param newValue the argument from a user update call + * @return result of the update function + */ + abstract long fn(long currentValue, long newValue); + + /** + * Handles cases of updates involving initialization, resizing, + * creating new Cells, and/or contention. See above for + * explanation. This method suffers the usual non-modularity + * problems of optimistic retry code, relying on rechecked sets of + * reads. + * + * @param x the value + * @param hc the hash code holder + * @param wasUncontended false if CAS failed before call + */ + final void retryUpdate(long x, HashCode hc, boolean wasUncontended) { + int h = hc.code; + boolean collide = false; // True if last slot nonempty + for (;;) { + Cell[] as; Cell a; int n; long v; + if ((as = cells) != null && (n = as.length) > 0) { + if ((a = as[(n - 1) & h]) == null) { + if (busy == 0) { // Try to attach new Cell + Cell r = new Cell(x); // Optimistically create + if (busy == 0 && casBusy()) { + boolean created = false; + try { // Recheck under lock + Cell[] rs; int m, j; + if ((rs = cells) != null && + (m = rs.length) > 0 && + rs[j = (m - 1) & h] == null) { + rs[j] = r; + created = true; + } + } finally { + busy = 0; + } + if (created) + break; + continue; // Slot is now non-empty + } + } + collide = false; + } + else if (!wasUncontended) // CAS already known to fail + wasUncontended = true; // Continue after rehash + else if (a.cas(v = a.value, fn(v, x))) + break; + else if (n >= NCPU || cells != as) + collide = false; // At max size or stale + else if (!collide) + collide = true; + else if (busy == 0 && casBusy()) { + try { + if (cells == as) { // Expand table unless stale + Cell[] rs = new Cell[n << 1]; + for (int i = 0; i < n; ++i) + rs[i] = as[i]; + cells = rs; + } + } finally { + busy = 0; + } + collide = false; + continue; // Retry with expanded table + } + h ^= h << 13; // Rehash + h ^= h >>> 17; + h ^= h << 5; + } + else if (busy == 0 && cells == as && casBusy()) { + boolean init = false; + try { // Initialize table + if (cells == as) { + Cell[] rs = new Cell[2]; + rs[h & 1] = new Cell(x); + cells = rs; + init = true; + } + } finally { + busy = 0; + } + if (init) + break; + } + else if (casBase(v = base, fn(v, x))) + break; // Fall back on using base + } + hc.code = h; // Record index for next time + } + + + /** + * Sets base and all cells to the given value. + */ + final void internalReset(long initialValue) { + Cell[] as = cells; + base = initialValue; + if (as != null) { + int n = as.length; + for (int i = 0; i < n; ++i) { + Cell a = as[i]; + if (a != null) + a.value = initialValue; + } + } + } +} diff --git a/vendor/bundle/gems/concurrent-ruby-1.1.5/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166y/ThreadLocalRandom.java b/vendor/bundle/gems/concurrent-ruby-1.1.5/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166y/ThreadLocalRandom.java new file mode 100644 index 0000000..3ea409f --- /dev/null +++ b/vendor/bundle/gems/concurrent-ruby-1.1.5/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166y/ThreadLocalRandom.java @@ -0,0 +1,199 @@ +/* + * Written by Doug Lea with assistance from members of JCP JSR-166 + * Expert Group and released to the public domain, as explained at + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +// This is based on 1.16 version + +package com.concurrent_ruby.ext.jsr166y; + +import java.util.Random; + +/** + * A random number generator isolated to the current thread. Like the + * global {@link java.util.Random} generator used by the {@link + * java.lang.Math} class, a {@code ThreadLocalRandom} is initialized + * with an internally generated seed that may not otherwise be + * modified. When applicable, use of {@code ThreadLocalRandom} rather + * than shared {@code Random} objects in concurrent programs will + * typically encounter much less overhead and contention. Use of + * {@code ThreadLocalRandom} is particularly appropriate when multiple + * tasks (for example, each a {@link ForkJoinTask}) use random numbers + * in parallel in thread pools. + * + *

Usages of this class should typically be of the form: + * {@code ThreadLocalRandom.current().nextX(...)} (where + * {@code X} is {@code Int}, {@code Long}, etc). + * When all usages are of this form, it is never possible to + * accidently share a {@code ThreadLocalRandom} across multiple threads. + * + *

This class also provides additional commonly used bounded random + * generation methods. + * + * @since 1.7 + * @author Doug Lea + */ +public class ThreadLocalRandom extends Random { + // same constants as Random, but must be redeclared because private + private static final long multiplier = 0x5DEECE66DL; + private static final long addend = 0xBL; + private static final long mask = (1L << 48) - 1; + + /** + * The random seed. We can't use super.seed. + */ + private long rnd; + + /** + * Initialization flag to permit calls to setSeed to succeed only + * while executing the Random constructor. We can't allow others + * since it would cause setting seed in one part of a program to + * unintentionally impact other usages by the thread. + */ + boolean initialized; + + // Padding to help avoid memory contention among seed updates in + // different TLRs in the common case that they are located near + // each other. + private long pad0, pad1, pad2, pad3, pad4, pad5, pad6, pad7; + + /** + * The actual ThreadLocal + */ + private static final ThreadLocal localRandom = + new ThreadLocal() { + protected ThreadLocalRandom initialValue() { + return new ThreadLocalRandom(); + } + }; + + + /** + * Constructor called only by localRandom.initialValue. + */ + ThreadLocalRandom() { + super(); + initialized = true; + } + + /** + * Returns the current thread's {@code ThreadLocalRandom}. + * + * @return the current thread's {@code ThreadLocalRandom} + */ + public static ThreadLocalRandom current() { + return localRandom.get(); + } + + /** + * Throws {@code UnsupportedOperationException}. Setting seeds in + * this generator is not supported. + * + * @throws UnsupportedOperationException always + */ + public void setSeed(long seed) { + if (initialized) + throw new UnsupportedOperationException(); + rnd = (seed ^ multiplier) & mask; + } + + protected int next(int bits) { + rnd = (rnd * multiplier + addend) & mask; + return (int) (rnd >>> (48-bits)); + } + + /** + * Returns a pseudorandom, uniformly distributed value between the + * given least value (inclusive) and bound (exclusive). + * + * @param least the least value returned + * @param bound the upper bound (exclusive) + * @throws IllegalArgumentException if least greater than or equal + * to bound + * @return the next value + */ + public int nextInt(int least, int bound) { + if (least >= bound) + throw new IllegalArgumentException(); + return nextInt(bound - least) + least; + } + + /** + * Returns a pseudorandom, uniformly distributed value + * between 0 (inclusive) and the specified value (exclusive). + * + * @param n the bound on the random number to be returned. Must be + * positive. + * @return the next value + * @throws IllegalArgumentException if n is not positive + */ + public long nextLong(long n) { + if (n <= 0) + throw new IllegalArgumentException("n must be positive"); + // Divide n by two until small enough for nextInt. On each + // iteration (at most 31 of them but usually much less), + // randomly choose both whether to include high bit in result + // (offset) and whether to continue with the lower vs upper + // half (which makes a difference only if odd). + long offset = 0; + while (n >= Integer.MAX_VALUE) { + int bits = next(2); + long half = n >>> 1; + long nextn = ((bits & 2) == 0) ? half : n - half; + if ((bits & 1) == 0) + offset += n - nextn; + n = nextn; + } + return offset + nextInt((int) n); + } + + /** + * Returns a pseudorandom, uniformly distributed value between the + * given least value (inclusive) and bound (exclusive). + * + * @param least the least value returned + * @param bound the upper bound (exclusive) + * @return the next value + * @throws IllegalArgumentException if least greater than or equal + * to bound + */ + public long nextLong(long least, long bound) { + if (least >= bound) + throw new IllegalArgumentException(); + return nextLong(bound - least) + least; + } + + /** + * Returns a pseudorandom, uniformly distributed {@code double} value + * between 0 (inclusive) and the specified value (exclusive). + * + * @param n the bound on the random number to be returned. Must be + * positive. + * @return the next value + * @throws IllegalArgumentException if n is not positive + */ + public double nextDouble(double n) { + if (n <= 0) + throw new IllegalArgumentException("n must be positive"); + return nextDouble() * n; + } + + /** + * Returns a pseudorandom, uniformly distributed value between the + * given least value (inclusive) and bound (exclusive). + * + * @param least the least value returned + * @param bound the upper bound (exclusive) + * @return the next value + * @throws IllegalArgumentException if least greater than or equal + * to bound + */ + public double nextDouble(double least, double bound) { + if (least >= bound) + throw new IllegalArgumentException(); + return nextDouble() * (bound - least) + least; + } + + private static final long serialVersionUID = -5851777807851030925L; +} diff --git a/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent-ruby.rb b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent-ruby.rb new file mode 100644 index 0000000..08917e3 --- /dev/null +++ b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent-ruby.rb @@ -0,0 +1 @@ +require_relative "./concurrent" diff --git a/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent.rb b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent.rb new file mode 100644 index 0000000..87de46f --- /dev/null +++ b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent.rb @@ -0,0 +1,134 @@ +require 'concurrent/version' +require 'concurrent/constants' +require 'concurrent/errors' +require 'concurrent/configuration' + +require 'concurrent/atomics' +require 'concurrent/executors' +require 'concurrent/synchronization' + +require 'concurrent/atomic/atomic_markable_reference' +require 'concurrent/atomic/atomic_reference' +require 'concurrent/agent' +require 'concurrent/atom' +require 'concurrent/array' +require 'concurrent/hash' +require 'concurrent/set' +require 'concurrent/map' +require 'concurrent/tuple' +require 'concurrent/async' +require 'concurrent/dataflow' +require 'concurrent/delay' +require 'concurrent/exchanger' +require 'concurrent/future' +require 'concurrent/immutable_struct' +require 'concurrent/ivar' +require 'concurrent/maybe' +require 'concurrent/mutable_struct' +require 'concurrent/mvar' +require 'concurrent/promise' +require 'concurrent/scheduled_task' +require 'concurrent/settable_struct' +require 'concurrent/timer_task' +require 'concurrent/tvar' +require 'concurrent/promises' + +require 'concurrent/thread_safe/synchronized_delegator' +require 'concurrent/thread_safe/util' + +require 'concurrent/options' + +# @!macro internal_implementation_note +# +# @note **Private Implementation:** This abstraction is a private, internal +# implementation detail. It should never be used directly. + +# @!macro monotonic_clock_warning +# +# @note Time calculations on all platforms and languages are sensitive to +# changes to the system clock. To alleviate the potential problems +# associated with changing the system clock while an application is running, +# most modern operating systems provide a monotonic clock that operates +# independently of the system clock. A monotonic clock cannot be used to +# determine human-friendly clock times. A monotonic clock is used exclusively +# for calculating time intervals. Not all Ruby platforms provide access to an +# operating system monotonic clock. On these platforms a pure-Ruby monotonic +# clock will be used as a fallback. An operating system monotonic clock is both +# faster and more reliable than the pure-Ruby implementation. The pure-Ruby +# implementation should be fast and reliable enough for most non-realtime +# operations. At this time the common Ruby platforms that provide access to an +# operating system monotonic clock are MRI 2.1 and above and JRuby (all versions). +# +# @see http://linux.die.net/man/3/clock_gettime Linux clock_gettime(3) + +# @!macro copy_options +# +# ## Copy Options +# +# Object references in Ruby are mutable. This can lead to serious +# problems when the {#value} of an object is a mutable reference. Which +# is always the case unless the value is a `Fixnum`, `Symbol`, or similar +# "primitive" data type. Each instance can be configured with a few +# options that can help protect the program from potentially dangerous +# operations. Each of these options can be optionally set when the object +# instance is created: +# +# * `:dup_on_deref` When true the object will call the `#dup` method on +# the `value` object every time the `#value` method is called +# (default: false) +# * `:freeze_on_deref` When true the object will call the `#freeze` +# method on the `value` object every time the `#value` method is called +# (default: false) +# * `:copy_on_deref` When given a `Proc` object the `Proc` will be run +# every time the `#value` method is called. The `Proc` will be given +# the current `value` as its only argument and the result returned by +# the block will be the return value of the `#value` call. When `nil` +# this option will be ignored (default: nil) +# +# When multiple deref options are set the order of operations is strictly defined. +# The order of deref operations is: +# * `:copy_on_deref` +# * `:dup_on_deref` +# * `:freeze_on_deref` +# +# Because of this ordering there is no need to `#freeze` an object created by a +# provided `:copy_on_deref` block. Simply set `:freeze_on_deref` to `true`. +# Setting both `:dup_on_deref` to `true` and `:freeze_on_deref` to `true` is +# as close to the behavior of a "pure" functional language (like Erlang, Clojure, +# or Haskell) as we are likely to get in Ruby. + +# @!macro deref_options +# +# @option opts [Boolean] :dup_on_deref (false) Call `#dup` before +# returning the data from {#value} +# @option opts [Boolean] :freeze_on_deref (false) Call `#freeze` before +# returning the data from {#value} +# @option opts [Proc] :copy_on_deref (nil) When calling the {#value} +# method, call the given proc passing the internal value as the sole +# argument then return the new value returned from the proc. + +# @!macro executor_and_deref_options +# +# @param [Hash] opts the options used to define the behavior at update and deref +# and to specify the executor on which to perform actions +# @option opts [Executor] :executor when set use the given `Executor` instance. +# Three special values are also supported: `:io` returns the global pool for +# long, blocking (IO) tasks, `:fast` returns the global pool for short, fast +# operations, and `:immediate` returns the global `ImmediateExecutor` object. +# @!macro deref_options + +# @!macro warn.edge +# @api Edge +# @note **Edge Features** are under active development and may change frequently. +# +# - Deprecations are not added before incompatible changes. +# - Edge version: _major_ is always 0, _minor_ bump means incompatible change, +# _patch_ bump means compatible change. +# - Edge features may also lack tests and documentation. +# - Features developed in `concurrent-ruby-edge` are expected to move +# to `concurrent-ruby` when finalised. + + +# {include:file:README.md} +module Concurrent +end diff --git a/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/agent.rb b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/agent.rb new file mode 100644 index 0000000..815dca0 --- /dev/null +++ b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/agent.rb @@ -0,0 +1,587 @@ +require 'concurrent/configuration' +require 'concurrent/atomic/atomic_reference' +require 'concurrent/atomic/thread_local_var' +require 'concurrent/collection/copy_on_write_observer_set' +require 'concurrent/concern/observable' +require 'concurrent/synchronization' + +module Concurrent + + # `Agent` is inspired by Clojure's [agent](http://clojure.org/agents) + # function. An agent is a shared, mutable variable providing independent, + # uncoordinated, *asynchronous* change of individual values. Best used when + # the value will undergo frequent, complex updates. Suitable when the result + # of an update does not need to be known immediately. `Agent` is (mostly) + # functionally equivalent to Clojure's agent, except where the runtime + # prevents parity. + # + # Agents are reactive, not autonomous - there is no imperative message loop + # and no blocking receive. The state of an Agent should be itself immutable + # and the `#value` of an Agent is always immediately available for reading by + # any thread without any messages, i.e. observation does not require + # cooperation or coordination. + # + # Agent action dispatches are made using the various `#send` methods. These + # methods always return immediately. At some point later, in another thread, + # the following will happen: + # + # 1. The given `action` will be applied to the state of the Agent and the + # `args`, if any were supplied. + # 2. The return value of `action` will be passed to the validator lambda, + # if one has been set on the Agent. + # 3. If the validator succeeds or if no validator was given, the return value + # of the given `action` will become the new `#value` of the Agent. See + # `#initialize` for details. + # 4. If any observers were added to the Agent, they will be notified. See + # `#add_observer` for details. + # 5. If during the `action` execution any other dispatches are made (directly + # or indirectly), they will be held until after the `#value` of the Agent + # has been changed. + # + # If any exceptions are thrown by an action function, no nested dispatches + # will occur, and the exception will be cached in the Agent itself. When an + # Agent has errors cached, any subsequent interactions will immediately throw + # an exception, until the agent's errors are cleared. Agent errors can be + # examined with `#error` and the agent restarted with `#restart`. + # + # The actions of all Agents get interleaved amongst threads in a thread pool. + # At any point in time, at most one action for each Agent is being executed. + # Actions dispatched to an agent from another single agent or thread will + # occur in the order they were sent, potentially interleaved with actions + # dispatched to the same agent from other sources. The `#send` method should + # be used for actions that are CPU limited, while the `#send_off` method is + # appropriate for actions that may block on IO. + # + # Unlike in Clojure, `Agent` cannot participate in `Concurrent::TVar` transactions. + # + # ## Example + # + # ``` + # def next_fibonacci(set = nil) + # return [0, 1] if set.nil? + # set + [set[-2..-1].reduce{|sum,x| sum + x }] + # end + # + # # create an agent with an initial value + # agent = Concurrent::Agent.new(next_fibonacci) + # + # # send a few update requests + # 5.times do + # agent.send{|set| next_fibonacci(set) } + # end + # + # # wait for them to complete + # agent.await + # + # # get the current value + # agent.value #=> [0, 1, 1, 2, 3, 5, 8] + # ``` + # + # ## Observation + # + # Agents support observers through the {Concurrent::Observable} mixin module. + # Notification of observers occurs every time an action dispatch returns and + # the new value is successfully validated. Observation will *not* occur if the + # action raises an exception, if validation fails, or when a {#restart} occurs. + # + # When notified the observer will receive three arguments: `time`, `old_value`, + # and `new_value`. The `time` argument is the time at which the value change + # occurred. The `old_value` is the value of the Agent when the action began + # processing. The `new_value` is the value to which the Agent was set when the + # action completed. Note that `old_value` and `new_value` may be the same. + # This is not an error. It simply means that the action returned the same + # value. + # + # ## Nested Actions + # + # It is possible for an Agent action to post further actions back to itself. + # The nested actions will be enqueued normally then processed *after* the + # outer action completes, in the order they were sent, possibly interleaved + # with action dispatches from other threads. Nested actions never deadlock + # with one another and a failure in a nested action will never affect the + # outer action. + # + # Nested actions can be called using the Agent reference from the enclosing + # scope or by passing the reference in as a "send" argument. Nested actions + # cannot be post using `self` from within the action block/proc/lambda; `self` + # in this context will not reference the Agent. The preferred method for + # dispatching nested actions is to pass the Agent as an argument. This allows + # Ruby to more effectively manage the closing scope. + # + # Prefer this: + # + # ``` + # agent = Concurrent::Agent.new(0) + # agent.send(agent) do |value, this| + # this.send {|v| v + 42 } + # 3.14 + # end + # agent.value #=> 45.14 + # ``` + # + # Over this: + # + # ``` + # agent = Concurrent::Agent.new(0) + # agent.send do |value| + # agent.send {|v| v + 42 } + # 3.14 + # end + # ``` + # + # @!macro agent_await_warning + # + # **NOTE** Never, *under any circumstances*, call any of the "await" methods + # ({#await}, {#await_for}, {#await_for!}, and {#wait}) from within an action + # block/proc/lambda. The call will block the Agent and will always fail. + # Calling either {#await} or {#wait} (with a timeout of `nil`) will + # hopelessly deadlock the Agent with no possibility of recovery. + # + # @!macro thread_safe_variable_comparison + # + # @see http://clojure.org/Agents Clojure Agents + # @see http://clojure.org/state Values and Change - Clojure's approach to Identity and State + class Agent < Synchronization::LockableObject + include Concern::Observable + + ERROR_MODES = [:continue, :fail].freeze + private_constant :ERROR_MODES + + AWAIT_FLAG = ::Object.new + private_constant :AWAIT_FLAG + + AWAIT_ACTION = ->(value, latch) { latch.count_down; AWAIT_FLAG } + private_constant :AWAIT_ACTION + + DEFAULT_ERROR_HANDLER = ->(agent, error) { nil } + private_constant :DEFAULT_ERROR_HANDLER + + DEFAULT_VALIDATOR = ->(value) { true } + private_constant :DEFAULT_VALIDATOR + + Job = Struct.new(:action, :args, :executor, :caller) + private_constant :Job + + # Raised during action processing or any other time in an Agent's lifecycle. + class Error < StandardError + def initialize(message = nil) + message ||= 'agent must be restarted before jobs can post' + super(message) + end + end + + # Raised when a new value obtained during action processing or at `#restart` + # fails validation. + class ValidationError < Error + def initialize(message = nil) + message ||= 'invalid value' + super(message) + end + end + + # The error mode this Agent is operating in. See {#initialize} for details. + attr_reader :error_mode + + # Create a new `Agent` with the given initial value and options. + # + # The `:validator` option must be `nil` or a side-effect free proc/lambda + # which takes one argument. On any intended value change the validator, if + # provided, will be called. If the new value is invalid the validator should + # return `false` or raise an error. + # + # The `:error_handler` option must be `nil` or a proc/lambda which takes two + # arguments. When an action raises an error or validation fails, either by + # returning false or raising an error, the error handler will be called. The + # arguments to the error handler will be a reference to the agent itself and + # the error object which was raised. + # + # The `:error_mode` may be either `:continue` (the default if an error + # handler is given) or `:fail` (the default if error handler nil or not + # given). + # + # If an action being run by the agent throws an error or doesn't pass + # validation the error handler, if present, will be called. After the + # handler executes if the error mode is `:continue` the Agent will continue + # as if neither the action that caused the error nor the error itself ever + # happened. + # + # If the mode is `:fail` the Agent will become {#failed?} and will stop + # accepting new action dispatches. Any previously queued actions will be + # held until {#restart} is called. The {#value} method will still work, + # returning the value of the Agent before the error. + # + # @param [Object] initial the initial value + # @param [Hash] opts the configuration options + # + # @option opts [Symbol] :error_mode either `:continue` or `:fail` + # @option opts [nil, Proc] :error_handler the (optional) error handler + # @option opts [nil, Proc] :validator the (optional) validation procedure + def initialize(initial, opts = {}) + super() + synchronize { ns_initialize(initial, opts) } + end + + # The current value (state) of the Agent, irrespective of any pending or + # in-progress actions. The value is always available and is non-blocking. + # + # @return [Object] the current value + def value + @current.value # TODO (pitr 12-Sep-2015): broken unsafe read? + end + + alias_method :deref, :value + + # When {#failed?} and {#error_mode} is `:fail`, returns the error object + # which caused the failure, else `nil`. When {#error_mode} is `:continue` + # will *always* return `nil`. + # + # @return [nil, Error] the error which caused the failure when {#failed?} + def error + @error.value + end + + alias_method :reason, :error + + # @!macro agent_send + # + # Dispatches an action to the Agent and returns immediately. Subsequently, + # in a thread from a thread pool, the {#value} will be set to the return + # value of the action. Action dispatches are only allowed when the Agent + # is not {#failed?}. + # + # The action must be a block/proc/lambda which takes 1 or more arguments. + # The first argument is the current {#value} of the Agent. Any arguments + # passed to the send method via the `args` parameter will be passed to the + # action as the remaining arguments. The action must return the new value + # of the Agent. + # + # * {#send} and {#send!} should be used for actions that are CPU limited + # * {#send_off}, {#send_off!}, and {#<<} are appropriate for actions that + # may block on IO + # * {#send_via} and {#send_via!} are used when a specific executor is to + # be used for the action + # + # @param [Array] args zero or more arguments to be passed to + # the action + # @param [Proc] action the action dispatch to be enqueued + # + # @yield [agent, value, *args] process the old value and return the new + # @yieldparam [Object] value the current {#value} of the Agent + # @yieldparam [Array] args zero or more arguments to pass to the + # action + # @yieldreturn [Object] the new value of the Agent + # + # @!macro send_return + # @return [Boolean] true if the action is successfully enqueued, false if + # the Agent is {#failed?} + def send(*args, &action) + enqueue_action_job(action, args, Concurrent.global_fast_executor) + end + + # @!macro agent_send + # + # @!macro send_bang_return_and_raise + # @return [Boolean] true if the action is successfully enqueued + # @raise [Concurrent::Agent::Error] if the Agent is {#failed?} + def send!(*args, &action) + raise Error.new unless send(*args, &action) + true + end + + # @!macro agent_send + # @!macro send_return + def send_off(*args, &action) + enqueue_action_job(action, args, Concurrent.global_io_executor) + end + + alias_method :post, :send_off + + # @!macro agent_send + # @!macro send_bang_return_and_raise + def send_off!(*args, &action) + raise Error.new unless send_off(*args, &action) + true + end + + # @!macro agent_send + # @!macro send_return + # @param [Concurrent::ExecutorService] executor the executor on which the + # action is to be dispatched + def send_via(executor, *args, &action) + enqueue_action_job(action, args, executor) + end + + # @!macro agent_send + # @!macro send_bang_return_and_raise + # @param [Concurrent::ExecutorService] executor the executor on which the + # action is to be dispatched + def send_via!(executor, *args, &action) + raise Error.new unless send_via(executor, *args, &action) + true + end + + # Dispatches an action to the Agent and returns immediately. Subsequently, + # in a thread from a thread pool, the {#value} will be set to the return + # value of the action. Appropriate for actions that may block on IO. + # + # @param [Proc] action the action dispatch to be enqueued + # @return [Concurrent::Agent] self + # @see #send_off + def <<(action) + send_off(&action) + self + end + + # Blocks the current thread (indefinitely!) until all actions dispatched + # thus far, from this thread or nested by the Agent, have occurred. Will + # block when {#failed?}. Will never return if a failed Agent is {#restart} + # with `:clear_actions` true. + # + # Returns a reference to `self` to support method chaining: + # + # ``` + # current_value = agent.await.value + # ``` + # + # @return [Boolean] self + # + # @!macro agent_await_warning + def await + wait(nil) + self + end + + # Blocks the current thread until all actions dispatched thus far, from this + # thread or nested by the Agent, have occurred, or the timeout (in seconds) + # has elapsed. + # + # @param [Float] timeout the maximum number of seconds to wait + # @return [Boolean] true if all actions complete before timeout else false + # + # @!macro agent_await_warning + def await_for(timeout) + wait(timeout.to_f) + end + + # Blocks the current thread until all actions dispatched thus far, from this + # thread or nested by the Agent, have occurred, or the timeout (in seconds) + # has elapsed. + # + # @param [Float] timeout the maximum number of seconds to wait + # @return [Boolean] true if all actions complete before timeout + # + # @raise [Concurrent::TimeoutError] when timout is reached + # + # @!macro agent_await_warning + def await_for!(timeout) + raise Concurrent::TimeoutError unless wait(timeout.to_f) + true + end + + # Blocks the current thread until all actions dispatched thus far, from this + # thread or nested by the Agent, have occurred, or the timeout (in seconds) + # has elapsed. Will block indefinitely when timeout is nil or not given. + # + # Provided mainly for consistency with other classes in this library. Prefer + # the various `await` methods instead. + # + # @param [Float] timeout the maximum number of seconds to wait + # @return [Boolean] true if all actions complete before timeout else false + # + # @!macro agent_await_warning + def wait(timeout = nil) + latch = Concurrent::CountDownLatch.new(1) + enqueue_await_job(latch) + latch.wait(timeout) + end + + # Is the Agent in a failed state? + # + # @see #restart + def failed? + !@error.value.nil? + end + + alias_method :stopped?, :failed? + + # When an Agent is {#failed?}, changes the Agent {#value} to `new_value` + # then un-fails the Agent so that action dispatches are allowed again. If + # the `:clear_actions` option is give and true, any actions queued on the + # Agent that were being held while it was failed will be discarded, + # otherwise those held actions will proceed. The `new_value` must pass the + # validator if any, or `restart` will raise an exception and the Agent will + # remain failed with its old {#value} and {#error}. Observers, if any, will + # not be notified of the new state. + # + # @param [Object] new_value the new value for the Agent once restarted + # @param [Hash] opts the configuration options + # @option opts [Symbol] :clear_actions true if all enqueued but unprocessed + # actions should be discarded on restart, else false (default: false) + # @return [Boolean] true + # + # @raise [Concurrent:AgentError] when not failed + def restart(new_value, opts = {}) + clear_actions = opts.fetch(:clear_actions, false) + synchronize do + raise Error.new('agent is not failed') unless failed? + raise ValidationError unless ns_validate(new_value) + @current.value = new_value + @error.value = nil + @queue.clear if clear_actions + ns_post_next_job unless @queue.empty? + end + true + end + + class << self + + # Blocks the current thread (indefinitely!) until all actions dispatched + # thus far to all the given Agents, from this thread or nested by the + # given Agents, have occurred. Will block when any of the agents are + # failed. Will never return if a failed Agent is restart with + # `:clear_actions` true. + # + # @param [Array] agents the Agents on which to wait + # @return [Boolean] true + # + # @!macro agent_await_warning + def await(*agents) + agents.each { |agent| agent.await } + true + end + + # Blocks the current thread until all actions dispatched thus far to all + # the given Agents, from this thread or nested by the given Agents, have + # occurred, or the timeout (in seconds) has elapsed. + # + # @param [Float] timeout the maximum number of seconds to wait + # @param [Array] agents the Agents on which to wait + # @return [Boolean] true if all actions complete before timeout else false + # + # @!macro agent_await_warning + def await_for(timeout, *agents) + end_at = Concurrent.monotonic_time + timeout.to_f + ok = agents.length.times do |i| + break false if (delay = end_at - Concurrent.monotonic_time) < 0 + break false unless agents[i].await_for(delay) + end + !!ok + end + + # Blocks the current thread until all actions dispatched thus far to all + # the given Agents, from this thread or nested by the given Agents, have + # occurred, or the timeout (in seconds) has elapsed. + # + # @param [Float] timeout the maximum number of seconds to wait + # @param [Array] agents the Agents on which to wait + # @return [Boolean] true if all actions complete before timeout + # + # @raise [Concurrent::TimeoutError] when timout is reached + # @!macro agent_await_warning + def await_for!(timeout, *agents) + raise Concurrent::TimeoutError unless await_for(timeout, *agents) + true + end + end + + private + + def ns_initialize(initial, opts) + @error_mode = opts[:error_mode] + @error_handler = opts[:error_handler] + + if @error_mode && !ERROR_MODES.include?(@error_mode) + raise ArgumentError.new('unrecognized error mode') + elsif @error_mode.nil? + @error_mode = @error_handler ? :continue : :fail + end + + @error_handler ||= DEFAULT_ERROR_HANDLER + @validator = opts.fetch(:validator, DEFAULT_VALIDATOR) + @current = Concurrent::AtomicReference.new(initial) + @error = Concurrent::AtomicReference.new(nil) + @caller = Concurrent::ThreadLocalVar.new(nil) + @queue = [] + + self.observers = Collection::CopyOnNotifyObserverSet.new + end + + def enqueue_action_job(action, args, executor) + raise ArgumentError.new('no action given') unless action + job = Job.new(action, args, executor, @caller.value || Thread.current.object_id) + synchronize { ns_enqueue_job(job) } + end + + def enqueue_await_job(latch) + synchronize do + if (index = ns_find_last_job_for_thread) + job = Job.new(AWAIT_ACTION, [latch], Concurrent.global_immediate_executor, + Thread.current.object_id) + ns_enqueue_job(job, index+1) + else + latch.count_down + true + end + end + end + + def ns_enqueue_job(job, index = nil) + # a non-nil index means this is an await job + return false if index.nil? && failed? + index ||= @queue.length + @queue.insert(index, job) + # if this is the only job, post to executor + ns_post_next_job if @queue.length == 1 + true + end + + def ns_post_next_job + @queue.first.executor.post { execute_next_job } + end + + def execute_next_job + job = synchronize { @queue.first } + old_value = @current.value + + @caller.value = job.caller # for nested actions + new_value = job.action.call(old_value, *job.args) + @caller.value = nil + + return if new_value == AWAIT_FLAG + + if ns_validate(new_value) + @current.value = new_value + observers.notify_observers(Time.now, old_value, new_value) + else + handle_error(ValidationError.new) + end + rescue => error + handle_error(error) + ensure + synchronize do + @queue.shift + unless failed? || @queue.empty? + ns_post_next_job + end + end + end + + def ns_validate(value) + @validator.call(value) + rescue + false + end + + def handle_error(error) + # stop new jobs from posting + @error.value = error if @error_mode == :fail + @error_handler.call(self, error) + rescue + # do nothing + end + + def ns_find_last_job_for_thread + @queue.rindex { |job| job.caller == Thread.current.object_id } + end + end +end diff --git a/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/array.rb b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/array.rb new file mode 100644 index 0000000..18e58e5 --- /dev/null +++ b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/array.rb @@ -0,0 +1,66 @@ +require 'concurrent/utility/engine' +require 'concurrent/thread_safe/util' + +module Concurrent + + # @!macro concurrent_array + # + # A thread-safe subclass of Array. This version locks against the object + # itself for every method call, ensuring only one thread can be reading + # or writing at a time. This includes iteration methods like `#each`. + # + # @note `a += b` is **not** a **thread-safe** operation on + # `Concurrent::Array`. It reads array `a`, then it creates new `Concurrent::Array` + # which is concatenation of `a` and `b`, then it writes the concatenation to `a`. + # The read and write are independent operations they do not form a single atomic + # operation therefore when two `+=` operations are executed concurrently updates + # may be lost. Use `#concat` instead. + # + # @see http://ruby-doc.org/core-2.2.0/Array.html Ruby standard library `Array` + + # @!macro internal_implementation_note + ArrayImplementation = case + when Concurrent.on_cruby? + # Array is thread-safe in practice because CRuby runs + # threads one at a time and does not do context + # switching during the execution of C functions. + ::Array + + when Concurrent.on_jruby? + require 'jruby/synchronized' + + class JRubyArray < ::Array + include JRuby::Synchronized + end + JRubyArray + + when Concurrent.on_rbx? + require 'monitor' + require 'concurrent/thread_safe/util/data_structures' + + class RbxArray < ::Array + end + + ThreadSafe::Util.make_synchronized_on_rbx RbxArray + RbxArray + + when Concurrent.on_truffleruby? + require 'concurrent/thread_safe/util/data_structures' + + class TruffleRubyArray < ::Array + end + + ThreadSafe::Util.make_synchronized_on_truffleruby TruffleRubyArray + TruffleRubyArray + + else + warn 'Possibly unsupported Ruby implementation' + ::Array + end + private_constant :ArrayImplementation + + # @!macro concurrent_array + class Array < ArrayImplementation + end + +end diff --git a/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/async.rb b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/async.rb new file mode 100644 index 0000000..8fd4886 --- /dev/null +++ b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/async.rb @@ -0,0 +1,459 @@ +require 'concurrent/configuration' +require 'concurrent/ivar' +require 'concurrent/synchronization/lockable_object' + +module Concurrent + + # A mixin module that provides simple asynchronous behavior to a class, + # turning it into a simple actor. Loosely based on Erlang's + # [gen_server](http://www.erlang.org/doc/man/gen_server.html), but without + # supervision or linking. + # + # A more feature-rich {Concurrent::Actor} is also available when the + # capabilities of `Async` are too limited. + # + # ```cucumber + # Feature: + # As a stateful, plain old Ruby class + # I want safe, asynchronous behavior + # So my long-running methods don't block the main thread + # ``` + # + # The `Async` module is a way to mix simple yet powerful asynchronous + # capabilities into any plain old Ruby object or class, turning each object + # into a simple Actor. Method calls are processed on a background thread. The + # caller is free to perform other actions while processing occurs in the + # background. + # + # Method calls to the asynchronous object are made via two proxy methods: + # `async` (alias `cast`) and `await` (alias `call`). These proxy methods post + # the method call to the object's background thread and return a "future" + # which will eventually contain the result of the method call. + # + # This behavior is loosely patterned after Erlang's `gen_server` behavior. + # When an Erlang module implements the `gen_server` behavior it becomes + # inherently asynchronous. The `start` or `start_link` function spawns a + # process (similar to a thread but much more lightweight and efficient) and + # returns the ID of the process. Using the process ID, other processes can + # send messages to the `gen_server` via the `cast` and `call` methods. Unlike + # Erlang's `gen_server`, however, `Async` classes do not support linking or + # supervision trees. + # + # ## Basic Usage + # + # When this module is mixed into a class, objects of the class become inherently + # asynchronous. Each object gets its own background thread on which to post + # asynchronous method calls. Asynchronous method calls are executed in the + # background one at a time in the order they are received. + # + # To create an asynchronous class, simply mix in the `Concurrent::Async` module: + # + # ``` + # class Hello + # include Concurrent::Async + # + # def hello(name) + # "Hello, #{name}!" + # end + # end + # ``` + # + # When defining a constructor it is critical that the first line be a call to + # `super` with no arguments. The `super` method initializes the background + # thread and other asynchronous components. + # + # ``` + # class BackgroundLogger + # include Concurrent::Async + # + # def initialize(level) + # super() + # @logger = Logger.new(STDOUT) + # @logger.level = level + # end + # + # def info(msg) + # @logger.info(msg) + # end + # end + # ``` + # + # Mixing this module into a class provides each object two proxy methods: + # `async` and `await`. These methods are thread safe with respect to the + # enclosing object. The former proxy allows methods to be called + # asynchronously by posting to the object's internal thread. The latter proxy + # allows a method to be called synchronously but does so safely with respect + # to any pending asynchronous method calls and ensures proper ordering. Both + # methods return a {Concurrent::IVar} which can be inspected for the result + # of the proxied method call. Calling a method with `async` will return a + # `:pending` `IVar` whereas `await` will return a `:complete` `IVar`. + # + # ``` + # class Echo + # include Concurrent::Async + # + # def echo(msg) + # print "#{msg}\n" + # end + # end + # + # horn = Echo.new + # horn.echo('zero') # synchronous, not thread-safe + # # returns the actual return value of the method + # + # horn.async.echo('one') # asynchronous, non-blocking, thread-safe + # # returns an IVar in the :pending state + # + # horn.await.echo('two') # synchronous, blocking, thread-safe + # # returns an IVar in the :complete state + # ``` + # + # ## Let It Fail + # + # The `async` and `await` proxy methods have built-in error protection based + # on Erlang's famous "let it fail" philosophy. Instance methods should not be + # programmed defensively. When an exception is raised by a delegated method + # the proxy will rescue the exception, expose it to the caller as the `reason` + # attribute of the returned future, then process the next method call. + # + # ## Calling Methods Internally + # + # External method calls should *always* use the `async` and `await` proxy + # methods. When one method calls another method, the `async` proxy should + # rarely be used and the `await` proxy should *never* be used. + # + # When an object calls one of its own methods using the `await` proxy the + # second call will be enqueued *behind* the currently running method call. + # Any attempt to wait on the result will fail as the second call will never + # run until after the current call completes. + # + # Calling a method using the `await` proxy from within a method that was + # itself called using `async` or `await` will irreversibly deadlock the + # object. Do *not* do this, ever. + # + # ## Instance Variables and Attribute Accessors + # + # Instance variables do not need to be thread-safe so long as they are private. + # Asynchronous method calls are processed in the order they are received and + # are processed one at a time. Therefore private instance variables can only + # be accessed by one thread at a time. This is inherently thread-safe. + # + # When using private instance variables within asynchronous methods, the best + # practice is to read the instance variable into a local variable at the start + # of the method then update the instance variable at the *end* of the method. + # This way, should an exception be raised during method execution the internal + # state of the object will not have been changed. + # + # ### Reader Attributes + # + # The use of `attr_reader` is discouraged. Internal state exposed externally, + # when necessary, should be done through accessor methods. The instance + # variables exposed by these methods *must* be thread-safe, or they must be + # called using the `async` and `await` proxy methods. These two approaches are + # subtly different. + # + # When internal state is accessed via the `async` and `await` proxy methods, + # the returned value represents the object's state *at the time the call is + # processed*, which may *not* be the state of the object at the time the call + # is made. + # + # To get the state *at the current* time, irrespective of an enqueued method + # calls, a reader method must be called directly. This is inherently unsafe + # unless the instance variable is itself thread-safe, preferably using one + # of the thread-safe classes within this library. Because the thread-safe + # classes within this library are internally-locking or non-locking, they can + # be safely used from within asynchronous methods without causing deadlocks. + # + # Generally speaking, the best practice is to *not* expose internal state via + # reader methods. The best practice is to simply use the method's return value. + # + # ### Writer Attributes + # + # Writer attributes should never be used with asynchronous classes. Changing + # the state externally, even when done in the thread-safe way, is not logically + # consistent. Changes to state need to be timed with respect to all asynchronous + # method calls which my be in-process or enqueued. The only safe practice is to + # pass all necessary data to each method as arguments and let the method update + # the internal state as necessary. + # + # ## Class Constants, Variables, and Methods + # + # ### Class Constants + # + # Class constants do not need to be thread-safe. Since they are read-only and + # immutable they may be safely read both externally and from within + # asynchronous methods. + # + # ### Class Variables + # + # Class variables should be avoided. Class variables represent shared state. + # Shared state is anathema to concurrency. Should there be a need to share + # state using class variables they *must* be thread-safe, preferably + # using the thread-safe classes within this library. When updating class + # variables, never assign a new value/object to the variable itself. Assignment + # is not thread-safe in Ruby. Instead, use the thread-safe update functions + # of the variable itself to change the value. + # + # The best practice is to *never* use class variables with `Async` classes. + # + # ### Class Methods + # + # Class methods which are pure functions are safe. Class methods which modify + # class variables should be avoided, for all the reasons listed above. + # + # ## An Important Note About Thread Safe Guarantees + # + # > Thread safe guarantees can only be made when asynchronous method calls + # > are not mixed with direct method calls. Use only direct method calls + # > when the object is used exclusively on a single thread. Use only + # > `async` and `await` when the object is shared between threads. Once you + # > call a method using `async` or `await`, you should no longer call methods + # > directly on the object. Use `async` and `await` exclusively from then on. + # + # @example + # + # class Echo + # include Concurrent::Async + # + # def echo(msg) + # print "#{msg}\n" + # end + # end + # + # horn = Echo.new + # horn.echo('zero') # synchronous, not thread-safe + # # returns the actual return value of the method + # + # horn.async.echo('one') # asynchronous, non-blocking, thread-safe + # # returns an IVar in the :pending state + # + # horn.await.echo('two') # synchronous, blocking, thread-safe + # # returns an IVar in the :complete state + # + # @see Concurrent::Actor + # @see https://en.wikipedia.org/wiki/Actor_model "Actor Model" at Wikipedia + # @see http://www.erlang.org/doc/man/gen_server.html Erlang gen_server + # @see http://c2.com/cgi/wiki?LetItCrash "Let It Crash" at http://c2.com/ + module Async + + # @!method self.new(*args, &block) + # + # Instanciate a new object and ensure proper initialization of the + # synchronization mechanisms. + # + # @param [Array] args Zero or more arguments to be passed to the + # object's initializer. + # @param [Proc] block Optional block to pass to the object's initializer. + # @return [Object] A properly initialized object of the asynchronous class. + + # Check for the presence of a method on an object and determine if a given + # set of arguments matches the required arity. + # + # @param [Object] obj the object to check against + # @param [Symbol] method the method to check the object for + # @param [Array] args zero or more arguments for the arity check + # + # @raise [NameError] the object does not respond to `method` method + # @raise [ArgumentError] the given `args` do not match the arity of `method` + # + # @note This check is imperfect because of the way Ruby reports the arity of + # methods with a variable number of arguments. It is possible to determine + # if too few arguments are given but impossible to determine if too many + # arguments are given. This check may also fail to recognize dynamic behavior + # of the object, such as methods simulated with `method_missing`. + # + # @see http://www.ruby-doc.org/core-2.1.1/Method.html#method-i-arity Method#arity + # @see http://ruby-doc.org/core-2.1.0/Object.html#method-i-respond_to-3F Object#respond_to? + # @see http://www.ruby-doc.org/core-2.1.0/BasicObject.html#method-i-method_missing BasicObject#method_missing + # + # @!visibility private + def self.validate_argc(obj, method, *args) + argc = args.length + arity = obj.method(method).arity + + if arity >= 0 && argc != arity + raise ArgumentError.new("wrong number of arguments (#{argc} for #{arity})") + elsif arity < 0 && (arity = (arity + 1).abs) > argc + raise ArgumentError.new("wrong number of arguments (#{argc} for #{arity}..*)") + end + end + + # @!visibility private + def self.included(base) + base.singleton_class.send(:alias_method, :original_new, :new) + base.extend(ClassMethods) + super(base) + end + + # @!visibility private + module ClassMethods + def new(*args, &block) + obj = original_new(*args, &block) + obj.send(:init_synchronization) + obj + end + end + private_constant :ClassMethods + + # Delegates asynchronous, thread-safe method calls to the wrapped object. + # + # @!visibility private + class AsyncDelegator < Synchronization::LockableObject + safe_initialization! + + # Create a new delegator object wrapping the given delegate. + # + # @param [Object] delegate the object to wrap and delegate method calls to + def initialize(delegate) + super() + @delegate = delegate + @queue = [] + @executor = Concurrent.global_io_executor + end + + # Delegates method calls to the wrapped object. + # + # @param [Symbol] method the method being called + # @param [Array] args zero or more arguments to the method + # + # @return [IVar] the result of the method call + # + # @raise [NameError] the object does not respond to `method` method + # @raise [ArgumentError] the given `args` do not match the arity of `method` + def method_missing(method, *args, &block) + super unless @delegate.respond_to?(method) + Async::validate_argc(@delegate, method, *args) + + ivar = Concurrent::IVar.new + synchronize do + @queue.push [ivar, method, args, block] + @executor.post { perform } if @queue.length == 1 + end + + ivar + end + + # Check whether the method is responsive + # + # @param [Symbol] method the method being called + def respond_to_missing?(method, include_private = false) + @delegate.respond_to?(method) || super + end + + # Perform all enqueued tasks. + # + # This method must be called from within the executor. It must not be + # called while already running. It will loop until the queue is empty. + def perform + loop do + ivar, method, args, block = synchronize { @queue.first } + break unless ivar # queue is empty + + begin + ivar.set(@delegate.send(method, *args, &block)) + rescue => error + ivar.fail(error) + end + + synchronize do + @queue.shift + return if @queue.empty? + end + end + end + end + private_constant :AsyncDelegator + + # Delegates synchronous, thread-safe method calls to the wrapped object. + # + # @!visibility private + class AwaitDelegator + + # Create a new delegator object wrapping the given delegate. + # + # @param [AsyncDelegator] delegate the object to wrap and delegate method calls to + def initialize(delegate) + @delegate = delegate + end + + # Delegates method calls to the wrapped object. + # + # @param [Symbol] method the method being called + # @param [Array] args zero or more arguments to the method + # + # @return [IVar] the result of the method call + # + # @raise [NameError] the object does not respond to `method` method + # @raise [ArgumentError] the given `args` do not match the arity of `method` + def method_missing(method, *args, &block) + ivar = @delegate.send(method, *args, &block) + ivar.wait + ivar + end + + # Check whether the method is responsive + # + # @param [Symbol] method the method being called + def respond_to_missing?(method, include_private = false) + @delegate.respond_to?(method) || super + end + end + private_constant :AwaitDelegator + + # Causes the chained method call to be performed asynchronously on the + # object's thread. The delegated method will return a future in the + # `:pending` state and the method call will have been scheduled on the + # object's thread. The final disposition of the method call can be obtained + # by inspecting the returned future. + # + # @!macro async_thread_safety_warning + # @note The method call is guaranteed to be thread safe with respect to + # all other method calls against the same object that are called with + # either `async` or `await`. The mutable nature of Ruby references + # (and object orientation in general) prevent any other thread safety + # guarantees. Do NOT mix direct method calls with delegated method calls. + # Use *only* delegated method calls when sharing the object between threads. + # + # @return [Concurrent::IVar] the pending result of the asynchronous operation + # + # @raise [NameError] the object does not respond to the requested method + # @raise [ArgumentError] the given `args` do not match the arity of + # the requested method + def async + @__async_delegator__ + end + alias_method :cast, :async + + # Causes the chained method call to be performed synchronously on the + # current thread. The delegated will return a future in either the + # `:fulfilled` or `:rejected` state and the delegated method will have + # completed. The final disposition of the delegated method can be obtained + # by inspecting the returned future. + # + # @!macro async_thread_safety_warning + # + # @return [Concurrent::IVar] the completed result of the synchronous operation + # + # @raise [NameError] the object does not respond to the requested method + # @raise [ArgumentError] the given `args` do not match the arity of the + # requested method + def await + @__await_delegator__ + end + alias_method :call, :await + + # Initialize the internal serializer and other stnchronization mechanisms. + # + # @note This method *must* be called immediately upon object construction. + # This is the only way thread-safe initialization can be guaranteed. + # + # @!visibility private + def init_synchronization + return self if defined?(@__async_initialized__) && @__async_initialized__ + @__async_initialized__ = true + @__async_delegator__ = AsyncDelegator.new(self) + @__await_delegator__ = AwaitDelegator.new(@__async_delegator__) + self + end + end +end diff --git a/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/atom.rb b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/atom.rb new file mode 100644 index 0000000..abef1cb --- /dev/null +++ b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/atom.rb @@ -0,0 +1,222 @@ +require 'concurrent/atomic/atomic_reference' +require 'concurrent/collection/copy_on_notify_observer_set' +require 'concurrent/concern/observable' +require 'concurrent/synchronization' + +# @!macro thread_safe_variable_comparison +# +# ## Thread-safe Variable Classes +# +# Each of the thread-safe variable classes is designed to solve a different +# problem. In general: +# +# * *{Concurrent::Agent}:* Shared, mutable variable providing independent, +# uncoordinated, *asynchronous* change of individual values. Best used when +# the value will undergo frequent, complex updates. Suitable when the result +# of an update does not need to be known immediately. +# * *{Concurrent::Atom}:* Shared, mutable variable providing independent, +# uncoordinated, *synchronous* change of individual values. Best used when +# the value will undergo frequent reads but only occasional, though complex, +# updates. Suitable when the result of an update must be known immediately. +# * *{Concurrent::AtomicReference}:* A simple object reference that can be +# atomically. Updates are synchronous but fast. Best used when updates a +# simple set operations. Not suitable when updates are complex. +# {Concurrent::AtomicBoolean} and {Concurrent::AtomicFixnum} are similar +# but optimized for the given data type. +# * *{Concurrent::Exchanger}:* Shared, stateless synchronization point. Used +# when two or more threads need to exchange data. The threads will pair then +# block on each other until the exchange is complete. +# * *{Concurrent::MVar}:* Shared synchronization point. Used when one thread +# must give a value to another, which must take the value. The threads will +# block on each other until the exchange is complete. +# * *{Concurrent::ThreadLocalVar}:* Shared, mutable, isolated variable which +# holds a different value for each thread which has access. Often used as +# an instance variable in objects which must maintain different state +# for different threads. +# * *{Concurrent::TVar}:* Shared, mutable variables which provide +# *coordinated*, *synchronous*, change of *many* stated. Used when multiple +# value must change together, in an all-or-nothing transaction. + + +module Concurrent + + # Atoms provide a way to manage shared, synchronous, independent state. + # + # An atom is initialized with an initial value and an optional validation + # proc. At any time the value of the atom can be synchronously and safely + # changed. If a validator is given at construction then any new value + # will be checked against the validator and will be rejected if the + # validator returns false or raises an exception. + # + # There are two ways to change the value of an atom: {#compare_and_set} and + # {#swap}. The former will set the new value if and only if it validates and + # the current value matches the new value. The latter will atomically set the + # new value to the result of running the given block if and only if that + # value validates. + # + # ## Example + # + # ``` + # def next_fibonacci(set = nil) + # return [0, 1] if set.nil? + # set + [set[-2..-1].reduce{|sum,x| sum + x }] + # end + # + # # create an atom with an initial value + # atom = Concurrent::Atom.new(next_fibonacci) + # + # # send a few update requests + # 5.times do + # atom.swap{|set| next_fibonacci(set) } + # end + # + # # get the current value + # atom.value #=> [0, 1, 1, 2, 3, 5, 8] + # ``` + # + # ## Observation + # + # Atoms support observers through the {Concurrent::Observable} mixin module. + # Notification of observers occurs every time the value of the Atom changes. + # When notified the observer will receive three arguments: `time`, `old_value`, + # and `new_value`. The `time` argument is the time at which the value change + # occurred. The `old_value` is the value of the Atom when the change began + # The `new_value` is the value to which the Atom was set when the change + # completed. Note that `old_value` and `new_value` may be the same. This is + # not an error. It simply means that the change operation returned the same + # value. + # + # Unlike in Clojure, `Atom` cannot participate in {Concurrent::TVar} transactions. + # + # @!macro thread_safe_variable_comparison + # + # @see http://clojure.org/atoms Clojure Atoms + # @see http://clojure.org/state Values and Change - Clojure's approach to Identity and State + class Atom < Synchronization::Object + include Concern::Observable + + safe_initialization! + attr_atomic(:value) + private :value=, :swap_value, :compare_and_set_value, :update_value + public :value + alias_method :deref, :value + + # @!method value + # The current value of the atom. + # + # @return [Object] The current value. + + # Create a new atom with the given initial value. + # + # @param [Object] value The initial value + # @param [Hash] opts The options used to configure the atom + # @option opts [Proc] :validator (nil) Optional proc used to validate new + # values. It must accept one and only one argument which will be the + # intended new value. The validator will return true if the new value + # is acceptable else return false (preferrably) or raise an exception. + # + # @!macro deref_options + # + # @raise [ArgumentError] if the validator is not a `Proc` (when given) + def initialize(value, opts = {}) + super() + @Validator = opts.fetch(:validator, -> v { true }) + self.observers = Collection::CopyOnNotifyObserverSet.new + self.value = value + end + + # Atomically swaps the value of atom using the given block. The current + # value will be passed to the block, as will any arguments passed as + # arguments to the function. The new value will be validated against the + # (optional) validator proc given at construction. If validation fails the + # value will not be changed. + # + # Internally, {#swap} reads the current value, applies the block to it, and + # attempts to compare-and-set it in. Since another thread may have changed + # the value in the intervening time, it may have to retry, and does so in a + # spin loop. The net effect is that the value will always be the result of + # the application of the supplied block to a current value, atomically. + # However, because the block might be called multiple times, it must be free + # of side effects. + # + # @note The given block may be called multiple times, and thus should be free + # of side effects. + # + # @param [Object] args Zero or more arguments passed to the block. + # + # @yield [value, args] Calculates a new value for the atom based on the + # current value and any supplied arguments. + # @yieldparam value [Object] The current value of the atom. + # @yieldparam args [Object] All arguments passed to the function, in order. + # @yieldreturn [Object] The intended new value of the atom. + # + # @return [Object] The final value of the atom after all operations and + # validations are complete. + # + # @raise [ArgumentError] When no block is given. + def swap(*args) + raise ArgumentError.new('no block given') unless block_given? + + loop do + old_value = value + new_value = yield(old_value, *args) + begin + break old_value unless valid?(new_value) + break new_value if compare_and_set(old_value, new_value) + rescue + break old_value + end + end + end + + # Atomically sets the value of atom to the new value if and only if the + # current value of the atom is identical to the old value and the new + # value successfully validates against the (optional) validator given + # at construction. + # + # @param [Object] old_value The expected current value. + # @param [Object] new_value The intended new value. + # + # @return [Boolean] True if the value is changed else false. + def compare_and_set(old_value, new_value) + if valid?(new_value) && compare_and_set_value(old_value, new_value) + observers.notify_observers(Time.now, old_value, new_value) + true + else + false + end + end + + # Atomically sets the value of atom to the new value without regard for the + # current value so long as the new value successfully validates against the + # (optional) validator given at construction. + # + # @param [Object] new_value The intended new value. + # + # @return [Object] The final value of the atom after all operations and + # validations are complete. + def reset(new_value) + old_value = value + if valid?(new_value) + self.value = new_value + observers.notify_observers(Time.now, old_value, new_value) + new_value + else + old_value + end + end + + private + + # Is the new value valid? + # + # @param [Object] new_value The intended new value. + # @return [Boolean] false if the validator function returns false or raises + # an exception else true + def valid?(new_value) + @Validator.call(new_value) + rescue + false + end + end +end diff --git a/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic/abstract_thread_local_var.rb b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic/abstract_thread_local_var.rb new file mode 100644 index 0000000..fcdeed7 --- /dev/null +++ b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic/abstract_thread_local_var.rb @@ -0,0 +1,66 @@ +require 'concurrent/constants' + +module Concurrent + + # @!macro thread_local_var + # @!macro internal_implementation_note + # @!visibility private + class AbstractThreadLocalVar + + # @!macro thread_local_var_method_initialize + def initialize(default = nil, &default_block) + if default && block_given? + raise ArgumentError, "Cannot use both value and block as default value" + end + + if block_given? + @default_block = default_block + @default = nil + else + @default_block = nil + @default = default + end + + allocate_storage + end + + # @!macro thread_local_var_method_get + def value + raise NotImplementedError + end + + # @!macro thread_local_var_method_set + def value=(value) + raise NotImplementedError + end + + # @!macro thread_local_var_method_bind + def bind(value, &block) + if block_given? + old_value = self.value + begin + self.value = value + yield + ensure + self.value = old_value + end + end + end + + protected + + # @!visibility private + def allocate_storage + raise NotImplementedError + end + + # @!visibility private + def default + if @default_block + self.value = @default_block.call + else + @default + end + end + end +end diff --git a/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic/atomic_boolean.rb b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic/atomic_boolean.rb new file mode 100644 index 0000000..4da4419 --- /dev/null +++ b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic/atomic_boolean.rb @@ -0,0 +1,126 @@ +require 'concurrent/atomic/mutex_atomic_boolean' +require 'concurrent/synchronization' + +module Concurrent + + ################################################################### + + # @!macro atomic_boolean_method_initialize + # + # Creates a new `AtomicBoolean` with the given initial value. + # + # @param [Boolean] initial the initial value + + # @!macro atomic_boolean_method_value_get + # + # Retrieves the current `Boolean` value. + # + # @return [Boolean] the current value + + # @!macro atomic_boolean_method_value_set + # + # Explicitly sets the value. + # + # @param [Boolean] value the new value to be set + # + # @return [Boolean] the current value + + # @!macro atomic_boolean_method_true_question + # + # Is the current value `true` + # + # @return [Boolean] true if the current value is `true`, else false + + # @!macro atomic_boolean_method_false_question + # + # Is the current value `false` + # + # @return [Boolean] true if the current value is `false`, else false + + # @!macro atomic_boolean_method_make_true + # + # Explicitly sets the value to true. + # + # @return [Boolean] true is value has changed, otherwise false + + # @!macro atomic_boolean_method_make_false + # + # Explicitly sets the value to false. + # + # @return [Boolean] true is value has changed, otherwise false + + ################################################################### + + # @!macro atomic_boolean_public_api + # + # @!method initialize(initial = false) + # @!macro atomic_boolean_method_initialize + # + # @!method value + # @!macro atomic_boolean_method_value_get + # + # @!method value=(value) + # @!macro atomic_boolean_method_value_set + # + # @!method true? + # @!macro atomic_boolean_method_true_question + # + # @!method false? + # @!macro atomic_boolean_method_false_question + # + # @!method make_true + # @!macro atomic_boolean_method_make_true + # + # @!method make_false + # @!macro atomic_boolean_method_make_false + + ################################################################### + + # @!visibility private + # @!macro internal_implementation_note + AtomicBooleanImplementation = case + when defined?(JavaAtomicBoolean) + JavaAtomicBoolean + when defined?(CAtomicBoolean) + CAtomicBoolean + else + MutexAtomicBoolean + end + private_constant :AtomicBooleanImplementation + + # @!macro atomic_boolean + # + # A boolean value that can be updated atomically. Reads and writes to an atomic + # boolean and thread-safe and guaranteed to succeed. Reads and writes may block + # briefly but no explicit locking is required. + # + # @!macro thread_safe_variable_comparison + # + # Performance: + # + # ``` + # Testing with ruby 2.1.2 + # Testing with Concurrent::MutexAtomicBoolean... + # 2.790000 0.000000 2.790000 ( 2.791454) + # Testing with Concurrent::CAtomicBoolean... + # 0.740000 0.000000 0.740000 ( 0.740206) + # + # Testing with jruby 1.9.3 + # Testing with Concurrent::MutexAtomicBoolean... + # 5.240000 2.520000 7.760000 ( 3.683000) + # Testing with Concurrent::JavaAtomicBoolean... + # 3.340000 0.010000 3.350000 ( 0.855000) + # ``` + # + # @see http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/atomic/AtomicBoolean.html java.util.concurrent.atomic.AtomicBoolean + # + # @!macro atomic_boolean_public_api + class AtomicBoolean < AtomicBooleanImplementation + # @return [String] Short string representation. + def to_s + format '%s value:%s>', super[0..-2], value + end + + alias_method :inspect, :to_s + end +end diff --git a/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic/atomic_fixnum.rb b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic/atomic_fixnum.rb new file mode 100644 index 0000000..c67166d --- /dev/null +++ b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic/atomic_fixnum.rb @@ -0,0 +1,143 @@ +require 'concurrent/atomic/mutex_atomic_fixnum' +require 'concurrent/synchronization' + +module Concurrent + + ################################################################### + + # @!macro atomic_fixnum_method_initialize + # + # Creates a new `AtomicFixnum` with the given initial value. + # + # @param [Fixnum] initial the initial value + # @raise [ArgumentError] if the initial value is not a `Fixnum` + + # @!macro atomic_fixnum_method_value_get + # + # Retrieves the current `Fixnum` value. + # + # @return [Fixnum] the current value + + # @!macro atomic_fixnum_method_value_set + # + # Explicitly sets the value. + # + # @param [Fixnum] value the new value to be set + # + # @return [Fixnum] the current value + # + # @raise [ArgumentError] if the new value is not a `Fixnum` + + # @!macro atomic_fixnum_method_increment + # + # Increases the current value by the given amount (defaults to 1). + # + # @param [Fixnum] delta the amount by which to increase the current value + # + # @return [Fixnum] the current value after incrementation + + # @!macro atomic_fixnum_method_decrement + # + # Decreases the current value by the given amount (defaults to 1). + # + # @param [Fixnum] delta the amount by which to decrease the current value + # + # @return [Fixnum] the current value after decrementation + + # @!macro atomic_fixnum_method_compare_and_set + # + # Atomically sets the value to the given updated value if the current + # value == the expected value. + # + # @param [Fixnum] expect the expected value + # @param [Fixnum] update the new value + # + # @return [Boolean] true if the value was updated else false + + # @!macro atomic_fixnum_method_update + # + # Pass the current value to the given block, replacing it + # with the block's result. May retry if the value changes + # during the block's execution. + # + # @yield [Object] Calculate a new value for the atomic reference using + # given (old) value + # @yieldparam [Object] old_value the starting value of the atomic reference + # + # @return [Object] the new value + + ################################################################### + + # @!macro atomic_fixnum_public_api + # + # @!method initialize(initial = 0) + # @!macro atomic_fixnum_method_initialize + # + # @!method value + # @!macro atomic_fixnum_method_value_get + # + # @!method value=(value) + # @!macro atomic_fixnum_method_value_set + # + # @!method increment(delta = 1) + # @!macro atomic_fixnum_method_increment + # + # @!method decrement(delta = 1) + # @!macro atomic_fixnum_method_decrement + # + # @!method compare_and_set(expect, update) + # @!macro atomic_fixnum_method_compare_and_set + # + # @!method update + # @!macro atomic_fixnum_method_update + + ################################################################### + + # @!visibility private + # @!macro internal_implementation_note + AtomicFixnumImplementation = case + when defined?(JavaAtomicFixnum) + JavaAtomicFixnum + when defined?(CAtomicFixnum) + CAtomicFixnum + else + MutexAtomicFixnum + end + private_constant :AtomicFixnumImplementation + + # @!macro atomic_fixnum + # + # A numeric value that can be updated atomically. Reads and writes to an atomic + # fixnum and thread-safe and guaranteed to succeed. Reads and writes may block + # briefly but no explicit locking is required. + # + # @!macro thread_safe_variable_comparison + # + # Performance: + # + # ``` + # Testing with ruby 2.1.2 + # Testing with Concurrent::MutexAtomicFixnum... + # 3.130000 0.000000 3.130000 ( 3.136505) + # Testing with Concurrent::CAtomicFixnum... + # 0.790000 0.000000 0.790000 ( 0.785550) + # + # Testing with jruby 1.9.3 + # Testing with Concurrent::MutexAtomicFixnum... + # 5.460000 2.460000 7.920000 ( 3.715000) + # Testing with Concurrent::JavaAtomicFixnum... + # 4.520000 0.030000 4.550000 ( 1.187000) + # ``` + # + # @see http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/atomic/AtomicLong.html java.util.concurrent.atomic.AtomicLong + # + # @!macro atomic_fixnum_public_api + class AtomicFixnum < AtomicFixnumImplementation + # @return [String] Short string representation. + def to_s + format '%s value:%s>', super[0..-2], value + end + + alias_method :inspect, :to_s + end +end diff --git a/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic/atomic_markable_reference.rb b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic/atomic_markable_reference.rb new file mode 100644 index 0000000..f20cd46 --- /dev/null +++ b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic/atomic_markable_reference.rb @@ -0,0 +1,164 @@ +module Concurrent + # An atomic reference which maintains an object reference along with a mark bit + # that can be updated atomically. + # + # @see http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/atomic/AtomicMarkableReference.html + # java.util.concurrent.atomic.AtomicMarkableReference + class AtomicMarkableReference < ::Concurrent::Synchronization::Object + + attr_atomic(:reference) + private :reference, :reference=, :swap_reference, :compare_and_set_reference, :update_reference + + def initialize(value = nil, mark = false) + super() + self.reference = immutable_array(value, mark) + end + + # Atomically sets the value and mark to the given updated value and + # mark given both: + # - the current value == the expected value && + # - the current mark == the expected mark + # + # @param [Object] expected_val the expected value + # @param [Object] new_val the new value + # @param [Boolean] expected_mark the expected mark + # @param [Boolean] new_mark the new mark + # + # @return [Boolean] `true` if successful. A `false` return indicates + # that the actual value was not equal to the expected value or the + # actual mark was not equal to the expected mark + def compare_and_set(expected_val, new_val, expected_mark, new_mark) + # Memoize a valid reference to the current AtomicReference for + # later comparison. + current = reference + curr_val, curr_mark = current + + # Ensure that that the expected marks match. + return false unless expected_mark == curr_mark + + if expected_val.is_a? Numeric + # If the object is a numeric, we need to ensure we are comparing + # the numerical values + return false unless expected_val == curr_val + else + # Otherwise, we need to ensure we are comparing the object identity. + # Theoretically, this could be incorrect if a user monkey-patched + # `Object#equal?`, but they should know that they are playing with + # fire at that point. + return false unless expected_val.equal? curr_val + end + + prospect = immutable_array(new_val, new_mark) + + compare_and_set_reference current, prospect + end + + alias_method :compare_and_swap, :compare_and_set + + # Gets the current reference and marked values. + # + # @return [Array] the current reference and marked values + def get + reference + end + + # Gets the current value of the reference + # + # @return [Object] the current value of the reference + def value + reference[0] + end + + # Gets the current marked value + # + # @return [Boolean] the current marked value + def mark + reference[1] + end + + alias_method :marked?, :mark + + # _Unconditionally_ sets to the given value of both the reference and + # the mark. + # + # @param [Object] new_val the new value + # @param [Boolean] new_mark the new mark + # + # @return [Array] both the new value and the new mark + def set(new_val, new_mark) + self.reference = immutable_array(new_val, new_mark) + end + + # Pass the current value and marked state to the given block, replacing it + # with the block's results. May retry if the value changes during the + # block's execution. + # + # @yield [Object] Calculate a new value and marked state for the atomic + # reference using given (old) value and (old) marked + # @yieldparam [Object] old_val the starting value of the atomic reference + # @yieldparam [Boolean] old_mark the starting state of marked + # + # @return [Array] the new value and new mark + def update + loop do + old_val, old_mark = reference + new_val, new_mark = yield old_val, old_mark + + if compare_and_set old_val, new_val, old_mark, new_mark + return immutable_array(new_val, new_mark) + end + end + end + + # Pass the current value to the given block, replacing it + # with the block's result. Raise an exception if the update + # fails. + # + # @yield [Object] Calculate a new value and marked state for the atomic + # reference using given (old) value and (old) marked + # @yieldparam [Object] old_val the starting value of the atomic reference + # @yieldparam [Boolean] old_mark the starting state of marked + # + # @return [Array] the new value and marked state + # + # @raise [Concurrent::ConcurrentUpdateError] if the update fails + def try_update! + old_val, old_mark = reference + new_val, new_mark = yield old_val, old_mark + + unless compare_and_set old_val, new_val, old_mark, new_mark + fail ::Concurrent::ConcurrentUpdateError, + 'AtomicMarkableReference: Update failed due to race condition.', + 'Note: If you would like to guarantee an update, please use ' + + 'the `AtomicMarkableReference#update` method.' + end + + immutable_array(new_val, new_mark) + end + + # Pass the current value to the given block, replacing it with the + # block's result. Simply return nil if update fails. + # + # @yield [Object] Calculate a new value and marked state for the atomic + # reference using given (old) value and (old) marked + # @yieldparam [Object] old_val the starting value of the atomic reference + # @yieldparam [Boolean] old_mark the starting state of marked + # + # @return [Array] the new value and marked state, or nil if + # the update failed + def try_update + old_val, old_mark = reference + new_val, new_mark = yield old_val, old_mark + + return unless compare_and_set old_val, new_val, old_mark, new_mark + + immutable_array(new_val, new_mark) + end + + private + + def immutable_array(*args) + args.freeze + end + end +end diff --git a/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic/atomic_reference.rb b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic/atomic_reference.rb new file mode 100644 index 0000000..620c069 --- /dev/null +++ b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic/atomic_reference.rb @@ -0,0 +1,204 @@ +require 'concurrent/synchronization' +require 'concurrent/utility/engine' +require 'concurrent/atomic_reference/numeric_cas_wrapper' + +# Shim for TruffleRuby::AtomicReference +if Concurrent.on_truffleruby? && !defined?(TruffleRuby::AtomicReference) + # @!visibility private + module TruffleRuby + AtomicReference = Truffle::AtomicReference + end +end + +module Concurrent + + # Define update methods that use direct paths + # + # @!visibility private + # @!macro internal_implementation_note + module AtomicDirectUpdate + + # @!macro atomic_reference_method_update + # + # Pass the current value to the given block, replacing it + # with the block's result. May retry if the value changes + # during the block's execution. + # + # @yield [Object] Calculate a new value for the atomic reference using + # given (old) value + # @yieldparam [Object] old_value the starting value of the atomic reference + # @return [Object] the new value + def update + true until compare_and_set(old_value = get, new_value = yield(old_value)) + new_value + end + + # @!macro atomic_reference_method_try_update + # + # Pass the current value to the given block, replacing it + # with the block's result. Return nil if the update fails. + # + # @yield [Object] Calculate a new value for the atomic reference using + # given (old) value + # @yieldparam [Object] old_value the starting value of the atomic reference + # @note This method was altered to avoid raising an exception by default. + # Instead, this method now returns `nil` in case of failure. For more info, + # please see: https://github.com/ruby-concurrency/concurrent-ruby/pull/336 + # @return [Object] the new value, or nil if update failed + def try_update + old_value = get + new_value = yield old_value + + return unless compare_and_set old_value, new_value + + new_value + end + + # @!macro atomic_reference_method_try_update! + # + # Pass the current value to the given block, replacing it + # with the block's result. Raise an exception if the update + # fails. + # + # @yield [Object] Calculate a new value for the atomic reference using + # given (old) value + # @yieldparam [Object] old_value the starting value of the atomic reference + # @note This behavior mimics the behavior of the original + # `AtomicReference#try_update` API. The reason this was changed was to + # avoid raising exceptions (which are inherently slow) by default. For more + # info: https://github.com/ruby-concurrency/concurrent-ruby/pull/336 + # @return [Object] the new value + # @raise [Concurrent::ConcurrentUpdateError] if the update fails + def try_update! + old_value = get + new_value = yield old_value + unless compare_and_set(old_value, new_value) + if $VERBOSE + raise ConcurrentUpdateError, "Update failed" + else + raise ConcurrentUpdateError, "Update failed", ConcurrentUpdateError::CONC_UP_ERR_BACKTRACE + end + end + new_value + end + end + + require 'concurrent/atomic_reference/mutex_atomic' + + # @!macro atomic_reference + # + # An object reference that may be updated atomically. All read and write + # operations have java volatile semantic. + # + # @!macro thread_safe_variable_comparison + # + # @see http://docs.oracle.com/javase/8/docs/api/java/util/concurrent/atomic/AtomicReference.html + # @see http://docs.oracle.com/javase/8/docs/api/java/util/concurrent/atomic/package-summary.html + # + # @!method initialize(value = nil) + # @!macro atomic_reference_method_initialize + # @param [Object] value The initial value. + # + # @!method get + # @!macro atomic_reference_method_get + # Gets the current value. + # @return [Object] the current value + # + # @!method set(new_value) + # @!macro atomic_reference_method_set + # Sets to the given value. + # @param [Object] new_value the new value + # @return [Object] the new value + # + # @!method get_and_set(new_value) + # @!macro atomic_reference_method_get_and_set + # Atomically sets to the given value and returns the old value. + # @param [Object] new_value the new value + # @return [Object] the old value + # + # @!method compare_and_set(old_value, new_value) + # @!macro atomic_reference_method_compare_and_set + # + # Atomically sets the value to the given updated value if + # the current value == the expected value. + # + # @param [Object] old_value the expected value + # @param [Object] new_value the new value + # + # @return [Boolean] `true` if successful. A `false` return indicates + # that the actual value was not equal to the expected value. + # + # @!method update + # @!macro atomic_reference_method_update + # + # @!method try_update + # @!macro atomic_reference_method_try_update + # + # @!method try_update! + # @!macro atomic_reference_method_try_update! + + + # @!macro internal_implementation_note + class ConcurrentUpdateError < ThreadError + # frozen pre-allocated backtrace to speed ConcurrentUpdateError + CONC_UP_ERR_BACKTRACE = ['backtrace elided; set verbose to enable'].freeze + end + + # @!macro internal_implementation_note + AtomicReferenceImplementation = case + when Concurrent.on_cruby? && Concurrent.c_extensions_loaded? + # @!visibility private + # @!macro internal_implementation_note + class CAtomicReference + include AtomicDirectUpdate + include AtomicNumericCompareAndSetWrapper + alias_method :compare_and_swap, :compare_and_set + end + CAtomicReference + when Concurrent.on_jruby? + # @!visibility private + # @!macro internal_implementation_note + class JavaAtomicReference + include AtomicDirectUpdate + end + JavaAtomicReference + when Concurrent.on_truffleruby? + class TruffleRubyAtomicReference < TruffleRuby::AtomicReference + include AtomicDirectUpdate + alias_method :value, :get + alias_method :value=, :set + alias_method :compare_and_swap, :compare_and_set + alias_method :swap, :get_and_set + end + when Concurrent.on_rbx? + # @note Extends `Rubinius::AtomicReference` version adding aliases + # and numeric logic. + # + # @!visibility private + # @!macro internal_implementation_note + class RbxAtomicReference < Rubinius::AtomicReference + alias_method :_compare_and_set, :compare_and_set + include AtomicDirectUpdate + include AtomicNumericCompareAndSetWrapper + alias_method :value, :get + alias_method :value=, :set + alias_method :swap, :get_and_set + alias_method :compare_and_swap, :compare_and_set + end + RbxAtomicReference + else + MutexAtomicReference + end + private_constant :AtomicReferenceImplementation + + # @!macro atomic_reference + class AtomicReference < AtomicReferenceImplementation + + # @return [String] Short string representation. + def to_s + format '%s value:%s>', super[0..-2], get + end + + alias_method :inspect, :to_s + end +end diff --git a/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic/count_down_latch.rb b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic/count_down_latch.rb new file mode 100644 index 0000000..4c0158d --- /dev/null +++ b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic/count_down_latch.rb @@ -0,0 +1,100 @@ +require 'concurrent/atomic/mutex_count_down_latch' +require 'concurrent/atomic/java_count_down_latch' +require 'concurrent/utility/engine' + +module Concurrent + + ################################################################### + + # @!macro count_down_latch_method_initialize + # + # Create a new `CountDownLatch` with the initial `count`. + # + # @param [new] count the initial count + # + # @raise [ArgumentError] if `count` is not an integer or is less than zero + + # @!macro count_down_latch_method_wait + # + # Block on the latch until the counter reaches zero or until `timeout` is reached. + # + # @param [Fixnum] timeout the number of seconds to wait for the counter or `nil` + # to block indefinitely + # @return [Boolean] `true` if the `count` reaches zero else false on `timeout` + + # @!macro count_down_latch_method_count_down + # + # Signal the latch to decrement the counter. Will signal all blocked threads when + # the `count` reaches zero. + + # @!macro count_down_latch_method_count + # + # The current value of the counter. + # + # @return [Fixnum] the current value of the counter + + ################################################################### + + # @!macro count_down_latch_public_api + # + # @!method initialize(count = 1) + # @!macro count_down_latch_method_initialize + # + # @!method wait(timeout = nil) + # @!macro count_down_latch_method_wait + # + # @!method count_down + # @!macro count_down_latch_method_count_down + # + # @!method count + # @!macro count_down_latch_method_count + + ################################################################### + + # @!visibility private + # @!macro internal_implementation_note + CountDownLatchImplementation = case + when Concurrent.on_jruby? + JavaCountDownLatch + else + MutexCountDownLatch + end + private_constant :CountDownLatchImplementation + + # @!macro count_down_latch + # + # A synchronization object that allows one thread to wait on multiple other threads. + # The thread that will wait creates a `CountDownLatch` and sets the initial value + # (normally equal to the number of other threads). The initiating thread passes the + # latch to the other threads then waits for the other threads by calling the `#wait` + # method. Each of the other threads calls `#count_down` when done with its work. + # When the latch counter reaches zero the waiting thread is unblocked and continues + # with its work. A `CountDownLatch` can be used only once. Its value cannot be reset. + # + # @!macro count_down_latch_public_api + # @example Waiter and Decrementer + # latch = Concurrent::CountDownLatch.new(3) + # + # waiter = Thread.new do + # latch.wait() + # puts ("Waiter released") + # end + # + # decrementer = Thread.new do + # sleep(1) + # latch.count_down + # puts latch.count + # + # sleep(1) + # latch.count_down + # puts latch.count + # + # sleep(1) + # latch.count_down + # puts latch.count + # end + # + # [waiter, decrementer].each(&:join) + class CountDownLatch < CountDownLatchImplementation + end +end diff --git a/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic/cyclic_barrier.rb b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic/cyclic_barrier.rb new file mode 100644 index 0000000..42f5a94 --- /dev/null +++ b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic/cyclic_barrier.rb @@ -0,0 +1,128 @@ +require 'concurrent/synchronization' +require 'concurrent/utility/native_integer' + +module Concurrent + + # A synchronization aid that allows a set of threads to all wait for each + # other to reach a common barrier point. + # @example + # barrier = Concurrent::CyclicBarrier.new(3) + # jobs = Array.new(3) { |i| -> { sleep i; p done: i } } + # process = -> (i) do + # # waiting to start at the same time + # barrier.wait + # # execute job + # jobs[i].call + # # wait for others to finish + # barrier.wait + # end + # threads = 2.times.map do |i| + # Thread.new(i, &process) + # end + # + # # use main as well + # process.call 2 + # + # # here we can be sure that all jobs are processed + class CyclicBarrier < Synchronization::LockableObject + + # @!visibility private + Generation = Struct.new(:status) + private_constant :Generation + + # Create a new `CyclicBarrier` that waits for `parties` threads + # + # @param [Fixnum] parties the number of parties + # @yield an optional block that will be executed that will be executed after + # the last thread arrives and before the others are released + # + # @raise [ArgumentError] if `parties` is not an integer or is less than zero + def initialize(parties, &block) + Utility::NativeInteger.ensure_integer_and_bounds parties + Utility::NativeInteger.ensure_positive_and_no_zero parties + + super(&nil) + synchronize { ns_initialize parties, &block } + end + + # @return [Fixnum] the number of threads needed to pass the barrier + def parties + synchronize { @parties } + end + + # @return [Fixnum] the number of threads currently waiting on the barrier + def number_waiting + synchronize { @number_waiting } + end + + # Blocks on the barrier until the number of waiting threads is equal to + # `parties` or until `timeout` is reached or `reset` is called + # If a block has been passed to the constructor, it will be executed once by + # the last arrived thread before releasing the others + # @param [Fixnum] timeout the number of seconds to wait for the counter or + # `nil` to block indefinitely + # @return [Boolean] `true` if the `count` reaches zero else false on + # `timeout` or on `reset` or if the barrier is broken + def wait(timeout = nil) + synchronize do + + return false unless @generation.status == :waiting + + @number_waiting += 1 + + if @number_waiting == @parties + @action.call if @action + ns_generation_done @generation, :fulfilled + true + else + generation = @generation + if ns_wait_until(timeout) { generation.status != :waiting } + generation.status == :fulfilled + else + ns_generation_done generation, :broken, false + false + end + end + end + end + + # resets the barrier to its initial state + # If there is at least one waiting thread, it will be woken up, the `wait` + # method will return false and the barrier will be broken + # If the barrier is broken, this method restores it to the original state + # + # @return [nil] + def reset + synchronize { ns_generation_done @generation, :reset } + end + + # A barrier can be broken when: + # - a thread called the `reset` method while at least one other thread was waiting + # - at least one thread timed out on `wait` method + # + # A broken barrier can be restored using `reset` it's safer to create a new one + # @return [Boolean] true if the barrier is broken otherwise false + def broken? + synchronize { @generation.status != :waiting } + end + + protected + + def ns_generation_done(generation, status, continue = true) + generation.status = status + ns_next_generation if continue + ns_broadcast + end + + def ns_next_generation + @generation = Generation.new(:waiting) + @number_waiting = 0 + end + + def ns_initialize(parties, &block) + @parties = parties + @action = block + ns_next_generation + end + end +end diff --git a/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic/event.rb b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic/event.rb new file mode 100644 index 0000000..825f38a --- /dev/null +++ b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic/event.rb @@ -0,0 +1,109 @@ +require 'thread' +require 'concurrent/synchronization' + +module Concurrent + + # Old school kernel-style event reminiscent of Win32 programming in C++. + # + # When an `Event` is created it is in the `unset` state. Threads can choose to + # `#wait` on the event, blocking until released by another thread. When one + # thread wants to alert all blocking threads it calls the `#set` method which + # will then wake up all listeners. Once an `Event` has been set it remains set. + # New threads calling `#wait` will return immediately. An `Event` may be + # `#reset` at any time once it has been set. + # + # @see http://msdn.microsoft.com/en-us/library/windows/desktop/ms682655.aspx + # @example + # event = Concurrent::Event.new + # + # t1 = Thread.new do + # puts "t1 is waiting" + # event.wait(1) + # puts "event ocurred" + # end + # + # t2 = Thread.new do + # puts "t2 calling set" + # event.set + # end + # + # [t1, t2].each(&:join) + # + # # prints: + # # t2 calling set + # # t1 is waiting + # # event occurred + class Event < Synchronization::LockableObject + + # Creates a new `Event` in the unset state. Threads calling `#wait` on the + # `Event` will block. + def initialize + super + synchronize { ns_initialize } + end + + # Is the object in the set state? + # + # @return [Boolean] indicating whether or not the `Event` has been set + def set? + synchronize { @set } + end + + # Trigger the event, setting the state to `set` and releasing all threads + # waiting on the event. Has no effect if the `Event` has already been set. + # + # @return [Boolean] should always return `true` + def set + synchronize { ns_set } + end + + def try? + synchronize { @set ? false : ns_set } + end + + # Reset a previously set event back to the `unset` state. + # Has no effect if the `Event` has not yet been set. + # + # @return [Boolean] should always return `true` + def reset + synchronize do + if @set + @set = false + @iteration +=1 + end + true + end + end + + # Wait a given number of seconds for the `Event` to be set by another + # thread. Will wait forever when no `timeout` value is given. Returns + # immediately if the `Event` has already been set. + # + # @return [Boolean] true if the `Event` was set before timeout else false + def wait(timeout = nil) + synchronize do + unless @set + iteration = @iteration + ns_wait_until(timeout) { iteration < @iteration || @set } + else + true + end + end + end + + protected + + def ns_set + unless @set + @set = true + ns_broadcast + end + true + end + + def ns_initialize + @set = false + @iteration = 0 + end + end +end diff --git a/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic/java_count_down_latch.rb b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic/java_count_down_latch.rb new file mode 100644 index 0000000..cb5b35a --- /dev/null +++ b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic/java_count_down_latch.rb @@ -0,0 +1,42 @@ +if Concurrent.on_jruby? + + module Concurrent + + # @!macro count_down_latch + # @!visibility private + # @!macro internal_implementation_note + class JavaCountDownLatch + + # @!macro count_down_latch_method_initialize + def initialize(count = 1) + Utility::NativeInteger.ensure_integer_and_bounds(count) + Utility::NativeInteger.ensure_positive(count) + @latch = java.util.concurrent.CountDownLatch.new(count) + end + + # @!macro count_down_latch_method_wait + def wait(timeout = nil) + result = nil + if timeout.nil? + Synchronization::JRuby.sleep_interruptibly { @latch.await } + result = true + else + Synchronization::JRuby.sleep_interruptibly do + result = @latch.await(1000 * timeout, java.util.concurrent.TimeUnit::MILLISECONDS) + end + end + result + end + + # @!macro count_down_latch_method_count_down + def count_down + @latch.countDown + end + + # @!macro count_down_latch_method_count + def count + @latch.getCount + end + end + end +end diff --git a/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic/java_thread_local_var.rb b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic/java_thread_local_var.rb new file mode 100644 index 0000000..b41018f --- /dev/null +++ b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic/java_thread_local_var.rb @@ -0,0 +1,37 @@ +require 'concurrent/atomic/abstract_thread_local_var' + +if Concurrent.on_jruby? + + module Concurrent + + # @!visibility private + # @!macro internal_implementation_note + class JavaThreadLocalVar < AbstractThreadLocalVar + + # @!macro thread_local_var_method_get + def value + value = @var.get + + if value.nil? + default + elsif value == NULL + nil + else + value + end + end + + # @!macro thread_local_var_method_set + def value=(value) + @var.set(value) + end + + protected + + # @!visibility private + def allocate_storage + @var = java.lang.ThreadLocal.new + end + end + end +end diff --git a/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic/mutex_atomic_boolean.rb b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic/mutex_atomic_boolean.rb new file mode 100644 index 0000000..a033de4 --- /dev/null +++ b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic/mutex_atomic_boolean.rb @@ -0,0 +1,62 @@ +require 'concurrent/synchronization' + +module Concurrent + + # @!macro atomic_boolean + # @!visibility private + # @!macro internal_implementation_note + class MutexAtomicBoolean < Synchronization::LockableObject + + # @!macro atomic_boolean_method_initialize + def initialize(initial = false) + super() + synchronize { ns_initialize(initial) } + end + + # @!macro atomic_boolean_method_value_get + def value + synchronize { @value } + end + + # @!macro atomic_boolean_method_value_set + def value=(value) + synchronize { @value = !!value } + end + + # @!macro atomic_boolean_method_true_question + def true? + synchronize { @value } + end + + # @!macro atomic_boolean_method_false_question + def false? + synchronize { !@value } + end + + # @!macro atomic_boolean_method_make_true + def make_true + synchronize { ns_make_value(true) } + end + + # @!macro atomic_boolean_method_make_false + def make_false + synchronize { ns_make_value(false) } + end + + protected + + # @!visibility private + def ns_initialize(initial) + @value = !!initial + end + + private + + # @!visibility private + def ns_make_value(value) + old = @value + @value = value + old != @value + end + end +end diff --git a/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic/mutex_atomic_fixnum.rb b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic/mutex_atomic_fixnum.rb new file mode 100644 index 0000000..77b91d2 --- /dev/null +++ b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic/mutex_atomic_fixnum.rb @@ -0,0 +1,75 @@ +require 'concurrent/synchronization' +require 'concurrent/utility/native_integer' + +module Concurrent + + # @!macro atomic_fixnum + # @!visibility private + # @!macro internal_implementation_note + class MutexAtomicFixnum < Synchronization::LockableObject + + # @!macro atomic_fixnum_method_initialize + def initialize(initial = 0) + super() + synchronize { ns_initialize(initial) } + end + + # @!macro atomic_fixnum_method_value_get + def value + synchronize { @value } + end + + # @!macro atomic_fixnum_method_value_set + def value=(value) + synchronize { ns_set(value) } + end + + # @!macro atomic_fixnum_method_increment + def increment(delta = 1) + synchronize { ns_set(@value + delta.to_i) } + end + + alias_method :up, :increment + + # @!macro atomic_fixnum_method_decrement + def decrement(delta = 1) + synchronize { ns_set(@value - delta.to_i) } + end + + alias_method :down, :decrement + + # @!macro atomic_fixnum_method_compare_and_set + def compare_and_set(expect, update) + synchronize do + if @value == expect.to_i + @value = update.to_i + true + else + false + end + end + end + + # @!macro atomic_fixnum_method_update + def update + synchronize do + @value = yield @value + end + end + + protected + + # @!visibility private + def ns_initialize(initial) + ns_set(initial) + end + + private + + # @!visibility private + def ns_set(value) + Utility::NativeInteger.ensure_integer_and_bounds value + @value = value + end + end +end diff --git a/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic/mutex_count_down_latch.rb b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic/mutex_count_down_latch.rb new file mode 100644 index 0000000..e99744c --- /dev/null +++ b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic/mutex_count_down_latch.rb @@ -0,0 +1,44 @@ +require 'concurrent/synchronization' +require 'concurrent/utility/native_integer' + +module Concurrent + + # @!macro count_down_latch + # @!visibility private + # @!macro internal_implementation_note + class MutexCountDownLatch < Synchronization::LockableObject + + # @!macro count_down_latch_method_initialize + def initialize(count = 1) + Utility::NativeInteger.ensure_integer_and_bounds count + Utility::NativeInteger.ensure_positive count + + super() + synchronize { ns_initialize count } + end + + # @!macro count_down_latch_method_wait + def wait(timeout = nil) + synchronize { ns_wait_until(timeout) { @count == 0 } } + end + + # @!macro count_down_latch_method_count_down + def count_down + synchronize do + @count -= 1 if @count > 0 + ns_broadcast if @count == 0 + end + end + + # @!macro count_down_latch_method_count + def count + synchronize { @count } + end + + protected + + def ns_initialize(count) + @count = count + end + end +end diff --git a/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic/mutex_semaphore.rb b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic/mutex_semaphore.rb new file mode 100644 index 0000000..2042f73 --- /dev/null +++ b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic/mutex_semaphore.rb @@ -0,0 +1,115 @@ +require 'concurrent/synchronization' +require 'concurrent/utility/native_integer' + +module Concurrent + + # @!macro semaphore + # @!visibility private + # @!macro internal_implementation_note + class MutexSemaphore < Synchronization::LockableObject + + # @!macro semaphore_method_initialize + def initialize(count) + Utility::NativeInteger.ensure_integer_and_bounds count + + super() + synchronize { ns_initialize count } + end + + # @!macro semaphore_method_acquire + def acquire(permits = 1) + Utility::NativeInteger.ensure_integer_and_bounds permits + Utility::NativeInteger.ensure_positive permits + + synchronize do + try_acquire_timed(permits, nil) + nil + end + end + + # @!macro semaphore_method_available_permits + def available_permits + synchronize { @free } + end + + # @!macro semaphore_method_drain_permits + # + # Acquires and returns all permits that are immediately available. + # + # @return [Integer] + def drain_permits + synchronize do + @free.tap { |_| @free = 0 } + end + end + + # @!macro semaphore_method_try_acquire + def try_acquire(permits = 1, timeout = nil) + Utility::NativeInteger.ensure_integer_and_bounds permits + Utility::NativeInteger.ensure_positive permits + + synchronize do + if timeout.nil? + try_acquire_now(permits) + else + try_acquire_timed(permits, timeout) + end + end + end + + # @!macro semaphore_method_release + def release(permits = 1) + Utility::NativeInteger.ensure_integer_and_bounds permits + Utility::NativeInteger.ensure_positive permits + + synchronize do + @free += permits + permits.times { ns_signal } + end + nil + end + + # Shrinks the number of available permits by the indicated reduction. + # + # @param [Fixnum] reduction Number of permits to remove. + # + # @raise [ArgumentError] if `reduction` is not an integer or is negative + # + # @raise [ArgumentError] if `@free` - `@reduction` is less than zero + # + # @return [nil] + # + # @!visibility private + def reduce_permits(reduction) + Utility::NativeInteger.ensure_integer_and_bounds reduction + Utility::NativeInteger.ensure_positive reduction + + synchronize { @free -= reduction } + nil + end + + protected + + # @!visibility private + def ns_initialize(count) + @free = count + end + + private + + # @!visibility private + def try_acquire_now(permits) + if @free >= permits + @free -= permits + true + else + false + end + end + + # @!visibility private + def try_acquire_timed(permits, timeout) + ns_wait_until(timeout) { try_acquire_now(permits) } + end + end +end diff --git a/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic/read_write_lock.rb b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic/read_write_lock.rb new file mode 100644 index 0000000..246f21a --- /dev/null +++ b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic/read_write_lock.rb @@ -0,0 +1,254 @@ +require 'thread' +require 'concurrent/atomic/atomic_fixnum' +require 'concurrent/errors' +require 'concurrent/synchronization' + +module Concurrent + + # Ruby read-write lock implementation + # + # Allows any number of concurrent readers, but only one concurrent writer + # (And if the "write" lock is taken, any readers who come along will have to wait) + # + # If readers are already active when a writer comes along, the writer will wait for + # all the readers to finish before going ahead. + # Any additional readers that come when the writer is already waiting, will also + # wait (so writers are not starved). + # + # This implementation is based on `java.util.concurrent.ReentrantReadWriteLock`. + # + # @example + # lock = Concurrent::ReadWriteLock.new + # lock.with_read_lock { data.retrieve } + # lock.with_write_lock { data.modify! } + # + # @note Do **not** try to acquire the write lock while already holding a read lock + # **or** try to acquire the write lock while you already have it. + # This will lead to deadlock + # + # @see http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/locks/ReentrantReadWriteLock.html java.util.concurrent.ReentrantReadWriteLock + class ReadWriteLock < Synchronization::Object + + # @!visibility private + WAITING_WRITER = 1 << 15 + + # @!visibility private + RUNNING_WRITER = 1 << 29 + + # @!visibility private + MAX_READERS = WAITING_WRITER - 1 + + # @!visibility private + MAX_WRITERS = RUNNING_WRITER - MAX_READERS - 1 + + safe_initialization! + + # Implementation notes: + # A goal is to make the uncontended path for both readers/writers lock-free + # Only if there is reader-writer or writer-writer contention, should locks be used + # Internal state is represented by a single integer ("counter"), and updated + # using atomic compare-and-swap operations + # When the counter is 0, the lock is free + # Each reader increments the counter by 1 when acquiring a read lock + # (and decrements by 1 when releasing the read lock) + # The counter is increased by (1 << 15) for each writer waiting to acquire the + # write lock, and by (1 << 29) if the write lock is taken + + # Create a new `ReadWriteLock` in the unlocked state. + def initialize + super() + @Counter = AtomicFixnum.new(0) # single integer which represents lock state + @ReadLock = Synchronization::Lock.new + @WriteLock = Synchronization::Lock.new + end + + # Execute a block operation within a read lock. + # + # @yield the task to be performed within the lock. + # + # @return [Object] the result of the block operation. + # + # @raise [ArgumentError] when no block is given. + # @raise [Concurrent::ResourceLimitError] if the maximum number of readers + # is exceeded. + def with_read_lock + raise ArgumentError.new('no block given') unless block_given? + acquire_read_lock + begin + yield + ensure + release_read_lock + end + end + + # Execute a block operation within a write lock. + # + # @yield the task to be performed within the lock. + # + # @return [Object] the result of the block operation. + # + # @raise [ArgumentError] when no block is given. + # @raise [Concurrent::ResourceLimitError] if the maximum number of readers + # is exceeded. + def with_write_lock + raise ArgumentError.new('no block given') unless block_given? + acquire_write_lock + begin + yield + ensure + release_write_lock + end + end + + # Acquire a read lock. If a write lock has been acquired will block until + # it is released. Will not block if other read locks have been acquired. + # + # @return [Boolean] true if the lock is successfully acquired + # + # @raise [Concurrent::ResourceLimitError] if the maximum number of readers + # is exceeded. + def acquire_read_lock + while true + c = @Counter.value + raise ResourceLimitError.new('Too many reader threads') if max_readers?(c) + + # If a writer is waiting when we first queue up, we need to wait + if waiting_writer?(c) + @ReadLock.wait_until { !waiting_writer? } + + # after a reader has waited once, they are allowed to "barge" ahead of waiting writers + # but if a writer is *running*, the reader still needs to wait (naturally) + while true + c = @Counter.value + if running_writer?(c) + @ReadLock.wait_until { !running_writer? } + else + return if @Counter.compare_and_set(c, c+1) + end + end + else + break if @Counter.compare_and_set(c, c+1) + end + end + true + end + + # Release a previously acquired read lock. + # + # @return [Boolean] true if the lock is successfully released + def release_read_lock + while true + c = @Counter.value + if @Counter.compare_and_set(c, c-1) + # If one or more writers were waiting, and we were the last reader, wake a writer up + if waiting_writer?(c) && running_readers(c) == 1 + @WriteLock.signal + end + break + end + end + true + end + + # Acquire a write lock. Will block and wait for all active readers and writers. + # + # @return [Boolean] true if the lock is successfully acquired + # + # @raise [Concurrent::ResourceLimitError] if the maximum number of writers + # is exceeded. + def acquire_write_lock + while true + c = @Counter.value + raise ResourceLimitError.new('Too many writer threads') if max_writers?(c) + + if c == 0 # no readers OR writers running + # if we successfully swap the RUNNING_WRITER bit on, then we can go ahead + break if @Counter.compare_and_set(0, RUNNING_WRITER) + elsif @Counter.compare_and_set(c, c+WAITING_WRITER) + while true + # Now we have successfully incremented, so no more readers will be able to increment + # (they will wait instead) + # However, readers OR writers could decrement right here, OR another writer could increment + @WriteLock.wait_until do + # So we have to do another check inside the synchronized section + # If a writer OR reader is running, then go to sleep + c = @Counter.value + !running_writer?(c) && !running_readers?(c) + end + + # We just came out of a wait + # If we successfully turn the RUNNING_WRITER bit on with an atomic swap, + # Then we are OK to stop waiting and go ahead + # Otherwise go back and wait again + c = @Counter.value + break if !running_writer?(c) && !running_readers?(c) && @Counter.compare_and_set(c, c+RUNNING_WRITER-WAITING_WRITER) + end + break + end + end + true + end + + # Release a previously acquired write lock. + # + # @return [Boolean] true if the lock is successfully released + def release_write_lock + return true unless running_writer? + c = @Counter.update { |counter| counter - RUNNING_WRITER } + @ReadLock.broadcast + @WriteLock.signal if waiting_writers(c) > 0 + true + end + + # Queries if the write lock is held by any thread. + # + # @return [Boolean] true if the write lock is held else false` + def write_locked? + @Counter.value >= RUNNING_WRITER + end + + # Queries whether any threads are waiting to acquire the read or write lock. + # + # @return [Boolean] true if any threads are waiting for a lock else false + def has_waiters? + waiting_writer?(@Counter.value) + end + + private + + # @!visibility private + def running_readers(c = @Counter.value) + c & MAX_READERS + end + + # @!visibility private + def running_readers?(c = @Counter.value) + (c & MAX_READERS) > 0 + end + + # @!visibility private + def running_writer?(c = @Counter.value) + c >= RUNNING_WRITER + end + + # @!visibility private + def waiting_writers(c = @Counter.value) + (c & MAX_WRITERS) / WAITING_WRITER + end + + # @!visibility private + def waiting_writer?(c = @Counter.value) + c >= WAITING_WRITER + end + + # @!visibility private + def max_readers?(c = @Counter.value) + (c & MAX_READERS) == MAX_READERS + end + + # @!visibility private + def max_writers?(c = @Counter.value) + (c & MAX_WRITERS) == MAX_WRITERS + end + end +end diff --git a/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic/reentrant_read_write_lock.rb b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic/reentrant_read_write_lock.rb new file mode 100644 index 0000000..42d7f3c --- /dev/null +++ b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic/reentrant_read_write_lock.rb @@ -0,0 +1,379 @@ +require 'thread' +require 'concurrent/atomic/atomic_reference' +require 'concurrent/errors' +require 'concurrent/synchronization' +require 'concurrent/atomic/thread_local_var' + +module Concurrent + + # Re-entrant read-write lock implementation + # + # Allows any number of concurrent readers, but only one concurrent writer + # (And while the "write" lock is taken, no read locks can be obtained either. + # Hence, the write lock can also be called an "exclusive" lock.) + # + # If another thread has taken a read lock, any thread which wants a write lock + # will block until all the readers release their locks. However, once a thread + # starts waiting to obtain a write lock, any additional readers that come along + # will also wait (so writers are not starved). + # + # A thread can acquire both a read and write lock at the same time. A thread can + # also acquire a read lock OR a write lock more than once. Only when the read (or + # write) lock is released as many times as it was acquired, will the thread + # actually let it go, allowing other threads which might have been waiting + # to proceed. Therefore the lock can be upgraded by first acquiring + # read lock and then write lock and that the lock can be downgraded by first + # having both read and write lock a releasing just the write lock. + # + # If both read and write locks are acquired by the same thread, it is not strictly + # necessary to release them in the same order they were acquired. In other words, + # the following code is legal: + # + # @example + # lock = Concurrent::ReentrantReadWriteLock.new + # lock.acquire_write_lock + # lock.acquire_read_lock + # lock.release_write_lock + # # At this point, the current thread is holding only a read lock, not a write + # # lock. So other threads can take read locks, but not a write lock. + # lock.release_read_lock + # # Now the current thread is not holding either a read or write lock, so + # # another thread could potentially acquire a write lock. + # + # This implementation was inspired by `java.util.concurrent.ReentrantReadWriteLock`. + # + # @example + # lock = Concurrent::ReentrantReadWriteLock.new + # lock.with_read_lock { data.retrieve } + # lock.with_write_lock { data.modify! } + # + # @see http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/locks/ReentrantReadWriteLock.html java.util.concurrent.ReentrantReadWriteLock + class ReentrantReadWriteLock < Synchronization::Object + + # Implementation notes: + # + # A goal is to make the uncontended path for both readers/writers mutex-free + # Only if there is reader-writer or writer-writer contention, should mutexes be used + # Otherwise, a single CAS operation is all we need to acquire/release a lock + # + # Internal state is represented by a single integer ("counter"), and updated + # using atomic compare-and-swap operations + # When the counter is 0, the lock is free + # Each thread which has one OR MORE read locks increments the counter by 1 + # (and decrements by 1 when releasing the read lock) + # The counter is increased by (1 << 15) for each writer waiting to acquire the + # write lock, and by (1 << 29) if the write lock is taken + # + # Additionally, each thread uses a thread-local variable to count how many times + # it has acquired a read lock, AND how many times it has acquired a write lock. + # It uses a similar trick; an increment of 1 means a read lock was taken, and + # an increment of (1 << 15) means a write lock was taken + # This is what makes re-entrancy possible + # + # 2 rules are followed to ensure good liveness properties: + # 1) Once a writer has queued up and is waiting for a write lock, no other thread + # can take a lock without waiting + # 2) When a write lock is released, readers are given the "first chance" to wake + # up and acquire a read lock + # Following these rules means readers and writers tend to "take turns", so neither + # can starve the other, even under heavy contention + + # @!visibility private + READER_BITS = 15 + # @!visibility private + WRITER_BITS = 14 + + # Used with @Counter: + # @!visibility private + WAITING_WRITER = 1 << READER_BITS + # @!visibility private + RUNNING_WRITER = 1 << (READER_BITS + WRITER_BITS) + # @!visibility private + MAX_READERS = WAITING_WRITER - 1 + # @!visibility private + MAX_WRITERS = RUNNING_WRITER - MAX_READERS - 1 + + # Used with @HeldCount: + # @!visibility private + WRITE_LOCK_HELD = 1 << READER_BITS + # @!visibility private + READ_LOCK_MASK = WRITE_LOCK_HELD - 1 + # @!visibility private + WRITE_LOCK_MASK = MAX_WRITERS + + safe_initialization! + + # Create a new `ReentrantReadWriteLock` in the unlocked state. + def initialize + super() + @Counter = AtomicFixnum.new(0) # single integer which represents lock state + @ReadQueue = Synchronization::Lock.new # used to queue waiting readers + @WriteQueue = Synchronization::Lock.new # used to queue waiting writers + @HeldCount = ThreadLocalVar.new(0) # indicates # of R & W locks held by this thread + end + + # Execute a block operation within a read lock. + # + # @yield the task to be performed within the lock. + # + # @return [Object] the result of the block operation. + # + # @raise [ArgumentError] when no block is given. + # @raise [Concurrent::ResourceLimitError] if the maximum number of readers + # is exceeded. + def with_read_lock + raise ArgumentError.new('no block given') unless block_given? + acquire_read_lock + begin + yield + ensure + release_read_lock + end + end + + # Execute a block operation within a write lock. + # + # @yield the task to be performed within the lock. + # + # @return [Object] the result of the block operation. + # + # @raise [ArgumentError] when no block is given. + # @raise [Concurrent::ResourceLimitError] if the maximum number of readers + # is exceeded. + def with_write_lock + raise ArgumentError.new('no block given') unless block_given? + acquire_write_lock + begin + yield + ensure + release_write_lock + end + end + + # Acquire a read lock. If a write lock is held by another thread, will block + # until it is released. + # + # @return [Boolean] true if the lock is successfully acquired + # + # @raise [Concurrent::ResourceLimitError] if the maximum number of readers + # is exceeded. + def acquire_read_lock + if (held = @HeldCount.value) > 0 + # If we already have a lock, there's no need to wait + if held & READ_LOCK_MASK == 0 + # But we do need to update the counter, if we were holding a write + # lock but not a read lock + @Counter.update { |c| c + 1 } + end + @HeldCount.value = held + 1 + return true + end + + while true + c = @Counter.value + raise ResourceLimitError.new('Too many reader threads') if max_readers?(c) + + # If a writer is waiting OR running when we first queue up, we need to wait + if waiting_or_running_writer?(c) + # Before going to sleep, check again with the ReadQueue mutex held + @ReadQueue.synchronize do + @ReadQueue.ns_wait if waiting_or_running_writer? + end + # Note: the above 'synchronize' block could have used #wait_until, + # but that waits repeatedly in a loop, checking the wait condition + # each time it wakes up (to protect against spurious wakeups) + # But we are already in a loop, which is only broken when we successfully + # acquire the lock! So we don't care about spurious wakeups, and would + # rather not pay the extra overhead of using #wait_until + + # After a reader has waited once, they are allowed to "barge" ahead of waiting writers + # But if a writer is *running*, the reader still needs to wait (naturally) + while true + c = @Counter.value + if running_writer?(c) + @ReadQueue.synchronize do + @ReadQueue.ns_wait if running_writer? + end + elsif @Counter.compare_and_set(c, c+1) + @HeldCount.value = held + 1 + return true + end + end + elsif @Counter.compare_and_set(c, c+1) + @HeldCount.value = held + 1 + return true + end + end + end + + # Try to acquire a read lock and return true if we succeed. If it cannot be + # acquired immediately, return false. + # + # @return [Boolean] true if the lock is successfully acquired + def try_read_lock + if (held = @HeldCount.value) > 0 + if held & READ_LOCK_MASK == 0 + # If we hold a write lock, but not a read lock... + @Counter.update { |c| c + 1 } + end + @HeldCount.value = held + 1 + return true + else + c = @Counter.value + if !waiting_or_running_writer?(c) && @Counter.compare_and_set(c, c+1) + @HeldCount.value = held + 1 + return true + end + end + false + end + + # Release a previously acquired read lock. + # + # @return [Boolean] true if the lock is successfully released + def release_read_lock + held = @HeldCount.value = @HeldCount.value - 1 + rlocks_held = held & READ_LOCK_MASK + if rlocks_held == 0 + c = @Counter.update { |counter| counter - 1 } + # If one or more writers were waiting, and we were the last reader, wake a writer up + if waiting_or_running_writer?(c) && running_readers(c) == 0 + @WriteQueue.signal + end + elsif rlocks_held == READ_LOCK_MASK + raise IllegalOperationError, "Cannot release a read lock which is not held" + end + true + end + + # Acquire a write lock. Will block and wait for all active readers and writers. + # + # @return [Boolean] true if the lock is successfully acquired + # + # @raise [Concurrent::ResourceLimitError] if the maximum number of writers + # is exceeded. + def acquire_write_lock + if (held = @HeldCount.value) >= WRITE_LOCK_HELD + # if we already have a write (exclusive) lock, there's no need to wait + @HeldCount.value = held + WRITE_LOCK_HELD + return true + end + + while true + c = @Counter.value + raise ResourceLimitError.new('Too many writer threads') if max_writers?(c) + + # To go ahead and take the lock without waiting, there must be no writer + # running right now, AND no writers who came before us still waiting to + # acquire the lock + # Additionally, if any read locks have been taken, we must hold all of them + if c == held + # If we successfully swap the RUNNING_WRITER bit on, then we can go ahead + if @Counter.compare_and_set(c, c+RUNNING_WRITER) + @HeldCount.value = held + WRITE_LOCK_HELD + return true + end + elsif @Counter.compare_and_set(c, c+WAITING_WRITER) + while true + # Now we have successfully incremented, so no more readers will be able to increment + # (they will wait instead) + # However, readers OR writers could decrement right here + @WriteQueue.synchronize do + # So we have to do another check inside the synchronized section + # If a writer OR another reader is running, then go to sleep + c = @Counter.value + @WriteQueue.ns_wait if running_writer?(c) || running_readers(c) != held + end + # Note: if you are thinking of replacing the above 'synchronize' block + # with #wait_until, read the comment in #acquire_read_lock first! + + # We just came out of a wait + # If we successfully turn the RUNNING_WRITER bit on with an atomic swap, + # then we are OK to stop waiting and go ahead + # Otherwise go back and wait again + c = @Counter.value + if !running_writer?(c) && + running_readers(c) == held && + @Counter.compare_and_set(c, c+RUNNING_WRITER-WAITING_WRITER) + @HeldCount.value = held + WRITE_LOCK_HELD + return true + end + end + end + end + end + + # Try to acquire a write lock and return true if we succeed. If it cannot be + # acquired immediately, return false. + # + # @return [Boolean] true if the lock is successfully acquired + def try_write_lock + if (held = @HeldCount.value) >= WRITE_LOCK_HELD + @HeldCount.value = held + WRITE_LOCK_HELD + return true + else + c = @Counter.value + if !waiting_or_running_writer?(c) && + running_readers(c) == held && + @Counter.compare_and_set(c, c+RUNNING_WRITER) + @HeldCount.value = held + WRITE_LOCK_HELD + return true + end + end + false + end + + # Release a previously acquired write lock. + # + # @return [Boolean] true if the lock is successfully released + def release_write_lock + held = @HeldCount.value = @HeldCount.value - WRITE_LOCK_HELD + wlocks_held = held & WRITE_LOCK_MASK + if wlocks_held == 0 + c = @Counter.update { |counter| counter - RUNNING_WRITER } + @ReadQueue.broadcast + @WriteQueue.signal if waiting_writers(c) > 0 + elsif wlocks_held == WRITE_LOCK_MASK + raise IllegalOperationError, "Cannot release a write lock which is not held" + end + true + end + + private + + # @!visibility private + def running_readers(c = @Counter.value) + c & MAX_READERS + end + + # @!visibility private + def running_readers?(c = @Counter.value) + (c & MAX_READERS) > 0 + end + + # @!visibility private + def running_writer?(c = @Counter.value) + c >= RUNNING_WRITER + end + + # @!visibility private + def waiting_writers(c = @Counter.value) + (c & MAX_WRITERS) >> READER_BITS + end + + # @!visibility private + def waiting_or_running_writer?(c = @Counter.value) + c >= WAITING_WRITER + end + + # @!visibility private + def max_readers?(c = @Counter.value) + (c & MAX_READERS) == MAX_READERS + end + + # @!visibility private + def max_writers?(c = @Counter.value) + (c & MAX_WRITERS) == MAX_WRITERS + end + end +end diff --git a/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic/ruby_thread_local_var.rb b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic/ruby_thread_local_var.rb new file mode 100644 index 0000000..06afae7 --- /dev/null +++ b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic/ruby_thread_local_var.rb @@ -0,0 +1,161 @@ +require 'thread' +require 'concurrent/atomic/abstract_thread_local_var' + +module Concurrent + + # @!visibility private + # @!macro internal_implementation_note + class RubyThreadLocalVar < AbstractThreadLocalVar + + # Each thread has a (lazily initialized) array of thread-local variable values + # Each time a new thread-local var is created, we allocate an "index" for it + # For example, if the allocated index is 1, that means slot #1 in EVERY + # thread's thread-local array will be used for the value of that TLV + # + # The good thing about using a per-THREAD structure to hold values, rather + # than a per-TLV structure, is that no synchronization is needed when + # reading and writing those values (since the structure is only ever + # accessed by a single thread) + # + # Of course, when a TLV is GC'd, 1) we need to recover its index for use + # by other new TLVs (otherwise the thread-local arrays could get bigger + # and bigger with time), and 2) we need to null out all the references + # held in the now-unused slots (both to avoid blocking GC of those objects, + # and also to prevent "stale" values from being passed on to a new TLV + # when the index is reused) + # Because we need to null out freed slots, we need to keep references to + # ALL the thread-local arrays -- ARRAYS is for that + # But when a Thread is GC'd, we need to drop the reference to its thread-local + # array, so we don't leak memory + + # @!visibility private + FREE = [] + LOCK = Mutex.new + ARRAYS = {} # used as a hash set + @@next = 0 + private_constant :FREE, :LOCK, :ARRAYS + + # @!macro thread_local_var_method_get + def value + if array = get_threadlocal_array + value = array[@index] + if value.nil? + default + elsif value.equal?(NULL) + nil + else + value + end + else + default + end + end + + # @!macro thread_local_var_method_set + def value=(value) + me = Thread.current + # We could keep the thread-local arrays in a hash, keyed by Thread + # But why? That would require locking + # Using Ruby's built-in thread-local storage is faster + unless array = get_threadlocal_array(me) + array = set_threadlocal_array([], me) + LOCK.synchronize { ARRAYS[array.object_id] = array } + ObjectSpace.define_finalizer(me, self.class.thread_finalizer(array)) + end + array[@index] = (value.nil? ? NULL : value) + value + end + + protected + + # @!visibility private + def allocate_storage + @index = LOCK.synchronize do + FREE.pop || begin + result = @@next + @@next += 1 + result + end + end + ObjectSpace.define_finalizer(self, self.class.threadlocal_finalizer(@index)) + end + + # @!visibility private + def self.threadlocal_finalizer(index) + proc do + Thread.new do # avoid error: can't be called from trap context + LOCK.synchronize do + FREE.push(index) + # The cost of GC'ing a TLV is linear in the number of threads using TLVs + # But that is natural! More threads means more storage is used per TLV + # So naturally more CPU time is required to free more storage + ARRAYS.each_value do |array| + array[index] = nil + end + end + end + end + end + + # @!visibility private + def self.thread_finalizer(array) + proc do + Thread.new do # avoid error: can't be called from trap context + LOCK.synchronize do + # The thread which used this thread-local array is now gone + # So don't hold onto a reference to the array (thus blocking GC) + ARRAYS.delete(array.object_id) + end + end + end + end + + private + + if Thread.instance_methods.include?(:thread_variable_get) + + def get_threadlocal_array(thread = Thread.current) + thread.thread_variable_get(:__threadlocal_array__) + end + + def set_threadlocal_array(array, thread = Thread.current) + thread.thread_variable_set(:__threadlocal_array__, array) + end + + else + + def get_threadlocal_array(thread = Thread.current) + thread[:__threadlocal_array__] + end + + def set_threadlocal_array(array, thread = Thread.current) + thread[:__threadlocal_array__] = array + end + end + + # This exists only for use in testing + # @!visibility private + def value_for(thread) + if array = get_threadlocal_array(thread) + value = array[@index] + if value.nil? + default_for(thread) + elsif value.equal?(NULL) + nil + else + value + end + else + default_for(thread) + end + end + + def default_for(thread) + if @default_block + raise "Cannot use default_for with default block" + else + @default + end + end + end +end diff --git a/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic/semaphore.rb b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic/semaphore.rb new file mode 100644 index 0000000..1b2bd8c --- /dev/null +++ b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic/semaphore.rb @@ -0,0 +1,145 @@ +require 'concurrent/atomic/mutex_semaphore' +require 'concurrent/synchronization' + +module Concurrent + + ################################################################### + + # @!macro semaphore_method_initialize + # + # Create a new `Semaphore` with the initial `count`. + # + # @param [Fixnum] count the initial count + # + # @raise [ArgumentError] if `count` is not an integer or is less than zero + + # @!macro semaphore_method_acquire + # + # Acquires the given number of permits from this semaphore, + # blocking until all are available. + # + # @param [Fixnum] permits Number of permits to acquire + # + # @raise [ArgumentError] if `permits` is not an integer or is less than + # one + # + # @return [nil] + + # @!macro semaphore_method_available_permits + # + # Returns the current number of permits available in this semaphore. + # + # @return [Integer] + + # @!macro semaphore_method_drain_permits + # + # Acquires and returns all permits that are immediately available. + # + # @return [Integer] + + # @!macro semaphore_method_try_acquire + # + # Acquires the given number of permits from this semaphore, + # only if all are available at the time of invocation or within + # `timeout` interval + # + # @param [Fixnum] permits the number of permits to acquire + # + # @param [Fixnum] timeout the number of seconds to wait for the counter + # or `nil` to return immediately + # + # @raise [ArgumentError] if `permits` is not an integer or is less than + # one + # + # @return [Boolean] `false` if no permits are available, `true` when + # acquired a permit + + # @!macro semaphore_method_release + # + # Releases the given number of permits, returning them to the semaphore. + # + # @param [Fixnum] permits Number of permits to return to the semaphore. + # + # @raise [ArgumentError] if `permits` is not a number or is less than one + # + # @return [nil] + + ################################################################### + + # @!macro semaphore_public_api + # + # @!method initialize(count) + # @!macro semaphore_method_initialize + # + # @!method acquire(permits = 1) + # @!macro semaphore_method_acquire + # + # @!method available_permits + # @!macro semaphore_method_available_permits + # + # @!method drain_permits + # @!macro semaphore_method_drain_permits + # + # @!method try_acquire(permits = 1, timeout = nil) + # @!macro semaphore_method_try_acquire + # + # @!method release(permits = 1) + # @!macro semaphore_method_release + + ################################################################### + + # @!visibility private + # @!macro internal_implementation_note + SemaphoreImplementation = case + when defined?(JavaSemaphore) + JavaSemaphore + else + MutexSemaphore + end + private_constant :SemaphoreImplementation + + # @!macro semaphore + # + # A counting semaphore. Conceptually, a semaphore maintains a set of + # permits. Each {#acquire} blocks if necessary until a permit is + # available, and then takes it. Each {#release} adds a permit, potentially + # releasing a blocking acquirer. + # However, no actual permit objects are used; the Semaphore just keeps a + # count of the number available and acts accordingly. + # + # @!macro semaphore_public_api + # @example + # semaphore = Concurrent::Semaphore.new(2) + # + # t1 = Thread.new do + # semaphore.acquire + # puts "Thread 1 acquired semaphore" + # end + # + # t2 = Thread.new do + # semaphore.acquire + # puts "Thread 2 acquired semaphore" + # end + # + # t3 = Thread.new do + # semaphore.acquire + # puts "Thread 3 acquired semaphore" + # end + # + # t4 = Thread.new do + # sleep(2) + # puts "Thread 4 releasing semaphore" + # semaphore.release + # end + # + # [t1, t2, t3, t4].each(&:join) + # + # # prints: + # # Thread 3 acquired semaphore + # # Thread 2 acquired semaphore + # # Thread 4 releasing semaphore + # # Thread 1 acquired semaphore + # + class Semaphore < SemaphoreImplementation + end +end diff --git a/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic/thread_local_var.rb b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic/thread_local_var.rb new file mode 100644 index 0000000..9f09e4c --- /dev/null +++ b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic/thread_local_var.rb @@ -0,0 +1,104 @@ +require 'concurrent/atomic/ruby_thread_local_var' +require 'concurrent/atomic/java_thread_local_var' +require 'concurrent/utility/engine' + +module Concurrent + + ################################################################### + + # @!macro thread_local_var_method_initialize + # + # Creates a thread local variable. + # + # @param [Object] default the default value when otherwise unset + # @param [Proc] default_block Optional block that gets called to obtain the + # default value for each thread + + # @!macro thread_local_var_method_get + # + # Returns the value in the current thread's copy of this thread-local variable. + # + # @return [Object] the current value + + # @!macro thread_local_var_method_set + # + # Sets the current thread's copy of this thread-local variable to the specified value. + # + # @param [Object] value the value to set + # @return [Object] the new value + + # @!macro thread_local_var_method_bind + # + # Bind the given value to thread local storage during + # execution of the given block. + # + # @param [Object] value the value to bind + # @yield the operation to be performed with the bound variable + # @return [Object] the value + + + ################################################################### + + # @!macro thread_local_var_public_api + # + # @!method initialize(default = nil, &default_block) + # @!macro thread_local_var_method_initialize + # + # @!method value + # @!macro thread_local_var_method_get + # + # @!method value=(value) + # @!macro thread_local_var_method_set + # + # @!method bind(value, &block) + # @!macro thread_local_var_method_bind + + ################################################################### + + # @!visibility private + # @!macro internal_implementation_note + ThreadLocalVarImplementation = case + when Concurrent.on_jruby? + JavaThreadLocalVar + else + RubyThreadLocalVar + end + private_constant :ThreadLocalVarImplementation + + # @!macro thread_local_var + # + # A `ThreadLocalVar` is a variable where the value is different for each thread. + # Each variable may have a default value, but when you modify the variable only + # the current thread will ever see that change. + # + # @!macro thread_safe_variable_comparison + # + # @example + # v = ThreadLocalVar.new(14) + # v.value #=> 14 + # v.value = 2 + # v.value #=> 2 + # + # @example + # v = ThreadLocalVar.new(14) + # + # t1 = Thread.new do + # v.value #=> 14 + # v.value = 1 + # v.value #=> 1 + # end + # + # t2 = Thread.new do + # v.value #=> 14 + # v.value = 2 + # v.value #=> 2 + # end + # + # v.value #=> 14 + # + # @see https://docs.oracle.com/javase/7/docs/api/java/lang/ThreadLocal.html Java ThreadLocal + # + # @!macro thread_local_var_public_api + class ThreadLocalVar < ThreadLocalVarImplementation + end +end diff --git a/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic_reference/mutex_atomic.rb b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic_reference/mutex_atomic.rb new file mode 100644 index 0000000..d092aed --- /dev/null +++ b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic_reference/mutex_atomic.rb @@ -0,0 +1,56 @@ +module Concurrent + + # @!visibility private + # @!macro internal_implementation_note + class MutexAtomicReference < Synchronization::LockableObject + include AtomicDirectUpdate + include AtomicNumericCompareAndSetWrapper + alias_method :compare_and_swap, :compare_and_set + + # @!macro atomic_reference_method_initialize + def initialize(value = nil) + super() + synchronize { ns_initialize(value) } + end + + # @!macro atomic_reference_method_get + def get + synchronize { @value } + end + alias_method :value, :get + + # @!macro atomic_reference_method_set + def set(new_value) + synchronize { @value = new_value } + end + alias_method :value=, :set + + # @!macro atomic_reference_method_get_and_set + def get_and_set(new_value) + synchronize do + old_value = @value + @value = new_value + old_value + end + end + alias_method :swap, :get_and_set + + # @!macro atomic_reference_method_compare_and_set + def _compare_and_set(old_value, new_value) + synchronize do + if @value.equal? old_value + @value = new_value + true + else + false + end + end + end + + protected + + def ns_initialize(value) + @value = value + end + end +end diff --git a/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic_reference/numeric_cas_wrapper.rb b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic_reference/numeric_cas_wrapper.rb new file mode 100644 index 0000000..709a382 --- /dev/null +++ b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic_reference/numeric_cas_wrapper.rb @@ -0,0 +1,28 @@ +module Concurrent + + # Special "compare and set" handling of numeric values. + # + # @!visibility private + # @!macro internal_implementation_note + module AtomicNumericCompareAndSetWrapper + + # @!macro atomic_reference_method_compare_and_set + def compare_and_set(old_value, new_value) + if old_value.kind_of? Numeric + while true + old = get + + return false unless old.kind_of? Numeric + + return false unless old == old_value + + result = _compare_and_set(old, new_value) + return result if result + end + else + _compare_and_set(old_value, new_value) + end + end + + end +end diff --git a/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/atomics.rb b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/atomics.rb new file mode 100644 index 0000000..16cbe66 --- /dev/null +++ b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/atomics.rb @@ -0,0 +1,10 @@ +require 'concurrent/atomic/atomic_reference' +require 'concurrent/atomic/atomic_boolean' +require 'concurrent/atomic/atomic_fixnum' +require 'concurrent/atomic/cyclic_barrier' +require 'concurrent/atomic/count_down_latch' +require 'concurrent/atomic/event' +require 'concurrent/atomic/read_write_lock' +require 'concurrent/atomic/reentrant_read_write_lock' +require 'concurrent/atomic/semaphore' +require 'concurrent/atomic/thread_local_var' diff --git a/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/collection/copy_on_notify_observer_set.rb b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/collection/copy_on_notify_observer_set.rb new file mode 100644 index 0000000..50d52a6 --- /dev/null +++ b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/collection/copy_on_notify_observer_set.rb @@ -0,0 +1,107 @@ +require 'concurrent/synchronization' + +module Concurrent + module Collection + + # A thread safe observer set implemented using copy-on-read approach: + # observers are added and removed from a thread safe collection; every time + # a notification is required the internal data structure is copied to + # prevent concurrency issues + # + # @api private + class CopyOnNotifyObserverSet < Synchronization::LockableObject + + def initialize + super() + synchronize { ns_initialize } + end + + # @!macro observable_add_observer + def add_observer(observer = nil, func = :update, &block) + if observer.nil? && block.nil? + raise ArgumentError, 'should pass observer as a first argument or block' + elsif observer && block + raise ArgumentError.new('cannot provide both an observer and a block') + end + + if block + observer = block + func = :call + end + + synchronize do + @observers[observer] = func + observer + end + end + + # @!macro observable_delete_observer + def delete_observer(observer) + synchronize do + @observers.delete(observer) + observer + end + end + + # @!macro observable_delete_observers + def delete_observers + synchronize do + @observers.clear + self + end + end + + # @!macro observable_count_observers + def count_observers + synchronize { @observers.count } + end + + # Notifies all registered observers with optional args + # @param [Object] args arguments to be passed to each observer + # @return [CopyOnWriteObserverSet] self + def notify_observers(*args, &block) + observers = duplicate_observers + notify_to(observers, *args, &block) + self + end + + # Notifies all registered observers with optional args and deletes them. + # + # @param [Object] args arguments to be passed to each observer + # @return [CopyOnWriteObserverSet] self + def notify_and_delete_observers(*args, &block) + observers = duplicate_and_clear_observers + notify_to(observers, *args, &block) + self + end + + protected + + def ns_initialize + @observers = {} + end + + private + + def duplicate_and_clear_observers + synchronize do + observers = @observers.dup + @observers.clear + observers + end + end + + def duplicate_observers + synchronize { @observers.dup } + end + + def notify_to(observers, *args) + raise ArgumentError.new('cannot give arguments and a block') if block_given? && !args.empty? + observers.each do |observer, function| + args = yield if block_given? + observer.send(function, *args) + end + end + end + end +end diff --git a/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/collection/copy_on_write_observer_set.rb b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/collection/copy_on_write_observer_set.rb new file mode 100644 index 0000000..3f3f7cc --- /dev/null +++ b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/collection/copy_on_write_observer_set.rb @@ -0,0 +1,111 @@ +require 'concurrent/synchronization' + +module Concurrent + module Collection + + # A thread safe observer set implemented using copy-on-write approach: + # every time an observer is added or removed the whole internal data structure is + # duplicated and replaced with a new one. + # + # @api private + class CopyOnWriteObserverSet < Synchronization::LockableObject + + def initialize + super() + synchronize { ns_initialize } + end + + # @!macro observable_add_observer + def add_observer(observer = nil, func = :update, &block) + if observer.nil? && block.nil? + raise ArgumentError, 'should pass observer as a first argument or block' + elsif observer && block + raise ArgumentError.new('cannot provide both an observer and a block') + end + + if block + observer = block + func = :call + end + + synchronize do + new_observers = @observers.dup + new_observers[observer] = func + @observers = new_observers + observer + end + end + + # @!macro observable_delete_observer + def delete_observer(observer) + synchronize do + new_observers = @observers.dup + new_observers.delete(observer) + @observers = new_observers + observer + end + end + + # @!macro observable_delete_observers + def delete_observers + self.observers = {} + self + end + + # @!macro observable_count_observers + def count_observers + observers.count + end + + # Notifies all registered observers with optional args + # @param [Object] args arguments to be passed to each observer + # @return [CopyOnWriteObserverSet] self + def notify_observers(*args, &block) + notify_to(observers, *args, &block) + self + end + + # Notifies all registered observers with optional args and deletes them. + # + # @param [Object] args arguments to be passed to each observer + # @return [CopyOnWriteObserverSet] self + def notify_and_delete_observers(*args, &block) + old = clear_observers_and_return_old + notify_to(old, *args, &block) + self + end + + protected + + def ns_initialize + @observers = {} + end + + private + + def notify_to(observers, *args) + raise ArgumentError.new('cannot give arguments and a block') if block_given? && !args.empty? + observers.each do |observer, function| + args = yield if block_given? + observer.send(function, *args) + end + end + + def observers + synchronize { @observers } + end + + def observers=(new_set) + synchronize { @observers = new_set } + end + + def clear_observers_and_return_old + synchronize do + old_observers = @observers + @observers = {} + old_observers + end + end + end + end +end diff --git a/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/collection/java_non_concurrent_priority_queue.rb b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/collection/java_non_concurrent_priority_queue.rb new file mode 100644 index 0000000..2be9e43 --- /dev/null +++ b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/collection/java_non_concurrent_priority_queue.rb @@ -0,0 +1,84 @@ +if Concurrent.on_jruby? + + module Concurrent + module Collection + + + # @!macro priority_queue + # + # @!visibility private + # @!macro internal_implementation_note + class JavaNonConcurrentPriorityQueue + + # @!macro priority_queue_method_initialize + def initialize(opts = {}) + order = opts.fetch(:order, :max) + if [:min, :low].include?(order) + @queue = java.util.PriorityQueue.new(11) # 11 is the default initial capacity + else + @queue = java.util.PriorityQueue.new(11, java.util.Collections.reverseOrder()) + end + end + + # @!macro priority_queue_method_clear + def clear + @queue.clear + true + end + + # @!macro priority_queue_method_delete + def delete(item) + found = false + while @queue.remove(item) do + found = true + end + found + end + + # @!macro priority_queue_method_empty + def empty? + @queue.size == 0 + end + + # @!macro priority_queue_method_include + def include?(item) + @queue.contains(item) + end + alias_method :has_priority?, :include? + + # @!macro priority_queue_method_length + def length + @queue.size + end + alias_method :size, :length + + # @!macro priority_queue_method_peek + def peek + @queue.peek + end + + # @!macro priority_queue_method_pop + def pop + @queue.poll + end + alias_method :deq, :pop + alias_method :shift, :pop + + # @!macro priority_queue_method_push + def push(item) + raise ArgumentError.new('cannot enqueue nil') if item.nil? + @queue.add(item) + end + alias_method :<<, :push + alias_method :enq, :push + + # @!macro priority_queue_method_from_list + def self.from_list(list, opts = {}) + queue = new(opts) + list.each{|item| queue << item } + queue + end + end + end + end +end diff --git a/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/collection/lock_free_stack.rb b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/collection/lock_free_stack.rb new file mode 100644 index 0000000..d003d3c --- /dev/null +++ b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/collection/lock_free_stack.rb @@ -0,0 +1,158 @@ +module Concurrent + + # @!macro warn.edge + class LockFreeStack < Synchronization::Object + + safe_initialization! + + class Node + # TODO (pitr-ch 20-Dec-2016): Could be unified with Stack class? + + # @return [Node] + attr_reader :next_node + + # @return [Object] + attr_reader :value + + # @!visibility private + # allow to nil-ify to free GC when the entry is no longer relevant, not synchronised + attr_writer :value + + def initialize(value, next_node) + @value = value + @next_node = next_node + end + + singleton_class.send :alias_method, :[], :new + end + + # The singleton for empty node + EMPTY = Node[nil, nil] + def EMPTY.next_node + self + end + + attr_atomic(:head) + private :head, :head=, :swap_head, :compare_and_set_head, :update_head + + # @!visibility private + def self.of1(value) + new Node[value, EMPTY] + end + + # @!visibility private + def self.of2(value1, value2) + new Node[value1, Node[value2, EMPTY]] + end + + # @param [Node] head + def initialize(head = EMPTY) + super() + self.head = head + end + + # @param [Node] head + # @return [true, false] + def empty?(head = head()) + head.equal? EMPTY + end + + # @param [Node] head + # @param [Object] value + # @return [true, false] + def compare_and_push(head, value) + compare_and_set_head head, Node[value, head] + end + + # @param [Object] value + # @return [self] + def push(value) + while true + current_head = head + return self if compare_and_set_head current_head, Node[value, current_head] + end + end + + # @return [Node] + def peek + head + end + + # @param [Node] head + # @return [true, false] + def compare_and_pop(head) + compare_and_set_head head, head.next_node + end + + # @return [Object] + def pop + while true + current_head = head + return current_head.value if compare_and_set_head current_head, current_head.next_node + end + end + + # @param [Node] head + # @return [true, false] + def compare_and_clear(head) + compare_and_set_head head, EMPTY + end + + include Enumerable + + # @param [Node] head + # @return [self] + def each(head = nil) + return to_enum(:each, head) unless block_given? + it = head || peek + until it.equal?(EMPTY) + yield it.value + it = it.next_node + end + self + end + + # @return [true, false] + def clear + while true + current_head = head + return false if current_head == EMPTY + return true if compare_and_set_head current_head, EMPTY + end + end + + # @param [Node] head + # @return [true, false] + def clear_if(head) + compare_and_set_head head, EMPTY + end + + # @param [Node] head + # @param [Node] new_head + # @return [true, false] + def replace_if(head, new_head) + compare_and_set_head head, new_head + end + + # @return [self] + # @yield over the cleared stack + # @yieldparam [Object] value + def clear_each(&block) + while true + current_head = head + return self if current_head == EMPTY + if compare_and_set_head current_head, EMPTY + each current_head, &block + return self + end + end + end + + # @return [String] Short string representation. + def to_s + format '%s %s>', super[0..-2], to_a.to_s + end + + alias_method :inspect, :to_s + end +end diff --git a/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/collection/map/atomic_reference_map_backend.rb b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/collection/map/atomic_reference_map_backend.rb new file mode 100644 index 0000000..dc51893 --- /dev/null +++ b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/collection/map/atomic_reference_map_backend.rb @@ -0,0 +1,927 @@ +require 'concurrent/constants' +require 'concurrent/thread_safe/util' +require 'concurrent/thread_safe/util/adder' +require 'concurrent/thread_safe/util/cheap_lockable' +require 'concurrent/thread_safe/util/power_of_two_tuple' +require 'concurrent/thread_safe/util/volatile' +require 'concurrent/thread_safe/util/xor_shift_random' + +module Concurrent + + # @!visibility private + module Collection + + # A Ruby port of the Doug Lea's jsr166e.ConcurrentHashMapV8 class version 1.59 + # available in public domain. + # + # Original source code available here: + # http://gee.cs.oswego.edu/cgi-bin/viewcvs.cgi/jsr166/src/jsr166e/ConcurrentHashMapV8.java?revision=1.59 + # + # The Ruby port skips out the +TreeBin+ (red-black trees for use in bins whose + # size exceeds a threshold). + # + # A hash table supporting full concurrency of retrievals and high expected + # concurrency for updates. However, even though all operations are + # thread-safe, retrieval operations do _not_ entail locking, and there is + # _not_ any support for locking the entire table in a way that prevents all + # access. + # + # Retrieval operations generally do not block, so may overlap with update + # operations. Retrievals reflect the results of the most recently _completed_ + # update operations holding upon their onset. (More formally, an update + # operation for a given key bears a _happens-before_ relation with any (non + # +nil+) retrieval for that key reporting the updated value.) For aggregate + # operations such as +clear()+, concurrent retrievals may reflect insertion or + # removal of only some entries. Similarly, the +each_pair+ iterator yields + # elements reflecting the state of the hash table at some point at or since + # the start of the +each_pair+. Bear in mind that the results of aggregate + # status methods including +size()+ and +empty?+} are typically useful only + # when a map is not undergoing concurrent updates in other threads. Otherwise + # the results of these methods reflect transient states that may be adequate + # for monitoring or estimation purposes, but not for program control. + # + # The table is dynamically expanded when there are too many collisions (i.e., + # keys that have distinct hash codes but fall into the same slot modulo the + # table size), with the expected average effect of maintaining roughly two + # bins per mapping (corresponding to a 0.75 load factor threshold for + # resizing). There may be much variance around this average as mappings are + # added and removed, but overall, this maintains a commonly accepted + # time/space tradeoff for hash tables. However, resizing this or any other + # kind of hash table may be a relatively slow operation. When possible, it is + # a good idea to provide a size estimate as an optional :initial_capacity + # initializer argument. An additional optional :load_factor constructor + # argument provides a further means of customizing initial table capacity by + # specifying the table density to be used in calculating the amount of space + # to allocate for the given number of elements. Note that using many keys with + # exactly the same +hash+ is a sure way to slow down performance of any hash + # table. + # + # ## Design overview + # + # The primary design goal of this hash table is to maintain concurrent + # readability (typically method +[]+, but also iteration and related methods) + # while minimizing update contention. Secondary goals are to keep space + # consumption about the same or better than plain +Hash+, and to support high + # initial insertion rates on an empty table by many threads. + # + # Each key-value mapping is held in a +Node+. The validation-based approach + # explained below leads to a lot of code sprawl because retry-control + # precludes factoring into smaller methods. + # + # The table is lazily initialized to a power-of-two size upon the first + # insertion. Each bin in the table normally contains a list of +Node+s (most + # often, the list has only zero or one +Node+). Table accesses require + # volatile/atomic reads, writes, and CASes. The lists of nodes within bins are + # always accurately traversable under volatile reads, so long as lookups check + # hash code and non-nullness of value before checking key equality. + # + # We use the top two bits of +Node+ hash fields for control purposes -- they + # are available anyway because of addressing constraints. As explained further + # below, these top bits are used as follows: + # + # - 00 - Normal + # - 01 - Locked + # - 11 - Locked and may have a thread waiting for lock + # - 10 - +Node+ is a forwarding node + # + # The lower 28 bits of each +Node+'s hash field contain a the key's hash code, + # except for forwarding nodes, for which the lower bits are zero (and so + # always have hash field == +MOVED+). + # + # Insertion (via +[]=+ or its variants) of the first node in an empty bin is + # performed by just CASing it to the bin. This is by far the most common case + # for put operations under most key/hash distributions. Other update + # operations (insert, delete, and replace) require locks. We do not want to + # waste the space required to associate a distinct lock object with each bin, + # so instead use the first node of a bin list itself as a lock. Blocking + # support for these locks relies +Concurrent::ThreadSafe::Util::CheapLockable. However, we also need a + # +try_lock+ construction, so we overlay these by using bits of the +Node+ + # hash field for lock control (see above), and so normally use builtin + # monitors only for blocking and signalling using + # +cheap_wait+/+cheap_broadcast+ constructions. See +Node#try_await_lock+. + # + # Using the first node of a list as a lock does not by itself suffice though: + # When a node is locked, any update must first validate that it is still the + # first node after locking it, and retry if not. Because new nodes are always + # appended to lists, once a node is first in a bin, it remains first until + # deleted or the bin becomes invalidated (upon resizing). However, operations + # that only conditionally update may inspect nodes until the point of update. + # This is a converse of sorts to the lazy locking technique described by + # Herlihy & Shavit. + # + # The main disadvantage of per-bin locks is that other update operations on + # other nodes in a bin list protected by the same lock can stall, for example + # when user +eql?+ or mapping functions take a long time. However, + # statistically, under random hash codes, this is not a common problem. + # Ideally, the frequency of nodes in bins follows a Poisson distribution + # (http://en.wikipedia.org/wiki/Poisson_distribution) with a parameter of + # about 0.5 on average, given the resizing threshold of 0.75, although with a + # large variance because of resizing granularity. Ignoring variance, the + # expected occurrences of list size k are (exp(-0.5) * pow(0.5, k) / + # factorial(k)). The first values are: + # + # - 0: 0.60653066 + # - 1: 0.30326533 + # - 2: 0.07581633 + # - 3: 0.01263606 + # - 4: 0.00157952 + # - 5: 0.00015795 + # - 6: 0.00001316 + # - 7: 0.00000094 + # - 8: 0.00000006 + # - more: less than 1 in ten million + # + # Lock contention probability for two threads accessing distinct elements is + # roughly 1 / (8 * #elements) under random hashes. + # + # The table is resized when occupancy exceeds a percentage threshold + # (nominally, 0.75, but see below). Only a single thread performs the resize + # (using field +size_control+, to arrange exclusion), but the table otherwise + # remains usable for reads and updates. Resizing proceeds by transferring + # bins, one by one, from the table to the next table. Because we are using + # power-of-two expansion, the elements from each bin must either stay at same + # index, or move with a power of two offset. We eliminate unnecessary node + # creation by catching cases where old nodes can be reused because their next + # fields won't change. On average, only about one-sixth of them need cloning + # when a table doubles. The nodes they replace will be garbage collectable as + # soon as they are no longer referenced by any reader thread that may be in + # the midst of concurrently traversing table. Upon transfer, the old table bin + # contains only a special forwarding node (with hash field +MOVED+) that + # contains the next table as its key. On encountering a forwarding node, + # access and update operations restart, using the new table. + # + # Each bin transfer requires its bin lock. However, unlike other cases, a + # transfer can skip a bin if it fails to acquire its lock, and revisit it + # later. Method +rebuild+ maintains a buffer of TRANSFER_BUFFER_SIZE bins that + # have been skipped because of failure to acquire a lock, and blocks only if + # none are available (i.e., only very rarely). The transfer operation must + # also ensure that all accessible bins in both the old and new table are + # usable by any traversal. When there are no lock acquisition failures, this + # is arranged simply by proceeding from the last bin (+table.size - 1+) up + # towards the first. Upon seeing a forwarding node, traversals arrange to move + # to the new table without revisiting nodes. However, when any node is skipped + # during a transfer, all earlier table bins may have become visible, so are + # initialized with a reverse-forwarding node back to the old table until the + # new ones are established. (This sometimes requires transiently locking a + # forwarding node, which is possible under the above encoding.) These more + # expensive mechanics trigger only when necessary. + # + # The traversal scheme also applies to partial traversals of + # ranges of bins (via an alternate Traverser constructor) + # to support partitioned aggregate operations. Also, read-only + # operations give up if ever forwarded to a null table, which + # provides support for shutdown-style clearing, which is also not + # currently implemented. + # + # Lazy table initialization minimizes footprint until first use. + # + # The element count is maintained using a +Concurrent::ThreadSafe::Util::Adder+, + # which avoids contention on updates but can encounter cache thrashing + # if read too frequently during concurrent access. To avoid reading so + # often, resizing is attempted either when a bin lock is + # contended, or upon adding to a bin already holding two or more + # nodes (checked before adding in the +x_if_absent+ methods, after + # adding in others). Under uniform hash distributions, the + # probability of this occurring at threshold is around 13%, + # meaning that only about 1 in 8 puts check threshold (and after + # resizing, many fewer do so). But this approximation has high + # variance for small table sizes, so we check on any collision + # for sizes <= 64. The bulk putAll operation further reduces + # contention by only committing count updates upon these size + # checks. + # + # @!visibility private + class AtomicReferenceMapBackend + + # @!visibility private + class Table < Concurrent::ThreadSafe::Util::PowerOfTwoTuple + def cas_new_node(i, hash, key, value) + cas(i, nil, Node.new(hash, key, value)) + end + + def try_to_cas_in_computed(i, hash, key) + succeeded = false + new_value = nil + new_node = Node.new(locked_hash = hash | LOCKED, key, NULL) + if cas(i, nil, new_node) + begin + if NULL == (new_value = yield(NULL)) + was_null = true + else + new_node.value = new_value + end + succeeded = true + ensure + volatile_set(i, nil) if !succeeded || was_null + new_node.unlock_via_hash(locked_hash, hash) + end + end + return succeeded, new_value + end + + def try_lock_via_hash(i, node, node_hash) + node.try_lock_via_hash(node_hash) do + yield if volatile_get(i) == node + end + end + + def delete_node_at(i, node, predecessor_node) + if predecessor_node + predecessor_node.next = node.next + else + volatile_set(i, node.next) + end + end + end + + # Key-value entry. Nodes with a hash field of +MOVED+ are special, and do + # not contain user keys or values. Otherwise, keys are never +nil+, and + # +NULL+ +value+ fields indicate that a node is in the process of being + # deleted or created. For purposes of read-only access, a key may be read + # before a value, but can only be used after checking value to be +!= NULL+. + # + # @!visibility private + class Node + extend Concurrent::ThreadSafe::Util::Volatile + attr_volatile :hash, :value, :next + + include Concurrent::ThreadSafe::Util::CheapLockable + + bit_shift = Concurrent::ThreadSafe::Util::FIXNUM_BIT_SIZE - 2 # need 2 bits for ourselves + # Encodings for special uses of Node hash fields. See above for explanation. + MOVED = ('10' << ('0' * bit_shift)).to_i(2) # hash field for forwarding nodes + LOCKED = ('01' << ('0' * bit_shift)).to_i(2) # set/tested only as a bit + WAITING = ('11' << ('0' * bit_shift)).to_i(2) # both bits set/tested together + HASH_BITS = ('00' << ('1' * bit_shift)).to_i(2) # usable bits of normal node hash + + SPIN_LOCK_ATTEMPTS = Concurrent::ThreadSafe::Util::CPU_COUNT > 1 ? Concurrent::ThreadSafe::Util::CPU_COUNT * 2 : 0 + + attr_reader :key + + def initialize(hash, key, value, next_node = nil) + super() + @key = key + self.lazy_set_hash(hash) + self.lazy_set_value(value) + self.next = next_node + end + + # Spins a while if +LOCKED+ bit set and this node is the first of its bin, + # and then sets +WAITING+ bits on hash field and blocks (once) if they are + # still set. It is OK for this method to return even if lock is not + # available upon exit, which enables these simple single-wait mechanics. + # + # The corresponding signalling operation is performed within callers: Upon + # detecting that +WAITING+ has been set when unlocking lock (via a failed + # CAS from non-waiting +LOCKED+ state), unlockers acquire the + # +cheap_synchronize+ lock and perform a +cheap_broadcast+. + def try_await_lock(table, i) + if table && i >= 0 && i < table.size # bounds check, TODO: why are we bounds checking? + spins = SPIN_LOCK_ATTEMPTS + randomizer = base_randomizer = Concurrent::ThreadSafe::Util::XorShiftRandom.get + while equal?(table.volatile_get(i)) && self.class.locked_hash?(my_hash = hash) + if spins >= 0 + if (randomizer = (randomizer >> 1)).even? # spin at random + if (spins -= 1) == 0 + Thread.pass # yield before blocking + else + randomizer = base_randomizer = Concurrent::ThreadSafe::Util::XorShiftRandom.xorshift(base_randomizer) if randomizer.zero? + end + end + elsif cas_hash(my_hash, my_hash | WAITING) + force_acquire_lock(table, i) + break + end + end + end + end + + def key?(key) + @key.eql?(key) + end + + def matches?(key, hash) + pure_hash == hash && key?(key) + end + + def pure_hash + hash & HASH_BITS + end + + def try_lock_via_hash(node_hash = hash) + if cas_hash(node_hash, locked_hash = node_hash | LOCKED) + begin + yield + ensure + unlock_via_hash(locked_hash, node_hash) + end + end + end + + def locked? + self.class.locked_hash?(hash) + end + + def unlock_via_hash(locked_hash, node_hash) + unless cas_hash(locked_hash, node_hash) + self.hash = node_hash + cheap_synchronize { cheap_broadcast } + end + end + + private + def force_acquire_lock(table, i) + cheap_synchronize do + if equal?(table.volatile_get(i)) && (hash & WAITING) == WAITING + cheap_wait + else + cheap_broadcast # possibly won race vs signaller + end + end + end + + class << self + def locked_hash?(hash) + (hash & LOCKED) != 0 + end + end + end + + # shorthands + MOVED = Node::MOVED + LOCKED = Node::LOCKED + WAITING = Node::WAITING + HASH_BITS = Node::HASH_BITS + + NOW_RESIZING = -1 + DEFAULT_CAPACITY = 16 + MAX_CAPACITY = Concurrent::ThreadSafe::Util::MAX_INT + + # The buffer size for skipped bins during transfers. The + # value is arbitrary but should be large enough to avoid + # most locking stalls during resizes. + TRANSFER_BUFFER_SIZE = 32 + + extend Concurrent::ThreadSafe::Util::Volatile + attr_volatile :table, # The array of bins. Lazily initialized upon first insertion. Size is always a power of two. + + # Table initialization and resizing control. When negative, the + # table is being initialized or resized. Otherwise, when table is + # null, holds the initial table size to use upon creation, or 0 + # for default. After initialization, holds the next element count + # value upon which to resize the table. + :size_control + + def initialize(options = nil) + super() + @counter = Concurrent::ThreadSafe::Util::Adder.new + initial_capacity = options && options[:initial_capacity] || DEFAULT_CAPACITY + self.size_control = (capacity = table_size_for(initial_capacity)) > MAX_CAPACITY ? MAX_CAPACITY : capacity + end + + def get_or_default(key, else_value = nil) + hash = key_hash(key) + current_table = table + while current_table + node = current_table.volatile_get_by_hash(hash) + current_table = + while node + if (node_hash = node.hash) == MOVED + break node.key + elsif (node_hash & HASH_BITS) == hash && node.key?(key) && NULL != (value = node.value) + return value + end + node = node.next + end + end + else_value + end + + def [](key) + get_or_default(key) + end + + def key?(key) + get_or_default(key, NULL) != NULL + end + + def []=(key, value) + get_and_set(key, value) + value + end + + def compute_if_absent(key) + hash = key_hash(key) + current_table = table || initialize_table + while true + if !(node = current_table.volatile_get(i = current_table.hash_to_index(hash))) + succeeded, new_value = current_table.try_to_cas_in_computed(i, hash, key) { yield } + if succeeded + increment_size + return new_value + end + elsif (node_hash = node.hash) == MOVED + current_table = node.key + elsif NULL != (current_value = find_value_in_node_list(node, key, hash, node_hash & HASH_BITS)) + return current_value + elsif Node.locked_hash?(node_hash) + try_await_lock(current_table, i, node) + else + succeeded, value = attempt_internal_compute_if_absent(key, hash, current_table, i, node, node_hash) { yield } + return value if succeeded + end + end + end + + def compute_if_present(key) + new_value = nil + internal_replace(key) do |old_value| + if (new_value = yield(NULL == old_value ? nil : old_value)).nil? + NULL + else + new_value + end + end + new_value + end + + def compute(key) + internal_compute(key) do |old_value| + if (new_value = yield(NULL == old_value ? nil : old_value)).nil? + NULL + else + new_value + end + end + end + + def merge_pair(key, value) + internal_compute(key) do |old_value| + if NULL == old_value || !(value = yield(old_value)).nil? + value + else + NULL + end + end + end + + def replace_pair(key, old_value, new_value) + NULL != internal_replace(key, old_value) { new_value } + end + + def replace_if_exists(key, new_value) + if (result = internal_replace(key) { new_value }) && NULL != result + result + end + end + + def get_and_set(key, value) # internalPut in the original CHMV8 + hash = key_hash(key) + current_table = table || initialize_table + while true + if !(node = current_table.volatile_get(i = current_table.hash_to_index(hash))) + if current_table.cas_new_node(i, hash, key, value) + increment_size + break + end + elsif (node_hash = node.hash) == MOVED + current_table = node.key + elsif Node.locked_hash?(node_hash) + try_await_lock(current_table, i, node) + else + succeeded, old_value = attempt_get_and_set(key, value, hash, current_table, i, node, node_hash) + break old_value if succeeded + end + end + end + + def delete(key) + replace_if_exists(key, NULL) + end + + def delete_pair(key, value) + result = internal_replace(key, value) { NULL } + if result && NULL != result + !!result + else + false + end + end + + def each_pair + return self unless current_table = table + current_table_size = base_size = current_table.size + i = base_index = 0 + while base_index < base_size + if node = current_table.volatile_get(i) + if node.hash == MOVED + current_table = node.key + current_table_size = current_table.size + else + begin + if NULL != (value = node.value) # skip deleted or special nodes + yield node.key, value + end + end while node = node.next + end + end + + if (i_with_base = i + base_size) < current_table_size + i = i_with_base # visit upper slots if present + else + i = base_index += 1 + end + end + self + end + + def size + (sum = @counter.sum) < 0 ? 0 : sum # ignore transient negative values + end + + def empty? + size == 0 + end + + # Implementation for clear. Steps through each bin, removing all nodes. + def clear + return self unless current_table = table + current_table_size = current_table.size + deleted_count = i = 0 + while i < current_table_size + if !(node = current_table.volatile_get(i)) + i += 1 + elsif (node_hash = node.hash) == MOVED + current_table = node.key + current_table_size = current_table.size + elsif Node.locked_hash?(node_hash) + decrement_size(deleted_count) # opportunistically update count + deleted_count = 0 + node.try_await_lock(current_table, i) + else + current_table.try_lock_via_hash(i, node, node_hash) do + begin + deleted_count += 1 if NULL != node.value # recheck under lock + node.value = nil + end while node = node.next + current_table.volatile_set(i, nil) + i += 1 + end + end + end + decrement_size(deleted_count) + self + end + + private + # Internal versions of the insertion methods, each a + # little more complicated than the last. All have + # the same basic structure: + # 1. If table uninitialized, create + # 2. If bin empty, try to CAS new node + # 3. If bin stale, use new table + # 4. Lock and validate; if valid, scan and add or update + # + # The others interweave other checks and/or alternative actions: + # * Plain +get_and_set+ checks for and performs resize after insertion. + # * compute_if_absent prescans for mapping without lock (and fails to add + # if present), which also makes pre-emptive resize checks worthwhile. + # + # Someday when details settle down a bit more, it might be worth + # some factoring to reduce sprawl. + def internal_replace(key, expected_old_value = NULL, &block) + hash = key_hash(key) + current_table = table + while current_table + if !(node = current_table.volatile_get(i = current_table.hash_to_index(hash))) + break + elsif (node_hash = node.hash) == MOVED + current_table = node.key + elsif (node_hash & HASH_BITS) != hash && !node.next # precheck + break # rules out possible existence + elsif Node.locked_hash?(node_hash) + try_await_lock(current_table, i, node) + else + succeeded, old_value = attempt_internal_replace(key, expected_old_value, hash, current_table, i, node, node_hash, &block) + return old_value if succeeded + end + end + NULL + end + + def attempt_internal_replace(key, expected_old_value, hash, current_table, i, node, node_hash) + current_table.try_lock_via_hash(i, node, node_hash) do + predecessor_node = nil + old_value = NULL + begin + if node.matches?(key, hash) && NULL != (current_value = node.value) + if NULL == expected_old_value || expected_old_value == current_value # NULL == expected_old_value means whatever value + old_value = current_value + if NULL == (node.value = yield(old_value)) + current_table.delete_node_at(i, node, predecessor_node) + decrement_size + end + end + break + end + + predecessor_node = node + end while node = node.next + + return true, old_value + end + end + + def find_value_in_node_list(node, key, hash, pure_hash) + do_check_for_resize = false + while true + if pure_hash == hash && node.key?(key) && NULL != (value = node.value) + return value + elsif node = node.next + do_check_for_resize = true # at least 2 nodes -> check for resize + pure_hash = node.pure_hash + else + return NULL + end + end + ensure + check_for_resize if do_check_for_resize + end + + def internal_compute(key, &block) + hash = key_hash(key) + current_table = table || initialize_table + while true + if !(node = current_table.volatile_get(i = current_table.hash_to_index(hash))) + succeeded, new_value = current_table.try_to_cas_in_computed(i, hash, key, &block) + if succeeded + if NULL == new_value + break nil + else + increment_size + break new_value + end + end + elsif (node_hash = node.hash) == MOVED + current_table = node.key + elsif Node.locked_hash?(node_hash) + try_await_lock(current_table, i, node) + else + succeeded, new_value = attempt_compute(key, hash, current_table, i, node, node_hash, &block) + break new_value if succeeded + end + end + end + + def attempt_internal_compute_if_absent(key, hash, current_table, i, node, node_hash) + added = false + current_table.try_lock_via_hash(i, node, node_hash) do + while true + if node.matches?(key, hash) && NULL != (value = node.value) + return true, value + end + last = node + unless node = node.next + last.next = Node.new(hash, key, value = yield) + added = true + increment_size + return true, value + end + end + end + ensure + check_for_resize if added + end + + def attempt_compute(key, hash, current_table, i, node, node_hash) + added = false + current_table.try_lock_via_hash(i, node, node_hash) do + predecessor_node = nil + while true + if node.matches?(key, hash) && NULL != (value = node.value) + if NULL == (node.value = value = yield(value)) + current_table.delete_node_at(i, node, predecessor_node) + decrement_size + value = nil + end + return true, value + end + predecessor_node = node + unless node = node.next + if NULL == (value = yield(NULL)) + value = nil + else + predecessor_node.next = Node.new(hash, key, value) + added = true + increment_size + end + return true, value + end + end + end + ensure + check_for_resize if added + end + + def attempt_get_and_set(key, value, hash, current_table, i, node, node_hash) + node_nesting = nil + current_table.try_lock_via_hash(i, node, node_hash) do + node_nesting = 1 + old_value = nil + found_old_value = false + while node + if node.matches?(key, hash) && NULL != (old_value = node.value) + found_old_value = true + node.value = value + break + end + last = node + unless node = node.next + last.next = Node.new(hash, key, value) + break + end + node_nesting += 1 + end + + return true, old_value if found_old_value + increment_size + true + end + ensure + check_for_resize if node_nesting && (node_nesting > 1 || current_table.size <= 64) + end + + def initialize_copy(other) + super + @counter = Concurrent::ThreadSafe::Util::Adder.new + self.table = nil + self.size_control = (other_table = other.table) ? other_table.size : DEFAULT_CAPACITY + self + end + + def try_await_lock(current_table, i, node) + check_for_resize # try resizing if can't get lock + node.try_await_lock(current_table, i) + end + + def key_hash(key) + key.hash & HASH_BITS + end + + # Returns a power of two table size for the given desired capacity. + def table_size_for(entry_count) + size = 2 + size <<= 1 while size < entry_count + size + end + + # Initializes table, using the size recorded in +size_control+. + def initialize_table + until current_table ||= table + if (size_ctrl = size_control) == NOW_RESIZING + Thread.pass # lost initialization race; just spin + else + try_in_resize_lock(current_table, size_ctrl) do + initial_size = size_ctrl > 0 ? size_ctrl : DEFAULT_CAPACITY + current_table = self.table = Table.new(initial_size) + initial_size - (initial_size >> 2) # 75% load factor + end + end + end + current_table + end + + # If table is too small and not already resizing, creates next table and + # transfers bins. Rechecks occupancy after a transfer to see if another + # resize is already needed because resizings are lagging additions. + def check_for_resize + while (current_table = table) && MAX_CAPACITY > (table_size = current_table.size) && NOW_RESIZING != (size_ctrl = size_control) && size_ctrl < @counter.sum + try_in_resize_lock(current_table, size_ctrl) do + self.table = rebuild(current_table) + (table_size << 1) - (table_size >> 1) # 75% load factor + end + end + end + + def try_in_resize_lock(current_table, size_ctrl) + if cas_size_control(size_ctrl, NOW_RESIZING) + begin + if current_table == table # recheck under lock + size_ctrl = yield # get new size_control + end + ensure + self.size_control = size_ctrl + end + end + end + + # Moves and/or copies the nodes in each bin to new table. See above for explanation. + def rebuild(table) + old_table_size = table.size + new_table = table.next_in_size_table + # puts "#{old_table_size} -> #{new_table.size}" + forwarder = Node.new(MOVED, new_table, NULL) + rev_forwarder = nil + locked_indexes = nil # holds bins to revisit; nil until needed + locked_arr_idx = 0 + bin = old_table_size - 1 + i = bin + while true + if !(node = table.volatile_get(i)) + # no lock needed (or available) if bin >= 0, because we're not popping values from locked_indexes until we've run through the whole table + redo unless (bin >= 0 ? table.cas(i, nil, forwarder) : lock_and_clean_up_reverse_forwarders(table, old_table_size, new_table, i, forwarder)) + elsif Node.locked_hash?(node_hash = node.hash) + locked_indexes ||= ::Array.new + if bin < 0 && locked_arr_idx > 0 + locked_arr_idx -= 1 + i, locked_indexes[locked_arr_idx] = locked_indexes[locked_arr_idx], i # swap with another bin + redo + end + if bin < 0 || locked_indexes.size >= TRANSFER_BUFFER_SIZE + node.try_await_lock(table, i) # no other options -- block + redo + end + rev_forwarder ||= Node.new(MOVED, table, NULL) + redo unless table.volatile_get(i) == node && node.locked? # recheck before adding to list + locked_indexes << i + new_table.volatile_set(i, rev_forwarder) + new_table.volatile_set(i + old_table_size, rev_forwarder) + else + redo unless split_old_bin(table, new_table, i, node, node_hash, forwarder) + end + + if bin > 0 + i = (bin -= 1) + elsif locked_indexes && !locked_indexes.empty? + bin = -1 + i = locked_indexes.pop + locked_arr_idx = locked_indexes.size - 1 + else + return new_table + end + end + end + + def lock_and_clean_up_reverse_forwarders(old_table, old_table_size, new_table, i, forwarder) + # transiently use a locked forwarding node + locked_forwarder = Node.new(moved_locked_hash = MOVED | LOCKED, new_table, NULL) + if old_table.cas(i, nil, locked_forwarder) + new_table.volatile_set(i, nil) # kill the potential reverse forwarders + new_table.volatile_set(i + old_table_size, nil) # kill the potential reverse forwarders + old_table.volatile_set(i, forwarder) + locked_forwarder.unlock_via_hash(moved_locked_hash, MOVED) + true + end + end + + # Splits a normal bin with list headed by e into lo and hi parts; installs in given table. + def split_old_bin(table, new_table, i, node, node_hash, forwarder) + table.try_lock_via_hash(i, node, node_hash) do + split_bin(new_table, i, node, node_hash) + table.volatile_set(i, forwarder) + end + end + + def split_bin(new_table, i, node, node_hash) + bit = new_table.size >> 1 # bit to split on + run_bit = node_hash & bit + last_run = nil + low = nil + high = nil + current_node = node + # this optimises for the lowest amount of volatile writes and objects created + while current_node = current_node.next + unless (b = current_node.hash & bit) == run_bit + run_bit = b + last_run = current_node + end + end + if run_bit == 0 + low = last_run + else + high = last_run + end + current_node = node + until current_node == last_run + pure_hash = current_node.pure_hash + if (pure_hash & bit) == 0 + low = Node.new(pure_hash, current_node.key, current_node.value, low) + else + high = Node.new(pure_hash, current_node.key, current_node.value, high) + end + current_node = current_node.next + end + new_table.volatile_set(i, low) + new_table.volatile_set(i + bit, high) + end + + def increment_size + @counter.increment + end + + def decrement_size(by = 1) + @counter.add(-by) + end + end + end +end diff --git a/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/collection/map/mri_map_backend.rb b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/collection/map/mri_map_backend.rb new file mode 100644 index 0000000..d5d809d --- /dev/null +++ b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/collection/map/mri_map_backend.rb @@ -0,0 +1,66 @@ +require 'thread' +require 'concurrent/collection/map/non_concurrent_map_backend' + +module Concurrent + + # @!visibility private + module Collection + + # @!visibility private + class MriMapBackend < NonConcurrentMapBackend + + def initialize(options = nil) + super(options) + @write_lock = Mutex.new + end + + def []=(key, value) + @write_lock.synchronize { super } + end + + def compute_if_absent(key) + if stored_value = _get(key) # fast non-blocking path for the most likely case + stored_value + else + @write_lock.synchronize { super } + end + end + + def compute_if_present(key) + @write_lock.synchronize { super } + end + + def compute(key) + @write_lock.synchronize { super } + end + + def merge_pair(key, value) + @write_lock.synchronize { super } + end + + def replace_pair(key, old_value, new_value) + @write_lock.synchronize { super } + end + + def replace_if_exists(key, new_value) + @write_lock.synchronize { super } + end + + def get_and_set(key, value) + @write_lock.synchronize { super } + end + + def delete(key) + @write_lock.synchronize { super } + end + + def delete_pair(key, value) + @write_lock.synchronize { super } + end + + def clear + @write_lock.synchronize { super } + end + end + end +end diff --git a/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/collection/map/non_concurrent_map_backend.rb b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/collection/map/non_concurrent_map_backend.rb new file mode 100644 index 0000000..e7c62e6 --- /dev/null +++ b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/collection/map/non_concurrent_map_backend.rb @@ -0,0 +1,140 @@ +require 'concurrent/constants' + +module Concurrent + + # @!visibility private + module Collection + + # @!visibility private + class NonConcurrentMapBackend + + # WARNING: all public methods of the class must operate on the @backend + # directly without calling each other. This is important because of the + # SynchronizedMapBackend which uses a non-reentrant mutex for performance + # reasons. + def initialize(options = nil) + @backend = {} + end + + def [](key) + @backend[key] + end + + def []=(key, value) + @backend[key] = value + end + + def compute_if_absent(key) + if NULL != (stored_value = @backend.fetch(key, NULL)) + stored_value + else + @backend[key] = yield + end + end + + def replace_pair(key, old_value, new_value) + if pair?(key, old_value) + @backend[key] = new_value + true + else + false + end + end + + def replace_if_exists(key, new_value) + if NULL != (stored_value = @backend.fetch(key, NULL)) + @backend[key] = new_value + stored_value + end + end + + def compute_if_present(key) + if NULL != (stored_value = @backend.fetch(key, NULL)) + store_computed_value(key, yield(stored_value)) + end + end + + def compute(key) + store_computed_value(key, yield(@backend[key])) + end + + def merge_pair(key, value) + if NULL == (stored_value = @backend.fetch(key, NULL)) + @backend[key] = value + else + store_computed_value(key, yield(stored_value)) + end + end + + def get_and_set(key, value) + stored_value = @backend[key] + @backend[key] = value + stored_value + end + + def key?(key) + @backend.key?(key) + end + + def delete(key) + @backend.delete(key) + end + + def delete_pair(key, value) + if pair?(key, value) + @backend.delete(key) + true + else + false + end + end + + def clear + @backend.clear + self + end + + def each_pair + dupped_backend.each_pair do |k, v| + yield k, v + end + self + end + + def size + @backend.size + end + + def get_or_default(key, default_value) + @backend.fetch(key, default_value) + end + + alias_method :_get, :[] + alias_method :_set, :[]= + private :_get, :_set + private + def initialize_copy(other) + super + @backend = {} + self + end + + def dupped_backend + @backend.dup + end + + def pair?(key, expected_value) + NULL != (stored_value = @backend.fetch(key, NULL)) && expected_value.equal?(stored_value) + end + + def store_computed_value(key, new_value) + if new_value.nil? + @backend.delete(key) + nil + else + @backend[key] = new_value + end + end + end + end +end diff --git a/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/collection/map/synchronized_map_backend.rb b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/collection/map/synchronized_map_backend.rb new file mode 100644 index 0000000..190c8d9 --- /dev/null +++ b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/collection/map/synchronized_map_backend.rb @@ -0,0 +1,82 @@ +require 'concurrent/collection/map/non_concurrent_map_backend' + +module Concurrent + + # @!visibility private + module Collection + + # @!visibility private + class SynchronizedMapBackend < NonConcurrentMapBackend + + require 'mutex_m' + include Mutex_m + # WARNING: Mutex_m is a non-reentrant lock, so the synchronized methods are + # not allowed to call each other. + + def [](key) + synchronize { super } + end + + def []=(key, value) + synchronize { super } + end + + def compute_if_absent(key) + synchronize { super } + end + + def compute_if_present(key) + synchronize { super } + end + + def compute(key) + synchronize { super } + end + + def merge_pair(key, value) + synchronize { super } + end + + def replace_pair(key, old_value, new_value) + synchronize { super } + end + + def replace_if_exists(key, new_value) + synchronize { super } + end + + def get_and_set(key, value) + synchronize { super } + end + + def key?(key) + synchronize { super } + end + + def delete(key) + synchronize { super } + end + + def delete_pair(key, value) + synchronize { super } + end + + def clear + synchronize { super } + end + + def size + synchronize { super } + end + + def get_or_default(key, default_value) + synchronize { super } + end + + private + def dupped_backend + synchronize { super } + end + end + end +end diff --git a/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/collection/non_concurrent_priority_queue.rb b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/collection/non_concurrent_priority_queue.rb new file mode 100644 index 0000000..695ffdf --- /dev/null +++ b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/collection/non_concurrent_priority_queue.rb @@ -0,0 +1,143 @@ +require 'concurrent/collection/java_non_concurrent_priority_queue' +require 'concurrent/collection/ruby_non_concurrent_priority_queue' +require 'concurrent/utility/engine' + +module Concurrent + module Collection + + # @!visibility private + # @!macro internal_implementation_note + NonConcurrentPriorityQueueImplementation = case + when Concurrent.on_jruby? + JavaNonConcurrentPriorityQueue + else + RubyNonConcurrentPriorityQueue + end + private_constant :NonConcurrentPriorityQueueImplementation + + # @!macro priority_queue + # + # A queue collection in which the elements are sorted based on their + # comparison (spaceship) operator `<=>`. Items are added to the queue + # at a position relative to their priority. On removal the element + # with the "highest" priority is removed. By default the sort order is + # from highest to lowest, but a lowest-to-highest sort order can be + # set on construction. + # + # The API is based on the `Queue` class from the Ruby standard library. + # + # The pure Ruby implementation, `RubyNonConcurrentPriorityQueue` uses a heap algorithm + # stored in an array. The algorithm is based on the work of Robert Sedgewick + # and Kevin Wayne. + # + # The JRuby native implementation is a thin wrapper around the standard + # library `java.util.NonConcurrentPriorityQueue`. + # + # When running under JRuby the class `NonConcurrentPriorityQueue` extends `JavaNonConcurrentPriorityQueue`. + # When running under all other interpreters it extends `RubyNonConcurrentPriorityQueue`. + # + # @note This implementation is *not* thread safe. + # + # @see http://en.wikipedia.org/wiki/Priority_queue + # @see http://ruby-doc.org/stdlib-2.0.0/libdoc/thread/rdoc/Queue.html + # + # @see http://algs4.cs.princeton.edu/24pq/index.php#2.6 + # @see http://algs4.cs.princeton.edu/24pq/MaxPQ.java.html + # + # @see http://docs.oracle.com/javase/7/docs/api/java/util/PriorityQueue.html + # + # @!visibility private + class NonConcurrentPriorityQueue < NonConcurrentPriorityQueueImplementation + + alias_method :has_priority?, :include? + + alias_method :size, :length + + alias_method :deq, :pop + alias_method :shift, :pop + + alias_method :<<, :push + alias_method :enq, :push + + # @!method initialize(opts = {}) + # @!macro priority_queue_method_initialize + # + # Create a new priority queue with no items. + # + # @param [Hash] opts the options for creating the queue + # @option opts [Symbol] :order (:max) dictates the order in which items are + # stored: from highest to lowest when `:max` or `:high`; from lowest to + # highest when `:min` or `:low` + + # @!method clear + # @!macro priority_queue_method_clear + # + # Removes all of the elements from this priority queue. + + # @!method delete(item) + # @!macro priority_queue_method_delete + # + # Deletes all items from `self` that are equal to `item`. + # + # @param [Object] item the item to be removed from the queue + # @return [Object] true if the item is found else false + + # @!method empty? + # @!macro priority_queue_method_empty + # + # Returns `true` if `self` contains no elements. + # + # @return [Boolean] true if there are no items in the queue else false + + # @!method include?(item) + # @!macro priority_queue_method_include + # + # Returns `true` if the given item is present in `self` (that is, if any + # element == `item`), otherwise returns false. + # + # @param [Object] item the item to search for + # + # @return [Boolean] true if the item is found else false + + # @!method length + # @!macro priority_queue_method_length + # + # The current length of the queue. + # + # @return [Fixnum] the number of items in the queue + + # @!method peek + # @!macro priority_queue_method_peek + # + # Retrieves, but does not remove, the head of this queue, or returns `nil` + # if this queue is empty. + # + # @return [Object] the head of the queue or `nil` when empty + + # @!method pop + # @!macro priority_queue_method_pop + # + # Retrieves and removes the head of this queue, or returns `nil` if this + # queue is empty. + # + # @return [Object] the head of the queue or `nil` when empty + + # @!method push(item) + # @!macro priority_queue_method_push + # + # Inserts the specified element into this priority queue. + # + # @param [Object] item the item to insert onto the queue + + # @!method self.from_list(list, opts = {}) + # @!macro priority_queue_method_from_list + # + # Create a new priority queue from the given list. + # + # @param [Enumerable] list the list to build the queue from + # @param [Hash] opts the options for creating the queue + # + # @return [NonConcurrentPriorityQueue] the newly created and populated queue + end + end +end diff --git a/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/collection/ruby_non_concurrent_priority_queue.rb b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/collection/ruby_non_concurrent_priority_queue.rb new file mode 100644 index 0000000..bdf3cba --- /dev/null +++ b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/collection/ruby_non_concurrent_priority_queue.rb @@ -0,0 +1,150 @@ +module Concurrent + module Collection + + # @!macro priority_queue + # + # @!visibility private + # @!macro internal_implementation_note + class RubyNonConcurrentPriorityQueue + + # @!macro priority_queue_method_initialize + def initialize(opts = {}) + order = opts.fetch(:order, :max) + @comparator = [:min, :low].include?(order) ? -1 : 1 + clear + end + + # @!macro priority_queue_method_clear + def clear + @queue = [nil] + @length = 0 + true + end + + # @!macro priority_queue_method_delete + def delete(item) + return false if empty? + original_length = @length + k = 1 + while k <= @length + if @queue[k] == item + swap(k, @length) + @length -= 1 + sink(k) + @queue.pop + else + k += 1 + end + end + @length != original_length + end + + # @!macro priority_queue_method_empty + def empty? + size == 0 + end + + # @!macro priority_queue_method_include + def include?(item) + @queue.include?(item) + end + alias_method :has_priority?, :include? + + # @!macro priority_queue_method_length + def length + @length + end + alias_method :size, :length + + # @!macro priority_queue_method_peek + def peek + empty? ? nil : @queue[1] + end + + # @!macro priority_queue_method_pop + def pop + return nil if empty? + max = @queue[1] + swap(1, @length) + @length -= 1 + sink(1) + @queue.pop + max + end + alias_method :deq, :pop + alias_method :shift, :pop + + # @!macro priority_queue_method_push + def push(item) + raise ArgumentError.new('cannot enqueue nil') if item.nil? + @length += 1 + @queue << item + swim(@length) + true + end + alias_method :<<, :push + alias_method :enq, :push + + # @!macro priority_queue_method_from_list + def self.from_list(list, opts = {}) + queue = new(opts) + list.each{|item| queue << item } + queue + end + + private + + # Exchange the values at the given indexes within the internal array. + # + # @param [Integer] x the first index to swap + # @param [Integer] y the second index to swap + # + # @!visibility private + def swap(x, y) + temp = @queue[x] + @queue[x] = @queue[y] + @queue[y] = temp + end + + # Are the items at the given indexes ordered based on the priority + # order specified at construction? + # + # @param [Integer] x the first index from which to retrieve a comparable value + # @param [Integer] y the second index from which to retrieve a comparable value + # + # @return [Boolean] true if the two elements are in the correct priority order + # else false + # + # @!visibility private + def ordered?(x, y) + (@queue[x] <=> @queue[y]) == @comparator + end + + # Percolate down to maintain heap invariant. + # + # @param [Integer] k the index at which to start the percolation + # + # @!visibility private + def sink(k) + while (j = (2 * k)) <= @length do + j += 1 if j < @length && ! ordered?(j, j+1) + break if ordered?(k, j) + swap(k, j) + k = j + end + end + + # Percolate up to maintain heap invariant. + # + # @param [Integer] k the index at which to start the percolation + # + # @!visibility private + def swim(k) + while k > 1 && ! ordered?(k/2, k) do + swap(k, k/2) + k = k/2 + end + end + end + end +end diff --git a/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/concern/deprecation.rb b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/concern/deprecation.rb new file mode 100644 index 0000000..35ae4b2 --- /dev/null +++ b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/concern/deprecation.rb @@ -0,0 +1,34 @@ +require 'concurrent/concern/logging' + +module Concurrent + module Concern + + # @!visibility private + # @!macro internal_implementation_note + module Deprecation + # TODO require additional parameter: a version. Display when it'll be removed based on that. Error if not removed. + include Concern::Logging + + def deprecated(message, strip = 2) + caller_line = caller(strip).first if strip > 0 + klass = if Module === self + self + else + self.class + end + message = if strip > 0 + format("[DEPRECATED] %s\ncalled on: %s", message, caller_line) + else + format('[DEPRECATED] %s', message) + end + log WARN, klass.to_s, message + end + + def deprecated_method(old_name, new_name) + deprecated "`#{old_name}` is deprecated and it'll removed in next release, use `#{new_name}` instead", 3 + end + + extend self + end + end +end diff --git a/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/concern/dereferenceable.rb b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/concern/dereferenceable.rb new file mode 100644 index 0000000..b0d1a2e --- /dev/null +++ b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/concern/dereferenceable.rb @@ -0,0 +1,73 @@ +module Concurrent + module Concern + + # Object references in Ruby are mutable. This can lead to serious problems when + # the `#value` of a concurrent object is a mutable reference. Which is always the + # case unless the value is a `Fixnum`, `Symbol`, or similar "primitive" data type. + # Most classes in this library that expose a `#value` getter method do so using the + # `Dereferenceable` mixin module. + # + # @!macro copy_options + module Dereferenceable + # NOTE: This module is going away in 2.0. In the mean time we need it to + # play nicely with the synchronization layer. This means that the + # including class SHOULD be synchronized and it MUST implement a + # `#synchronize` method. Not doing so will lead to runtime errors. + + # Return the value this object represents after applying the options specified + # by the `#set_deref_options` method. + # + # @return [Object] the current value of the object + def value + synchronize { apply_deref_options(@value) } + end + alias_method :deref, :value + + protected + + # Set the internal value of this object + # + # @param [Object] value the new value + def value=(value) + synchronize{ @value = value } + end + + # @!macro dereferenceable_set_deref_options + # Set the options which define the operations #value performs before + # returning data to the caller (dereferencing). + # + # @note Most classes that include this module will call `#set_deref_options` + # from within the constructor, thus allowing these options to be set at + # object creation. + # + # @param [Hash] opts the options defining dereference behavior. + # @option opts [String] :dup_on_deref (false) call `#dup` before returning the data + # @option opts [String] :freeze_on_deref (false) call `#freeze` before returning the data + # @option opts [String] :copy_on_deref (nil) call the given `Proc` passing + # the internal value and returning the value returned from the proc + def set_deref_options(opts = {}) + synchronize{ ns_set_deref_options(opts) } + end + + # @!macro dereferenceable_set_deref_options + # @!visibility private + def ns_set_deref_options(opts) + @dup_on_deref = opts[:dup_on_deref] || opts[:dup] + @freeze_on_deref = opts[:freeze_on_deref] || opts[:freeze] + @copy_on_deref = opts[:copy_on_deref] || opts[:copy] + @do_nothing_on_deref = !(@dup_on_deref || @freeze_on_deref || @copy_on_deref) + nil + end + + # @!visibility private + def apply_deref_options(value) + return nil if value.nil? + return value if @do_nothing_on_deref + value = @copy_on_deref.call(value) if @copy_on_deref + value = value.dup if @dup_on_deref + value = value.freeze if @freeze_on_deref + value + end + end + end +end diff --git a/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/concern/logging.rb b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/concern/logging.rb new file mode 100644 index 0000000..2c74999 --- /dev/null +++ b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/concern/logging.rb @@ -0,0 +1,32 @@ +require 'logger' + +module Concurrent + module Concern + + # Include where logging is needed + # + # @!visibility private + module Logging + include Logger::Severity + + # Logs through {Concurrent.global_logger}, it can be overridden by setting @logger + # @param [Integer] level one of Logger::Severity constants + # @param [String] progname e.g. a path of an Actor + # @param [String, nil] message when nil block is used to generate the message + # @yieldreturn [String] a message + def log(level, progname, message = nil, &block) + #NOTE: Cannot require 'concurrent/configuration' above due to circular references. + # Assume that the gem has been initialized if we've gotten this far. + logger = if defined?(@logger) && @logger + @logger + else + Concurrent.global_logger + end + logger.call level, progname, message, &block + rescue => error + $stderr.puts "`Concurrent.configuration.logger` failed to log #{[level, progname, message, block]}\n" + + "#{error.message} (#{error.class})\n#{error.backtrace.join "\n"}" + end + end + end +end diff --git a/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/concern/obligation.rb b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/concern/obligation.rb new file mode 100644 index 0000000..2c9ac12 --- /dev/null +++ b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/concern/obligation.rb @@ -0,0 +1,220 @@ +require 'thread' +require 'timeout' + +require 'concurrent/atomic/event' +require 'concurrent/concern/dereferenceable' + +module Concurrent + module Concern + + module Obligation + include Concern::Dereferenceable + # NOTE: The Dereferenceable module is going away in 2.0. In the mean time + # we need it to place nicely with the synchronization layer. This means + # that the including class SHOULD be synchronized and it MUST implement a + # `#synchronize` method. Not doing so will lead to runtime errors. + + # Has the obligation been fulfilled? + # + # @return [Boolean] + def fulfilled? + state == :fulfilled + end + alias_method :realized?, :fulfilled? + + # Has the obligation been rejected? + # + # @return [Boolean] + def rejected? + state == :rejected + end + + # Is obligation completion still pending? + # + # @return [Boolean] + def pending? + state == :pending + end + + # Is the obligation still unscheduled? + # + # @return [Boolean] + def unscheduled? + state == :unscheduled + end + + # Has the obligation completed processing? + # + # @return [Boolean] + def complete? + [:fulfilled, :rejected].include? state + end + + # Is the obligation still awaiting completion of processing? + # + # @return [Boolean] + def incomplete? + ! complete? + end + + # The current value of the obligation. Will be `nil` while the state is + # pending or the operation has been rejected. + # + # @param [Numeric] timeout the maximum time in seconds to wait. + # @return [Object] see Dereferenceable#deref + def value(timeout = nil) + wait timeout + deref + end + + # Wait until obligation is complete or the timeout has been reached. + # + # @param [Numeric] timeout the maximum time in seconds to wait. + # @return [Obligation] self + def wait(timeout = nil) + event.wait(timeout) if timeout != 0 && incomplete? + self + end + + # Wait until obligation is complete or the timeout is reached. Will re-raise + # any exceptions raised during processing (but will not raise an exception + # on timeout). + # + # @param [Numeric] timeout the maximum time in seconds to wait. + # @return [Obligation] self + # @raise [Exception] raises the reason when rejected + def wait!(timeout = nil) + wait(timeout).tap { raise self if rejected? } + end + alias_method :no_error!, :wait! + + # The current value of the obligation. Will be `nil` while the state is + # pending or the operation has been rejected. Will re-raise any exceptions + # raised during processing (but will not raise an exception on timeout). + # + # @param [Numeric] timeout the maximum time in seconds to wait. + # @return [Object] see Dereferenceable#deref + # @raise [Exception] raises the reason when rejected + def value!(timeout = nil) + wait(timeout) + if rejected? + raise self + else + deref + end + end + + # The current state of the obligation. + # + # @return [Symbol] the current state + def state + synchronize { @state } + end + + # If an exception was raised during processing this will return the + # exception object. Will return `nil` when the state is pending or if + # the obligation has been successfully fulfilled. + # + # @return [Exception] the exception raised during processing or `nil` + def reason + synchronize { @reason } + end + + # @example allows Obligation to be risen + # rejected_ivar = Ivar.new.fail + # raise rejected_ivar + def exception(*args) + raise 'obligation is not rejected' unless rejected? + reason.exception(*args) + end + + protected + + # @!visibility private + def get_arguments_from(opts = {}) + [*opts.fetch(:args, [])] + end + + # @!visibility private + def init_obligation + @event = Event.new + @value = @reason = nil + end + + # @!visibility private + def event + @event + end + + # @!visibility private + def set_state(success, value, reason) + if success + @value = value + @state = :fulfilled + else + @reason = reason + @state = :rejected + end + end + + # @!visibility private + def state=(value) + synchronize { ns_set_state(value) } + end + + # Atomic compare and set operation + # State is set to `next_state` only if `current state == expected_current`. + # + # @param [Symbol] next_state + # @param [Symbol] expected_current + # + # @return [Boolean] true is state is changed, false otherwise + # + # @!visibility private + def compare_and_set_state(next_state, *expected_current) + synchronize do + if expected_current.include? @state + @state = next_state + true + else + false + end + end + end + + # Executes the block within mutex if current state is included in expected_states + # + # @return block value if executed, false otherwise + # + # @!visibility private + def if_state(*expected_states) + synchronize do + raise ArgumentError.new('no block given') unless block_given? + + if expected_states.include? @state + yield + else + false + end + end + end + + protected + + # Am I in the current state? + # + # @param [Symbol] expected The state to check against + # @return [Boolean] true if in the expected state else false + # + # @!visibility private + def ns_check_state?(expected) + @state == expected + end + + # @!visibility private + def ns_set_state(value) + @state = value + end + end + end +end diff --git a/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/concern/observable.rb b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/concern/observable.rb new file mode 100644 index 0000000..b513271 --- /dev/null +++ b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/concern/observable.rb @@ -0,0 +1,110 @@ +require 'concurrent/collection/copy_on_notify_observer_set' +require 'concurrent/collection/copy_on_write_observer_set' + +module Concurrent + module Concern + + # The [observer pattern](http://en.wikipedia.org/wiki/Observer_pattern) is one + # of the most useful design patterns. + # + # The workflow is very simple: + # - an `observer` can register itself to a `subject` via a callback + # - many `observers` can be registered to the same `subject` + # - the `subject` notifies all registered observers when its status changes + # - an `observer` can deregister itself when is no more interested to receive + # event notifications + # + # In a single threaded environment the whole pattern is very easy: the + # `subject` can use a simple data structure to manage all its subscribed + # `observer`s and every `observer` can react directly to every event without + # caring about synchronization. + # + # In a multi threaded environment things are more complex. The `subject` must + # synchronize the access to its data structure and to do so currently we're + # using two specialized ObserverSet: {Concurrent::Concern::CopyOnWriteObserverSet} + # and {Concurrent::Concern::CopyOnNotifyObserverSet}. + # + # When implementing and `observer` there's a very important rule to remember: + # **there are no guarantees about the thread that will execute the callback** + # + # Let's take this example + # ``` + # class Observer + # def initialize + # @count = 0 + # end + # + # def update + # @count += 1 + # end + # end + # + # obs = Observer.new + # [obj1, obj2, obj3, obj4].each { |o| o.add_observer(obs) } + # # execute [obj1, obj2, obj3, obj4] + # ``` + # + # `obs` is wrong because the variable `@count` can be accessed by different + # threads at the same time, so it should be synchronized (using either a Mutex + # or an AtomicFixum) + module Observable + + # @!macro observable_add_observer + # + # Adds an observer to this set. If a block is passed, the observer will be + # created by this method and no other params should be passed. + # + # @param [Object] observer the observer to add + # @param [Symbol] func the function to call on the observer during notification. + # Default is :update + # @return [Object] the added observer + def add_observer(observer = nil, func = :update, &block) + observers.add_observer(observer, func, &block) + end + + # As `#add_observer` but can be used for chaining. + # + # @param [Object] observer the observer to add + # @param [Symbol] func the function to call on the observer during notification. + # @return [Observable] self + def with_observer(observer = nil, func = :update, &block) + add_observer(observer, func, &block) + self + end + + # @!macro observable_delete_observer + # + # Remove `observer` as an observer on this object so that it will no + # longer receive notifications. + # + # @param [Object] observer the observer to remove + # @return [Object] the deleted observer + def delete_observer(observer) + observers.delete_observer(observer) + end + + # @!macro observable_delete_observers + # + # Remove all observers associated with this object. + # + # @return [Observable] self + def delete_observers + observers.delete_observers + self + end + + # @!macro observable_count_observers + # + # Return the number of observers associated with this object. + # + # @return [Integer] the observers count + def count_observers + observers.count_observers + end + + protected + + attr_accessor :observers + end + end +end diff --git a/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/concurrent_ruby.jar b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/concurrent_ruby.jar new file mode 100644 index 0000000000000000000000000000000000000000..bf8261fae791e58b4badb96045cb94e017aead7e GIT binary patch literal 137051 zcmcG#bChKLwk4XCwr$(CtxB7fwrxe)wzJZ^;_+YtAJF84yrZAV^3^pm`N5d7%Ht2MP!jNKQ;eh)zmgoZ({}2nYm7K?VZy z&k5jv9#i3VO@t-%#3CT-|iz%zn%ZcC1O-{(l(9zGq$ z*zfOuJ;mI=yaLYeUq$R}ja(cZO>CW&T)ufIn>e~!7@5!;SsOYzg(&NOMG->fTd1*V z2~mRajZ{Ud3k?7u;Fom{>BhA0X2s?b3dmx0lIW>U2CTmR+aK2a#)Nrt9$^P&G^y)OZ||$OV*oWkC9(Ymr35fm7@Ch zL?T^lE61}q>16^>KPh>w@EBVe%!K8MiwBG#a0VefErT}|)qI__L%&%@!IIEmN;(w; zUqn4a)$C$dvp5IC^TwO{NJdO(9(IK}=MCn(Q(EYccHhBq+(YN&4b*zXmrEqiB{Pr#*6IqLIj)smNq)h*Cu4JVRX%HqvzB3!Es;VdL zTOp(}44ua&YDvY?MQBxM{xMr@t8w-KR+?A!S5nI7FTP2f8xfL^ec;TgiDOw~vfBcB z+dvojaYD!_@Z>CEhQoc8L0k~`uw_1FMSR9#7%Moi;q|uA0B==lMHrrL#?%}F#Dt%L zf=)9Dnlx%n7Hu{tuL!o;q87aj5PB`zTAu+K3Yu$3)db_c(no3qwN5>Bt#nMr^v>q3 zobgDCd!y$uhb|5zQ3^UL#df^+wOc>0c&RTmZUx+R!AId|I+9IM>T zPjkAiZX>*Yd}P}_vQqEin2f>!uSF>JKWr1XBaIOcOvjOSz#b^>+3uF5Z1^z_++7bp z#KyexEdva_ILQI~0^jF*;gKJ(%pV?DzBQKl97e}AiDtyh+wunS#S1kMrJn~_kxIn> zg4WF^b(l|AAFO6?$wBhN4P%&p(TF7uI3;!EdW&Rhd4plX+Yh6bmL?Be9R6modKo?W0Sb`-7eyz_O&Zw6ooQn8J z=7)mz^(P>kZYEapnH4;CWKL&EAKQ4lr}5{jERG+n4D!ZiqUF?Zu01qJH{2Jbwy1!; z6|S3#EBz5;Fv?zxTvdAwH`QpJ`8n&;6ga06Q8RVNe>NrHt`wN!Z}<4ay0f)o*1@(^uyHNkhiT-Xnf zhVUR*eidHJ(SBe+1_D6^pkDDwwHgu;dAeANQ{U8ldh*sDQH0VzH9RtwyimPBl+`C><=|9Z{kL(D10Pk}0N0S6>V_G`M ze>@D$#@;*V^!jJPkAIJxXD>3~l%EJ|j2d=;x*j`A^Tv)P0YJU5Vhx~ftJ{-B40D#; z25kPy4~#{f9IV4xK{W?vlE_`c+ZsOymH=3r(eqs&va~G7Z{J0M@nw(Ul$dFp1u$tz zu-tnt|3D3sjfr}Cc!FAlZS$b%GH6|UC_1ui;KpdF>A=m-c5(SC2zw`)YKXN>6=3Wr zf_c`|V7zT*S-p{-feiV$&b7bJZ>;eu=}T0|gQ7KDL`Ls}m0_{DXUwPx{xs+!vEta( ze00{*IlY*|A`^v{@*Q@?pcH?io^Oi9dID{=p)YbcGM7 zDC>|TdGj?qu9z;a@kVZH(y8)+;Z%w1^b~DTERvdEAZDE z*^&~C5Y*e&Y`1#P9wM}@#e+2%aaZdqXFya`Ay>=BNmFL!d`bN{4QfO$yC1t6?P1uU zA(B>6Tw276z2Ff3g=!tb8``1Dskstk#W(mBRn`Kj5Te{;2a!BqCn4~*Qg@sYl{5`P z25Q<>Y9({Ex-c>{cpWtS6x2giTBDjkZQ!j#((I-x>+N`dQ}5Ky9p@KogcR}uc#V{+ zXOHvhzzyoFT_ZHN+i=0`B61tcWu5kGH8uStG3r^3M)2g$Zc5G85wG7Uy}oh`>9x^-c^aR5Q=u)v5Gk^!cqeN~)#i6V;W;41?IjW9v0u*%ru}MeklVh+ma*Iw-rD zYmG%ie93Xw8o2e9RKdC#R!!yIO3S8{nW$zz9N|4u#e%yzrFiDD7aGVdrXO+I(^1Sf z(necPFqd(86z2w4ipCD-KU|p=jNZmx7n(Ruat@skik_8hR$FD4)+Xw06v;Sy5duMb zsHq*q)i`pojeot7XakL423y0t`(!t8v^FFZ&Tr1YqV5wBfJF=9qP_ElLcW@Fg^6TM zfLH|oN^X}ZHTMl3PoaRUBBl*ZP8a}L1qk6DD+m81`8XFM1(B$7CMCIs#BJgqnV>9x zR&7{=+4#C^!eAC?)cOKNJXDCZ!+AzfaT^QOC|lSsHg009hrAAgP)3-4-W05s-u zJh)>enj^&KSZWr^FE4|-k9CgLsY(8-h+*g8_$_HmNu}dF0slRgQqPDW8c#!U4ud=>kwHVEWpv%pptTAPq-xEGsd2HGPp;AR!GiT zTPoO3q>>~d46*O0ixm(JQVMRC%6alzNHX5w(>-^$D>MA2aa@OjLoB91VV=Nu`<~q+ zKdJGaehY885MPmapC3j$(X%X~qf0D{RPG}ums>p=2#U^mEWJKK<%#>8?~rV6(AjBg} zg_^)ta&CdYUX^dbxeSrFad_btpkpm z_vTQ2q!<6yK|t?-lV+O+(rVO#=x|w#)4<7wE!Do+Sc8xHM#u-M&p29^;JZ!LSWr^^a8}X5_DErLJ_7$SH z4ou(~OCd!SF6c+W9M(cUs3i)bfM`E261TBE2HhyCu!2ZjzU?Z542s=qKD|%i+wjzP z5-v|!(CxYlJ`#TVY)E{=W>l3}rY6^@v&WgVmzcHK@4}%hHvvaZ#oA~bTnu;PeQcWs zW~ukc)c=t5NG{8ugTFl+bOt>J~Lp z3%9v{xe#lQSScC|CKMGJ8jL{5FfUoK(A~UBSgQ*7H1xiC#b6|t^k(FLD~vL8!-<3& zes-LmOuyGPoqm6QdBpJ-+gxOd@`g_2E@8~>myMe_<&1O^;;b=L8_0qOxwnAEU`1uq zF*eAAEo7_tk%l*ca$It7n^MV4no2uzXU8-MGb8~Uj@CkjC2pBQ!qJ`$P(kn*D2cG` z=)}s$bPU-?QWV;1SYo6|)UaLoSoY z#>quu)Mm#K4}`*gzSkkjytPjO&2|K}nxsAA{dq>ErOs*`Z-;mN#9Gv6q$yL02@_){?RDO?()P8pGs-tqaZK4*Z5UtO~E1C%l^~k0#KwYjSbiX!~ zR&~aXGsrH<2l@r_q?@hhIR;z{FyAJSqURGo>JjJfEA3yy`ww>A2Bi`WKWb&2iv^55 z$pGV@UDR+3c{6Z~Q4opws8HGBGFS*ibl~?i>xuw(ekkGk4djKK7Mfy%$ zMS(pW*MGLtY$}W{$m9QkH3A;pMSkuu&P2?hyj*6P=Z{S8`{hr7TU(S{;Qg&-m;csP zS^wLv`pJk_!BBhvY{kFx__G>rqCBBCXLQg!l1}#N;Wdu?*l$aDv zV9WzR6(dN(x@IG3OEIOiyPi-~|x58OZ_uE30S9V0x?PFA%VX28&FiC*Jazt?f63vS0-+S*pp2!7zVR)6)k)UX;U~x7%6__rWqagP4iXbwwqF`m z0YEl2?$G@UH2>~2Z#A=I_B!rBm)JIMRkiqD>@PVJ9U-;80)^Tl1FWPK&}t_E@k6w4 zXTKJ06NuiYyM)ho$xQVKZ=oBqvb_Svgvu?An*}|h8*s8>=UieOp&YWz6tH9&ds#uU z23U{8s?A7FNk47l-~Y;0FZFwRJb&Yw3*zs@%iq-?{!%G&hW5gSMph=a#{aL%lC})+ zZ|)hEu`=c8px|=`sj{z97ontLT+Aarr7~UV8(vPwbR+px<%0tI*>s#Gn3Xt(sY~#T>BE#2|?p`d&Z6ll|=q!V>ssqPaF((6d{`l}wh$ zO{@Pg5_U`m4^usBJdV-{ec1ASFRkNAn5}%CY18?h?ql@KkYfDNS)$xj?%A!~(2xua z1`3c+vC&+SjDty%0dA*4eW>!bJ#d2K-nZ zf2))uaM|@D95pgowztTksas9x>Ak`S`w+z&s&X1R-T>^FN4rHo@!^zWF=l__^Iw8= zzn4bJ{{OfBApSS;`rp{}UoQVg^{`mg=Ieq2s_zwe1&$bCA7&Rv%RVWU8I?62(Ify8 z%QE;mdDAuZGRCpvA*2<-mwb=lnSu3-?7PAHMJ#g;yzoAzpVh4I}@{{5oZt*?^4`^f~iVSnW5|esEB812jJ3k8ePP1?tVgTDfXiM zNP;?PqhBk`uIt-r#1GuBA^T3FjEe=1UHdS4)VRrr#As^92C^2ln4W6tT)zdSv&fD^-z)^=GuDp}FKSF(;DWo$7e{*pTOjm-V z`p4-&BfyM1#|U89N=L+fRgjD8$@Ue?17Ph`$TrD5sZ+j&t;aa2FNY$+fV5bkU_Cko zXA3ddSGzKv{n_z;7sT%wz5e9FjM1a$Hcj~Lv|45_W!bVnK6BnsgG=B04Pi##of$R} z0|yF!?uir5kvn0{+FHe**rN$)tm{GQ>-<`%wu(z;^>DnPL>AKseDCBf29f3tB}NrhL@#~S;kHqc&?u098u?D=46(4g{AI?gxhMMvR992CrmJfnZoPWeE;2L3zq| zyg_hpkVg0buF(Rkdoa+{tGwgT{5BJaK!_?NYf|#vv*+ri{kxZI&#&iaC_#aeA9Mb} zS?xzjD)~gk1rY5KdJkowZL|KKIx38L$Oa01WMHXiIwqxl)*5qJs;n4TF=i3+Li+aa zoAp=E%@irE{L}=xM&@yx4d!0&Wh1kiUp)M__r&?T;E5u+?etpb^uR=5r<9){xowif z;hf?(Jiu+{o7)nbVONgm2PB7S>CX;#9`Hgpwyk$FaL=@9S%^Okx*bVq0lJVeCfy|f+_PSq+FCA$c40zd{Cu(6{8GtufZ!f8-3o0pB z0Vh1nql!#d8?gG+?i*(s=sMr+?Bjl%?aYwFy5*RQgP4hYa_Fk|1E?6;v>)&iV_`hJ zDX7)I#zrN^culfb70_JtvpyY1dcbio7VII$Segqr03hKl&4=Wqn*oYNXq)ql0Eh7& zz((3EV>lI{x_s4wGazx1{f*|qTtLM52NPB6fc+Ukth5lo(A}>RQ_JmC0I<-(N;p8Y z-g)Tdtu{H8f_sJh1#ptgZR>RM;dNW754Xf&NJ(x7)zsVN^@Ecb?`=b;0BWUCQ-lLI zeG)Q~2!BIlf&s?%sDaEjg3FMZ_tmbfG)JV$9%LK!MFh~zw}LJ*vn>sc_mHt5I7fmS zKKcP7sRRR_kddI5MTA|&DPDTeC|v+K_4l;K5Cu$e48+fRwP}@r}qnBMFG=7$!;u<+`cRURIdQ9 z#y&!A1yPTrhY0KvT7H3m4VoL(xms%4uQA_+j;@D9_#QHPRBqqJG)NueHi3zqyj`G> zU#bxH@v>Q=pA*OatiMe#MI#J(N&Zo86ZQpXcb7@cjql2)vP3mg-hq6CWD z0zFjDN{^EZ+-K7vIJ0?5Nh%2wz^rE2NBEg?-QItT4_-YMU)RS>(olUyFV0cAH0Sy7a_^P1|gHY4hm2Y%q zqNJ0%4~{Cn%M3}K;XBZFC?v@HD!Dg=WX0fM&d&vtaAVJwk^}~EYuP8lYmYF_R@tp=sZXB!D0rNNd^@+Cz3b1^8)9M|`ruGE?`NxAnQam$SDrTw z8*!aIiBwu`OE0KBB(Y%P-EJu?%@S#%1?!}`gMj7Zp{E2?XD=bi&vnQ_h{Y<97G%H^ zO8H<&o{M^F1I!zDDf46IsKcp)rSPSl>4V4;CQ7A?Qjny*1)urOu(T|yiJ&?Q(lczQ z?!Y{hQpyIA*Jz27-T`3=G^Kh%Q`*6aG`q@Wv7Eg($XW5^k1??2z^T|$yKL(>;1m`v zaC`cP9D{SibMk&F4q5x0zhoRfzQa{nTD*WDWrUiG+9KDmSaouTrAE~8&%J;(Nnz8> zZrebUMKyd$h0lUH^ca)`%tdj+FO(-|v7%InJgh0i(u6ISGDPk^;r!vy$ZK{?>fh*e z`@fA&)_;gAN z6T*kT-!29wiX348wh!Yl21?IZ>6>iV7c%s@5~rVIIZaQMK0GYB-JL!VyR4>KbAVu- z1jjSwKNL#P9`OaX9F%JTpq8b|ScGh#(Z>Zw0!`1@$j@47zD=DD1FO51puBj3C!jxv zxASw5cxB;kSq#7)&LY!1Lf{2Ea!zPB!?j6iY>8NJetR5@gQvhaUi;&fE^r$2sL_z# zzN0r!ZiC&8Ic2YX(*$!SqgX8aJ+u43exn1Um0aSLd!llb2cR5YtvqoO4Xy2N1pD>d zhD);r{s*S(JG=;|GPQjK7mYRl4~FA5r;SnVA`A9$lZyTuO0|)%+ZZVn);?N}ZRxou zCF<7>j|R8k4zQP8vBE`?AmTzeuvD!@{`2g}6Vo^da9hk51s_4Z;BwxD5gC*e5zTuq zF|f>ZCEl5PB6xP0$tK$_;Gv;yIWHd98GF-6Q|7zY4?86lLaUfyCCAd;Mt4+16K=J> zPY)BEWt)l37Rfq>7-p>;>eS|~Q4Vl;t2Krct`=biEdY60g?hH4ctv}$0n!r6PweXb z41k4>*{MxfHXw*o)Tp6LJT}K9tdb!y5C`_;qh1BN9@c2XXlS-=?49Z1-4LkEIC7Kf z&dWrE6L#W}+~#ud<7vA*8#j8!oRS;?scE!Jn+;b);=;`-oP2Y-37Z>e%venW_I4Fr zJ~(1mjY$2tq?JJy0P#s{6zyb=Zrhtl+%v)0dbDhGx}!C+N7)6&rtknMM(oJ@C4n_R zpeVig$ug{39+yxB2sq5y#Y8IV4$?!s&8deeeLYKkO-EJd4_@A_dIg09s?8=8A#zA?+&y9YcAVFOY$X zt7?ByariESQ*L4LhJ~axauy+rSaY!B@rcZTW#mQtf?wt1teQ`614$Nz@+DO-3-*xe zH$Lki94-oVIk?tDDu2#Hi1SR7tP}SL1@3VC!KbhVmMHCSeCGcxKL4)G@Gt4xe`M@ns@v?iaLW3jM+>0-|;+6c;#ik)K5-zc%LnNMn5G^yv2D%rXK ztZg;#DSSZ^6B87RfY?T>T6ZjhQ>CO9<5Ia11mor8z3Bx(t^55ZENgP?I1N(Dmo+}u zQ#>C#x7|N%2s{qD-@ecyJ$j7xosfEO2hifC+<-w?Z~DzO+w$$^4Kdt~BSGmO$JR(b zmxD0JaGPIh&|f+3+HR8U>3v+e7_h|LH={(YpY-~0p7pNWxa4#Q5ZvDg(*JbwqsBnE z8|QdF$ZUH?e?s80$40n6-Qazd=g03WM8-e(aIo#3;G`h)o(XXD&Na)4yD0)mJ|00J zeV_f(@^VW8CE#ih{&I`TAz&CNa5OjL{=mvHb92(L`JR=jZ+|ai>pL{%^x}@saCjj0 zo{_mD`K-&|`OL@RXTKZ!jB@Rn9`G?)*u&`Omwdw!&@Z=fV8f@KbfraB`xOoX%vhhZ z@_aiEIl5dizyjy|oR0@P<5#!1e#6Yd?vrP;M`!o3+Lt{P${X?ixisXMCd&NQ3^_Md zQpUPZ*#Wf~aKEmRjZPOHn;HztTTkp?JQ?TjJlL7Mk1oE~!*q?cGK3v6XXqO1h+)oc zAo2Eu)jupbk5|IET09w#w{ey*81$>#yTY5Ir5h8UtIbq+QaKfDkADK^9MZ0WiPo}?ohI}FGBq_gwlt^}6 zYVEc*d!dh@M3n_Cu~Mu-jxFz7&mm8CpZmt=0s&&_CL0+%CYJ^0mHaD$rzn_bb1K{O z5NJo~dt%ra?)$ zr6a5eDuwnf9oxAAY=={*P!gsP6GHu5CLpnVuof)bMGZ4jNeKp=^jO%n(JWmr+HaeK zst1x@5A4)?Xc5=Iv7Z8MoDk>?V(q^os+*Ntp=flCEj2ryAP>*aO&sLZgK(!qnrO<0 zLE(!MKWW`ZH{t8ut~CcPj1_?{Vn*JzI`l0f6C6P!q!L++;j=mnH|eRTn%5ObYwb`w zQ(+BiwbkX|$*dB*yJVJlRQy^B&VWaz$qr5=`8Cx$M5OcdgK-Tcy)2YgH^~T zABDanKVB<)y{i~j&Lp+%?UHs%Zws6M7^N&jS!G8b{j8qb_l-^%Durk=j|z114aL5v z*~{Cx8O+MVR$BACn+hm?i*Y*53re;`nDwVj;bFm``Kk(>js87=3G4M#R=I!YthzuP zS(mMybe!^Wz0bZg@;RrsJofY>Z@qd@r07?_vFA?+;w`=dEPKEVELE{*&~gcl=gel3 z#r{fNZej;|0kW8ZDRw(uxQjgSM)R8%@01JbvlTE=gNfq8?1YojBdoH>*)kvDE5R0dDo4^IxT9c{}&%=JnFG4b853C#MZ?S0 zGo=ouBR!qxv@fybXX0E8d_etSf~p5uxj6s(SFD-?ec*=9cy~(9%!s-*sf_|d*_D1p zI>cmq@!_6Za4K#n&La5aTk;U2z@@Yf8IwkX@1()B^`h)cjPJXddf1^@>&p}ViV)s#yN;>SrP<9EajMUgor4`E#DO}jNiDif}Ky7a+ohzLeLb66a zD*v)Q(7hzoS4Qa)LrfGOONA{EQGx*a&9X~2Kw2({ym4lRopoMRl6tGKy)Be%>_&CYp-UfWk2J)d5Lrlz>;!Txr8TS@x3O<>(#5t^_viO{+WCim(vo^^> zGUa!MIZli=X8KKU<8`R>M^t2^W0&uXFr5lEijd7A;v$AYz59w3BGL84%)pcl|xt0#>bTF<0hlPYHGa1O`1<$i(HG`mvY7*Zk2$?8~T|FuN< zVMQ8udc$IMQN_Rq_Ioi3DC)e6rVODi0scj}#)pT?;ljCRIr5+mT>bTgM{cHwbJ=V5tww~W2D6|xE6AF*<9V0A)?$~WXBNdta8~=p zIXHF67WEk^{W8=05Q9uud;#BgS)=sl&hX{nz%tvH{FkTqZkj(I5#S4-r$)vCQkBzQ zkPk8+;oc-^u2Ov~y7ND?o2_N>8EU^peFWae2TcK4rvb#RLweai=AiBJuP@D|y=5qm zFyfd>u(OXD*oUjqzCZ}jntMY{xkFJ!zSegT!sqXj)@w~1<4XLTORU8rc~d;t#J8mI z_ap3xP{4$uK0fy=@_3P!s~=PRq+&f(^+ z&-gE@Gm?zNw=B3CH~av-L+H!#8GIx>6tQoW7vhh4PmI&n9Z|gTcOXP~C$J;WH4z2A zA;f56JrUzSR|xc!?H3O^`L@a~1(_p+-vovsrC@>6RZNTj4muD=zhXga|M;!n0@YDn zX*IH7Bm5(XlHAu?!o1%2evcz}nm<8d4t2F`OIX@wP_{j1&6!EQ9i5zJZXMMV5s9-a zm-d^Jq``9eVRG{r;wyS17lwZHQQ>cKWht$&^$UOzi$Z=zNQ%f@H}q)~QYHFFyMu-3WS=~YmCG=d zl%z*PPjq-94NXNQHC5(9jFU&h61WPRi`taPsR%|u?Z*X%Vi~| zSo+;!E{-{^@J6MKES4oknt9=QVFt$5Gs0@_u@AOZ$zw|;T^Ol6l&4gbYj3pT+Ma+* zWctLs1^rk~5ocb_ns}^qH7qhiBPbV}qHXbCvMYRPvP#oa{Zx}yHr|+EDeLe9YRSd} zjW@r*sjxK77=ywpzxx4*DX=3Zg}pgMlU(s`_6X%35#7kSQcX}$(saP)GIOJ|lgl`q zz=ZVE!Ed0!I}=K(rGadTIFJ3*1a2<5ci1{s@tbnXTOGxsTN_^gz^CE1bZ^*}( zP-ZyYMon^?OHQZ6siZX4QXx>QZP0FRyki`aG-{+?BD-n;He6oxgu0eD>byRC0^r=} zMXG>2uKk*pC{n|jZYg$*yHqPFKl(DR_8s8RTO@dmvn^K6;us$~DkdL$N_uMn(YI`L8cU+E=Eo+SB79!zo0p!=m+R)tzKSy3bO{Y1btf3p42R|=iBFS+4L zoSXgXMxgCH7Ssqb1|JUEKE+F^+FckuLo!?*`@Fowrb>SNj~IuCzy9iJ#&p&exBec+ zDkb~7I^*BXEBs4J^v_J`ADL0Iir=4Z7as!mdNObWA*XKyp*0~*UIDY{$RH>l?H5wT zPeR?dtf4#U_UYacrNnsieTW+oEIS?pPBUQL<+trizLQP2Ya72`A0H^a#LFtAjMcj_ z5m2(!trg#(7oZ>Pb0Z2N&SqH(zo9=FPlm9Q3@FTnIAGl~qZ`vQKXk5Fr^vHU6|yb7 z4u(wS*+BOF>aVEttpiYT;-?p{SJ|Nn6KQSEeRdS#vqL=GXyw16| zitwa8#{@hl*W^^gAsD5Sx$*nF7p&5D0~ZuJZeL4zDB0C@23aa^!jJT#jnwu*Z1Q9u zjQ+((qeud<7z%3UD;7$c6tCfmD|gkMeTM8flhQLnu5q2Hrq#5i1##g(U{(c;cv~9=(_hIqdf@9b%KaP;& zfn$QwkjxxixF7t9)U1uJz&l7s*6n49j~dwF-DknF+Dln;EQ zcOb)++iD_j%USeGh}tO$3ENECLGNhNaJNSq%;;5%#JSB);hUz1@*Qi%`i^!E$UO=zFm>3j0el2K(sUh*!HX7AAn|;*@pn#4DG;029JAF%?rA9)MHrmwcG||{(lKHntC>(E*;c%auROAkSsLVJ z9^<;Lopx$cnlY6ouBwL^l~7`u<}5hqYWgzSHJ!uBTIqaAk2-t>YYspQY|^7ga)#Ve zbt!6nANFWRR3 zTXHJzzn4E^FNeXiPKDkjZ8mhH1%DW;4AC;;THRBqL1rfJ2kkcD_MMM^Fse)=`TV3~ zh%*t7vS_sut;Sy&caV$3+k-c>YsafGse+VBZa_Bkk7Ap9Xnnd!FlHDyTeS$mHKu%L z$l$-HN)@FbnzYyj9ku`!*#QS(NlRAT!K5^h6`AVgW-mHQ3D{^g%8QY+YzhFs!d^{? zdAg|r*p$27)QiR`D{3#y*;aDL9F^77h9!jb<-r+R<%!cZZMy2$AtZqy&GMF;DKlMS z5)8t7^~VvgqFhGs+{x#9fxKWP#MEMygdRK9Q#w3Ks3Gb~1L^{PZ31yrSz&IR_B>sP5(~QV38&qh>D(gE|Nh1vxEsJx`uF})X>?1w>p%=VAm9OlT~U+?})5O zMJhx+nL=3?lSxh^Hh?-_g&$km*9%vL54>EM@F7hMH(UJzY?#`Q=n0qG<|VhZ8KVrN zOYK+{r`fzmB5Om9PL3%4dnQ0;d_u)T9&y=ZzMof}@dTx*J7w2+a*nn;@Eyk~D5n1N zFM7yy<&<0aT|2(`+uHHpPI>)ny!aPQWb=E%7}2-6vFj%%sMeuaQAA!dA?~6Z5xI>r zvShxLhtv&B+jiCCsLP6V4Y+SgMv^FspTEF3r`u400`l6#<;4{L{p7{u#jp3rO9+2n zy&ssuT+9=wY;k|RdOY=_RLqvjg3wWWJd1E(pEW7N;}XX|2A ze6~>FM#_-Y(m6|(x99_hYBoLY8Z>rER!7-NFQp1)Dl#EmmeGdQ#Hs_lYL~BNW$XQJ znVXf%v2B+aykFN-7?!X_ytmM*KT`||GkFZcQi=w!05sLRJeQ`~6?_#}@#N=H6NZ=* zA46J&7MHpPC8=MnKjz$u%`G&(Nmg!bbJcXyY>Xe{*!D6z9f_i~RMWb&d&}a0&tsBA zCZM`%Q_*|FO@PZ*VrqzO_fjfh=U}@7PGH5f+2uMQ_@G7hDY`wfV_#@+u=OzA!_{G5 za1yJSD>oOz3`se^pJ^62=)nLcmzcrY*uB`#W`~oHO`B3p0otF-0o((7*JYL)^A-?g zdZTLe-j}cAXwglZ#rrOGi(q5mPoIM<@m;Vm9nlI#zbemUbw`LS^*MEzG<>q2$bQA* zchP4!6R-`qbEE|#&bN49Sv!3OIcpi7=mjqxjp7-kTDlvB=r&)KX?Z0= zV=z#OrWBHY?CQ6~*#e!fTtsrP|8aWS;TZ{ zKpqKtjQ7{VjdJisEWqj)R!QOx{{Eq@JOtq+<0 zDaEY(8EYO={ZlDX(~HB9PHlX8XgR&V^)@~KcCmdekpnVT|p+$MI|_god&Vu6oo zxSw$a#d&CXa8k3yR0$mNYVp4n&h~i_U!stosG~JdbwZZl6^(OH0;!alethfJ_8G|w zE3$sk)7Y0G)Vm^O%DAjT9ubYePR;QoVQ=QY6pl4lNSEF3QN_ByRXBe;67rvc>3_EA ze+is_T>po@sZzC3#!*G}MNfbWhaqYN16c&GA|vgkek=|N1Pd01ZA2E$C*HcKHZ*LA ze47vK8T>6tBHI^} zPr!n-8I}*yps3TUgG#Vx8fAKKtzi&2gvmdQoJrTPhmNDRT00KFP4q<;!M8%H9Et0_ zq~A2m%oSG%+hzH}G}JHw$Y`lb@OSyA=6E(wKL zYy`Ao>dOt@V#*nCV68bN((#P#fzml5V)nCS>!UCj7nv~v7s<${#t(6hsejIXGf)&p z4g#)jR2N)5qk{r*=L?)6^A50qQoqd!#@kH3A#){pmhQ*Dl2TahBX%V#oh3cBW)G+# zOR6OZAld^W0iNbq&hjq~7P){vJ>iO@eydKUKBpp6P0XN_5ESxE)cZi);)557rOw@J zn3!o7(Q2aI+WVN_GCKvOFDQ~c(ZK6`2sA*RwZJOzpKBJO5O*omIs<)RQ-Rk@fT|lL zZfBVSF8r=29d31oV96ncJmO7b$BdYRe(OnJrRA0 zsLtGM@OvFH?gzvI&(v}`Gc$+1xIEHwczz~!Tej`cewo&#CzR(l)yw2X!og~c|Bs~{ zQ))FSdiNNitsjkIzciL&-9_R!PzRP$Ev@PqdW>}pg4Z1dTK6gPZ^^tH9-my<4dw5-Gi284S@~mrHp-rMqD4Dxc!7F-!S(iXyBT9;BiDABVEcS$XRlqv!vpAyWdoMOi!mFHCi zo+=~8N7gv__D_O%bg zP_IgS0yqetC}Cmm)|y#I=(=%Klq9_fGtC2%iHiUABezGgB4`7f)GE(+HpJ_+FojVR z&oB!+Q1+KW1St_W#~NY-US+Pzf^CQz?INJwkMI_8Yi%c91E(hM8bZAIS_kaJuOjpO z;E5yb?u!P9RNkcA>;{>+doIDd{|2|jGr>G{>m!?v8|0+*5wx zn*Gg*voL?hi){aWUi^=G*FUvglAaeXI?Nrhf|!CfqWq0+SRE@_0BS~3DrisKk1efO z9k;2U)4elFi3#TW2saW~C)FB_DWF%5EFFnvIqBKyUAaBozkq53)w#jr<@RG@I?|+w z@y@-dFw>TpLf^3QPu;_^0G;r7nsE2n=&ZcoFxTt(HNo`Rb-V`nkc5KzR#5{h3e4Ct zJI5HiRk!p7DpS~{m*(Z`oK+}OX@>gsipLqXQ_5}eEAm_|%LSipgEEuXnOc4Yo=9!n z6>FJDR<`v&?l3+a{T}7IEpX-(X}^H(Mq#zoxzuEfrFHnK1wSBp#js2rzT}plsmERS zBC(TPP-YlU)Mn~+()n5qn>59-8D~=dCTP-9@>|+D%esOLU=oEEpt|OvVsrz@K*;fN zG=?JrGDpqbzwJ`xFm*PW3^el07E+SnuTFW8p3Q zE$w`_!2uVac+eBpuHFC=0`-i{Wcd&*+xHD%Z0OLQ?*JPa3djoHTpIns^~YqI^NN@1 z?xs3cpfc{K&qJ1!9{{l+@zQv`ZRZl#UC1T|+-l59{64P)JwslG3Deflbi>AMSs}RfsNk6eWwr$(C?T&4BY}Daby+wM3wZ@+7=bM`rV^}T!D{ptUE zo{v>EXN?-OYE(9=A#|LWeob62r4U4x?{8>#i5zmSNDN6Y#4gMz0Ph-33doPi#SwoX z^#0wy9b!8%q5A;=BL4{O{>a#VzyJj!OFbJ?YrFqj-9&>1_+tjexTQ|1z)>X$d^^XK z5?hH7pD?*YpXTEznXYsIn>{3ynb0W5x&> z_Q$hE-JUgWkByEdtGsyfIfY<6!YFw93pxpoaHZEm-R!6Tzu z5rzO`nZ?md3GxUCfxbeas@rV+#Z^jCed~*Gv_BY6aUXALN^$jc_xfBNT!Q9CTa4$I zg9|XP#gd~d)lbLURuUxW$i^Rm+n|>jOq`1D>23w-s~Y^@Z~HWKBTc44E2i0$dwIB7S3uc@=_p#Wl)*I_oE1y?K}R0V@| zguCk?i905H+}`jqPmS#T%odWxOMS`YA*0x#hL#0NPK=(|Z0(a%bV--hc$z-Csv}4z z)4y|{H)9lf&!C+~DsJ~k)t-Dw*t=rL=9IwT~Fg}*V>9~(NzFi$7 z#>3-O&lp|(K>79^7p><@V;k=;_Z&VdS8ZRk(^2TQ1s4M{&>VAsOiIm|l_OZL5)n<+rUlonZO-rLmC*v1tnf4iR6J{jC+Y0r%1APnJHDeo%wfiPNlk$nIxK#2&X7Gqn z;)@MzL~_iM>+{@E_lpZ@DbcH(R77g?xS|0+i55>9-HT8*9#QVdlZ8f1bp;wLZg#1N z9P&=|rz)2GBir}*fc`olBJ+`E-sj{`A{{6M0`vYz3D9Xn5HlisqJD(bkp+X0dr(x= zW8kw=bF#6cJnSV5Z1YtmVCa~nd2nAO!L>e`~uNl8;Pf@q)2c<2-qeSdFSOP zX%^H>uz_A6YjQd#1ZnE#UX8XvY#?;lsSk!F^6=+CfZ4A1?`fpi!sad%;8af)D;8j- z-fs7oxg=A0kmSd)l70UP^<75{a)9;xK(o2NkYitj6tlw zN^&Kns)|LS7VAg*Bgf~J{V7dR)0!V}e7%UD!QZ^0P(jEwFo4vd=yIElo2?1~C?n21 zpG8_}E-N8H9$WNB=(R+73rYz=PBmRyZq++0Ss6~PXtLK=w4d(rb!j?o&h^>mVuz_s zBm%6y>IDod#f&jXbLOuXna%zn#g$sWlJ|6vf??FyVuU85qtSnpqrU{Sp_B~we19bg z0`EqOpNuW_pWBCqp~~xxFc(dU@khWSv&usV2y`x>`(#ac~`B z%mu}No{r{|78nUFPHp{yES8rDG*>oI(yU}4yTH_pw`L>;b!G5~TH!6IJWZ+N268}5 z0>)?;g}jOeH~FXog`ja1HYR}+4CGlWmzJwWHC940{Qy;=E+@vrd+X>h&iAk+za~ZS zJ`C)X9r_96seM(z+)I~6@T60C!(K7{?vee3;$Snmr&LF7?h3FiLj-|kio4y$Scd;- zK7Y>5UsgN$Vxwvb-ELBhPkDe@%3zZKybR5fa>YJ|Nw}c{v=jkc@5^yCbhq$8uR5m3 z5KUi9)_Em>iwm<^{|N+SoKG|_$QV2c$S&#gF&VJKpxw34nPCgGVhXa zZsO6NT6d22#qXfd=;c(~sxtMTitz9x6Bxwb>4d9O=0|Mr_ipBmf?CES$JxivNb!dP zUoZn+JjAk}uLIf#sK=m%KC2-~5~tx4>Ar@|KR&Tp4C)#*@dqPL?#7*@t*Tn3|n-nr=|4K{Z*Otil`}3&Iy5EGJO0XH__O}- z${!PkH?V)oVf6pa9QMyDO9JDAL1*sQw9Aw;IM%?>D6@$-((IgZ}jTO5&s9Qe}Xa!fNcu*&oLm z>zgUj*{OhaqdIvhIbh#989kRnkcU@OGC7|MXA!Ed8UC z@{AF5cWvCHU70v6(kEV^OnyFh47foqN!<@QW*nEAgKuBiHm*+{@Lx{+ehO=)*b3tf>-WT#1wp5let@uwEkbB^EcUZ^(IMF#~Y*8Ymt zmmG-vUHmasZ!VE)racfl;OLTJ<8+|IW~6v4!neU?K(-Ne2&!k=6BAANyL(C|QEHN- z2^y?aC1b<41!M!!b!sxErLC;u&m7Z7AW9=96fBA@?RG-;RL(`PT$D_VQ@d|UjYFqe zs;sR3;=gVR`j862d2$}1YF|e zqX?XR*xV#~Zs*aA{q%CyKAoABiWVNlBWS^wWH0TL1)s*!Kg$_%cp z4mmF4(MvNbetP-%3B~`*@LAyiOw6mWZ@|C|JTOMoA^m?su5GlXuT?BB34)@V$ zBT`Ukbl(`DtCzI)5T5l~hju2Suck&y;ldcolj@k4W$V?(Hmr(QkWaI$)-I&Iq$(IZ zlS4QXD!r;M2DaPLCVl@-DL^(!MKehr905P9?Q{}~aq=3`;BmVI)U(M#;anU~yQULN z>YHV(*Apd64vA%;;*_Y?~irWzv0q6i_nr57^=~22W-3HC zt&c7bqd^`Rmm&qS*WkZ|Kv61D{}KYt6f#2nsFdrwqbpB7x;zwx1q0kv9-W0L9tSIC zOUb!ZcbEVw+09enHba^&f72fH26(-gom=+F=COP^g3`i%l}5CoD8FM(4x`xDTsqMRp! zVVQfglnk$Q}2$DsJA&}NAg`0+aqVmtioibc)USNBi{ZYyNy}K*)PWFAf z!=B=xADHsVx3agidwQuk-U?=W^=Q)t*XRO;#?(YTqFQFIXbr$-GOlp3BqKeO5a*&? zlyec54^$Z7SntWIcVSvzT#=MUvRawvrRxfCddVHcpP$8+!OF>M} z^YesezE)T@DF`np={B7ys{eILIn@1$<(qABwPrNkl81T(67@uq>uJJKe$>gggi%+v z+P+QPCDwl*RHN2h3obM4)n)coo433bwIAAx zwM|ro7DC2yb*Vqn`s~qZHT47ORSzK;DM&WiOwH-yW@4H4Dzj=^@J0xkh z5AQbpPkZ+tLzMr!ck9-x9tkwO9> zfY(edBrT^Fl`|Y-=$e$?kAt3~uX8bVquO@yenK?9{c$h2LA@Y>rZ}QbLFy>zM`-%= zn;e4ESLS>hqQKRrkc#Laa=cU?Yje2<;WGF0_$gl~s)n-&u#P>&MPHN{GcqlXm`bZs zY~!2DXro2c!lN&FLKYRX=glJ&7wPZr?Rz`<{++>T`5oG1fSr!qM}iOEIuU6=u3`4< z^BmQq3&*0o1fQh7r?_4n1iQ(&x0zv#RA2FYsQ2Q$A0VU+PO48LOh)D(PU8+?;F|k> zkem(6pFdPyy+RDVQ30Yb`0wz|f@0J8_7kF76&23WUJocbIlpqK`)Hx^8PM?9_?*M5 zkz9A;KYVMr@%!Y~gTA@DeE9a~|FmzJ{s9RUG(I5VhjdPFdF9I2i~9PzCpenM+&eOP zQvI%2^al7=;HNcGX>Cr5$feDdgV&#=mhax3~IBGeF4z09OPDC zD|U$*QZbyDsF0d#47qFe(f+UDAB*L?L3-|fMk#UbMsaOF|$ ze8Hv+1}y_?fH8?RnYyiV05>!k(``@7XCKVwNt;n_2vO<+dcO`}Z+7gbtZnV}a0r$` zDC~oEh*ois(2KMH!1OMZ;kWamW_5+yNwGaC_gS&NF@m%hE=k zO--{?*!CR5Gs;@h4JQFw!Ovj2@Et7MynKpiM(AJK*B6j~VJ-TSitUn*U=a8tza{a9 z+TdScEk1pF2Rl6j2T5xKb3J_vBN_c~Mg|W5h>)1EzsSyjiP_~v^@<90IF;P6Cjnr; zvQXJjKLjN8qy7NNe!e1R2KtoXy9!N6YL8Did>cZoA;I{~ra(zeI^#_TFs`oNZeZ0B zZjgw{H?lX?owAEc>P=jM!UVMU(>RNSn2%cI6*`_KgFB6N>n4NFqXCA+AmSrCgir-s zdt1?>B_;?IyT0OSd+VZM6y~+f2h!+R#gld%-(Vi7OjM7FmrF$NmC6TK=9e2KQQ~ey z=!;-Knr65Q%(15)+&IEB_PRlZHH)rDws@gLSemz#k8dxYOX=6U__z)@N z12vyk`?rm>%GV@W?pgI^3mmbN^KiC%b4hzn9OdXdo+R#Y?PjDoAjdL6g>y>_D=t4S zL!hW{A%ISbkaba7p##iNL0?ySNd*;d;4r%HOK8Y`yb-!EO8i1KiXkXGzprX4Z}Aqf z4-c~a@i=q-_dWQVrv4ADqzuI~>EH65iPThI6f1yoaaFTx2Z8XtKBX?|0`W=uAIkh} z$%ywgs!v&~*d%yhCiV&8`Ec8?OA$tKbZlccz(p6ddN5U6vN5*2+ zJX?$u5shV?MX8f>I-A_PlYOA4rIlFcS$XCxo$0Q}Axr9jPo-=9&{fmglep@euTSfi zt7lJL`-#Eqsv1{$0M0+Zer>Z*^);D+E(#Qpi&pA3cS4Zo8Voy4wHFasf;e;gLm7`) zTX(55G(>3^JyG+$GuN;wMqV>8D-|REO!aIHm3cjmPcJj4j7^_*pKwAEk+i~gIdZC6!GrUu&#{?ydStEQJXwpl&|S33#Er4fx4H+O7=~2< z=lID%Y4;Vx%W~y8>c~oeXNW&4lv#!Oo=d~o==KeRr_|>_UkbuQm!|pyItz!$`=@nb z4$-N$vMbs^G_WvliiTk^{zoU)3`KowIlQkgfT1|yvi5_QRikjuu%GmFqi-5bbRrYt zU=AhGgL}6XpNfmZ_+(^a-@JBB@4zAoql07QA3@3)?fnFgIAS*-a(DPGQ{Ads~9Ir#*+qVU(C&%+p@u4|-P#;=N`Pp*1@L<(G^X93R$II&xOcyAOc)HP4A1Mi-$sE&}971Uac14^J*4ZDa3sZnu<9;@^E@yCfx{v|d zZkIkz!^o7%m1*3jl#;O{LUdYhN3yKls<%o=(A-=RGl#0HNRTL$ovbFBOHp>~a z8O@!N0$ZpIFsppJkTifoq)G#(4112eCRi0d&R73A&Q4Q*MmMcIN%Y2qeoNV5VD`$r zWxKIu9JVT|b5;)tjv`y|pMbFKi>2sjfr8iSK7D~keL5&b(`jj7d$ZNPHpcxQx;{^g zk33AYhA=dat!RMnBITq4-y*<&gyxW>nd6Fh@Jppw)}LJ^Vv7NfRY)>us?)XiWYBKH z$3~k|)OVO|eyEaU2X5IBgcF4?GIZG*s$|Xe)av~C|#S67I7+sE~od;d9OxF>VSS8AdGvO_Kg1x++w`2Q; z7+!hLmES&-_UN0<8`B&^Xaen)nBFBkIYjC3vuv;$2yBrleF{ChK!3A5#GWvnWC2^e z{1;KOj69AA54G+lD3}-rQUV>Onp5;1#M+NNvDi9izu`7vd2(_X(*ed;+R&j{n!nIN zTT+fV_y;mj{9q6N_3PK4+T8vHI{2Mi_#7PUl&vj3C>o1@EQ@JV(y&Gp`GAg2`mJkl ztAxDYQsbH9d;Kl4oq@>2mSqWwDj=?aIaPP=q9s))>VFzEoa-Fg7p%{!D1ykFmN5d& zj+lEZ&M{yN6i$g3APkRaj;BAiUwnv0-z;pvdIQ^Fl2G8(1fXh2ic6PkFQLbtF>nKX#jvLXg7ty^wmPM$2-LVuc!sF#+i6rY)X2AlmU)i zw5o~X9*Y*U{_W%DpVh7#Qu(U!zvfuc*i{rv0JQ!H+;=Pc1O1Iw5kl(~a77pvQeuah;qNBt^LHD7R z25Oiu)j_hqyU`~l*caL&$m=iOSb zN=-$FlphLEX)m^+s_3TZR%BN*d3Z`8I!!rnw2*PBo0HSlLM>cA{zemjFI4|zXxM?{ zUu-UW&pM3>x` z9v0TPtB-wQY`fG4bWM%Ihs>~yXnIF^E&9}wtJxX^k9wi8V5F(ncp3yf0h_Qft4^vA z!6Nsq&5#b!wuuHU&;1}?rn-r!z+W561qrg5NyGx6-8t9XQ1)GK{~cbTvhgB}G&MOR zBSI$;1F-WhhXYU}XMH~JDW5PAL*4uYg-UXSgO)wvyH!fX^wr2z5?S=@hS5F7 zP1sZCN~Ie7#|~WG>yImmUD|N?eEkKcnt~FX4ZdTIU2py;d0UK>teE!it1wUXQ#vo8 zOH_7ZeYiAmfJ@}Mjn2=OUCtoCxR$&-r&PthHNp;>>oi`*xaQMvO(gmeWEqC`@&$`?XND)QO?|JaGIa)&C z?0JXXbZnR^SCq|4>78P1xA8eD?6)O{110pFBmtijPQa7^Tdv4GtQDJ}$O)_j0Y9O% z6)$UeAF9n#*5Q3s%Lkn3Lu^TsMoGX}Yb&EUYMM{#`-4FJu$-3RR5uLNg>|g(%~%E8 z=qZ?{U1_Zg`Y~7NY2@S7LyvbUC(>yV1Pmf`ia0Dr7>rxz5n#^LI2_xVw7X+RQ`9DE z5!UE)-*y{guGK+@!MV5`+f}k|cP#^%kPzBcZAC1Z;e%6o`k4u?6^NB6APkPCYHRD7gpdZIp85x;e?7!GK1Mq}CR|B$ zS7+h+eDKy9UQYK~H=U1L;j^1=fCKTNmcf1&ByTJuPJL_P<4HoQah{!$Lus@9CRf=e z3hynUDTXLuKy zK`y%|!F5Wsgwn^W`dw^|j`D43Hqn7gM8M^z_4mi za;~h2+>W@kTY%RQf-z%j=m?bdj16ej&p4)5-!hP-5R#ha1ThDi%EMGKbT4G4){q9q z)`&8wO32!x(-6VodU-aHea>UVz8WI&pUp*tdg={E=#es|U05^MxXCxFQ%2L3&kjkQ z``&DrAk35qbg(LQ#rWY|TAcCZi8ux$%1+B{8zlAfW5jU7X~N7@He0^Z_3uA@o6HVI zNA>pfq!NrI8`x4-LBgi@SnCPkq8y!Rgjn`hdZhq|h31sC&`B3s3UH$v4$qQSTfCHB ziMuuMg@6H!nkXYnEfh)=9r#r1=$0S;sh`k$D3tY7C-wn2N#F(aLdx@!TBA zxL6W6Jw%AcoFW-9spq5$QgxY-YXExdru$8;6WBC|?k@? zv7AY-b(U%FGBqEA7t4{B*;PQ_=lC_^bAf z_bh6WF9dHj4^FXKp^F;S5h@P}%87xLR@0YDv&|W#yN|$SmKR`4eljHs5OP4M46nRk z@`&Ws*%{j;31+q>$7Q8Z_lscR5zeKnewXkE;qJwLw^q&mv7FBjxb($2yM*ymft68x%Q8jKGL2R{OzBvaKl)NWTvg(fuT}s-CxvQGM7DP703wN zLgMlttml(vEWwC|){o<`k_nza)ceMq&wzHoFMBnwoE2g%;S(R}myE{n$>ESrB#sJ$ET?r6B4ZAGoGEyf~k1C_lwo zW3(h7by|#YDEO}GgWGxWd6L=AfS(G*Kx|yKw>xhm8QOi_L+<2OmPqd6_bK?~7}%XN zJ&Z%g_*Kimx;sZI*LE>)@+p9W;9S$(y)*hE*?dU$gq&(3mi1#bn+SovxM!RyUX&8l z{Ida-RmIeCjJuJ8icsQ;t^MEyTaP-;ly;w8c6#n|{d~U(y+{HNu{}G4Ovk~D!dT|Pgx~Luk{;!m-dTV!2B%c=!={MykiPgche$o@nmAvepOPVhAW0~Gk`%oa_G25i zCq?)|8J8S>x0n&`Xi&m@!xmVfAUg{gv5Vv za6$9);K3AALY|X8oBg4(6u1#dW4g!m}7GXTx= zSG(~x=j4WoB$ZDss?>T=H!#OAD4(N()ve{D5%?Ga{re!WKiNisaPx8r} z>+78}@iOUsdsK5jyA&OM_e0-jLlW}^B_A#)PlAItrbEpUhnR)5FNQ}pD~Un*dlw4K zh|@jqhocDo=%_!n|Nhs2>K{CWN{9-^0Qx%>gp*oTRb2!@HC?@JHLv@EJG-e*Hm zxZb*qL~8v2qcAO_I=k={Ov@RDqnKK=8G^Qz+w2PHiqpXTL*#~|JJ`FsJC>GMI8mA( z;Vw609yen41O=P6pRth58ubSU8}WA_(N#d# zV*SkNxrs%&D>ucEWwy*fCyF0HxJz_3`-^&S)j+e9X^D3j_`6J$V6qfw$#-t_UqL}( zhg_s3vs5m#!)Zc~l8^u+4>jRc#io5<_(#JWAIRH@cZiTPy1T2sJV4X6mT#v$Bxbv+ zbu}P+h@f4Es(P#R{kklleDI`u;q8Z2y6uj=B0zoxdQo&)bN;EdqBw;+7Ad8bJ(Gm%7&tg5hPolxu{_?G; z0jj0)rZ|IfRdAZ{XjG7qlYVqel-(cz1@TDLv|k*<_NR;=J3V5^Xe2-e5W~wZ6Jemh z@cmi(w~|w9>0_HoLAlBi z5nDX)426Tf%e2|YaC$Gko(QxI$%fn8+j5%7(12+iiGBVG;j?f=29Z|wZ{gHxKf)p{ zi7L^%w?y^F>Cy|V^pXN0#!Ztu^8?XosaMeNtl;mgc#gk%g&09Po(nNVJs^8g?XY~U z+zAXsE`O5jE75c012x9k@=4QJvxWv$QKYIwkM>|+?pPm&N{N8*H5 zTg~?ts7D@QjWlbbw=#R+(k3_~<}oTad-%SH9=sHmqv~rkA6Di%2b~ZlDIxYTs*Yt{ zJ$QBaruh+=Ge@zk>SPaMP!UAKN$Vf$)(TE|2 z;6`r9L3VsECC6qx^pF?Gvo(QmZ)MfpC5g{w%}$9q;*_?NyPeAP0_r_?$!s<9kQUgn zg9WM7*2W2yig;wKY@Ztb_08A&+XL#`*#3+S%mI@J$j^io*IK(ogM^=$Rq1>F{^!H0 zE^6fiEE|wg1wpB--U6W5?}DO>Ia5Qqhf%Yr6mg8ZGGQf zOf6*N1g>0&UB`KBTN0kCe+~vQ6uM6p9{bWuYm#nJ9VF8D>Ni*d=x}J?ln!+C5SB;! zOHLgJR%$Ub3sJ+;f3zx>{j${yqcUl9O`2Yh(A@siEy3zHcK#sFMz>;3Gj-_(L>Mogjg}f}^+qx+r|G&it8l%aTk_M~(x@%y6rymwc#y zQ9(MdF^lRZgsh{hl|!K$_yYd(&&x?jDfEz9wL@T+q55ZMv+G@IEa!Z|sqV#(ASIEu?_M(QSU|(r z$6k_xJ9)H&)bSge!q8hgSWn}W4xTC~EhNpiTp(?}y1Z2r`qu6uNnTOdvlz;gE{Moyk2cO*7LBHo3dVOu6e=9Av0{D#2w0>1U_Ixos*i!wq!RevwQL znJppIZ5&~6&irb(hPI4R=cC(bF;7>km0tKVcGqL$y11m^YOue4k@FF$#`Qh>4Lg=z z0&jq%>>x*~m|Y{pc|hm6#KLg);qsh;W*9x{T!CV2YC z5`h^m5`G>r60yGf9ph3sKxdZwbfQZe=MCpCQu8ckWl-^h0ChTbSk4H|~uD>2R(^Xb_^XKk&QC&=FsPAO^f+vwe{c6lLM4FI%d5T)DY>;eMrettqPZOIT#2z@BCjS+S zgSkpdbccV?FDiic+7?Lr>Kr73>mUp32Xg`{cE!LPq6i0+x4GT<+!t6;06N&BLMsC1ZL#~#w`ps-|Od>E-nnl z1f5mhTO;_I-rMTn`JP`syNh5hC5a~tHoiG}+u9pDovSDe1``1%Vm=lPmt7qZk%wG( z4M(Z(v?#d;HBLh%F6}rn`uQuxetK4trn+K$s>*hZYWh~1YBuM z^Jozc2nR?4a61QqYJ+`22Tee)lhGD^I3dC^e1RqH=`VrIsKKJKsll+BrGK+^xBB|u z_dm=T`$u#BNjBhLn)B}ftx#IAo0dgdE7Ft~H`g#P+txDwqMVwWsIAMMHx~(DXPw^a zLN7wx{@mm5Ln0>f!ie+HhBIut2S<>^iHm|QJwMESa$5J(`TAzz7mnbqKNJ*~f~iUl zXWrtMo!&aMu_Vf{Faw33mR1zC1ElzmqH!GI%+uED%T0x;sdI`HcPI-P{Ko0}@x@=I zBFi3jriB{i$F`-H4*Fz`fr-Ys0OKzGngUXk{;2V;Bpn) z42~+{AlMPD+KR5LBA07~sIv}^ZEY^_e92ew#E-l2hnP(tTel9kHqKdR972rNQ z`~8~XX-U4_i-Ts7O_}zIk4LEy^_mRaUf~s~Yckb;rJtHG`8PRYH4oJ>QUFh`@GdwU2G)i)fn)P!SKsRpXAbl; zX?&J8yqeri$vQb{#_LRTfL2M?1qT!NEdjO5)a2I5_LEyetZiR=8o>*?kmU|W5=jDX z6pOGFgO)ZtjjMpY6lMz45PR}|08c@5Oa75pM0}v0XY5@{0#c-*B4r}%K;JjKRXNq5 zQ6-Mb$;S}A?fkkQ%!~K+w8O9V@4C?{t!;J@@ zwIXv{<3D8rr{)k|a#DdjAY5)kXy@*Ia(`Ai6!%f_T@b!hYDU>S=_G50-%LY*J=w@z z`P>fdk#r6}K3{@X_hAZqO?PFGHL z2`?zL0~u=dmZnUgdx&EI*8vJy0Ry7Cd zTuB!m3V~VD`KwR3bz#3@vDm%%4GAw&xZ-!@i;lJUuTNh!TJowagu) zl~U9C*y!w`QlHT;#`+=t2?g=l?*cC&Yq73g)cDkl#10>UBa1LO{lk+){A#>p{XV?JkS}H$_Zmq=bAmH zIo-N#F|lQ-7vIL11m2D-XenQJ>LzW3`+CrN`n_rM6dGI~4!};~&wWUz@((G_A1k*j zxVm0Zdc?1$1u=4RzpOX6f`;NeI$)bbOW&lvN(AE-xvJiAR@R|eWXipuSsTZ?(Nm%f zpdO?e^?a>x1*=85 zl?Xee54}AER1d`sSLzh-5)M!vdTe?cvO&HQDiBL}DnTZCQtJ~6G&U}IQgtX}B~CKj zKlr=`W^n1UzgK|5c0h=NT7<$t50Tvx64R9oa9>12M?+6W_f?0+|EhVo#9ojemDPZ^Fs4zW78vb!U6HlRUXC6k`eb+9fc|l5V6~v9v>; z&>eX^z7z{L8dtYnKRbQZh5wamJA(&Xgyl}Kb)cm}!`~(|Nqw+|(B4^wqzR#5UVh2l zD$}G@RXa-s4~L{uY?nsz#e(z5uFmLsu4eWyY)iI=>qt9Yu$#CEzt-E#q%?juE*ygZ zI_LQpP4^s6Gbd1v@3&$xR^FMTLm9bsyp@dMLVL37Ih*=bzI+If8FQb0v+;iM{%*hibeX2|dG$NZFq)%4pDF>T%Ov#YofrFL zZ|vlvf(UbRex~zH|K)fXPXc)#~OgQ4D`3S z4<%Zlsd7TdjJZe7Z80}W4WmWAIZ}v^ zB$(#r{Zw@GTw_1q>d8&snKFEsYL-Q+t+G5VHAWv?o%*tjOsaY$>5(l?V&ByqZW!NU zCl{ZXIKBr8f*%IOO3zkSRBm-HOMh2x>$e?4We{cd?MAcqbP4`0H&>unEKuaqvdUylGRQRO`LQ##hr)Q(RCNE21Wn zxGft>{=A7n!$G0t!KJ3kvWXp4TPFihs1?9jlazawBz8ICy-SF+z>uK7Y?sA2@v@ENI6=*KAK;8k$1fP9*5%hI zY#N)a-zQzvdBk38sujTO*w#)*OdngNGf>#`d9+2-o)lGgUXk|CugZ1zb4(V6wRzgNqA>-rJ$#aa!H%mT|;4m+siD8uT^fW7B4v8hbG}G`YNE@HM z`Bc&7L1f-i-J&lgi^osAoJ2mqyJk6NZx=j%DPrvbUg0m?L;wtlJ`_Fy8Vk&n9Ha}X} z1F%;S6N>QH=h%a4DIE#}+3~u44)IY0#)gOBYS>7M4wVI#=dJlHDavQUyA2oH=i{M{ z(pUoJU)Aq7p7bMB!vh}5&SYh$S5Zu4m}n zQ?1+T4TRK=9`1W=n3|0pzI$$LV{hKjS9rC164lcQm)KX(iRzB+k<+i^KRUL|PcIq= z-+V_-vzV8i`L;|qe8@R@I}^>&wEWpEDLPf6H#%3Tbvg|Mbj%h!75&}<_}C?pz$-8? z#UeLkMUc6?&4V&LAiF{ge|S{fz3}UMm4039nCyI$3i0$|zQ^If1%+n8vu{}V5tu&d zF6($r-GqyU>54@qLY?PGyU(TE-P|L=d0xzl8R;mj6F7oH&VNd&_qql3PFbw_mfZ0D zwpFiEld=QxhMT_yVc9CLJ>s0Y8pHoditz{R)aWoo2@rD`^kOCZWM1fE1v_1_IGUW6 z(`K8^x}a$Pr-1P^m0G=B5_E80Na%R6Z)9~{>)58v(kunA;B=~OBl~81CH)3dNpN9W ze5#%Np-PfL92@>5;zO3KUy61-e!R51V65G})XO5XDh2wK>Srwb2OkS5-sLE08G)|? z$XobhR(UBvZLzu?rz_)~gS*kSAx!GON(bcyedk(Liy%ZgsTFAQi-2}%_lS^AJG49{ zOWe@-*!hmqe3f0v_q`cKn<<#N4pd`SIn?O2Ce&JU*L(N<#`}K!?MO|ICFXr>*Za%k zhWOjc#pZ_GnkOdg;t3vSHxALU5bP@siq`w@$3fW|1?TXO@@WL-Pb`bO5E@u?$-ZFqre$| zH2JC~4vu&B3~na;rczYkbre$-OS07EaPui0q zlfjetvqqUGRCs8_QIL4ri_>J8UgpHNB=g17Y|MQvw>k_dRqL7bj}!#8kOoHH{>_Z@ zm68p9ECGY9?${BktE#XF8ilDz_d1YkC)uxhfR>QN^`9iSr5rg0k4QI+21;<+7Z1>o z{$I&ud0lvI?3iXJF!8y~!b=fz(3zwDs*vtfYKuBQtVaAttBL%ZfhT0;VCO34U}UG~ zU~TtzywxeG$$Tu@d!wF&!J>|r;*Ay(4DgE=f-_T-fY09)5JN;jWB{*e-!bw za$W~;4H$5+r$U@OR-pxT+PY%=oux>Lgxts?OIHTjLgP&C>%=|&A zw=7dWdMzz$vC?%{JIBFF>KB8!7M`e`w!n-ikP`_aAV*k-kM{B-pA3Z_W29SsHW51Z zVS7kP;U@#i)ehTM5b6PcSjc2C;y{C`nMSwxmoVRn)WT}~z(naV#vViILrW4Ln718H zzEwkmljTGdMn-$42UAzA!}_##k~T9!%fRVkGYyWP_0^0VLIr{=9Co4ZFvNn5wi+{8 zvEyfNFCCp+=$|5%2xUMK6+;ZVKjcc%^%cAW9H&jq|MOE1YXWe%%0Crb}sEg1QpBtl>+6(@=S zi-0IFmdqHMKns+A1G9{!%3Wz*tc@d6SQbW>WcYt*d&eNn)@EC@yKLLlW!tuG+qV6d zZC96VccHt?F59;4s#9xyd*6+FPM_E-?u|EMzJKPg5%YO6bL7a8(z~5-908WtNCGENWgVcl6cq5$q>{wI3ry??8h&-Y*45%lAh{p^01MI>Db}I>4~RpaMSG$v&jJkuTUSGU&P}McfX|rbtBfcUXGbRT_qP^jRKx-iWF-AHmYFkq`=|6 zAe)@b6W_(}?;Fnkt6C%5CzAmN`KKW9C*Ag+Zn(eCs*;KGe-w#DDppR&Dj2@9^~0>g zuFy2X5%RXE(}g5^(?BxeRmoCkpp8vf!Va0$CJyc0#zB`#&)srxz4VMwF*AzkSJy?q z-LtQr1FnMnf)KAbG7%)(;bgNcZgQN~S8jA4GJo-X0CoAQD{u!9Q8F@sOyUdRh!qTN zdwAf686waPoy0|;7les-?sMNdC5>}=0& zNe?FZGb*PsfM$r&4C2sD$Yl0Q7%lHh8TVwkz_e)ZdgRi+yd@H)50^>L-^}ovEYxNt zbPe%JM>Uv_qtYUZ1?^>s^wOY! zZimN2B-dLl9c>+R1q%)0w784#W4q!sup46+B}kO;{JegnT0d-Eo2l9wpVppCOGjKx zxH{H^#tHf)!FG}q-ymJ{A(sXskLOjPRJn!>2D=9WFmN;)oU zSc0F;6aC&tT)SeI{v=?X`V%l?fj}{NKfcyHam1MrdamI@7B_#?bH6w=W*NH z*= z*94PGYZx+->1UL?T7@GjmLGcVxR6S<0Ob0mws=Wqk;a?R;WOD{`G&CIui&V6ikKT4 zt|ti){Obl>)jFa6aa6x{|4@i;@&;dk1N~R5{AsQQ3p-J})=;}7VK19ii@@sofj64` zLz*79rz3vydq*x-Zip)C3Bq3>X+Z=8u|!fT+rz|CrHr(+?#+N+c2-cp_gt5&ix`(6njlu(~=a)789 zqwDo}kTO^F3iVQ>p1>OYMP*ooHR1;!$}aO=}?Z#R|GeO5_bs^Oo=MZk;IB&mcA8?Ew)7;RI|? zx7u@Ak`fwKlGbqN7)qzaydKO_%tiN`#FB8-+kmA*Tea{^q!1oY6v&Y*8nZv|DWn*C zgvKp7EKWalj046_A+q_u_VDC5ubz*5dXM~o`qS+x^ba!~F%#SWoaOw14n~BP`G0pb z|1%q$s|fp7D>E%bCJeP$lgoPfN8pWAWP;Kfqm%zLN!HCxf>dOL;*BIXQY4c%GIS61 zEq}%$<|8Jib>fZFUE&tycBEt?<((3hd-A~B8{(ngyC2XAqJw6}GcAt;`d%1E{b~OAyDp%6} zRIZrmhAWZ8K@5WPHRhqHUCeXAt3<1k2OqO-l-*Nnr8aA@)6Eu=Pg7!}- z^#6)ib~FQgH$P7Q#&^py$tYg`fPPR3qgn}Ja0Y}5Z8~&j7n^g*gw=YDEIJ3CSkSsaHSIPQ!fxi6q3li1AS@=d0hrf|XJ}06^&{lC$xYuOC z5c!>1M^gL>V3-z zRRdxXfvAtidPo42WV}de52Yq(Q_G5dq_YAe9G2@2=wHPNkI+Qfk58bm{s@%F=Nm?L zHgraIwni?FjwZIwdX6rJ9&{$|&UAlDc%)4JdugKluNEVid1^|YNYaSN?l(&hZHsX`$)!}(RB3oiADOf=lEkhI(nqLYuX{1IO)ziMm4VxM z7RSaz_Gh#4{rTn%+@C^t?UH~%PXIj&L&d)Lld`6O_+g>vC@XHI*qwxJ8#O%E=)Izcxjq?DOaXHeuJQXcq%Q__nK~_ziRl{()ps6wXl(eo zs#|OJI8*cRy#ob+LooX66j8f?mH0N`}DxwG-OlH08PF|eLmMC=osIWtAEOz4?7(ErZ()k z&=QpaaboDVZasmmnyo!Maw+l3g<{-X$4@A@W*Do z@IOCO|D}YeQq@wzQ9*bg*Tryk!#KxqB!WulL`dN$0PJPRd~-tzU_n3$T|>seF|{{E zL;mpp6#$N3)p3bX!5mh`_-t72eFOg}MBZ_mo=pvM->vDx;nOy8bY4SOV&?mDGWh+A zvk^~}XcS=5U!g84*a6$3ZZ+PXEea}1{E;9^GD<29f+)(tfgQ6OrI;wKyHb6hcojbJ zJ_y3{l3)E*zNZ5q0*M)dm8q3cZq{mIRb0pJ5E>xEs9Yq|>#E#4T+_~! z9Y)wn1OnqSlX)DIF{@(4!_m?_+#nl4$Xru}U7px-80beRln@~>HkoA*GJNPfzba`e zLzLMU(C9z~_*V+3eC;6H7MJ-{3H}o~F>k^j6lOYY4&_$Lh43@2Sc3Xzgni ztp$CYA)DZ=p$mpytO#&br|#*fAIIff-2Wj>xENwysodIa{u{PF;gzG@bP*IH(QQb1uA{(|kLUz%TXJkGkKBVm9)6}wZ)}g)TbMVW z*daUKSb707qk!yoy0{t{r<_qI;W93-==Ms#r|DF4`}={)S}|O{4B2plX2Yi z4ijdyUpIjq23X5^(bN#hBt z9eF=$^OhTSXFr!}lIgcfGst3@x~K9QL|;y^Rff;sxPu??Bggzq46Hz=i+voW-3UaO zqAJ|!^=gztxI>QaLw?b}in-W&Nl_0`)kJkDToshZukWeiN;bNBrp{EL6BH*TJuUk+jTSb+vtmy?&>8;d!Yh9$*IX_@KQq zXo-vh`!%4(Q&~SB{lvh!fE`5iiG(r*knAH~l1J+)&lN6}a1mH6n(>WC0nx0SGN zzM}x|dAx6h)Oo5Jq6OCwsY$5lWl>#-mL$$1Ae(_N zC%Zqfd2(KO9CZbp$N6@F(s&>@zo@_f2kdu!MR}GbAatE0hGGSm3$TLIb(#VN7Mgf} zf7@p0V1L6D&iD2BTYJpJ(RINVGuRgD@PA=oN)05b5D7gux7h$49{cr3rH9czBeFjB|wd_m}naqY_ZvIU(Il-@PT)D<(=| zU`7qsW=6*c#0P|b1;Nnxl!{=g`%{yGsnQP*bb%KBz~HxB#AIJ$|62z3>MVTG{Rs=z zAG5W88d?53WZBVma9;&500#%F4&q68`~pTn!pd0T1i}K1^{m+15{zta3j(}y42*U!JSm3|rtk|RTLNAys2Ta0Tm1^${2M|AXtA<5&NmLwh!N0n>k zp>}V?Yy|e*$D;(~O_GuFMpsF=s*%z$Kvy2!z9hVja>xGkAg49r9?*us5cFqthRoC5nPdj^PcnmG^(<$!<(DhN z?nl624xGD_O6tB4QSN~&`wRfa*s@{Ro>oHGP*P6Ry$VI%b{`+Pr z)kQm^Aaa6svpz-ycNAz@#YKpquF*)F={m*0?m&&rO?fFB$($-Fnp8dnViUaa-m_8ApMU5hdS>yW4Q7js|Oak%OQd zKA0K-Qj%B5zgKTPnABv&l^9V3|CY^zsK@(F4hlJ&KKfqA;DjFkS(gbWMU#EsC}n3p+Nko6c3X zyJj z(b72-hedK~S>71x;78MU+o2pQO%FpZ?TO3Dz`jV-35p7j#Lzi>Mu1XODn?I4pTeyK zOszYRoFs8P68rpWCv=X54TYE?suhr7Mzk97^w6QZN-h~7wht0UDynwq8yWpA!C{1% zh860JYci8tXJvV{Mx6@^$^2CY{}zH3i!`IT^J&1+-0F|#`pa$1x$^oJ5XTS3DY237 zmYn$3^L*JC?uAT1W^fAF@ztKfKCgUlYeM&kB}e0@pl1ZkZ6Frg#`py*O`vbY;CP;R zg%6G3(FZ$|67X9T_1{S=_&EjJ*nGghWiJUyakhigWUmPA0$)RO3)RC{`M)9|KA|q} zE*vIpNk-43R-Zjrk(W5AlbXbT{R46Gb_gX&5iSltMMj~J!X1g z&dxmiUXaL#@rGmhA}`YN>>Y4Ja}&l=|4o{_A-3;iX(hVvJ?+Ck3H&g{FTVzuUBJXa z5mpc^kzEM>CrgNv3r|c#A+oW=@9ki6zZvve!27?Z5!2+H8-9F}{`Y^(*LnY8IFhw9 z{x_4BBGoe`WfhE%wFb8b+J#8!NUgv)x-y!`dUFYg0V#;OFmvh*eG@l2boIqfDYpxt ziek}yn3)pO=UheQ(^k8h zh{@qI+ywiy$c@cih*am{!vO`^_1z3R3HOy~8OE3`Tw9%Fg!tTH^;kT}wl8&hGWXC3 zfra7?$wR z_PCbtY|LzsLv~WdQTp5&vw@*eh0CLKBiyUNot+ZdK!}=0_c^7AO_rd-uX%$+eu2jQ z3MqKSv)E^|H_Q1U>p2l(&J9Y@ed6CNEbIHL3uo}5l3kTNsF~L2^F^zg0j%V?<9^P2 z)k3Rowu_2Q0tw5sWi?2iWx;?ikUl`GUX6?16gcmS2_mJMTf!r=xBKMq4rj=}Q*WLj z+#NvUDN6aX17+NGyG^YXYLgSu5#(!OS5T9P$n*3BP=JxZQ2P+n^a=8EqbgvwbRdfZ ziP^4^Tq$bh$u@t4!a`%F*k+UwN?llB&D|1GWzeG((pTyOsG{>_1vR@JvgGX(uzlf6 zK$NQ*N04Nx*n=KWqqcy*g2EGNILl@0ePbB|3|H8z`kQf#gUoV^{KCIlU$P?=-Iq^k zITe`1Cd3yiFJr^t*+O&)vlNZK=YxBh=a4^tyoj4#O|QO3FrHHnwY(+X##wes1wFk^ zeV3D!qvszM_qJWkLu9m=WT#=7=5L=W@j{kU(wiWg+}!8&>~05_C?VeK*MBtJ4YSeJL_cx zNX_-hj@l=)eqCG%uG2dGPz#=(H@VaTp^KKY&YhSSefE|pJ#Y$A#pC4+TevwgaKa+! zTev0^=m?uY%A8>*q@DHyb)5{gA*;&h-rOw-xaa~pDc*vx&J4j=|*C8Om>@Ht1=fvfj!v@d#jz5) z{WO_um!9Icn3%bgq*htS)?qs4reWrdw?^gRG`N)xJ>$B|E!nOFA39-h*eLoLvU=H} z5C%a=DV`;?n>iY8PP?!)b4nO>{9|>55i|KgUr^c?&CCrF4~@SUWcq~rx!gA< zRqdj!V(y-|2i@ra;$T1bEP8$=tiYKVEBTSk7L!dvyh=*ED!}l9m$?W}ZuFoqe*SIf zJTKzSJd{@GcLn+(xQY*)1>0hgCu-4sIR7KkX6Vb`V`_c|wp&J@h7H$$?BV`Xq0hgp z00ce$_5WXmKH0K=O|BvMD6RDMmGgw-6R6YC{w1b1*Ay1Hn%q=j9aY5Mwp}DP^sNf@ z^~Wc%#k$l#?*Gx@VYfflF;TMz)y3gS>#BW<$jL2}m|(x^7xu(u2C3$BkJ7KW+gt3& zNaEhmuYJ`)KYjmZ&kYmQ#ETVOlM$mIfY1pbM()O|-- z5&aG+2Q=imca!oeisrH}hRmQs?Ox*WZE-q;=AsQU;;Vsh!N-NJ#&P(R_8ZsCRWt8B-yp#tNRg?iRWX zkZpg2TK4}VsQ;sQ{;yc$aX)?uyxYzfMAQNl6R1$)VRekKQr8ilhpZK*soiV>Kgw?x ze}yL{JGh?sVEFj$?c?VS)Y{k8pA!iz7;C1Xi6k2u39w_A`VqA>Ly4%sYn#vj9@wHi zm~aq`){GxANH?DGK(FM_gq8G~&Wf4K?gGOeX`R(H%6RSl{r=S5Y)kFjFuzg72t9Muz&p*96p`nn(Jv;V$UExD3x$ z4EM!w15%Xt=+t2V#<2G;C%@EOm3k{Nbhz0Gs_F^(~|U8bEe#v7W1dl7k08)_nZ=| z9ua2QM=mo8H7QmdV#USKiZq1Zs#5(nc_iJD`$5?lJaFf+XPrjj2;=pF&`*t$YXPGM z#lm?_%C3E5CxC+qw*`|SvBXLAC2VBA7<3)#d1+tu&rf&eO!8i=t&=XptgasL{MD-O z^OBJM!7z=W`Rf;v^ZV2xA_QuZkV+3^dCUS%MVFKHuP6331AnGa!V&4P5OnXqg|3c_ z)L;IZe+~E}u>VxU^zXp_r^`WB<}X8>tcBRZa1}4aXJF4Tbad2+(y$_eW@YY1_vEqq zzK~XISL8ass?fg-%UdnFgieM(C$@WkXFl9@u77FepBM~?B@)a$@M!*)CESqYXP@Gb zTzkNtoZrfkjTHFAsL(+kP3%5p*oa!NGIjU!%~tOlz)1&-5I-fIYZBjzMAE2`67PqP z*Y?xsoCfN(gY;*-%MRBYtf1IIP5cfjRmFi1NAg0%QyPipS*#*>cs?1fI32}gTh$0y zKlcNS`Z;@o2m;T^M@|ZlUNsxRy2s3A#*U)_<8Z<#muwW0HJdDa}t~R=MGao9uhn)B?v=?6fs6Llqk9#qedvZ z0TWe2d%kb*W~w_vchFz!$vY9b&1LaS+e*)?%W~cyy}iI|1H7Raq=^yx3j?wOG=QC8 zG~~>Oaiz@V+KxMZ4tBv|Fsi6|@T&dn&*tyDDH_=6S8_-t+-rR;AF$b&Q`RsNKNTl= zgA%Hgr_w|zKTp=juoWkX39^+sWLkF&lVCN`a^RZ;$hpC_VB`9Z%7j)ldrTigtk7xs zw2tTivgfp*VH)L(77=CuUUDHBsqNT);&VUZiQNLs&YLxp zF!WZ9&3i8xnnfxDeIH^s3=%yA{A4we_&01>@wBO7{jhENJdzO+@?urJZq-yh1dsDM z<0Dx>Q##=^&XGlwVI}&YL?$OWc0Won!{y(^p_6`x;$J@hil05a|MNWlKfRfh?5!>S zi(I!z;oq~yN{Nd3oYyb#o(>9mVHBv4;?St@kh&(=DH#ZCCT>K}R(P-ezRC_p0)=l78mg6T!E1f3l@B}UP6 zQYfoFQx)DL%jfaE%)K8|2X8-jrzUKPf8TVb@#*4zOv6C>W{oA)CZ_Q8(5>SFAbowU(>)2xG&s)YB^_dsuMA>2W-^8HD=7ut$(M&1$FUzSv@8Fp!-*Vbd zG`m7g9WJvDK7Sh?IZoqF=j&~LHdtLo7zz$U(M{~swO|wC(}cOXO4V(|9;ykp5;5YA z%KFGLZ5BvL6?iKZfcOd{${gg&zl=4fFI$!GN0PTlkCYG{jZopF7JSPXRI=n5a=TcK zVW<`{A^JmJ^VFG>A_m9Iuid%l+?MTPNZ*3O-DPa5j_k(IahZ|GR-(4f+rU8cnYa;H z63SZ3^xbGNqIIfZb=F{I`s&3jDz=H$&gy)DENP}b4PRm~!U5^>^53C-(*fUFDZ91C zW}qT~f(F92Vlk$^e&qcWU`pC;jvgGsaKT2TaFeo`K2(~tx26cd-7^2hQO<8+(EJ<) zSV~B#av|VGk)^aBb}@T2SzlVhhzUuQfjS;>qLdI}n^~|fs-Z`S$v7*^<7HYmxfLm0 zN<=no7Dm!??vF-f%AwzZy59p)OA)F4lIlVlJxOt{Zr0d#9TzxHB4{>VSpr{$^+qJh z+y{e_szy!C=QZvL6>|+$aXQ}0fr6daRJD4|YeBq>xs>3QrZ!?z*ZPCc5RQS9OGzqi z7;dKsjwwE3^0(Gc`Vf$5uy-TUUYDVtY+_?^fxXdHchGNb=uHgb%G}0y z5To0v)CsjV(-PhHIF3~g-Yiz?p?6r`rLzbrgdaghY zoFZ-{_xXI5i*YtAmi5LL5jpJEM9GawkxW?T=Ag!RS)b8<`hOH?7~_$fYmXFNLj0<+ zs)BD2`w8#s$R_bJl0dx z7;C`9ar6hkxNm)b&S|Ycwr?{6Rd4AmVu&kH2x$P&g z25AC41%{J_ z)uV)>^SB#!?;TX9B)FrXL#j%WNv(EXpg3pn#Q2-~$t#Fihir`}+&Dv_XP#19O6Yg_&Y&hVpClG4<#+PdE? z+ZGHil9!CG2lg{eOrc9^11s4VS((%bPtNA4EVeq6WUS#;8S4%<5f1VqCgSOS`ip7v z*R_V-ZA)1r>M?Zhm?P^EeIrFc-q>Whq8X2n@TR2Q$!`sGaDZ*;cUeW;*m83K_6xIk zrLfiPhamdEQ%035UEFB{r&AAop(ab>aBUluw5@!DDfD zGnTZrsVhD@KJRxt6=KTNWVPn2pu+wcaimEo)h7BYDw#yFpJv z3-6Xg)ePY#N$Td*chvaYLb7TJ+4$`2rgvj2SKJF^()7wTDc;Vonr&)Z0nG;XX=fMj z`rzrC(QYsK)88rDY+;PR@+bMT0Q0BV{ZCUgWk(YeL5qL$BN$VMc2=JI@qWMLwf~#L z2}zJ3PRKpUEg_wro4D%|Mt}fHK>UkP5xoQ)J%XveX;1G7_MPUIDmgC{kYT)ZlX_4v z|6JfKR%NWEUUPF(bG7SS#aX@1nN56h+{=X%DIG?o;Dx4JRqX@s%hj*X@$OJHx=-at zmwHEuVRDRBt>z$mWxSC3pk`%a1t&-4!lsxooC1gSq`6%|L>hPG34PpUuI_l|?v*Kr zb^Ld&z2HojS8&@Y2P)gdiWQgD0It0w9m-~lR_uqNY%iDFUcV9O2ahY0Rl3kKPS?#} z#0@p#g78BIB_TSCze`%0nuby9o+z6~=4?5*$!oX^w+*s|-GxW4SU67OSG@KvOJ7&3 z48KPJ!)Q2Ap~213)EQpcGuO9TqXDz0``pd@H<2K!W5-5Q z&^7}V;hH%KU9sEhL6tTA8-Si+UePq;N$g3 zFNQirRI^bdW^`3Q=Sf-e(o{~gshngL7nbF2)tM0`)o&_Qh^-~w=gM?y0otvEsi)>gQ28eyo1K>-iiXXzV_ z%vykD*!Ofvcn@|txvzIYKUN2js7n%>Y@*0kxKDLqdQW^#?);2Mg=OBU3{blS4#~^L zsia|<4*@Z-wToFhUB~36*lk<{YDpT+64WR zG}Sny)LofK9_b@1%cse2mUf*MXf1^-4VKmNJUk)`a-O^`XrFEjn13) zZ!ry07K?1QB%G96fn&;PEBL#+dY*doDsmk`os}+)I}sdBVzWy{V>JCylRX5u`&!}h zBk(>Ffy{6j)Kmqi8wk;}15^d*S5@MDJ@IpiMKOl3O9>gU^`LrY3S;9xQIf-cG}ZAc zqyGw%<}ydbOa@TGSGCYKi$^H2X>oeNXpY>cqQ)9eho65Va~E%vQcezA@Cn62^i(`{ zFC#(;b@fVT)h+6_6m)ShUS?5noNa#fd)iivpW&83zD}HhOgzV1Vwtb@o-ZmllPD0w z4XBCk&u1WG=OlhCjuEe4KaF1xiga2Ii?Y4(r;sW` zhSww?<*IW5Ko!{Bl(`*Bj-NvkIwYOBZI86XT9CrDs5(YgXN=q$i0_LEG2tU(rqls4 z>Vl-DdRPr;cDg1@i;#Za$Ztbj1HxwaVE!?#ScMGy{k z*7(d7fpIx_Vvd41#Av~F;cJ{D1inN(f3xm>FTsOr6U=IH*H?}kM(mv(Q-L8*(9 zsr5Gjh1&#~`J!@Kaic?~5!7!K8FVwvk^7Q*OlVIr-Ao$?d|)<94({cF`Is3L-h+8B zN8%+io#n689l`HAm!$Dp!fZaUmuOQhDFCr?JifIB@5bp^R|uvQU!Wj(qOYNeIRzo#9AWXbbMm)ZtdPmXRinr40=SDa(ihE!?ZYtLKr{MabSZ(7R z6%UE-Gg4YKOX4NyzDQGgEUR1>Do+|Aew09>-3Ju?&kQ1Bce28F($PAfxkjI;@z*A;{pI$ig1daP%*a@1- zKQ?$V6I$6(ibv>(jJZcMpJ}L6?&J0uHU1{8R}qN)pojT&WFc6?V~@&`Ay~~+5vU35 z=&0=#uVViFzM9hYLL@Neh<{JQI6cXMqgYf3`5C3%>Cl}4`gl4^MW6r^_*yy$gI9Jq z{eTTtF_*HGt=Hx_nlYk0aNX4SAYHf32_i%BRJNxdE8Jc;=GmnbYM6$23AomRG;hRg zZ@3w~<%TK1(eQpOPV(PLxD1V((SddS9hAn6CHimGsk-(;=*e zA6YLP-JM1D3M*@?;!5Zz*u@q7`hk_rcNT(n&`xFLaeW&7Q=pKbvO>K`XX7H?*rq%P z%w4e`R$g@p3}!m%*UXb4t8zbP&R?7e457H!=p^B-m<|Ct-aUtXT9WwR7bp4o^b`qi zd?LQEjV(M41;`w(09GCK6kOq;xh=?v`>x{pC{v=N5e5ad80Y9}KHKW2!q8^G{wu=y2k zFL4%(dlY&03%dxkLNgpkM$t%ZK?rrU6k_NjrW@TsiLzK_TAakvtz^L%&C*ykr!P!@2HiV@VOZmfPOb4+_jnm_$9Q_paURA6>74o z)Ebp55*rc!+ARv z)aK&LAn2H^+Q!a3A|bHoApqzC1&aH{+FX||piaIoHE(GMTH~6N+16A|J3>_6c<<%h ze(`1L$B-z9+whRb#hxk0ZDmrEW^C!2OwODo6ABzTZz#9r+DZ~L*~x>m{SF|~o#}TB zF6G!rHEC)+D*Es{j`KEOCnS<)0x^phew%lpJ?%l7W~7f{n3|4^CfTNICtNQV7$mX; zdLlW==uBTR>x<_dk=*2T9lGkuo_bjG8k6>n_@04eO!ej|nq!gClZL0IUp=u~>afQ= zt>TE0(Rmw#%Y;byo=~iz#YrR)(zzKj-KjQ^qrrENeJAkdjS|RORHTmDmFpS&-UIS3 zL!J9>g6M~3^~kh1gk6`7-!J=4?RsOxE_*9`jqINDojA+)Yt7XKlIZtuQND=ymrUk+ z8%9um;T|4LquXX0dhbvt6MkKymka8Me%aAGcC{q0GR;}P`YoSg7dhXJnu9+8YXg2G z=lgs{4>v7KwC`|UNgoM3-G2Q!I~VmcuWY9#?|5FxA1R&veuL}#e)<=eeD!Wm;HN_G z8>e)yLih0Rh*n#EitHci!<&8z&zHX_ec#zkU_ROz13qFIgFl4Qhd;`G@6n9hb*N|z z(UI$I+elXFyB2@%j=Nu;7A4U-@qa)?TFCVwD(Wa*36@OpOte&8M5HI!$ZGPnK7uX?}yNlE@+sLMOOqoEM zi1TK4Ts`m;;=?;bEE&Csvy!kZ#;HQ(yKhiGGTU5(LYMc7-qxuN*{6iM{adZbwDKJhw(s(Y z$g+9s4y=X(6L<-28bk7)1RFX_F@R9nZ4m6;3)(ljj|mR$P3tZV2fyc4=M zdS~53DOnQ`Z~QWze<^a|+MOxQo#!9M|FPBtQE$x-91)`Vn zkQTX~T=*25cd>t$(XkN~@Cs9~Fm(l(tZ*q8GJ6dY%@o|IA$s9!l@1R1iy40ty5*OP z2URjQ$~_Dw%x4pg;3Y@$VUXQ5ET>oa_H2+dBourYTZ`cjYe>KvF3_st{dJ{J{}iFO zRtsj(mjOtE0Vz8Fi%d@_Q%|f~A9)8M(Blt`Xt#b0Z-RksBL=vGewL`ntKSIym9c3< zU||y0wGj0NhEUj4#r5HaSecZ?4e5qpnM9I{+6p5h{npLuzYRwFv{i{l)U#V-Y>;ji zs|a-0E zgSXMBl3^68cEhnuI%<=K4U-v_0Lh?+Rf-hz6r;k~^@^2hs^MXdk&C&j6s(l+5oyvBSG=P z+RZeQI$+O7s}N(airk@kQDtN$!gsvH0K=2d%Pv4hV+f85cCXP-ez#Xw<6txY8~gr8 zSR3{JatSx^xDaL*1^JrgSz!Zx@V@Wu&V*b_hV-xcfvJw~`#Q{ixH%txHz%4YjvgHm zzI=Hm_!GhLPgTu-5gh*^SZq=pmF?k2;Zf4mRps~ovOOJ))Km&d7*I>dkU&67Oud@c z93`))$q1V7w?o%TPfGjx#qIb5!;Y%W5Y!=o>tNg`=f@8_J-V7NvbD&eQDYc_ixC9a z0x^W#)1YnxFjLK&9eb`ELCf|yw8zco9;-&BEHZAT>D5NE0DLbZ(t*x_i-F}X;PHQK6hXt}84=vik0?kh}F7kl66XX%1?h+Wn?sk~d40dIjL9B(Lo0$)Tnkk$^ zC7lMBk`f&(VA~ifC+f&aC~}&Iyf-+8Dsbt<&$*YX;mOVXDr{DbZMAdZwAbI)_)8ov zF|U55;`&;_f#x6{A;*l~7$cEzf!&W63NO8!It17v%ZlmEs*uD`AAP16-?v6!R4S*( z#F4}itK(G*E9ni&}7Z=;2vpaQXf+)Qpg*J;XtD3iMF(Fulg?Rml+yRmSGc7LL81R?AP>7#q!o*d|WShJk|#$qj0j z9zGIlzhwr9I70S~Es~i)WNRnUqIahjS_-`Qmk=%$%+;$zu4d z%C%Ohoe~8!=wWP0U7ED_O+wIJ>flu&aNpTr6Iu;tu#Z!)W>k!5?m2&Tvy3KR(AXV= zM&{9!9JA`xww7st5 z0DwivQwX?lH_SGaDMQz!o{Z^_q`D>-Hwapg^FjYWtwWPE+foLY2H5%Uc_$;wOpOb! znF2^eokMOIoU{`_LQ3i#n>#M#j%8(q#$^>+<#`TKlVY4--~-J|>^ft8XZ4r$$WGv@ z@V27ia0AZbMQRG2BEOD+JI9}O!-CK37@q6)xtC*TxF_?tC&$v#3X{#^qO--4c1N5a z@@D+ZyTcfx61&X7>uo{e32Z#k2T}636e#)jV)dVmEcr6EhHkhUoPGYQNP1d(c_5eAB%5M#^^U{Y2RBZbgNm|v76 zDhrCU# z1Fbu`5Ha9Zl3^}O+7^04!JsZ*Gxmoi9Dx%#tj?#z4*rvbd#Y}sZxf9ZC znFhBDCm8?&Q)hs|D419>=bm7}OqQ8{WHT@iZYh_Ll|`0(R4X_4X|`oe>U=K6V@)1s zA^*;8=iwS7cWlU-A=QeFU=E6SsuDM#0civ^Fx5w|3)fO<>hA4Xa^T{($czXzPFS$X zO-d|H+3HUUI+kX5_@t9HTD-G&lX7;wC*=`P=uB$6@2*Q8c$5@KbZ97^IyOncEW2() z{84}o16!2QCI9iDKOoihN7$=R({?lTMuK1!3j7n6QsO+8trroYxS~F_#l!?nrG< zPo55Vc5A4$l}sQWE&+v_%1BE-;%KcH1u7R$*k>uyHleDqdfM#VyfuT@(MMcH#19&P##HLTx0+hSn6bJJ?3f)e%!HZ5$6=Xf$_jaEzli$~VU)i#`U*Y{P#?Gm|vL@W3 z9dyufc5I_#+dH;x+qRu_$F^b$+WXRsuz#eYQRhvEvWw7t?Q&89!qV`Q!$-As-h|*x>mjlmP zhc^Kp{;yO%WFo7*=UT+rV{)4mgQv;v!9B%93%_IGnC$Q^0K#sEB5q0F2Gg+Ro5fBz{tIWj!jDU?&HB4q3$`yr0^yeJ&fgs!j? z8x#6wH@Ec(BOUo6^3B}>WEVQ^$S23cBkD~!Re3OKe^Q5}0d>E6qfBSJe&>pO>?%{V z(P@PNV&%~4b?WFsMdL;BP+RZ|YCOyg=begjY#Y-Jr$y}hpARN9BkhcdEO3o``69c}z}PAJEXE|(y0#NpGzML@ zd4&z)M%D-4O4y2moIFwm;Ng@?_2)3N(%%%xeNaJ+f3zHZ5>H+zV?k*C<@Z`73)bXQFEZCzIWDPuaOzh%{Ha&+!X zms0a`8#L(Ka*Q(ks^Xx7_e__mO8j^$M!2I%m6p$(lCl{^1U0qfjh*cr_U2xC+b|!? z7RSd=O_z6hoHceEyDi=v{GS!zO<#=;x)ijk= z@G`VlYDQ|Apyp*{dj^fi$6l2ZmFdn6HRV@onhSC(yr2`~!6fh)ITnnteh$%fmj`8>+9b~o$7A|I$K#CzXvvA-&IA{o9SRvM;~j`t6g`jzVfebHh|0;t6p=(MwuRFbUdpR zEDE84#+m*CwbEK{H9RlN?(1ce}9<=!rw&v|1J2(4Mf7F#_It)u39!+|p=WvyzM)jjhG(P~mhkPo}LtSv#d9a8+a#HF=g{ zWj0#Wo%*pfi{Eplm9_zYaZ5^man}09Cp5BLhug^+x!Fi@y^#lmL$zn{CjVje!Gw#5 zyHVY-IlF2Oxlg1jAP#rb8 z#MB+P(V$u}XliP=vcf*;GGPd!4_Jmtt@--{_o)T-pjU%o{~;qfw_#Z~w9M5)yR(L~ zj@8tV16!09IaP_vM(7QQT}k%QEUElQUpetsK04pRxOl!SCXclT# zNL&=<03MS*)$7L52)jDytsYM(Az8zC!(ECArrF?#(Fk!{)>LVR6{VY$p`!GPpvfE} ztDOg@$7QnRO8OOv+4OQVvTZ3WeW_tsK)XHyz*9ddoVfI9Tl0A; z2!E&8#RA#I#jIinv9DyFr;HzCWr`Lqn-i@;GjV>Of%es^LC6xoqXD!qb6XC!ViC|a z>CYQ`g)Li^+}ASN^;qY}9w|xG-$l_-wFCDtCzoO`rZJGH*s-b{r9~@0{N)0TX&qk) zh|ft)LewEv(Y6R05HL_)si`wwC5E+H#`n&QtXS9E|&H=n9~~=o@%(ME*mMSG9N)ykxwfk=!}r5uBNVLTQNR&m<3av zF&frLdt{HPr;>I-ZC^E7s{vS0w~Jck9cY zf0ZLVV;pdyg(lxjgE-U_DtdN?*&twvkiPuHff&-LNGDq8;%{2On7>vnr50Yl`klAQ zL|JA3z{-#@OflH-}&*cVduO0eHt>`~&-z6!5P zXrcOW?Val~>0c7<3EnrYrleZG4l)1L?tDf&{RPp;6c+XeG32vmzGsROR$RC>;eLpSah9&wRD$+mUp0z5D@}jZ|fy)+y!QbRV>%L@(;~$bWIc<4H z?&{VfUI)Vlc4dFZ-4t>ffvLqqazWHu577m!L_{PBYDA#t(6vBU8mjU-q2nEOl!++& zo-fOv>_U@a?Ak1-MK+w7dp$43WM?NEB{M?9ga!%&ZSRQXrta!5Kcf?OHIfxn6^UVY zP9Bp(2cTl-Yz-`YG^8^g!(M*}DEEfwsf`4<2nP_6)WzDD2N*w0wbIhSsj3HeY%tF9 zT2yJO*K}0W<(3qAK@)DDXj>sqCMN%r{WC5yNu6+kx!LMVVau<}==*f&3 zs`vDly$sLcfL?WdJPEGD*RXN^;z6R6dqw7)3ww%OLmw|{}ZsW*f-`_9?I;`7l;p9uC1*uF&`RB5H*g7N^B)0g2}F3Pv2SCPB?ryyh(#$cJ-$Lw9Je6zuUUq zgrCC9DG40pYO0C|+C4B&elVB8Z`(e{P?XoK@2H5M5Iy|dw&s}dj9f)!fz~CQh419yULb(-Ku={#`-Ihe{U*(;TkkeJO`lo$PQ~j$U?1^ z_2`x#&=2RO%c}t{h1+2yzUd^r`Ooq7$PZ`=}Y$^hQ z!g5F%2`IQ@MqH)>qDa+0fj5p3S~DfA{Ce1C>ZRCCAiAw^_jQ?i+%T6k_aO55#UgMZ zRHS4$&czn+2fu;wdF6-um*CTel@?QlX%KChY2Fm7T1v83%8ukInwr1cg0(7Ok(a&7 z&IRU6*R!!mlm2AH?aHcU?y{*?YqBM5Zj>*PJ}+{kY3r^a6x~vjgb~y#`f?o;bkU7@ z!|$>Rx<7V-e|7#zX~f-i6$Je|0LQikFYHNJ>Vyt{n|R)^@h78N;}nFwUxC`0%PGeN z{DJ~+QARA4Mn~c9U(w-H-TY%fJ2T;=0-~$HtIaV9vw=ch)dC|0oj|}XqmzP;f7V#J zUC}L;6cMMQJ1*L9TRoga$sdLD3}Ji6M;N}~9ytDPa|9p&YdAYv=PZGPs?km8Ojn^H zJLI{b4f%6CK=X3uY<#~3xxsVDo{6T0%o%TDGqf$DTgAJ?qC-u4$;c%4 z1|0!+Y^Q^2#Oe+(?L^ny*jrbOst!m%!jpd5t!zrJ7u0xS>^9MXVN1%GR-qReOE=0h z!!=Q;jcD-a&E%h-5W^%fAb-U<_^S<`(|hM%VU}M~87#sQo`+UT4E@GO(KqPeozAON zx{5}Vq|&6F+c;%=y_B6?yG+a>mk}eOoZb1=g)*f!WImh`8_1IM?N@F>j-NA$1U`!1wTvH{&wi z?U^M294gtdpghL>#Ts|bTwWax&;2F>x0S(V$D08j;*09_=>)V}R~?$4A0Upz;Gmn) zZhup5#Q7r*SoN&|v}g|D>VK+PcGmp_<_U7vh3|rbWKkt{NXG7~SHvuh(eImD5IxB6 z&HK905HNPv2Dx&tM9z1|0As?(PWf?1wQgPL;^@_#KA>Y8a+Ju-gYS^r+&2>1D5kOu zn)QO3>fK6Ji4SM|*;Hh*$H|B}R-ZfJ-Da*?GIGL*iN%jO{iZ}fyLXHX|~fnJ_0w6@9@4Puf%vs z4SZc8lgv+ZE11;rW@O{2DCw9HUY@P`pizbHuecavPvFjch%N8n8;M_nRkuv{%J8#w zTv9(A!HcN65l3)>>k|#_vgTkT%{}OpkyOfIY2~%;g3EeLl!M!q;P6y9xQkA&6HWuS zO6{*PSO~VI(ARVAF>78T``73HZ)_q(y+&i|WA$v4r%Z-jYN zZQp@v) zajNH~jvG*2+JD324DM49GhpJ(epgPc2I)+4R*qJ~^l#RxhBE|Cb+|746kMeO?H5Ha z$RCG{q&l_tnG1}9d5dBlO;{h#T&^5FU$m`#s@I%=B3e=>C%~MbIb|F=93k%|}^abi6L*0|dhh}Rgkiklk zLoEm*rkIq1zy(uEO;6Rr?WAJc>jEwv{p2+Y8sX`&kTIyJg?{8?-|-}A<`Lc4Ar_RU z6|33o3NNy5HB5kQH%ozp;f+raZpWiqRoro2CAs?YuR!6AJN_V7%c7BHS70VUiW#_+ z4WUJzD4~Hip=_vgzL|tzLO5(E-fxj3!tG z!*4zyq^u;vuyBWp;zo|*h8!JvC3+3Zp~`vwc;Y9JWKQjbfnfHp)#)ud#12*h^|?nI znZtd)VZYw{dkqj>h=~7l5L-1*lPA-mNWSUc>IUN2$1WGDp7i!Z-Sr6#OZltht*4+0 zy~cSRh5nEX5r$&yqer+qx2G0$KZQ?*>BinoL9PQEzfUxrH$s!-r&cUi_$TI8zc2C! zxexV;%sS02E{@^u()g*t%safM&Xv-R*kFg|yBaOkfn z2N^H*terYvcX{x5m2CddEobXvcpzJy-U=x4pc*Re9t1iQ!^^Yni9NXB1pJu8R zY(^{o^Ss7+(9z1<40S&JXbBmsQpN90Ul6sAf5o^gcnZA#RB}e2|F;oj?5S4w%9WINnMgacq=yR$B04ka3F{#5A;8pYrCxU%KjMNY z8ITAp>(~2LaB7Fswk9L{&M?wMHCyZ>%8v?#5*fco+$fSA#2V2A!kxt$nLfFXVlkUd zx(zkVN)yC>x)Q{$l(yllFrcqo9K~Ld6OIZbni``R;FuP{(by~63k_EFNaM$e4P@7e zRB6^^M@p5E$F}q$>>fMPi+2C9)gaZEhE7~-QtebWAFMb@s?rjycp+wB7`(&&ui-qX zn-uZzhdUP@hzpI}yikXtb9RZ%*r`r?+fwP}8qqDZ)Jk4md3>U^6TB+g?rFyp*x&T8fH(S;;*V`~H8?J{7?_*H$4q z&G=|^p3QHN?~%V3z+#!aig@6H#HwtIX+S&`u&Gsi33A?$j!OBEmOrABOGB?E9-ub!J_DLdMQ>al*m-5S-qBeGR9QlN7f`$; z6>q$y`>V}6zY0!Z|K&$n@+MUb);dj2hs?YIJ+eivA{)V*lq8LuHIOx{elGu}Nv)jc ztf6XPc4P!QJ6;wSm z`{Rv2D3oJl`5?Sy`Bi9wD!Q7~2L+c3;!(b`tF$YAlMAmnP2`A+Qj8~qgu?%*`bU^B z=rx$QR96lMQn|Q~bzo&XPwd+4d9YhZD$>4Yg}FbHyhhU?1=4C47*~8~bmi|4D{+Sk zl3rB!=gyE45}H6*U^PkIq(>~7s$qh0eh`%9Adj^_a+MsdF&6DX%)!E7=>n_j%=PpY zm;V}upFor%#|Gu?CQ-VA*Fn-pbRo$rvy(LW&A$Ghj~7a*(4-NJ7M$&H=c7voJrS|O z5^&YKT*h4(B4h$h^Y3;C%m867H^>#)`;5YfN(Z3z&PVyOgV-vV+=^1&!5GWj*P-swJ^9taJJb{Su9v*kd}Ix{Y?-JULuuFRz@)DwwWe z<%(KI)An(rp}zQ%y3WghiSFPQH%Q=b=bGvO>#p^hJ|*E^F6U671_CBRV$|?~we78U z4B_=?Yn)hu%UPcrNn__I0=qM4oeZNql{?=G0z(2C9O*-GT`*dtA-9q~cg!UsLkoZ8 zPkAAZ;pxKun090HOz?q-NZyz!((jjFd0n2e-dI@!arD%GKs9-M4m=vL?gzgadp$g}F+qMI&C zRsyR#cer<^!SN{$vrA|Vg13Yg*B6;ms?6BkltOCpuOd>Ve`>};ov$(m)%?LKVsT_@ zHmUZm;lQe^DQ;pa_?*+4rCq)~Y$LCdbl#DnUAH~R`rl&h#cJXCPir1e<+Ywqk%O(J zD*bOYTpfPnnU>E5mMZ#%hALSnx*1!`kVUsWss&pcnI&6mkvUttM5CtGU4wMh%c{we z;Dw87v`c8YeEWQ(Lc3;KV=Q4gW?bLeE$wj-1xUvvJtgcL4gUEDw-~l?%@(PZrvZw& zv1iQuI+%H28~?jFhbVTAP!Bu^AJN2Ng&hG&Tb`k_Fru(pRztk{W_%>h+Oq-G{2NS^ zOO!1(sJKln5!EO;)s~9cfe}%@p`T$ik+Vjfw_Kq1D4J_OUJQ*4v8EKyL0ME3UHdJw zDqixVTJ!xMp2c)69vX?zzDXPNKhc5L5^41IwADX`{D|P{hZ!bxBoSI>ZzcM@(&L65 z>+KQtTcKo?^#?kVB$Cue=RsG=7;Exs=xa9p$aBP?Dso(f`EykJir8S&?Lk?Eb8-o` zwp1=`kQ((8<;!`Sg6W0GJr$0WxUh>ySKx{)VSv6J*D^hxVuN>4m?k-2fyb zL{j^!_RhAE(y~sQ!VRjY{>f}Rq=IVZQx9ZTP;Nw4rG&ubrhBdN9Glyqb5r{u8Mg2j%*m%WqnG`P%r4*cyV(Wmv^7Vz;xaD!U zd&@cu_AQ1!;ff>g4vgJ^AfLVj51czmaRvbF$swjb8=ZKIyZGEYMBbTGWJ&z_f_RcM zCt@S#c{6C;(w@0AyGgQS{hJ5>8We8O3Y-~s(9Qu(i0X!5&)pAh_}vZ*nDajkpo@Jv zdHB8Px4=isXMwrS>!^`o9O_=&-E}zpL?_5gif4&efyki2Eyz!A*8 z$O7LfFQEB`T^s1G!7e1a$^*LIjT zZx~SEh&=*%en0yA-w*U}$CIU`0D~a*3Q?V9C@b=3Y7Y{2(NpXwESRtr`88|$^$k~Z zC;M`JWf}@i)|}x5Kq=Q|Yg1B<7`_L{)(rp3dl1`~k}!6}X~@#C%^Bd#88#rH)$jfM z;nRWxgB!YM9Dsfd;|`%Tvo?RtzGsKaNnsUHhT-6f;~xYGFbj{+PWK#16jKpgP~4&C zz$#SHB64!j3{N$C(6Bv?R9xMJm2k(27+C@@x)pllVO_u z1e4-r2hp@lB&1*GNWWU{yeXtS3s!2MtZ(|xm)}^stzF(@R%&-0^Cn`Z8Mq<~&il?u zf*PdF7`Q?b2_~m7R<8zwpFBHDzbaee*D#6arY6vS39n){TQjZC>Do8fPCAONrP^is zDINt`>%ek3o;Ss>V&X3LH}AhdUtN{tkPko&XDUS5flK8c%t!K5MlMRRR(|0K#%gIa zcoSqk1><$#e?vxcgR-IPpMOvsfbD4UV*GwVRGYffhr|DM_F~uKAM2O?EY^}78^F`? zTP&hT`>Q_F>{?`;1!7QK;mH02#PA(>F58W%+YcnLM9r+VGp=GxB}hk145y+OP6ljx zf#wZ}gl=zc z3?M_Du7h5}1$f2=dEVu8_E@*DQQMr*9L?ezw~YTvyUxB1D`cLg+Wyoy(i6*HQ zZi#g~c&TI$e;$zdQS?Fps{|}E!aU;l=|H9Pi}@qi=aF}(jvhw48pkcT)6XU%iSL7R=Z4Yj>K@s zb!NQ<`!x(a*S!zqm{v!8)-95cpV5&@XSa3f7UqNoWYR}ax;w~(PoQ3jfT0ar_y?^F zfulo`*~%_F^Dq*#E@=UP{>(WiHI5;_QJhEvdleM;@4s=*N~vQX8i5UQ0r)PI2N%#a zVcz9oic6<#$K$Ls}F~Qk@ zM~yD86RInGmvLzMhlH%DQC6mowpjk ztyr)ZNA|QYJn~zpi-|R4>>q(O=N6cg=Qu zUrfByTSH*))z4&K*q_1Of!R0T72!`;?lE1{DBqslm(U90uQ0frC;NLH;FZ89wC2E< zbk7H6`B8Q)%*)X^(I-(Ki?B{qg$DMEhAlV%{ zV6dP#b=5-V@&f5a*AHoMeYpO_acQGj3X99jsX|N(FZ>3`t`DhhCl#QIv{p35wzBA z*~Ml)X5?c-vpk6I|HbQ-;Q85mFiOqQq?8r&L(g&mk4{VNx;DkjlBC-3^FL7F?2bV1 zvxW2ZmeK1W46^c%&SJ6zH2uNsbRN6rc6QP3q)M;DCgt|EOl7w0?o2RFjx%W=LcXir zn}H2%Bb#u0V*i+ph12TV0-S7TSvF=HU#cn>o(TjiR~ELdC=wS`8rj^${^`u9Plxz; zz{AdY_KS5QWE4IHQ@*Cs3*rB$56=>95S)%bC5_u2)~}O8D6|PJvzdN=oHKTrFuHQjpfa0wP@P&@hat}mHo5*UcpTx z8|*C{kKi@Vhqj|^I&p6nu*)6nmcaRmQ2^aDBP#!U91r_&oKy1h4o>9E8|sj2$?0$o zgY@r!W&IfFWOr2rlZk4hHck6zr zwSkA?ooHNt5Y7?5x=#Y+Sh^W_n(j#M2j>NPctIAO&ar0|_b-{0KY!31P?6_kJpM#I zjc9rny4&m=Wzt=phSw{e980?pO;mAb;~F&`G;$*d@{iuh_Y+0vU|6u>fX2WC`|UkK zdMPZ&NV8LHlPy^T)=k|(ZEk3BnOjy8O+{{4e(K&Xk%c^a80jMsC*c7|gC+1vK1LUyg+y@38>jF7#Ww{L$` zRB#M?I~0Q?WZfAuplDVV5~9@g^Pa1>UB?!!z7V=CqA?R|<}%1AsW@It!;znAlDPCZ zKdrv-WEt&tjg-qUu!7%L=x ze`TCb1U9g1uP{2KwBZ-JpQNrBs~M8h3>BfUt`w^ow9&D0gG7>Gkq#Kg>rZ;M>cAZj z9+Xo!xdJ+Jeq9ilhr}8+-Jj^k=zYJvS+w$W!Iw@gw@tihDn}&sB7G>i^Q%MojwkXXWpPS_0Md;l&pw!J7HD(A&t zXpqO)_61$gqG##$c|Ke+^H+U$5AF8fxkDX^E4KMJ6OXN`ty@VY?_5^4}8)embh(ePTUemZq z$#n<4;O^XG!m3V^Xshtf9z}`u!m_R1Kt-|)P8$9#8mE5WCW4NU*=btvsx!+39fhSO zz5bAjcQy}AF%4^uX(%N#H~_>tX*A?f(vIuDTOZ{hI>^78hmP z-jv|;Pa<^}&gz0G`{H)I;*a)qf@t5f5lVgm+!1Yi`9G%VgKme8eS@K2w?-n}k~0dr z;{)&UDocI&^FMhj7Qdj`j`4eV623}4k*wzVLONf`=h6-WXJO6tg4PXTCvmxs<6OXD z5!_><6o&P~En(ITClpdNx1;*4G+|-ij8{3tp~@ckKf# zr^Ti(oNz(3c8Ofna5qei@wLoA7GgBXeI*D8)bD5XT3h{WdQ!=T~{PQv7@n9(dK`Njth(^3nNa)0XX@ zLjJ07sGnwL*REJo*|2b_J40^$fs`SXke;j~U>K$~gJe@z?>>PcuPy5!tNmyBJ3O>a zR=dS|H=%^_??mSw`$+?&_;0UhYC9N8_h528DRRVz@q zgCRNfVbM`_?2R;1Y*zIxc7Z~dqu%OOwqbwKg@Mgp(eyY2ci7d#6pbn ziJcg!k4*~r>4;HpqnI%)3;Id&0a8qi+gkZB)QoL{m9ub3m>c@2g-h5xJHLqmHuN67 z(jxFyj1D{Sc?ihf%g1oBnBMxav-nv|Q$fj5AlDHLh?}esz2rlP&x<3W{X=pMsLn{?k*>5`v`Fn4R%!F5RI~$FeW{0% zD+bt-X4F765k=_4T>0y{2<(4#gBLKgn8-J#7kQoMIuvBvnH^xe{d@?I;kt;}zWKc# zkh@W?#@40)=;RqT`)yva1({B5@W&#=z|yz1Wu=gpacQ4&-E*w3A`#x78_%%sLq{|J zojDE%G@Co&>gHtw0H-XXpjJq)toxc1Q(#i_HUXIbGY@QC!+aTCAQ(8vZTZDb9 zu{njp<~AJ1Gq=Szq|y($h+Zi^bmZnk2McXl>O3DB50(%V;u1+HTHWh6>_zGvemZMp zp_Pj9N1n1`V%oWa3~rzzuWt|xZs2mZPy5kc;{DmXgzn!+rC;5EiP%t=+nq|%UKRi@ zBJU^glfg4K`7WXl@3I$@*Y6`t38WOJ7JBwdUQDk-)VubsRQi-%;%z%EyQzOs z7yGa}SEZcjawg~&xBdBnC>&|53G)jBk z^9QU_f`s8C4NPa+IH^UD3O3-^g@>9PktbSTWKNG9%_HYuBIqSO*^2`&X`vx|kWuRS zYiNf?;m?rZ&kD>I8}t=8`8Fd~ybM%mQ&0MCNh$INvcXdV!ezb>jVJY>q7}$~e|rYi zqY&Rynx&lfn4wlYDc8V#kuz2l(hLdMV*RrI8X&b>o;f*vMuuf}JXNdyB6MDbCvI(5 zf?9R4d4oXRQ@}X)?;cWm3BT}-87GSpa8ve9`pv)G2SFk58T2Ym5NrxeZ1PNO<7CVh zeZ-K~CNR^Ir5T7_vLw@;b~==m`R?(Ku6-|sgOCJ-w<+byYV+Q7 zEKc|=_dmeb5d_Iy0Y>fk0a{PQbvrUSDZ^&$>{AvYegN0T*lm;1t=XOPq@C6Yq>?Z3 zpTB_63GKLJO~7}Bt)vs^xD)X>bE?sXXd{mZqg8nC4K7&$vO!G0dhw>Yq^Z+=Sihvi zPQYRFt0LlMG6hSqu?h;i+y-Gf1Ub1ykAn+{CC}1kq57Nab^BI)A7zizWtbg&Ezc9m zN}EPKsgo<9t(`A+)WrrS&K1rQ?dR%a+$)mOlPnUyH=apG9Q#4#z>SgLd>=Ixg zRn%&Fm@)Li%(T;$`){S77nQ~x4M`Uu{Hg%(JVoAhP~5kK8hQ0=w>|E;Dq^&m$jLQS zrD&JG2jqGm@oN3JYGmVQ?_!57*nB-w9Cx~4$0Xtv!Hv#L9YIK>E$CaGYUgq@MWs73 zgc}}F7s9Y$#m|NeR*=(CfT7X`ZUNm@e^& zZfq-2Qo}c4qldR$0ooO4v&!{U0{Ng-63;UQ>!w>w3 z6MIw+_)swDE;)G=3H5~nvo?kWy;&WzoRt-|jJ<9B%E1j-5=?XHuAcpdv{A&>+Qv)?cCi*Az=~A5lG4Qc9yj zcW_Uv5V}I_o^gQ=(*PIbxJ*cWYkNhi$7c}2gij`Aw@~`lUX0%}mvnNd7qmmSHuDik z*2t8{54=$ycol2p=kyQiir+CVt^;%fbq+zX^^mkiQ`Q3{ksendVf9AjF3Ha6X6s=r9o#Yz+4BG z_YPT%c}XCxHH&Nt;@DOP0x&VIpXdG)C0$jp(w(J(rP|+e(sBpyj?;{>Iy7_=dz;}- zgaazjJljW#`nhYto2EO615}J&&*9t~HsT!So9RA?mcH`cK{)!=F{jREJEMgHlM_c^ z`XF#D#5HTWooA{)39oj6Mcd>4(B)}7VdlK5(^A5$e?i;QV#L#5bSP`qr|r;JCO*-m zI@xB073nNJ`86u?7crKaq-duzmz&_J2;`|qD6g5x>DGjwc9yAATmMfpcGPVaNbn{e z{p1=zqO)_G+ZHXsvtIu%o2bi=mmE<-KT!hh-`tX$*)ZG6a}tC)(vw<&ORFAj9*D4E z*BdIxuL_i5-U;oSEA5xy^AtT^!MwI3st1aIc``{YVJ@Ndfkf5GmM9m;G+44taRH zH?bE@Bn@sTszh#<93HSsFX9|ZRCgPD2o>tD5Y?VevT4JwPMUdz0XkwAA;#2@1+Qwc zu)t#(5O07Jif<`_{HG0Yu^pS4RJK{_MzyWURDCGsT9^V^SW#7V|EA(ca_C~0SN6y6 z&kX-Ad7bRzINO%nb&zJ`nIH`Oh;RjO*`APGtV_#fWbcuk1O%{%qSuY8a2%jfY-Q z&h+ygJ7ne^^GAd$0mi1lpWVq&<}115b6j{+H&Om3R}$F!n1H{Vped)ga7-P0`b|&N z>`PoAhwkKkf>$IZk7z=fu8dmenZJ_Vi+d9{PijUrL0vsf1>;@OxOX$*G&@cEX>BRF zFEYU(JC1rRHyw9)uC$&9Jn?WIrUFf_UXE{*at||Oi<)ubJ5W(7QWsgGJWfiZ%Nct3B*qi!?Sc$X-rR{loS_S&9jD>5YCf=FnHE{$@$~0b6JpL(oLs0LA`T#etZn{GhV~EpB2Fl70&< zz#|JN?D3l+6a61y5yR4-pbq^uEYwh zId2RLcZz0gHKj~r|ydnMa;_C%&P1A3mrF3e5dP~sKS9L1hjah<;TZaVu|DYjAARTl z{0jm`M*mAS&s5%W1Sq3?HoeqNx@hM75tJZoAZqAkZ7}$CplY5wqoGj9SL3%!w$fxi zFTJsW5)r_g1A5BN+n4^l9Yy>b78nXS02%puh@V6Ei8{r;cx5wJ5N!;;w&Hk|^>Nwy z^ftvq-u>?V1>(ork*^2V=B*an--jb_<}q+y5@2|oIC~TrwZ{u&$eHON`9TNc*jNAO z3t$`A2TO@D6c!NgCY&Pwv)E1poQv3`ddy9FNFB3UQqB6rL_sq8xF}HyyT3D(RLmRs zcl&&Zv1JHPULj)}x&6n}=``){H`0V?Ol0n8Z*OT}Yx_L3c3;_NL9Qs%Ji`XgoB^dr z$6K7p94|rdzh!2@a#Ca<+hch0n1E)aEbfvCPDNCTBbLY=X>?1e>{-a9yCB_!wu;GW zI528>fhLnTu`3sPgZ-~3P4j$UQ!4WiX@#ZLY4(Ciqn!lPP(R0&4un);w0$V->?k)K zxkWX&F?Hf%Qi}(P63HAUUiEQ4J?ha!K_$Wf*#r9%EbHN;cu_yc68})v=<>S!Qqj|| zqOres_TEE9RY_6oTQ8)wEnbc%w>pL=7MMy>m{O(_Et~NuX(|u#y}3X^Hq8M+)M>r) zU$_jYhRY=eIDp@ADRF-l7~qOZX|l55hv!AO4bzlvD^kNJgCFi(+~ycHiIFzsO+{pZ zF4jCn>VQ_GB=lm7YW-sqSBZM+4TZl$QBiD4DF$qk=O)DYyXsbo3{+A~8b%q4vk`ZU zhe}$AkY0c_qmIJeJh#>$OjO%^VZnLd%*vie=$fB+QeSx`fm59_IwRU*uFY8z;$}P> zU;Alcq#YcV=){lkD7TdQk~c9Npu+PmvYdt_N&MXQ5H&5XQuS`&998PtQxlZgiW4O~ zgf>9=>#|&A(P2d#*B1>uuct5V@i$)DG7ERg0~^Y{wmohMxc&}k5Fk+H(# zXrc3!V;euM#da+zwZ62^O*pCWRg7^3$6|_{CXk=U^A>_0FvZ zy}~F1YFlL8>@o4PP)6%iL%Zd=01eC&9Bdmgs_XV>-y7nsndpmlg6P7bdjks}Ml{8x z?Ciq1>N<3{N2zr~KU?goAKi94%vP$4ZM5ouoCq;0TTV!Tzk{zZ8fr6c#I90$E0^7$ z0WJ={Ao@1?i2kr0s3JR*wHFrHCEaC;e0vZcIb-Hlj^b=dAtTW$7>dyHXMw($04z8= z4huIS2{emRoZ(XQgsh`q&z0_8?q;~ZRn-^&L5~Q{sLhUkEnI&YuWs6H^ZtjncMQ{XYqEqZZQHhO+h(P0v(mP0Ta~u$N~_XlrH#otXQq3)udi#~ zzD|FC?&tq{V((b7BVw)Um0#wkm!KUtrQo0)^P;C6XK}o`k3?=2xgw!ozhK57?@~uo z63b7*>JEc$_PlrDAk7P!!j@obTWo3i;$?H&69{j~RVRl&vw7*FR!ELcAbLe-XMr%X z-59ln8wqxK*u~9E8AoDJ}&)E=-Qkx3d6AQ2A8}XLmHv+GhMptu|LfX zn2%3wCou4wAKNDdpYr6(e{{ZP_!sVfB_{`SJ7XhOW@3iFbuOY5wdIf$;CY(0mtC|A zQGvTJk@AZA0MmKF2?)Ti3UVbDz;q{ET*;d?F5*30AzrCG%fjG>OnnmVo6HE5kyuyK zR?=DzvsXCye7s&@^)TdcknPuovH***e-841JzU{4!QmiZjtn(KVPMq*_E#v58p+`6 zmU$z8XWfYf7R6GWk!{-~eA}=U$s9dZhKN)the@Ec)lKht$&Kumq5_;cP~Zm(i%hCj zKtMmNj`lh7i2D?ySyK)14_phESyA!;&Z^khOtBIngr$P!NZ#54m@dyt*2hZXQ-Z@IbUI_`pyeeXuxAO`Oe~Vu zlRfNiOaF+Owob+E^a*BNho& zV?^X~N8`%f+db};rEdn=qu1N)f@%N_Rvq3dq`uH?G}$#otLBd51^Xq;p*r(dpT&G7 zZLmdt#5UeY*6sA?cFZGYvRi<^nVzuKF6JRkb&Aw!B>O$s)!8-eS%TmAOUQDxV`jgz z^zAFL&8-ESJyZ;bLraD48O7^gnq$sdt`HQTnn=rk)Er~}Z^faIv6a>T4n<{crMXYt zICH-Sk|0H8%6xD@Ru%E5ay$_bC>6<|5J09A_VnVl_;^z5S-^KfT~7c}CnwR(H-%_d zp?O&`Q_mxwcDBQxJP#&wzxa54KowFzt0B-wxM-Bhj-!juQlm9Z%mF%WvZ@?b^kwpCkBc`v$#bc)6I>w}Rl{J9 zr%y@OtQNv2HdG@(jbGDGV#C~M$-3d;)n}pQj$$0OTYe}vzL{FA7b!&m3W8J^TT)jz zy6fhdpE|2b(o85X0IBq`8$k$#ilYxYLl}mJswaQ@{utEzj*t3Zt*_Es^IMoy>kH%UsQ#v{Qd9oCbN!0vYdrCINzeNw`jGcofoTzV z7-~1tv12dQ+!C9iD}<%N(sf*6J^=)|ZK1(a=yGv2QeopMr`WY7aLCu=P~Mdjt% ziAKSp^Grn12US6GLa!bEYDvyu`nj-oi)Htgbj8-RD0)9+AP}|@D(h@ic6Z%>29`cF z?+ef+P)FbC7dd!(Xg;fmU6_7{%)35zCHt~Q<-us$f_`~iWPP?JsidBN(ZAO*P0Qj%98UGb)@PE(O%4QD6`bPgT1hN#hrGC2;?k=TUTNE4X{t$v& z75a)O6$?%v?6(j=c#FKtc{J9BRo|{KeIIZ_l9!A3BPRU9LRTGbp=YAik=^lECfCjD z^(Am_U=pyw2xY1qexMbg0yX&vmAXM_lFZsICG@sn?I?U1r*&^^d0G?u)e8*)R);Y{ z0Vrd_R~eh9hA2FTX0F9@u_hX$g`H|DEznN+N(@jFnrBX?*!Lfa&L zD18@fMRFca31+7U3K@l)F_9Mu@^A4s4xZhtuSgY**ME2*8+PGsPtPQF;L|iEe{TbA zI@&e_7fmHy0cuRiR}mCNUq|;}SK!aeV__u(Ylu%{JI3_N^^M{qbYc~LbP@DQ*dTL0 z`MTpFOj-ogCX!Acs9_|^9)i&aEkdxDODOj+sgLla_M3*uEsP;se*Ue$&-H)(xBe?- z_`m(Fe`mfdH7_frC6tfPna*i%ZnD}aAX&gCGfhMxdI-H9fHr1=Sa?DF!mqy79<{pu zWriiwti+W?+IcT#OI4?fjX$CoC^5}Xh{1`4g@WEiiX^g{T<}gBB+iS?R~N0bnw$x# zj1GCZrofabUx*%ldTcyBW;srCEInOK;%NiO$IL;Nf7k7W=O#=RGsbQ-?DQYLY@xpg;Syxbjf~kMbNI1Z0@RupBV$zX*fYM=dv99D$C1Df`vV% z*(9)(^W+<$3Dbc8y_wc8ak$%)A>1mqA#9FHl3z?-niXrS7WXC`#g?l3jyk&BFL44i z^tXu&KY7q+6f5iU+RkAGCyXs?`1j5v&9_5b%mPT^yNtR>F-}_=j6B2m#C9pxLoI99 z+c093*GU_&-@Z{I@$@QbJ4#7TNFufV=+oy(tJG|?K-qj7vYVIyU4rpBkt~e*zJZ9c zeR#N4#a)UstL&s76YgQw6&YmeJ5aS!x?*9jP$z<}LF*u8Mv?^1=a-In0Bdtw23=)c5giwj+_YCtp?tBI^wZ< zA$PWTQiQTtqxx;=)K^dhInjOzlTOG&_?)1^Ng}bO<5-cicR?;4E`I!M7~6ckB{u%^ka9h;=Iqd8XST zx5Cr!tjpP<4@jtL&C}q&&Ui{glwhpWP@N}}Y@Yi#!8SWC)#7-T>3rI>=%hG2p^u>ba1c=}ZOqc=EKJG%T-rG76d`pRS z&1MUHaZ`yMKGhI+0|_#qHy-?D@xh4MbtkyT4-1IZSG4UTh|#6eNiyAI2u@jr0=aw6 z!qBB}4P9hLK0u%mbBz^B}b670kU%z^11&v;)% z_I~M>JVqqe*&|05TmoAWDepN)_Bn$BhWC@@1oh9K->gb&Z(L+RF%msMs@jtw3eRs)H~wt21DxhQz}14*Ew)sZKD zeFel4xF4h2qXg$-?2B1F@PfH&kjfXk=S-anCnwGdZSNdMX+1T^XdaB=>{|;(C7v zB^GA}e-XT72@1R1DG%s#wF}pnXm$ZCG zzpkiWxx6#IV-Byxym8+H=N_4so^>xVHx5ysslL8qc?WgZ--A&kBgI)MIU2&3ZL2q2&NKxPn89P-p2>wl771vyBSz6$Rb0x*y}*w`CRZLTtjYhZeJQmPEQp z48gh(6$lN<+LTPRsfd;shQmd#g{prFJS0F?jln20CXgyHLsXRY-Exi~7i_7vtl=b6leq)gi00)_|B{u#2Csd#$J9#k<^@XFvPN4FiIAepKL|`YY`+B3qW%O(eNldH(AD+} zcbI2a-31`ty)v8EVTpr7#|LiuX8JnoJ}Y`C4l0JR9ZdF z;f4|D)px$%Tt)UB;7$(w%a;h)KXDa_f1&=cv30g_)HgAv6SB22bars~G#mcSLS^*r zRN09I44uqv|F^SUk?gqSr=v4Zb5mHDN;|^q7c~3b+z3KVNCJ6Ea$3suF(-6O!Ayu7 z_!rAh$#*`<%+J)d?#_1D%kz-^5VOSg#T0#DIjmOf=&HC(EimA$ZD%@jL5pVPJU_lq z8+RIFv{H-mq9ZPlynTGhgmsLLb!j*Ql_1O9HZU{MncYs^jHH7>v6Z4ZhOHwlC#N9~ z{UuQFh>X;m^&(2QGS$<*vhKi%JyF`chT13gEQU#})vic2Z;$O*mx4q_!O^ma1ll`= zDDwS=o)DhXETk=v#wp+eWI+IV9xbA_a|pN{K&c@O`HlPu_&2^X!n(1?pXh~s^1=U_ z3rhV{dE3L? zU5bTeKtdL?9WwsnIp|D#`fclM7w#Q;4&NRKTT^Zz!jAp(;5De;zk;M)cTaIyL5U+B zDc`(F`6nk05AH<#47K*@uD!#CQz2GEM6qyaNzt63j&acRdQPGwdsR{0(ogR7D+_NI ztqj341X_`&iGk^X0G9}qgqq}i(H!WI__A4wems&jc*j_iun~l2M;$;Jlyx9lW zy*M2I6lU2Db9nm9NJiMM+q*U`SFw>0Y-Odm(uBKP57F&o`w#1>{zL9_KTjgD>POlb zLPCu9*vaUGTnkXlyU=MTZr%{OQgM-U9Wqe)G=7RJ_!8AjL5af{M|yG_9;M}*TeRK9 zYGi7KTE=Kzwfeh%CCnRiE+|EtH>cUqMdHjwH3?aGy5jNxVU9L7by}cIPxtk6#*iw8 zyDJ{{P%=_xjqG66N=4YhOIMOBy~-?~9vG%(s+j)4LUit5bh6lzP8F|9k+x8;D#@>F z6=%Y1nY!D}Hru)ZiLG7|X511H zbVe~Yk8q4#NN5E0a&xojFN24)HL=IM&_FTPlhnwp)Cg^0RruwM5~a$DmREg0p;d1^ z!YHxbL7^o%eE|v(FwR&6H!E|FQI*b>JWX?hUEpdYdB!yT1Q6|n^3aCpvqHaCbcn`D4;a=R{4k_pnRHGB6OwMe6o0d=c2 z+envocw0SKv^#D*E}}ATt+i>XdsqGHbO;i;34_xyRZ|T_$rSH@?<1y z?EYJ4tE}zWj^v&R&sz~bsHV#nDhS??4Tvj#-WE}&D`E_!nKr8W3}zN?vGQclTk)vJ z>BXJf#*B}i8+S!kA#8b8qJZ|&#CxVDx!Jr&UjHi5U}c$1kjjR>KJOR(ar!=vh0GbZ zqj~49g&!1;~#5GIQo2_c^=E2L0Z>@?hV<@HwF&kjd|Hz2`}H)6*x;+ z%vw_b>72o7rZq!$&CE7;9j7xi*zAmiy-Om|SGz&7A;7E3fzNjWFD1JF*LW zI7o$Zv$XtYvd3ZzxwIFx5eUd9c13xeaiJS*4(i|Y{G8tC0e;~%S%I?)?+oLuYs86Co1sh|EhIL~As<jvvk8OU#i;3r}Q9h&m}*x9PM z8`t!QI|%^?VjWBt)ls=18)hufY`r*ep~@CQzz@fl3?xN}4PWF~t1n23KQLn11C1E) zbCN-9$^o};YIAQmthP|QA%k?G3%3#nwF7I<4hL{q}U5(M_(tfRa)?jNaemGfqs^epXZ3I zaY)a&%2^#UwBlf5x&o&&5oTE7PtkPwZzGT~uFBZa18oJ5o%=CXL@O_|hEhDUcPb-cAv3{Wh?Ev zUD&$Bb~jt`DeE4qwqZ5`%oYi=gZHsx^62u}KEf4fu+Cm)u$ktD@A>12&%^PfYOg!$ zwK?Gf{%KS2y?JAxyWrcWgi+%|zgw(NVksKW?T7EtGv049u;g#km-H$7j|BH8-lg$R z$$+q}vw@Ydp!?sy%C>+1{{33Vb!WT83knKK1ghf#3hM%DBMQ1W;P0O)iC?w7lYiVd z(u0zztC^U2lAM+>HiEyMnUkWWshpUuwiBnGxt*a7Z<3diq@Fx7)zd!$pP89ckpr!U zpsSp&T$ZD$ke{QXWn^ZOT%MDno356bnp_?;h>nu-I$M~jTp|gM6Azb%fiYkwap`uj zUxdnWNQ{bBg381Q?)x(@Cm}VZ`d3b3Qo2^(h@Mbbz%n`pIz}owzi(VnoL`zB$cTY~ z9!P=}v=)DQ1VCYMmR~{?(qH{$#agY>sZYox{>ZQt{%1h_7r_(-MTJ3=K@$b7-g=$Q z-&1a$$W#^ug*bfe*qhDIpYK478Q6i|9Pp=y>Z}~76LtZ`6$SmZ_p0?~)Dr%f|LHCI zHXn}oR=Gnc#8j{R*6}*qu`|%1>_t6P+On5Ix?dQ)CuAlH%IgGxJKfPSPk5L)u(LC7 zAPNc)+xg59=6?MAE}>C2N)idpn#k@O73UY{7X|=~uJH*dhPp2`DX1#_=ui(xVJkYn z#WDu_3j1HCF~hcdX=9%-vHuZB`F{$eh>eqj`+s(4`YMMcfcy?>mO|T)1EA1>Pl;^Q zN={itE*LlK4@V;7#@Np&XulGFp1jpPD@=j_glP+*^B!e8(6B?Y@<_vjC@~K`)^Ud6Gt5P=>2`G@2N4mXz?pY@9+2 zJ@a9x^YLC`f6s0cO_sgXo&)1>EO~VC&6gFT_#(lZe!1#UXMOcP9Y$QbO7XcnB*?wR z%E)nfoO7?tHTViq%+~#=SW)|sR!%0PDPv=0K3O;;f zC6jG$bCOgW7nN2z497+zG2<2i?YtoU3J_#0IEERbAuM+E=k3SirTX&C9gVH}6D@5> zEveR0gC&BGXD6|TiKOkM2`220>1$W4C~awnsaqwh!4T*N!VQWSat_A4;)tTnlQjGb zaL_n7?UMBCn0I^VRm!U^#Tl7VkGO@_N6ig?%-_j~b6Sx*F^sj(t5Io6Tk(o%suPC| zI_qaJTkTgEMCG-tAzyW^mE_arQan$qc~xc~mFQaWl$zYm!OjXj02#9j&4R3sC$j#m zhq#X?4mOz%2k^wAAx_C#xZyZ_al*c%;JTejO70f(^|^eTnuD0k{>2vT5EQ<6NtNg$ zuz*T=z=p8=`Lxd40dXF5SP*M5g7`|r{0`Ch%0wF%?P=+?Je~+GQxDf3>B~5b->W)B z)}8-3TtGkqA0P{@!WC{uI-yhG0Oi@$9I{ctrr;>PERDX^Znm}gW2k%1`N>cAeU7La z_$7)JX36vZ)Y&jmnXM>)dCgo+0wlEIelYC0lV}b$5Gr*si?Im%{?g;e={egEQIAx@ ziedOEMQnjpI(eq*Edb5{0*gcVHnYQqO_9^TicBvC&|B`$v4&)bKjjs<|B;icWbE|s zm1dQy<>v?{y0>iIDC?*T6pe7We6F&1LNan7q;+Ujkl^vB2Nb5TeFm+ueS5D_z@^gj zjDE`zJtPw>N*V%ANcu+^bE;#6D2nwWV*DdGQ&LD)YDp$8EEUbt%~-p zwl!%kkj5oOnGAD6EJX#P$7e0}-rszD4jX(Kl~Y+j(ub&rGs&l?QwJ#whYlo-X44&^ zo3-}b3VCxU=FL9loqQI7ta8zozGuewJP#~$?E1$w!I;u|c^+h@xxR48K{Cs>iM|u1 ze^c)GzG-5B;Xac|r-h#fI?fUi{FNMX04WQF&UmSAj%dM|G6a^(<|^Kob%))=euPz$ zBq8_3^NwkJ5&Y-&%A%X;F|FCe6r`2-BgLl@{j&{EL>8s~GkSxy-{O9o5XhRzk!o*V zJmkTV?}rQN9bhtU@RR+@cPT5p=tJq0Z=ZF1TeT{Jd`NV(+}8s#KsBZUuL>x(^ZMi} zsD^@YbtpTH+l1nmop=#4sC|PC>?R{V+e?9E5+1WuV8qR}{I`fN>RM?E2c!zbZ$K$vCDPP^gmYxNzniPkT|y$&s|j|LmubnSxIrtnN7 z6{!oCkS^atm{Zvc^kp672t`V!_htJv&Sozu@2~Barn7LVs`7aFcy4_Xb@e94h7iqk zyaIC~W=_AYycdwx_&Uo?zg{2g#bu{x-my@u8=cgWLDeBIZ@Z#B$JyjQH|VFV%yM~I zz1q&}3F>qz5aqB*6-D##@BpHncyhlZHRygheXVBQpfin{1)kxlE39PSAilqC-Rc|# zLOT>1Ik_2(x*QJ}UvefAvrYsUvtw)ow~rw5OWDsvd^Wb)VfUR>mzt!}N?=WCMJ(oy zf5!C#fiCV6!e2sb3&`cS@r2+Rt;uy-7bRaRq0Z|};?lp6rY6`}FW2%L+FX-I%cWe( z1-5mAM|3K1s?ZIp*6lZW<3xNmJzC-$reA7u;0xPAdM5y{ST4@@S~2Ci1Qr0M#Om?I z-;T!AkMJNE+ko88hSV<-b(5-4ELh%&`_&Z{yzsg+8}SbEdF z25j7;FntZSk1^uM1z~)G4H`mcD`EdbImSgH$*+v^eQp|*vFyZQd+z<2j3Z6|RWH2S z_RxLQy!8tCH_WJqkDwDc`8zy`2eN@Xl7{04o^ly^Tq2!GZ6pb`&;1HPHWw9&;Ip-p6O|?|hv#qmC_NP47+eg_Ae>68#~XVD z7P~2r!i*%oQP`q_P`MJsPN+%AOku$;R^}Y)r@-9YbZ^WQyK&ZZC~#{e5%{oIH(U>W zcVVi7TTg9b!W;gP&MZ(;JTO@2Ky(w`jF>rqtG`PydMx;-(I$b#jN1d-%( zc~J!GsTJR71@f(%3KII`h7}RsEDvJ_by<+>NLHRkSiq|meJv+dk4-O}G$9|0pU|9|PGgpHG+`{zg5_Ve@qu7Qrrjz|AHHPBYlzf=QFcOaHX;v>Yv5u;mponZ0{Us8dYvqqHKg&Hh(eJDnW-^5`dwY=4CD zAHU`QM+A$axNQ3P(E|^`_`VY;j$!)(VyeokQyj_XxA0T&H<)R>aZ{Tvtu`WbX+pxr z3W?NG#FOGEO-NsvADq72K?r8+0!|NnI!JniouUe=E*|Z6WXRfhIle%Q%HUFYhDbQ%H(kD`o&dpcy>|4x7h`RE=K83 z4!R!?P83K0(o9%7{2h=*@G##|S@Y8ak|XGS2BgZ$W1l#Cp26Q#8oWu^MXg^-F2LDx5b|EVVP7U(D`1G;m_uZ~d4nxwqy zS)L$k&_YU!3%6uWyRKwvLeGjGW|-83dM(0drsYMFy8+0ONn@q%N>&I&S#@>q=?mJu zqKe%KQgfzCqdB`$Zyz>GmGotC*CgCa^b79eYw>ELy_{W!p+->IRS_UUP3>>keFzYT zExxF&PIl4;;dv#QuB*(0Z6>pOEe76dXlwC0L$Xx(euJ8maAJr`*bg(qKfYF*x5nPx z!uUv2-g?Xo&ji^72dz3)t}4E#Z7w=y^yQAEmE>b+PI)k&>|CqFR+7T$64j&`nOkzD z7ePhNw)2I5F)%BZ6A$&Cx8>9&KBYVdIF^CN@%18%$o^@74D`s<5Rb$E6=81M@R^Fi zkCBwH^OeK@H7Mq^5bGp9CPO64NW^1@>BKwt3jU4?3>+7Supyq^w@!CJyqH|XzdNu` zmtaU&DLHmIRc=ja^at=`g9cxgcfR#wSn_~cppWD|cr6>QM6I;Hmq5R~MlOs1qFVsD zPXy~btoAF@ClU;~H_hiW~*{-?R;uM1~=$`mRh4rEvnNvwlwT)f(`QQ1y1Msfn z%aQ50iB%w?g;d0K1XVV!!LEhK505lx>f-KVDX$oL)|uAd?ie~>hWYqD0JoqQ`$BgN zz+tm%q*tX7ob)8r zJ7@{Wwu1w?g+eB2WEO6I*0IdBFnFc;P0}|QOZU4d_m5V53j&kM@NxEfQ}K)>XdwWG z)^?1UhhZtI7}B)RzeAwoEP&3-Y%(d89n6P%-{+RloK4T;*oAIg)|%0hLy;*^ucfwO zf2bTgNkH~FE5uz{yli#kHcmz9SH-(jE_N}OY{WBJaCm<`@^-Zs#2ZN^xnAHc!SL*= z>l&*vc2*%9)pER;9j`A4^wFa3i>VvJ;UvFN?85Yxquw9~=vDs-)1HLQRc^8jJ);hE z)J)EVs>{YUqWpDAAtEQv8SY!fft{4Ibi{TK79EP%oYGu&<{6%i8~*3KUYRaU67f%B zJcjs|DRIWKQ|QiGo^KdROAxRNS+o+5iQ8_wyH2x>XPP@XYRc=y+4){9RM1GkC1od! z;%Q}8I=i~`BXhyMRgu-JtR=5r3E7?B`2wCm>#mCFZ#}+P6m2GiVY`;MR*f+y5brFQ z>4wK6JljoGs*8B5Ac1f%_dHYNk?#USGDdHeMt*If(H9xQ889ZcOVKZ-H*HA2K@?XAB=j>)Ry#{Gk@ z_Ldn_k69PD{GQw0wRaX93Y}I7Dz%tsqol?NnfF;5H}?*HqhH|1NTFQqR@*>7$$RQJ zo>=G+#O&2J&?-sTbC@XND9!hQt(Z$MIcg!is&ID2UUl!#wf~AE#NeKgk@Fgmx;G4Yp1d3N=IYgJ0vey2K!spK#zrK~Y@;q`C_9y|r zPHU~TTO=UE0Ql8-3F_s$Ug)2dumg)efoMYZjiDBPE7gEIq1aK}YcDU0D;AfN6jmGQ z65IpPg!&5JDc$x$e@;ZFtrx&2;N1wf0?-F!j6h%vku~NloP-@sIZmsH;fLcS$&Q2I zTX7LG`i2qdc99czgZdf$3}`*CI$zez3|rujV624nmxUG2Rwa5yOVPIR2{QYW6 zKHlAp&~*I0yK;X9%2D9w{}gO?z$!^Zi6nQWFaJ51=m5tw=4FJk5afy8+dVT0egjJk zVd`p^d0&&)K^m|C&YKTDwGiICbEn4rgvH4^lk$p!6J)jXRTwE?dZsmOs&|03%`>o@ z*LmS$H_#l0A-~TD>y_&$;quDzkxYHKFm*P&WL@;9psSa2$cQS64yGY3&M^6m5OK_G z_hAq})p|!@#jVq~P+FqfzsRvqLcey$XZVR?Au;9g zFlrhv*2$91dQBVG;ocKHQ2{Z78ydq3u47?kSjYH(C?asYu%dteK{_XUq0XIlJQ2B3 zBb~=_%fQ^#V8Rt%N|1RsY>ztVo%MK9VP}mSS zua^7+(Sd=Mgm{vNLRLEnt-&5vzu&}qu^n9Jg(&tBpp=eVz6`>GsBu3?@(rxB8Lsoq zx9JwdIoj2@huXSGxz5C7_%P$?pltWiST}6!aL1eEy~VyqvbJd_e9HJlJoqA=152(?=IzySt;Z8J&x$_>k-k-E<$Ka+Ftc1 z&@gaN>$Dy&3oXRAVb6Ky1>f9zOOeIMC+4#O_d-!?N0oBLry69~rtuM{QIU35kbW7( z31pGiD4E_*W;ChaHZ2C$8&qoSX-Ga+zUPb?m5&759qwheA&q{QU?Gcdd=fs^n)%MX z-M*KapF)v8tt>%oIBcNT?3F*6&~z#v<~h_9#H5e0P>v5<(b`&T8aD{9`|fV|pfxuecFs@J zk3@w;mB>>o-io!}PjP3>?t3=pkDq0 zs+mJk+>(9z2TEVaE;uMVXweA%_>T|vqkL11apO6cASU-tV#;cbCKhCN)l1m^J)L_? zU!xyaM~3wUwLy(laad-`lzlp;Bk$v8IVb&Y{ZD#>@5i2NN;C!|=SAWMx>u#aaUV`3 zfmYO}_CSu^%1uTgI@F+k#fdnEmkOSKM_HVr|zoyFgfOc^fdk3<%p`zmI zHKd%m)S=wLpHR4KWG}sS z7y3#@AK-rb3iJE+5TC(q9{g-hQ2((45c{X8O!nV)%c@jQf9pbgoKG6g8T-W|N)ZU~ z&ohGnl7p;iVoF&C6wMba|FBrMv6-`;TW?$8XTk^UXD8n!GtKmi)Cy=mg+CaerxH}RLUI9FF|_CuHU3SjeG zsQO7tDdzTElw`*b-E{<&GHhVToW|#&nkHpRmS}HLkeu)tQk&Tp<}U-yQ6@STr;%iC zq^k-&Q&?v^t*c@s!si!_+8SNuhNF$t97_VL>W7fAiChzo0uR*tgR{lk$c;I<$Inp% z4jDZ}8Pof%5tW;$TASI{<1FN)E|;*1x0%J!!;3C%FK83N9tz7dcINkn%woiS?U)64LUZsp}?MHlBY{bfY4J6v(@;0Wkp>VrW~?tW@1ax)0X&#Lw= zxTZ%=3jpP?pPA5QZmF;to=xRDh@l46AVakr{WC@37n|PAGT4%82Gb z;$Wa)r-!GqFT_+v0-yxP*5hI+L+7jZjbX76kIghe5y4I~$DDW$Fi&|Pt;$E3QPAiy_jTe=lWsZW3Bh#Y5+!#?-0|6#%iKIh{nfg6t zgL2>)u&P)E3f(g>Ww;w4q9)a!tjh*+W0&TK+>%H&Dxpwsf8G{* z-xZ=@0{Ft%oVuo>=yBuIlNb>eVq^Xq1J?! z99L~(G9J;$kU}r7Kw#GA7&}E%UH$UtR9=QmZb;TJ{tGl}yYKE9RHmNpF7XTEuiBbR z-YPylHM?}}L3h@m>6PYXVFxz+OEkjxTe2o8rdyKnW$ImuFkB3|fF9JrIqHG~xRLGz zny=asO$s>N0jsUhMWKY*NqONv>dcjO09xBS0#P>V!GW>Jw(oTOzL{^Nh!EzS;Ul{s zD%@9FhQL+UDG&y8rc~ituKUoZ+uia8_x$z9xesr54*e;>aQMgW@IRh9k~6R{HvD{R z|NHkp4$2hC{XJc;v^FqMt{sMlug(VBMs5)CqaoCPhp@LwI=Gf~AdNnO;$TVZ5x_f< z-MlBLjOy3HPsa&!cEwrT`FR3-CF zIhaJz{d<4yuAhEzj1b`;<-*RAEDJ7OWgFtSMYE*EoU?!~JEP~{2OfF!vi(kg+NoAT zPoL>+_K);g^`9od|BU~?wL6MpdOuei;DbNPtPKnn{L%3Ezw2n-$`7FCQG^1Blq z^=o47ke-&7;KT467*-&r6Hkjuorg=uXjcLknJF^Cr^8%R?lA;G$T%(h@5{_`JT)|b{8R_ zVVM|IhmYMj>rGxHU!Q16!>oV#VD)nRk+}m8j-xE1-3QyY_)vM;Q(JwZb!M~B-=%`5 zG9L&`=7@C-)rgr2tF?N;}`$Co%|5OCggPgQe|PxjxpBC zT7rwWE$_e%gza{D=7u!Ic9r^Am_AD8>nmtCYD4p3Sbtuusg+~c$HcHCOTuTk}h z?mA}rFZ%3QA-%zJr1j$_gl774q=b)BDo5g4RZ8s-cGX78 zW;90Llau2()3|i96|oUseL{sg8n;|mg%K*F10GX|1=FHLy@iy8+tdDyl^o@|&|{iH zjq;47*bTeyQHg@nN;Z-7fxVmk=gl9EkwW-R@+i&_Uyn35>#Ko=+u9s!I~CyCRlSrk z@Vtg!{+cMOow+~y9r;s#ME@U;=KX)(II_L6ef-FoAwO2bdj<#KF6HTLZuuh-kpKxW z32^asCSB?YW!9}-+CjZ11Ms%Q5SnA%zSwq+cce{Bcx;|d(|u7p3S@_zA!K$_*rV!` zV4QjcGv!coyiYVyOl&?B5@gn%K%vWu3&c;mV>6gN-8@_Sbx$J5y)XsNp}E(!<&>Lb zHzcmI;rPP3J$Q2`f&w{1$_@E8HBhrV*y1#}S7(&QlCoZHB*|s`RAJ;ZpJ`1ubt~WA z(6+FOk`5;+!8Fp^IE(lxAdL9!!yO+V-l8=4BoJtx$JR|%Xlum<%D$x+d>GeIISqXh z|M@SlTSSU!E>R!2b8^<=;1_Br05p9sy`~h6K<=8o-O>G3B)yT9S_G z7lYZ64hYU`S}%cK$&U*`1Wu>gIUcm8cD=njeZI5^vx#yI5#$Y4lxrbG*SGt3+p9XT zn8MWyLb^I8BgKyzRJYuSBIa9hE@RifZ+%nqZucN8L^_X(EfB!~lnvH|u#HrQg^bBke+w8=6*?rp2MEY(2UoR$8SR&mnmsI&fNJUubm*yoXyHO^P>B zclg|$a`NexIQl5zB!tH-4M`8=VFt1VS>#7vc8RF%?ga(|P@2z1&LYfVc}iR`cH()>@H3)u#_(X}0^Q295U%axUVLx-RQoW9RGtRTUsBQNn$?mh z1}OB&Z^4kd!_{^Vs7U#wk~JLH&n1f+l#;t8?!_hstL=-$G>Zc7ad9X~Ch-p!C8dte z_#}_sO1S@uXUsrO%xpHE3W7_k<{&d`hQ8Wi^dvG-uF$`)Zr^OjlCw{sll}Hkb~l-d=t^;C-o47$*uE<{-Wr5LoDS z1Y3hPri$G2Ba2MZ%z}A=J_8b}ElGlDh2+5%Ce*f!>dNx5?JyhxOP2%t3D=l61|U>S zlmy)lUG|*2t#EW)rbixCh$uE=K=>&$2RyJh#;SpJI`U|Mzq?e;!l{t@gZd|n|BE5RSNPtoN_qX#MEXwO|3%t6 zMQ0kd>zY-uZQHEawr$(V7rSEHwr$(CZB%Thv)1n2y+@DH|H(RhPv<*5ujjh8sA)d0 zb>3l;oWC&RA79=%zYoAW8n;j|=EZk0|JXyAS-e9Y%14`#O`f9M{xuAnl^Q3dIL_$l zP50!Bd#S6WT*wNuiol%rYF%6%EHJ5zWp$Qk4J%m{eEM%$Q_ZzHc+k&}H~x>divRIs zretqz;q-qJI8}=OyGU`cP{m5_>leI-!<-NzGL&L08W9nst_k*XIs%&spYV4&xbI&+ z$xU?xi>|eS8&f={>#hMWukJ2j_JQ7!>KGn;Z$UBo@_vMNivU3fC2B!Csv`M=@Spt) zAqS`I-CNjE(`1#RV7jM?S&eijhN`7ACl1sPQ_JFuVExg+y!UpSPJcBG?;9o-NI-iW zk6i(>=MpgQkSs!6;)`j9E-{wFQPG;&kfA{dK#p22BQ=sys5=W6OhAEda>Dblm+$HP0$HHWB-IP2b)*B z%-;Ib|JN+{Ys4iUho_#4(X z2>}Dfh{bl{@DhuJWwoRa7i*K!0rLt*>58taedC$H=4^=vESN;qaFt&YI@-K=zG9A{ zFV*js;e5761$S!AZuhTW<6pb}Y!q|3!g}OeW!40$*6Q)YYX|6okbO%|{GA-fmd3_X zH!`i!!wUt}YZ~awI>(=)`NQ32)!%E^ktP>jeJVg5)PI&)XSX;RR@Y?H|+$9AA znq@KI#Zp^BK%3D}q1?$oYm5djyOONlGj8;;k{=vyfO6(1HHi%nP_Le;*o*X!m2USB z)&jsU_Lwaad)F#E$!Ui*sXworH+bG zLRBIMYsHGKm-TX4*X?b!02G~Pu)^4#E->wuL|h^kdW#EqEskB>{$kfg%M#DVI!)A7 zWT#mLM#swCGrX%MfFR}}qB%!zbD`r^-@(QXUY=A-O~$8;jrNwD3bs~=8aqVYcf$p= zs~NP-#P!S5BF`l?vD#os} zLrxDLHM`Zpr0%va9LjIp;131F4Ck#JycKZwU`s5tcwfVk*TMOOpPgy*GugFSl4aVq zMvjgRy$7h<<0sKO-+MFwOvnq(cm@YXJ?Uk4J7yLS_Sb3vV;PmU;3#3>mr+m3D{SC$ z&nX7sNt?7C*&&FhhnA$96{Oyo!PvD*YkECxb`_P+-&$uV71aw6uMr7IZJ|~i-f%vU z9xn_^B4Cm|be;!8YN;E$f;5p%M2svN~zbpyn$I z>qN;MRZbt=E6vmB2?Je@7`4ei`KG^3H<;%4Up2r!BPL+$QUW?N5BGSKV5Hqf;+!%) znhmA0q1mgMu4qh%86zpl(dHe#a1-aBk$FR@0>tjTxGR-Q53Y%t`?M2x#!i`>*=Pa& zf7Xx)pm2u@g!hc4ZcK2ZL5PPDLCkiS---Jc)RUzr4WPa0^qTgah1*IcY~j6wlIRUZ zyicVnGmkPhU{ux9)n$B{pbJz2`*o?=YfApKqf-eqE2n+ys1J@QrJH2w(F}B)o`Z-<9v>zQJRFxnP~beD<~1 zBKX#q5u&ZW5Y`BK$lV7aw5!UH_zFG#dPYOg6fO?ODF>Hid1_W)G<3Jyy$;k_o$6lZ z;p)FT&$cscw{(`FZ#7Pk0OiLAeEGd=B7W~B2SxH4Gap<%8e%Qj|^!rF$dxd$t zbJQm4;Va$mOmx{L3op&m8N*A{5DN5<^K~u-kKe!VW4K0aQF&JVtHkI;c zMX0>R%fG(!+xmIuuA0K~VrUAl8#Kk!H8qEC0)(~R934?lsxyA|SX>qnUwBLJzm}&F zLOFF(uwTCj;QkX;^grGkDg#VR1TFq=L&TIiq?7WB``1;6>v5O+Jqd~5-w=dA9dl8b z3{aQ|#0UYJesLfg92nxW-@t}orlK^>x53CB_X+~-|gcaNNK8@$aJyRj_0f2RT9he6<7FK~GA11`Am2OaTi&m?)*6MKm8 zctd19Mp9A{+K3tW`Vrcfu>(eT-;}{Cn>csl zJe0Iu2QwFn9uHrm&+SWJl@s}&R@Go5hy9~$l;}WYB@DN|n<0`urwp4G_q|XR)t>70 zaFyWlf9B*|I5{>;OVgX$N^1D7IdCj5UlE{GMECHB-d3Q^<6OWJJMTe5=mzMuf=ibz#oR!qcY_FMX#KbKy$h>hO zVKzg{z~G22c1K>q!b@O3XrnRQzsxxjp)T`7OTegHASd?58G-o}9W zL_Nbu$Ia6VxA{FYPQWz_f${c+fPG0owE!)_Fx5n`6)AkL_1cjP#N5z>9UFA}WiaL# z46m3n(Bc@#Z>X}Z5^Cf?tT+>3mqsjCMuAI~AB3rwlGW92m$z6_Hw#;;j^xMPT%wtH zJWt~_R$vHyKW(XdlOXDvfl{fBRLavzBoHuS20L{r=-^jN1rqzsKgs8o?}0dst<10F z&2{no6Hh%QD@iob$%=qGRUQpDNZ~8j;9!AEOcJ)FUXRuWEppkKI2TTKDg>m(=RS6b z35aC^TqspMhpL3N>U3(u(*G`lw}I+hPs~>hZLCfJB)O_nm6n9Fba=KDCn0EI<_)@& zAc}D2BS^##%EIbjG;bg0#BDnNc%bft3)A3euqd2Us3y>s)e&mlG@&(ta_A67rfOlI z3pw9bj5ZSC#3@LW*3WYOvy3QYcdjpL7}=Fr`%2%BljfYGb^iLc}ax+SmMgfzS zG@&xS*1*Lejgu(2EepO>qzJAfxG{3E1{*iI@AIfAcXu?@`HEF!(B z8_j+?6)qI(s<@zS3w|@YC7kLAvw4T?(dOI9A*E-s@NGT6m>2P_kWHGuk-}}KfJ~CB zyyvl9$M}3;-`xMkm;g6K!Vkpsf{=WbPrNlK-5s#W3+ZzP{Zvkj6ByTwg-!_vZM1$b zP)9}=XA^PZS&<(zswj+m$fhrfUoMYNRvaT%nV&CL7N&|KNCGOH7gw}EDPvAo#uB26 zB25aHC~_#AcT=Q%SyCR4=P{Nn5;$~+LP27h`G$(j7d2*fk|p6C@{!-;*7q&^hw1cG z*MpcQus3tI99pSUMnLI;%CaXj&)iY2KD80!-8e~DuOcwz3lY4th&AU;t}Oyv9Q$vw zJokhF;Hc9Pt74w>R7&M?E$AP8#(yMXoR;X&RvZ#6`at7yGJi{iwpheb;V+7OcP|}) zftPzYd7lABDUZ6CvCH}hhAE;VV22F2lcwL|fQV7NknJDB3bog*ej`y1Iz)@V&|PIt zkUwguH=bZ$o;^+gJP=4t=F_A{@p{1p-~0r=x~KM^6vWf_{_$aAy$xzjNI9_84g2&$ zgu5d(KWy1G=|&;{$>N?$Il%1(I&p(Iac{b87#MAfyrQBish`llfC~OwR=8twHY(|j zXC;96XEX+|@?=PAG~NE^$RZcCt7liX;+p<%DCI1tULxL;bP=-4BV+;cwbNUxxe)N@ zsYKAE70Peh^plrSzo^rt&~20M{7hRE1k>35T)SBjv`H&ry##_kJ&2#92OptloF?Ju zvpEJOm#Z+ZkFcVA)nGV;>LG~S;R@WQ z8+P21i>phw@a?)U041+ zSHT0VxvU)#1vhxnyLb7?76YIuQs$Z6XCt(4*5rF|`6>Lsp%x?Bt(udaCfa21OQ=68 zG{jXYRSFIn)rWe5Y)RZ*nd+7Nr|F}+9axw?`h2A>c%eauOyx7<;}4?*HfT-_x#JNm z3tNE+#ayN7k4dkb?&C)Fg14=JQ zA1dx&L<>gcBMQQyd<>--=Njezis=M$ycZsr1xg&>aN;U1^K#XW(k0WcDw&ngM-Uhu z$(9!u%WTCv4Rs;a{IVh^G+IDA2^6N0CmFg-hYhTg^)wlNppt%lCMDfsr-{S(#1%Z) zj7r4CgjAdJ_-GdlU}zcov}bnF<~x9)T*GT@8U*!wBa*owTZm+y18W#V(PNKL1J!7>H^-SfERV`mdxEAD$^sQ_x^5QQo0-leMwzKN_H8x>-HdWUpWto1gytIoKrxtn~ zA_168rw2&FOi-D?cEuStZkT-%?P|E5am(e*{PFrJv0@(~3-hntp`8)(9>|@GjjVd- zlJR5-IG>o#Y6W_JzPwY!`85cADbZc&4TS#MkDY^IWsYtCfnf7Yg3?*Aku6qRyk#O32pxxGFq{ZP{R## zEJl55!u|+0-9IqEEZnSxUDctdd(nI9jV;qtk1&?zYkmUk^-uq@klE_hHY>G}+ z`3u4BLFAeTFmK~DUF@t2F)c1#Y|5Ir7#spebx%@lod9R#;JC6QRyiB+i)Y0)llxFeG(d6*5O3}nxPR1x36sA>G z#snT3(>h#3R!{B3`Yl5`Pt{4IkwGsPK^lFt!Kn+{x>#?D@ih*GzAqi+dbuH@pfJ*K zt<8Mn&?3p~R+TmZ8YQyujYJEgLK=i6$)Zw)ayFRD($}liDTej-U1_ddt_TnUkR;#Kvsz35+CKY)^i!OwG<-yjangP zX!9O)=C71Im@$-Oize3@jdXq+{O8MkLJ`?~O(i4qb>6E&dQRYz?|B_#^!K zhRKatWBxFBM~7&EI3z?Zz;< zUcAnBEE(xjBOL=7etYQKI8*iE5cYtq!OrFB@*-mk)2p)+XtiGu3#?dameDBRJby6u z0zhgU)5obMnE4{p7t0Z^SeiZ(zz+fg?mW`dgsaDy7v8EyWxctvUv1_J)TBWNt@V}% z<0iG*gg;LRzdq9GS=aTF)zYu;mM@}4n&`Pj#@sIXfaye<4s#JdsH}7LF=-O}*TgW};94$_@gn*|6o&2l33rZ5RZJoP))Ekq&Wo54WL_AYinqUt zz=Tnf7H1Mio=IRAAWI5)PkmouMS6f~U6knN|AfE<&8P<6$X3|+8?esBEUl;B1J?Ap2~rjMjCLc9&Bh!pSp%&jt>$*!x?ytiHp^t( zhWxJGb8Y8^d{qt4a+|{vq&jY)f7|1#n2~U1}4#UR893A@vR+mUq(_}~hrzFRMxgFV><#K9JNAq2tiKP?0& z69glWk z_s>~wlSUJS+T)617YP0LkaPS$GbK{P!7-Vj4Yu;WKr@rQ;d{*v^8XAuubZA1 z@JNN3EV$4jcruo$hfL2CuM;n`HrMX2j~B>Zg8DzoB$f&>g^{`s5~nGMBKdG5j09g` zCYikwd0FucBtpX$zd4BaDgzS(nT4F!YhmnC4VZc3T2dN2ZH~N0_L5yEZcnbPyp|Tw zbji8wW@tGtkP%;7`M8Z%FqF3iaZ>_=EK=1FdgJD5pu6mj+<#qC(`R>S&V2j zTlcv04N0JqGtnicj%9lU`mpY|P6GhUry+8*M^qgqXDfk>Y^_6g`X$E%B&V17EEIPyvaU8IzbFcmeTEG*`ybTh1J) z7a|@Xo73?};}@`MgLzCOwM(scYayUXX4X4n_te1{8OeXH-as`4U_4P^4*UZVK?}@H zTU8OqGHu_uJ6PZzeXA_gQM83c`$)Bu?M3=jn5oVCFod3S06Hz&J@U1NF)w8I;=nOBg*}IotjIqWgSg~OYBf)$ zD2VXwy4^v)u_AO4>i=71S$qGz1XD8?PaNjo(#m8?P7^aKDU16y4=2<%_2tN3Glu=e9 zU166%_hoDT?z-sq8Pd`e!tyM6Tmh9Y&_^m^+_0$d1{Ab21c^R?njx+vWf!Vs9>lpQ zcP!5-QCrTF%9|}-#PY0K13H#USN0)C@&CV`T=0h*GP1Lw zH~N39-+BONLw9-;Hz#^aM*t%$s|o#oQiK058fd8t(hEfdjqgiW##QZETJ8@AS)l~b z9vsJ^JW!xeJOWAVF94P>AsH};pg%|J5VW0+D=Pd=ck1IXFoMD@$hG5^^UC}7(xbYw z8h`wUuFA8tX+Ma@z<0~{Pq=nXOu5>1SNU7FT1~|Wx)#1S(68Y7H5_{T06+Bb0g-+C zI_y8UNYN=!9%<|!(FaG>2OI0_>|eUt-+e66=UB>5P;An})I)2c(Q0jkziZHFsu}qQ ztcDB{*samvtC7uCI;Qi}Z8;m5 z)+4_t;9SdXruhi_bo__5{4aU-?Q2SXb6Q<{*sW1Rz`08g0`MXSLbz+8@X?-|!x}E} zHvMAU?8$kl;5yiOkNtqWv~jX`TQ^{h#3{Xya*&94FNe);Gz%A+tuxyjPY(qzmcdu!M z1^=YWO8x8{J`%8x_cr!#%mUzi5nFZ;Lwdpob_Vx!9NgqW-@DnpdT)i;X!=`*hQx6A zTn3Q5G7iXXOHm(`*cKt*(y)`E-`cRT0#jjMf}-`H-dr7Cz(;BV`?}Grzs7E{;c_TP zVvr?khU~UpaDC|Ez5xgDF9qx$BuDU=r#t&M__$wGG%rje-Z;;W+`YHxAPt4pWd@It z{0kT9y^`%TRTa?8C@ZS0tYBD(n+?=f8Ng9bOwWh>r351Z5S?y@ZQgVKyR=nSRGKm& zX^&33tKqxU6*XGQjhZ-S%MrvQtbvKQk5eT}VBxA8PNrQl9Kawv+Bt+x=n4spfvwRj z+7J>-a#~2G`N=lt%Bl$QT)eewr6l|#dU(Q znApk`l$F$%l$G>G(!d28X2x`xfFhDXL5vc{6V>QaipduwG}nzCbFC~Mo~+>4x<1EK z*W}xXW@j;S9eEoF5h%*@Tsj98w57{x;0P%E?a{=X&1h<>AXgq0s?x{T*OaqJ(p5O| zev>N%3_UtW$s{?;)EhK;EP#ixX*1ZvB+=7zmo<8+zJBcYTLcXiF;SO#QEuVs{mHXe zU7NhIL5F3=PYVv8q0i*)sx|zIoNpsFJpYK)50aA0QslOJdx)nTLcO1mG)+m2b2Cwb z9+}CJSLb#-ciQLid$T!y=%-{NOO(#hW47`qh)=+n9P~YYh>w3|BA2C4V|1{@gse%H z%hC~4rq0mh*2ui!2r6QdD#_$GO;=|(+W*L(Ph%=oS0a5x>7T79O`@g(XZb0Wf-5ht zsj##)&%MGBZq`s$Vkwo6Kl2Ov-L8d7VQpTj%@d9>RjQ$^wzjy*52O0E0wbEDvP2(2 z!PS%Z+PD+|RFMuwYhjHMpM#H~thS@QqTa^9P|B`tZfJ5~h=tc6_JI1yZb>82S5c~- zaC(0bomqg<5VW-92iBKcXDXB1T9zEyq%3O`IA1-s91S-3HCQlY^)NYBU^<=5X+=%) z;-oR_0`spu_p*}+WC<89m`M5x>s_3IY?vt;uSMJe+XjbdBHJFYqG^v1l`6}_nMpJ# zjlksVb31uva&IPst;gOFh+z1FD9!JG)tk>h5SY$y^H_Pblq3WAfQF$cgwsrV+zH@6 z>n&i2fJ}-YWgkgdS_Ca-rstxaWmp(48u|82i-xPlAsu=sA0JL@cE^9GJ?4QD!~sPE zJ|Wuwt|b5RH3I$(FC%ML{e}N{+`9%L_tCS7D)_s~!Cfe~KK&r6naKDW6S`U5s#*0=6l$m+ zUHM|oF1RnXd|F-Q9C4kEZU6!Fh_EQufHFR$zhei+UKm!0R!yOof0{Ncc5sIa0*pzJ zlqBrL$L{zCh_C`n@Ye{q!^4S`P1pJq(H}BDtvrTBV0Qa8HF-!x_aW=@C8jipMn>3u zM6CY7$%&aoCv z@Y7V7&znM$ZlT!9S=rk{E|z$9654Az%Fwp?1@-(s_YE*%mlAyW@(p_X?%X-w@kLY(K4eZlD`0hcSy{u$c> z!-B#`P7LQj)Z|>RA!8ak+CIW21gbIF+V&LwQ9K?tp+y$Lz#@;2#u1zxPviCN?eXD? zCNkvZI9;M}1<*AXG#bsF7EK^5ns5{bfdv@L+Ny0{?lN2XhH+8xY~*teai>$Zb%GpN z5RI`VCx7hzG#Pm58<;rKCSBnbFrzk8yNM4JkEgebB4PC5M=(Mj!zRUda(smu>!~ZL z(NYg3o7QVraxRJA91(Tiv)e9Oc)A-G7SpUya-l?KbN58aHVk<=^brFPik$A5kl zApgQ6^GbcgMh(RzmSAZu0GoO@aZzXfqzIv?$E+f(t@HtPz_&N{d(}P*%3A6l-D%e( zpertj!k}>~Q-x$We8K9EEP!T`1*2Ie+BM&wyr9V&`v&)S#ats+Y{uc^hI)!hYmo_s zB9wr&yf8XfJL#I-jgAo$d*|fp!8#^qr;-A$fK;z6O(%7pni#}RO@HI0nlETD^N-7t zsfJ}gTN_AhVhKfV`xs^8Hql{G24Rb%osK`#EX)#I@^q@y)kN3GVB#~QgeX%Wu4?UWW|cpJ zQFk>_QOT{VyWZ^!K%|>Ij|Yu`u#917L5jBujkOy325p0*9+D>v;%zTqhKzI_j@&aH z3IE4w)p6jTr(=s~9ulnC>S;FW+QDKqSpLSwz6qi2G*fD4TM8vzia2qg| zM%Q{jKO_vQg3RLZOBh+K!4@J&M|UcfCP{IY)%`cuWcq+#-FjS-6?$$9?TxsIgoRrLOd3E}S0frLBP54Mk zZFB_EuOSm*g~e2M&hKCr9#&GYii5&32BB{e-d}Q~p*A<}{!IrquRL9xb|LHsK4r}* z0zsBRNT<4R>i>L;f+ftpXw0-m6JF2xRy}*cS$PkYXAfQz#BkW-qNTI6u{N;0)E%|{ z87#j|YKr?jHTPB`^3-)DSI;$Y-|16E$vj3IcPZb(N%^UWmgmn|%)Adi z&qKNpNwJZ$Bu&Bnc=aQC_$1%Y1>UB>dRIc0$WgIGB!An8Z)K=-^=M~9Bhu_6f`T0pdOL!Qd zzD%R29b73N_%X(1 zUUs#oHbUD3UsbiHv=aF`qV*Svg;JCUo%~OKb4SNdA8{69X1Iw;>9SLiImyDKs?`j)!sX9uQnY~Nx`#RDs;$|pM_J`tSLm#uuEj$ zH)Ic~4K+54oBem|F9Gko;SGuoAMi|YO_$TB_TVF)cu*JO%FMz&dVaQR2>M>p%_FAM zCt&bi?euL4?n5`CwfXezvGhr4FDmpOq|>MAU{l7a)(v^PkCyH?<}{*(Ag8Y;31nC{ zF@S3CExdwnMVkGW*oRr#OiONvqj zymQLSaIjL z9uym(kf>Y)b^K!zz98{X%2HdvpEN^?_`$}O67oIlh1Maqj)M}GepuF?^yJ>?=BQ!G zMFdxWuVVPuffb<%mFz6B6HEBjvV*0MFfmHGp$8IAax+hHOyCzOW(H=I=@1hq(LzsZ zV+Ou}XTN`uus5^w&-nNSG}GGSrOoEPr^F(W7!PA0b&n)=^3<}%c=RimFRVb%p+>mT zH=`hR66wh`+M;>HKA?iGE5+7A?$caKh>Mex@w9|5Oj7nWk9=}crui^Epo=I`LA~IK zLobcD0Z-ycZNPQ0`kCHV?fY2zAz5z(Y7LAZFYtpOsVF|Nh|6#P?H7I$J*82rQH>QqXj4Qvz`T8)3-N2nkBQH$IVq@ zgj*ji{ON1neAJ?_4y4(2vga7PfU5{ygllALDe9fsQYpfzV$S4gwOW4EWx81!8+dF3 zi+&tqho1UuYiU*Th)Yn~(LXeFm$nccd?a+E{qlYCl1!(x@f29 zEw#L$6DePdk8Y_sl1~gy8*tyy1~nW!^;1!w86K!ZOKfE)8}`enPboTxrou}Muu`~O zu^%>|bwzF(&844HD#0fq1-<-cI9j|qK99+6-n%mFqcGmaVw-U516pZ9BLcP4Ks6EM zZn2r6KE`F3MaA!w^AKf=GqxWy%!e=j;zU4)wFucFfNO2qg_@b!gmlWRP9)JYN64Q- zk?t7X0@244>3X8QCS~M_YR^L4U-~HxQLd41;~6w-7Bv_1mX*nEr<=cuE7i<0o6w^` z2)Pd-NF#vVX*#I(vx>v|TW?C{a$pp;oi}C3R8ibq*>G_|&=Jy0{9_Z}o?=kbwhb_j zQ1UCG?Z%C+>zo1UQ;HQ|l-$G(+yj-EbAA(ElyEsGnc(!vPxfs|YTUR+G-YL5gE&Y{ zyI8&RFS0F~>-g0i*8ue9DJlOGP8h?7^ojT^!3m-_tcvAvR!NV|@APW|6-5g(^!>?t zKo~=A-~nHsLT`@Y0}IJww3u?IpWuiKqufib;YtsLiLY1sNX$PyN5A^cPCfSnrfHnF zRPAZN;zZ-X|eJ z?1zxnfjlijFi^SJpRtziw;<@-q!J~|v86V*jx#j7XFB5R?~Skn-X0>mKdR0#>$ zS_tJBh?^#7(5J2c^T*dHwNQjtCKz{29jY5YXI7(A=Mpct=DqLSrXt zUc6fV@@X-rH2?>L^sVOV*Iq&qCh_C*M5?z3@Zy1cb0c~0hiTp-)p`A|UTc3&!cLJw zATP@fCh~&xjCXf=RaLL#eP9cmKtpd|u|#Y?DMN9rmbgLrmv|!YS3Qe8_@n7n97ys~}9Z6}4U$v^ASXD5HGhtzA zL~cnt#@!MYg;GkdaueEd*@S+WCU{B3<7^>a2#Y0fW({kboLbEYQ|U$9d6ip`2ez%E z-H44-cgVC}-aY*y?F&4X!%n&HApR29E#J8g%wk7c@%9XiGqsdFzW&xCxJLS0ER0__ zXay<~2v(z;mOjEbg*jU49tSmk*an=jL}R>s6Tb9sl`YM@8r+8qJ3pKbBpxilFXi6? zb?h9BiefJk7lr2JqX%b=t!rSnxgvbYp$Yu41wjO2RRp8A3x8W*YhwqJKha|chC$a_ z#YZ&|K{-m+V-E!SQRKi!CD3CJ90*3N=3rbS*z$OM&Wpo+T z))}3{ASyXRqS+!UaT%`a8?ZC7OmQLFL58jg%|(mx>tI;DML3q@k(X!Gx35%RIhT*v zpgu$DOm^AO|CnqFaK&EL-O%&si-nEw(OE58L>r>Kx8*^^?u@y9Yk%OKo`KjqG;=#B z;;{`C74!4Z=P6JC5z~5!1$(TxM0?CRX!?gPAd#Gt{(MNtW|3%+rdxF^{+N-P|_fslWdlsN37)Ebd{&a zCYx*Z4V1Sd&)Y5t^oT(`=aDUa2FcsD73?C1e9R_W^bD52))4GEfp~mEIOmZoeFn)va{!Y{_K|b25hVB zJ7~3-bHn+__mbES|6KllOZxb$yBu$jZ3)dw&t}MOk@Z7@SY#$BKw6#l<>baCiH}b6fdEv03TwZ>3PYCNrZ&OMC1+Y zoK*)y!;!f`-fW_bRcFtZl}8R|Z#aq<5DIzbLp8QA(ARPTt5EF(yJ*dX9HW}CBmd2d zS^|8rb9p8XAgzs+16@xZS&BH!GAkt79+i?#A|+wdRSgo(6Ch`CikO`aOul9m0NQn6 z%683ok!xVXO(Pd{OH{Qj_$5ztL+sv-@I)lg$=Bs>CK_}m3$>5cdwpv_Lul75U-K3j zJAubs4%Nl7ZQXs)P;0GHU_&T$)stuVLUe^U8|~#-vWaS@`jk}L{!{;r_yQbSS`^5J zySn74XKD0v?y}If5b3ppz)8gi`v~&jm|whbV)DO!PZHPe@ngZ?rpl9}%TJgTu0XkvWHUugdhg!sHa(bW#*N(z_eU8x3S7$fyZPGV7 zFOoAI`FZo5{(ryQmyx+O$p$UD2sHiyCoiLWSbgdjF5{0k{+7=(>Eouwwrs>vuRaoJaDmCp zgY}1Q>bv2xf!Gvz#>@5OI@@k=x)y1FE_0#r%7YE4Y4Une#fJNy7v-;DMUtudmC&e= znjbanWJ6?PO*>Z>v3gRh&*SOC6v2)S;(py;YOS z1*;9qMtPM1yoJ?T!#d|vybIVnbds;$M(c$Ekt46CLy?2=U4FZnNKVtBl z5|Y2{uoFWZdOrQS3c=3`A)Y;hvg9g>a5Lqoqj+LvOH;%f@PZv1!Pmj9@o#|p?WW;Q z1UZ$RHmgSqAXXB5*jGH=;*31?E_OYA{fI{1U5H{2(Wu+v0j+mgCTeixe>m~jRq5mB z6fd-S(+?xNP8~BHz?P(oINC}63|x$_XuoQ68gI=D!D+y^*VDAy%CUD8iLpNQOP%$P zv`PPt_7U+gjX6l`O1^R}I7-TX)g;Y1Iog$FUlvI%hoV1jQU**(b|0^>CWN2{1%*7m z@%V`J>$7B01A%oRadutKV$$a-6=Fj{du1(MAuDk8m3fQr`$@s`kdBgR20QWN?d9d5 zqZ8#3@i5cc1cs3L6?qmX2R1M^_d?A}Fvk;bYK-z-2iY1Ubue(~ef);r4>&Z=#Nl+$ za>0IpH;EZT`1GIEn>sOu%P6AK{>d6KK_t6cPjj|gEhFbRAv&Ud?7a_gOGaQ#@PJW! z;X#~UL;y&cNm9$z$B`fMmxxk4bjpvilj!+qdUkusR(iQ3S{x;4uF+Qs>R<;ZDc+=p z0W6m!66Inf69-ZsrtG~IIiqkVuwBtQgWAhW2cm39MA9!+P zFjHY$M2Qvnz9*0#1>X-Rf!@D%!#+2&hy9X$`g~(2fgiH9&x;q(Aa2t8>Q+<8WnYqe zk#j*V`ZDEX3JyBN`qUm_Ka+Ge4md)Xa)3=vjEgZffcIhn83o>f2zEhEU7tdz*#Zh% zuZk_-g2Pd_GHN0~vG5HebnO$o#Jd}O?w7peRj=8CH?xB8e_a3uhhzbmyK@>2f9*j?Zqj&G5(XSROw()C2zZ1GJ{^MM+% z&|R#u$7bF5giAf&ZFaQYv+8sS&MNmFHns9Kg=YC}Ak~zk(a@Yzwc=x7W<`6!U6u5y zYh}~|(1%Y*SnI>|^zT@W82smaWr`1gfA`Oz8{VoWxpHtWxi@mh>9gpxLV0eJbo00R z^W0H?7~=d9u|(G*%DA?Jm8*oa6FvV&|0)J$JwKD8$F%bPls=TB|UZnxa% zwJ>T^R{tKiU5>m9txFe1wQJ9ThunFm2str@c8!O8CCJ8~_$Q&vhrU4)BCb}wc7+p} zAZG-{0Xr%mzp(fGwP4DZPS>PPbDvtuBO)u&(^sBsw% z+Hv}pZ7uvs{8vA9%?{DjKl0~ipczuJIJ2RI>#?X`r(dd_zYbpDe8}m>uLrq&$YpcA zeAf(5qinTbqN*=lRl#l_YIihHj$Ahd#m`)eHLt{bnCJxGvi`5u&H^BdtZTp^(%qfX zUDDkk-6$Q>-7TF0(%m44N=k=>bc1wA2}pOtKkokDy7;O4sXGe1pv*HT?z!ildFLJ> zH)laSm=1z<=gqiBzPsGV-F?Ov_!&#iFa2jIn^0Fz)7~lCEI6YGzaS61iqIpIYEqJ> zX*@kfpJy`kpt;_SXb*N^wf7f#7SV3W4PO41SFM)z%L%>4<833pU(@pmnDbF4358?LE-y+dJN- zc+l4OMFz>aB44IqwvK!7T)w41QH*U7}`4&}=5*XG0atcQy^MeF@AQbNd&47tC~ z5kAni=fNhsWN!n(552ly<0*V`N5-ig#3gC|9(wP9TA4#*=+n#tMXn67)%zasMJwi$ z>mK-zdIofj56UMYyb$VwGkRVx<4%x!5!CtU5czmw9EO89VP`l$IbLw$vXxqXuo%p& zpdkuc8nfP#;qbP3{0gD561kBeP;hyX9OE*=0_^@GzD)S}GxIcAoDv16`7HCa#1(Jw zbOW_e2oru#2N)-NkaONUCSg`P-Tjl$9mMa{Tf>Fa8imx(pt!pRQK+3-p!{VlvN&+5 zoSM-)weCmhnQ{mC#ic}gEb3QO6Unp3dXRMnhA4;M^%7oen6?Yrox?YTzUyR#y93|i z9@*w@U(DNOSB~H0;JIOyq>=JsjC`rchmuGcWJFxDPpY|t z$-Ij1?!kdFCf#Ka%ksxif+AX|k2E@O$c}|y?RtHA3boL$vNlYrwqXBZrWzX}1lISU zjdxHjH98&|qsi~?M^)du4#g{|+0UUYj*%_SN!c!-Qt}QZhuF|Kt6f0*s3xAofc!hK4kdp6-f00KF)BGKVH(rBl-B|^HSPu4gWcj z(j;Q;_}6lV=oSP+uKshr1U-Qv7f1v2`YG#fEOpw0l(bD~GndTCdQCZnm+n~I#ZRCw zr8u5h`zKveC$<**NO{rGE~aF~>p=Ehx>bkIgFHHYYJ=GXy}eS^l5@%Cl0Gjx;R&gC zg5eT$gUq+&+>&rff0{HeljwO>dE?)-&A(+hOF5$J%dVCcMGjXrDLg7(R31%8*`tf9Y8n>@84zMQr|o zu=M_=L1lz@OaF>a*}>U7bX}8v!a?!m)h9s3ZF!6^WqHIf z4R^7fA@rbg$#CH3OHnZ5-4wl_B6=FAM3J}Y8EMvLkn}W}JRlS^y~7J6=sJhQ$uh4Y zYMsan#{n~=i@VN#{KBw`7gP{B3|&=xgM^w?Lph|+K@p(n}*Ci5|BF_ zrj=2`m_eNrobuyIRNtfY;4gH1B7FrrxlJU>MK_pG)oqCaQH>x(UmF(1BL{J8<8;;t z3ln@pEE;D6hi>G)U|3pQrQdrbAh^BPTv<%$Z#%he!Hs5RcEPBe?|lCB?TBCIxrafy zv*2UVb_B}gI(WBOBg2A?7afMrt-*AV>cK;K%F$Y=)cRG;${1psrOm9!d*esrn*?b0 z9pK^8gtK1@zpxU}nFVvLg}&#&AU(L0DJ}N|^++U9^3X8*`6o&F!simRij-Cs$gnRk zq#4?lQ44V4O1XR3JmeHPP~ABLy;~Kkr&J#!rjoF+sf8myN_qZZwnG~-ub*Hw{D4gx zGA96jCZeD+00WvcQh>=c_4P|BwK0{*zJ92p)P;f>rvorjVZH$I^1_Bm*3!p8 z_X(t0?;L7>7;_h9Xh4J;yAW!eBW(W!CNWK$n7#TIju-PWDsO;EoLBfLYlxwBfL#`q z)cunl8Iy+6$nudhGex=QzQws@5zg(QYH-Ei-M-?PhSCzvub2^{F4C6T9n+a{mAdZD9o&vD#h_Wq~svcvFcy=Wcud#bQ4 z)lgQ#LNlE=RF&vgFUmr%NnCAHFe$j>`30onS`|%5-{E9w3~x|MgigSJns9`6ps4#e zLWzSwbuO`1h$+DDi3T?!E}X>&(S5YkmA}-P9#Eo8h}d(4VnSLQxTbL~QmxDo&~qed zlco((wXtGT%m?nYM$uw$?l2+afH;JCn6z8Q_u#k#B={tjjIzp_5w81^*o zlEX|r_q`m{o9T4a2*eftCFhH!+>$~;ylrPC#wu*9avrZLjs#*{3x|~y1rM3#1d{3y z;zj}u_2s8VaH0u9WRc2UBd??73YKG-l!YA9_>dh@(zrtUlQ|S#hP?8h^-}7btmT19 z2vge%WH?}Q_E{V+4wB{<#W#&F^Qk&JV>-*vJFS^d{KS6nRCj}8$kM9mV^4iRemcc; zKXU~F8g3=ZCTNtUa|VuT+51TWs&K(Q4Opg8HWLY+fRAPc4_1MK@Rj!{08@0-Q3n3S@e?%NfwP7ynpEm0Yk>qN$3&4)jfm;T$!bn(8*(KqcmtL(J zJj*$EJgxTP<$E>%WMG|r@!-7gG}H^WF>gL}XQOH%;e6}#l^5i7rggLThPPqCd>7mm z{o_w3C3mm1$CR2P;YW6i?;XB+DdGJ@W|+{B<;e1-(0u=kLwt**Yp6%BcMOf!BTMZM zJJQl7d8az{s<%MImM=Jt6EygrotXQi>-*5AHU!VXh@dcwak|6fKMORpWK zc%9;jH3!u;Dflpii?e@1MIYQoG7V$fp-T4oiYV9EY|oOcH2P4=WKukc?fQjqhRp(q zO_&a8nGa?H(r)y80L9&1>W%h~!@1q+8O6&O1?ri4pabZI?6bq~<_u@OOitnVG=hNc z(=_q2MK)UT(3K6VYHWVfim;sxwj?}iqZOmvA4D_~R3py2B*$f*#r|&5(|tZ5ysymERl+~<4SuBBK&(b0#pGVCLE0Eoi&n1AlS_rf^8+AUe zO<8lcpA@Cas8vBp%{4(i2#)_iD!9P1 z>9f3EilFwvEW3zG7Rdoe7V{*n`I4lD(HB-X5IS@Lyb2kzFc6XX>D@JBQlW@vckhK8 zChDGM%}DrQi&3YVsh6~^A;62_yks8M!`I5NKJoJ^g6%$&_Q{=?Q;cW!% zCcP}}A8gyzQ^i)}92yh*2iV#dAabA>7(@4%4S2MTVml@GK^)A1CW0RFMW;=iQajv= zk9*K5#s|rtzvbh!R5W?fS#bio%eZ$%iila!pjx}tEXpZ-s=@oo6>k31)?1=@eib33 z0?hg+i>nA}M!UK;U6&yY?vT>iDzkRMPrrNW8HdmKs&#za3e%>F#2n7aEI5xE6B>b7N@qZY-IrpE z>!H#-l3q^uBf&l_{0yf#QO^zfqFRsY=xox~5ZvN&c;j43%;=&0Hxia(hg?W|th{;0Zz|)AsCNpqoZDk*yWbJc>E%oBR3&~$WzLt;LPvEw zPdH1l!|kx(?p>@b*>rfdrWTK(ron}m?m;A7C~9u4)7N>Gj!J=l#HA2xg~D4ep>*bY zRn@8wbQM2(tF>(JPTnesYfZ{MkoR700CrkqYL*k);bi&PLL*xr17-}Iqg#)6OfL?Dxz#h&uZK11r zG->Bo%VGy?v5I)Ya2?s77*mPKd4>*!v1Gf-bqZ-*w_c)(YMkKcH^i$K=jb^E8t#B6 zxXM3yFwFrX)3ChzN*Mc ze3>bcUZ<-K-j}14zib(#MALmDpl{WW{{fj0>u759_^DlFkubt&LlRDH1WqmJ77MRX+2w!(7E1q3=G3;1O=8AK?205~vb+#|pS2wqCwM z);XSLwj@sSB^^m49nm5knI#?JWt{MwNAAq!z?T;)lYzo53K{`@=+y6|KDgjaIwPkr ziQ1BrJ?QhvigT2+Mgc#*4otnH^qBnVRMp$n>lidyP%*Dg3_X%kI$oj5*AW)bv$Ou& z+mQxkX=q~mb6d%xK9#(A*U&F6QlK)^@y>;o)Y=L+i6SV_9U{T`mf(c2+wqujy1I=%%WsVq>Jcc z7vij4LACEBpdvok1UYg5DRHccyl5HdB1bXD(t@fF^o1*7a2pF-2^)U4DwFAAMc!D| zCiD`UV+6ayUPuDJE4nwUcAL1}5NbtU;Mw-HJ?4EI_WL%7*elu+hZFW8Bbtg0C>oC! zEX21_@ly_;TE^5*^i8;JW|9*SPjAANulhE;>cN`~Ki|+w#_bUqP)y(xAKnS!yeA6- zY32(PRDiOr?3{>{uGR@xJe<}PfPYH5fcy@M2oaqUwQTt=G4Z47B?}aT;~7gN6U=uX z_Yn%xo1~be7Ygg~fJgrZkd0q2dzZD|xEQ15inNm04%m8lZ&iv>E(acdlM0THp+Z%KQTaYOt|2SwP{W=$$-3> zfO3(HuqgwkJc9qOF?vFcYqO{z=mitfH75~wESrVwxVSDSg%sSbNnp5lpwqmQAlTFX_I=IysjWd)iVo8zo}gp~ zc{@5XcUV#1J~Zf1<7zEpTKUt3dvwVjN!gkumd3Al;JBs{ zXt*yEBQ-z+IWbXsV~F4LfV(9f=sl?c5lfC^?B{{cPg0TBQ*Gs80SW61C>hnHJ$iJv z9^LJk8y>D?1Dk}+oDesA>N~BRQ{D_=0>%r6cn^d$zDRxnCDeD;gWOIj5>EC?0T*^< zJ5TED(tTA@z^^~@Ko@oAwBqlHYB;?PwsmST`?#qkUD@g8EKpDObmGAnd}TutuK5y* zbi`csXrnPzvFE@rL1!-p)nE+Jl)72F3iwFS@!;Mhl%Im^B@H!f))YK&Buo5A9n7L8 z99;u3+~>$@)Bhyvqg$d$I&@5LdFBicZhXpWOVUS(>Ruj1E2e>iR6)x-l`C*lHXHA02VR^zV!!Ex(+E z^)n$m=XmLfGG^&_|25N{)Nvl$qF(G*lJnr^G$;cTpc}>(x>yz$sSq80aJ1PYCnRLtE1-|jpuX{Zc)o!$yGO{~FD@rPaUQ_dOi|pCm z)QxU6mY4<2hd6bp4QH%iS4o&3?fI`TyXgj*&-8Xz?A02#$1g9P975RpysVnct|KKV zw*y#WJyaU2$D9Ndd=|!IKY3KSu&B~|Z}jYFXY7=EC-+pK?ZLmh7bT)i{N^nUecOXi ztWAyBitprbNV^!M4I5GfmG&Kb(D>JmY$}Z`{0OpU6-dMKUl;YSl7@O@;j1ATXN1o4 z4zf4%Bzyupz!wVn5NeFSi0^`^BU`3 z8t=E6Z{aE%Dw}0mSBaZyvE86ohRat5qPjB3awNQEPdhsJFfUMEH!FuSfw=1t3Z^w5 zfQRQc$H0cNBf{+vNgN{tpzW0Ex&{f#S@RtpAOs1{Au|qhqmaA8>V`Vg!sjmORXXFr z=aUp1VvGn-`#jvatG<*5W$gN3L34-n)X$k&bBQhpw?XLx#*T7z2RA&yVLB9d{m25< z4xGyzH|q6b8aSb=aqGYwr_Eb;lnOT*qz3zE?sDxeJ3P`y{T&2z;1gkM0@*clQh1BJ z@ImfHHuY6Fa=Q>V@y^8bd-Uybb2ys9+@8;a)~CBoddwCpy_mn ze9dom{i1w!Y>iw^zEG zkSeBraK;yr%Yx+ER1IXUD%JgJ5_$vJ5edCeK~B>P^#~E@e7V`)IY=J|vsVcpDt8>Y zeMy3?55{U{Dbr8p$KXyvjx~T z=tD&z+QNqqvr~et$ldUVHbUkE4#l$BksFfM=tQi^+;E4cL*@h>V26l?_)!`@@0zwl z?uIzz9x^9>=#~8_hm+a2{tzZN`26SsG3*x*4!6gm7L*y4Av+MenvF2w@P&R$kJu)H#AcNmc}emd0Xhmz|B z{hn8&wd{T?!3Z*U%Q=iH#(fp95XtfnNyp*XF~=m0bHK;t`_sE7H5JD5 zxxtlI1}zjbN(+m~?sXYAO47;BYw|S=sB=AnKvxPZ=VU27$q&fC4`ygX6gS+5ub&+% zUZN5Z;U8*iq0HnXiEf%2|4e}iKEIG9DJ_D$T#VT`S$RJ;=|QjPkp?%HQBs{g6R(+} zePG_xjR$6+XdLHU);Bulz>ZgRV zv{dK^g_l!1>8ZA-hT7{tFV;^9A6HJrM8sswS#;ct8ays$*I2&MAxBTzGo<~*yNP#p zSkt0$bi9@n?jL~)Micd*cV+!y9agP%L83kMBdOKFAuC4n6J`-J=(Yqykgya96O0oQ ziu*;=*qmfesTx%j?+U3_vLEO64wXlHG9U^;Kwdi|sdo7nyjuZR=a|-fo8rKT((}qQ zKu9T$#fn#vVl1a{`s2)+v{d=}hSj!C2#P3t1b1J^D?xU6x+S*wWE51}OVYcOZ3Lq_ zp9tq*Nk{V!ZRrw9Bk8sXL7%6fWd{;W!H~YRr6ANA=BW=!z2AX?ejWy8VrV@N0uQC%{yKm1hx|Lb4N}=f)tzE<0pplw7{gCV zl=b9^+8MVFOV+ebi!XUp4$?zrL_|Gy>3xwScqpbD9Op{ngHB)q8%kdkFZ8r}L)z8n z?2}U)m~HS(J+$sH2t7fqsDMDXrY_Jn33I;tQGpP~{|-Ac$K-bLK^T@4kE$cM87bZi zIMo#e$JQ)Lr#DGOU|OVJPnN7ihWt1Xh<&tEKSCZdH9l=Qz+~$-(auzg8(LgR^pbrV zgLGReRdq$)(J%+fJ#5pA*zDoVG_y zhE6&aV4)@SPS6b1v;qIXO}{adVXYxFa4fUfKP+6xO9%|1cs_pD%unh9W85ueqp{V` z6WQE0MEr(%+#^AA&fv~mhn2?@g!9^6g%F)9X0L@iOZ*2FzO%V3QGngBH-fZ68S#8;=pew5=;O`M! z8U13-yYqOfERvQEF7isW*Zu1Z2XMBSpqIV7O@qz@&>P0V4bsx!J<7RnaKY-wSt@$3 zk7$bpwQ-7D>+?$%2tV)fqa&g^`4Z^JMg;q#8G-j_W&|Y%J5w7YLpD}YVIvERKld+4 zi}_AD=dlE z??RsDgd2bIXPpsb=(qp4oMu0lw0?bdvBCZXCf(?n7?a;!DQcc>U`wrB-F&9vVLKS$ z?v-AW&l_cCZ58DS<*XMiO6X>AAxhg~z8GE_;ABT}qiTvelAdHol3ezq6$mF0h&ue% z1e*{G)Ine@xgtfvbuz=KTJHfhwF)H@D#6}ZPiN%A}L@ooRXE>XM{2N zH2T^`3t}lCmSn>5%!E`7ZjKCxopu^mx**c8;9v-iP^g?AhRy80o)km$Jui}8CoB%( zp;szu?+?$}T&39X*8Qgq3kInPsC2XwHv2Rp&=_(A#qUDaFDa5)*)u0nRy=&?47VP* z_37!J?^J@NyMk_*r?{!{0K}`J1FTMizPCJO5LO=^!=Qtu6yyt>)HzBXK$uPFcRt!*MXwV5Z6gyj)Ngf8i;jwyt6Qf3Nm1rd%dSYBYCwol5@9CMH z>C~m&;F8pY)qSXtyZ)d{4-(suYPz{1KZ`SmvxxI~(n#vK=3srctI9$0N5?+VzC5Op zPXWy~Vf$FyB(l9r1S;`Vb#mq^UGZK|;`Zl}E~IJ19El|FTI`qlzGv}_mK8Z?2u|4V znP)m$Prddh=X;wxRy-(ul%g#`OX^K>guZtG#vw@%(T8p${wCk-&PMgqyg#-WV{077 zWdXLsF$VgNe}9V&!C&)E2|arg;lEn7#wp0ip$Q^+^1@BGauCrH(!UOU0{P)B2$MUo zIc8fmwAmsKs$8ydrH_WOI~y~G= z4`QLmDe|O-$%Y4pDKzlSM^1uA#t}=<9d}xTRfFJ;8X7q;Y>mozycm>=u`F0lO^0IO zUbf1c8&k7zqcxVDKhp*_a&B#q?y7oE^z7VE!cM=vPAoTLJ~-<@(vR5W`Pt5L$s=X1 zxMF5puA%!}xZTg|0iN?zLaj<52~vPqUh_d9*hLD?<3~JJF5O@L+1n zRXO1)sTie4H^UQ|4UmWT2ymSECWJ>gA)G7*sk@{)t9mb|?oW`_7X$Ym?hm}ZvOn!( z*k#b{Ce_2|%Oa;OjLm96&kxpk;v9zUg6DY7(efx|v7Q@_(X+T}bF?5~2kXk==@dFo z(c4s^r&hWW#4NIo7z1kvAuA?}R5(q5fx5l1h#(pQw)z#AjBC5mg4wE5kFBy^$xew*X`^&7f%xzNf74^)* zE}l8fNQGQ<%~3Bj6!Fe5WwK;lM##kw4Kb>4XO7o#_&-<~7raNzLwjLnKaNh{I(IJC z=RglP@+`kNHfkmakZZP zBwIv^mY$o4+~}*y<@Dmt@;w=wVFlkRvxu&(VfB6^X30@}oh1Ev7&lq3(8@d9_&AF= z@mQe-6Kjbg(OLVEXsxnM`}HIUKo5X6{CG%=sj zpVncyV2&0e%KeXyW%W#vC7qF1Ws7I^Q`uOplv8$0uqYfocO zFOeoDijO_$Md{E=aKy1my}QXpZfH5>IGn$9)TU9Me!slj_0UHiHTQ+$4UvkaufGY> zOXW9lDEbuCB{I8}rl*5V2Zv7}lwCX+I&`7d8y5`9fF{YJ&JQN) zC3%a)vQ`HIZX4-)_>UmkE4N7xBYM>@UWs(|tQ12%E2(d>pijZt?UHCtnu{+#P+GEA ziZ`y?WKZpv;f`0cn>>?-r)L|BQo70a0`=xe(+HeD;cp|b<8hyID(=#)Wa~-xfn@)f znpzct6zs@LG}XD29QZ8NATs_jg{5_}=03{WvKU~7uaN~VvUdD%cmu^*z`Izvydxs)_7uy(dai7stE5tJG@T_W2YKEYE! z?8D8v%tV7BE(}^_QjKnYzs(l*MEq&aJzcB!yOJ}7dI(xLifiM$Naw6Gv?Oi*v>|*g zZ#xLw;nS)meTuT6X*uDj7me0^iLrWwrH^9C}tumbDJ7)|ReJEwth z!*%zv_Ku;6M5=^F6ZO6qjgCIf2V@FXdYTZ9IN>HZjXPnu4&%rmcCb6DPaPde|J2DG2 zG6#QNP&s@kx;zERzqf+VE0ovarG~(%yUJ1~;qzVyo;Ve-t^^oAc^)Dsz4s*hEXh%R=sg|mV@rBi;q>Maz;F7YGOlD@Ew?7(<0B5Dk zJfrkAGGl6?C|%^Zi`J){E;dm$Ykt@B(PL*V__FL1xG`HZ$P97PJSGca_Lhzz3$ws} z&JEu6dt7mt^C2lyJnoHf<%M_3*HfWv*NuePmJ7UgizFvkahmGe0{jcYh2yGG6Ts$l zKpF8Q%o0S+4M7Fi9fjiw8a;~Es|)RwscP`5Yf!4sQnNyGOY?iT+GWFOc5>eF>+3o@ z;qLQ%lC!xI-y_5O*iG1;!{?d6Mb)noIl`6UcF4i|?vTpup}dv#5c`gO0to-(iwvUh zjJ?zPPOv?Dt23S5t_PRahY6c!D%UvmfqpJ&2K7GTyX4|7U^>RYPsG=;sE$vJ&OZWBV#LSig*i2fp{6)m$vBHKEqw0a*19(Nm=jx3_`W5>BB3&Cc*~#yHT?o{ z0e@5JQk5rYtk<-H7xn;d3bSNipLcWf{HUVK*L$!}7xAACq-TO-cXU2wS zrvB?V6<3g-lOfK-LFQ(l=MWwIkJ4h!e@u&A8UD=sb=|#?xpvKqAMFw@egT5!p z-3xQP3B32Y3_6u&qWEp6m&&Z?9fMk+I~#`WE}|i`$k#u@>4{|@M|wC$S3aa3ji|QE zp$~;kZKcG%??HtxclLU7K3F%?%`{PhNjbhhshxTlj^Y92wwg)jxFXE#ZuJgqM%L~s zk&Vbsf=I<;MWO5G$xieSlO6V7vEu*R zP(?!tPX+Co{*aaxOgicvh%FXMnrL4MQ~{A5b~c#sV2Q~hireGY2EuX!%5v=Fg`uv4 z>V8cYuV|#tu>&0~U_=~h1Ai1&G+S%~} z84L>#N01(W>sO)v*buMvC1T{ou0I?@J}=tJ*;eE{Y( z#ytWXk*0ePcuW!!51EQgI?#F5Xc;T=6XI0JvZ&nnm~MQV<1`;u2Vt3g9Ibzy7%!MQ zTor*MYaA9JQdwY3*ZQH>wY!Ab80E4eNu3^-7Yxjp+Al3!_}*Q7T6;e;jb$+nx=*l$ zxvNFy<#fh6T6xzlL85v0l$}{CC2}c3IY|biR2{u~sST@{iVa!IPG?f#u7)OVJ_te? zS@qiv$x?BV*}d;el5#Q)`zWC~_L-K<3$`MRN)s0^`lwNfp@bMfZyggwA5cu4VdLGa;*f{Zy1%42!qCja{`P>6M_ zwVO%~+b}QKiLamr*y~p7E3Kc65yRE(DUA~PI1ydbz2FnsI@wL1@U`Y9F!Q}xA@5ve zM^dk%f(aIrf}w4O40xEFHTHN9O5;T{PN%5-!Xg5)6?&I^>Ju>kJhm)Z+dY4(iP>~+ z=e>P;&PfDs0-4SjBQyj0K}tPX-&b4s88B|}A`I2|J0=5``Z^cGD_UlL;IK4q!{hL*Q&YZihHNAZy z4hzvF!91&zGc9UnUAlpLEzf&>37T*5>4g29PPQBEKx*7bQ)^vJx#739q)2@lkZdo()p z{*|&OY6ZDP+$Ms!TMC$#sWZMa*AkmoQg;66VasYHlPP;%*bNS%-)<{-u$uhpQ6=~XaVX!0)K3Y40;C*1PufV z3JPRc+5B_UT@WC8{&7Hq0RQRMAtS0RNGB;P#&F&Jd7ixet1^A;h$6s$7X?1hZvEQl zV&`AKDkCT>DJH6&Vwlpzi*1H3@&mJ}?QKh5|G{XKuT<->wu1~lgjM2*`_GMwLLe#igAcG$3} z0jBeRF?;ZTo5}KR`w5w1i_ikhtp8$~68|=n_1nyd`jWhAV1N~neLd`;fuDSD5PW60 z|M<*TKmImxuKE)68Q?)MKywoyRZ1g@}!FqsP zu|TB1J->)@|2*~E3=7VH25TUWlmeX3OO`JO4B!945HoeLan;Pp-1hDj;D*MJ zyh6#yQqRW3+U}obf2n@vxmbW;4fvki-$Vcbsg(Rl!e_1hg(dxC^#1kjx5XnF^+#3( zLjM6Gki^}BcL2?SeoDMs@Nd&-Fi}|K0Y$0;_5xzV-;OZFpQC;I{D{WKP@j#J12Aq6 zCT^AQpI^z<%D}|V+RD^T&%xCCpTY!OJ$r{0z)1Qp27=m8GrluftsbGtx&e%u|6+Vl z|7pf|CZ|g9Yj+obQT|_y#)m)6_|9a+T=;6S5MT%cu6^4c$(ldSxc_aN(|JqPNdgQ` zK#tG-tiBx2wSSr+sBiCJr)S^*6y41A^ev3!^v#S69KH<}F+3sh1E5C0yvOr}3cNDa z{b}$YWA%IaQbR(P6eOTRWniEJZxH<3p=$7Jq;E@9X6DcJ5a8?p8SU*5{_gRwaReP4 z>{P8SfN^EA_$SpuuWj+PFRI#+dyY^MA43Q?@)9UchPY0QM93n?6B6qHKPR z`Xf(4=y5t00BH9B8ZfEy^f1D)S)PgcwJKMVS{i$uI>VA%o$ zXadCfycYj*B)fl~@Sk2PU)J59@8<(+6jC8mF-NO!qYm};{XPQcj|Nb;wYKl^E2!^+ zrqeHItO3&~0Ghj<9tnE=0_fY)w0CXDFaeNbV0_({2HocuAgUbS3l{c5GGHr!+MEHk z34AdR@T%DF*Fb^>pQpudi`B1t3AX{9I~Sk{-J;zI_%)i4>5q8wWlGNBfSr1P6}{js zo>t(m@q{I0zVzDqdQH%OYFhdZz_0`2eeR|A3#!UKgh02Bv+-X5W%slN>U0WTJP34Z|y zbkV>l6}_cZtF&L^JuvzQ=W(MNuPO$d#~g4Tkz2@?%wI(8IRZOmg`tOs=fj{xA_mK5*#uXBGyOq6pduK}XLF<{Qy?ju?7 zYeZn$|05uLTS9L=s|5pa5RiQ!`T)g=za4fje~tI0Kke5+=iXx0?mj?x3uyCpbQdi9 zHP)ZWnUL$BUw<5Dn=6tscL91WK>yq{^2UJs!ExKHyJ8}H4( zgZ{n?+wO}xtO2|rfPH&X3$6Se_K%fq$4m?IIhphUsJEliTGj7SzpLb%bfe((oNUeqfB0a)-BGo|*|m`X;*mPX&3TlgJg z#;ySjDS*4JRp0tw#MxMwI{au39nFmpoC4y*_Rp0Ol(J z;=A}QkJ%ghHQqN`Mt`l-H&101Ki`Z&0id_1F@>q$fZBgQ*E@lTaSZ{gH1zKXdvo4;Fnb0Ej$oUjpFUDgfp7eViy@iStj| z`69sWhXKB($i44IGY2T2Wx1U}Jf-{@%C{ao_?jhq4SFO81p*QVEJttK+mQMvSl@Og z84;bRb->OcfH1(FfWI9`EdPA+AN8#z{m~M8Rqlc4=j5Ogz}_yX+OYm6_Qz}cabO?k zI3OaR0{0?rC)LyUe-r$}MZRLGW;0~f2il5S?vQmn-UERL6(|GC z-4b3vty=Yfr}YC< z^zCKWwEXY0f9PjaCRhYLfWAN9c~H9LXDF(_iT<2A{B!xh25g?J9RM=|7jRp4ZMA>l z0)A9SkQgGUdIy9TTEOJDXO#@~-$ehops<2uvjqpFWUD}2yPe?9YW^np`&qSCqJ{7o zpy**h(YL1*Z6LY-t%&k7B?h|(@&>2CW%L4%`-KKP*gubT~!d1-KFb z_qM`OEPt2#PZf#|VmWm(2oMk=g#XGWfcK63mdyW9s%RgGKJ^7k6+XmYYn#dOx5+=K zS+K<7TmpU!5(LEe+p(P{@L%A5Kg_<6sG>LmOiTt$Dz|f1xZvLe{|{A+Wo33%CO~8Z zV9j!SHuVnsH|ReoXZ(GyD51ZFRN#(8KN=NdN!< literal 0 HcmV?d00001 diff --git a/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/configuration.rb b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/configuration.rb new file mode 100644 index 0000000..c6774da --- /dev/null +++ b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/configuration.rb @@ -0,0 +1,184 @@ +require 'thread' +require 'concurrent/delay' +require 'concurrent/errors' +require 'concurrent/atomic/atomic_reference' +require 'concurrent/concern/logging' +require 'concurrent/executor/immediate_executor' +require 'concurrent/executor/cached_thread_pool' +require 'concurrent/utility/at_exit' +require 'concurrent/utility/processor_counter' + +module Concurrent + extend Concern::Logging + + autoload :Options, 'concurrent/options' + autoload :TimerSet, 'concurrent/executor/timer_set' + autoload :ThreadPoolExecutor, 'concurrent/executor/thread_pool_executor' + + # @return [Logger] Logger with provided level and output. + def self.create_simple_logger(level = Logger::FATAL, output = $stderr) + # TODO (pitr-ch 24-Dec-2016): figure out why it had to be replaced, stdlogger was deadlocking + lambda do |severity, progname, message = nil, &block| + return false if severity < level + + message = block ? block.call : message + formatted_message = case message + when String + message + when Exception + format "%s (%s)\n%s", + message.message, message.class, (message.backtrace || []).join("\n") + else + message.inspect + end + + output.print format "[%s] %5s -- %s: %s\n", + Time.now.strftime('%Y-%m-%d %H:%M:%S.%L'), + Logger::SEV_LABEL[severity], + progname, + formatted_message + true + end + end + + # Use logger created by #create_simple_logger to log concurrent-ruby messages. + def self.use_simple_logger(level = Logger::FATAL, output = $stderr) + Concurrent.global_logger = create_simple_logger level, output + end + + # @return [Logger] Logger with provided level and output. + # @deprecated + def self.create_stdlib_logger(level = Logger::FATAL, output = $stderr) + logger = Logger.new(output) + logger.level = level + logger.formatter = lambda do |severity, datetime, progname, msg| + formatted_message = case msg + when String + msg + when Exception + format "%s (%s)\n%s", + msg.message, msg.class, (msg.backtrace || []).join("\n") + else + msg.inspect + end + format "[%s] %5s -- %s: %s\n", + datetime.strftime('%Y-%m-%d %H:%M:%S.%L'), + severity, + progname, + formatted_message + end + + lambda do |loglevel, progname, message = nil, &block| + logger.add loglevel, message, progname, &block + end + end + + # Use logger created by #create_stdlib_logger to log concurrent-ruby messages. + # @deprecated + def self.use_stdlib_logger(level = Logger::FATAL, output = $stderr) + Concurrent.global_logger = create_stdlib_logger level, output + end + + # TODO (pitr-ch 27-Dec-2016): remove deadlocking stdlib_logger methods + + # Suppresses all output when used for logging. + NULL_LOGGER = lambda { |level, progname, message = nil, &block| } + + # @!visibility private + GLOBAL_LOGGER = AtomicReference.new(create_simple_logger(Logger::WARN)) + private_constant :GLOBAL_LOGGER + + def self.global_logger + GLOBAL_LOGGER.value + end + + def self.global_logger=(value) + GLOBAL_LOGGER.value = value + end + + # @!visibility private + GLOBAL_FAST_EXECUTOR = Delay.new { Concurrent.new_fast_executor(auto_terminate: true) } + private_constant :GLOBAL_FAST_EXECUTOR + + # @!visibility private + GLOBAL_IO_EXECUTOR = Delay.new { Concurrent.new_io_executor(auto_terminate: true) } + private_constant :GLOBAL_IO_EXECUTOR + + # @!visibility private + GLOBAL_TIMER_SET = Delay.new { TimerSet.new(auto_terminate: true) } + private_constant :GLOBAL_TIMER_SET + + # @!visibility private + GLOBAL_IMMEDIATE_EXECUTOR = ImmediateExecutor.new + private_constant :GLOBAL_IMMEDIATE_EXECUTOR + + # Disables AtExit handlers including pool auto-termination handlers. + # When disabled it will be the application programmer's responsibility + # to ensure that the handlers are shutdown properly prior to application + # exit by calling {AtExit.run} method. + # + # @note this option should be needed only because of `at_exit` ordering + # issues which may arise when running some of the testing frameworks. + # E.g. Minitest's test-suite runs itself in `at_exit` callback which + # executes after the pools are already terminated. Then auto termination + # needs to be disabled and called manually after test-suite ends. + # @note This method should *never* be called + # from within a gem. It should *only* be used from within the main + # application and even then it should be used only when necessary. + # @see AtExit + def self.disable_at_exit_handlers! + AtExit.enabled = false + end + + # Global thread pool optimized for short, fast *operations*. + # + # @return [ThreadPoolExecutor] the thread pool + def self.global_fast_executor + GLOBAL_FAST_EXECUTOR.value + end + + # Global thread pool optimized for long, blocking (IO) *tasks*. + # + # @return [ThreadPoolExecutor] the thread pool + def self.global_io_executor + GLOBAL_IO_EXECUTOR.value + end + + def self.global_immediate_executor + GLOBAL_IMMEDIATE_EXECUTOR + end + + # Global thread pool user for global *timers*. + # + # @return [Concurrent::TimerSet] the thread pool + def self.global_timer_set + GLOBAL_TIMER_SET.value + end + + # General access point to global executors. + # @param [Symbol, Executor] executor_identifier symbols: + # - :fast - {Concurrent.global_fast_executor} + # - :io - {Concurrent.global_io_executor} + # - :immediate - {Concurrent.global_immediate_executor} + # @return [Executor] + def self.executor(executor_identifier) + Options.executor(executor_identifier) + end + + def self.new_fast_executor(opts = {}) + FixedThreadPool.new( + [2, Concurrent.processor_count].max, + auto_terminate: opts.fetch(:auto_terminate, true), + idletime: 60, # 1 minute + max_queue: 0, # unlimited + fallback_policy: :abort # shouldn't matter -- 0 max queue + ) + end + + def self.new_io_executor(opts = {}) + CachedThreadPool.new( + auto_terminate: opts.fetch(:auto_terminate, true), + fallback_policy: :abort # shouldn't matter -- 0 max queue + ) + end +end diff --git a/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/constants.rb b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/constants.rb new file mode 100644 index 0000000..676c2af --- /dev/null +++ b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/constants.rb @@ -0,0 +1,8 @@ +module Concurrent + + # Various classes within allows for +nil+ values to be stored, + # so a special +NULL+ token is required to indicate the "nil-ness". + # @!visibility private + NULL = ::Object.new + +end diff --git a/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/dataflow.rb b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/dataflow.rb new file mode 100644 index 0000000..d55f19d --- /dev/null +++ b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/dataflow.rb @@ -0,0 +1,81 @@ +require 'concurrent/future' +require 'concurrent/atomic/atomic_fixnum' + +module Concurrent + + # @!visibility private + class DependencyCounter # :nodoc: + + def initialize(count, &block) + @counter = AtomicFixnum.new(count) + @block = block + end + + def update(time, value, reason) + if @counter.decrement == 0 + @block.call + end + end + end + + # Dataflow allows you to create a task that will be scheduled when all of its data dependencies are available. + # {include:file:docs-source/dataflow.md} + # + # @param [Future] inputs zero or more `Future` operations that this dataflow depends upon + # + # @yield The operation to perform once all the dependencies are met + # @yieldparam [Future] inputs each of the `Future` inputs to the dataflow + # @yieldreturn [Object] the result of the block operation + # + # @return [Object] the result of all the operations + # + # @raise [ArgumentError] if no block is given + # @raise [ArgumentError] if any of the inputs are not `IVar`s + def dataflow(*inputs, &block) + dataflow_with(Concurrent.global_io_executor, *inputs, &block) + end + module_function :dataflow + + def dataflow_with(executor, *inputs, &block) + call_dataflow(:value, executor, *inputs, &block) + end + module_function :dataflow_with + + def dataflow!(*inputs, &block) + dataflow_with!(Concurrent.global_io_executor, *inputs, &block) + end + module_function :dataflow! + + def dataflow_with!(executor, *inputs, &block) + call_dataflow(:value!, executor, *inputs, &block) + end + module_function :dataflow_with! + + private + + def call_dataflow(method, executor, *inputs, &block) + raise ArgumentError.new('an executor must be provided') if executor.nil? + raise ArgumentError.new('no block given') unless block_given? + unless inputs.all? { |input| input.is_a? IVar } + raise ArgumentError.new("Not all dependencies are IVars.\nDependencies: #{ inputs.inspect }") + end + + result = Future.new(executor: executor) do + values = inputs.map { |input| input.send(method) } + block.call(*values) + end + + if inputs.empty? + result.execute + else + counter = DependencyCounter.new(inputs.size) { result.execute } + + inputs.each do |input| + input.add_observer counter + end + end + + result + end + module_function :call_dataflow +end diff --git a/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/delay.rb b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/delay.rb new file mode 100644 index 0000000..83799d0 --- /dev/null +++ b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/delay.rb @@ -0,0 +1,199 @@ +require 'thread' +require 'concurrent/concern/obligation' +require 'concurrent/executor/immediate_executor' +require 'concurrent/synchronization' + +module Concurrent + + # This file has circular require issues. It must be autoloaded here. + autoload :Options, 'concurrent/options' + + # Lazy evaluation of a block yielding an immutable result. Useful for + # expensive operations that may never be needed. It may be non-blocking, + # supports the `Concern::Obligation` interface, and accepts the injection of + # custom executor upon which to execute the block. Processing of + # block will be deferred until the first time `#value` is called. + # At that time the caller can choose to return immediately and let + # the block execute asynchronously, block indefinitely, or block + # with a timeout. + # + # When a `Delay` is created its state is set to `pending`. The value and + # reason are both `nil`. The first time the `#value` method is called the + # enclosed opration will be run and the calling thread will block. Other + # threads attempting to call `#value` will block as well. Once the operation + # is complete the *value* will be set to the result of the operation or the + # *reason* will be set to the raised exception, as appropriate. All threads + # blocked on `#value` will return. Subsequent calls to `#value` will immediately + # return the cached value. The operation will only be run once. This means that + # any side effects created by the operation will only happen once as well. + # + # `Delay` includes the `Concurrent::Concern::Dereferenceable` mixin to support thread + # safety of the reference returned by `#value`. + # + # @!macro copy_options + # + # @!macro delay_note_regarding_blocking + # @note The default behavior of `Delay` is to block indefinitely when + # calling either `value` or `wait`, executing the delayed operation on + # the current thread. This makes the `timeout` value completely + # irrelevant. To enable non-blocking behavior, use the `executor` + # constructor option. This will cause the delayed operation to be + # execute on the given executor, allowing the call to timeout. + # + # @see Concurrent::Concern::Dereferenceable + class Delay < Synchronization::LockableObject + include Concern::Obligation + + # NOTE: Because the global thread pools are lazy-loaded with these objects + # there is a performance hit every time we post a new task to one of these + # thread pools. Subsequently it is critical that `Delay` perform as fast + # as possible post-completion. This class has been highly optimized using + # the benchmark script `examples/lazy_and_delay.rb`. Do NOT attempt to + # DRY-up this class or perform other refactoring with running the + # benchmarks and ensuring that performance is not negatively impacted. + + # Create a new `Delay` in the `:pending` state. + # + # @!macro executor_and_deref_options + # + # @yield the delayed operation to perform + # + # @raise [ArgumentError] if no block is given + def initialize(opts = {}, &block) + raise ArgumentError.new('no block given') unless block_given? + super(&nil) + synchronize { ns_initialize(opts, &block) } + end + + # Return the value this object represents after applying the options + # specified by the `#set_deref_options` method. If the delayed operation + # raised an exception this method will return nil. The execption object + # can be accessed via the `#reason` method. + # + # @param [Numeric] timeout the maximum number of seconds to wait + # @return [Object] the current value of the object + # + # @!macro delay_note_regarding_blocking + def value(timeout = nil) + if @executor # TODO (pitr 12-Sep-2015): broken unsafe read? + super + else + # this function has been optimized for performance and + # should not be modified without running new benchmarks + synchronize do + execute = @evaluation_started = true unless @evaluation_started + if execute + begin + set_state(true, @task.call, nil) + rescue => ex + set_state(false, nil, ex) + end + elsif incomplete? + raise IllegalOperationError, 'Recursive call to #value during evaluation of the Delay' + end + end + if @do_nothing_on_deref + @value + else + apply_deref_options(@value) + end + end + end + + # Return the value this object represents after applying the options + # specified by the `#set_deref_options` method. If the delayed operation + # raised an exception, this method will raise that exception (even when) + # the operation has already been executed). + # + # @param [Numeric] timeout the maximum number of seconds to wait + # @return [Object] the current value of the object + # @raise [Exception] when `#rejected?` raises `#reason` + # + # @!macro delay_note_regarding_blocking + def value!(timeout = nil) + if @executor + super + else + result = value + raise @reason if @reason + result + end + end + + # Return the value this object represents after applying the options + # specified by the `#set_deref_options` method. + # + # @param [Integer] timeout (nil) the maximum number of seconds to wait for + # the value to be computed. When `nil` the caller will block indefinitely. + # + # @return [Object] self + # + # @!macro delay_note_regarding_blocking + def wait(timeout = nil) + if @executor + execute_task_once + super(timeout) + else + value + end + self + end + + # Reconfigures the block returning the value if still `#incomplete?` + # + # @yield the delayed operation to perform + # @return [true, false] if success + def reconfigure(&block) + synchronize do + raise ArgumentError.new('no block given') unless block_given? + unless @evaluation_started + @task = block + true + else + false + end + end + end + + protected + + def ns_initialize(opts, &block) + init_obligation + set_deref_options(opts) + @executor = opts[:executor] + + @task = block + @state = :pending + @evaluation_started = false + end + + private + + # @!visibility private + def execute_task_once # :nodoc: + # this function has been optimized for performance and + # should not be modified without running new benchmarks + execute = task = nil + synchronize do + execute = @evaluation_started = true unless @evaluation_started + task = @task + end + + if execute + executor = Options.executor_from_options(executor: @executor) + executor.post do + begin + result = task.call + success = true + rescue => ex + reason = ex + end + synchronize do + set_state(success, result, reason) + event.set + end + end + end + end + end +end diff --git a/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/errors.rb b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/errors.rb new file mode 100644 index 0000000..b69fec0 --- /dev/null +++ b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/errors.rb @@ -0,0 +1,69 @@ +module Concurrent + + Error = Class.new(StandardError) + + # Raised when errors occur during configuration. + ConfigurationError = Class.new(Error) + + # Raised when an asynchronous operation is cancelled before execution. + CancelledOperationError = Class.new(Error) + + # Raised when a lifecycle method (such as `stop`) is called in an improper + # sequence or when the object is in an inappropriate state. + LifecycleError = Class.new(Error) + + # Raised when an attempt is made to violate an immutability guarantee. + ImmutabilityError = Class.new(Error) + + # Raised when an operation is attempted which is not legal given the + # receiver's current state + IllegalOperationError = Class.new(Error) + + # Raised when an object's methods are called when it has not been + # properly initialized. + InitializationError = Class.new(Error) + + # Raised when an object with a start/stop lifecycle has been started an + # excessive number of times. Often used in conjunction with a restart + # policy or strategy. + MaxRestartFrequencyError = Class.new(Error) + + # Raised when an attempt is made to modify an immutable object + # (such as an `IVar`) after its final state has been set. + class MultipleAssignmentError < Error + attr_reader :inspection_data + + def initialize(message = nil, inspection_data = nil) + @inspection_data = inspection_data + super message + end + + def inspect + format '%s %s>', super[0..-2], @inspection_data.inspect + end + end + + # Raised by an `Executor` when it is unable to process a given task, + # possibly because of a reject policy or other internal error. + RejectedExecutionError = Class.new(Error) + + # Raised when any finite resource, such as a lock counter, exceeds its + # maximum limit/threshold. + ResourceLimitError = Class.new(Error) + + # Raised when an operation times out. + TimeoutError = Class.new(Error) + + # Aggregates multiple exceptions. + class MultipleErrors < Error + attr_reader :errors + + def initialize(errors, message = "#{errors.size} errors") + @errors = errors + super [*message, + *errors.map { |e| [format('%s (%s)', e.message, e.class), *e.backtrace] }.flatten(1) + ].join("\n") + end + end + +end diff --git a/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/exchanger.rb b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/exchanger.rb new file mode 100644 index 0000000..5a99550 --- /dev/null +++ b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/exchanger.rb @@ -0,0 +1,352 @@ +require 'concurrent/constants' +require 'concurrent/errors' +require 'concurrent/maybe' +require 'concurrent/atomic/atomic_reference' +require 'concurrent/atomic/count_down_latch' +require 'concurrent/utility/engine' +require 'concurrent/utility/monotonic_time' + +module Concurrent + + # @!macro exchanger + # + # A synchronization point at which threads can pair and swap elements within + # pairs. Each thread presents some object on entry to the exchange method, + # matches with a partner thread, and receives its partner's object on return. + # + # @!macro thread_safe_variable_comparison + # + # This implementation is very simple, using only a single slot for each + # exchanger (unlike more advanced implementations which use an "arena"). + # This approach will work perfectly fine when there are only a few threads + # accessing a single `Exchanger`. Beyond a handful of threads the performance + # will degrade rapidly due to contention on the single slot, but the algorithm + # will remain correct. + # + # @see http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/Exchanger.html java.util.concurrent.Exchanger + # @example + # + # exchanger = Concurrent::Exchanger.new + # + # threads = [ + # Thread.new { puts "first: " << exchanger.exchange('foo', 1) }, #=> "first: bar" + # Thread.new { puts "second: " << exchanger.exchange('bar', 1) } #=> "second: foo" + # ] + # threads.each {|t| t.join(2) } + + # @!visibility private + class AbstractExchanger < Synchronization::Object + + # @!visibility private + CANCEL = ::Object.new + private_constant :CANCEL + + def initialize + super + end + + # @!macro exchanger_method_do_exchange + # + # Waits for another thread to arrive at this exchange point (unless the + # current thread is interrupted), and then transfers the given object to + # it, receiving its object in return. The timeout value indicates the + # approximate number of seconds the method should block while waiting + # for the exchange. When the timeout value is `nil` the method will + # block indefinitely. + # + # @param [Object] value the value to exchange with another thread + # @param [Numeric, nil] timeout in seconds, `nil` blocks indefinitely + # + # @!macro exchanger_method_exchange + # + # In some edge cases when a `timeout` is given a return value of `nil` may be + # ambiguous. Specifically, if `nil` is a valid value in the exchange it will + # be impossible to tell whether `nil` is the actual return value or if it + # signifies timeout. When `nil` is a valid value in the exchange consider + # using {#exchange!} or {#try_exchange} instead. + # + # @return [Object] the value exchanged by the other thread or `nil` on timeout + def exchange(value, timeout = nil) + (value = do_exchange(value, timeout)) == CANCEL ? nil : value + end + + # @!macro exchanger_method_do_exchange + # @!macro exchanger_method_exchange_bang + # + # On timeout a {Concurrent::TimeoutError} exception will be raised. + # + # @return [Object] the value exchanged by the other thread + # @raise [Concurrent::TimeoutError] on timeout + def exchange!(value, timeout = nil) + if (value = do_exchange(value, timeout)) == CANCEL + raise Concurrent::TimeoutError + else + value + end + end + + # @!macro exchanger_method_do_exchange + # @!macro exchanger_method_try_exchange + # + # The return value will be a {Concurrent::Maybe} set to `Just` on success or + # `Nothing` on timeout. + # + # @return [Concurrent::Maybe] on success a `Just` maybe will be returned with + # the item exchanged by the other thread as `#value`; on timeout a + # `Nothing` maybe will be returned with {Concurrent::TimeoutError} as `#reason` + # + # @example + # + # exchanger = Concurrent::Exchanger.new + # + # result = exchanger.exchange(:foo, 0.5) + # + # if result.just? + # puts result.value #=> :bar + # else + # puts 'timeout' + # end + def try_exchange(value, timeout = nil) + if (value = do_exchange(value, timeout)) == CANCEL + Concurrent::Maybe.nothing(Concurrent::TimeoutError) + else + Concurrent::Maybe.just(value) + end + end + + private + + # @!macro exchanger_method_do_exchange + # + # @return [Object, CANCEL] the value exchanged by the other thread; {CANCEL} on timeout + def do_exchange(value, timeout) + raise NotImplementedError + end + end + + # @!macro internal_implementation_note + # @!visibility private + class RubyExchanger < AbstractExchanger + # A simplified version of java.util.concurrent.Exchanger written by + # Doug Lea, Bill Scherer, and Michael Scott with assistance from members + # of JCP JSR-166 Expert Group and released to the public domain. It does + # not include the arena or the multi-processor spin loops. + # http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/6-b14/java/util/concurrent/Exchanger.java + + safe_initialization! + + class Node < Concurrent::Synchronization::Object + attr_atomic :value + safe_initialization! + + def initialize(item) + super() + @Item = item + @Latch = Concurrent::CountDownLatch.new + self.value = nil + end + + def latch + @Latch + end + + def item + @Item + end + end + private_constant :Node + + def initialize + super + end + + private + + attr_atomic(:slot) + + # @!macro exchanger_method_do_exchange + # + # @return [Object, CANCEL] the value exchanged by the other thread; {CANCEL} on timeout + def do_exchange(value, timeout) + + # ALGORITHM + # + # From the original Java version: + # + # > The basic idea is to maintain a "slot", which is a reference to + # > a Node containing both an Item to offer and a "hole" waiting to + # > get filled in. If an incoming "occupying" thread sees that the + # > slot is null, it CAS'es (compareAndSets) a Node there and waits + # > for another to invoke exchange. That second "fulfilling" thread + # > sees that the slot is non-null, and so CASes it back to null, + # > also exchanging items by CASing the hole, plus waking up the + # > occupying thread if it is blocked. In each case CAS'es may + # > fail because a slot at first appears non-null but is null upon + # > CAS, or vice-versa. So threads may need to retry these + # > actions. + # + # This version: + # + # An exchange occurs between an "occupier" thread and a "fulfiller" thread. + # The "slot" is used to setup this interaction. The first thread in the + # exchange puts itself into the slot (occupies) and waits for a fulfiller. + # The second thread removes the occupier from the slot and attempts to + # perform the exchange. Removing the occupier also frees the slot for + # another occupier/fulfiller pair. + # + # Because the occupier and the fulfiller are operating independently and + # because there may be contention with other threads, any failed operation + # indicates contention. Both the occupier and the fulfiller operate within + # spin loops. Any failed actions along the happy path will cause the thread + # to repeat the loop and try again. + # + # When a timeout value is given the thread must be cognizant of time spent + # in the spin loop. The remaining time is checked every loop. When the time + # runs out the thread will exit. + # + # A "node" is the data structure used to perform the exchange. Only the + # occupier's node is necessary. It's the node used for the exchange. + # Each node has an "item," a "hole" (self), and a "latch." The item is the + # node's initial value. It never changes. It's what the fulfiller returns on + # success. The occupier's hole is where the fulfiller put its item. It's the + # item that the occupier returns on success. The latch is used for synchronization. + # Because a thread may act as either an occupier or fulfiller (or possibly + # both in periods of high contention) every thread creates a node when + # the exchange method is first called. + # + # The following steps occur within the spin loop. If any actions fail + # the thread will loop and try again, so long as there is time remaining. + # If time runs out the thread will return CANCEL. + # + # Check the slot for an occupier: + # + # * If the slot is empty try to occupy + # * If the slot is full try to fulfill + # + # Attempt to occupy: + # + # * Attempt to CAS myself into the slot + # * Go to sleep and wait to be woken by a fulfiller + # * If the sleep is successful then the fulfiller completed its happy path + # - Return the value from my hole (the value given by the fulfiller) + # * When the sleep fails (time ran out) attempt to cancel the operation + # - Attempt to CAS myself out of the hole + # - If successful there is no contention + # - Return CANCEL + # - On failure, I am competing with a fulfiller + # - Attempt to CAS my hole to CANCEL + # - On success + # - Let the fulfiller deal with my cancel + # - Return CANCEL + # - On failure the fulfiller has completed its happy path + # - Return th value from my hole (the fulfiller's value) + # + # Attempt to fulfill: + # + # * Attempt to CAS the occupier out of the slot + # - On failure loop again + # * Attempt to CAS my item into the occupier's hole + # - On failure the occupier is trying to cancel + # - Loop again + # - On success we are on the happy path + # - Wake the sleeping occupier + # - Return the occupier's item + + value = NULL if value.nil? # The sentinel allows nil to be a valid value + me = Node.new(value) # create my node in case I need to occupy + end_at = Concurrent.monotonic_time + timeout.to_f # The time to give up + + result = loop do + other = slot + if other && compare_and_set_slot(other, nil) + # try to fulfill + if other.compare_and_set_value(nil, value) + # happy path + other.latch.count_down + break other.item + end + elsif other.nil? && compare_and_set_slot(nil, me) + # try to occupy + timeout = end_at - Concurrent.monotonic_time if timeout + if me.latch.wait(timeout) + # happy path + break me.value + else + # attempt to remove myself from the slot + if compare_and_set_slot(me, nil) + break CANCEL + elsif !me.compare_and_set_value(nil, CANCEL) + # I've failed to block the fulfiller + break me.value + end + end + end + break CANCEL if timeout && Concurrent.monotonic_time >= end_at + end + + result == NULL ? nil : result + end + end + + if Concurrent.on_jruby? + + # @!macro internal_implementation_note + # @!visibility private + class JavaExchanger < AbstractExchanger + + def initialize + @exchanger = java.util.concurrent.Exchanger.new + end + + private + + # @!macro exchanger_method_do_exchange + # + # @return [Object, CANCEL] the value exchanged by the other thread; {CANCEL} on timeout + def do_exchange(value, timeout) + result = nil + if timeout.nil? + Synchronization::JRuby.sleep_interruptibly do + result = @exchanger.exchange(value) + end + else + Synchronization::JRuby.sleep_interruptibly do + result = @exchanger.exchange(value, 1000 * timeout, java.util.concurrent.TimeUnit::MILLISECONDS) + end + end + result + rescue java.util.concurrent.TimeoutException + CANCEL + end + end + end + + # @!visibility private + # @!macro internal_implementation_note + ExchangerImplementation = case + when Concurrent.on_jruby? + JavaExchanger + else + RubyExchanger + end + private_constant :ExchangerImplementation + + # @!macro exchanger + class Exchanger < ExchangerImplementation + + # @!method initialize + # Creates exchanger instance + + # @!method exchange(value, timeout = nil) + # @!macro exchanger_method_do_exchange + # @!macro exchanger_method_exchange + + # @!method exchange!(value, timeout = nil) + # @!macro exchanger_method_do_exchange + # @!macro exchanger_method_exchange_bang + + # @!method try_exchange(value, timeout = nil) + # @!macro exchanger_method_do_exchange + # @!macro exchanger_method_try_exchange + end +end diff --git a/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/executor/abstract_executor_service.rb b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/executor/abstract_executor_service.rb new file mode 100644 index 0000000..80ff953 --- /dev/null +++ b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/executor/abstract_executor_service.rb @@ -0,0 +1,134 @@ +require 'concurrent/errors' +require 'concurrent/executor/executor_service' +require 'concurrent/synchronization' +require 'concurrent/utility/at_exit' + +module Concurrent + + # @!macro abstract_executor_service_public_api + # @!visibility private + class AbstractExecutorService < Synchronization::LockableObject + include ExecutorService + + # The set of possible fallback policies that may be set at thread pool creation. + FALLBACK_POLICIES = [:abort, :discard, :caller_runs].freeze + + # @!macro executor_service_attr_reader_fallback_policy + attr_reader :fallback_policy + + # Create a new thread pool. + def initialize(*args, &block) + super(&nil) + synchronize { ns_initialize(*args, &block) } + end + + # @!macro executor_service_method_shutdown + def shutdown + raise NotImplementedError + end + + # @!macro executor_service_method_kill + def kill + raise NotImplementedError + end + + # @!macro executor_service_method_wait_for_termination + def wait_for_termination(timeout = nil) + raise NotImplementedError + end + + # @!macro executor_service_method_running_question + def running? + synchronize { ns_running? } + end + + # @!macro executor_service_method_shuttingdown_question + def shuttingdown? + synchronize { ns_shuttingdown? } + end + + # @!macro executor_service_method_shutdown_question + def shutdown? + synchronize { ns_shutdown? } + end + + # @!macro executor_service_method_auto_terminate_question + def auto_terminate? + synchronize { ns_auto_terminate? } + end + + # @!macro executor_service_method_auto_terminate_setter + def auto_terminate=(value) + synchronize { self.ns_auto_terminate = value } + end + + private + + # Handler which executes the `fallback_policy` once the queue size + # reaches `max_queue`. + # + # @param [Array] args the arguments to the task which is being handled. + # + # @!visibility private + def handle_fallback(*args) + case fallback_policy + when :abort + raise RejectedExecutionError + when :discard + false + when :caller_runs + begin + yield(*args) + rescue => ex + # let it fail + log DEBUG, ex + end + true + else + fail "Unknown fallback policy #{fallback_policy}" + end + end + + def ns_execute(*args, &task) + raise NotImplementedError + end + + # @!macro executor_service_method_ns_shutdown_execution + # + # Callback method called when an orderly shutdown has completed. + # The default behavior is to signal all waiting threads. + def ns_shutdown_execution + # do nothing + end + + # @!macro executor_service_method_ns_kill_execution + # + # Callback method called when the executor has been killed. + # The default behavior is to do nothing. + def ns_kill_execution + # do nothing + end + + def ns_auto_terminate? + !!@auto_terminate + end + + def ns_auto_terminate=(value) + case value + when true + AtExit.add(self) { terminate_at_exit } + @auto_terminate = true + when false + AtExit.delete(self) + @auto_terminate = false + else + raise ArgumentError + end + end + + def terminate_at_exit + kill # TODO be gentle first + wait_for_termination(10) + end + end +end diff --git a/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/executor/cached_thread_pool.rb b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/executor/cached_thread_pool.rb new file mode 100644 index 0000000..1c7c18d --- /dev/null +++ b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/executor/cached_thread_pool.rb @@ -0,0 +1,62 @@ +require 'concurrent/utility/engine' +require 'concurrent/executor/thread_pool_executor' + +module Concurrent + + # A thread pool that dynamically grows and shrinks to fit the current workload. + # New threads are created as needed, existing threads are reused, and threads + # that remain idle for too long are killed and removed from the pool. These + # pools are particularly suited to applications that perform a high volume of + # short-lived tasks. + # + # On creation a `CachedThreadPool` has zero running threads. New threads are + # created on the pool as new operations are `#post`. The size of the pool + # will grow until `#max_length` threads are in the pool or until the number + # of threads exceeds the number of running and pending operations. When a new + # operation is post to the pool the first available idle thread will be tasked + # with the new operation. + # + # Should a thread crash for any reason the thread will immediately be removed + # from the pool. Similarly, threads which remain idle for an extended period + # of time will be killed and reclaimed. Thus these thread pools are very + # efficient at reclaiming unused resources. + # + # The API and behavior of this class are based on Java's `CachedThreadPool` + # + # @!macro thread_pool_options + class CachedThreadPool < ThreadPoolExecutor + + # @!macro cached_thread_pool_method_initialize + # + # Create a new thread pool. + # + # @param [Hash] opts the options defining pool behavior. + # @option opts [Symbol] :fallback_policy (`:abort`) the fallback policy + # + # @raise [ArgumentError] if `fallback_policy` is not a known policy + # + # @see http://docs.oracle.com/javase/8/docs/api/java/util/concurrent/Executors.html#newCachedThreadPool-- + def initialize(opts = {}) + defaults = { idletime: DEFAULT_THREAD_IDLETIMEOUT } + overrides = { min_threads: 0, + max_threads: DEFAULT_MAX_POOL_SIZE, + max_queue: DEFAULT_MAX_QUEUE_SIZE } + super(defaults.merge(opts).merge(overrides)) + end + + private + + # @!macro cached_thread_pool_method_initialize + # @!visibility private + def ns_initialize(opts) + super(opts) + if Concurrent.on_jruby? + @max_queue = 0 + @executor = java.util.concurrent.Executors.newCachedThreadPool + @executor.setRejectedExecutionHandler(FALLBACK_POLICY_CLASSES[@fallback_policy].new) + @executor.setKeepAliveTime(opts.fetch(:idletime, DEFAULT_THREAD_IDLETIMEOUT), java.util.concurrent.TimeUnit::SECONDS) + self.auto_terminate = opts.fetch(:auto_terminate, true) + end + end + end +end diff --git a/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/executor/executor_service.rb b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/executor/executor_service.rb new file mode 100644 index 0000000..0fcbeee --- /dev/null +++ b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/executor/executor_service.rb @@ -0,0 +1,185 @@ +require 'concurrent/concern/logging' + +module Concurrent + + ################################################################### + + # @!macro executor_service_method_post + # + # Submit a task to the executor for asynchronous processing. + # + # @param [Array] args zero or more arguments to be passed to the task + # + # @yield the asynchronous task to perform + # + # @return [Boolean] `true` if the task is queued, `false` if the executor + # is not running + # + # @raise [ArgumentError] if no task is given + + # @!macro executor_service_method_left_shift + # + # Submit a task to the executor for asynchronous processing. + # + # @param [Proc] task the asynchronous task to perform + # + # @return [self] returns itself + + # @!macro executor_service_method_can_overflow_question + # + # Does the task queue have a maximum size? + # + # @return [Boolean] True if the task queue has a maximum size else false. + + # @!macro executor_service_method_serialized_question + # + # Does this executor guarantee serialization of its operations? + # + # @return [Boolean] True if the executor guarantees that all operations + # will be post in the order they are received and no two operations may + # occur simultaneously. Else false. + + ################################################################### + + # @!macro executor_service_public_api + # + # @!method post(*args, &task) + # @!macro executor_service_method_post + # + # @!method <<(task) + # @!macro executor_service_method_left_shift + # + # @!method can_overflow? + # @!macro executor_service_method_can_overflow_question + # + # @!method serialized? + # @!macro executor_service_method_serialized_question + + ################################################################### + + # @!macro executor_service_attr_reader_fallback_policy + # @return [Symbol] The fallback policy in effect. Either `:abort`, `:discard`, or `:caller_runs`. + + # @!macro executor_service_method_shutdown + # + # Begin an orderly shutdown. Tasks already in the queue will be executed, + # but no new tasks will be accepted. Has no additional effect if the + # thread pool is not running. + + # @!macro executor_service_method_kill + # + # Begin an immediate shutdown. In-progress tasks will be allowed to + # complete but enqueued tasks will be dismissed and no new tasks + # will be accepted. Has no additional effect if the thread pool is + # not running. + + # @!macro executor_service_method_wait_for_termination + # + # Block until executor shutdown is complete or until `timeout` seconds have + # passed. + # + # @note Does not initiate shutdown or termination. Either `shutdown` or `kill` + # must be called before this method (or on another thread). + # + # @param [Integer] timeout the maximum number of seconds to wait for shutdown to complete + # + # @return [Boolean] `true` if shutdown complete or false on `timeout` + + # @!macro executor_service_method_running_question + # + # Is the executor running? + # + # @return [Boolean] `true` when running, `false` when shutting down or shutdown + + # @!macro executor_service_method_shuttingdown_question + # + # Is the executor shuttingdown? + # + # @return [Boolean] `true` when not running and not shutdown, else `false` + + # @!macro executor_service_method_shutdown_question + # + # Is the executor shutdown? + # + # @return [Boolean] `true` when shutdown, `false` when shutting down or running + + # @!macro executor_service_method_auto_terminate_question + # + # Is the executor auto-terminate when the application exits? + # + # @return [Boolean] `true` when auto-termination is enabled else `false`. + + # @!macro executor_service_method_auto_terminate_setter + # + # Set the auto-terminate behavior for this executor. + # + # @param [Boolean] value The new auto-terminate value to set for this executor. + # + # @return [Boolean] `true` when auto-termination is enabled else `false`. + + ################################################################### + + # @!macro abstract_executor_service_public_api + # + # @!macro executor_service_public_api + # + # @!attribute [r] fallback_policy + # @!macro executor_service_attr_reader_fallback_policy + # + # @!method shutdown + # @!macro executor_service_method_shutdown + # + # @!method kill + # @!macro executor_service_method_kill + # + # @!method wait_for_termination(timeout = nil) + # @!macro executor_service_method_wait_for_termination + # + # @!method running? + # @!macro executor_service_method_running_question + # + # @!method shuttingdown? + # @!macro executor_service_method_shuttingdown_question + # + # @!method shutdown? + # @!macro executor_service_method_shutdown_question + # + # @!method auto_terminate? + # @!macro executor_service_method_auto_terminate_question + # + # @!method auto_terminate=(value) + # @!macro executor_service_method_auto_terminate_setter + + ################################################################### + + # @!macro executor_service_public_api + # @!visibility private + module ExecutorService + include Concern::Logging + + # @!macro executor_service_method_post + def post(*args, &task) + raise NotImplementedError + end + + # @!macro executor_service_method_left_shift + def <<(task) + post(&task) + self + end + + # @!macro executor_service_method_can_overflow_question + # + # @note Always returns `false` + def can_overflow? + false + end + + # @!macro executor_service_method_serialized_question + # + # @note Always returns `false` + def serialized? + false + end + end +end diff --git a/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/executor/fixed_thread_pool.rb b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/executor/fixed_thread_pool.rb new file mode 100644 index 0000000..c9e03da --- /dev/null +++ b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/executor/fixed_thread_pool.rb @@ -0,0 +1,206 @@ +require 'concurrent/utility/engine' +require 'concurrent/executor/thread_pool_executor' + +module Concurrent + + # @!macro thread_pool_executor_constant_default_max_pool_size + # Default maximum number of threads that will be created in the pool. + + # @!macro thread_pool_executor_constant_default_min_pool_size + # Default minimum number of threads that will be retained in the pool. + + # @!macro thread_pool_executor_constant_default_max_queue_size + # Default maximum number of tasks that may be added to the task queue. + + # @!macro thread_pool_executor_constant_default_thread_timeout + # Default maximum number of seconds a thread in the pool may remain idle + # before being reclaimed. + + # @!macro thread_pool_executor_attr_reader_max_length + # The maximum number of threads that may be created in the pool. + # @return [Integer] The maximum number of threads that may be created in the pool. + + # @!macro thread_pool_executor_attr_reader_min_length + # The minimum number of threads that may be retained in the pool. + # @return [Integer] The minimum number of threads that may be retained in the pool. + + # @!macro thread_pool_executor_attr_reader_largest_length + # The largest number of threads that have been created in the pool since construction. + # @return [Integer] The largest number of threads that have been created in the pool since construction. + + # @!macro thread_pool_executor_attr_reader_scheduled_task_count + # The number of tasks that have been scheduled for execution on the pool since construction. + # @return [Integer] The number of tasks that have been scheduled for execution on the pool since construction. + + # @!macro thread_pool_executor_attr_reader_completed_task_count + # The number of tasks that have been completed by the pool since construction. + # @return [Integer] The number of tasks that have been completed by the pool since construction. + + # @!macro thread_pool_executor_attr_reader_idletime + # The number of seconds that a thread may be idle before being reclaimed. + # @return [Integer] The number of seconds that a thread may be idle before being reclaimed. + + # @!macro thread_pool_executor_attr_reader_max_queue + # The maximum number of tasks that may be waiting in the work queue at any one time. + # When the queue size reaches `max_queue` subsequent tasks will be rejected in + # accordance with the configured `fallback_policy`. + # + # @return [Integer] The maximum number of tasks that may be waiting in the work queue at any one time. + # When the queue size reaches `max_queue` subsequent tasks will be rejected in + # accordance with the configured `fallback_policy`. + + # @!macro thread_pool_executor_attr_reader_length + # The number of threads currently in the pool. + # @return [Integer] The number of threads currently in the pool. + + # @!macro thread_pool_executor_attr_reader_queue_length + # The number of tasks in the queue awaiting execution. + # @return [Integer] The number of tasks in the queue awaiting execution. + + # @!macro thread_pool_executor_attr_reader_remaining_capacity + # Number of tasks that may be enqueued before reaching `max_queue` and rejecting + # new tasks. A value of -1 indicates that the queue may grow without bound. + # + # @return [Integer] Number of tasks that may be enqueued before reaching `max_queue` and rejecting + # new tasks. A value of -1 indicates that the queue may grow without bound. + + + + + + # @!macro thread_pool_executor_public_api + # + # @!macro abstract_executor_service_public_api + # + # @!attribute [r] max_length + # @!macro thread_pool_executor_attr_reader_max_length + # + # @!attribute [r] min_length + # @!macro thread_pool_executor_attr_reader_min_length + # + # @!attribute [r] largest_length + # @!macro thread_pool_executor_attr_reader_largest_length + # + # @!attribute [r] scheduled_task_count + # @!macro thread_pool_executor_attr_reader_scheduled_task_count + # + # @!attribute [r] completed_task_count + # @!macro thread_pool_executor_attr_reader_completed_task_count + # + # @!attribute [r] idletime + # @!macro thread_pool_executor_attr_reader_idletime + # + # @!attribute [r] max_queue + # @!macro thread_pool_executor_attr_reader_max_queue + # + # @!attribute [r] length + # @!macro thread_pool_executor_attr_reader_length + # + # @!attribute [r] queue_length + # @!macro thread_pool_executor_attr_reader_queue_length + # + # @!attribute [r] remaining_capacity + # @!macro thread_pool_executor_attr_reader_remaining_capacity + # + # @!method can_overflow? + # @!macro executor_service_method_can_overflow_question + + + + + # @!macro thread_pool_options + # + # **Thread Pool Options** + # + # Thread pools support several configuration options: + # + # * `idletime`: The number of seconds that a thread may be idle before being reclaimed. + # * `max_queue`: The maximum number of tasks that may be waiting in the work queue at + # any one time. When the queue size reaches `max_queue` and no new threads can be created, + # subsequent tasks will be rejected in accordance with the configured `fallback_policy`. + # * `auto_terminate`: When true (default) an `at_exit` handler will be registered which + # will stop the thread pool when the application exits. See below for more information + # on shutting down thread pools. + # * `fallback_policy`: The policy defining how rejected tasks are handled. + # + # Three fallback policies are supported: + # + # * `:abort`: Raise a `RejectedExecutionError` exception and discard the task. + # * `:discard`: Discard the task and return false. + # * `:caller_runs`: Execute the task on the calling thread. + # + # **Shutting Down Thread Pools** + # + # Killing a thread pool while tasks are still being processed, either by calling + # the `#kill` method or at application exit, will have unpredictable results. There + # is no way for the thread pool to know what resources are being used by the + # in-progress tasks. When those tasks are killed the impact on those resources + # cannot be predicted. The *best* practice is to explicitly shutdown all thread + # pools using the provided methods: + # + # * Call `#shutdown` to initiate an orderly termination of all in-progress tasks + # * Call `#wait_for_termination` with an appropriate timeout interval an allow + # the orderly shutdown to complete + # * Call `#kill` *only when* the thread pool fails to shutdown in the allotted time + # + # On some runtime platforms (most notably the JVM) the application will not + # exit until all thread pools have been shutdown. To prevent applications from + # "hanging" on exit all thread pools include an `at_exit` handler that will + # stop the thread pool when the application exits. This handler uses a brute + # force method to stop the pool and makes no guarantees regarding resources being + # used by any tasks still running. Registration of this `at_exit` handler can be + # prevented by setting the thread pool's constructor `:auto_terminate` option to + # `false` when the thread pool is created. All thread pools support this option. + # + # ```ruby + # pool1 = Concurrent::FixedThreadPool.new(5) # an `at_exit` handler will be registered + # pool2 = Concurrent::FixedThreadPool.new(5, auto_terminate: false) # prevent `at_exit` handler registration + # ``` + # + # @note Failure to properly shutdown a thread pool can lead to unpredictable results. + # Please read *Shutting Down Thread Pools* for more information. + # + # @see http://docs.oracle.com/javase/tutorial/essential/concurrency/pools.html Java Tutorials: Thread Pools + # @see http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/Executors.html Java Executors class + # @see http://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ExecutorService.html Java ExecutorService interface + # @see http://ruby-doc.org//core-2.2.0/Kernel.html#method-i-at_exit Kernel#at_exit + + + + + + # @!macro fixed_thread_pool + # + # A thread pool that reuses a fixed number of threads operating off an unbounded queue. + # At any point, at most `num_threads` will be active processing tasks. When all threads are busy new + # tasks `#post` to the thread pool are enqueued until a thread becomes available. + # Should a thread crash for any reason the thread will immediately be removed + # from the pool and replaced. + # + # The API and behavior of this class are based on Java's `FixedThreadPool` + # + # @!macro thread_pool_options + class FixedThreadPool < ThreadPoolExecutor + + # @!macro fixed_thread_pool_method_initialize + # + # Create a new thread pool. + # + # @param [Integer] num_threads the number of threads to allocate + # @param [Hash] opts the options defining pool behavior. + # @option opts [Symbol] :fallback_policy (`:abort`) the fallback policy + # + # @raise [ArgumentError] if `num_threads` is less than or equal to zero + # @raise [ArgumentError] if `fallback_policy` is not a known policy + # + # @see http://docs.oracle.com/javase/8/docs/api/java/util/concurrent/Executors.html#newFixedThreadPool-int- + def initialize(num_threads, opts = {}) + raise ArgumentError.new('number of threads must be greater than zero') if num_threads.to_i < 1 + defaults = { max_queue: DEFAULT_MAX_QUEUE_SIZE, + idletime: DEFAULT_THREAD_IDLETIMEOUT } + overrides = { min_threads: num_threads, + max_threads: num_threads } + super(defaults.merge(opts).merge(overrides)) + end + end +end diff --git a/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/executor/immediate_executor.rb b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/executor/immediate_executor.rb new file mode 100644 index 0000000..282df7a --- /dev/null +++ b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/executor/immediate_executor.rb @@ -0,0 +1,66 @@ +require 'concurrent/atomic/event' +require 'concurrent/executor/abstract_executor_service' +require 'concurrent/executor/serial_executor_service' + +module Concurrent + + # An executor service which runs all operations on the current thread, + # blocking as necessary. Operations are performed in the order they are + # received and no two operations can be performed simultaneously. + # + # This executor service exists mainly for testing an debugging. When used + # it immediately runs every `#post` operation on the current thread, blocking + # that thread until the operation is complete. This can be very beneficial + # during testing because it makes all operations deterministic. + # + # @note Intended for use primarily in testing and debugging. + class ImmediateExecutor < AbstractExecutorService + include SerialExecutorService + + # Creates a new executor + def initialize + @stopped = Concurrent::Event.new + end + + # @!macro executor_service_method_post + def post(*args, &task) + raise ArgumentError.new('no block given') unless block_given? + return false unless running? + task.call(*args) + true + end + + # @!macro executor_service_method_left_shift + def <<(task) + post(&task) + self + end + + # @!macro executor_service_method_running_question + def running? + ! shutdown? + end + + # @!macro executor_service_method_shuttingdown_question + def shuttingdown? + false + end + + # @!macro executor_service_method_shutdown_question + def shutdown? + @stopped.set? + end + + # @!macro executor_service_method_shutdown + def shutdown + @stopped.set + true + end + alias_method :kill, :shutdown + + # @!macro executor_service_method_wait_for_termination + def wait_for_termination(timeout = nil) + @stopped.wait(timeout) + end + end +end diff --git a/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/executor/indirect_immediate_executor.rb b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/executor/indirect_immediate_executor.rb new file mode 100644 index 0000000..4f9769f --- /dev/null +++ b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/executor/indirect_immediate_executor.rb @@ -0,0 +1,44 @@ +require 'concurrent/executor/immediate_executor' +require 'concurrent/executor/simple_executor_service' + +module Concurrent + # An executor service which runs all operations on a new thread, blocking + # until it completes. Operations are performed in the order they are received + # and no two operations can be performed simultaneously. + # + # This executor service exists mainly for testing an debugging. When used it + # immediately runs every `#post` operation on a new thread, blocking the + # current thread until the operation is complete. This is similar to how the + # ImmediateExecutor works, but the operation has the full stack of the new + # thread at its disposal. This can be helpful when the operations will spawn + # more operations on the same executor and so on - such a situation might + # overflow the single stack in case of an ImmediateExecutor, which is + # inconsistent with how it would behave for a threaded executor. + # + # @note Intended for use primarily in testing and debugging. + class IndirectImmediateExecutor < ImmediateExecutor + # Creates a new executor + def initialize + super + @internal_executor = SimpleExecutorService.new + end + + # @!macro executor_service_method_post + def post(*args, &task) + raise ArgumentError.new("no block given") unless block_given? + return false unless running? + + event = Concurrent::Event.new + @internal_executor.post do + begin + task.call(*args) + ensure + event.set + end + end + event.wait + + true + end + end +end diff --git a/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/executor/java_executor_service.rb b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/executor/java_executor_service.rb new file mode 100644 index 0000000..113e0bc --- /dev/null +++ b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/executor/java_executor_service.rb @@ -0,0 +1,91 @@ +if Concurrent.on_jruby? + + require 'concurrent/errors' + require 'concurrent/utility/engine' + require 'concurrent/executor/abstract_executor_service' + + module Concurrent + + # @!macro abstract_executor_service_public_api + # @!visibility private + class JavaExecutorService < AbstractExecutorService + java_import 'java.lang.Runnable' + + FALLBACK_POLICY_CLASSES = { + abort: java.util.concurrent.ThreadPoolExecutor::AbortPolicy, + discard: java.util.concurrent.ThreadPoolExecutor::DiscardPolicy, + caller_runs: java.util.concurrent.ThreadPoolExecutor::CallerRunsPolicy + }.freeze + private_constant :FALLBACK_POLICY_CLASSES + + def initialize(*args, &block) + super + end + + def post(*args, &task) + raise ArgumentError.new('no block given') unless block_given? + return handle_fallback(*args, &task) unless running? + @executor.submit Job.new(args, task) + true + rescue Java::JavaUtilConcurrent::RejectedExecutionException + raise RejectedExecutionError + end + + def wait_for_termination(timeout = nil) + if timeout.nil? + ok = @executor.awaitTermination(60, java.util.concurrent.TimeUnit::SECONDS) until ok + true + else + @executor.awaitTermination(1000 * timeout, java.util.concurrent.TimeUnit::MILLISECONDS) + end + end + + def shutdown + synchronize do + self.ns_auto_terminate = false + @executor.shutdown + nil + end + end + + def kill + synchronize do + self.ns_auto_terminate = false + @executor.shutdownNow + nil + end + end + + private + + def ns_running? + !(ns_shuttingdown? || ns_shutdown?) + end + + def ns_shuttingdown? + if @executor.respond_to? :isTerminating + @executor.isTerminating + else + false + end + end + + def ns_shutdown? + @executor.isShutdown || @executor.isTerminated + end + + class Job + include Runnable + def initialize(args, block) + @args = args + @block = block + end + + def run + @block.call(*@args) + end + end + private_constant :Job + end + end +end diff --git a/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/executor/java_single_thread_executor.rb b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/executor/java_single_thread_executor.rb new file mode 100644 index 0000000..1cf59b0 --- /dev/null +++ b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/executor/java_single_thread_executor.rb @@ -0,0 +1,29 @@ +if Concurrent.on_jruby? + + require 'concurrent/executor/java_executor_service' + require 'concurrent/executor/serial_executor_service' + + module Concurrent + + # @!macro single_thread_executor + # @!macro abstract_executor_service_public_api + # @!visibility private + class JavaSingleThreadExecutor < JavaExecutorService + include SerialExecutorService + + # @!macro single_thread_executor_method_initialize + def initialize(opts = {}) + super(opts) + end + + private + + def ns_initialize(opts) + @executor = java.util.concurrent.Executors.newSingleThreadExecutor + @fallback_policy = opts.fetch(:fallback_policy, :discard) + raise ArgumentError.new("#{@fallback_policy} is not a valid fallback policy") unless FALLBACK_POLICY_CLASSES.keys.include?(@fallback_policy) + self.auto_terminate = opts.fetch(:auto_terminate, true) + end + end + end +end diff --git a/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/executor/java_thread_pool_executor.rb b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/executor/java_thread_pool_executor.rb new file mode 100644 index 0000000..6308e4f --- /dev/null +++ b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/executor/java_thread_pool_executor.rb @@ -0,0 +1,123 @@ +if Concurrent.on_jruby? + + require 'concurrent/executor/java_executor_service' + + module Concurrent + + # @!macro thread_pool_executor + # @!macro thread_pool_options + # @!visibility private + class JavaThreadPoolExecutor < JavaExecutorService + + # @!macro thread_pool_executor_constant_default_max_pool_size + DEFAULT_MAX_POOL_SIZE = java.lang.Integer::MAX_VALUE # 2147483647 + + # @!macro thread_pool_executor_constant_default_min_pool_size + DEFAULT_MIN_POOL_SIZE = 0 + + # @!macro thread_pool_executor_constant_default_max_queue_size + DEFAULT_MAX_QUEUE_SIZE = 0 + + # @!macro thread_pool_executor_constant_default_thread_timeout + DEFAULT_THREAD_IDLETIMEOUT = 60 + + # @!macro thread_pool_executor_attr_reader_max_length + attr_reader :max_length + + # @!macro thread_pool_executor_attr_reader_max_queue + attr_reader :max_queue + + # @!macro thread_pool_executor_method_initialize + def initialize(opts = {}) + super(opts) + end + + # @!macro executor_service_method_can_overflow_question + def can_overflow? + @max_queue != 0 + end + + # @!macro thread_pool_executor_attr_reader_min_length + def min_length + @executor.getCorePoolSize + end + + # @!macro thread_pool_executor_attr_reader_max_length + def max_length + @executor.getMaximumPoolSize + end + + # @!macro thread_pool_executor_attr_reader_length + def length + @executor.getPoolSize + end + + # @!macro thread_pool_executor_attr_reader_largest_length + def largest_length + @executor.getLargestPoolSize + end + + # @!macro thread_pool_executor_attr_reader_scheduled_task_count + def scheduled_task_count + @executor.getTaskCount + end + + # @!macro thread_pool_executor_attr_reader_completed_task_count + def completed_task_count + @executor.getCompletedTaskCount + end + + # @!macro thread_pool_executor_attr_reader_idletime + def idletime + @executor.getKeepAliveTime(java.util.concurrent.TimeUnit::SECONDS) + end + + # @!macro thread_pool_executor_attr_reader_queue_length + def queue_length + @executor.getQueue.size + end + + # @!macro thread_pool_executor_attr_reader_remaining_capacity + def remaining_capacity + @max_queue == 0 ? -1 : @executor.getQueue.remainingCapacity + end + + # @!macro executor_service_method_running_question + def running? + super && !@executor.isTerminating + end + + private + + def ns_initialize(opts) + min_length = opts.fetch(:min_threads, DEFAULT_MIN_POOL_SIZE).to_i + max_length = opts.fetch(:max_threads, DEFAULT_MAX_POOL_SIZE).to_i + idletime = opts.fetch(:idletime, DEFAULT_THREAD_IDLETIMEOUT).to_i + @max_queue = opts.fetch(:max_queue, DEFAULT_MAX_QUEUE_SIZE).to_i + @fallback_policy = opts.fetch(:fallback_policy, :abort) + + raise ArgumentError.new("`max_threads` cannot be less than #{DEFAULT_MIN_POOL_SIZE}") if max_length < DEFAULT_MIN_POOL_SIZE + raise ArgumentError.new("`max_threads` cannot be greater than #{DEFAULT_MAX_POOL_SIZE}") if max_length > DEFAULT_MAX_POOL_SIZE + raise ArgumentError.new("`min_threads` cannot be less than #{DEFAULT_MIN_POOL_SIZE}") if min_length < DEFAULT_MIN_POOL_SIZE + raise ArgumentError.new("`min_threads` cannot be more than `max_threads`") if min_length > max_length + raise ArgumentError.new("#{fallback_policy} is not a valid fallback policy") unless FALLBACK_POLICY_CLASSES.include?(@fallback_policy) + + if @max_queue == 0 + queue = java.util.concurrent.LinkedBlockingQueue.new + else + queue = java.util.concurrent.LinkedBlockingQueue.new(@max_queue) + end + + @executor = java.util.concurrent.ThreadPoolExecutor.new( + min_length, + max_length, + idletime, + java.util.concurrent.TimeUnit::SECONDS, + queue, + FALLBACK_POLICY_CLASSES[@fallback_policy].new) + + self.auto_terminate = opts.fetch(:auto_terminate, true) + end + end + end +end diff --git a/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/executor/ruby_executor_service.rb b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/executor/ruby_executor_service.rb new file mode 100644 index 0000000..7b2ee73 --- /dev/null +++ b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/executor/ruby_executor_service.rb @@ -0,0 +1,78 @@ +require 'concurrent/executor/abstract_executor_service' +require 'concurrent/atomic/event' + +module Concurrent + + # @!macro abstract_executor_service_public_api + # @!visibility private + class RubyExecutorService < AbstractExecutorService + safe_initialization! + + def initialize(*args, &block) + super + @StopEvent = Event.new + @StoppedEvent = Event.new + end + + def post(*args, &task) + raise ArgumentError.new('no block given') unless block_given? + synchronize do + # If the executor is shut down, reject this task + return handle_fallback(*args, &task) unless running? + ns_execute(*args, &task) + true + end + end + + def shutdown + synchronize do + break unless running? + self.ns_auto_terminate = false + stop_event.set + ns_shutdown_execution + end + true + end + + def kill + synchronize do + break if shutdown? + self.ns_auto_terminate = false + stop_event.set + ns_kill_execution + stopped_event.set + end + true + end + + def wait_for_termination(timeout = nil) + stopped_event.wait(timeout) + end + + private + + def stop_event + @StopEvent + end + + def stopped_event + @StoppedEvent + end + + def ns_shutdown_execution + stopped_event.set + end + + def ns_running? + !stop_event.set? + end + + def ns_shuttingdown? + !(ns_running? || ns_shutdown?) + end + + def ns_shutdown? + stopped_event.set? + end + end +end diff --git a/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/executor/ruby_single_thread_executor.rb b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/executor/ruby_single_thread_executor.rb new file mode 100644 index 0000000..305a49e --- /dev/null +++ b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/executor/ruby_single_thread_executor.rb @@ -0,0 +1,22 @@ +require 'concurrent/executor/ruby_thread_pool_executor' + +module Concurrent + + # @!macro single_thread_executor + # @!macro abstract_executor_service_public_api + # @!visibility private + class RubySingleThreadExecutor < RubyThreadPoolExecutor + + # @!macro single_thread_executor_method_initialize + def initialize(opts = {}) + super( + min_threads: 1, + max_threads: 1, + max_queue: 0, + idletime: DEFAULT_THREAD_IDLETIMEOUT, + fallback_policy: opts.fetch(:fallback_policy, :discard), + auto_terminate: opts.fetch(:auto_terminate, true) + ) + end + end +end diff --git a/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/executor/ruby_thread_pool_executor.rb b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/executor/ruby_thread_pool_executor.rb new file mode 100644 index 0000000..92fbd31 --- /dev/null +++ b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/executor/ruby_thread_pool_executor.rb @@ -0,0 +1,362 @@ +require 'thread' +require 'concurrent/atomic/event' +require 'concurrent/concern/logging' +require 'concurrent/executor/ruby_executor_service' +require 'concurrent/utility/monotonic_time' + +module Concurrent + + # @!macro thread_pool_executor + # @!macro thread_pool_options + # @!visibility private + class RubyThreadPoolExecutor < RubyExecutorService + + # @!macro thread_pool_executor_constant_default_max_pool_size + DEFAULT_MAX_POOL_SIZE = 2_147_483_647 # java.lang.Integer::MAX_VALUE + + # @!macro thread_pool_executor_constant_default_min_pool_size + DEFAULT_MIN_POOL_SIZE = 0 + + # @!macro thread_pool_executor_constant_default_max_queue_size + DEFAULT_MAX_QUEUE_SIZE = 0 + + # @!macro thread_pool_executor_constant_default_thread_timeout + DEFAULT_THREAD_IDLETIMEOUT = 60 + + # @!macro thread_pool_executor_attr_reader_max_length + attr_reader :max_length + + # @!macro thread_pool_executor_attr_reader_min_length + attr_reader :min_length + + # @!macro thread_pool_executor_attr_reader_idletime + attr_reader :idletime + + # @!macro thread_pool_executor_attr_reader_max_queue + attr_reader :max_queue + + # @!macro thread_pool_executor_method_initialize + def initialize(opts = {}) + super(opts) + end + + # @!macro thread_pool_executor_attr_reader_largest_length + def largest_length + synchronize { @largest_length } + end + + # @!macro thread_pool_executor_attr_reader_scheduled_task_count + def scheduled_task_count + synchronize { @scheduled_task_count } + end + + # @!macro thread_pool_executor_attr_reader_completed_task_count + def completed_task_count + synchronize { @completed_task_count } + end + + # @!macro executor_service_method_can_overflow_question + def can_overflow? + synchronize { ns_limited_queue? } + end + + # @!macro thread_pool_executor_attr_reader_length + def length + synchronize { @pool.length } + end + + # @!macro thread_pool_executor_attr_reader_queue_length + def queue_length + synchronize { @queue.length } + end + + # @!macro thread_pool_executor_attr_reader_remaining_capacity + def remaining_capacity + synchronize do + if ns_limited_queue? + @max_queue - @queue.length + else + -1 + end + end + end + + # @!visibility private + def remove_busy_worker(worker) + synchronize { ns_remove_busy_worker worker } + end + + # @!visibility private + def ready_worker(worker) + synchronize { ns_ready_worker worker } + end + + # @!visibility private + def worker_not_old_enough(worker) + synchronize { ns_worker_not_old_enough worker } + end + + # @!visibility private + def worker_died(worker) + synchronize { ns_worker_died worker } + end + + # @!visibility private + def worker_task_completed + synchronize { @completed_task_count += 1 } + end + + private + + # @!visibility private + def ns_initialize(opts) + @min_length = opts.fetch(:min_threads, DEFAULT_MIN_POOL_SIZE).to_i + @max_length = opts.fetch(:max_threads, DEFAULT_MAX_POOL_SIZE).to_i + @idletime = opts.fetch(:idletime, DEFAULT_THREAD_IDLETIMEOUT).to_i + @max_queue = opts.fetch(:max_queue, DEFAULT_MAX_QUEUE_SIZE).to_i + @fallback_policy = opts.fetch(:fallback_policy, :abort) + raise ArgumentError.new("#{@fallback_policy} is not a valid fallback policy") unless FALLBACK_POLICIES.include?(@fallback_policy) + + raise ArgumentError.new("`max_threads` cannot be less than #{DEFAULT_MIN_POOL_SIZE}") if @max_length < DEFAULT_MIN_POOL_SIZE + raise ArgumentError.new("`max_threads` cannot be greater than #{DEFAULT_MAX_POOL_SIZE}") if @max_length > DEFAULT_MAX_POOL_SIZE + raise ArgumentError.new("`min_threads` cannot be less than #{DEFAULT_MIN_POOL_SIZE}") if @min_length < DEFAULT_MIN_POOL_SIZE + raise ArgumentError.new("`min_threads` cannot be more than `max_threads`") if min_length > max_length + + self.auto_terminate = opts.fetch(:auto_terminate, true) + + @pool = [] # all workers + @ready = [] # used as a stash (most idle worker is at the start) + @queue = [] # used as queue + # @ready or @queue is empty at all times + @scheduled_task_count = 0 + @completed_task_count = 0 + @largest_length = 0 + @ruby_pid = $$ # detects if Ruby has forked + + @gc_interval = opts.fetch(:gc_interval, @idletime / 2.0).to_i # undocumented + @next_gc_time = Concurrent.monotonic_time + @gc_interval + end + + # @!visibility private + def ns_limited_queue? + @max_queue != 0 + end + + # @!visibility private + def ns_execute(*args, &task) + ns_reset_if_forked + + if ns_assign_worker(*args, &task) || ns_enqueue(*args, &task) + @scheduled_task_count += 1 + else + handle_fallback(*args, &task) + end + + ns_prune_pool if @next_gc_time < Concurrent.monotonic_time + end + + # @!visibility private + def ns_shutdown_execution + ns_reset_if_forked + + if @pool.empty? + # nothing to do + stopped_event.set + end + + if @queue.empty? + # no more tasks will be accepted, just stop all workers + @pool.each(&:stop) + end + end + + # @!visibility private + def ns_kill_execution + # TODO log out unprocessed tasks in queue + # TODO try to shutdown first? + @pool.each(&:kill) + @pool.clear + @ready.clear + end + + # tries to assign task to a worker, tries to get one from @ready or to create new one + # @return [true, false] if task is assigned to a worker + # + # @!visibility private + def ns_assign_worker(*args, &task) + # keep growing if the pool is not at the minimum yet + worker = (@ready.pop if @pool.size >= @min_length) || ns_add_busy_worker + if worker + worker << [task, args] + true + else + false + end + rescue ThreadError + # Raised when the operating system refuses to create the new thread + return false + end + + # tries to enqueue task + # @return [true, false] if enqueued + # + # @!visibility private + def ns_enqueue(*args, &task) + if !ns_limited_queue? || @queue.size < @max_queue + @queue << [task, args] + true + else + false + end + end + + # @!visibility private + def ns_worker_died(worker) + ns_remove_busy_worker worker + replacement_worker = ns_add_busy_worker + ns_ready_worker replacement_worker, false if replacement_worker + end + + # creates new worker which has to receive work to do after it's added + # @return [nil, Worker] nil of max capacity is reached + # + # @!visibility private + def ns_add_busy_worker + return if @pool.size >= @max_length + + @pool << (worker = Worker.new(self)) + @largest_length = @pool.length if @pool.length > @largest_length + worker + end + + # handle ready worker, giving it new job or assigning back to @ready + # + # @!visibility private + def ns_ready_worker(worker, success = true) + task_and_args = @queue.shift + if task_and_args + worker << task_and_args + else + # stop workers when !running?, do not return them to @ready + if running? + @ready.push(worker) + else + worker.stop + end + end + end + + # returns back worker to @ready which was not idle for enough time + # + # @!visibility private + def ns_worker_not_old_enough(worker) + # let's put workers coming from idle_test back to the start (as the oldest worker) + @ready.unshift(worker) + true + end + + # removes a worker which is not in not tracked in @ready + # + # @!visibility private + def ns_remove_busy_worker(worker) + @pool.delete(worker) + stopped_event.set if @pool.empty? && !running? + true + end + + # try oldest worker if it is idle for enough time, it's returned back at the start + # + # @!visibility private + def ns_prune_pool + return if @pool.size <= @min_length + + last_used = @ready.shift + last_used << :idle_test if last_used + + @next_gc_time = Concurrent.monotonic_time + @gc_interval + end + + def ns_reset_if_forked + if $$ != @ruby_pid + @queue.clear + @ready.clear + @pool.clear + @scheduled_task_count = 0 + @completed_task_count = 0 + @largest_length = 0 + @ruby_pid = $$ + end + end + + # @!visibility private + class Worker + include Concern::Logging + + def initialize(pool) + # instance variables accessed only under pool's lock so no need to sync here again + @queue = Queue.new + @pool = pool + @thread = create_worker @queue, pool, pool.idletime + end + + def <<(message) + @queue << message + end + + def stop + @queue << :stop + end + + def kill + @thread.kill + end + + private + + def create_worker(queue, pool, idletime) + Thread.new(queue, pool, idletime) do |my_queue, my_pool, my_idletime| + last_message = Concurrent.monotonic_time + catch(:stop) do + loop do + + case message = my_queue.pop + when :idle_test + if (Concurrent.monotonic_time - last_message) > my_idletime + my_pool.remove_busy_worker(self) + throw :stop + else + my_pool.worker_not_old_enough(self) + end + + when :stop + my_pool.remove_busy_worker(self) + throw :stop + + else + task, args = message + run_task my_pool, task, args + last_message = Concurrent.monotonic_time + + my_pool.ready_worker(self) + end + end + end + end + end + + def run_task(pool, task, args) + task.call(*args) + pool.worker_task_completed + rescue => ex + # let it fail + log DEBUG, ex + rescue Exception => ex + log ERROR, ex + pool.worker_died(self) + throw :stop + end + end + + private_constant :Worker + end +end diff --git a/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/executor/safe_task_executor.rb b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/executor/safe_task_executor.rb new file mode 100644 index 0000000..414aa64 --- /dev/null +++ b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/executor/safe_task_executor.rb @@ -0,0 +1,35 @@ +require 'concurrent/synchronization' + +module Concurrent + + # A simple utility class that executes a callable and returns and array of three elements: + # success - indicating if the callable has been executed without errors + # value - filled by the callable result if it has been executed without errors, nil otherwise + # reason - the error risen by the callable if it has been executed with errors, nil otherwise + class SafeTaskExecutor < Synchronization::LockableObject + + def initialize(task, opts = {}) + @task = task + @exception_class = opts.fetch(:rescue_exception, false) ? Exception : StandardError + super() # ensures visibility + end + + # @return [Array] + def execute(*args) + synchronize do + success = false + value = reason = nil + + begin + value = @task.call(*args) + success = true + rescue @exception_class => ex + reason = ex + success = false + end + + [success, value, reason] + end + end + end +end diff --git a/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/executor/serial_executor_service.rb b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/executor/serial_executor_service.rb new file mode 100644 index 0000000..f1c38ec --- /dev/null +++ b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/executor/serial_executor_service.rb @@ -0,0 +1,34 @@ +require 'concurrent/executor/executor_service' + +module Concurrent + + # Indicates that the including `ExecutorService` guarantees + # that all operations will occur in the order they are post and that no + # two operations may occur simultaneously. This module provides no + # functionality and provides no guarantees. That is the responsibility + # of the including class. This module exists solely to allow the including + # object to be interrogated for its serialization status. + # + # @example + # class Foo + # include Concurrent::SerialExecutor + # end + # + # foo = Foo.new + # + # foo.is_a? Concurrent::ExecutorService #=> true + # foo.is_a? Concurrent::SerialExecutor #=> true + # foo.serialized? #=> true + # + # @!visibility private + module SerialExecutorService + include ExecutorService + + # @!macro executor_service_method_serialized_question + # + # @note Always returns `true` + def serialized? + true + end + end +end diff --git a/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/executor/serialized_execution.rb b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/executor/serialized_execution.rb new file mode 100644 index 0000000..d314e90 --- /dev/null +++ b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/executor/serialized_execution.rb @@ -0,0 +1,107 @@ +require 'concurrent/errors' +require 'concurrent/concern/logging' +require 'concurrent/synchronization' + +module Concurrent + + # Ensures passed jobs in a serialized order never running at the same time. + class SerializedExecution < Synchronization::LockableObject + include Concern::Logging + + def initialize() + super() + synchronize { ns_initialize } + end + + Job = Struct.new(:executor, :args, :block) do + def call + block.call(*args) + end + end + + # Submit a task to the executor for asynchronous processing. + # + # @param [Executor] executor to be used for this job + # + # @param [Array] args zero or more arguments to be passed to the task + # + # @yield the asynchronous task to perform + # + # @return [Boolean] `true` if the task is queued, `false` if the executor + # is not running + # + # @raise [ArgumentError] if no task is given + def post(executor, *args, &task) + posts [[executor, args, task]] + true + end + + # As {#post} but allows to submit multiple tasks at once, it's guaranteed that they will not + # be interleaved by other tasks. + # + # @param [Array, Proc)>] posts array of triplets where + # first is a {ExecutorService}, second is array of args for task, third is a task (Proc) + def posts(posts) + # if can_overflow? + # raise ArgumentError, 'SerializedExecution does not support thread-pools which can overflow' + # end + + return nil if posts.empty? + + jobs = posts.map { |executor, args, task| Job.new executor, args, task } + + job_to_post = synchronize do + if @being_executed + @stash.push(*jobs) + nil + else + @being_executed = true + @stash.push(*jobs[1..-1]) + jobs.first + end + end + + call_job job_to_post if job_to_post + true + end + + private + + def ns_initialize + @being_executed = false + @stash = [] + end + + def call_job(job) + did_it_run = begin + job.executor.post { work(job) } + true + rescue RejectedExecutionError => ex + false + end + + # TODO not the best idea to run it myself + unless did_it_run + begin + work job + rescue => ex + # let it fail + log DEBUG, ex + end + end + end + + # ensures next job is executed if any is stashed + def work(job) + job.call + ensure + synchronize do + job = @stash.shift || (@being_executed = false) + end + + # TODO maybe be able to tell caching pool to just enqueue this job, because the current one end at the end + # of this block + call_job job if job + end + end +end diff --git a/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/executor/serialized_execution_delegator.rb b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/executor/serialized_execution_delegator.rb new file mode 100644 index 0000000..8197781 --- /dev/null +++ b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/executor/serialized_execution_delegator.rb @@ -0,0 +1,28 @@ +require 'delegate' +require 'concurrent/executor/serial_executor_service' +require 'concurrent/executor/serialized_execution' + +module Concurrent + + # A wrapper/delegator for any `ExecutorService` that + # guarantees serialized execution of tasks. + # + # @see [SimpleDelegator](http://www.ruby-doc.org/stdlib-2.1.2/libdoc/delegate/rdoc/SimpleDelegator.html) + # @see Concurrent::SerializedExecution + class SerializedExecutionDelegator < SimpleDelegator + include SerialExecutorService + + def initialize(executor) + @executor = executor + @serializer = SerializedExecution.new + super(executor) + end + + # @!macro executor_service_method_post + def post(*args, &task) + raise ArgumentError.new('no block given') unless block_given? + return false unless running? + @serializer.post(@executor, *args, &task) + end + end +end diff --git a/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/executor/simple_executor_service.rb b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/executor/simple_executor_service.rb new file mode 100644 index 0000000..b278dbf --- /dev/null +++ b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/executor/simple_executor_service.rb @@ -0,0 +1,100 @@ +require 'concurrent/atomics' +require 'concurrent/executor/executor_service' + +module Concurrent + + # An executor service in which every operation spawns a new, + # independently operating thread. + # + # This is perhaps the most inefficient executor service in this + # library. It exists mainly for testing an debugging. Thread creation + # and management is expensive in Ruby and this executor performs no + # resource pooling. This can be very beneficial during testing and + # debugging because it decouples the using code from the underlying + # executor implementation. In production this executor will likely + # lead to suboptimal performance. + # + # @note Intended for use primarily in testing and debugging. + class SimpleExecutorService < RubyExecutorService + + # @!macro executor_service_method_post + def self.post(*args) + raise ArgumentError.new('no block given') unless block_given? + Thread.new(*args) do + Thread.current.abort_on_exception = false + yield(*args) + end + true + end + + # @!macro executor_service_method_left_shift + def self.<<(task) + post(&task) + self + end + + # @!macro executor_service_method_post + def post(*args, &task) + raise ArgumentError.new('no block given') unless block_given? + return false unless running? + @count.increment + Thread.new(*args) do + Thread.current.abort_on_exception = false + begin + yield(*args) + ensure + @count.decrement + @stopped.set if @running.false? && @count.value == 0 + end + end + end + + # @!macro executor_service_method_left_shift + def <<(task) + post(&task) + self + end + + # @!macro executor_service_method_running_question + def running? + @running.true? + end + + # @!macro executor_service_method_shuttingdown_question + def shuttingdown? + @running.false? && ! @stopped.set? + end + + # @!macro executor_service_method_shutdown_question + def shutdown? + @stopped.set? + end + + # @!macro executor_service_method_shutdown + def shutdown + @running.make_false + @stopped.set if @count.value == 0 + true + end + + # @!macro executor_service_method_kill + def kill + @running.make_false + @stopped.set + true + end + + # @!macro executor_service_method_wait_for_termination + def wait_for_termination(timeout = nil) + @stopped.wait(timeout) + end + + private + + def ns_initialize + @running = Concurrent::AtomicBoolean.new(true) + @stopped = Concurrent::Event.new + @count = Concurrent::AtomicFixnum.new(0) + end + end +end diff --git a/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/executor/single_thread_executor.rb b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/executor/single_thread_executor.rb new file mode 100644 index 0000000..797cb18 --- /dev/null +++ b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/executor/single_thread_executor.rb @@ -0,0 +1,56 @@ +require 'concurrent/executor/ruby_single_thread_executor' + +module Concurrent + + if Concurrent.on_jruby? + require 'concurrent/executor/java_single_thread_executor' + end + + SingleThreadExecutorImplementation = case + when Concurrent.on_jruby? + JavaSingleThreadExecutor + else + RubySingleThreadExecutor + end + private_constant :SingleThreadExecutorImplementation + + # @!macro single_thread_executor + # + # A thread pool with a single thread an unlimited queue. Should the thread + # die for any reason it will be removed and replaced, thus ensuring that + # the executor will always remain viable and available to process jobs. + # + # A common pattern for background processing is to create a single thread + # on which an infinite loop is run. The thread's loop blocks on an input + # source (perhaps blocking I/O or a queue) and processes each input as it + # is received. This pattern has several issues. The thread itself is highly + # susceptible to errors during processing. Also, the thread itself must be + # constantly monitored and restarted should it die. `SingleThreadExecutor` + # encapsulates all these bahaviors. The task processor is highly resilient + # to errors from within tasks. Also, should the thread die it will + # automatically be restarted. + # + # The API and behavior of this class are based on Java's `SingleThreadExecutor`. + # + # @!macro abstract_executor_service_public_api + class SingleThreadExecutor < SingleThreadExecutorImplementation + + # @!macro single_thread_executor_method_initialize + # + # Create a new thread pool. + # + # @option opts [Symbol] :fallback_policy (:discard) the policy for handling new + # tasks that are received when the queue size has reached + # `max_queue` or the executor has shut down + # + # @raise [ArgumentError] if `:fallback_policy` is not one of the values specified + # in `FALLBACK_POLICIES` + # + # @see http://docs.oracle.com/javase/tutorial/essential/concurrency/pools.html + # @see http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/Executors.html + # @see http://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ExecutorService.html + + # @!method initialize(opts = {}) + # @!macro single_thread_executor_method_initialize + end +end diff --git a/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/executor/thread_pool_executor.rb b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/executor/thread_pool_executor.rb new file mode 100644 index 0000000..72e1bae --- /dev/null +++ b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/executor/thread_pool_executor.rb @@ -0,0 +1,87 @@ +require 'concurrent/utility/engine' +require 'concurrent/executor/ruby_thread_pool_executor' + +module Concurrent + + if Concurrent.on_jruby? + require 'concurrent/executor/java_thread_pool_executor' + end + + ThreadPoolExecutorImplementation = case + when Concurrent.on_jruby? + JavaThreadPoolExecutor + else + RubyThreadPoolExecutor + end + private_constant :ThreadPoolExecutorImplementation + + # @!macro thread_pool_executor + # + # An abstraction composed of one or more threads and a task queue. Tasks + # (blocks or `proc` objects) are submitted to the pool and added to the queue. + # The threads in the pool remove the tasks and execute them in the order + # they were received. + # + # A `ThreadPoolExecutor` will automatically adjust the pool size according + # to the bounds set by `min-threads` and `max-threads`. When a new task is + # submitted and fewer than `min-threads` threads are running, a new thread + # is created to handle the request, even if other worker threads are idle. + # If there are more than `min-threads` but less than `max-threads` threads + # running, a new thread will be created only if the queue is full. + # + # Threads that are idle for too long will be garbage collected, down to the + # configured minimum options. Should a thread crash it, too, will be garbage collected. + # + # `ThreadPoolExecutor` is based on the Java class of the same name. From + # the official Java documentation; + # + # > Thread pools address two different problems: they usually provide + # > improved performance when executing large numbers of asynchronous tasks, + # > due to reduced per-task invocation overhead, and they provide a means + # > of bounding and managing the resources, including threads, consumed + # > when executing a collection of tasks. Each ThreadPoolExecutor also + # > maintains some basic statistics, such as the number of completed tasks. + # > + # > To be useful across a wide range of contexts, this class provides many + # > adjustable parameters and extensibility hooks. However, programmers are + # > urged to use the more convenient Executors factory methods + # > [CachedThreadPool] (unbounded thread pool, with automatic thread reclamation), + # > [FixedThreadPool] (fixed size thread pool) and [SingleThreadExecutor] (single + # > background thread), that preconfigure settings for the most common usage + # > scenarios. + # + # @!macro thread_pool_options + # + # @!macro thread_pool_executor_public_api + class ThreadPoolExecutor < ThreadPoolExecutorImplementation + + # @!macro thread_pool_executor_method_initialize + # + # Create a new thread pool. + # + # @param [Hash] opts the options which configure the thread pool. + # + # @option opts [Integer] :max_threads (DEFAULT_MAX_POOL_SIZE) the maximum + # number of threads to be created + # @option opts [Integer] :min_threads (DEFAULT_MIN_POOL_SIZE) When a new task is submitted + # and fewer than `min_threads` are running, a new thread is created + # @option opts [Integer] :idletime (DEFAULT_THREAD_IDLETIMEOUT) the maximum + # number of seconds a thread may be idle before being reclaimed + # @option opts [Integer] :max_queue (DEFAULT_MAX_QUEUE_SIZE) the maximum + # number of tasks allowed in the work queue at any one time; a value of + # zero means the queue may grow without bound + # @option opts [Symbol] :fallback_policy (:abort) the policy for handling new + # tasks that are received when the queue size has reached + # `max_queue` or the executor has shut down + # + # @raise [ArgumentError] if `:max_threads` is less than one + # @raise [ArgumentError] if `:min_threads` is less than zero + # @raise [ArgumentError] if `:fallback_policy` is not one of the values specified + # in `FALLBACK_POLICIES` + # + # @see http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/ThreadPoolExecutor.html + + # @!method initialize(opts = {}) + # @!macro thread_pool_executor_method_initialize + end +end diff --git a/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/executor/timer_set.rb b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/executor/timer_set.rb new file mode 100644 index 0000000..3649101 --- /dev/null +++ b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/executor/timer_set.rb @@ -0,0 +1,173 @@ +require 'concurrent/scheduled_task' +require 'concurrent/atomic/event' +require 'concurrent/collection/non_concurrent_priority_queue' +require 'concurrent/executor/executor_service' +require 'concurrent/executor/single_thread_executor' + +require 'concurrent/options' + +module Concurrent + + # Executes a collection of tasks, each after a given delay. A master task + # monitors the set and schedules each task for execution at the appropriate + # time. Tasks are run on the global thread pool or on the supplied executor. + # Each task is represented as a `ScheduledTask`. + # + # @see Concurrent::ScheduledTask + # + # @!macro monotonic_clock_warning + class TimerSet < RubyExecutorService + + # Create a new set of timed tasks. + # + # @!macro executor_options + # + # @param [Hash] opts the options used to specify the executor on which to perform actions + # @option opts [Executor] :executor when set use the given `Executor` instance. + # Three special values are also supported: `:task` returns the global task pool, + # `:operation` returns the global operation pool, and `:immediate` returns a new + # `ImmediateExecutor` object. + def initialize(opts = {}) + super(opts) + end + + # Post a task to be execute run after a given delay (in seconds). If the + # delay is less than 1/100th of a second the task will be immediately post + # to the executor. + # + # @param [Float] delay the number of seconds to wait for before executing the task. + # @param [Array] args the arguments passed to the task on execution. + # + # @yield the task to be performed. + # + # @return [Concurrent::ScheduledTask, false] IVar representing the task if the post + # is successful; false after shutdown. + # + # @raise [ArgumentError] if the intended execution time is not in the future. + # @raise [ArgumentError] if no block is given. + def post(delay, *args, &task) + raise ArgumentError.new('no block given') unless block_given? + return false unless running? + opts = { executor: @task_executor, + args: args, + timer_set: self } + task = ScheduledTask.execute(delay, opts, &task) # may raise exception + task.unscheduled? ? false : task + end + + # Begin an immediate shutdown. In-progress tasks will be allowed to + # complete but enqueued tasks will be dismissed and no new tasks + # will be accepted. Has no additional effect if the thread pool is + # not running. + def kill + shutdown + end + + private :<< + + private + + # Initialize the object. + # + # @param [Hash] opts the options to create the object with. + # @!visibility private + def ns_initialize(opts) + @queue = Collection::NonConcurrentPriorityQueue.new(order: :min) + @task_executor = Options.executor_from_options(opts) || Concurrent.global_io_executor + @timer_executor = SingleThreadExecutor.new + @condition = Event.new + @ruby_pid = $$ # detects if Ruby has forked + self.auto_terminate = opts.fetch(:auto_terminate, true) + end + + # Post the task to the internal queue. + # + # @note This is intended as a callback method from ScheduledTask + # only. It is not intended to be used directly. Post a task + # by using the `SchedulesTask#execute` method. + # + # @!visibility private + def post_task(task) + synchronize { ns_post_task(task) } + end + + # @!visibility private + def ns_post_task(task) + return false unless ns_running? + ns_reset_if_forked + if (task.initial_delay) <= 0.01 + task.executor.post { task.process_task } + else + @queue.push(task) + # only post the process method when the queue is empty + @timer_executor.post(&method(:process_tasks)) if @queue.length == 1 + @condition.set + end + true + end + + # Remove the given task from the queue. + # + # @note This is intended as a callback method from `ScheduledTask` + # only. It is not intended to be used directly. Cancel a task + # by using the `ScheduledTask#cancel` method. + # + # @!visibility private + def remove_task(task) + synchronize { @queue.delete(task) } + end + + # `ExecutorService` callback called during shutdown. + # + # @!visibility private + def ns_shutdown_execution + ns_reset_if_forked + @queue.clear + @timer_executor.kill + stopped_event.set + end + + def ns_reset_if_forked + if $$ != @ruby_pid + @queue.clear + @condition.reset + @ruby_pid = $$ + end + end + + # Run a loop and execute tasks in the scheduled order and at the approximate + # scheduled time. If no tasks remain the thread will exit gracefully so that + # garbage collection can occur. If there are no ready tasks it will sleep + # for up to 60 seconds waiting for the next scheduled task. + # + # @!visibility private + def process_tasks + loop do + task = synchronize { @condition.reset; @queue.peek } + break unless task + + now = Concurrent.monotonic_time + diff = task.schedule_time - now + + if diff <= 0 + # We need to remove the task from the queue before passing + # it to the executor, to avoid race conditions where we pass + # the peek'ed task to the executor and then pop a different + # one that's been added in the meantime. + # + # Note that there's no race condition between the peek and + # this pop - this pop could retrieve a different task from + # the peek, but that task would be due to fire now anyway + # (because @queue is a priority queue, and this thread is + # the only reader, so whatever timer is at the head of the + # queue now must have the same pop time, or a closer one, as + # when we peeked). + task = synchronize { @queue.pop } + task.executor.post { task.process_task } + else + @condition.wait([diff, 60].min) + end + end + end + end +end diff --git a/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/executors.rb b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/executors.rb new file mode 100644 index 0000000..eb1972c --- /dev/null +++ b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/executors.rb @@ -0,0 +1,20 @@ +require 'concurrent/executor/abstract_executor_service' +require 'concurrent/executor/cached_thread_pool' +require 'concurrent/executor/executor_service' +require 'concurrent/executor/fixed_thread_pool' +require 'concurrent/executor/immediate_executor' +require 'concurrent/executor/indirect_immediate_executor' +require 'concurrent/executor/java_executor_service' +require 'concurrent/executor/java_single_thread_executor' +require 'concurrent/executor/java_thread_pool_executor' +require 'concurrent/executor/ruby_executor_service' +require 'concurrent/executor/ruby_single_thread_executor' +require 'concurrent/executor/ruby_thread_pool_executor' +require 'concurrent/executor/cached_thread_pool' +require 'concurrent/executor/safe_task_executor' +require 'concurrent/executor/serial_executor_service' +require 'concurrent/executor/serialized_execution' +require 'concurrent/executor/serialized_execution_delegator' +require 'concurrent/executor/single_thread_executor' +require 'concurrent/executor/thread_pool_executor' +require 'concurrent/executor/timer_set' diff --git a/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/future.rb b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/future.rb new file mode 100644 index 0000000..1af182e --- /dev/null +++ b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/future.rb @@ -0,0 +1,141 @@ +require 'thread' +require 'concurrent/constants' +require 'concurrent/errors' +require 'concurrent/ivar' +require 'concurrent/executor/safe_task_executor' + +require 'concurrent/options' + +# TODO (pitr-ch 14-Mar-2017): deprecate, Future, Promise, etc. + + +module Concurrent + + # {include:file:docs-source/future.md} + # + # @!macro copy_options + # + # @see http://ruby-doc.org/stdlib-2.1.1/libdoc/observer/rdoc/Observable.html Ruby Observable module + # @see http://clojuredocs.org/clojure_core/clojure.core/future Clojure's future function + # @see http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/Future.html java.util.concurrent.Future + class Future < IVar + + # Create a new `Future` in the `:unscheduled` state. + # + # @yield the asynchronous operation to perform + # + # @!macro executor_and_deref_options + # + # @option opts [object, Array] :args zero or more arguments to be passed the task + # block on execution + # + # @raise [ArgumentError] if no block is given + def initialize(opts = {}, &block) + raise ArgumentError.new('no block given') unless block_given? + super(NULL, opts.merge(__task_from_block__: block), &nil) + end + + # Execute an `:unscheduled` `Future`. Immediately sets the state to `:pending` and + # passes the block to a new thread/thread pool for eventual execution. + # Does nothing if the `Future` is in any state other than `:unscheduled`. + # + # @return [Future] a reference to `self` + # + # @example Instance and execute in separate steps + # future = Concurrent::Future.new{ sleep(1); 42 } + # future.state #=> :unscheduled + # future.execute + # future.state #=> :pending + # + # @example Instance and execute in one line + # future = Concurrent::Future.new{ sleep(1); 42 }.execute + # future.state #=> :pending + def execute + if compare_and_set_state(:pending, :unscheduled) + @executor.post{ safe_execute(@task, @args) } + self + end + end + + # Create a new `Future` object with the given block, execute it, and return the + # `:pending` object. + # + # @yield the asynchronous operation to perform + # + # @!macro executor_and_deref_options + # + # @option opts [object, Array] :args zero or more arguments to be passed the task + # block on execution + # + # @raise [ArgumentError] if no block is given + # + # @return [Future] the newly created `Future` in the `:pending` state + # + # @example + # future = Concurrent::Future.execute{ sleep(1); 42 } + # future.state #=> :pending + def self.execute(opts = {}, &block) + Future.new(opts, &block).execute + end + + # @!macro ivar_set_method + def set(value = NULL, &block) + check_for_block_or_value!(block_given?, value) + synchronize do + if @state != :unscheduled + raise MultipleAssignmentError + else + @task = block || Proc.new { value } + end + end + execute + end + + # Attempt to cancel the operation if it has not already processed. + # The operation can only be cancelled while still `pending`. It cannot + # be cancelled once it has begun processing or has completed. + # + # @return [Boolean] was the operation successfully cancelled. + def cancel + if compare_and_set_state(:cancelled, :pending) + complete(false, nil, CancelledOperationError.new) + true + else + false + end + end + + # Has the operation been successfully cancelled? + # + # @return [Boolean] + def cancelled? + state == :cancelled + end + + # Wait the given number of seconds for the operation to complete. + # On timeout attempt to cancel the operation. + # + # @param [Numeric] timeout the maximum time in seconds to wait. + # @return [Boolean] true if the operation completed before the timeout + # else false + def wait_or_cancel(timeout) + wait(timeout) + if complete? + true + else + cancel + false + end + end + + protected + + def ns_initialize(value, opts) + super + @state = :unscheduled + @task = opts[:__task_from_block__] + @executor = Options.executor_from_options(opts) || Concurrent.global_io_executor + @args = get_arguments_from(opts) + end + end +end diff --git a/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/hash.rb b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/hash.rb new file mode 100644 index 0000000..9816ca3 --- /dev/null +++ b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/hash.rb @@ -0,0 +1,59 @@ +require 'concurrent/utility/engine' +require 'concurrent/thread_safe/util' + +module Concurrent + + # @!macro concurrent_hash + # + # A thread-safe subclass of Hash. This version locks against the object + # itself for every method call, ensuring only one thread can be reading + # or writing at a time. This includes iteration methods like `#each`, + # which takes the lock repeatedly when reading an item. + # + # @see http://ruby-doc.org/core-2.2.0/Hash.html Ruby standard library `Hash` + + # @!macro internal_implementation_note + HashImplementation = case + when Concurrent.on_cruby? + # Hash is thread-safe in practice because CRuby runs + # threads one at a time and does not do context + # switching during the execution of C functions. + ::Hash + + when Concurrent.on_jruby? + require 'jruby/synchronized' + + class JRubyHash < ::Hash + include JRuby::Synchronized + end + JRubyHash + + when Concurrent.on_rbx? + require 'monitor' + require 'concurrent/thread_safe/util/data_structures' + + class RbxHash < ::Hash + end + ThreadSafe::Util.make_synchronized_on_rbx RbxHash + RbxHash + + when Concurrent.on_truffleruby? + require 'concurrent/thread_safe/util/data_structures' + + class TruffleRubyHash < ::Hash + end + + ThreadSafe::Util.make_synchronized_on_truffleruby TruffleRubyHash + TruffleRubyHash + + else + warn 'Possibly unsupported Ruby implementation' + ::Hash + end + private_constant :HashImplementation + + # @!macro concurrent_hash + class Hash < HashImplementation + end + +end diff --git a/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/immutable_struct.rb b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/immutable_struct.rb new file mode 100644 index 0000000..05b8035 --- /dev/null +++ b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/immutable_struct.rb @@ -0,0 +1,93 @@ +require 'concurrent/synchronization/abstract_struct' +require 'concurrent/synchronization' + +module Concurrent + + # A thread-safe, immutable variation of Ruby's standard `Struct`. + # + # @see http://ruby-doc.org/core-2.2.0/Struct.html Ruby standard library `Struct` + module ImmutableStruct + include Synchronization::AbstractStruct + + def self.included(base) + base.safe_initialization! + end + + # @!macro struct_values + def values + ns_values + end + + alias_method :to_a, :values + + # @!macro struct_values_at + def values_at(*indexes) + ns_values_at(indexes) + end + + # @!macro struct_inspect + def inspect + ns_inspect + end + + alias_method :to_s, :inspect + + # @!macro struct_merge + def merge(other, &block) + ns_merge(other, &block) + end + + # @!macro struct_to_h + def to_h + ns_to_h + end + + # @!macro struct_get + def [](member) + ns_get(member) + end + + # @!macro struct_equality + def ==(other) + ns_equality(other) + end + + # @!macro struct_each + def each(&block) + return enum_for(:each) unless block_given? + ns_each(&block) + end + + # @!macro struct_each_pair + def each_pair(&block) + return enum_for(:each_pair) unless block_given? + ns_each_pair(&block) + end + + # @!macro struct_select + def select(&block) + return enum_for(:select) unless block_given? + ns_select(&block) + end + + # @!macro struct_new + def self.new(*args, &block) + clazz_name = nil + if args.length == 0 + raise ArgumentError.new('wrong number of arguments (0 for 1+)') + elsif args.length > 0 && args.first.is_a?(String) + clazz_name = args.shift + end + FACTORY.define_struct(clazz_name, args, &block) + end + + FACTORY = Class.new(Synchronization::LockableObject) do + def define_struct(name, members, &block) + synchronize do + Synchronization::AbstractStruct.define_struct_class(ImmutableStruct, Synchronization::Object, name, members, &block) + end + end + end.new + private_constant :FACTORY + end +end diff --git a/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/ivar.rb b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/ivar.rb new file mode 100644 index 0000000..2a724db --- /dev/null +++ b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/ivar.rb @@ -0,0 +1,207 @@ +require 'concurrent/constants' +require 'concurrent/errors' +require 'concurrent/collection/copy_on_write_observer_set' +require 'concurrent/concern/obligation' +require 'concurrent/concern/observable' +require 'concurrent/synchronization' + +module Concurrent + + # An `IVar` is like a future that you can assign. As a future is a value that + # is being computed that you can wait on, an `IVar` is a value that is waiting + # to be assigned, that you can wait on. `IVars` are single assignment and + # deterministic. + # + # Then, express futures as an asynchronous computation that assigns an `IVar`. + # The `IVar` becomes the primitive on which [futures](Future) and + # [dataflow](Dataflow) are built. + # + # An `IVar` is a single-element container that is normally created empty, and + # can only be set once. The I in `IVar` stands for immutable. Reading an + # `IVar` normally blocks until it is set. It is safe to set and read an `IVar` + # from different threads. + # + # If you want to have some parallel task set the value in an `IVar`, you want + # a `Future`. If you want to create a graph of parallel tasks all executed + # when the values they depend on are ready you want `dataflow`. `IVar` is + # generally a low-level primitive. + # + # ## Examples + # + # Create, set and get an `IVar` + # + # ```ruby + # ivar = Concurrent::IVar.new + # ivar.set 14 + # ivar.value #=> 14 + # ivar.set 2 # would now be an error + # ``` + # + # ## See Also + # + # 1. For the theory: Arvind, R. Nikhil, and K. Pingali. + # [I-Structures: Data structures for parallel computing](http://dl.acm.org/citation.cfm?id=69562). + # In Proceedings of Workshop on Graph Reduction, 1986. + # 2. For recent application: + # [DataDrivenFuture in Habanero Java from Rice](http://www.cs.rice.edu/~vs3/hjlib/doc/edu/rice/hj/api/HjDataDrivenFuture.html). + class IVar < Synchronization::LockableObject + include Concern::Obligation + include Concern::Observable + + # Create a new `IVar` in the `:pending` state with the (optional) initial value. + # + # @param [Object] value the initial value + # @param [Hash] opts the options to create a message with + # @option opts [String] :dup_on_deref (false) call `#dup` before returning + # the data + # @option opts [String] :freeze_on_deref (false) call `#freeze` before + # returning the data + # @option opts [String] :copy_on_deref (nil) call the given `Proc` passing + # the internal value and returning the value returned from the proc + def initialize(value = NULL, opts = {}, &block) + if value != NULL && block_given? + raise ArgumentError.new('provide only a value or a block') + end + super(&nil) + synchronize { ns_initialize(value, opts, &block) } + end + + # Add an observer on this object that will receive notification on update. + # + # Upon completion the `IVar` will notify all observers in a thread-safe way. + # The `func` method of the observer will be called with three arguments: the + # `Time` at which the `Future` completed the asynchronous operation, the + # final `value` (or `nil` on rejection), and the final `reason` (or `nil` on + # fulfillment). + # + # @param [Object] observer the object that will be notified of changes + # @param [Symbol] func symbol naming the method to call when this + # `Observable` has changes` + def add_observer(observer = nil, func = :update, &block) + raise ArgumentError.new('cannot provide both an observer and a block') if observer && block + direct_notification = false + + if block + observer = block + func = :call + end + + synchronize do + if event.set? + direct_notification = true + else + observers.add_observer(observer, func) + end + end + + observer.send(func, Time.now, self.value, reason) if direct_notification + observer + end + + # @!macro ivar_set_method + # Set the `IVar` to a value and wake or notify all threads waiting on it. + # + # @!macro ivar_set_parameters_and_exceptions + # @param [Object] value the value to store in the `IVar` + # @yield A block operation to use for setting the value + # @raise [ArgumentError] if both a value and a block are given + # @raise [Concurrent::MultipleAssignmentError] if the `IVar` has already + # been set or otherwise completed + # + # @return [IVar] self + def set(value = NULL) + check_for_block_or_value!(block_given?, value) + raise MultipleAssignmentError unless compare_and_set_state(:processing, :pending) + + begin + value = yield if block_given? + complete_without_notification(true, value, nil) + rescue => ex + complete_without_notification(false, nil, ex) + end + + notify_observers(self.value, reason) + self + end + + # @!macro ivar_fail_method + # Set the `IVar` to failed due to some error and wake or notify all threads waiting on it. + # + # @param [Object] reason for the failure + # @raise [Concurrent::MultipleAssignmentError] if the `IVar` has already + # been set or otherwise completed + # @return [IVar] self + def fail(reason = StandardError.new) + complete(false, nil, reason) + end + + # Attempt to set the `IVar` with the given value or block. Return a + # boolean indicating the success or failure of the set operation. + # + # @!macro ivar_set_parameters_and_exceptions + # + # @return [Boolean] true if the value was set else false + def try_set(value = NULL, &block) + set(value, &block) + true + rescue MultipleAssignmentError + false + end + + protected + + # @!visibility private + def ns_initialize(value, opts) + value = yield if block_given? + init_obligation + self.observers = Collection::CopyOnWriteObserverSet.new + set_deref_options(opts) + + @state = :pending + if value != NULL + ns_complete_without_notification(true, value, nil) + end + end + + # @!visibility private + def safe_execute(task, args = []) + if compare_and_set_state(:processing, :pending) + success, val, reason = SafeTaskExecutor.new(task, rescue_exception: true).execute(*@args) + complete(success, val, reason) + yield(success, val, reason) if block_given? + end + end + + # @!visibility private + def complete(success, value, reason) + complete_without_notification(success, value, reason) + notify_observers(self.value, reason) + self + end + + # @!visibility private + def complete_without_notification(success, value, reason) + synchronize { ns_complete_without_notification(success, value, reason) } + self + end + + # @!visibility private + def notify_observers(value, reason) + observers.notify_and_delete_observers{ [Time.now, value, reason] } + end + + # @!visibility private + def ns_complete_without_notification(success, value, reason) + raise MultipleAssignmentError if [:fulfilled, :rejected].include? @state + set_state(success, value, reason) + event.set + end + + # @!visibility private + def check_for_block_or_value!(block_given, value) # :nodoc: + if (block_given && value != NULL) || (! block_given && value == NULL) + raise ArgumentError.new('must set with either a value or a block') + end + end + end +end diff --git a/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/map.rb b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/map.rb new file mode 100644 index 0000000..5b71447 --- /dev/null +++ b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/map.rb @@ -0,0 +1,337 @@ +require 'thread' +require 'concurrent/constants' +require 'concurrent/synchronization' +require 'concurrent/utility/engine' + +module Concurrent + # @!visibility private + module Collection + + # @!visibility private + MapImplementation = case + when Concurrent.on_jruby? + # noinspection RubyResolve + JRubyMapBackend + when Concurrent.on_cruby? + require 'concurrent/collection/map/mri_map_backend' + MriMapBackend + when Concurrent.on_rbx? || Concurrent.on_truffleruby? + require 'concurrent/collection/map/atomic_reference_map_backend' + AtomicReferenceMapBackend + else + warn 'Concurrent::Map: unsupported Ruby engine, using a fully synchronized Concurrent::Map implementation' + require 'concurrent/collection/map/synchronized_map_backend' + SynchronizedMapBackend + end + end + + # `Concurrent::Map` is a hash-like object and should have much better performance + # characteristics, especially under high concurrency, than `Concurrent::Hash`. + # However, `Concurrent::Map `is not strictly semantically equivalent to a ruby `Hash` + # -- for instance, it does not necessarily retain ordering by insertion time as `Hash` + # does. For most uses it should do fine though, and we recommend you consider + # `Concurrent::Map` instead of `Concurrent::Hash` for your concurrency-safe hash needs. + class Map < Collection::MapImplementation + + # @!macro map.atomic_method + # This method is atomic. + + # @!macro map.atomic_method_with_block + # This method is atomic. + # @note Atomic methods taking a block do not allow the `self` instance + # to be used within the block. Doing so will cause a deadlock. + + # @!method compute_if_absent(key) + # Compute and store new value for key if the key is absent. + # @param [Object] key + # @yield new value + # @yieldreturn [Object] new value + # @return [Object] new value or current value + # @!macro map.atomic_method_with_block + + # @!method compute_if_present(key) + # Compute and store new value for key if the key is present. + # @param [Object] key + # @yield new value + # @yieldparam old_value [Object] + # @yieldreturn [Object, nil] new value, when nil the key is removed + # @return [Object, nil] new value or nil + # @!macro map.atomic_method_with_block + + # @!method compute(key) + # Compute and store new value for key. + # @param [Object] key + # @yield compute new value from old one + # @yieldparam old_value [Object, nil] old_value, or nil when key is absent + # @yieldreturn [Object, nil] new value, when nil the key is removed + # @return [Object, nil] new value or nil + # @!macro map.atomic_method_with_block + + # @!method merge_pair(key, value) + # If the key is absent, the value is stored, otherwise new value is + # computed with a block. + # @param [Object] key + # @param [Object] value + # @yield compute new value from old one + # @yieldparam old_value [Object] old value + # @yieldreturn [Object, nil] new value, when nil the key is removed + # @return [Object, nil] new value or nil + # @!macro map.atomic_method_with_block + + # @!method replace_pair(key, old_value, new_value) + # Replaces old_value with new_value if key exists and current value + # matches old_value + # @param [Object] key + # @param [Object] old_value + # @param [Object] new_value + # @return [true, false] true if replaced + # @!macro map.atomic_method + + # @!method replace_if_exists(key, new_value) + # Replaces current value with new_value if key exists + # @param [Object] key + # @param [Object] new_value + # @return [Object, nil] old value or nil + # @!macro map.atomic_method + + # @!method get_and_set(key, value) + # Get the current value under key and set new value. + # @param [Object] key + # @param [Object] value + # @return [Object, nil] old value or nil when the key was absent + # @!macro map.atomic_method + + # @!method delete(key) + # Delete key and its value. + # @param [Object] key + # @return [Object, nil] old value or nil when the key was absent + # @!macro map.atomic_method + + # @!method delete_pair(key, value) + # Delete pair and its value if current value equals the provided value. + # @param [Object] key + # @param [Object] value + # @return [true, false] true if deleted + # @!macro map.atomic_method + + + def initialize(options = nil, &block) + if options.kind_of?(::Hash) + validate_options_hash!(options) + else + options = nil + end + + super(options) + @default_proc = block + end + + # Get a value with key + # @param [Object] key + # @return [Object] the value + def [](key) + if value = super # non-falsy value is an existing mapping, return it right away + value + # re-check is done with get_or_default(key, NULL) instead of a simple !key?(key) in order to avoid a race condition, whereby by the time the current thread gets to the key?(key) call + # a key => value mapping might have already been created by a different thread (key?(key) would then return true, this elsif branch wouldn't be taken and an incorrent +nil+ value + # would be returned) + # note: nil == value check is not technically necessary + elsif @default_proc && nil == value && NULL == (value = get_or_default(key, NULL)) + @default_proc.call(self, key) + else + value + end + end + + alias_method :get, :[] + # TODO (pitr-ch 30-Oct-2018): doc + alias_method :put, :[]= + + # Get a value with key, or default_value when key is absent, + # or fail when no default_value is given. + # @param [Object] key + # @param [Object] default_value + # @yield default value for a key + # @yieldparam key [Object] + # @yieldreturn [Object] default value + # @return [Object] the value or default value + # @raise [KeyError] when key is missing and no default_value is provided + # @!macro map_method_not_atomic + # @note The "fetch-then-act" methods of `Map` are not atomic. `Map` is intended + # to be use as a concurrency primitive with strong happens-before + # guarantees. It is not intended to be used as a high-level abstraction + # supporting complex operations. All read and write operations are + # thread safe, but no guarantees are made regarding race conditions + # between the fetch operation and yielding to the block. Additionally, + # this method does not support recursion. This is due to internal + # constraints that are very unlikely to change in the near future. + def fetch(key, default_value = NULL) + if NULL != (value = get_or_default(key, NULL)) + value + elsif block_given? + yield key + elsif NULL != default_value + default_value + else + raise_fetch_no_key + end + end + + # Fetch value with key, or store default value when key is absent, + # or fail when no default_value is given. This is a two step operation, + # therefore not atomic. The store can overwrite other concurrently + # stored value. + # @param [Object] key + # @param [Object] default_value + # @yield default value for a key + # @yieldparam key [Object] + # @yieldreturn [Object] default value + # @return [Object] the value or default value + # @!macro map.atomic_method_with_block + def fetch_or_store(key, default_value = NULL) + fetch(key) do + put(key, block_given? ? yield(key) : (NULL == default_value ? raise_fetch_no_key : default_value)) + end + end + + # Insert value into map with key if key is absent in one atomic step. + # @param [Object] key + # @param [Object] value + # @return [Object, nil] the value or nil when key was present + def put_if_absent(key, value) + computed = false + result = compute_if_absent(key) do + computed = true + value + end + computed ? nil : result + end unless method_defined?(:put_if_absent) + + # Is the value stored in the map. Iterates over all values. + # @param [Object] value + # @return [true, false] + def value?(value) + each_value do |v| + return true if value.equal?(v) + end + false + end + + # All keys + # @return [::Array] keys + def keys + arr = [] + each_pair { |k, v| arr << k } + arr + end unless method_defined?(:keys) + + # All values + # @return [::Array] values + def values + arr = [] + each_pair { |k, v| arr << v } + arr + end unless method_defined?(:values) + + # Iterates over each key. + # @yield for each key in the map + # @yieldparam key [Object] + # @return [self] + # @!macro map.atomic_method_with_block + def each_key + each_pair { |k, v| yield k } + end unless method_defined?(:each_key) + + # Iterates over each value. + # @yield for each value in the map + # @yieldparam value [Object] + # @return [self] + # @!macro map.atomic_method_with_block + def each_value + each_pair { |k, v| yield v } + end unless method_defined?(:each_value) + + # Iterates over each key value pair. + # @yield for each key value pair in the map + # @yieldparam key [Object] + # @yieldparam value [Object] + # @return [self] + # @!macro map.atomic_method_with_block + def each_pair + return enum_for :each_pair unless block_given? + super + end + + alias_method :each, :each_pair unless method_defined?(:each) + + # Find key of a value. + # @param [Object] value + # @return [Object, nil] key or nil when not found + def key(value) + each_pair { |k, v| return k if v == value } + nil + end unless method_defined?(:key) + alias_method :index, :key if RUBY_VERSION < '1.9' + + # Is map empty? + # @return [true, false] + def empty? + each_pair { |k, v| return false } + true + end unless method_defined?(:empty?) + + # The size of map. + # @return [Integer] size + def size + count = 0 + each_pair { |k, v| count += 1 } + count + end unless method_defined?(:size) + + # @!visibility private + def marshal_dump + raise TypeError, "can't dump hash with default proc" if @default_proc + h = {} + each_pair { |k, v| h[k] = v } + h + end + + # @!visibility private + def marshal_load(hash) + initialize + populate_from(hash) + end + + undef :freeze + + # @!visibility private + def inspect + format '%s entries=%d default_proc=%s>', to_s[0..-2], size.to_s, @default_proc.inspect + end + + private + + def raise_fetch_no_key + raise KeyError, 'key not found' + end + + def initialize_copy(other) + super + populate_from(other) + end + + def populate_from(hash) + hash.each_pair { |k, v| self[k] = v } + self + end + + def validate_options_hash!(options) + if (initial_capacity = options[:initial_capacity]) && (!initial_capacity.kind_of?(Integer) || initial_capacity < 0) + raise ArgumentError, ":initial_capacity must be a positive Integer" + end + if (load_factor = options[:load_factor]) && (!load_factor.kind_of?(Numeric) || load_factor <= 0 || load_factor > 1) + raise ArgumentError, ":load_factor must be a number between 0 and 1" + end + end + end +end diff --git a/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/maybe.rb b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/maybe.rb new file mode 100644 index 0000000..7ba3d3e --- /dev/null +++ b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/maybe.rb @@ -0,0 +1,229 @@ +require 'concurrent/synchronization' + +module Concurrent + + # A `Maybe` encapsulates an optional value. A `Maybe` either contains a value + # of (represented as `Just`), or it is empty (represented as `Nothing`). Using + # `Maybe` is a good way to deal with errors or exceptional cases without + # resorting to drastic measures such as exceptions. + # + # `Maybe` is a replacement for the use of `nil` with better type checking. + # + # For compatibility with {Concurrent::Concern::Obligation} the predicate and + # accessor methods are aliased as `fulfilled?`, `rejected?`, `value`, and + # `reason`. + # + # ## Motivation + # + # A common pattern in languages with pattern matching, such as Erlang and + # Haskell, is to return *either* a value *or* an error from a function + # Consider this Erlang code: + # + # ```erlang + # case file:consult("data.dat") of + # {ok, Terms} -> do_something_useful(Terms); + # {error, Reason} -> lager:error(Reason) + # end. + # ``` + # + # In this example the standard library function `file:consult` returns a + # [tuple](http://erlang.org/doc/reference_manual/data_types.html#id69044) + # with two elements: an [atom](http://erlang.org/doc/reference_manual/data_types.html#id64134) + # (similar to a ruby symbol) and a variable containing ancillary data. On + # success it returns the atom `ok` and the data from the file. On failure it + # returns `error` and a string with an explanation of the problem. With this + # pattern there is no ambiguity regarding success or failure. If the file is + # empty the return value cannot be misinterpreted as an error. And when an + # error occurs the return value provides useful information. + # + # In Ruby we tend to return `nil` when an error occurs or else we raise an + # exception. Both of these idioms are problematic. Returning `nil` is + # ambiguous because `nil` may also be a valid value. It also lacks + # information pertaining to the nature of the error. Raising an exception + # is both expensive and usurps the normal flow of control. All of these + # problems can be solved with the use of a `Maybe`. + # + # A `Maybe` is unambiguous with regard to whether or not it contains a value. + # When `Just` it contains a value, when `Nothing` it does not. When `Just` + # the value it contains may be `nil`, which is perfectly valid. When + # `Nothing` the reason for the lack of a value is contained as well. The + # previous Erlang example can be duplicated in Ruby in a principled way by + # having functions return `Maybe` objects: + # + # ```ruby + # result = MyFileUtils.consult("data.dat") # returns a Maybe + # if result.just? + # do_something_useful(result.value) # or result.just + # else + # logger.error(result.reason) # or result.nothing + # end + # ``` + # + # @example Returning a Maybe from a Function + # module MyFileUtils + # def self.consult(path) + # file = File.open(path, 'r') + # Concurrent::Maybe.just(file.read) + # rescue => ex + # return Concurrent::Maybe.nothing(ex) + # ensure + # file.close if file + # end + # end + # + # maybe = MyFileUtils.consult('bogus.file') + # maybe.just? #=> false + # maybe.nothing? #=> true + # maybe.reason #=> # + # + # maybe = MyFileUtils.consult('README.md') + # maybe.just? #=> true + # maybe.nothing? #=> false + # maybe.value #=> "# Concurrent Ruby\n[![Gem Version..." + # + # @example Using Maybe with a Block + # result = Concurrent::Maybe.from do + # Client.find(10) # Client is an ActiveRecord model + # end + # + # # -- if the record was found + # result.just? #=> true + # result.value #=> # + # + # # -- if the record was not found + # result.just? #=> false + # result.reason #=> ActiveRecord::RecordNotFound + # + # @example Using Maybe with the Null Object Pattern + # # In a Rails controller... + # result = ClientService.new(10).find # returns a Maybe + # render json: result.or(NullClient.new) + # + # @see https://hackage.haskell.org/package/base-4.2.0.1/docs/Data-Maybe.html Haskell Data.Maybe + # @see https://github.com/purescript/purescript-maybe/blob/master/docs/Data.Maybe.md PureScript Data.Maybe + class Maybe < Synchronization::Object + include Comparable + safe_initialization! + + # Indicates that the given attribute has not been set. + # When `Just` the {#nothing} getter will return `NONE`. + # When `Nothing` the {#just} getter will return `NONE`. + NONE = ::Object.new.freeze + + # The value of a `Maybe` when `Just`. Will be `NONE` when `Nothing`. + attr_reader :just + + # The reason for the `Maybe` when `Nothing`. Will be `NONE` when `Just`. + attr_reader :nothing + + private_class_method :new + + # Create a new `Maybe` using the given block. + # + # Runs the given block passing all function arguments to the block as block + # arguments. If the block runs to completion without raising an exception + # a new `Just` is created with the value set to the return value of the + # block. If the block raises an exception a new `Nothing` is created with + # the reason being set to the raised exception. + # + # @param [Array] args Zero or more arguments to pass to the block. + # @yield The block from which to create a new `Maybe`. + # @yieldparam [Array] args Zero or more block arguments passed as + # arguments to the function. + # + # @return [Maybe] The newly created object. + # + # @raise [ArgumentError] when no block given. + def self.from(*args) + raise ArgumentError.new('no block given') unless block_given? + begin + value = yield(*args) + return new(value, NONE) + rescue => ex + return new(NONE, ex) + end + end + + # Create a new `Just` with the given value. + # + # @param [Object] value The value to set for the new `Maybe` object. + # + # @return [Maybe] The newly created object. + def self.just(value) + return new(value, NONE) + end + + # Create a new `Nothing` with the given (optional) reason. + # + # @param [Exception] error The reason to set for the new `Maybe` object. + # When given a string a new `StandardError` will be created with the + # argument as the message. When no argument is given a new + # `StandardError` with an empty message will be created. + # + # @return [Maybe] The newly created object. + def self.nothing(error = '') + if error.is_a?(Exception) + nothing = error + else + nothing = StandardError.new(error.to_s) + end + return new(NONE, nothing) + end + + # Is this `Maybe` a `Just` (successfully fulfilled with a value)? + # + # @return [Boolean] True if `Just` or false if `Nothing`. + def just? + ! nothing? + end + alias :fulfilled? :just? + + # Is this `Maybe` a `nothing` (rejected with an exception upon fulfillment)? + # + # @return [Boolean] True if `Nothing` or false if `Just`. + def nothing? + @nothing != NONE + end + alias :rejected? :nothing? + + alias :value :just + + alias :reason :nothing + + # Comparison operator. + # + # @return [Integer] 0 if self and other are both `Nothing`; + # -1 if self is `Nothing` and other is `Just`; + # 1 if self is `Just` and other is nothing; + # `self.just <=> other.just` if both self and other are `Just`. + def <=>(other) + if nothing? + other.nothing? ? 0 : -1 + else + other.nothing? ? 1 : just <=> other.just + end + end + + # Return either the value of self or the given default value. + # + # @return [Object] The value of self when `Just`; else the given default. + def or(other) + just? ? just : other + end + + private + + # Create a new `Maybe` with the given attributes. + # + # @param [Object] just The value when `Just` else `NONE`. + # @param [Exception, Object] nothing The exception when `Nothing` else `NONE`. + # + # @return [Maybe] The new `Maybe`. + # + # @!visibility private + def initialize(just, nothing) + @just = just + @nothing = nothing + end + end +end diff --git a/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/mutable_struct.rb b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/mutable_struct.rb new file mode 100644 index 0000000..836b7f4 --- /dev/null +++ b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/mutable_struct.rb @@ -0,0 +1,229 @@ +require 'concurrent/synchronization/abstract_struct' +require 'concurrent/synchronization' + +module Concurrent + + # An thread-safe variation of Ruby's standard `Struct`. Values can be set at + # construction or safely changed at any time during the object's lifecycle. + # + # @see http://ruby-doc.org/core-2.2.0/Struct.html Ruby standard library `Struct` + module MutableStruct + include Synchronization::AbstractStruct + + # @!macro struct_new + # + # Factory for creating new struct classes. + # + # ``` + # new([class_name] [, member_name]+>) -> StructClass click to toggle source + # new([class_name] [, member_name]+>) {|StructClass| block } -> StructClass + # new(value, ...) -> obj + # StructClass[value, ...] -> obj + # ``` + # + # The first two forms are used to create a new struct subclass `class_name` + # that can contain a value for each member_name . This subclass can be + # used to create instances of the structure like any other Class . + # + # If the `class_name` is omitted an anonymous struct class will be created. + # Otherwise, the name of this struct will appear as a constant in the struct class, + # so it must be unique for all structs under this base class and must start with a + # capital letter. Assigning a struct class to a constant also gives the class + # the name of the constant. + # + # If a block is given it will be evaluated in the context of `StructClass`, passing + # the created class as a parameter. This is the recommended way to customize a struct. + # Subclassing an anonymous struct creates an extra anonymous class that will never be used. + # + # The last two forms create a new instance of a struct subclass. The number of value + # parameters must be less than or equal to the number of attributes defined for the + # struct. Unset parameters default to nil. Passing more parameters than number of attributes + # will raise an `ArgumentError`. + # + # @see http://ruby-doc.org/core-2.2.0/Struct.html#method-c-new Ruby standard library `Struct#new` + + # @!macro struct_values + # + # Returns the values for this struct as an Array. + # + # @return [Array] the values for this struct + # + def values + synchronize { ns_values } + end + alias_method :to_a, :values + + # @!macro struct_values_at + # + # Returns the struct member values for each selector as an Array. + # + # A selector may be either an Integer offset or a Range of offsets (as in `Array#values_at`). + # + # @param [Fixnum, Range] indexes the index(es) from which to obatin the values (in order) + def values_at(*indexes) + synchronize { ns_values_at(indexes) } + end + + # @!macro struct_inspect + # + # Describe the contents of this struct in a string. + # + # @return [String] the contents of this struct in a string + def inspect + synchronize { ns_inspect } + end + alias_method :to_s, :inspect + + # @!macro struct_merge + # + # Returns a new struct containing the contents of `other` and the contents + # of `self`. If no block is specified, the value for entries with duplicate + # keys will be that of `other`. Otherwise the value for each duplicate key + # is determined by calling the block with the key, its value in `self` and + # its value in `other`. + # + # @param [Hash] other the hash from which to set the new values + # @yield an options block for resolving duplicate keys + # @yieldparam [String, Symbol] member the name of the member which is duplicated + # @yieldparam [Object] selfvalue the value of the member in `self` + # @yieldparam [Object] othervalue the value of the member in `other` + # + # @return [Synchronization::AbstractStruct] a new struct with the new values + # + # @raise [ArgumentError] of given a member that is not defined in the struct + def merge(other, &block) + synchronize { ns_merge(other, &block) } + end + + # @!macro struct_to_h + # + # Returns a hash containing the names and values for the struct’s members. + # + # @return [Hash] the names and values for the struct’s members + def to_h + synchronize { ns_to_h } + end + + # @!macro struct_get + # + # Attribute Reference + # + # @param [Symbol, String, Integer] member the string or symbol name of the member + # for which to obtain the value or the member's index + # + # @return [Object] the value of the given struct member or the member at the given index. + # + # @raise [NameError] if the member does not exist + # @raise [IndexError] if the index is out of range. + def [](member) + synchronize { ns_get(member) } + end + + # @!macro struct_equality + # + # Equality + # + # @return [Boolean] true if other has the same struct subclass and has + # equal member values (according to `Object#==`) + def ==(other) + synchronize { ns_equality(other) } + end + + # @!macro struct_each + # + # Yields the value of each struct member in order. If no block is given + # an enumerator is returned. + # + # @yield the operation to be performed on each struct member + # @yieldparam [Object] value each struct value (in order) + def each(&block) + return enum_for(:each) unless block_given? + synchronize { ns_each(&block) } + end + + # @!macro struct_each_pair + # + # Yields the name and value of each struct member in order. If no block is + # given an enumerator is returned. + # + # @yield the operation to be performed on each struct member/value pair + # @yieldparam [Object] member each struct member (in order) + # @yieldparam [Object] value each struct value (in order) + def each_pair(&block) + return enum_for(:each_pair) unless block_given? + synchronize { ns_each_pair(&block) } + end + + # @!macro struct_select + # + # Yields each member value from the struct to the block and returns an Array + # containing the member values from the struct for which the given block + # returns a true value (equivalent to `Enumerable#select`). + # + # @yield the operation to be performed on each struct member + # @yieldparam [Object] value each struct value (in order) + # + # @return [Array] an array containing each value for which the block returns true + def select(&block) + return enum_for(:select) unless block_given? + synchronize { ns_select(&block) } + end + + # @!macro struct_set + # + # Attribute Assignment + # + # Sets the value of the given struct member or the member at the given index. + # + # @param [Symbol, String, Integer] member the string or symbol name of the member + # for which to obtain the value or the member's index + # + # @return [Object] the value of the given struct member or the member at the given index. + # + # @raise [NameError] if the name does not exist + # @raise [IndexError] if the index is out of range. + def []=(member, value) + if member.is_a? Integer + length = synchronize { @values.length } + if member >= length + raise IndexError.new("offset #{member} too large for struct(size:#{length})") + end + synchronize { @values[member] = value } + else + send("#{member}=", value) + end + rescue NoMethodError + raise NameError.new("no member '#{member}' in struct") + end + + # @!macro struct_new + def self.new(*args, &block) + clazz_name = nil + if args.length == 0 + raise ArgumentError.new('wrong number of arguments (0 for 1+)') + elsif args.length > 0 && args.first.is_a?(String) + clazz_name = args.shift + end + FACTORY.define_struct(clazz_name, args, &block) + end + + FACTORY = Class.new(Synchronization::LockableObject) do + def define_struct(name, members, &block) + synchronize do + clazz = Synchronization::AbstractStruct.define_struct_class(MutableStruct, Synchronization::LockableObject, name, members, &block) + members.each_with_index do |member, index| + clazz.send :remove_method, member + clazz.send(:define_method, member) do + synchronize { @values[index] } + end + clazz.send(:define_method, "#{member}=") do |value| + synchronize { @values[index] = value } + end + end + clazz + end + end + end.new + private_constant :FACTORY + end +end diff --git a/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/mvar.rb b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/mvar.rb new file mode 100644 index 0000000..9034711 --- /dev/null +++ b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/mvar.rb @@ -0,0 +1,242 @@ +require 'concurrent/concern/dereferenceable' +require 'concurrent/synchronization' + +module Concurrent + + # An `MVar` is a synchronized single element container. They are empty or + # contain one item. Taking a value from an empty `MVar` blocks, as does + # putting a value into a full one. You can either think of them as blocking + # queue of length one, or a special kind of mutable variable. + # + # On top of the fundamental `#put` and `#take` operations, we also provide a + # `#mutate` that is atomic with respect to operations on the same instance. + # These operations all support timeouts. + # + # We also support non-blocking operations `#try_put!` and `#try_take!`, a + # `#set!` that ignores existing values, a `#value` that returns the value + # without removing it or returns `MVar::EMPTY`, and a `#modify!` that yields + # `MVar::EMPTY` if the `MVar` is empty and can be used to set `MVar::EMPTY`. + # You shouldn't use these operations in the first instance. + # + # `MVar` is a [Dereferenceable](Dereferenceable). + # + # `MVar` is related to M-structures in Id, `MVar` in Haskell and `SyncVar` in Scala. + # + # Note that unlike the original Haskell paper, our `#take` is blocking. This is how + # Haskell and Scala do it today. + # + # @!macro copy_options + # + # ## See Also + # + # 1. P. Barth, R. Nikhil, and Arvind. [M-Structures: Extending a parallel, non- strict, functional language with state](http://dl.acm.org/citation.cfm?id=652538). In Proceedings of the 5th + # ACM Conference on Functional Programming Languages and Computer Architecture (FPCA), 1991. + # + # 2. S. Peyton Jones, A. Gordon, and S. Finne. [Concurrent Haskell](http://dl.acm.org/citation.cfm?id=237794). + # In Proceedings of the 23rd Symposium on Principles of Programming Languages + # (PoPL), 1996. + class MVar < Synchronization::Object + include Concern::Dereferenceable + safe_initialization! + + # Unique value that represents that an `MVar` was empty + EMPTY = ::Object.new + + # Unique value that represents that an `MVar` timed out before it was able + # to produce a value. + TIMEOUT = ::Object.new + + # Create a new `MVar`, either empty or with an initial value. + # + # @param [Hash] opts the options controlling how the future will be processed + # + # @!macro deref_options + def initialize(value = EMPTY, opts = {}) + @value = value + @mutex = Mutex.new + @empty_condition = ConditionVariable.new + @full_condition = ConditionVariable.new + set_deref_options(opts) + end + + # Remove the value from an `MVar`, leaving it empty, and blocking if there + # isn't a value. A timeout can be set to limit the time spent blocked, in + # which case it returns `TIMEOUT` if the time is exceeded. + # @return [Object] the value that was taken, or `TIMEOUT` + def take(timeout = nil) + @mutex.synchronize do + wait_for_full(timeout) + + # If we timed out we'll still be empty + if unlocked_full? + value = @value + @value = EMPTY + @empty_condition.signal + apply_deref_options(value) + else + TIMEOUT + end + end + end + + # acquires lock on the from an `MVAR`, yields the value to provided block, + # and release lock. A timeout can be set to limit the time spent blocked, + # in which case it returns `TIMEOUT` if the time is exceeded. + # @return [Object] the value returned by the block, or `TIMEOUT` + def borrow(timeout = nil) + @mutex.synchronize do + wait_for_full(timeout) + + # if we timeoud out we'll still be empty + if unlocked_full? + yield @value + else + TIMEOUT + end + end + end + + # Put a value into an `MVar`, blocking if there is already a value until + # it is empty. A timeout can be set to limit the time spent blocked, in + # which case it returns `TIMEOUT` if the time is exceeded. + # @return [Object] the value that was put, or `TIMEOUT` + def put(value, timeout = nil) + @mutex.synchronize do + wait_for_empty(timeout) + + # If we timed out we won't be empty + if unlocked_empty? + @value = value + @full_condition.signal + apply_deref_options(value) + else + TIMEOUT + end + end + end + + # Atomically `take`, yield the value to a block for transformation, and then + # `put` the transformed value. Returns the transformed value. A timeout can + # be set to limit the time spent blocked, in which case it returns `TIMEOUT` + # if the time is exceeded. + # @return [Object] the transformed value, or `TIMEOUT` + def modify(timeout = nil) + raise ArgumentError.new('no block given') unless block_given? + + @mutex.synchronize do + wait_for_full(timeout) + + # If we timed out we'll still be empty + if unlocked_full? + value = @value + @value = yield value + @full_condition.signal + apply_deref_options(value) + else + TIMEOUT + end + end + end + + # Non-blocking version of `take`, that returns `EMPTY` instead of blocking. + def try_take! + @mutex.synchronize do + if unlocked_full? + value = @value + @value = EMPTY + @empty_condition.signal + apply_deref_options(value) + else + EMPTY + end + end + end + + # Non-blocking version of `put`, that returns whether or not it was successful. + def try_put!(value) + @mutex.synchronize do + if unlocked_empty? + @value = value + @full_condition.signal + true + else + false + end + end + end + + # Non-blocking version of `put` that will overwrite an existing value. + def set!(value) + @mutex.synchronize do + old_value = @value + @value = value + @full_condition.signal + apply_deref_options(old_value) + end + end + + # Non-blocking version of `modify` that will yield with `EMPTY` if there is no value yet. + def modify! + raise ArgumentError.new('no block given') unless block_given? + + @mutex.synchronize do + value = @value + @value = yield value + if unlocked_empty? + @empty_condition.signal + else + @full_condition.signal + end + apply_deref_options(value) + end + end + + # Returns if the `MVar` is currently empty. + def empty? + @mutex.synchronize { @value == EMPTY } + end + + # Returns if the `MVar` currently contains a value. + def full? + !empty? + end + + protected + + def synchronize(&block) + @mutex.synchronize(&block) + end + + private + + def unlocked_empty? + @value == EMPTY + end + + def unlocked_full? + ! unlocked_empty? + end + + def wait_for_full(timeout) + wait_while(@full_condition, timeout) { unlocked_empty? } + end + + def wait_for_empty(timeout) + wait_while(@empty_condition, timeout) { unlocked_full? } + end + + def wait_while(condition, timeout) + if timeout.nil? + while yield + condition.wait(@mutex) + end + else + stop = Concurrent.monotonic_time + timeout + while yield && timeout > 0.0 + condition.wait(@mutex, timeout) + timeout = stop - Concurrent.monotonic_time + end + end + end + end +end diff --git a/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/options.rb b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/options.rb new file mode 100644 index 0000000..bdd22a9 --- /dev/null +++ b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/options.rb @@ -0,0 +1,42 @@ +require 'concurrent/configuration' + +module Concurrent + + # @!visibility private + module Options + + # Get the requested `Executor` based on the values set in the options hash. + # + # @param [Hash] opts the options defining the requested executor + # @option opts [Executor] :executor when set use the given `Executor` instance. + # Three special values are also supported: `:fast` returns the global fast executor, + # `:io` returns the global io executor, and `:immediate` returns a new + # `ImmediateExecutor` object. + # + # @return [Executor, nil] the requested thread pool, or nil when no option specified + # + # @!visibility private + def self.executor_from_options(opts = {}) # :nodoc: + if identifier = opts.fetch(:executor, nil) + executor(identifier) + else + nil + end + end + + def self.executor(executor_identifier) + case executor_identifier + when :fast + Concurrent.global_fast_executor + when :io + Concurrent.global_io_executor + when :immediate + Concurrent.global_immediate_executor + when Concurrent::ExecutorService + executor_identifier + else + raise ArgumentError, "executor not recognized by '#{executor_identifier}'" + end + end + end +end diff --git a/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/promise.rb b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/promise.rb new file mode 100644 index 0000000..f5f31eb --- /dev/null +++ b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/promise.rb @@ -0,0 +1,579 @@ +require 'thread' +require 'concurrent/constants' +require 'concurrent/errors' +require 'concurrent/ivar' +require 'concurrent/executor/safe_task_executor' + +require 'concurrent/options' + +module Concurrent + + PromiseExecutionError = Class.new(StandardError) + + # Promises are inspired by the JavaScript [Promises/A](http://wiki.commonjs.org/wiki/Promises/A) + # and [Promises/A+](http://promises-aplus.github.io/promises-spec/) specifications. + # + # > A promise represents the eventual value returned from the single + # > completion of an operation. + # + # Promises are similar to futures and share many of the same behaviours. + # Promises are far more robust, however. Promises can be chained in a tree + # structure where each promise may have zero or more children. Promises are + # chained using the `then` method. The result of a call to `then` is always + # another promise. Promises are resolved asynchronously (with respect to the + # main thread) but in a strict order: parents are guaranteed to be resolved + # before their children, children before their younger siblings. The `then` + # method takes two parameters: an optional block to be executed upon parent + # resolution and an optional callable to be executed upon parent failure. The + # result of each promise is passed to each of its children upon resolution. + # When a promise is rejected all its children will be summarily rejected and + # will receive the reason. + # + # Promises have several possible states: *:unscheduled*, *:pending*, + # *:processing*, *:rejected*, or *:fulfilled*. These are also aggregated as + # `#incomplete?` and `#complete?`. When a Promise is created it is set to + # *:unscheduled*. Once the `#execute` method is called the state becomes + # *:pending*. Once a job is pulled from the thread pool's queue and is given + # to a thread for processing (often immediately upon `#post`) the state + # becomes *:processing*. The future will remain in this state until processing + # is complete. A future that is in the *:unscheduled*, *:pending*, or + # *:processing* is considered `#incomplete?`. A `#complete?` Promise is either + # *:rejected*, indicating that an exception was thrown during processing, or + # *:fulfilled*, indicating success. If a Promise is *:fulfilled* its `#value` + # will be updated to reflect the result of the operation. If *:rejected* the + # `reason` will be updated with a reference to the thrown exception. The + # predicate methods `#unscheduled?`, `#pending?`, `#rejected?`, and + # `#fulfilled?` can be called at any time to obtain the state of the Promise, + # as can the `#state` method, which returns a symbol. + # + # Retrieving the value of a promise is done through the `value` (alias: + # `deref`) method. Obtaining the value of a promise is a potentially blocking + # operation. When a promise is *rejected* a call to `value` will return `nil` + # immediately. When a promise is *fulfilled* a call to `value` will + # immediately return the current value. When a promise is *pending* a call to + # `value` will block until the promise is either *rejected* or *fulfilled*. A + # *timeout* value can be passed to `value` to limit how long the call will + # block. If `nil` the call will block indefinitely. If `0` the call will not + # block. Any other integer or float value will indicate the maximum number of + # seconds to block. + # + # Promises run on the global thread pool. + # + # @!macro copy_options + # + # ### Examples + # + # Start by requiring promises + # + # ```ruby + # require 'concurrent' + # ``` + # + # Then create one + # + # ```ruby + # p = Concurrent::Promise.execute do + # # do something + # 42 + # end + # ``` + # + # Promises can be chained using the `then` method. The `then` method accepts a + # block and an executor, to be executed on fulfillment, and a callable argument to be executed + # on rejection. The result of the each promise is passed as the block argument + # to chained promises. + # + # ```ruby + # p = Concurrent::Promise.new{10}.then{|x| x * 2}.then{|result| result - 10 }.execute + # ``` + # + # And so on, and so on, and so on... + # + # ```ruby + # p = Concurrent::Promise.fulfill(20). + # then{|result| result - 10 }. + # then{|result| result * 3 }. + # then(executor: different_executor){|result| result % 5 }.execute + # ``` + # + # The initial state of a newly created Promise depends on the state of its parent: + # - if parent is *unscheduled* the child will be *unscheduled* + # - if parent is *pending* the child will be *pending* + # - if parent is *fulfilled* the child will be *pending* + # - if parent is *rejected* the child will be *pending* (but will ultimately be *rejected*) + # + # Promises are executed asynchronously from the main thread. By the time a + # child Promise finishes intialization it may be in a different state than its + # parent (by the time a child is created its parent may have completed + # execution and changed state). Despite being asynchronous, however, the order + # of execution of Promise objects in a chain (or tree) is strictly defined. + # + # There are multiple ways to create and execute a new `Promise`. Both ways + # provide identical behavior: + # + # ```ruby + # # create, operate, then execute + # p1 = Concurrent::Promise.new{ "Hello World!" } + # p1.state #=> :unscheduled + # p1.execute + # + # # create and immediately execute + # p2 = Concurrent::Promise.new{ "Hello World!" }.execute + # + # # execute during creation + # p3 = Concurrent::Promise.execute{ "Hello World!" } + # ``` + # + # Once the `execute` method is called a `Promise` becomes `pending`: + # + # ```ruby + # p = Concurrent::Promise.execute{ "Hello, world!" } + # p.state #=> :pending + # p.pending? #=> true + # ``` + # + # Wait a little bit, and the promise will resolve and provide a value: + # + # ```ruby + # p = Concurrent::Promise.execute{ "Hello, world!" } + # sleep(0.1) + # + # p.state #=> :fulfilled + # p.fulfilled? #=> true + # p.value #=> "Hello, world!" + # ``` + # + # If an exception occurs, the promise will be rejected and will provide + # a reason for the rejection: + # + # ```ruby + # p = Concurrent::Promise.execute{ raise StandardError.new("Here comes the Boom!") } + # sleep(0.1) + # + # p.state #=> :rejected + # p.rejected? #=> true + # p.reason #=> "#" + # ``` + # + # #### Rejection + # + # When a promise is rejected all its children will be rejected and will + # receive the rejection `reason` as the rejection callable parameter: + # + # ```ruby + # p = Concurrent::Promise.execute { Thread.pass; raise StandardError } + # + # c1 = p.then(-> reason { 42 }) + # c2 = p.then(-> reason { raise 'Boom!' }) + # + # c1.wait.state #=> :fulfilled + # c1.value #=> 45 + # c2.wait.state #=> :rejected + # c2.reason #=> # + # ``` + # + # Once a promise is rejected it will continue to accept children that will + # receive immediately rejection (they will be executed asynchronously). + # + # #### Aliases + # + # The `then` method is the most generic alias: it accepts a block to be + # executed upon parent fulfillment and a callable to be executed upon parent + # rejection. At least one of them should be passed. The default block is `{ + # |result| result }` that fulfills the child with the parent value. The + # default callable is `{ |reason| raise reason }` that rejects the child with + # the parent reason. + # + # - `on_success { |result| ... }` is the same as `then {|result| ... }` + # - `rescue { |reason| ... }` is the same as `then(Proc.new { |reason| ... } )` + # - `rescue` is aliased by `catch` and `on_error` + class Promise < IVar + + # Initialize a new Promise with the provided options. + # + # @!macro executor_and_deref_options + # + # @!macro promise_init_options + # + # @option opts [Promise] :parent the parent `Promise` when building a chain/tree + # @option opts [Proc] :on_fulfill fulfillment handler + # @option opts [Proc] :on_reject rejection handler + # @option opts [object, Array] :args zero or more arguments to be passed + # the task block on execution + # + # @yield The block operation to be performed asynchronously. + # + # @raise [ArgumentError] if no block is given + # + # @see http://wiki.commonjs.org/wiki/Promises/A + # @see http://promises-aplus.github.io/promises-spec/ + def initialize(opts = {}, &block) + opts.delete_if { |k, v| v.nil? } + super(NULL, opts.merge(__promise_body_from_block__: block), &nil) + end + + # Create a new `Promise` and fulfill it immediately. + # + # @!macro executor_and_deref_options + # + # @!macro promise_init_options + # + # @raise [ArgumentError] if no block is given + # + # @return [Promise] the newly created `Promise` + def self.fulfill(value, opts = {}) + Promise.new(opts).tap { |p| p.send(:synchronized_set_state!, true, value, nil) } + end + + # Create a new `Promise` and reject it immediately. + # + # @!macro executor_and_deref_options + # + # @!macro promise_init_options + # + # @raise [ArgumentError] if no block is given + # + # @return [Promise] the newly created `Promise` + def self.reject(reason, opts = {}) + Promise.new(opts).tap { |p| p.send(:synchronized_set_state!, false, nil, reason) } + end + + # Execute an `:unscheduled` `Promise`. Immediately sets the state to `:pending` and + # passes the block to a new thread/thread pool for eventual execution. + # Does nothing if the `Promise` is in any state other than `:unscheduled`. + # + # @return [Promise] a reference to `self` + def execute + if root? + if compare_and_set_state(:pending, :unscheduled) + set_pending + realize(@promise_body) + end + else + @parent.execute + end + self + end + + # @!macro ivar_set_method + # + # @raise [Concurrent::PromiseExecutionError] if not the root promise + def set(value = NULL, &block) + raise PromiseExecutionError.new('supported only on root promise') unless root? + check_for_block_or_value!(block_given?, value) + synchronize do + if @state != :unscheduled + raise MultipleAssignmentError + else + @promise_body = block || Proc.new { |result| value } + end + end + execute + end + + # @!macro ivar_fail_method + # + # @raise [Concurrent::PromiseExecutionError] if not the root promise + def fail(reason = StandardError.new) + set { raise reason } + end + + # Create a new `Promise` object with the given block, execute it, and return the + # `:pending` object. + # + # @!macro executor_and_deref_options + # + # @!macro promise_init_options + # + # @return [Promise] the newly created `Promise` in the `:pending` state + # + # @raise [ArgumentError] if no block is given + # + # @example + # promise = Concurrent::Promise.execute{ sleep(1); 42 } + # promise.state #=> :pending + def self.execute(opts = {}, &block) + new(opts, &block).execute + end + + # Chain a new promise off the current promise. + # + # @return [Promise] the new promise + # @yield The block operation to be performed asynchronously. + # @overload then(rescuer, executor, &block) + # @param [Proc] rescuer An optional rescue block to be executed if the + # promise is rejected. + # @param [ThreadPool] executor An optional thread pool executor to be used + # in the new Promise + # @overload then(rescuer, executor: executor, &block) + # @param [Proc] rescuer An optional rescue block to be executed if the + # promise is rejected. + # @param [ThreadPool] executor An optional thread pool executor to be used + # in the new Promise + def then(*args, &block) + if args.last.is_a?(::Hash) + executor = args.pop[:executor] + rescuer = args.first + else + rescuer, executor = args + end + + executor ||= @executor + + raise ArgumentError.new('rescuers and block are both missing') if rescuer.nil? && !block_given? + block = Proc.new { |result| result } unless block_given? + child = Promise.new( + parent: self, + executor: executor, + on_fulfill: block, + on_reject: rescuer + ) + + synchronize do + child.state = :pending if @state == :pending + child.on_fulfill(apply_deref_options(@value)) if @state == :fulfilled + child.on_reject(@reason) if @state == :rejected + @children << child + end + + child + end + + # Chain onto this promise an action to be undertaken on success + # (fulfillment). + # + # @yield The block to execute + # + # @return [Promise] self + def on_success(&block) + raise ArgumentError.new('no block given') unless block_given? + self.then(&block) + end + + # Chain onto this promise an action to be undertaken on failure + # (rejection). + # + # @yield The block to execute + # + # @return [Promise] self + def rescue(&block) + self.then(block) + end + + alias_method :catch, :rescue + alias_method :on_error, :rescue + + # Yield the successful result to the block that returns a promise. If that + # promise is also successful the result is the result of the yielded promise. + # If either part fails the whole also fails. + # + # @example + # Promise.execute { 1 }.flat_map { |v| Promise.execute { v + 2 } }.value! #=> 3 + # + # @return [Promise] + def flat_map(&block) + child = Promise.new( + parent: self, + executor: ImmediateExecutor.new, + ) + + on_error { |e| child.on_reject(e) } + on_success do |result1| + begin + inner = block.call(result1) + inner.execute + inner.on_success { |result2| child.on_fulfill(result2) } + inner.on_error { |e| child.on_reject(e) } + rescue => e + child.on_reject(e) + end + end + + child + end + + # Builds a promise that produces the result of promises in an Array + # and fails if any of them fails. + # + # @overload zip(*promises) + # @param [Array] promises + # + # @overload zip(*promises, opts) + # @param [Array] promises + # @param [Hash] opts the configuration options + # @option opts [Executor] :executor (ImmediateExecutor.new) when set use the given `Executor` instance. + # @option opts [Boolean] :execute (true) execute promise before returning + # + # @return [Promise] + def self.zip(*promises) + opts = promises.last.is_a?(::Hash) ? promises.pop.dup : {} + opts[:executor] ||= ImmediateExecutor.new + zero = if !opts.key?(:execute) || opts.delete(:execute) + fulfill([], opts) + else + Promise.new(opts) { [] } + end + + promises.reduce(zero) do |p1, p2| + p1.flat_map do |results| + p2.then do |next_result| + results << next_result + end + end + end + end + + # Builds a promise that produces the result of self and others in an Array + # and fails if any of them fails. + # + # @overload zip(*promises) + # @param [Array] others + # + # @overload zip(*promises, opts) + # @param [Array] others + # @param [Hash] opts the configuration options + # @option opts [Executor] :executor (ImmediateExecutor.new) when set use the given `Executor` instance. + # @option opts [Boolean] :execute (true) execute promise before returning + # + # @return [Promise] + def zip(*others) + self.class.zip(self, *others) + end + + # Aggregates a collection of promises and executes the `then` condition + # if all aggregated promises succeed. Executes the `rescue` handler with + # a `Concurrent::PromiseExecutionError` if any of the aggregated promises + # fail. Upon execution will execute any of the aggregate promises that + # were not already executed. + # + # @!macro promise_self_aggregate + # + # The returned promise will not yet have been executed. Additional `#then` + # and `#rescue` handlers may still be provided. Once the returned promise + # is execute the aggregate promises will be also be executed (if they have + # not been executed already). The results of the aggregate promises will + # be checked upon completion. The necessary `#then` and `#rescue` blocks + # on the aggregating promise will then be executed as appropriate. If the + # `#rescue` handlers are executed the raises exception will be + # `Concurrent::PromiseExecutionError`. + # + # @param [Array] promises Zero or more promises to aggregate + # @return [Promise] an unscheduled (not executed) promise that aggregates + # the promises given as arguments + def self.all?(*promises) + aggregate(:all?, *promises) + end + + # Aggregates a collection of promises and executes the `then` condition + # if any aggregated promises succeed. Executes the `rescue` handler with + # a `Concurrent::PromiseExecutionError` if any of the aggregated promises + # fail. Upon execution will execute any of the aggregate promises that + # were not already executed. + # + # @!macro promise_self_aggregate + def self.any?(*promises) + aggregate(:any?, *promises) + end + + protected + + def ns_initialize(value, opts) + super + + @executor = Options.executor_from_options(opts) || Concurrent.global_io_executor + @args = get_arguments_from(opts) + + @parent = opts.fetch(:parent) { nil } + @on_fulfill = opts.fetch(:on_fulfill) { Proc.new { |result| result } } + @on_reject = opts.fetch(:on_reject) { Proc.new { |reason| raise reason } } + + @promise_body = opts[:__promise_body_from_block__] || Proc.new { |result| result } + @state = :unscheduled + @children = [] + end + + # Aggregate a collection of zero or more promises under a composite promise, + # execute the aggregated promises and collect them into a standard Ruby array, + # call the given Ruby `Ennnumerable` predicate (such as `any?`, `all?`, `none?`, + # or `one?`) on the collection checking for the success or failure of each, + # then executing the composite's `#then` handlers if the predicate returns + # `true` or executing the composite's `#rescue` handlers if the predicate + # returns false. + # + # @!macro promise_self_aggregate + def self.aggregate(method, *promises) + composite = Promise.new do + completed = promises.collect do |promise| + promise.execute if promise.unscheduled? + promise.wait + promise + end + unless completed.empty? || completed.send(method){|promise| promise.fulfilled? } + raise PromiseExecutionError + end + end + composite + end + + # @!visibility private + def set_pending + synchronize do + @state = :pending + @children.each { |c| c.set_pending } + end + end + + # @!visibility private + def root? # :nodoc: + @parent.nil? + end + + # @!visibility private + def on_fulfill(result) + realize Proc.new { @on_fulfill.call(result) } + nil + end + + # @!visibility private + def on_reject(reason) + realize Proc.new { @on_reject.call(reason) } + nil + end + + # @!visibility private + def notify_child(child) + if_state(:fulfilled) { child.on_fulfill(apply_deref_options(@value)) } + if_state(:rejected) { child.on_reject(@reason) } + end + + # @!visibility private + def complete(success, value, reason) + children_to_notify = synchronize do + set_state!(success, value, reason) + @children.dup + end + + children_to_notify.each { |child| notify_child(child) } + observers.notify_and_delete_observers{ [Time.now, self.value, reason] } + end + + # @!visibility private + def realize(task) + @executor.post do + success, value, reason = SafeTaskExecutor.new(task, rescue_exception: true).execute(*@args) + complete(success, value, reason) + end + end + + # @!visibility private + def set_state!(success, value, reason) + set_state(success, value, reason) + event.set + end + + # @!visibility private + def synchronized_set_state!(success, value, reason) + synchronize { set_state!(success, value, reason) } + end + end +end diff --git a/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/promises.rb b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/promises.rb new file mode 100644 index 0000000..76af4d5 --- /dev/null +++ b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/promises.rb @@ -0,0 +1,2167 @@ +require 'concurrent/synchronization' +require 'concurrent/atomic/atomic_boolean' +require 'concurrent/atomic/atomic_fixnum' +require 'concurrent/collection/lock_free_stack' +require 'concurrent/errors' +require 'concurrent/re_include' + +module Concurrent + + # {include:file:docs-source/promises-main.md} + module Promises + + # @!macro promises.param.default_executor + # @param [Executor, :io, :fast] default_executor Instance of an executor or a name of the + # global executor. Default executor propagates to chained futures unless overridden with + # executor parameter or changed with {AbstractEventFuture#with_default_executor}. + # + # @!macro promises.param.executor + # @param [Executor, :io, :fast] executor Instance of an executor or a name of the + # global executor. The task is executed on it, default executor remains unchanged. + # + # @!macro promises.param.args + # @param [Object] args arguments which are passed to the task when it's executed. + # (It might be prepended with other arguments, see the @yeild section). + # + # @!macro promises.shortcut.on + # Shortcut of {#$0_on} with default `:io` executor supplied. + # @see #$0_on + # + # @!macro promises.shortcut.using + # Shortcut of {#$0_using} with default `:io` executor supplied. + # @see #$0_using + # + # @!macro promise.param.task-future + # @yieldreturn will become result of the returned Future. + # Its returned value becomes {Future#value} fulfilling it, + # raised exception becomes {Future#reason} rejecting it. + # + # @!macro promise.param.callback + # @yieldreturn is forgotten. + + # Container of all {Future}, {Event} factory methods. They are never constructed directly with + # new. + module FactoryMethods + extend ReInclude + extend self + + module Configuration + # @return [Executor, :io, :fast] the executor which is used when none is supplied + # to a factory method. The method can be overridden in the receivers of + # `include FactoryMethod` + def default_executor + :io + end + end + + include Configuration + + # @!macro promises.shortcut.on + # @return [ResolvableEvent] + def resolvable_event + resolvable_event_on default_executor + end + + # Created resolvable event, user is responsible for resolving the event once by + # {Promises::ResolvableEvent#resolve}. + # + # @!macro promises.param.default_executor + # @return [ResolvableEvent] + def resolvable_event_on(default_executor = self.default_executor) + ResolvableEventPromise.new(default_executor).future + end + + # @!macro promises.shortcut.on + # @return [ResolvableFuture] + def resolvable_future + resolvable_future_on default_executor + end + + # Creates resolvable future, user is responsible for resolving the future once by + # {Promises::ResolvableFuture#resolve}, {Promises::ResolvableFuture#fulfill}, + # or {Promises::ResolvableFuture#reject} + # + # @!macro promises.param.default_executor + # @return [ResolvableFuture] + def resolvable_future_on(default_executor = self.default_executor) + ResolvableFuturePromise.new(default_executor).future + end + + # @!macro promises.shortcut.on + # @return [Future] + def future(*args, &task) + future_on(default_executor, *args, &task) + end + + # Constructs new Future which will be resolved after block is evaluated on default executor. + # Evaluation begins immediately. + # + # @!macro promises.param.default_executor + # @!macro promises.param.args + # @yield [*args] to the task. + # @!macro promise.param.task-future + # @return [Future] + def future_on(default_executor, *args, &task) + ImmediateEventPromise.new(default_executor).future.then(*args, &task) + end + + # Creates resolved future with will be either fulfilled with the given value or rejection with + # the given reason. + # + # @param [true, false] fulfilled + # @param [Object] value + # @param [Object] reason + # @!macro promises.param.default_executor + # @return [Future] + def resolved_future(fulfilled, value, reason, default_executor = self.default_executor) + ImmediateFuturePromise.new(default_executor, fulfilled, value, reason).future + end + + # Creates resolved future with will be fulfilled with the given value. + # + # @!macro promises.param.default_executor + # @param [Object] value + # @return [Future] + def fulfilled_future(value, default_executor = self.default_executor) + resolved_future true, value, nil, default_executor + end + + # Creates resolved future with will be rejected with the given reason. + # + # @!macro promises.param.default_executor + # @param [Object] reason + # @return [Future] + def rejected_future(reason, default_executor = self.default_executor) + resolved_future false, nil, reason, default_executor + end + + # Creates resolved event. + # + # @!macro promises.param.default_executor + # @return [Event] + def resolved_event(default_executor = self.default_executor) + ImmediateEventPromise.new(default_executor).event + end + + # General constructor. Behaves differently based on the argument's type. It's provided for convenience + # but it's better to be explicit. + # + # @see rejected_future, resolved_event, fulfilled_future + # @!macro promises.param.default_executor + # @return [Event, Future] + # + # @overload make_future(nil, default_executor = self.default_executor) + # @param [nil] nil + # @return [Event] resolved event. + # + # @overload make_future(a_future, default_executor = self.default_executor) + # @param [Future] a_future + # @return [Future] a future which will be resolved when a_future is. + # + # @overload make_future(an_event, default_executor = self.default_executor) + # @param [Event] an_event + # @return [Event] an event which will be resolved when an_event is. + # + # @overload make_future(exception, default_executor = self.default_executor) + # @param [Exception] exception + # @return [Future] a rejected future with the exception as its reason. + # + # @overload make_future(value, default_executor = self.default_executor) + # @param [Object] value when none of the above overloads fits + # @return [Future] a fulfilled future with the value. + def make_future(argument = nil, default_executor = self.default_executor) + case argument + when AbstractEventFuture + # returning wrapper would change nothing + argument + when Exception + rejected_future argument, default_executor + when nil + resolved_event default_executor + else + fulfilled_future argument, default_executor + end + end + + # @!macro promises.shortcut.on + # @return [Future, Event] + def delay(*args, &task) + delay_on default_executor, *args, &task + end + + # Creates new event or future which is resolved only after it is touched, + # see {Concurrent::AbstractEventFuture#touch}. + # + # @!macro promises.param.default_executor + # @overload delay_on(default_executor, *args, &task) + # If task is provided it returns a {Future} representing the result of the task. + # @!macro promises.param.args + # @yield [*args] to the task. + # @!macro promise.param.task-future + # @return [Future] + # @overload delay_on(default_executor) + # If no task is provided, it returns an {Event} + # @return [Event] + def delay_on(default_executor, *args, &task) + event = DelayPromise.new(default_executor).event + task ? event.chain(*args, &task) : event + end + + # @!macro promises.shortcut.on + # @return [Future, Event] + def schedule(intended_time, *args, &task) + schedule_on default_executor, intended_time, *args, &task + end + + # Creates new event or future which is resolved in intended_time. + # + # @!macro promises.param.default_executor + # @!macro promises.param.intended_time + # @param [Numeric, Time] intended_time `Numeric` means to run in `intended_time` seconds. + # `Time` means to run on `intended_time`. + # @overload schedule_on(default_executor, intended_time, *args, &task) + # If task is provided it returns a {Future} representing the result of the task. + # @!macro promises.param.args + # @yield [*args] to the task. + # @!macro promise.param.task-future + # @return [Future] + # @overload schedule_on(default_executor, intended_time) + # If no task is provided, it returns an {Event} + # @return [Event] + def schedule_on(default_executor, intended_time, *args, &task) + event = ScheduledPromise.new(default_executor, intended_time).event + task ? event.chain(*args, &task) : event + end + + # @!macro promises.shortcut.on + # @return [Future] + def zip_futures(*futures_and_or_events) + zip_futures_on default_executor, *futures_and_or_events + end + + # Creates new future which is resolved after all futures_and_or_events are resolved. + # Its value is array of zipped future values. Its reason is array of reasons for rejection. + # If there is an error it rejects. + # @!macro promises.event-conversion + # If event is supplied, which does not have value and can be only resolved, it's + # represented as `:fulfilled` with value `nil`. + # + # @!macro promises.param.default_executor + # @param [AbstractEventFuture] futures_and_or_events + # @return [Future] + def zip_futures_on(default_executor, *futures_and_or_events) + ZipFuturesPromise.new_blocked_by(futures_and_or_events, default_executor).future + end + + alias_method :zip, :zip_futures + + # @!macro promises.shortcut.on + # @return [Event] + def zip_events(*futures_and_or_events) + zip_events_on default_executor, *futures_and_or_events + end + + # Creates new event which is resolved after all futures_and_or_events are resolved. + # (Future is resolved when fulfilled or rejected.) + # + # @!macro promises.param.default_executor + # @param [AbstractEventFuture] futures_and_or_events + # @return [Event] + def zip_events_on(default_executor, *futures_and_or_events) + ZipEventsPromise.new_blocked_by(futures_and_or_events, default_executor).event + end + + # @!macro promises.shortcut.on + # @return [Future] + def any_resolved_future(*futures_and_or_events) + any_resolved_future_on default_executor, *futures_and_or_events + end + + alias_method :any, :any_resolved_future + + # Creates new future which is resolved after first futures_and_or_events is resolved. + # Its result equals result of the first resolved future. + # @!macro promises.any-touch + # If resolved it does not propagate {Concurrent::AbstractEventFuture#touch}, leaving delayed + # futures un-executed if they are not required any more. + # @!macro promises.event-conversion + # + # @!macro promises.param.default_executor + # @param [AbstractEventFuture] futures_and_or_events + # @return [Future] + def any_resolved_future_on(default_executor, *futures_and_or_events) + AnyResolvedFuturePromise.new_blocked_by(futures_and_or_events, default_executor).future + end + + # @!macro promises.shortcut.on + # @return [Future] + def any_fulfilled_future(*futures_and_or_events) + any_fulfilled_future_on default_executor, *futures_and_or_events + end + + # Creates new future which is resolved after first of futures_and_or_events is fulfilled. + # Its result equals result of the first resolved future or if all futures_and_or_events reject, + # it has reason of the last resolved future. + # @!macro promises.any-touch + # @!macro promises.event-conversion + # + # @!macro promises.param.default_executor + # @param [AbstractEventFuture] futures_and_or_events + # @return [Future] + def any_fulfilled_future_on(default_executor, *futures_and_or_events) + AnyFulfilledFuturePromise.new_blocked_by(futures_and_or_events, default_executor).future + end + + # @!macro promises.shortcut.on + # @return [Future] + def any_event(*futures_and_or_events) + any_event_on default_executor, *futures_and_or_events + end + + # Creates new event which becomes resolved after first of the futures_and_or_events resolves. + # @!macro promises.any-touch + # + # @!macro promises.param.default_executor + # @param [AbstractEventFuture] futures_and_or_events + # @return [Event] + def any_event_on(default_executor, *futures_and_or_events) + AnyResolvedEventPromise.new_blocked_by(futures_and_or_events, default_executor).event + end + + # TODO consider adding first(count, *futures) + # TODO consider adding zip_by(slice, *futures) processing futures in slices + # TODO or rather a generic aggregator taking a function + end + + module InternalStates + # @!visibility private + class State + def resolved? + raise NotImplementedError + end + + def to_sym + raise NotImplementedError + end + end + + # @!visibility private + class Pending < State + def resolved? + false + end + + def to_sym + :pending + end + end + + # @!visibility private + class Reserved < Pending + end + + # @!visibility private + class ResolvedWithResult < State + def resolved? + true + end + + def to_sym + :resolved + end + + def result + [fulfilled?, value, reason] + end + + def fulfilled? + raise NotImplementedError + end + + def value + raise NotImplementedError + end + + def reason + raise NotImplementedError + end + + def apply + raise NotImplementedError + end + end + + # @!visibility private + class Fulfilled < ResolvedWithResult + + def initialize(value) + @Value = value + end + + def fulfilled? + true + end + + def apply(args, block) + block.call value, *args + end + + def value + @Value + end + + def reason + nil + end + + def to_sym + :fulfilled + end + end + + # @!visibility private + class FulfilledArray < Fulfilled + def apply(args, block) + block.call(*value, *args) + end + end + + # @!visibility private + class Rejected < ResolvedWithResult + def initialize(reason) + @Reason = reason + end + + def fulfilled? + false + end + + def value + nil + end + + def reason + @Reason + end + + def to_sym + :rejected + end + + def apply(args, block) + block.call reason, *args + end + end + + # @!visibility private + class PartiallyRejected < ResolvedWithResult + def initialize(value, reason) + super() + @Value = value + @Reason = reason + end + + def fulfilled? + false + end + + def to_sym + :rejected + end + + def value + @Value + end + + def reason + @Reason + end + + def apply(args, block) + block.call(*reason, *args) + end + end + + # @!visibility private + PENDING = Pending.new + # @!visibility private + RESERVED = Reserved.new + # @!visibility private + RESOLVED = Fulfilled.new(nil) + + def RESOLVED.to_sym + :resolved + end + end + + private_constant :InternalStates + + # @!macro promises.shortcut.event-future + # @see Event#$0 + # @see Future#$0 + + # @!macro promises.param.timeout + # @param [Numeric] timeout the maximum time in second to wait. + + # @!macro promises.warn.blocks + # @note This function potentially blocks current thread until the Future is resolved. + # Be careful it can deadlock. Try to chain instead. + + # Common ancestor of {Event} and {Future} classes, many shared methods are defined here. + class AbstractEventFuture < Synchronization::Object + safe_initialization! + attr_atomic(:internal_state) + private :internal_state=, :swap_internal_state, :compare_and_set_internal_state, :update_internal_state + # @!method internal_state + # @!visibility private + + include InternalStates + + def initialize(promise, default_executor) + super() + @Lock = Mutex.new + @Condition = ConditionVariable.new + @Promise = promise + @DefaultExecutor = default_executor + @Callbacks = LockFreeStack.new + @Waiters = AtomicFixnum.new 0 + self.internal_state = PENDING + end + + private :initialize + + # Returns its state. + # @return [Symbol] + # + # @overload an_event.state + # @return [:pending, :resolved] + # @overload a_future.state + # Both :fulfilled, :rejected implies :resolved. + # @return [:pending, :fulfilled, :rejected] + def state + internal_state.to_sym + end + + # Is it in pending state? + # @return [Boolean] + def pending? + !internal_state.resolved? + end + + # Is it in resolved state? + # @return [Boolean] + def resolved? + internal_state.resolved? + end + + # Propagates touch. Requests all the delayed futures, which it depends on, to be + # executed. This method is called by any other method requiring resolved state, like {#wait}. + # @return [self] + def touch + @Promise.touch + self + end + + # @!macro promises.touches + # Calls {Concurrent::AbstractEventFuture#touch}. + + # @!macro promises.method.wait + # Wait (block the Thread) until receiver is {#resolved?}. + # @!macro promises.touches + # + # @!macro promises.warn.blocks + # @!macro promises.param.timeout + # @return [self, true, false] self implies timeout was not used, true implies timeout was used + # and it was resolved, false implies it was not resolved within timeout. + def wait(timeout = nil) + result = wait_until_resolved(timeout) + timeout ? result : self + end + + # Returns default executor. + # @return [Executor] default executor + # @see #with_default_executor + # @see FactoryMethods#future_on + # @see FactoryMethods#resolvable_future + # @see FactoryMethods#any_fulfilled_future_on + # @see similar + def default_executor + @DefaultExecutor + end + + # @!macro promises.shortcut.on + # @return [Future] + def chain(*args, &task) + chain_on @DefaultExecutor, *args, &task + end + + # Chains the task to be executed asynchronously on executor after it is resolved. + # + # @!macro promises.param.executor + # @!macro promises.param.args + # @return [Future] + # @!macro promise.param.task-future + # + # @overload an_event.chain_on(executor, *args, &task) + # @yield [*args] to the task. + # @overload a_future.chain_on(executor, *args, &task) + # @yield [fulfilled, value, reason, *args] to the task. + # @yieldparam [true, false] fulfilled + # @yieldparam [Object] value + # @yieldparam [Object] reason + def chain_on(executor, *args, &task) + ChainPromise.new_blocked_by1(self, @DefaultExecutor, executor, args, &task).future + end + + # @return [String] Short string representation. + def to_s + format '%s %s>', super[0..-2], state + end + + alias_method :inspect, :to_s + + # Resolves the resolvable when receiver is resolved. + # + # @param [Resolvable] resolvable + # @return [self] + def chain_resolvable(resolvable) + on_resolution! { resolvable.resolve_with internal_state } + end + + alias_method :tangle, :chain_resolvable + + # @!macro promises.shortcut.using + # @return [self] + def on_resolution(*args, &callback) + on_resolution_using @DefaultExecutor, *args, &callback + end + + # Stores the callback to be executed synchronously on resolving thread after it is + # resolved. + # + # @!macro promises.param.args + # @!macro promise.param.callback + # @return [self] + # + # @overload an_event.on_resolution!(*args, &callback) + # @yield [*args] to the callback. + # @overload a_future.on_resolution!(*args, &callback) + # @yield [fulfilled, value, reason, *args] to the callback. + # @yieldparam [true, false] fulfilled + # @yieldparam [Object] value + # @yieldparam [Object] reason + def on_resolution!(*args, &callback) + add_callback :callback_on_resolution, args, callback + end + + # Stores the callback to be executed asynchronously on executor after it is resolved. + # + # @!macro promises.param.executor + # @!macro promises.param.args + # @!macro promise.param.callback + # @return [self] + # + # @overload an_event.on_resolution_using(executor, *args, &callback) + # @yield [*args] to the callback. + # @overload a_future.on_resolution_using(executor, *args, &callback) + # @yield [fulfilled, value, reason, *args] to the callback. + # @yieldparam [true, false] fulfilled + # @yieldparam [Object] value + # @yieldparam [Object] reason + def on_resolution_using(executor, *args, &callback) + add_callback :async_callback_on_resolution, executor, args, callback + end + + # @!macro promises.method.with_default_executor + # Crates new object with same class with the executor set as its new default executor. + # Any futures depending on it will use the new default executor. + # @!macro promises.shortcut.event-future + # @abstract + # @return [AbstractEventFuture] + def with_default_executor(executor) + raise NotImplementedError + end + + # @!visibility private + def resolve_with(state, raise_on_reassign = true, reserved = false) + if compare_and_set_internal_state(reserved ? RESERVED : PENDING, state) + # go to synchronized block only if there were waiting threads + @Lock.synchronize { @Condition.broadcast } unless @Waiters.value == 0 + call_callbacks state + else + return rejected_resolution(raise_on_reassign, state) + end + self + end + + # For inspection. + # @!visibility private + # @return [Array] + def blocks + @Callbacks.each_with_object([]) do |(method, args), promises| + promises.push(args[0]) if method == :callback_notify_blocked + end + end + + # For inspection. + # @!visibility private + def callbacks + @Callbacks.each.to_a + end + + # For inspection. + # @!visibility private + def promise + @Promise + end + + # For inspection. + # @!visibility private + def touched? + promise.touched? + end + + # For inspection. + # @!visibility private + def waiting_threads + @Waiters.each.to_a + end + + # @!visibility private + def add_callback_notify_blocked(promise, index) + add_callback :callback_notify_blocked, promise, index + end + + # @!visibility private + def add_callback_clear_delayed_node(node) + add_callback(:callback_clear_delayed_node, node) + end + + # @!visibility private + def with_hidden_resolvable + # TODO (pitr-ch 10-Dec-2018): documentation, better name if in edge + self + end + + private + + def add_callback(method, *args) + state = internal_state + if state.resolved? + call_callback method, state, args + else + @Callbacks.push [method, args] + state = internal_state + # take back if it was resolved in the meanwhile + call_callbacks state if state.resolved? + end + self + end + + def callback_clear_delayed_node(state, node) + node.value = nil + end + + # @return [Boolean] + def wait_until_resolved(timeout) + return true if resolved? + + touch + + @Lock.synchronize do + @Waiters.increment + begin + unless resolved? + @Condition.wait @Lock, timeout + end + ensure + # JRuby may raise ConcurrencyError + @Waiters.decrement + end + end + resolved? + end + + def call_callback(method, state, args) + self.send method, state, *args + end + + def call_callbacks(state) + method, args = @Callbacks.pop + while method + call_callback method, state, args + method, args = @Callbacks.pop + end + end + + def with_async(executor, *args, &block) + Concurrent.executor(executor).post(*args, &block) + end + + def async_callback_on_resolution(state, executor, args, callback) + with_async(executor, state, args, callback) do |st, ar, cb| + callback_on_resolution st, ar, cb + end + end + + def callback_notify_blocked(state, promise, index) + promise.on_blocker_resolution self, index + end + end + + # Represents an event which will happen in future (will be resolved). The event is either + # pending or resolved. It should be always resolved. Use {Future} to communicate rejections and + # cancellation. + class Event < AbstractEventFuture + + alias_method :then, :chain + + + # @!macro promises.method.zip + # Creates a new event or a future which will be resolved when receiver and other are. + # Returns an event if receiver and other are events, otherwise returns a future. + # If just one of the parties is Future then the result + # of the returned future is equal to the result of the supplied future. If both are futures + # then the result is as described in {FactoryMethods#zip_futures_on}. + # + # @return [Future, Event] + def zip(other) + if other.is_a?(Future) + ZipFutureEventPromise.new_blocked_by2(other, self, @DefaultExecutor).future + else + ZipEventEventPromise.new_blocked_by2(self, other, @DefaultExecutor).event + end + end + + alias_method :&, :zip + + # Creates a new event which will be resolved when the first of receiver, `event_or_future` + # resolves. + # + # @return [Event] + def any(event_or_future) + AnyResolvedEventPromise.new_blocked_by2(self, event_or_future, @DefaultExecutor).event + end + + alias_method :|, :any + + # Creates new event dependent on receiver which will not evaluate until touched, see {#touch}. + # In other words, it inserts delay into the chain of Futures making rest of it lazy evaluated. + # + # @return [Event] + def delay + event = DelayPromise.new(@DefaultExecutor).event + ZipEventEventPromise.new_blocked_by2(self, event, @DefaultExecutor).event + end + + # @!macro promise.method.schedule + # Creates new event dependent on receiver scheduled to execute on/in intended_time. + # In time is interpreted from the moment the receiver is resolved, therefore it inserts + # delay into the chain. + # + # @!macro promises.param.intended_time + # @return [Event] + def schedule(intended_time) + chain do + event = ScheduledPromise.new(@DefaultExecutor, intended_time).event + ZipEventEventPromise.new_blocked_by2(self, event, @DefaultExecutor).event + end.flat_event + end + + # Converts event to a future. The future is fulfilled when the event is resolved, the future may never fail. + # + # @return [Future] + def to_future + future = Promises.resolvable_future + ensure + chain_resolvable(future) + end + + # Returns self, since this is event + # @return [Event] + def to_event + self + end + + # @!macro promises.method.with_default_executor + # @return [Event] + def with_default_executor(executor) + EventWrapperPromise.new_blocked_by1(self, executor).event + end + + private + + def rejected_resolution(raise_on_reassign, state) + Concurrent::MultipleAssignmentError.new('Event can be resolved only once') if raise_on_reassign + return false + end + + def callback_on_resolution(state, args, callback) + callback.call(*args) + end + end + + # Represents a value which will become available in future. May reject with a reason instead, + # e.g. when the tasks raises an exception. + class Future < AbstractEventFuture + + # Is it in fulfilled state? + # @return [Boolean] + def fulfilled? + state = internal_state + state.resolved? && state.fulfilled? + end + + # Is it in rejected state? + # @return [Boolean] + def rejected? + state = internal_state + state.resolved? && !state.fulfilled? + end + + # @!macro promises.warn.nil + # @note Make sure returned `nil` is not confused with timeout, no value when rejected, + # no reason when fulfilled, etc. + # Use more exact methods if needed, like {#wait}, {#value!}, {#result}, etc. + + # @!macro promises.method.value + # Return value of the future. + # @!macro promises.touches + # + # @!macro promises.warn.blocks + # @!macro promises.warn.nil + # @!macro promises.param.timeout + # @!macro promises.param.timeout_value + # @param [Object] timeout_value a value returned by the method when it times out + # @return [Object, nil, timeout_value] the value of the Future when fulfilled, + # timeout_value on timeout, + # nil on rejection. + def value(timeout = nil, timeout_value = nil) + if wait_until_resolved timeout + internal_state.value + else + timeout_value + end + end + + # Returns reason of future's rejection. + # @!macro promises.touches + # + # @!macro promises.warn.blocks + # @!macro promises.warn.nil + # @!macro promises.param.timeout + # @!macro promises.param.timeout_value + # @return [Object, timeout_value] the reason, or timeout_value on timeout, or nil on fulfillment. + def reason(timeout = nil, timeout_value = nil) + if wait_until_resolved timeout + internal_state.reason + else + timeout_value + end + end + + # Returns triplet fulfilled?, value, reason. + # @!macro promises.touches + # + # @!macro promises.warn.blocks + # @!macro promises.param.timeout + # @return [Array(Boolean, Object, Object), nil] triplet of fulfilled?, value, reason, or nil + # on timeout. + def result(timeout = nil) + internal_state.result if wait_until_resolved timeout + end + + # @!macro promises.method.wait + # @raise [Exception] {#reason} on rejection + def wait!(timeout = nil) + result = wait_until_resolved!(timeout) + timeout ? result : self + end + + # @!macro promises.method.value + # @return [Object, nil, timeout_value] the value of the Future when fulfilled, + # or nil on rejection, + # or timeout_value on timeout. + # @raise [Exception] {#reason} on rejection + def value!(timeout = nil, timeout_value = nil) + if wait_until_resolved! timeout + internal_state.value + else + timeout_value + end + end + + # Allows rejected Future to be risen with `raise` method. + # If the reason is not an exception `Runtime.new(reason)` is returned. + # + # @example + # raise Promises.rejected_future(StandardError.new("boom")) + # raise Promises.rejected_future("or just boom") + # @raise [Concurrent::Error] when raising not rejected future + # @return [Exception] + def exception(*args) + raise Concurrent::Error, 'it is not rejected' unless rejected? + raise ArgumentError unless args.size <= 1 + reason = Array(internal_state.reason).flatten.compact + if reason.size > 1 + ex = Concurrent::MultipleErrors.new reason + ex.set_backtrace(caller) + ex + else + ex = if reason[0].respond_to? :exception + reason[0].exception(*args) + else + RuntimeError.new(reason[0]).exception(*args) + end + ex.set_backtrace Array(ex.backtrace) + caller + ex + end + end + + # @!macro promises.shortcut.on + # @return [Future] + def then(*args, &task) + then_on @DefaultExecutor, *args, &task + end + + # Chains the task to be executed asynchronously on executor after it fulfills. Does not run + # the task if it rejects. It will resolve though, triggering any dependent futures. + # + # @!macro promises.param.executor + # @!macro promises.param.args + # @!macro promise.param.task-future + # @return [Future] + # @yield [value, *args] to the task. + def then_on(executor, *args, &task) + ThenPromise.new_blocked_by1(self, @DefaultExecutor, executor, args, &task).future + end + + # @!macro promises.shortcut.on + # @return [Future] + def rescue(*args, &task) + rescue_on @DefaultExecutor, *args, &task + end + + # Chains the task to be executed asynchronously on executor after it rejects. Does not run + # the task if it fulfills. It will resolve though, triggering any dependent futures. + # + # @!macro promises.param.executor + # @!macro promises.param.args + # @!macro promise.param.task-future + # @return [Future] + # @yield [reason, *args] to the task. + def rescue_on(executor, *args, &task) + RescuePromise.new_blocked_by1(self, @DefaultExecutor, executor, args, &task).future + end + + # @!macro promises.method.zip + # @return [Future] + def zip(other) + if other.is_a?(Future) + ZipFuturesPromise.new_blocked_by2(self, other, @DefaultExecutor).future + else + ZipFutureEventPromise.new_blocked_by2(self, other, @DefaultExecutor).future + end + end + + alias_method :&, :zip + + # Creates a new event which will be resolved when the first of receiver, `event_or_future` + # resolves. Returning future will have value nil if event_or_future is event and resolves + # first. + # + # @return [Future] + def any(event_or_future) + AnyResolvedFuturePromise.new_blocked_by2(self, event_or_future, @DefaultExecutor).future + end + + alias_method :|, :any + + # Creates new future dependent on receiver which will not evaluate until touched, see {#touch}. + # In other words, it inserts delay into the chain of Futures making rest of it lazy evaluated. + # + # @return [Future] + def delay + event = DelayPromise.new(@DefaultExecutor).event + ZipFutureEventPromise.new_blocked_by2(self, event, @DefaultExecutor).future + end + + # @!macro promise.method.schedule + # @return [Future] + def schedule(intended_time) + chain do + event = ScheduledPromise.new(@DefaultExecutor, intended_time).event + ZipFutureEventPromise.new_blocked_by2(self, event, @DefaultExecutor).future + end.flat + end + + # @!macro promises.method.with_default_executor + # @return [Future] + def with_default_executor(executor) + FutureWrapperPromise.new_blocked_by1(self, executor).future + end + + # Creates new future which will have result of the future returned by receiver. If receiver + # rejects it will have its rejection. + # + # @param [Integer] level how many levels of futures should flatten + # @return [Future] + def flat_future(level = 1) + FlatFuturePromise.new_blocked_by1(self, level, @DefaultExecutor).future + end + + alias_method :flat, :flat_future + + # Creates new event which will be resolved when the returned event by receiver is. + # Be careful if the receiver rejects it will just resolve since Event does not hold reason. + # + # @return [Event] + def flat_event + FlatEventPromise.new_blocked_by1(self, @DefaultExecutor).event + end + + # @!macro promises.shortcut.using + # @return [self] + def on_fulfillment(*args, &callback) + on_fulfillment_using @DefaultExecutor, *args, &callback + end + + # Stores the callback to be executed synchronously on resolving thread after it is + # fulfilled. Does nothing on rejection. + # + # @!macro promises.param.args + # @!macro promise.param.callback + # @return [self] + # @yield [value, *args] to the callback. + def on_fulfillment!(*args, &callback) + add_callback :callback_on_fulfillment, args, callback + end + + # Stores the callback to be executed asynchronously on executor after it is + # fulfilled. Does nothing on rejection. + # + # @!macro promises.param.executor + # @!macro promises.param.args + # @!macro promise.param.callback + # @return [self] + # @yield [value, *args] to the callback. + def on_fulfillment_using(executor, *args, &callback) + add_callback :async_callback_on_fulfillment, executor, args, callback + end + + # @!macro promises.shortcut.using + # @return [self] + def on_rejection(*args, &callback) + on_rejection_using @DefaultExecutor, *args, &callback + end + + # Stores the callback to be executed synchronously on resolving thread after it is + # rejected. Does nothing on fulfillment. + # + # @!macro promises.param.args + # @!macro promise.param.callback + # @return [self] + # @yield [reason, *args] to the callback. + def on_rejection!(*args, &callback) + add_callback :callback_on_rejection, args, callback + end + + # Stores the callback to be executed asynchronously on executor after it is + # rejected. Does nothing on fulfillment. + # + # @!macro promises.param.executor + # @!macro promises.param.args + # @!macro promise.param.callback + # @return [self] + # @yield [reason, *args] to the callback. + def on_rejection_using(executor, *args, &callback) + add_callback :async_callback_on_rejection, executor, args, callback + end + + # Allows to use futures as green threads. The receiver has to evaluate to a future which + # represents what should be done next. It basically flattens indefinitely until non Future + # values is returned which becomes result of the returned future. Any encountered exception + # will become reason of the returned future. + # + # @return [Future] + # @param [#call(value)] run_test + # an object which when called returns either Future to keep running with + # or nil, then the run completes with the value. + # The run_test can be used to extract the Future from deeper structure, + # or to distinguish Future which is a resulting value from a future + # which is suppose to continue running. + # @example + # body = lambda do |v| + # v += 1 + # v < 5 ? Promises.future(v, &body) : v + # end + # Promises.future(0, &body).run.value! # => 5 + def run(run_test = method(:run_test)) + RunFuturePromise.new_blocked_by1(self, @DefaultExecutor, run_test).future + end + + # @!visibility private + def apply(args, block) + internal_state.apply args, block + end + + # Converts future to event which is resolved when future is resolved by fulfillment or rejection. + # + # @return [Event] + def to_event + event = Promises.resolvable_event + ensure + chain_resolvable(event) + end + + # Returns self, since this is a future + # @return [Future] + def to_future + self + end + + # @return [String] Short string representation. + def to_s + if resolved? + format '%s with %s>', super[0..-2], (fulfilled? ? value : reason).inspect + else + super + end + end + + alias_method :inspect, :to_s + + private + + def run_test(v) + v if v.is_a?(Future) + end + + def rejected_resolution(raise_on_reassign, state) + if raise_on_reassign + if internal_state == RESERVED + raise Concurrent::MultipleAssignmentError.new( + "Future can be resolved only once. It is already reserved.") + else + raise Concurrent::MultipleAssignmentError.new( + "Future can be resolved only once. It's #{result}, trying to set #{state.result}.", + current_result: result, + new_result: state.result) + end + end + return false + end + + def wait_until_resolved!(timeout = nil) + result = wait_until_resolved(timeout) + raise self if rejected? + result + end + + def async_callback_on_fulfillment(state, executor, args, callback) + with_async(executor, state, args, callback) do |st, ar, cb| + callback_on_fulfillment st, ar, cb + end + end + + def async_callback_on_rejection(state, executor, args, callback) + with_async(executor, state, args, callback) do |st, ar, cb| + callback_on_rejection st, ar, cb + end + end + + def callback_on_fulfillment(state, args, callback) + state.apply args, callback if state.fulfilled? + end + + def callback_on_rejection(state, args, callback) + state.apply args, callback unless state.fulfilled? + end + + def callback_on_resolution(state, args, callback) + callback.call(*state.result, *args) + end + + end + + # Marker module of Future, Event resolved manually. + module Resolvable + include InternalStates + end + + # A Event which can be resolved by user. + class ResolvableEvent < Event + include Resolvable + + # @!macro raise_on_reassign + # @raise [MultipleAssignmentError] when already resolved and raise_on_reassign is true. + + # @!macro promise.param.raise_on_reassign + # @param [Boolean] raise_on_reassign should method raise exception if already resolved + # @return [self, false] false is returner when raise_on_reassign is false and the receiver + # is already resolved. + # + + # Makes the event resolved, which triggers all dependent futures. + # + # @!macro promise.param.raise_on_reassign + # @!macro promise.param.reserved + # @param [true, false] reserved + # Set to true if the resolvable is {#reserve}d by you, + # marks resolution of reserved resolvable events and futures explicitly. + # Advanced feature, ignore unless you use {Resolvable#reserve} from edge. + def resolve(raise_on_reassign = true, reserved = false) + resolve_with RESOLVED, raise_on_reassign, reserved + end + + # Creates new event wrapping receiver, effectively hiding the resolve method. + # + # @return [Event] + def with_hidden_resolvable + @with_hidden_resolvable ||= EventWrapperPromise.new_blocked_by1(self, @DefaultExecutor).event + end + + # Behaves as {AbstractEventFuture#wait} but has one additional optional argument + # resolve_on_timeout. + # + # @param [true, false] resolve_on_timeout + # If it times out and the argument is true it will also resolve the event. + # @return [self, true, false] + # @see AbstractEventFuture#wait + def wait(timeout = nil, resolve_on_timeout = false) + super(timeout) or if resolve_on_timeout + # if it fails to resolve it was resolved in the meantime + # so return true as if there was no timeout + !resolve(false) + else + false + end + end + end + + # A Future which can be resolved by user. + class ResolvableFuture < Future + include Resolvable + + # Makes the future resolved with result of triplet `fulfilled?`, `value`, `reason`, + # which triggers all dependent futures. + # + # @param [true, false] fulfilled + # @param [Object] value + # @param [Object] reason + # @!macro promise.param.raise_on_reassign + # @!macro promise.param.reserved + def resolve(fulfilled = true, value = nil, reason = nil, raise_on_reassign = true, reserved = false) + resolve_with(fulfilled ? Fulfilled.new(value) : Rejected.new(reason), raise_on_reassign, reserved) + end + + # Makes the future fulfilled with `value`, + # which triggers all dependent futures. + # + # @param [Object] value + # @!macro promise.param.raise_on_reassign + # @!macro promise.param.reserved + def fulfill(value, raise_on_reassign = true, reserved = false) + resolve_with Fulfilled.new(value), raise_on_reassign, reserved + end + + # Makes the future rejected with `reason`, + # which triggers all dependent futures. + # + # @param [Object] reason + # @!macro promise.param.raise_on_reassign + # @!macro promise.param.reserved + def reject(reason, raise_on_reassign = true, reserved = false) + resolve_with Rejected.new(reason), raise_on_reassign, reserved + end + + # Evaluates the block and sets its result as future's value fulfilling, if the block raises + # an exception the future rejects with it. + # + # @yield [*args] to the block. + # @yieldreturn [Object] value + # @return [self] + def evaluate_to(*args, &block) + promise.evaluate_to(*args, block) + end + + # Evaluates the block and sets its result as future's value fulfilling, if the block raises + # an exception the future rejects with it. + # + # @yield [*args] to the block. + # @yieldreturn [Object] value + # @return [self] + # @raise [Exception] also raise reason on rejection. + def evaluate_to!(*args, &block) + promise.evaluate_to(*args, block).wait! + end + + # @!macro promises.resolvable.resolve_on_timeout + # @param [::Array(true, Object, nil), ::Array(false, nil, Exception), nil] resolve_on_timeout + # If it times out and the argument is not nil it will also resolve the future + # to the provided resolution. + + # Behaves as {AbstractEventFuture#wait} but has one additional optional argument + # resolve_on_timeout. + # + # @!macro promises.resolvable.resolve_on_timeout + # @return [self, true, false] + # @see AbstractEventFuture#wait + def wait(timeout = nil, resolve_on_timeout = nil) + super(timeout) or if resolve_on_timeout + # if it fails to resolve it was resolved in the meantime + # so return true as if there was no timeout + !resolve(*resolve_on_timeout, false) + else + false + end + end + + # Behaves as {Future#wait!} but has one additional optional argument + # resolve_on_timeout. + # + # @!macro promises.resolvable.resolve_on_timeout + # @return [self, true, false] + # @raise [Exception] {#reason} on rejection + # @see Future#wait! + def wait!(timeout = nil, resolve_on_timeout = nil) + super(timeout) or if resolve_on_timeout + if resolve(*resolve_on_timeout, false) + false + else + # if it fails to resolve it was resolved in the meantime + # so return true as if there was no timeout + raise self if rejected? + true + end + else + false + end + end + + # Behaves as {Future#value} but has one additional optional argument + # resolve_on_timeout. + # + # @!macro promises.resolvable.resolve_on_timeout + # @return [Object, timeout_value, nil] + # @see Future#value + def value(timeout = nil, timeout_value = nil, resolve_on_timeout = nil) + if wait_until_resolved timeout + internal_state.value + else + if resolve_on_timeout + unless resolve(*resolve_on_timeout, false) + # if it fails to resolve it was resolved in the meantime + # so return value as if there was no timeout + return internal_state.value + end + end + timeout_value + end + end + + # Behaves as {Future#value!} but has one additional optional argument + # resolve_on_timeout. + # + # @!macro promises.resolvable.resolve_on_timeout + # @return [Object, timeout_value, nil] + # @raise [Exception] {#reason} on rejection + # @see Future#value! + def value!(timeout = nil, timeout_value = nil, resolve_on_timeout = nil) + if wait_until_resolved! timeout + internal_state.value + else + if resolve_on_timeout + unless resolve(*resolve_on_timeout, false) + # if it fails to resolve it was resolved in the meantime + # so return value as if there was no timeout + raise self if rejected? + return internal_state.value + end + end + timeout_value + end + end + + # Behaves as {Future#reason} but has one additional optional argument + # resolve_on_timeout. + # + # @!macro promises.resolvable.resolve_on_timeout + # @return [Exception, timeout_value, nil] + # @see Future#reason + def reason(timeout = nil, timeout_value = nil, resolve_on_timeout = nil) + if wait_until_resolved timeout + internal_state.reason + else + if resolve_on_timeout + unless resolve(*resolve_on_timeout, false) + # if it fails to resolve it was resolved in the meantime + # so return value as if there was no timeout + return internal_state.reason + end + end + timeout_value + end + end + + # Behaves as {Future#result} but has one additional optional argument + # resolve_on_timeout. + # + # @!macro promises.resolvable.resolve_on_timeout + # @return [::Array(Boolean, Object, Exception), nil] + # @see Future#result + def result(timeout = nil, resolve_on_timeout = nil) + if wait_until_resolved timeout + internal_state.result + else + if resolve_on_timeout + unless resolve(*resolve_on_timeout, false) + # if it fails to resolve it was resolved in the meantime + # so return value as if there was no timeout + internal_state.result + end + end + # otherwise returns nil + end + end + + # Creates new future wrapping receiver, effectively hiding the resolve method and similar. + # + # @return [Future] + def with_hidden_resolvable + @with_hidden_resolvable ||= FutureWrapperPromise.new_blocked_by1(self, @DefaultExecutor).future + end + end + + # @abstract + # @private + class AbstractPromise < Synchronization::Object + safe_initialization! + include InternalStates + + def initialize(future) + super() + @Future = future + end + + def future + @Future + end + + alias_method :event, :future + + def default_executor + future.default_executor + end + + def state + future.state + end + + def touch + end + + def to_s + format '%s %s>', super[0..-2], @Future + end + + alias_method :inspect, :to_s + + def delayed_because + nil + end + + private + + def resolve_with(new_state, raise_on_reassign = true) + @Future.resolve_with(new_state, raise_on_reassign) + end + + # @return [Future] + def evaluate_to(*args, block) + resolve_with Fulfilled.new(block.call(*args)) + rescue Exception => error + resolve_with Rejected.new(error) + raise error unless error.is_a?(StandardError) + end + end + + class ResolvableEventPromise < AbstractPromise + def initialize(default_executor) + super ResolvableEvent.new(self, default_executor) + end + end + + class ResolvableFuturePromise < AbstractPromise + def initialize(default_executor) + super ResolvableFuture.new(self, default_executor) + end + + public :evaluate_to + end + + # @abstract + class InnerPromise < AbstractPromise + end + + # @abstract + class BlockedPromise < InnerPromise + + private_class_method :new + + def self.new_blocked_by1(blocker, *args, &block) + blocker_delayed = blocker.promise.delayed_because + promise = new(blocker_delayed, 1, *args, &block) + blocker.add_callback_notify_blocked promise, 0 + promise + end + + def self.new_blocked_by2(blocker1, blocker2, *args, &block) + blocker_delayed1 = blocker1.promise.delayed_because + blocker_delayed2 = blocker2.promise.delayed_because + delayed = if blocker_delayed1 && blocker_delayed2 + # TODO (pitr-ch 23-Dec-2016): use arrays when we know it will not grow (only flat adds delay) + LockFreeStack.of2(blocker_delayed1, blocker_delayed2) + else + blocker_delayed1 || blocker_delayed2 + end + promise = new(delayed, 2, *args, &block) + blocker1.add_callback_notify_blocked promise, 0 + blocker2.add_callback_notify_blocked promise, 1 + promise + end + + def self.new_blocked_by(blockers, *args, &block) + delayed = blockers.reduce(nil) { |d, f| add_delayed d, f.promise.delayed_because } + promise = new(delayed, blockers.size, *args, &block) + blockers.each_with_index { |f, i| f.add_callback_notify_blocked promise, i } + promise + end + + def self.add_delayed(delayed1, delayed2) + if delayed1 && delayed2 + delayed1.push delayed2 + delayed1 + else + delayed1 || delayed2 + end + end + + def initialize(delayed, blockers_count, future) + super(future) + @Delayed = delayed + @Countdown = AtomicFixnum.new blockers_count + end + + def on_blocker_resolution(future, index) + countdown = process_on_blocker_resolution(future, index) + resolvable = resolvable?(countdown, future, index) + + on_resolvable(future, index) if resolvable + end + + def delayed_because + @Delayed + end + + def touch + clear_and_propagate_touch + end + + # for inspection only + def blocked_by + blocked_by = [] + ObjectSpace.each_object(AbstractEventFuture) { |o| blocked_by.push o if o.blocks.include? self } + blocked_by + end + + private + + def clear_and_propagate_touch(stack_or_element = @Delayed) + return if stack_or_element.nil? + + if stack_or_element.is_a? LockFreeStack + stack_or_element.clear_each { |element| clear_and_propagate_touch element } + else + stack_or_element.touch unless stack_or_element.nil? # if still present + end + end + + # @return [true,false] if resolvable + def resolvable?(countdown, future, index) + countdown.zero? + end + + def process_on_blocker_resolution(future, index) + @Countdown.decrement + end + + def on_resolvable(resolved_future, index) + raise NotImplementedError + end + end + + # @abstract + class BlockedTaskPromise < BlockedPromise + def initialize(delayed, blockers_count, default_executor, executor, args, &task) + raise ArgumentError, 'no block given' unless block_given? + super delayed, 1, Future.new(self, default_executor) + @Executor = executor + @Task = task + @Args = args + end + + def executor + @Executor + end + end + + class ThenPromise < BlockedTaskPromise + private + + def initialize(delayed, blockers_count, default_executor, executor, args, &task) + super delayed, blockers_count, default_executor, executor, args, &task + end + + def on_resolvable(resolved_future, index) + if resolved_future.fulfilled? + Concurrent.executor(@Executor).post(resolved_future, @Args, @Task) do |future, args, task| + evaluate_to lambda { future.apply args, task } + end + else + resolve_with resolved_future.internal_state + end + end + end + + class RescuePromise < BlockedTaskPromise + private + + def initialize(delayed, blockers_count, default_executor, executor, args, &task) + super delayed, blockers_count, default_executor, executor, args, &task + end + + def on_resolvable(resolved_future, index) + if resolved_future.rejected? + Concurrent.executor(@Executor).post(resolved_future, @Args, @Task) do |future, args, task| + evaluate_to lambda { future.apply args, task } + end + else + resolve_with resolved_future.internal_state + end + end + end + + class ChainPromise < BlockedTaskPromise + private + + def on_resolvable(resolved_future, index) + if Future === resolved_future + Concurrent.executor(@Executor).post(resolved_future, @Args, @Task) do |future, args, task| + evaluate_to(*future.result, *args, task) + end + else + Concurrent.executor(@Executor).post(@Args, @Task) do |args, task| + evaluate_to(*args, task) + end + end + end + end + + # will be immediately resolved + class ImmediateEventPromise < InnerPromise + def initialize(default_executor) + super Event.new(self, default_executor).resolve_with(RESOLVED) + end + end + + class ImmediateFuturePromise < InnerPromise + def initialize(default_executor, fulfilled, value, reason) + super Future.new(self, default_executor). + resolve_with(fulfilled ? Fulfilled.new(value) : Rejected.new(reason)) + end + end + + class AbstractFlatPromise < BlockedPromise + + def initialize(delayed_because, blockers_count, event_or_future) + delayed = LockFreeStack.of1(self) + super(delayed, blockers_count, event_or_future) + # noinspection RubyArgCount + @Touched = AtomicBoolean.new false + @DelayedBecause = delayed_because || LockFreeStack.new + + event_or_future.add_callback_clear_delayed_node delayed.peek + end + + def touch + if @Touched.make_true + clear_and_propagate_touch @DelayedBecause + end + end + + private + + def touched? + @Touched.value + end + + def on_resolvable(resolved_future, index) + resolve_with resolved_future.internal_state + end + + def resolvable?(countdown, future, index) + !@Future.internal_state.resolved? && super(countdown, future, index) + end + + def add_delayed_of(future) + delayed = future.promise.delayed_because + if touched? + clear_and_propagate_touch delayed + else + BlockedPromise.add_delayed @DelayedBecause, delayed + clear_and_propagate_touch @DelayedBecause if touched? + end + end + + end + + class FlatEventPromise < AbstractFlatPromise + + private + + def initialize(delayed, blockers_count, default_executor) + super delayed, 2, Event.new(self, default_executor) + end + + def process_on_blocker_resolution(future, index) + countdown = super(future, index) + if countdown.nonzero? + internal_state = future.internal_state + + unless internal_state.fulfilled? + resolve_with RESOLVED + return countdown + end + + value = internal_state.value + case value + when AbstractEventFuture + add_delayed_of value + value.add_callback_notify_blocked self, nil + countdown + else + resolve_with RESOLVED + end + end + countdown + end + + end + + class FlatFuturePromise < AbstractFlatPromise + + private + + def initialize(delayed, blockers_count, levels, default_executor) + raise ArgumentError, 'levels has to be higher than 0' if levels < 1 + # flat promise may result to a future having delayed futures, therefore we have to have empty stack + # to be able to add new delayed futures + super delayed || LockFreeStack.new, 1 + levels, Future.new(self, default_executor) + end + + def process_on_blocker_resolution(future, index) + countdown = super(future, index) + if countdown.nonzero? + internal_state = future.internal_state + + unless internal_state.fulfilled? + resolve_with internal_state + return countdown + end + + value = internal_state.value + case value + when AbstractEventFuture + add_delayed_of value + value.add_callback_notify_blocked self, nil + countdown + else + evaluate_to(lambda { raise TypeError, "returned value #{value.inspect} is not a Future" }) + end + end + countdown + end + + end + + class RunFuturePromise < AbstractFlatPromise + + private + + def initialize(delayed, blockers_count, default_executor, run_test) + super delayed, 1, Future.new(self, default_executor) + @RunTest = run_test + end + + def process_on_blocker_resolution(future, index) + internal_state = future.internal_state + + unless internal_state.fulfilled? + resolve_with internal_state + return 0 + end + + value = internal_state.value + continuation_future = @RunTest.call value + + if continuation_future + add_delayed_of continuation_future + continuation_future.add_callback_notify_blocked self, nil + else + resolve_with internal_state + end + + 1 + end + end + + class ZipEventEventPromise < BlockedPromise + def initialize(delayed, blockers_count, default_executor) + super delayed, 2, Event.new(self, default_executor) + end + + private + + def on_resolvable(resolved_future, index) + resolve_with RESOLVED + end + end + + class ZipFutureEventPromise < BlockedPromise + def initialize(delayed, blockers_count, default_executor) + super delayed, 2, Future.new(self, default_executor) + @result = nil + end + + private + + def process_on_blocker_resolution(future, index) + # first blocking is future, take its result + @result = future.internal_state if index == 0 + # super has to be called after above to piggyback on volatile @Countdown + super future, index + end + + def on_resolvable(resolved_future, index) + resolve_with @result + end + end + + class EventWrapperPromise < BlockedPromise + def initialize(delayed, blockers_count, default_executor) + super delayed, 1, Event.new(self, default_executor) + end + + private + + def on_resolvable(resolved_future, index) + resolve_with RESOLVED + end + end + + class FutureWrapperPromise < BlockedPromise + def initialize(delayed, blockers_count, default_executor) + super delayed, 1, Future.new(self, default_executor) + end + + private + + def on_resolvable(resolved_future, index) + resolve_with resolved_future.internal_state + end + end + + class ZipFuturesPromise < BlockedPromise + + private + + def initialize(delayed, blockers_count, default_executor) + super(delayed, blockers_count, Future.new(self, default_executor)) + @Resolutions = ::Array.new(blockers_count, nil) + + on_resolvable nil, nil if blockers_count == 0 + end + + def process_on_blocker_resolution(future, index) + # TODO (pitr-ch 18-Dec-2016): Can we assume that array will never break under parallel access when never re-sized? + @Resolutions[index] = future.internal_state # has to be set before countdown in super + super future, index + end + + def on_resolvable(resolved_future, index) + all_fulfilled = true + values = ::Array.new(@Resolutions.size) + reasons = ::Array.new(@Resolutions.size) + + @Resolutions.each_with_index do |internal_state, i| + fulfilled, values[i], reasons[i] = internal_state.result + all_fulfilled &&= fulfilled + end + + if all_fulfilled + resolve_with FulfilledArray.new(values) + else + resolve_with PartiallyRejected.new(values, reasons) + end + end + end + + class ZipEventsPromise < BlockedPromise + + private + + def initialize(delayed, blockers_count, default_executor) + super delayed, blockers_count, Event.new(self, default_executor) + + on_resolvable nil, nil if blockers_count == 0 + end + + def on_resolvable(resolved_future, index) + resolve_with RESOLVED + end + end + + # @abstract + class AbstractAnyPromise < BlockedPromise + end + + class AnyResolvedEventPromise < AbstractAnyPromise + + private + + def initialize(delayed, blockers_count, default_executor) + super delayed, blockers_count, Event.new(self, default_executor) + end + + def resolvable?(countdown, future, index) + true + end + + def on_resolvable(resolved_future, index) + resolve_with RESOLVED, false + end + end + + class AnyResolvedFuturePromise < AbstractAnyPromise + + private + + def initialize(delayed, blockers_count, default_executor) + super delayed, blockers_count, Future.new(self, default_executor) + end + + def resolvable?(countdown, future, index) + true + end + + def on_resolvable(resolved_future, index) + resolve_with resolved_future.internal_state, false + end + end + + class AnyFulfilledFuturePromise < AnyResolvedFuturePromise + + private + + def resolvable?(countdown, future, index) + future.fulfilled? || + # inlined super from BlockedPromise + countdown.zero? + end + end + + class DelayPromise < InnerPromise + + def initialize(default_executor) + event = Event.new(self, default_executor) + @Delayed = LockFreeStack.of1(self) + super event + event.add_callback_clear_delayed_node @Delayed.peek + end + + def touch + @Future.resolve_with RESOLVED + end + + def delayed_because + @Delayed + end + + end + + class ScheduledPromise < InnerPromise + def intended_time + @IntendedTime + end + + def inspect + "#{to_s[0..-2]} intended_time: #{@IntendedTime}>" + end + + private + + def initialize(default_executor, intended_time) + super Event.new(self, default_executor) + + @IntendedTime = intended_time + + in_seconds = begin + now = Time.now + schedule_time = if @IntendedTime.is_a? Time + @IntendedTime + else + now + @IntendedTime + end + [0, schedule_time.to_f - now.to_f].max + end + + Concurrent.global_timer_set.post(in_seconds) do + @Future.resolve_with RESOLVED + end + end + end + + extend FactoryMethods + + private_constant :AbstractPromise, + :ResolvableEventPromise, + :ResolvableFuturePromise, + :InnerPromise, + :BlockedPromise, + :BlockedTaskPromise, + :ThenPromise, + :RescuePromise, + :ChainPromise, + :ImmediateEventPromise, + :ImmediateFuturePromise, + :AbstractFlatPromise, + :FlatFuturePromise, + :FlatEventPromise, + :RunFuturePromise, + :ZipEventEventPromise, + :ZipFutureEventPromise, + :EventWrapperPromise, + :FutureWrapperPromise, + :ZipFuturesPromise, + :ZipEventsPromise, + :AbstractAnyPromise, + :AnyResolvedFuturePromise, + :AnyFulfilledFuturePromise, + :AnyResolvedEventPromise, + :DelayPromise, + :ScheduledPromise + + + end +end diff --git a/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/re_include.rb b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/re_include.rb new file mode 100644 index 0000000..516d58c --- /dev/null +++ b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/re_include.rb @@ -0,0 +1,58 @@ +module Concurrent + + # Methods form module A included to a module B, which is already included into class C, + # will not be visible in the C class. If this module is extended to B then A's methods + # are correctly made visible to C. + # + # @example + # module A + # def a + # :a + # end + # end + # + # module B1 + # end + # + # class C1 + # include B1 + # end + # + # module B2 + # extend Concurrent::ReInclude + # end + # + # class C2 + # include B2 + # end + # + # B1.send :include, A + # B2.send :include, A + # + # C1.new.respond_to? :a # => false + # C2.new.respond_to? :a # => true + module ReInclude + # @!visibility private + def included(base) + (@re_include_to_bases ||= []) << [:include, base] + super(base) + end + + # @!visibility private + def extended(base) + (@re_include_to_bases ||= []) << [:extend, base] + super(base) + end + + # @!visibility private + def include(*modules) + result = super(*modules) + modules.reverse.each do |module_being_included| + (@re_include_to_bases ||= []).each do |method, mod| + mod.send method, module_being_included + end + end + result + end + end +end diff --git a/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/scheduled_task.rb b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/scheduled_task.rb new file mode 100644 index 0000000..90f78b0 --- /dev/null +++ b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/scheduled_task.rb @@ -0,0 +1,318 @@ +require 'concurrent/constants' +require 'concurrent/errors' +require 'concurrent/configuration' +require 'concurrent/ivar' +require 'concurrent/collection/copy_on_notify_observer_set' +require 'concurrent/utility/monotonic_time' + +require 'concurrent/options' + +module Concurrent + + # `ScheduledTask` is a close relative of `Concurrent::Future` but with one + # important difference: A `Future` is set to execute as soon as possible + # whereas a `ScheduledTask` is set to execute after a specified delay. This + # implementation is loosely based on Java's + # [ScheduledExecutorService](http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/ScheduledExecutorService.html). + # It is a more feature-rich variant of {Concurrent.timer}. + # + # The *intended* schedule time of task execution is set on object construction + # with the `delay` argument. The delay is a numeric (floating point or integer) + # representing a number of seconds in the future. Any other value or a numeric + # equal to or less than zero will result in an exception. The *actual* schedule + # time of task execution is set when the `execute` method is called. + # + # The constructor can also be given zero or more processing options. Currently + # the only supported options are those recognized by the + # [Dereferenceable](Dereferenceable) module. + # + # The final constructor argument is a block representing the task to be performed. + # If no block is given an `ArgumentError` will be raised. + # + # **States** + # + # `ScheduledTask` mixes in the [Obligation](Obligation) module thus giving it + # "future" behavior. This includes the expected lifecycle states. `ScheduledTask` + # has one additional state, however. While the task (block) is being executed the + # state of the object will be `:processing`. This additional state is necessary + # because it has implications for task cancellation. + # + # **Cancellation** + # + # A `:pending` task can be cancelled using the `#cancel` method. A task in any + # other state, including `:processing`, cannot be cancelled. The `#cancel` + # method returns a boolean indicating the success of the cancellation attempt. + # A cancelled `ScheduledTask` cannot be restarted. It is immutable. + # + # **Obligation and Observation** + # + # The result of a `ScheduledTask` can be obtained either synchronously or + # asynchronously. `ScheduledTask` mixes in both the [Obligation](Obligation) + # module and the + # [Observable](http://ruby-doc.org/stdlib-2.0/libdoc/observer/rdoc/Observable.html) + # module from the Ruby standard library. With one exception `ScheduledTask` + # behaves identically to [Future](Observable) with regard to these modules. + # + # @!macro copy_options + # + # @example Basic usage + # + # require 'concurrent' + # require 'thread' # for Queue + # require 'open-uri' # for open(uri) + # + # class Ticker + # def get_year_end_closing(symbol, year) + # uri = "http://ichart.finance.yahoo.com/table.csv?s=#{symbol}&a=11&b=01&c=#{year}&d=11&e=31&f=#{year}&g=m" + # data = open(uri) {|f| f.collect{|line| line.strip } } + # data[1].split(',')[4].to_f + # end + # end + # + # # Future + # price = Concurrent::Future.execute{ Ticker.new.get_year_end_closing('TWTR', 2013) } + # price.state #=> :pending + # sleep(1) # do other stuff + # price.value #=> 63.65 + # price.state #=> :fulfilled + # + # # ScheduledTask + # task = Concurrent::ScheduledTask.execute(2){ Ticker.new.get_year_end_closing('INTC', 2013) } + # task.state #=> :pending + # sleep(3) # do other stuff + # task.value #=> 25.96 + # + # @example Successful task execution + # + # task = Concurrent::ScheduledTask.new(2){ 'What does the fox say?' } + # task.state #=> :unscheduled + # task.execute + # task.state #=> pending + # + # # wait for it... + # sleep(3) + # + # task.unscheduled? #=> false + # task.pending? #=> false + # task.fulfilled? #=> true + # task.rejected? #=> false + # task.value #=> 'What does the fox say?' + # + # @example One line creation and execution + # + # task = Concurrent::ScheduledTask.new(2){ 'What does the fox say?' }.execute + # task.state #=> pending + # + # task = Concurrent::ScheduledTask.execute(2){ 'What do you get when you multiply 6 by 9?' } + # task.state #=> pending + # + # @example Failed task execution + # + # task = Concurrent::ScheduledTask.execute(2){ raise StandardError.new('Call me maybe?') } + # task.pending? #=> true + # + # # wait for it... + # sleep(3) + # + # task.unscheduled? #=> false + # task.pending? #=> false + # task.fulfilled? #=> false + # task.rejected? #=> true + # task.value #=> nil + # task.reason #=> # + # + # @example Task execution with observation + # + # observer = Class.new{ + # def update(time, value, reason) + # puts "The task completed at #{time} with value '#{value}'" + # end + # }.new + # + # task = Concurrent::ScheduledTask.new(2){ 'What does the fox say?' } + # task.add_observer(observer) + # task.execute + # task.pending? #=> true + # + # # wait for it... + # sleep(3) + # + # #>> The task completed at 2013-11-07 12:26:09 -0500 with value 'What does the fox say?' + # + # @!macro monotonic_clock_warning + # + # @see Concurrent.timer + class ScheduledTask < IVar + include Comparable + + # The executor on which to execute the task. + # @!visibility private + attr_reader :executor + + # Schedule a task for execution at a specified future time. + # + # @param [Float] delay the number of seconds to wait for before executing the task + # + # @yield the task to be performed + # + # @!macro executor_and_deref_options + # + # @option opts [object, Array] :args zero or more arguments to be passed the task + # block on execution + # + # @raise [ArgumentError] When no block is given + # @raise [ArgumentError] When given a time that is in the past + def initialize(delay, opts = {}, &task) + raise ArgumentError.new('no block given') unless block_given? + raise ArgumentError.new('seconds must be greater than zero') if delay.to_f < 0.0 + + super(NULL, opts, &nil) + + synchronize do + ns_set_state(:unscheduled) + @parent = opts.fetch(:timer_set, Concurrent.global_timer_set) + @args = get_arguments_from(opts) + @delay = delay.to_f + @task = task + @time = nil + @executor = Options.executor_from_options(opts) || Concurrent.global_io_executor + self.observers = Collection::CopyOnNotifyObserverSet.new + end + end + + # The `delay` value given at instanciation. + # + # @return [Float] the initial delay. + def initial_delay + synchronize { @delay } + end + + # The monotonic time at which the the task is scheduled to be executed. + # + # @return [Float] the schedule time or nil if `unscheduled` + def schedule_time + synchronize { @time } + end + + # Comparator which orders by schedule time. + # + # @!visibility private + def <=>(other) + schedule_time <=> other.schedule_time + end + + # Has the task been cancelled? + # + # @return [Boolean] true if the task is in the given state else false + def cancelled? + synchronize { ns_check_state?(:cancelled) } + end + + # In the task execution in progress? + # + # @return [Boolean] true if the task is in the given state else false + def processing? + synchronize { ns_check_state?(:processing) } + end + + # Cancel this task and prevent it from executing. A task can only be + # cancelled if it is pending or unscheduled. + # + # @return [Boolean] true if successfully cancelled else false + def cancel + if compare_and_set_state(:cancelled, :pending, :unscheduled) + complete(false, nil, CancelledOperationError.new) + # To avoid deadlocks this call must occur outside of #synchronize + # Changing the state above should prevent redundant calls + @parent.send(:remove_task, self) + else + false + end + end + + # Reschedule the task using the original delay and the current time. + # A task can only be reset while it is `:pending`. + # + # @return [Boolean] true if successfully rescheduled else false + def reset + synchronize{ ns_reschedule(@delay) } + end + + # Reschedule the task using the given delay and the current time. + # A task can only be reset while it is `:pending`. + # + # @param [Float] delay the number of seconds to wait for before executing the task + # + # @return [Boolean] true if successfully rescheduled else false + # + # @raise [ArgumentError] When given a time that is in the past + def reschedule(delay) + delay = delay.to_f + raise ArgumentError.new('seconds must be greater than zero') if delay < 0.0 + synchronize{ ns_reschedule(delay) } + end + + # Execute an `:unscheduled` `ScheduledTask`. Immediately sets the state to `:pending` + # and starts counting down toward execution. Does nothing if the `ScheduledTask` is + # in any state other than `:unscheduled`. + # + # @return [ScheduledTask] a reference to `self` + def execute + if compare_and_set_state(:pending, :unscheduled) + synchronize{ ns_schedule(@delay) } + end + self + end + + # Create a new `ScheduledTask` object with the given block, execute it, and return the + # `:pending` object. + # + # @param [Float] delay the number of seconds to wait for before executing the task + # + # @!macro executor_and_deref_options + # + # @return [ScheduledTask] the newly created `ScheduledTask` in the `:pending` state + # + # @raise [ArgumentError] if no block is given + def self.execute(delay, opts = {}, &task) + new(delay, opts, &task).execute + end + + # Execute the task. + # + # @!visibility private + def process_task + safe_execute(@task, @args) + end + + protected :set, :try_set, :fail, :complete + + protected + + # Schedule the task using the given delay and the current time. + # + # @param [Float] delay the number of seconds to wait for before executing the task + # + # @return [Boolean] true if successfully rescheduled else false + # + # @!visibility private + def ns_schedule(delay) + @delay = delay + @time = Concurrent.monotonic_time + @delay + @parent.send(:post_task, self) + end + + # Reschedule the task using the given delay and the current time. + # A task can only be reset while it is `:pending`. + # + # @param [Float] delay the number of seconds to wait for before executing the task + # + # @return [Boolean] true if successfully rescheduled else false + # + # @!visibility private + def ns_reschedule(delay) + return false unless ns_check_state?(:pending) + @parent.send(:remove_task, self) && ns_schedule(delay) + end + end +end diff --git a/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/set.rb b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/set.rb new file mode 100644 index 0000000..04dc936 --- /dev/null +++ b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/set.rb @@ -0,0 +1,66 @@ +require 'concurrent/utility/engine' +require 'concurrent/thread_safe/util' +require 'set' + +module Concurrent + + # @!macro concurrent_set + # + # A thread-safe subclass of Set. This version locks against the object + # itself for every method call, ensuring only one thread can be reading + # or writing at a time. This includes iteration methods like `#each`. + # + # @note `a += b` is **not** a **thread-safe** operation on + # `Concurrent::Set`. It reads Set `a`, then it creates new `Concurrent::Set` + # which is union of `a` and `b`, then it writes the union to `a`. + # The read and write are independent operations they do not form a single atomic + # operation therefore when two `+=` operations are executed concurrently updates + # may be lost. Use `#merge` instead. + # + # @see http://ruby-doc.org/stdlib-2.4.0/libdoc/set/rdoc/Set.html Ruby standard library `Set` + + + # @!macro internal_implementation_note + SetImplementation = case + when Concurrent.on_cruby? + # Because MRI never runs code in parallel, the existing + # non-thread-safe structures should usually work fine. + ::Set + + when Concurrent.on_jruby? + require 'jruby/synchronized' + + class JRubySet < ::Set + include JRuby::Synchronized + end + JRubySet + + when Concurrent.on_rbx? + require 'monitor' + require 'concurrent/thread_safe/util/data_structures' + + class RbxSet < ::Set + end + ThreadSafe::Util.make_synchronized_on_rbx Concurrent::RbxSet + RbxSet + + when Concurrent.on_truffleruby? + require 'concurrent/thread_safe/util/data_structures' + + class TruffleRubySet < ::Set + end + + ThreadSafe::Util.make_synchronized_on_truffleruby Concurrent::TruffleRubySet + TruffleRubySet + + else + warn 'Possibly unsupported Ruby implementation' + ::Set + end + private_constant :SetImplementation + + # @!macro concurrent_set + class Set < SetImplementation + end +end + diff --git a/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/settable_struct.rb b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/settable_struct.rb new file mode 100644 index 0000000..9706cff --- /dev/null +++ b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/settable_struct.rb @@ -0,0 +1,129 @@ +require 'concurrent/synchronization/abstract_struct' +require 'concurrent/errors' +require 'concurrent/synchronization' + +module Concurrent + + # An thread-safe, write-once variation of Ruby's standard `Struct`. + # Each member can have its value set at most once, either at construction + # or any time thereafter. Attempting to assign a value to a member + # that has already been set will result in a `Concurrent::ImmutabilityError`. + # + # @see http://ruby-doc.org/core-2.2.0/Struct.html Ruby standard library `Struct` + # @see http://en.wikipedia.org/wiki/Final_(Java) Java `final` keyword + module SettableStruct + include Synchronization::AbstractStruct + + # @!macro struct_values + def values + synchronize { ns_values } + end + alias_method :to_a, :values + + # @!macro struct_values_at + def values_at(*indexes) + synchronize { ns_values_at(indexes) } + end + + # @!macro struct_inspect + def inspect + synchronize { ns_inspect } + end + alias_method :to_s, :inspect + + # @!macro struct_merge + def merge(other, &block) + synchronize { ns_merge(other, &block) } + end + + # @!macro struct_to_h + def to_h + synchronize { ns_to_h } + end + + # @!macro struct_get + def [](member) + synchronize { ns_get(member) } + end + + # @!macro struct_equality + def ==(other) + synchronize { ns_equality(other) } + end + + # @!macro struct_each + def each(&block) + return enum_for(:each) unless block_given? + synchronize { ns_each(&block) } + end + + # @!macro struct_each_pair + def each_pair(&block) + return enum_for(:each_pair) unless block_given? + synchronize { ns_each_pair(&block) } + end + + # @!macro struct_select + def select(&block) + return enum_for(:select) unless block_given? + synchronize { ns_select(&block) } + end + + # @!macro struct_set + # + # @raise [Concurrent::ImmutabilityError] if the given member has already been set + def []=(member, value) + if member.is_a? Integer + length = synchronize { @values.length } + if member >= length + raise IndexError.new("offset #{member} too large for struct(size:#{length})") + end + synchronize do + unless @values[member].nil? + raise Concurrent::ImmutabilityError.new('struct member has already been set') + end + @values[member] = value + end + else + send("#{member}=", value) + end + rescue NoMethodError + raise NameError.new("no member '#{member}' in struct") + end + + # @!macro struct_new + def self.new(*args, &block) + clazz_name = nil + if args.length == 0 + raise ArgumentError.new('wrong number of arguments (0 for 1+)') + elsif args.length > 0 && args.first.is_a?(String) + clazz_name = args.shift + end + FACTORY.define_struct(clazz_name, args, &block) + end + + FACTORY = Class.new(Synchronization::LockableObject) do + def define_struct(name, members, &block) + synchronize do + clazz = Synchronization::AbstractStruct.define_struct_class(SettableStruct, Synchronization::LockableObject, name, members, &block) + members.each_with_index do |member, index| + clazz.send :remove_method, member if clazz.instance_methods.include? member + clazz.send(:define_method, member) do + synchronize { @values[index] } + end + clazz.send(:define_method, "#{member}=") do |value| + synchronize do + unless @values[index].nil? + raise Concurrent::ImmutabilityError.new('struct member has already been set') + end + @values[index] = value + end + end + end + clazz + end + end + end.new + private_constant :FACTORY + end +end diff --git a/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/synchronization.rb b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/synchronization.rb new file mode 100644 index 0000000..49c68eb --- /dev/null +++ b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/synchronization.rb @@ -0,0 +1,30 @@ +require 'concurrent/utility/engine' + +require 'concurrent/synchronization/abstract_object' +require 'concurrent/utility/native_extension_loader' # load native parts first +Concurrent.load_native_extensions + +require 'concurrent/synchronization/mri_object' +require 'concurrent/synchronization/jruby_object' +require 'concurrent/synchronization/rbx_object' +require 'concurrent/synchronization/truffleruby_object' +require 'concurrent/synchronization/object' +require 'concurrent/synchronization/volatile' + +require 'concurrent/synchronization/abstract_lockable_object' +require 'concurrent/synchronization/mutex_lockable_object' +require 'concurrent/synchronization/jruby_lockable_object' +require 'concurrent/synchronization/rbx_lockable_object' + +require 'concurrent/synchronization/lockable_object' + +require 'concurrent/synchronization/condition' +require 'concurrent/synchronization/lock' + +module Concurrent + # {include:file:docs-source/synchronization.md} + # {include:file:docs-source/synchronization-notes.md} + module Synchronization + end +end + diff --git a/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/synchronization/abstract_lockable_object.rb b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/synchronization/abstract_lockable_object.rb new file mode 100644 index 0000000..bc12603 --- /dev/null +++ b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/synchronization/abstract_lockable_object.rb @@ -0,0 +1,98 @@ +module Concurrent + module Synchronization + + # @!visibility private + class AbstractLockableObject < Synchronization::Object + + protected + + # @!macro synchronization_object_method_synchronize + # + # @yield runs the block synchronized against this object, + # equivalent of java's `synchronize(this) {}` + # @note can by made public in descendants if required by `public :synchronize` + def synchronize + raise NotImplementedError + end + + # @!macro synchronization_object_method_ns_wait_until + # + # Wait until condition is met or timeout passes, + # protects against spurious wake-ups. + # @param [Numeric, nil] timeout in seconds, `nil` means no timeout + # @yield condition to be met + # @yieldreturn [true, false] + # @return [true, false] if condition met + # @note only to be used inside synchronized block + # @note to provide direct access to this method in a descendant add method + # ``` + # def wait_until(timeout = nil, &condition) + # synchronize { ns_wait_until(timeout, &condition) } + # end + # ``` + def ns_wait_until(timeout = nil, &condition) + if timeout + wait_until = Concurrent.monotonic_time + timeout + loop do + now = Concurrent.monotonic_time + condition_result = condition.call + return condition_result if now >= wait_until || condition_result + ns_wait wait_until - now + end + else + ns_wait timeout until condition.call + true + end + end + + # @!macro synchronization_object_method_ns_wait + # + # Wait until another thread calls #signal or #broadcast, + # spurious wake-ups can happen. + # + # @param [Numeric, nil] timeout in seconds, `nil` means no timeout + # @return [self] + # @note only to be used inside synchronized block + # @note to provide direct access to this method in a descendant add method + # ``` + # def wait(timeout = nil) + # synchronize { ns_wait(timeout) } + # end + # ``` + def ns_wait(timeout = nil) + raise NotImplementedError + end + + # @!macro synchronization_object_method_ns_signal + # + # Signal one waiting thread. + # @return [self] + # @note only to be used inside synchronized block + # @note to provide direct access to this method in a descendant add method + # ``` + # def signal + # synchronize { ns_signal } + # end + # ``` + def ns_signal + raise NotImplementedError + end + + # @!macro synchronization_object_method_ns_broadcast + # + # Broadcast to all waiting threads. + # @return [self] + # @note only to be used inside synchronized block + # @note to provide direct access to this method in a descendant add method + # ``` + # def broadcast + # synchronize { ns_broadcast } + # end + # ``` + def ns_broadcast + raise NotImplementedError + end + + end + end +end diff --git a/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/synchronization/abstract_object.rb b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/synchronization/abstract_object.rb new file mode 100644 index 0000000..532388b --- /dev/null +++ b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/synchronization/abstract_object.rb @@ -0,0 +1,24 @@ +module Concurrent + module Synchronization + + # @!visibility private + # @!macro internal_implementation_note + class AbstractObject + + # @abstract has to be implemented based on Ruby runtime + def initialize + raise NotImplementedError + end + + # @!visibility private + # @abstract + def full_memory_barrier + raise NotImplementedError + end + + def self.attr_volatile(*names) + raise NotImplementedError + end + end + end +end diff --git a/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/synchronization/abstract_struct.rb b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/synchronization/abstract_struct.rb new file mode 100644 index 0000000..d94f657 --- /dev/null +++ b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/synchronization/abstract_struct.rb @@ -0,0 +1,160 @@ +module Concurrent + module Synchronization + + # @!visibility private + # @!macro internal_implementation_note + module AbstractStruct + + # @!visibility private + def initialize(*values) + super() + ns_initialize(*values) + end + + # @!macro struct_length + # + # Returns the number of struct members. + # + # @return [Fixnum] the number of struct members + def length + self.class::MEMBERS.length + end + alias_method :size, :length + + # @!macro struct_members + # + # Returns the struct members as an array of symbols. + # + # @return [Array] the struct members as an array of symbols + def members + self.class::MEMBERS.dup + end + + protected + + # @!macro struct_values + # + # @!visibility private + def ns_values + @values.dup + end + + # @!macro struct_values_at + # + # @!visibility private + def ns_values_at(indexes) + @values.values_at(*indexes) + end + + # @!macro struct_to_h + # + # @!visibility private + def ns_to_h + length.times.reduce({}){|memo, i| memo[self.class::MEMBERS[i]] = @values[i]; memo} + end + + # @!macro struct_get + # + # @!visibility private + def ns_get(member) + if member.is_a? Integer + if member >= @values.length + raise IndexError.new("offset #{member} too large for struct(size:#{@values.length})") + end + @values[member] + else + send(member) + end + rescue NoMethodError + raise NameError.new("no member '#{member}' in struct") + end + + # @!macro struct_equality + # + # @!visibility private + def ns_equality(other) + self.class == other.class && self.values == other.values + end + + # @!macro struct_each + # + # @!visibility private + def ns_each + values.each{|value| yield value } + end + + # @!macro struct_each_pair + # + # @!visibility private + def ns_each_pair + @values.length.times do |index| + yield self.class::MEMBERS[index], @values[index] + end + end + + # @!macro struct_select + # + # @!visibility private + def ns_select + values.select{|value| yield value } + end + + # @!macro struct_inspect + # + # @!visibility private + def ns_inspect + struct = pr_underscore(self.class.ancestors[1]) + clazz = ((self.class.to_s =~ /^#" + end + + # @!macro struct_merge + # + # @!visibility private + def ns_merge(other, &block) + self.class.new(*self.to_h.merge(other, &block).values) + end + + # @!visibility private + def pr_underscore(clazz) + word = clazz.to_s.dup # dup string to workaround JRuby 9.2.0.0 bug https://github.com/jruby/jruby/issues/5229 + word.gsub!(/::/, '/') + word.gsub!(/([A-Z]+)([A-Z][a-z])/,'\1_\2') + word.gsub!(/([a-z\d])([A-Z])/,'\1_\2') + word.tr!("-", "_") + word.downcase! + word + end + + # @!visibility private + def self.define_struct_class(parent, base, name, members, &block) + clazz = Class.new(base || Object) do + include parent + self.const_set(:MEMBERS, members.collect{|member| member.to_s.to_sym}.freeze) + def ns_initialize(*values) + raise ArgumentError.new('struct size differs') if values.length > length + @values = values.fill(nil, values.length..length-1) + end + end + unless name.nil? + begin + parent.send :remove_const, name if parent.const_defined?(name, false) + parent.const_set(name, clazz) + clazz + rescue NameError + raise NameError.new("identifier #{name} needs to be constant") + end + end + members.each_with_index do |member, index| + clazz.send :remove_method, member if clazz.instance_methods.include? member + clazz.send(:define_method, member) do + @values[index] + end + end + clazz.class_exec(&block) unless block.nil? + clazz.singleton_class.send :alias_method, :[], :new + clazz + end + end + end +end diff --git a/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/synchronization/condition.rb b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/synchronization/condition.rb new file mode 100644 index 0000000..f704b81 --- /dev/null +++ b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/synchronization/condition.rb @@ -0,0 +1,60 @@ +module Concurrent + module Synchronization + + # @!visibility private + # TODO (pitr-ch 04-Dec-2016): should be in edge + class Condition < LockableObject + safe_initialization! + + # TODO (pitr 12-Sep-2015): locks two objects, improve + # TODO (pitr 26-Sep-2015): study + # http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/8-b132/java/util/concurrent/locks/AbstractQueuedSynchronizer.java#AbstractQueuedSynchronizer.Node + + singleton_class.send :alias_method, :private_new, :new + private_class_method :new + + def initialize(lock) + super() + @Lock = lock + end + + def wait(timeout = nil) + @Lock.synchronize { ns_wait(timeout) } + end + + def ns_wait(timeout = nil) + synchronize { super(timeout) } + end + + def wait_until(timeout = nil, &condition) + @Lock.synchronize { ns_wait_until(timeout, &condition) } + end + + def ns_wait_until(timeout = nil, &condition) + synchronize { super(timeout, &condition) } + end + + def signal + @Lock.synchronize { ns_signal } + end + + def ns_signal + synchronize { super } + end + + def broadcast + @Lock.synchronize { ns_broadcast } + end + + def ns_broadcast + synchronize { super } + end + end + + class LockableObject < LockableObjectImplementation + def new_condition + Condition.private_new(self) + end + end + end +end diff --git a/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/synchronization/jruby_lockable_object.rb b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/synchronization/jruby_lockable_object.rb new file mode 100644 index 0000000..359a032 --- /dev/null +++ b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/synchronization/jruby_lockable_object.rb @@ -0,0 +1,13 @@ +module Concurrent + module Synchronization + + if Concurrent.on_jruby? && Concurrent.java_extensions_loaded? + + # @!visibility private + # @!macro internal_implementation_note + class JRubyLockableObject < AbstractLockableObject + + end + end + end +end diff --git a/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/synchronization/jruby_object.rb b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/synchronization/jruby_object.rb new file mode 100644 index 0000000..da91ac5 --- /dev/null +++ b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/synchronization/jruby_object.rb @@ -0,0 +1,45 @@ +module Concurrent + module Synchronization + + if Concurrent.on_jruby? && Concurrent.java_extensions_loaded? + + # @!visibility private + module JRubyAttrVolatile + def self.included(base) + base.extend(ClassMethods) + end + + module ClassMethods + def attr_volatile(*names) + names.each do |name| + + ivar = :"@volatile_#{name}" + + class_eval <<-RUBY, __FILE__, __LINE__ + 1 + def #{name} + instance_variable_get_volatile(:#{ivar}) + end + + def #{name}=(value) + instance_variable_set_volatile(:#{ivar}, value) + end + RUBY + + end + names.map { |n| [n, :"#{n}="] }.flatten + end + end + end + + # @!visibility private + # @!macro internal_implementation_note + class JRubyObject < AbstractObject + include JRubyAttrVolatile + + def initialize + # nothing to do + end + end + end + end +end diff --git a/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/synchronization/lock.rb b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/synchronization/lock.rb new file mode 100644 index 0000000..0dbad2e --- /dev/null +++ b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/synchronization/lock.rb @@ -0,0 +1,36 @@ +module Concurrent + module Synchronization + + # @!visibility private + # TODO (pitr-ch 04-Dec-2016): should be in edge + class Lock < LockableObject + # TODO use JavaReentrantLock on JRuby + + public :synchronize + + def wait(timeout = nil) + synchronize { ns_wait(timeout) } + end + + public :ns_wait + + def wait_until(timeout = nil, &condition) + synchronize { ns_wait_until(timeout, &condition) } + end + + public :ns_wait_until + + def signal + synchronize { ns_signal } + end + + public :ns_signal + + def broadcast + synchronize { ns_broadcast } + end + + public :ns_broadcast + end + end +end diff --git a/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/synchronization/lockable_object.rb b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/synchronization/lockable_object.rb new file mode 100644 index 0000000..34cc8d9 --- /dev/null +++ b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/synchronization/lockable_object.rb @@ -0,0 +1,74 @@ +module Concurrent + module Synchronization + + # @!visibility private + # @!macro internal_implementation_note + LockableObjectImplementation = case + when Concurrent.on_cruby? && Concurrent.ruby_version(:<=, 1, 9, 3) + MonitorLockableObject + when Concurrent.on_cruby? && Concurrent.ruby_version(:>, 1, 9, 3) + MutexLockableObject + when Concurrent.on_jruby? + JRubyLockableObject + when Concurrent.on_rbx? + RbxLockableObject + when Concurrent.on_truffleruby? + MutexLockableObject + else + warn 'Possibly unsupported Ruby implementation' + MonitorLockableObject + end + private_constant :LockableObjectImplementation + + # Safe synchronization under any Ruby implementation. + # It provides methods like {#synchronize}, {#wait}, {#signal} and {#broadcast}. + # Provides a single layer which can improve its implementation over time without changes needed to + # the classes using it. Use {Synchronization::Object} not this abstract class. + # + # @note this object does not support usage together with + # [`Thread#wakeup`](http://ruby-doc.org/core-2.2.0/Thread.html#method-i-wakeup) + # and [`Thread#raise`](http://ruby-doc.org/core-2.2.0/Thread.html#method-i-raise). + # `Thread#sleep` and `Thread#wakeup` will work as expected but mixing `Synchronization::Object#wait` and + # `Thread#wakeup` will not work on all platforms. + # + # @see Event implementation as an example of this class use + # + # @example simple + # class AnClass < Synchronization::Object + # def initialize + # super + # synchronize { @value = 'asd' } + # end + # + # def value + # synchronize { @value } + # end + # end + # + # @!visibility private + class LockableObject < LockableObjectImplementation + + # TODO (pitr 12-Sep-2015): make private for c-r, prohibit subclassing + # TODO (pitr 12-Sep-2015): we inherit too much ourselves :/ + + # @!method initialize(*args, &block) + # @!macro synchronization_object_method_initialize + + # @!method synchronize + # @!macro synchronization_object_method_synchronize + + # @!method wait_until(timeout = nil, &condition) + # @!macro synchronization_object_method_ns_wait_until + + # @!method wait(timeout = nil) + # @!macro synchronization_object_method_ns_wait + + # @!method signal + # @!macro synchronization_object_method_ns_signal + + # @!method broadcast + # @!macro synchronization_object_method_ns_broadcast + + end + end +end diff --git a/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/synchronization/mri_object.rb b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/synchronization/mri_object.rb new file mode 100644 index 0000000..4b1d6c2 --- /dev/null +++ b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/synchronization/mri_object.rb @@ -0,0 +1,44 @@ +module Concurrent + module Synchronization + + # @!visibility private + module MriAttrVolatile + def self.included(base) + base.extend(ClassMethods) + end + + module ClassMethods + def attr_volatile(*names) + names.each do |name| + ivar = :"@volatile_#{name}" + class_eval <<-RUBY, __FILE__, __LINE__ + 1 + def #{name} + #{ivar} + end + + def #{name}=(value) + #{ivar} = value + end + RUBY + end + names.map { |n| [n, :"#{n}="] }.flatten + end + end + + def full_memory_barrier + # relying on undocumented behavior of CRuby, GVL acquire has lock which ensures visibility of ivars + # https://github.com/ruby/ruby/blob/ruby_2_2/thread_pthread.c#L204-L211 + end + end + + # @!visibility private + # @!macro internal_implementation_note + class MriObject < AbstractObject + include MriAttrVolatile + + def initialize + # nothing to do + end + end + end +end diff --git a/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/synchronization/mutex_lockable_object.rb b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/synchronization/mutex_lockable_object.rb new file mode 100644 index 0000000..f288c51 --- /dev/null +++ b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/synchronization/mutex_lockable_object.rb @@ -0,0 +1,76 @@ +module Concurrent + # noinspection RubyInstanceVariableNamingConvention + module Synchronization + + # @!visibility private + # @!macro internal_implementation_note + module ConditionSignalling + protected + + def ns_signal + @__Condition__.signal + self + end + + def ns_broadcast + @__Condition__.broadcast + self + end + end + + + # @!visibility private + # @!macro internal_implementation_note + class MutexLockableObject < AbstractLockableObject + include ConditionSignalling + + safe_initialization! + + def initialize(*defaults) + super(*defaults) + @__Lock__ = ::Mutex.new + @__Condition__ = ::ConditionVariable.new + end + + protected + + def synchronize + if @__Lock__.owned? + yield + else + @__Lock__.synchronize { yield } + end + end + + def ns_wait(timeout = nil) + @__Condition__.wait @__Lock__, timeout + self + end + end + + # @!visibility private + # @!macro internal_implementation_note + class MonitorLockableObject < AbstractLockableObject + include ConditionSignalling + + safe_initialization! + + def initialize(*defaults) + super(*defaults) + @__Lock__ = ::Monitor.new + @__Condition__ = @__Lock__.new_cond + end + + protected + + def synchronize # TODO may be a problem with lock.synchronize { lock.wait } + @__Lock__.synchronize { yield } + end + + def ns_wait(timeout = nil) + @__Condition__.wait timeout + self + end + end + end +end diff --git a/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/synchronization/object.rb b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/synchronization/object.rb new file mode 100644 index 0000000..0e62112 --- /dev/null +++ b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/synchronization/object.rb @@ -0,0 +1,183 @@ +module Concurrent + module Synchronization + + # @!visibility private + # @!macro internal_implementation_note + ObjectImplementation = case + when Concurrent.on_cruby? + MriObject + when Concurrent.on_jruby? + JRubyObject + when Concurrent.on_rbx? + RbxObject + when Concurrent.on_truffleruby? + TruffleRubyObject + else + warn 'Possibly unsupported Ruby implementation' + MriObject + end + private_constant :ObjectImplementation + + # Abstract object providing final, volatile, ans CAS extensions to build other concurrent abstractions. + # - final instance variables see {Object.safe_initialization!} + # - volatile instance variables see {Object.attr_volatile} + # - volatile instance variables see {Object.attr_atomic} + class Object < ObjectImplementation + # TODO make it a module if possible + + # @!method self.attr_volatile(*names) + # Creates methods for reading and writing (as `attr_accessor` does) to a instance variable with + # volatile (Java) semantic. The instance variable should be accessed only through generated methods. + # + # @param [::Array] names of the instance variables to be volatile + # @return [::Array] names of defined method names + + # Has to be called by children. + def initialize + super + __initialize_atomic_fields__ + end + + # By calling this method on a class, it and all its children are marked to be constructed safely. Meaning that + # all writes (ivar initializations) are made visible to all readers of newly constructed object. It ensures + # same behaviour as Java's final fields. + # @example + # class AClass < Concurrent::Synchronization::Object + # safe_initialization! + # + # def initialize + # @AFinalValue = 'value' # published safely, does not have to be synchronized + # end + # end + # @return [true] + def self.safe_initialization! + # define only once, and not again in children + return if safe_initialization? + + # @!visibility private + def self.new(*args, &block) + object = super(*args, &block) + ensure + object.full_memory_barrier if object + end + + @safe_initialization = true + end + + # @return [true, false] if this class is safely initialized. + def self.safe_initialization? + @safe_initialization = false unless defined? @safe_initialization + @safe_initialization || (superclass.respond_to?(:safe_initialization?) && superclass.safe_initialization?) + end + + # For testing purposes, quite slow. Injects assert code to new method which will raise if class instance contains + # any instance variables with CamelCase names and isn't {.safe_initialization?}. + # @raise when offend found + # @return [true] + def self.ensure_safe_initialization_when_final_fields_are_present + Object.class_eval do + def self.new(*args, &block) + object = super(*args, &block) + ensure + has_final_field = object.instance_variables.any? { |v| v.to_s =~ /^@[A-Z]/ } + if has_final_field && !safe_initialization? + raise "there was an instance of #{object.class} with final field but not marked with safe_initialization!" + end + end + end + true + end + + # Creates methods for reading and writing to a instance variable with + # volatile (Java) semantic as {.attr_volatile} does. + # The instance variable should be accessed oly through generated methods. + # This method generates following methods: `value`, `value=(new_value) #=> new_value`, + # `swap_value(new_value) #=> old_value`, + # `compare_and_set_value(expected, value) #=> true || false`, `update_value(&block)`. + # @param [::Array] names of the instance variables to be volatile with CAS. + # @return [::Array] names of defined method names. + # @!macro attr_atomic + # @!method $1 + # @return [Object] The $1. + # @!method $1=(new_$1) + # Set the $1. + # @return [Object] new_$1. + # @!method swap_$1(new_$1) + # Set the $1 to new_$1 and return the old $1. + # @return [Object] old $1 + # @!method compare_and_set_$1(expected_$1, new_$1) + # Sets the $1 to new_$1 if the current $1 is expected_$1 + # @return [true, false] + # @!method update_$1(&block) + # Updates the $1 using the block. + # @yield [Object] Calculate a new $1 using given (old) $1 + # @yieldparam [Object] old $1 + # @return [Object] new $1 + def self.attr_atomic(*names) + @__atomic_fields__ ||= [] + @__atomic_fields__ += names + safe_initialization! + define_initialize_atomic_fields + + names.each do |name| + ivar = :"@Atomic#{name.to_s.gsub(/(?:^|_)(.)/) { $1.upcase }}" + class_eval <<-RUBY, __FILE__, __LINE__ + 1 + def #{name} + #{ivar}.get + end + + def #{name}=(value) + #{ivar}.set value + end + + def swap_#{name}(value) + #{ivar}.swap value + end + + def compare_and_set_#{name}(expected, value) + #{ivar}.compare_and_set expected, value + end + + def update_#{name}(&block) + #{ivar}.update(&block) + end + RUBY + end + names.flat_map { |n| [n, :"#{n}=", :"swap_#{n}", :"compare_and_set_#{n}", :"update_#{n}"] } + end + + # @param [true, false] inherited should inherited volatile with CAS fields be returned? + # @return [::Array] Returns defined volatile with CAS fields on this class. + def self.atomic_attributes(inherited = true) + @__atomic_fields__ ||= [] + ((superclass.atomic_attributes if superclass.respond_to?(:atomic_attributes) && inherited) || []) + @__atomic_fields__ + end + + # @return [true, false] is the attribute with name atomic? + def self.atomic_attribute?(name) + atomic_attributes.include? name + end + + private + + def self.define_initialize_atomic_fields + assignments = @__atomic_fields__.map do |name| + "@Atomic#{name.to_s.gsub(/(?:^|_)(.)/) { $1.upcase }} = Concurrent::AtomicReference.new(nil)" + end.join("\n") + + class_eval <<-RUBY, __FILE__, __LINE__ + 1 + def __initialize_atomic_fields__ + super + #{assignments} + end + RUBY + end + + private_class_method :define_initialize_atomic_fields + + def __initialize_atomic_fields__ + end + + end + end +end diff --git a/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/synchronization/rbx_lockable_object.rb b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/synchronization/rbx_lockable_object.rb new file mode 100644 index 0000000..8dbd3c3 --- /dev/null +++ b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/synchronization/rbx_lockable_object.rb @@ -0,0 +1,65 @@ +module Concurrent + module Synchronization + + # @!visibility private + # @!macro internal_implementation_note + class RbxLockableObject < AbstractLockableObject + safe_initialization! + + def initialize(*defaults) + super(*defaults) + @__Waiters__ = [] + @__owner__ = nil + end + + protected + + def synchronize(&block) + if @__owner__ == Thread.current + yield + else + result = nil + Rubinius.synchronize(self) do + begin + @__owner__ = Thread.current + result = yield + ensure + @__owner__ = nil + end + end + result + end + end + + def ns_wait(timeout = nil) + wchan = Rubinius::Channel.new + + begin + @__Waiters__.push wchan + Rubinius.unlock(self) + signaled = wchan.receive_timeout timeout + ensure + Rubinius.lock(self) + + if !signaled && !@__Waiters__.delete(wchan) + # we timed out, but got signaled afterwards, + # so pass that signal on to the next waiter + @__Waiters__.shift << true unless @__Waiters__.empty? + end + end + + self + end + + def ns_signal + @__Waiters__.shift << true unless @__Waiters__.empty? + self + end + + def ns_broadcast + @__Waiters__.shift << true until @__Waiters__.empty? + self + end + end + end +end diff --git a/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/synchronization/rbx_object.rb b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/synchronization/rbx_object.rb new file mode 100644 index 0000000..4b23f2a --- /dev/null +++ b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/synchronization/rbx_object.rb @@ -0,0 +1,49 @@ +module Concurrent + module Synchronization + + # @!visibility private + module RbxAttrVolatile + def self.included(base) + base.extend(ClassMethods) + end + + module ClassMethods + + def attr_volatile(*names) + names.each do |name| + ivar = :"@volatile_#{name}" + class_eval <<-RUBY, __FILE__, __LINE__ + 1 + def #{name} + Rubinius.memory_barrier + #{ivar} + end + + def #{name}=(value) + #{ivar} = value + Rubinius.memory_barrier + end + RUBY + end + names.map { |n| [n, :"#{n}="] }.flatten + end + + end + + def full_memory_barrier + # Rubinius instance variables are not volatile so we need to insert barrier + # TODO (pitr 26-Nov-2015): check comments like ^ + Rubinius.memory_barrier + end + end + + # @!visibility private + # @!macro internal_implementation_note + class RbxObject < AbstractObject + include RbxAttrVolatile + + def initialize + # nothing to do + end + end + end +end diff --git a/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/synchronization/truffleruby_object.rb b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/synchronization/truffleruby_object.rb new file mode 100644 index 0000000..3919c76 --- /dev/null +++ b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/synchronization/truffleruby_object.rb @@ -0,0 +1,47 @@ +module Concurrent + module Synchronization + + # @!visibility private + module TruffleRubyAttrVolatile + def self.included(base) + base.extend(ClassMethods) + end + + module ClassMethods + def attr_volatile(*names) + names.each do |name| + ivar = :"@volatile_#{name}" + + class_eval <<-RUBY, __FILE__, __LINE__ + 1 + def #{name} + full_memory_barrier + #{ivar} + end + + def #{name}=(value) + #{ivar} = value + full_memory_barrier + end + RUBY + end + + names.map { |n| [n, :"#{n}="] }.flatten + end + end + + def full_memory_barrier + TruffleRuby.full_memory_barrier + end + end + + # @!visibility private + # @!macro internal_implementation_note + class TruffleRubyObject < AbstractObject + include TruffleRubyAttrVolatile + + def initialize + # nothing to do + end + end + end +end diff --git a/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/synchronization/volatile.rb b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/synchronization/volatile.rb new file mode 100644 index 0000000..9dffa91 --- /dev/null +++ b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/synchronization/volatile.rb @@ -0,0 +1,36 @@ +module Concurrent + module Synchronization + + # Volatile adds the attr_volatile class method when included. + # + # @example + # class Foo + # include Concurrent::Synchronization::Volatile + # + # attr_volatile :bar + # + # def initialize + # self.bar = 1 + # end + # end + # + # foo = Foo.new + # foo.bar + # => 1 + # foo.bar = 2 + # => 2 + + Volatile = case + when Concurrent.on_cruby? + MriAttrVolatile + when Concurrent.on_jruby? + JRubyAttrVolatile + when Concurrent.on_rbx? + RbxAttrVolatile + when Concurrent.on_truffleruby? + TruffleRubyAttrVolatile + else + MriAttrVolatile + end + end +end diff --git a/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/thread_safe/synchronized_delegator.rb b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/thread_safe/synchronized_delegator.rb new file mode 100644 index 0000000..92e7c45 --- /dev/null +++ b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/thread_safe/synchronized_delegator.rb @@ -0,0 +1,50 @@ +require 'delegate' +require 'monitor' + +module Concurrent + unless defined?(SynchronizedDelegator) + + # This class provides a trivial way to synchronize all calls to a given object + # by wrapping it with a `Delegator` that performs `Monitor#enter/exit` calls + # around the delegated `#send`. Example: + # + # array = [] # not thread-safe on many impls + # array = SynchronizedDelegator.new([]) # thread-safe + # + # A simple `Monitor` provides a very coarse-grained way to synchronize a given + # object, in that it will cause synchronization for methods that have no need + # for it, but this is a trivial way to get thread-safety where none may exist + # currently on some implementations. + # + # This class is currently being considered for inclusion into stdlib, via + # https://bugs.ruby-lang.org/issues/8556 + # + # @!visibility private + class SynchronizedDelegator < SimpleDelegator + def setup + @old_abort = Thread.abort_on_exception + Thread.abort_on_exception = true + end + + def teardown + Thread.abort_on_exception = @old_abort + end + + def initialize(obj) + __setobj__(obj) + @monitor = Monitor.new + end + + def method_missing(method, *args, &block) + monitor = @monitor + begin + monitor.enter + super + ensure + monitor.exit + end + end + + end + end +end diff --git a/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/thread_safe/util.rb b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/thread_safe/util.rb new file mode 100644 index 0000000..c67084a --- /dev/null +++ b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/thread_safe/util.rb @@ -0,0 +1,16 @@ +module Concurrent + + # @!visibility private + module ThreadSafe + + # @!visibility private + module Util + + # TODO (pitr-ch 15-Oct-2016): migrate to Utility::NativeInteger + FIXNUM_BIT_SIZE = (0.size * 8) - 2 + MAX_INT = (2 ** FIXNUM_BIT_SIZE) - 1 + # TODO (pitr-ch 15-Oct-2016): migrate to Utility::ProcessorCounter + CPU_COUNT = 16 # is there a way to determine this? + end + end +end diff --git a/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/thread_safe/util/adder.rb b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/thread_safe/util/adder.rb new file mode 100644 index 0000000..7a6e8d5 --- /dev/null +++ b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/thread_safe/util/adder.rb @@ -0,0 +1,74 @@ +require 'concurrent/thread_safe/util' +require 'concurrent/thread_safe/util/striped64' + +module Concurrent + + # @!visibility private + module ThreadSafe + + # @!visibility private + module Util + + # A Ruby port of the Doug Lea's jsr166e.LondAdder class version 1.8 + # available in public domain. + # + # Original source code available here: + # http://gee.cs.oswego.edu/cgi-bin/viewcvs.cgi/jsr166/src/jsr166e/LongAdder.java?revision=1.8 + # + # One or more variables that together maintain an initially zero + # sum. When updates (method +add+) are contended across threads, + # the set of variables may grow dynamically to reduce contention. + # Method +sum+ returns the current total combined across the + # variables maintaining the sum. + # + # This class is usually preferable to single +Atomic+ reference when + # multiple threads update a common sum that is used for purposes such + # as collecting statistics, not for fine-grained synchronization + # control. Under low update contention, the two classes have similar + # characteristics. But under high contention, expected throughput of + # this class is significantly higher, at the expense of higher space + # consumption. + # + # @!visibility private + class Adder < Striped64 + # Adds the given value. + def add(x) + if (current_cells = cells) || !cas_base_computed {|current_base| current_base + x} + was_uncontended = true + hash = hash_code + unless current_cells && (cell = current_cells.volatile_get_by_hash(hash)) && (was_uncontended = cell.cas_computed {|current_value| current_value + x}) + retry_update(x, hash, was_uncontended) {|current_value| current_value + x} + end + end + end + + def increment + add(1) + end + + def decrement + add(-1) + end + + # Returns the current sum. The returned value is _NOT_ an + # atomic snapshot: Invocation in the absence of concurrent + # updates returns an accurate result, but concurrent updates that + # occur while the sum is being calculated might not be + # incorporated. + def sum + x = base + if current_cells = cells + current_cells.each do |cell| + x += cell.value if cell + end + end + x + end + + def reset + internal_reset(0) + end + end + end + end +end diff --git a/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/thread_safe/util/cheap_lockable.rb b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/thread_safe/util/cheap_lockable.rb new file mode 100644 index 0000000..d9b4c58 --- /dev/null +++ b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/thread_safe/util/cheap_lockable.rb @@ -0,0 +1,118 @@ +require 'concurrent/thread_safe/util' +require 'concurrent/thread_safe/util/volatile' + +module Concurrent + + # @!visibility private + module ThreadSafe + + # @!visibility private + module Util + + # Provides a cheapest possible (mainly in terms of memory usage) +Mutex+ + # with the +ConditionVariable+ bundled in. + # + # Usage: + # class A + # include CheapLockable + # + # def do_exlusively + # cheap_synchronize { yield } + # end + # + # def wait_for_something + # cheap_synchronize do + # cheap_wait until resource_available? + # do_something + # cheap_broadcast # wake up others + # end + # end + # end + # + # @!visibility private + module CheapLockable + private + engine = defined?(RUBY_ENGINE) && RUBY_ENGINE + if engine == 'rbx' + # Making use of the Rubinius' ability to lock via object headers to avoid the overhead of the extra Mutex objects. + def cheap_synchronize + Rubinius.lock(self) + begin + yield + ensure + Rubinius.unlock(self) + end + end + + def cheap_wait + wchan = Rubinius::Channel.new + + begin + waiters = @waiters ||= [] + waiters.push wchan + Rubinius.unlock(self) + signaled = wchan.receive_timeout nil + ensure + Rubinius.lock(self) + + unless signaled or waiters.delete(wchan) + # we timed out, but got signaled afterwards (e.g. while waiting to + # acquire @lock), so pass that signal on to the next waiter + waiters.shift << true unless waiters.empty? + end + end + + self + end + + def cheap_broadcast + waiters = @waiters ||= [] + waiters.shift << true until waiters.empty? + self + end + elsif engine == 'jruby' + # Use Java's native synchronized (this) { wait(); notifyAll(); } to avoid the overhead of the extra Mutex objects + require 'jruby' + + def cheap_synchronize + JRuby.reference0(self).synchronized { yield } + end + + def cheap_wait + JRuby.reference0(self).wait + end + + def cheap_broadcast + JRuby.reference0(self).notify_all + end + else + require 'thread' + + extend Volatile + attr_volatile :mutex + + # Non-reentrant Mutex#syncrhonize + def cheap_synchronize + true until (my_mutex = mutex) || cas_mutex(nil, my_mutex = Mutex.new) + my_mutex.synchronize { yield } + end + + # Releases this object's +cheap_synchronize+ lock and goes to sleep waiting for other threads to +cheap_broadcast+, reacquires the lock on wakeup. + # Must only be called in +cheap_broadcast+'s block. + def cheap_wait + conditional_variable = @conditional_variable ||= ConditionVariable.new + conditional_variable.wait(mutex) + end + + # Wakes up all threads waiting for this object's +cheap_synchronize+ lock. + # Must only be called in +cheap_broadcast+'s block. + def cheap_broadcast + if conditional_variable = @conditional_variable + conditional_variable.broadcast + end + end + end + end + end + end +end diff --git a/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/thread_safe/util/data_structures.rb b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/thread_safe/util/data_structures.rb new file mode 100644 index 0000000..ff1e8ed --- /dev/null +++ b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/thread_safe/util/data_structures.rb @@ -0,0 +1,63 @@ +require 'concurrent/thread_safe/util' + +# Shim for TruffleRuby.synchronized +if Concurrent.on_truffleruby? && !TruffleRuby.respond_to?(:synchronized) + module TruffleRuby + def self.synchronized(object, &block) + Truffle::System.synchronized(object, &block) + end + end +end + +module Concurrent + module ThreadSafe + module Util + def self.make_synchronized_on_rbx(klass) + klass.class_eval do + private + + def _mon_initialize + @_monitor = Monitor.new unless @_monitor # avoid double initialisation + end + + def self.new(*args) + obj = super(*args) + obj.send(:_mon_initialize) + obj + end + end + + klass.superclass.instance_methods(false).each do |method| + case method + when :new_range, :new_reserved + klass.class_eval <<-RUBY, __FILE__, __LINE__ + 1 + def #{method}(*args) + obj = super + obj.send(:_mon_initialize) + obj + end + RUBY + else + klass.class_eval <<-RUBY, __FILE__, __LINE__ + 1 + def #{method}(*args) + monitor = @_monitor + monitor or raise("BUG: Internal monitor was not properly initialized. Please report this to the concurrent-ruby developers.") + monitor.synchronize { super } + end + RUBY + end + end + end + + def self.make_synchronized_on_truffleruby(klass) + klass.superclass.instance_methods(false).each do |method| + klass.class_eval <<-RUBY, __FILE__, __LINE__ + 1 + def #{method}(*args, &block) + TruffleRuby.synchronized(self) { super(*args, &block) } + end + RUBY + end + end + end + end +end diff --git a/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/thread_safe/util/power_of_two_tuple.rb b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/thread_safe/util/power_of_two_tuple.rb new file mode 100644 index 0000000..b54be39 --- /dev/null +++ b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/thread_safe/util/power_of_two_tuple.rb @@ -0,0 +1,38 @@ +require 'concurrent/thread_safe/util' +require 'concurrent/tuple' + +module Concurrent + + # @!visibility private + module ThreadSafe + + # @!visibility private + module Util + + # @!visibility private + class PowerOfTwoTuple < Concurrent::Tuple + + def initialize(size) + raise ArgumentError, "size must be a power of 2 (#{size.inspect} provided)" unless size > 0 && size & (size - 1) == 0 + super(size) + end + + def hash_to_index(hash) + (size - 1) & hash + end + + def volatile_get_by_hash(hash) + volatile_get(hash_to_index(hash)) + end + + def volatile_set_by_hash(hash, value) + volatile_set(hash_to_index(hash), value) + end + + def next_in_size_table + self.class.new(size << 1) + end + end + end + end +end diff --git a/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/thread_safe/util/striped64.rb b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/thread_safe/util/striped64.rb new file mode 100644 index 0000000..5530e97 --- /dev/null +++ b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/thread_safe/util/striped64.rb @@ -0,0 +1,246 @@ +require 'concurrent/thread_safe/util' +require 'concurrent/thread_safe/util/power_of_two_tuple' +require 'concurrent/thread_safe/util/volatile' +require 'concurrent/thread_safe/util/xor_shift_random' + +module Concurrent + + # @!visibility private + module ThreadSafe + + # @!visibility private + module Util + + # A Ruby port of the Doug Lea's jsr166e.Striped64 class version 1.6 + # available in public domain. + # + # Original source code available here: + # http://gee.cs.oswego.edu/cgi-bin/viewcvs.cgi/jsr166/src/jsr166e/Striped64.java?revision=1.6 + # + # Class holding common representation and mechanics for classes supporting + # dynamic striping on 64bit values. + # + # This class maintains a lazily-initialized table of atomically updated + # variables, plus an extra +base+ field. The table size is a power of two. + # Indexing uses masked per-thread hash codes. Nearly all methods on this + # class are private, accessed directly by subclasses. + # + # Table entries are of class +Cell+; a variant of AtomicLong padded to + # reduce cache contention on most processors. Padding is overkill for most + # Atomics because they are usually irregularly scattered in memory and thus + # don't interfere much with each other. But Atomic objects residing in + # arrays will tend to be placed adjacent to each other, and so will most + # often share cache lines (with a huge negative performance impact) without + # this precaution. + # + # In part because +Cell+s are relatively large, we avoid creating them until + # they are needed. When there is no contention, all updates are made to the + # +base+ field. Upon first contention (a failed CAS on +base+ update), the + # table is initialized to size 2. The table size is doubled upon further + # contention until reaching the nearest power of two greater than or equal + # to the number of CPUS. Table slots remain empty (+nil+) until they are + # needed. + # + # A single spinlock (+busy+) is used for initializing and resizing the + # table, as well as populating slots with new +Cell+s. There is no need for + # a blocking lock: When the lock is not available, threads try other slots + # (or the base). During these retries, there is increased contention and + # reduced locality, which is still better than alternatives. + # + # Per-thread hash codes are initialized to random values. Contention and/or + # table collisions are indicated by failed CASes when performing an update + # operation (see method +retry_update+). Upon a collision, if the table size + # is less than the capacity, it is doubled in size unless some other thread + # holds the lock. If a hashed slot is empty, and lock is available, a new + # +Cell+ is created. Otherwise, if the slot exists, a CAS is tried. Retries + # proceed by "double hashing", using a secondary hash (XorShift) to try to + # find a free slot. + # + # The table size is capped because, when there are more threads than CPUs, + # supposing that each thread were bound to a CPU, there would exist a + # perfect hash function mapping threads to slots that eliminates collisions. + # When we reach capacity, we search for this mapping by randomly varying the + # hash codes of colliding threads. Because search is random, and collisions + # only become known via CAS failures, convergence can be slow, and because + # threads are typically not bound to CPUS forever, may not occur at all. + # However, despite these limitations, observed contention rates are + # typically low in these cases. + # + # It is possible for a +Cell+ to become unused when threads that once hashed + # to it terminate, as well as in the case where doubling the table causes no + # thread to hash to it under expanded mask. We do not try to detect or + # remove such cells, under the assumption that for long-running instances, + # observed contention levels will recur, so the cells will eventually be + # needed again; and for short-lived ones, it does not matter. + # + # @!visibility private + class Striped64 + + # Padded variant of AtomicLong supporting only raw accesses plus CAS. + # The +value+ field is placed between pads, hoping that the JVM doesn't + # reorder them. + # + # Optimisation note: It would be possible to use a release-only + # form of CAS here, if it were provided. + # + # @!visibility private + class Cell < Concurrent::AtomicReference + + alias_method :cas, :compare_and_set + + def cas_computed + cas(current_value = value, yield(current_value)) + end + + # @!visibility private + def self.padding + # TODO: this only adds padding after the :value slot, need to find a way to add padding before the slot + # TODO (pitr-ch 28-Jul-2018): the padding instance vars may not be created + # hide from yardoc in a method + attr_reader *(12.times.collect{ |i| "padding_#{i}".to_sym }) + end + padding + end + + extend Volatile + attr_volatile :cells, # Table of cells. When non-null, size is a power of 2. + :base, # Base value, used mainly when there is no contention, but also as a fallback during table initialization races. Updated via CAS. + :busy # Spinlock (locked via CAS) used when resizing and/or creating Cells. + + alias_method :busy?, :busy + + def initialize + super() + self.busy = false + self.base = 0 + end + + # Handles cases of updates involving initialization, resizing, + # creating new Cells, and/or contention. See above for + # explanation. This method suffers the usual non-modularity + # problems of optimistic retry code, relying on rechecked sets of + # reads. + # + # Arguments: + # [+x+] + # the value + # [+hash_code+] + # hash code used + # [+x+] + # false if CAS failed before call + def retry_update(x, hash_code, was_uncontended) # :yields: current_value + hash = hash_code + collided = false # True if last slot nonempty + while true + if current_cells = cells + if !(cell = current_cells.volatile_get_by_hash(hash)) + if busy? + collided = false + else # Try to attach new Cell + if try_to_install_new_cell(Cell.new(x), hash) # Optimistically create and try to insert new cell + break + else + redo # Slot is now non-empty + end + end + elsif !was_uncontended # CAS already known to fail + was_uncontended = true # Continue after rehash + elsif cell.cas_computed {|current_value| yield current_value} + break + elsif current_cells.size >= CPU_COUNT || cells != current_cells # At max size or stale + collided = false + elsif collided && expand_table_unless_stale(current_cells) + collided = false + redo # Retry with expanded table + else + collided = true + end + hash = XorShiftRandom.xorshift(hash) + + elsif try_initialize_cells(x, hash) || cas_base_computed {|current_base| yield current_base} + break + end + end + self.hash_code = hash + end + + private + # Static per-thread hash code key. Shared across all instances to + # reduce Thread locals pollution and because adjustments due to + # collisions in one table are likely to be appropriate for + # others. + THREAD_LOCAL_KEY = "#{name}.hash_code".to_sym + + # A thread-local hash code accessor. The code is initially + # random, but may be set to a different value upon collisions. + def hash_code + Thread.current[THREAD_LOCAL_KEY] ||= XorShiftRandom.get + end + + def hash_code=(hash) + Thread.current[THREAD_LOCAL_KEY] = hash + end + + # Sets base and all +cells+ to the given value. + def internal_reset(initial_value) + current_cells = cells + self.base = initial_value + if current_cells + current_cells.each do |cell| + cell.value = initial_value if cell + end + end + end + + def cas_base_computed + cas_base(current_base = base, yield(current_base)) + end + + def free? + !busy? + end + + def try_initialize_cells(x, hash) + if free? && !cells + try_in_busy do + unless cells # Recheck under lock + new_cells = PowerOfTwoTuple.new(2) + new_cells.volatile_set_by_hash(hash, Cell.new(x)) + self.cells = new_cells + end + end + end + end + + def expand_table_unless_stale(current_cells) + try_in_busy do + if current_cells == cells # Recheck under lock + new_cells = current_cells.next_in_size_table + current_cells.each_with_index {|x, i| new_cells.volatile_set(i, x)} + self.cells = new_cells + end + end + end + + def try_to_install_new_cell(new_cell, hash) + try_in_busy do + # Recheck under lock + if (current_cells = cells) && !current_cells.volatile_get(i = current_cells.hash_to_index(hash)) + current_cells.volatile_set(i, new_cell) + end + end + end + + def try_in_busy + if cas_busy(false, true) + begin + yield + ensure + self.busy = false + end + end + end + end + end + end +end diff --git a/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/thread_safe/util/volatile.rb b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/thread_safe/util/volatile.rb new file mode 100644 index 0000000..cdac2a3 --- /dev/null +++ b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/thread_safe/util/volatile.rb @@ -0,0 +1,75 @@ +require 'concurrent/thread_safe/util' + +module Concurrent + + # @!visibility private + module ThreadSafe + + # @!visibility private + module Util + + # @!visibility private + module Volatile + + # Provides +volatile+ (in the JVM's sense) attribute accessors implemented + # atop of +Concurrent::AtomicReference+. + # + # Usage: + # class Foo + # extend Concurrent::ThreadSafe::Util::Volatile + # attr_volatile :foo, :bar + # + # def initialize(bar) + # super() # must super() into parent initializers before using the volatile attribute accessors + # self.bar = bar + # end + # + # def hello + # my_foo = foo # volatile read + # self.foo = 1 # volatile write + # cas_foo(1, 2) # => true | a strong CAS + # end + # end + def attr_volatile(*attr_names) + return if attr_names.empty? + include(Module.new do + atomic_ref_setup = attr_names.map {|attr_name| "@__#{attr_name} = Concurrent::AtomicReference.new"} + initialize_copy_setup = attr_names.zip(atomic_ref_setup).map do |attr_name, ref_setup| + "#{ref_setup}(other.instance_variable_get(:@__#{attr_name}).get)" + end + class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1 + def initialize(*) + super + #{atomic_ref_setup.join('; ')} + end + + def initialize_copy(other) + super + #{initialize_copy_setup.join('; ')} + end + RUBY_EVAL + + attr_names.each do |attr_name| + class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1 + def #{attr_name} + @__#{attr_name}.get + end + + def #{attr_name}=(value) + @__#{attr_name}.set(value) + end + + def compare_and_set_#{attr_name}(old_value, new_value) + @__#{attr_name}.compare_and_set(old_value, new_value) + end + RUBY_EVAL + + alias_method :"cas_#{attr_name}", :"compare_and_set_#{attr_name}" + alias_method :"lazy_set_#{attr_name}", :"#{attr_name}=" + end + end) + end + end + end + end +end diff --git a/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/thread_safe/util/xor_shift_random.rb b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/thread_safe/util/xor_shift_random.rb new file mode 100644 index 0000000..bdde2dd --- /dev/null +++ b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/thread_safe/util/xor_shift_random.rb @@ -0,0 +1,50 @@ +require 'concurrent/thread_safe/util' + +module Concurrent + + # @!visibility private + module ThreadSafe + + # @!visibility private + module Util + + # A xorshift random number (positive +Fixnum+s) generator, provides + # reasonably cheap way to generate thread local random numbers without + # contending for the global +Kernel.rand+. + # + # Usage: + # x = XorShiftRandom.get # uses Kernel.rand to generate an initial seed + # while true + # if (x = XorShiftRandom.xorshift).odd? # thread-localy generate a next random number + # do_something_at_random + # end + # end + module XorShiftRandom + extend self + MAX_XOR_SHIFTABLE_INT = MAX_INT - 1 + + # Generates an initial non-zero positive +Fixnum+ via +Kernel.rand+. + def get + Kernel.rand(MAX_XOR_SHIFTABLE_INT) + 1 # 0 can't be xorshifted + end + + # xorshift based on: http://www.jstatsoft.org/v08/i14/paper + if 0.size == 4 + # using the "yˆ=y>>a; yˆ=y<>c;" transform with the (a,b,c) tuple with values (3,1,14) to minimise Bignum overflows + def xorshift(x) + x ^= x >> 3 + x ^= (x << 1) & MAX_INT # cut-off Bignum overflow + x ^= x >> 14 + end + else + # using the "yˆ=y>>a; yˆ=y<>c;" transform with the (a,b,c) tuple with values (1,1,54) to minimise Bignum overflows + def xorshift(x) + x ^= x >> 1 + x ^= (x << 1) & MAX_INT # cut-off Bignum overflow + x ^= x >> 54 + end + end + end + end + end +end diff --git a/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/timer_task.rb b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/timer_task.rb new file mode 100644 index 0000000..e766f2e --- /dev/null +++ b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/timer_task.rb @@ -0,0 +1,334 @@ +require 'concurrent/collection/copy_on_notify_observer_set' +require 'concurrent/concern/dereferenceable' +require 'concurrent/concern/observable' +require 'concurrent/atomic/atomic_boolean' +require 'concurrent/executor/executor_service' +require 'concurrent/executor/ruby_executor_service' +require 'concurrent/executor/safe_task_executor' +require 'concurrent/scheduled_task' + +module Concurrent + + # A very common concurrency pattern is to run a thread that performs a task at + # regular intervals. The thread that performs the task sleeps for the given + # interval then wakes up and performs the task. Lather, rinse, repeat... This + # pattern causes two problems. First, it is difficult to test the business + # logic of the task because the task itself is tightly coupled with the + # concurrency logic. Second, an exception raised while performing the task can + # cause the entire thread to abend. In a long-running application where the + # task thread is intended to run for days/weeks/years a crashed task thread + # can pose a significant problem. `TimerTask` alleviates both problems. + # + # When a `TimerTask` is launched it starts a thread for monitoring the + # execution interval. The `TimerTask` thread does not perform the task, + # however. Instead, the TimerTask launches the task on a separate thread. + # Should the task experience an unrecoverable crash only the task thread will + # crash. This makes the `TimerTask` very fault tolerant. Additionally, the + # `TimerTask` thread can respond to the success or failure of the task, + # performing logging or ancillary operations. `TimerTask` can also be + # configured with a timeout value allowing it to kill a task that runs too + # long. + # + # One other advantage of `TimerTask` is that it forces the business logic to + # be completely decoupled from the concurrency logic. The business logic can + # be tested separately then passed to the `TimerTask` for scheduling and + # running. + # + # In some cases it may be necessary for a `TimerTask` to affect its own + # execution cycle. To facilitate this, a reference to the TimerTask instance + # is passed as an argument to the provided block every time the task is + # executed. + # + # The `TimerTask` class includes the `Dereferenceable` mixin module so the + # result of the last execution is always available via the `#value` method. + # Dereferencing options can be passed to the `TimerTask` during construction or + # at any later time using the `#set_deref_options` method. + # + # `TimerTask` supports notification through the Ruby standard library + # {http://ruby-doc.org/stdlib-2.0/libdoc/observer/rdoc/Observable.html + # Observable} module. On execution the `TimerTask` will notify the observers + # with three arguments: time of execution, the result of the block (or nil on + # failure), and any raised exceptions (or nil on success). If the timeout + # interval is exceeded the observer will receive a `Concurrent::TimeoutError` + # object as the third argument. + # + # @!macro copy_options + # + # @example Basic usage + # task = Concurrent::TimerTask.new{ puts 'Boom!' } + # task.execute + # + # task.execution_interval #=> 60 (default) + # task.timeout_interval #=> 30 (default) + # + # # wait 60 seconds... + # #=> 'Boom!' + # + # task.shutdown #=> true + # + # @example Configuring `:execution_interval` and `:timeout_interval` + # task = Concurrent::TimerTask.new(execution_interval: 5, timeout_interval: 5) do + # puts 'Boom!' + # end + # + # task.execution_interval #=> 5 + # task.timeout_interval #=> 5 + # + # @example Immediate execution with `:run_now` + # task = Concurrent::TimerTask.new(run_now: true){ puts 'Boom!' } + # task.execute + # + # #=> 'Boom!' + # + # @example Last `#value` and `Dereferenceable` mixin + # task = Concurrent::TimerTask.new( + # dup_on_deref: true, + # execution_interval: 5 + # ){ Time.now } + # + # task.execute + # Time.now #=> 2013-11-07 18:06:50 -0500 + # sleep(10) + # task.value #=> 2013-11-07 18:06:55 -0500 + # + # @example Controlling execution from within the block + # timer_task = Concurrent::TimerTask.new(execution_interval: 1) do |task| + # task.execution_interval.times{ print 'Boom! ' } + # print "\n" + # task.execution_interval += 1 + # if task.execution_interval > 5 + # puts 'Stopping...' + # task.shutdown + # end + # end + # + # timer_task.execute # blocking call - this task will stop itself + # #=> Boom! + # #=> Boom! Boom! + # #=> Boom! Boom! Boom! + # #=> Boom! Boom! Boom! Boom! + # #=> Boom! Boom! Boom! Boom! Boom! + # #=> Stopping... + # + # @example Observation + # class TaskObserver + # def update(time, result, ex) + # if result + # print "(#{time}) Execution successfully returned #{result}\n" + # elsif ex.is_a?(Concurrent::TimeoutError) + # print "(#{time}) Execution timed out\n" + # else + # print "(#{time}) Execution failed with error #{ex}\n" + # end + # end + # end + # + # task = Concurrent::TimerTask.new(execution_interval: 1, timeout_interval: 1){ 42 } + # task.add_observer(TaskObserver.new) + # task.execute + # sleep 4 + # + # #=> (2013-10-13 19:08:58 -0400) Execution successfully returned 42 + # #=> (2013-10-13 19:08:59 -0400) Execution successfully returned 42 + # #=> (2013-10-13 19:09:00 -0400) Execution successfully returned 42 + # task.shutdown + # + # task = Concurrent::TimerTask.new(execution_interval: 1, timeout_interval: 1){ sleep } + # task.add_observer(TaskObserver.new) + # task.execute + # + # #=> (2013-10-13 19:07:25 -0400) Execution timed out + # #=> (2013-10-13 19:07:27 -0400) Execution timed out + # #=> (2013-10-13 19:07:29 -0400) Execution timed out + # task.shutdown + # + # task = Concurrent::TimerTask.new(execution_interval: 1){ raise StandardError } + # task.add_observer(TaskObserver.new) + # task.execute + # + # #=> (2013-10-13 19:09:37 -0400) Execution failed with error StandardError + # #=> (2013-10-13 19:09:38 -0400) Execution failed with error StandardError + # #=> (2013-10-13 19:09:39 -0400) Execution failed with error StandardError + # task.shutdown + # + # @see http://ruby-doc.org/stdlib-2.0/libdoc/observer/rdoc/Observable.html + # @see http://docs.oracle.com/javase/7/docs/api/java/util/TimerTask.html + class TimerTask < RubyExecutorService + include Concern::Dereferenceable + include Concern::Observable + + # Default `:execution_interval` in seconds. + EXECUTION_INTERVAL = 60 + + # Default `:timeout_interval` in seconds. + TIMEOUT_INTERVAL = 30 + + # Create a new TimerTask with the given task and configuration. + # + # @!macro timer_task_initialize + # @param [Hash] opts the options defining task execution. + # @option opts [Integer] :execution_interval number of seconds between + # task executions (default: EXECUTION_INTERVAL) + # @option opts [Integer] :timeout_interval number of seconds a task can + # run before it is considered to have failed (default: TIMEOUT_INTERVAL) + # @option opts [Boolean] :run_now Whether to run the task immediately + # upon instantiation or to wait until the first # execution_interval + # has passed (default: false) + # + # @!macro deref_options + # + # @raise ArgumentError when no block is given. + # + # @yield to the block after :execution_interval seconds have passed since + # the last yield + # @yieldparam task a reference to the `TimerTask` instance so that the + # block can control its own lifecycle. Necessary since `self` will + # refer to the execution context of the block rather than the running + # `TimerTask`. + # + # @return [TimerTask] the new `TimerTask` + def initialize(opts = {}, &task) + raise ArgumentError.new('no block given') unless block_given? + super + set_deref_options opts + end + + # Is the executor running? + # + # @return [Boolean] `true` when running, `false` when shutting down or shutdown + def running? + @running.true? + end + + # Execute a previously created `TimerTask`. + # + # @return [TimerTask] a reference to `self` + # + # @example Instance and execute in separate steps + # task = Concurrent::TimerTask.new(execution_interval: 10){ print "Hello World\n" } + # task.running? #=> false + # task.execute + # task.running? #=> true + # + # @example Instance and execute in one line + # task = Concurrent::TimerTask.new(execution_interval: 10){ print "Hello World\n" }.execute + # task.running? #=> true + def execute + synchronize do + if @running.false? + @running.make_true + schedule_next_task(@run_now ? 0 : @execution_interval) + end + end + self + end + + # Create and execute a new `TimerTask`. + # + # @!macro timer_task_initialize + # + # @example + # task = Concurrent::TimerTask.execute(execution_interval: 10){ print "Hello World\n" } + # task.running? #=> true + def self.execute(opts = {}, &task) + TimerTask.new(opts, &task).execute + end + + # @!attribute [rw] execution_interval + # @return [Fixnum] Number of seconds after the task completes before the + # task is performed again. + def execution_interval + synchronize { @execution_interval } + end + + # @!attribute [rw] execution_interval + # @return [Fixnum] Number of seconds after the task completes before the + # task is performed again. + def execution_interval=(value) + if (value = value.to_f) <= 0.0 + raise ArgumentError.new('must be greater than zero') + else + synchronize { @execution_interval = value } + end + end + + # @!attribute [rw] timeout_interval + # @return [Fixnum] Number of seconds the task can run before it is + # considered to have failed. + def timeout_interval + synchronize { @timeout_interval } + end + + # @!attribute [rw] timeout_interval + # @return [Fixnum] Number of seconds the task can run before it is + # considered to have failed. + def timeout_interval=(value) + if (value = value.to_f) <= 0.0 + raise ArgumentError.new('must be greater than zero') + else + synchronize { @timeout_interval = value } + end + end + + private :post, :<< + + private + + def ns_initialize(opts, &task) + set_deref_options(opts) + + self.execution_interval = opts[:execution] || opts[:execution_interval] || EXECUTION_INTERVAL + self.timeout_interval = opts[:timeout] || opts[:timeout_interval] || TIMEOUT_INTERVAL + @run_now = opts[:now] || opts[:run_now] + @executor = Concurrent::SafeTaskExecutor.new(task) + @running = Concurrent::AtomicBoolean.new(false) + @value = nil + + self.observers = Collection::CopyOnNotifyObserverSet.new + end + + # @!visibility private + def ns_shutdown_execution + @running.make_false + super + end + + # @!visibility private + def ns_kill_execution + @running.make_false + super + end + + # @!visibility private + def schedule_next_task(interval = execution_interval) + ScheduledTask.execute(interval, args: [Concurrent::Event.new], &method(:execute_task)) + nil + end + + # @!visibility private + def execute_task(completion) + return nil unless @running.true? + ScheduledTask.execute(timeout_interval, args: [completion], &method(:timeout_task)) + _success, value, reason = @executor.execute(self) + if completion.try? + self.value = value + schedule_next_task + time = Time.now + observers.notify_observers do + [time, self.value, reason] + end + end + nil + end + + # @!visibility private + def timeout_task(completion) + return unless @running.true? + if completion.try? + self.value = value + schedule_next_task + observers.notify_observers(Time.now, nil, Concurrent::TimeoutError.new) + end + end + end +end diff --git a/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/tuple.rb b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/tuple.rb new file mode 100644 index 0000000..f8c4c25 --- /dev/null +++ b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/tuple.rb @@ -0,0 +1,86 @@ +require 'concurrent/atomic/atomic_reference' + +module Concurrent + + # A fixed size array with volatile (synchronized, thread safe) getters/setters. + # Mixes in Ruby's `Enumerable` module for enhanced search, sort, and traversal. + # + # @example + # tuple = Concurrent::Tuple.new(16) + # + # tuple.set(0, :foo) #=> :foo | volatile write + # tuple.get(0) #=> :foo | volatile read + # tuple.compare_and_set(0, :foo, :bar) #=> true | strong CAS + # tuple.cas(0, :foo, :baz) #=> false | strong CAS + # tuple.get(0) #=> :bar | volatile read + # + # @see https://en.wikipedia.org/wiki/Tuple Tuple entry at Wikipedia + # @see http://www.erlang.org/doc/reference_manual/data_types.html#id70396 Erlang Tuple + # @see http://ruby-doc.org/core-2.2.2/Enumerable.html Enumerable + class Tuple + include Enumerable + + # The (fixed) size of the tuple. + attr_reader :size + + # @!visibility private + Tuple = defined?(Rubinius::Tuple) ? Rubinius::Tuple : ::Array + private_constant :Tuple + + # Create a new tuple of the given size. + # + # @param [Integer] size the number of elements in the tuple + def initialize(size) + @size = size + @tuple = tuple = Tuple.new(size) + i = 0 + while i < size + tuple[i] = Concurrent::AtomicReference.new + i += 1 + end + end + + # Get the value of the element at the given index. + # + # @param [Integer] i the index from which to retrieve the value + # @return [Object] the value at the given index or nil if the index is out of bounds + def get(i) + return nil if i >= @size || i < 0 + @tuple[i].get + end + alias_method :volatile_get, :get + + # Set the element at the given index to the given value + # + # @param [Integer] i the index for the element to set + # @param [Object] value the value to set at the given index + # + # @return [Object] the new value of the element at the given index or nil if the index is out of bounds + def set(i, value) + return nil if i >= @size || i < 0 + @tuple[i].set(value) + end + alias_method :volatile_set, :set + + # Set the value at the given index to the new value if and only if the current + # value matches the given old value. + # + # @param [Integer] i the index for the element to set + # @param [Object] old_value the value to compare against the current value + # @param [Object] new_value the value to set at the given index + # + # @return [Boolean] true if the value at the given element was set else false + def compare_and_set(i, old_value, new_value) + return false if i >= @size || i < 0 + @tuple[i].compare_and_set(old_value, new_value) + end + alias_method :cas, :compare_and_set + + # Calls the given block once for each element in self, passing that element as a parameter. + # + # @yieldparam [Object] ref the `Concurrent::AtomicReference` object at the current index + def each + @tuple.each {|ref| yield ref.get} + end + end +end diff --git a/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/tvar.rb b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/tvar.rb new file mode 100644 index 0000000..09138c8 --- /dev/null +++ b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/tvar.rb @@ -0,0 +1,258 @@ +require 'set' +require 'concurrent/synchronization' + +module Concurrent + + # A `TVar` is a transactional variable - a single-element container that + # is used as part of a transaction - see `Concurrent::atomically`. + # + # @!macro thread_safe_variable_comparison + # + # {include:file:docs-source/tvar.md} + class TVar < Synchronization::Object + safe_initialization! + + # Create a new `TVar` with an initial value. + def initialize(value) + @value = value + @version = 0 + @lock = Mutex.new + end + + # Get the value of a `TVar`. + def value + Concurrent::atomically do + Transaction::current.read(self) + end + end + + # Set the value of a `TVar`. + def value=(value) + Concurrent::atomically do + Transaction::current.write(self, value) + end + end + + # @!visibility private + def unsafe_value # :nodoc: + @value + end + + # @!visibility private + def unsafe_value=(value) # :nodoc: + @value = value + end + + # @!visibility private + def unsafe_version # :nodoc: + @version + end + + # @!visibility private + def unsafe_increment_version # :nodoc: + @version += 1 + end + + # @!visibility private + def unsafe_lock # :nodoc: + @lock + end + + end + + # Run a block that reads and writes `TVar`s as a single atomic transaction. + # With respect to the value of `TVar` objects, the transaction is atomic, in + # that it either happens or it does not, consistent, in that the `TVar` + # objects involved will never enter an illegal state, and isolated, in that + # transactions never interfere with each other. You may recognise these + # properties from database transactions. + # + # There are some very important and unusual semantics that you must be aware of: + # + # * Most importantly, the block that you pass to atomically may be executed + # more than once. In most cases your code should be free of + # side-effects, except for via TVar. + # + # * If an exception escapes an atomically block it will abort the transaction. + # + # * It is undefined behaviour to use callcc or Fiber with atomically. + # + # * If you create a new thread within an atomically, it will not be part of + # the transaction. Creating a thread counts as a side-effect. + # + # Transactions within transactions are flattened to a single transaction. + # + # @example + # a = new TVar(100_000) + # b = new TVar(100) + # + # Concurrent::atomically do + # a.value -= 10 + # b.value += 10 + # end + def atomically + raise ArgumentError.new('no block given') unless block_given? + + # Get the current transaction + + transaction = Transaction::current + + # Are we not already in a transaction (not nested)? + + if transaction.nil? + # New transaction + + begin + # Retry loop + + loop do + + # Create a new transaction + + transaction = Transaction.new + Transaction::current = transaction + + # Run the block, aborting on exceptions + + begin + result = yield + rescue Transaction::AbortError => e + transaction.abort + result = Transaction::ABORTED + rescue Transaction::LeaveError => e + transaction.abort + break result + rescue => e + transaction.abort + raise e + end + # If we can commit, break out of the loop + + if result != Transaction::ABORTED + if transaction.commit + break result + end + end + end + ensure + # Clear the current transaction + + Transaction::current = nil + end + else + # Nested transaction - flatten it and just run the block + + yield + end + end + + # Abort a currently running transaction - see `Concurrent::atomically`. + def abort_transaction + raise Transaction::AbortError.new + end + + # Leave a transaction without committing or aborting - see `Concurrent::atomically`. + def leave_transaction + raise Transaction::LeaveError.new + end + + module_function :atomically, :abort_transaction, :leave_transaction + + private + + class Transaction + + ABORTED = ::Object.new + + ReadLogEntry = Struct.new(:tvar, :version) + + AbortError = Class.new(StandardError) + LeaveError = Class.new(StandardError) + + def initialize + @read_log = [] + @write_log = {} + end + + def read(tvar) + Concurrent::abort_transaction unless valid? + + if @write_log.has_key? tvar + @write_log[tvar] + else + @read_log.push(ReadLogEntry.new(tvar, tvar.unsafe_version)) + tvar.unsafe_value + end + end + + def write(tvar, value) + # Have we already written to this TVar? + + unless @write_log.has_key? tvar + # Try to lock the TVar + + unless tvar.unsafe_lock.try_lock + # Someone else is writing to this TVar - abort + Concurrent::abort_transaction + end + + # If we previously wrote to it, check the version hasn't changed + + @read_log.each do |log_entry| + if log_entry.tvar == tvar and tvar.unsafe_version > log_entry.version + Concurrent::abort_transaction + end + end + end + + # Record the value written + + @write_log[tvar] = value + end + + def abort + unlock + end + + def commit + return false unless valid? + + @write_log.each_pair do |tvar, value| + tvar.unsafe_value = value + tvar.unsafe_increment_version + end + + unlock + + true + end + + def valid? + @read_log.each do |log_entry| + unless @write_log.has_key? log_entry.tvar + if log_entry.tvar.unsafe_version > log_entry.version + return false + end + end + end + + true + end + + def unlock + @write_log.each_key do |tvar| + tvar.unsafe_lock.unlock + end + end + + def self.current + Thread.current[:current_tvar_transaction] + end + + def self.current=(transaction) + Thread.current[:current_tvar_transaction] = transaction + end + + end + +end diff --git a/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/utility/at_exit.rb b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/utility/at_exit.rb new file mode 100644 index 0000000..0e52ca3 --- /dev/null +++ b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/utility/at_exit.rb @@ -0,0 +1,97 @@ +require 'logger' +require 'concurrent/synchronization' + +module Concurrent + + # Provides ability to add and remove handlers to be run at `Kernel#at_exit`, order is undefined. + # Each handler is executed at most once. + # + # @!visibility private + class AtExitImplementation < Synchronization::LockableObject + include Logger::Severity + + def initialize(*args) + super() + synchronize { ns_initialize(*args) } + end + + # Add a handler to be run at `Kernel#at_exit` + # @param [Object] handler_id optionally provide an id, if already present, handler is replaced + # @yield the handler + # @return id of the handler + def add(handler_id = nil, &handler) + id = handler_id || handler.object_id + synchronize { @handlers[id] = handler } + id + end + + # Delete a handler by handler_id + # @return [true, false] + def delete(handler_id) + !!synchronize { @handlers.delete handler_id } + end + + # Is handler with handler_id rpesent? + # @return [true, false] + def handler?(handler_id) + synchronize { @handlers.key? handler_id } + end + + # @return copy of the handlers + def handlers + synchronize { @handlers }.clone + end + + # install `Kernel#at_exit` callback to execute added handlers + def install + synchronize do + @installed ||= begin + at_exit { runner } + true + end + self + end + end + + # Will it run during `Kernel#at_exit` + def enabled? + synchronize { @enabled } + end + + # Configure if it runs during `Kernel#at_exit` + def enabled=(value) + synchronize { @enabled = value } + end + + # run the handlers manually + # @return ids of the handlers + def run + handlers, _ = synchronize { handlers, @handlers = @handlers, {} } + handlers.each do |_, handler| + begin + handler.call + rescue => error + Concurrent.global_logger.call(ERROR, error) + end + end + handlers.keys + end + + private + + def ns_initialize(enabled = true) + @handlers = {} + @enabled = enabled + end + + def runner + run if synchronize { @enabled } + end + end + + private_constant :AtExitImplementation + + # @see AtExitImplementation + # @!visibility private + AtExit = AtExitImplementation.new.install +end diff --git a/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/utility/engine.rb b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/utility/engine.rb new file mode 100644 index 0000000..bc4173e --- /dev/null +++ b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/utility/engine.rb @@ -0,0 +1,56 @@ +module Concurrent + module Utility + + # @!visibility private + module EngineDetector + def on_jruby? + ruby_engine == 'jruby' + end + + def on_jruby_9000? + on_jruby? && ruby_version(JRUBY_VERSION, :>=, 9, 0, 0) + end + + def on_cruby? + ruby_engine == 'ruby' + end + + def on_rbx? + ruby_engine == 'rbx' + end + + def on_truffleruby? + ruby_engine == 'truffleruby' + end + + def on_windows? + !(RbConfig::CONFIG['host_os'] =~ /mswin|mingw|cygwin/).nil? + end + + def on_osx? + !(RbConfig::CONFIG['host_os'] =~ /darwin|mac os/).nil? + end + + def on_linux? + !(RbConfig::CONFIG['host_os'] =~ /linux/).nil? + end + + def ruby_engine + defined?(RUBY_ENGINE) ? RUBY_ENGINE : 'ruby' + end + + def ruby_version(version = RUBY_VERSION, comparison, major, minor, patch) + result = (version.split('.').map(&:to_i) <=> [major, minor, patch]) + comparisons = { :== => [0], + :>= => [1, 0], + :<= => [-1, 0], + :> => [1], + :< => [-1] } + comparisons.fetch(comparison).include? result + end + end + end + + # @!visibility private + extend Utility::EngineDetector +end diff --git a/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/utility/monotonic_time.rb b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/utility/monotonic_time.rb new file mode 100644 index 0000000..c9f4b36 --- /dev/null +++ b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/utility/monotonic_time.rb @@ -0,0 +1,58 @@ +require 'concurrent/synchronization' + +module Concurrent + + class_definition = Class.new(Synchronization::LockableObject) do + def initialize + @last_time = Time.now.to_f + super() + end + + if defined?(Process::CLOCK_MONOTONIC) + # @!visibility private + def get_time + Process.clock_gettime(Process::CLOCK_MONOTONIC) + end + elsif Concurrent.on_jruby? + # @!visibility private + def get_time + java.lang.System.nanoTime() / 1_000_000_000.0 + end + else + + # @!visibility private + def get_time + synchronize do + now = Time.now.to_f + if @last_time < now + @last_time = now + else # clock has moved back in time + @last_time += 0.000_001 + end + end + end + + end + end + + # Clock that cannot be set and represents monotonic time since + # some unspecified starting point. + # + # @!visibility private + GLOBAL_MONOTONIC_CLOCK = class_definition.new + private_constant :GLOBAL_MONOTONIC_CLOCK + + # @!macro monotonic_get_time + # + # Returns the current time a tracked by the application monotonic clock. + # + # @return [Float] The current monotonic time since some unspecified + # starting point + # + # @!macro monotonic_clock_warning + def monotonic_time + GLOBAL_MONOTONIC_CLOCK.get_time + end + + module_function :monotonic_time +end diff --git a/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/utility/native_extension_loader.rb b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/utility/native_extension_loader.rb new file mode 100644 index 0000000..a944bd7 --- /dev/null +++ b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/utility/native_extension_loader.rb @@ -0,0 +1,79 @@ +require 'concurrent/utility/engine' + +module Concurrent + + module Utility + + # @!visibility private + module NativeExtensionLoader + + def allow_c_extensions? + Concurrent.on_cruby? + end + + def c_extensions_loaded? + defined?(@c_extensions_loaded) && @c_extensions_loaded + end + + def java_extensions_loaded? + defined?(@java_extensions_loaded) && @java_extensions_loaded + end + + def load_native_extensions + unless defined? Synchronization::AbstractObject + raise 'native_extension_loader loaded before Synchronization::AbstractObject' + end + + if Concurrent.on_cruby? && !c_extensions_loaded? + ['concurrent/concurrent_ruby_ext', + "concurrent/#{RUBY_VERSION[0..2]}/concurrent_ruby_ext" + ].each { |p| try_load_c_extension p } + end + + if Concurrent.on_jruby? && !java_extensions_loaded? + begin + require 'concurrent/concurrent_ruby.jar' + set_java_extensions_loaded + rescue LoadError => e + raise e, "Java extensions are required for JRuby.\n" + e.message, e.backtrace + end + end + end + + private + + def load_error_path(error) + if error.respond_to? :path + error.path + else + error.message.split(' -- ').last + end + end + + def set_c_extensions_loaded + @c_extensions_loaded = true + end + + def set_java_extensions_loaded + @java_extensions_loaded = true + end + + def try_load_c_extension(path) + require path + set_c_extensions_loaded + rescue LoadError => e + if load_error_path(e) == path + # move on with pure-Ruby implementations + # TODO (pitr-ch 12-Jul-2018): warning on verbose? + else + raise e + end + end + + end + end + + # @!visibility private + extend Utility::NativeExtensionLoader +end + diff --git a/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/utility/native_integer.rb b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/utility/native_integer.rb new file mode 100644 index 0000000..10719e7 --- /dev/null +++ b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/utility/native_integer.rb @@ -0,0 +1,53 @@ +module Concurrent + module Utility + # @private + module NativeInteger + # http://stackoverflow.com/questions/535721/ruby-max-integer + MIN_VALUE = -(2**(0.size * 8 - 2)) + MAX_VALUE = (2**(0.size * 8 - 2) - 1) + + def ensure_upper_bound(value) + if value > MAX_VALUE + raise RangeError.new("#{value} is greater than the maximum value of #{MAX_VALUE}") + end + value + end + + def ensure_lower_bound(value) + if value < MIN_VALUE + raise RangeError.new("#{value} is less than the maximum value of #{MIN_VALUE}") + end + value + end + + def ensure_integer(value) + unless value.is_a?(Integer) + raise ArgumentError.new("#{value} is not an Integer") + end + value + end + + def ensure_integer_and_bounds(value) + ensure_integer value + ensure_upper_bound value + ensure_lower_bound value + end + + def ensure_positive(value) + if value < 0 + raise ArgumentError.new("#{value} cannot be negative") + end + value + end + + def ensure_positive_and_no_zero(value) + if value < 1 + raise ArgumentError.new("#{value} cannot be negative or zero") + end + value + end + + extend self + end + end +end diff --git a/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/utility/processor_counter.rb b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/utility/processor_counter.rb new file mode 100644 index 0000000..6d6ae8d --- /dev/null +++ b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/utility/processor_counter.rb @@ -0,0 +1,158 @@ +require 'rbconfig' +require 'concurrent/delay' + +module Concurrent + module Utility + + # @!visibility private + class ProcessorCounter + def initialize + @processor_count = Delay.new { compute_processor_count } + @physical_processor_count = Delay.new { compute_physical_processor_count } + end + + # Number of processors seen by the OS and used for process scheduling. For + # performance reasons the calculated value will be memoized on the first + # call. + # + # When running under JRuby the Java runtime call + # `java.lang.Runtime.getRuntime.availableProcessors` will be used. According + # to the Java documentation this "value may change during a particular + # invocation of the virtual machine... [applications] should therefore + # occasionally poll this property." Subsequently the result will NOT be + # memoized under JRuby. + # + # On Windows the Win32 API will be queried for the + # `NumberOfLogicalProcessors from Win32_Processor`. This will return the + # total number "logical processors for the current instance of the + # processor", which taked into account hyperthreading. + # + # * AIX: /usr/sbin/pmcycles (AIX 5+), /usr/sbin/lsdev + # * Alpha: /usr/bin/nproc (/proc/cpuinfo exists but cannot be used) + # * BSD: /sbin/sysctl + # * Cygwin: /proc/cpuinfo + # * Darwin: /usr/bin/hwprefs, /usr/sbin/sysctl + # * HP-UX: /usr/sbin/ioscan + # * IRIX: /usr/sbin/sysconf + # * Linux: /proc/cpuinfo + # * Minix 3+: /proc/cpuinfo + # * Solaris: /usr/sbin/psrinfo + # * Tru64 UNIX: /usr/sbin/psrinfo + # * UnixWare: /usr/sbin/psrinfo + # + # @return [Integer] number of processors seen by the OS or Java runtime + # + # @see https://github.com/grosser/parallel/blob/4fc8b89d08c7091fe0419ca8fba1ec3ce5a8d185/lib/parallel.rb + # + # @see http://docs.oracle.com/javase/6/docs/api/java/lang/Runtime.html#availableProcessors() + # @see http://msdn.microsoft.com/en-us/library/aa394373(v=vs.85).aspx + def processor_count + @processor_count.value + end + + # Number of physical processor cores on the current system. For performance + # reasons the calculated value will be memoized on the first call. + # + # On Windows the Win32 API will be queried for the `NumberOfCores from + # Win32_Processor`. This will return the total number "of cores for the + # current instance of the processor." On Unix-like operating systems either + # the `hwprefs` or `sysctl` utility will be called in a subshell and the + # returned value will be used. In the rare case where none of these methods + # work or an exception is raised the function will simply return 1. + # + # @return [Integer] number physical processor cores on the current system + # + # @see https://github.com/grosser/parallel/blob/4fc8b89d08c7091fe0419ca8fba1ec3ce5a8d185/lib/parallel.rb + # + # @see http://msdn.microsoft.com/en-us/library/aa394373(v=vs.85).aspx + # @see http://www.unix.com/man-page/osx/1/HWPREFS/ + # @see http://linux.die.net/man/8/sysctl + def physical_processor_count + @physical_processor_count.value + end + + private + + def compute_processor_count + if Concurrent.on_jruby? + java.lang.Runtime.getRuntime.availableProcessors + else + os_name = RbConfig::CONFIG["target_os"] + if os_name =~ /mingw|mswin/ + require 'win32ole' + result = WIN32OLE.connect("winmgmts://").ExecQuery( + "select NumberOfLogicalProcessors from Win32_Processor") + result.to_enum.collect(&:NumberOfLogicalProcessors).reduce(:+) + elsif File.readable?("/proc/cpuinfo") && (cpuinfo_count = IO.read("/proc/cpuinfo").scan(/^processor/).size) > 0 + cpuinfo_count + elsif File.executable?("/usr/bin/nproc") + IO.popen("/usr/bin/nproc --all", &:read).to_i + elsif File.executable?("/usr/bin/hwprefs") + IO.popen("/usr/bin/hwprefs thread_count", &:read).to_i + elsif File.executable?("/usr/sbin/psrinfo") + IO.popen("/usr/sbin/psrinfo", &:read).scan(/^.*on-*line/).size + elsif File.executable?("/usr/sbin/ioscan") + IO.popen("/usr/sbin/ioscan -kC processor", &:read).scan(/^.*processor/).size + elsif File.executable?("/usr/sbin/pmcycles") + IO.popen("/usr/sbin/pmcycles -m", &:read).count("\n") + elsif File.executable?("/usr/sbin/lsdev") + IO.popen("/usr/sbin/lsdev -Cc processor -S 1", &:read).count("\n") + elsif File.executable?("/usr/sbin/sysconf") and os_name =~ /irix/i + IO.popen("/usr/sbin/sysconf NPROC_ONLN", &:read).to_i + elsif File.executable?("/usr/sbin/sysctl") + IO.popen("/usr/sbin/sysctl -n hw.ncpu", &:read).to_i + elsif File.executable?("/sbin/sysctl") + IO.popen("/sbin/sysctl -n hw.ncpu", &:read).to_i + else + # TODO (pitr-ch 05-Nov-2016): warn about failures + 1 + end + end + rescue + return 1 + end + + def compute_physical_processor_count + ppc = case RbConfig::CONFIG["target_os"] + when /darwin1/ + IO.popen("/usr/sbin/sysctl -n hw.physicalcpu", &:read).to_i + when /linux/ + cores = {} # unique physical ID / core ID combinations + phy = 0 + IO.read("/proc/cpuinfo").scan(/^physical id.*|^core id.*/) do |ln| + if ln.start_with?("physical") + phy = ln[/\d+/] + elsif ln.start_with?("core") + cid = phy + ":" + ln[/\d+/] + cores[cid] = true if not cores[cid] + end + end + cores.count + when /mswin|mingw/ + require 'win32ole' + result_set = WIN32OLE.connect("winmgmts://").ExecQuery( + "select NumberOfCores from Win32_Processor") + result_set.to_enum.collect(&:NumberOfCores).reduce(:+) + else + processor_count + end + # fall back to logical count if physical info is invalid + ppc > 0 ? ppc : processor_count + rescue + return 1 + end + end + end + + # create the default ProcessorCounter on load + @processor_counter = Utility::ProcessorCounter.new + singleton_class.send :attr_reader, :processor_counter + + def self.processor_count + processor_counter.processor_count + end + + def self.physical_processor_count + processor_counter.physical_processor_count + end +end diff --git a/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/version.rb b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/version.rb new file mode 100644 index 0000000..0f611c5 --- /dev/null +++ b/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/version.rb @@ -0,0 +1,3 @@ +module Concurrent + VERSION = '1.1.5' +end diff --git a/vendor/bundle/specifications/concurrent-ruby-1.1.5.gemspec b/vendor/bundle/specifications/concurrent-ruby-1.1.5.gemspec new file mode 100644 index 0000000..d615761 --- /dev/null +++ b/vendor/bundle/specifications/concurrent-ruby-1.1.5.gemspec @@ -0,0 +1,24 @@ +# -*- encoding: utf-8 -*- +# stub: concurrent-ruby 1.1.5 ruby lib + +Gem::Specification.new do |s| + s.name = "concurrent-ruby".freeze + s.version = "1.1.5" + + s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version= + s.metadata = { "source_code_uri" => "https://github.com/ruby-concurrency/concurrent-ruby" } if s.respond_to? :metadata= + s.require_paths = ["lib".freeze] + s.authors = ["Jerry D'Antonio".freeze, "Petr Chalupa".freeze, "The Ruby Concurrency Team".freeze] + s.date = "2019-03-11" + s.description = "Modern concurrency tools including agents, futures, promises, thread pools, actors, supervisors, and more.\nInspired by Erlang, Clojure, Go, JavaScript, actors, and classic concurrency patterns.\n".freeze + s.email = "concurrent-ruby@googlegroups.com".freeze + s.extra_rdoc_files = ["README.md".freeze, "LICENSE.md".freeze, "CHANGELOG.md".freeze] + s.files = ["CHANGELOG.md".freeze, "LICENSE.md".freeze, "README.md".freeze] + s.homepage = "http://www.concurrent-ruby.com".freeze + s.licenses = ["MIT".freeze] + s.required_ruby_version = Gem::Requirement.new(">= 1.9.3".freeze) + s.rubygems_version = "2.7.6".freeze + s.summary = "Modern concurrency tools for Ruby. Inspired by Erlang, Clojure, Scala, Haskell, F#, C#, Java, and classic concurrency patterns.".freeze + + s.installed_by_version = "2.7.6" if s.respond_to? :installed_by_version +end