summaryrefslogtreecommitdiff
path: root/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib')
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent-ruby.rb5
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent.rb134
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/agent.rb588
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/array.rb56
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/async.rb449
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/atom.rb222
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/atomic/atomic_boolean.rb127
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/atomic/atomic_fixnum.rb144
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/atomic/atomic_markable_reference.rb167
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/atomic/atomic_reference.rb135
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/atomic/count_down_latch.rb100
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/atomic/cyclic_barrier.rb128
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/atomic/event.rb109
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/atomic/fiber_local_var.rb109
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/atomic/java_count_down_latch.rb43
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/atomic/locals.rb189
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/atomic/lock_local_var.rb28
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/atomic/mutex_atomic_boolean.rb68
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/atomic/mutex_atomic_fixnum.rb81
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/atomic/mutex_count_down_latch.rb44
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/atomic/mutex_semaphore.rb131
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/atomic/read_write_lock.rb255
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/atomic/reentrant_read_write_lock.rb379
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/atomic/semaphore.rb163
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/atomic/thread_local_var.rb111
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/atomic_reference/atomic_direct_update.rb37
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/atomic_reference/mutex_atomic.rb67
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/atomic_reference/numeric_cas_wrapper.rb28
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/atomics.rb10
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/collection/copy_on_notify_observer_set.rb107
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/collection/copy_on_write_observer_set.rb111
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/collection/java_non_concurrent_priority_queue.rb84
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/collection/lock_free_stack.rb160
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/collection/map/mri_map_backend.rb66
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/collection/map/non_concurrent_map_backend.rb148
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/collection/map/synchronized_map_backend.rb85
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/collection/map/truffleruby_map_backend.rb14
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/collection/non_concurrent_priority_queue.rb143
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/collection/ruby_non_concurrent_priority_queue.rb160
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/concern/deprecation.rb34
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/concern/dereferenceable.rb73
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/concern/logging.rb121
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/concern/obligation.rb220
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/concern/observable.rb110
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/concurrent_ruby.jarbin0 -> 136775 bytes
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/configuration.rb105
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/constants.rb8
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/dataflow.rb81
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/delay.rb199
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/errors.rb74
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/exchanger.rb353
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/executor/abstract_executor_service.rb131
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/executor/cached_thread_pool.rb62
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/executor/executor_service.rb185
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/executor/fixed_thread_pool.rb224
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/executor/immediate_executor.rb66
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/executor/indirect_immediate_executor.rb44
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/executor/java_executor_service.rb100
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/executor/java_single_thread_executor.rb30
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/executor/java_thread_pool_executor.rb145
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/executor/ruby_executor_service.rb82
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/executor/ruby_single_thread_executor.rb21
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/executor/ruby_thread_pool_executor.rb373
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/executor/safe_task_executor.rb35
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/executor/serial_executor_service.rb34
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/executor/serialized_execution.rb107
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/executor/serialized_execution_delegator.rb28
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/executor/simple_executor_service.rb103
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/executor/single_thread_executor.rb57
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/executor/thread_pool_executor.rb88
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/executor/timer_set.rb176
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/executors.rb20
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/future.rb141
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/hash.rb52
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/immutable_struct.rb101
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/ivar.rb208
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/map.rb350
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/maybe.rb229
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/mutable_struct.rb239
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/mvar.rb242
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/options.rb42
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/promise.rb580
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/promises.rb2178
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/re_include.rb60
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/scheduled_task.rb331
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/set.rb64
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/settable_struct.rb139
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/synchronization.rb13
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/synchronization/abstract_lockable_object.rb102
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/synchronization/abstract_object.rb22
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/synchronization/abstract_struct.rb171
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/synchronization/condition.rb62
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/synchronization/full_memory_barrier.rb29
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/synchronization/jruby_lockable_object.rb15
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/synchronization/lock.rb38
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/synchronization/lockable_object.rb75
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/synchronization/mutex_lockable_object.rb89
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/synchronization/object.rb151
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/synchronization/safe_initialization.rb36
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/synchronization/volatile.rb101
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/thread_safe/synchronized_delegator.rb47
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/thread_safe/util.rb16
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/thread_safe/util/adder.rb74
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/thread_safe/util/data_structures.rb52
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/thread_safe/util/power_of_two_tuple.rb38
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/thread_safe/util/striped64.rb246
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/thread_safe/util/volatile.rb75
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/thread_safe/util/xor_shift_random.rb50
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/timer_task.rb361
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/tuple.rb82
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/tvar.rb222
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/utility/engine.rb45
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/utility/monotonic_time.rb19
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/utility/native_extension_loader.rb77
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/utility/native_integer.rb54
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/utility/processor_counter.rb220
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/version.rb3
117 files changed, 16215 insertions, 0 deletions
diff --git a/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent-ruby.rb b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent-ruby.rb
new file mode 100644
index 0000000..e9a3dea
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent-ruby.rb
@@ -0,0 +1,5 @@
+# This file is here so that there is a file with the same name as the gem that
+# can be required by Bundler.require. Applications should normally
+# require 'concurrent'.
+
+require_relative "concurrent"
diff --git a/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent.rb b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent.rb
new file mode 100644
index 0000000..87de46f
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/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/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/agent.rb b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/agent.rb
new file mode 100644
index 0000000..dc8a260
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/agent.rb
@@ -0,0 +1,588 @@
+require 'concurrent/configuration'
+require 'concurrent/atomic/atomic_reference'
+require 'concurrent/atomic/count_down_latch'
+require 'concurrent/atomic/thread_local_var'
+require 'concurrent/collection/copy_on_write_observer_set'
+require 'concurrent/concern/observable'
+require 'concurrent/synchronization/lockable_object'
+
+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<Object>] 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<Object>] 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 timeout 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<Concurrent::Agent>] 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<Concurrent::Agent>] 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<Concurrent::Agent>] agents the Agents on which to wait
+ # @return [Boolean] true if all actions complete before timeout
+ #
+ # @raise [Concurrent::TimeoutError] when timeout 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/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/array.rb b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/array.rb
new file mode 100644
index 0000000..c8761af
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/array.rb
@@ -0,0 +1,56 @@
+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/Array.html Ruby standard library `Array`
+
+ # @!macro internal_implementation_note
+ ArrayImplementation = case
+ when Concurrent.on_cruby?
+ # Array is not fully thread-safe on CRuby, see
+ # https://github.com/ruby-concurrency/concurrent-ruby/issues/929
+ # So we will need to add synchronization here
+ ::Array
+
+ when Concurrent.on_jruby?
+ require 'jruby/synchronized'
+
+ class JRubyArray < ::Array
+ include JRuby::Synchronized
+ end
+ JRubyArray
+
+ 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/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/async.rb b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/async.rb
new file mode 100644
index 0000000..97c5a6b
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/async.rb
@@ -0,0 +1,449 @@
+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
+ # ```
+ #
+ # 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)
+ #
+ # Instantiate a new object and ensure proper initialization of the
+ # synchronization mechanisms.
+ #
+ # @param [Array<Object>] 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
+ ruby2_keywords :new if respond_to?(:ruby2_keywords, true)
+ 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
+ @ruby_pid = $$
+ 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
+ reset_if_forked
+ @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
+
+ def reset_if_forked
+ if $$ != @ruby_pid
+ @queue.clear
+ @ruby_pid = $$
+ 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/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/atom.rb b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/atom.rb
new file mode 100644
index 0000000..f590a23
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/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/object'
+
+# @!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 updated
+# 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 (preferably) 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/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/atomic/atomic_boolean.rb b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/atomic/atomic_boolean.rb
new file mode 100644
index 0000000..f775691
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/atomic/atomic_boolean.rb
@@ -0,0 +1,127 @@
+require 'concurrent/utility/native_extension_loader' # load native parts first
+
+require 'concurrent/atomic/mutex_atomic_boolean'
+
+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 if value has changed, otherwise false
+
+ # @!macro atomic_boolean_method_make_false
+ #
+ # Explicitly sets the value to false.
+ #
+ # @return [Boolean] true if 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 Concurrent.on_cruby? && Concurrent.c_extensions_loaded?
+ CAtomicBoolean
+ when Concurrent.on_jruby?
+ JavaAtomicBoolean
+ 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/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/atomic/atomic_fixnum.rb b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/atomic/atomic_fixnum.rb
new file mode 100644
index 0000000..26cd05d
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/atomic/atomic_fixnum.rb
@@ -0,0 +1,144 @@
+require 'concurrent/utility/native_extension_loader' # load native parts first
+
+require 'concurrent/atomic/mutex_atomic_fixnum'
+
+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 Concurrent.on_cruby? && Concurrent.c_extensions_loaded?
+ CAtomicFixnum
+ when Concurrent.on_jruby?
+ JavaAtomicFixnum
+ 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/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/atomic/atomic_markable_reference.rb b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/atomic/atomic_markable_reference.rb
new file mode 100644
index 0000000..e16be65
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/atomic/atomic_markable_reference.rb
@@ -0,0 +1,167 @@
+require 'concurrent/errors'
+require 'concurrent/synchronization/object'
+
+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/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/atomic/atomic_reference.rb b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/atomic/atomic_reference.rb
new file mode 100644
index 0000000..bb5fb77
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/atomic/atomic_reference.rb
@@ -0,0 +1,135 @@
+require 'concurrent/utility/native_extension_loader' # load native parts first
+
+require 'concurrent/atomic_reference/atomic_direct_update'
+require 'concurrent/atomic_reference/numeric_cas_wrapper'
+require 'concurrent/atomic_reference/mutex_atomic'
+
+# Shim for TruffleRuby::AtomicReference
+if Concurrent.on_truffleruby? && !defined?(TruffleRuby::AtomicReference)
+ # @!visibility private
+ module TruffleRuby
+ AtomicReference = Truffle::AtomicReference
+ end
+end
+
+module Concurrent
+
+ # @!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
+ TruffleRubyAtomicReference
+ else
+ MutexAtomicReference
+ end
+ private_constant :AtomicReferenceImplementation
+
+ # 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
+ # 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
+ #
+ # @!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
+ #
+ # @!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
+ 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/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/atomic/count_down_latch.rb b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/atomic/count_down_latch.rb
new file mode 100644
index 0000000..d883aed
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/atomic/count_down_latch.rb
@@ -0,0 +1,100 @@
+require 'concurrent/utility/engine'
+require 'concurrent/atomic/mutex_count_down_latch'
+require 'concurrent/atomic/java_count_down_latch'
+
+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/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/atomic/cyclic_barrier.rb b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/atomic/cyclic_barrier.rb
new file mode 100644
index 0000000..9ebe29d
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/atomic/cyclic_barrier.rb
@@ -0,0 +1,128 @@
+require 'concurrent/synchronization/lockable_object'
+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/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/atomic/event.rb b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/atomic/event.rb
new file mode 100644
index 0000000..ccf84c9
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/atomic/event.rb
@@ -0,0 +1,109 @@
+require 'thread'
+require 'concurrent/synchronization/lockable_object'
+
+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 occurred"
+ # end
+ #
+ # t2 = Thread.new do
+ # puts "t2 calling set"
+ # event.set
+ # end
+ #
+ # [t1, t2].each(&:join)
+ #
+ # # prints:
+ # # t1 is waiting
+ # # t2 calling set
+ # # 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/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/atomic/fiber_local_var.rb b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/atomic/fiber_local_var.rb
new file mode 100644
index 0000000..e90fc24
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/atomic/fiber_local_var.rb
@@ -0,0 +1,109 @@
+require 'concurrent/constants'
+require_relative 'locals'
+
+module Concurrent
+
+ # A `FiberLocalVar` is a variable where the value is different for each fiber.
+ # Each variable may have a default value, but when you modify the variable only
+ # the current fiber will ever see that change.
+ #
+ # This is similar to Ruby's built-in fiber-local variables (`Thread.current[:name]`),
+ # but with these major advantages:
+ # * `FiberLocalVar` has its own identity, it doesn't need a Symbol.
+ # * Each Ruby's built-in fiber-local variable leaks some memory forever (it's a Symbol held forever on the fiber),
+ # so it's only OK to create a small amount of them.
+ # `FiberLocalVar` has no such issue and it is fine to create many of them.
+ # * Ruby's built-in fiber-local variables leak forever the value set on each fiber (unless set to nil explicitly).
+ # `FiberLocalVar` automatically removes the mapping for each fiber once the `FiberLocalVar` instance is GC'd.
+ #
+ # @example
+ # v = FiberLocalVar.new(14)
+ # v.value #=> 14
+ # v.value = 2
+ # v.value #=> 2
+ #
+ # @example
+ # v = FiberLocalVar.new(14)
+ #
+ # Fiber.new do
+ # v.value #=> 14
+ # v.value = 1
+ # v.value #=> 1
+ # end.resume
+ #
+ # Fiber.new do
+ # v.value #=> 14
+ # v.value = 2
+ # v.value #=> 2
+ # end.resume
+ #
+ # v.value #=> 14
+ class FiberLocalVar
+ LOCALS = FiberLocals.new
+
+ # Creates a fiber 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 fiber
+ 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
+
+ @index = LOCALS.next_index(self)
+ end
+
+ # Returns the value in the current fiber's copy of this fiber-local variable.
+ #
+ # @return [Object] the current value
+ def value
+ LOCALS.fetch(@index) { default }
+ end
+
+ # Sets the current fiber's copy of this fiber-local variable to the specified value.
+ #
+ # @param [Object] value the value to set
+ # @return [Object] the new value
+ def value=(value)
+ LOCALS.set(@index, value)
+ end
+
+ # Bind the given value to fiber 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
+ def bind(value)
+ if block_given?
+ old_value = self.value
+ self.value = value
+ begin
+ yield
+ ensure
+ self.value = old_value
+ end
+ end
+ end
+
+ protected
+
+ # @!visibility private
+ def default
+ if @default_block
+ self.value = @default_block.call
+ else
+ @default
+ end
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/atomic/java_count_down_latch.rb b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/atomic/java_count_down_latch.rb
new file mode 100644
index 0000000..3c119bc
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/atomic/java_count_down_latch.rb
@@ -0,0 +1,43 @@
+if Concurrent.on_jruby?
+ require 'concurrent/utility/native_extension_loader'
+
+ 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/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/atomic/locals.rb b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/atomic/locals.rb
new file mode 100644
index 0000000..0a276ae
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/atomic/locals.rb
@@ -0,0 +1,189 @@
+require 'fiber'
+require 'concurrent/utility/engine'
+require 'concurrent/constants'
+
+module Concurrent
+ # @!visibility private
+ # @!macro internal_implementation_note
+ #
+ # An abstract implementation of local storage, with sub-classes for
+ # per-thread and per-fiber locals.
+ #
+ # Each execution context (EC, thread or fiber) has a lazily initialized array
+ # of local variable values. Each time a new local variable is created, we
+ # allocate an "index" for it.
+ #
+ # For example, if the allocated index is 1, that means slot #1 in EVERY EC's
+ # locals array will be used for the value of that variable.
+ #
+ # The good thing about using a per-EC structure to hold values, rather than
+ # a global, 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 local variable is GC'd, 1) we need to recover its index
+ # for use by other new local variables (otherwise the locals 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
+ # local when the index is reused).
+ #
+ # Because we need to null out freed slots, we need to keep references to
+ # ALL the locals arrays, so we can null out the appropriate slots in all of
+ # them. This is why we need to use a finalizer to clean up the locals array
+ # when the EC goes out of scope.
+ class AbstractLocals
+ def initialize
+ @free = []
+ @lock = Mutex.new
+ @all_arrays = {}
+ @next = 0
+ end
+
+ def synchronize
+ @lock.synchronize { yield }
+ end
+
+ if Concurrent.on_cruby?
+ def weak_synchronize
+ yield
+ end
+ else
+ alias_method :weak_synchronize, :synchronize
+ end
+
+ def next_index(local)
+ index = synchronize do
+ if @free.empty?
+ @next += 1
+ else
+ @free.pop
+ end
+ end
+
+ # When the local goes out of scope, we should free the associated index
+ # and all values stored into it.
+ ObjectSpace.define_finalizer(local, local_finalizer(index))
+
+ index
+ end
+
+ def free_index(index)
+ weak_synchronize do
+ # The cost of GC'ing a TLV is linear in the number of ECs using local
+ # variables. But that is natural! More ECs means more storage is used
+ # per local variable. So naturally more CPU time is required to free
+ # more storage.
+ #
+ # DO NOT use each_value which might conflict with new pair assignment
+ # into the hash in #set method.
+ @all_arrays.values.each do |locals|
+ locals[index] = nil
+ end
+
+ # free index has to be published after the arrays are cleared:
+ @free << index
+ end
+ end
+
+ def fetch(index)
+ locals = self.locals
+ value = locals ? locals[index] : nil
+
+ if nil == value
+ yield
+ elsif NULL.equal?(value)
+ nil
+ else
+ value
+ end
+ end
+
+ def set(index, value)
+ locals = self.locals!
+ locals[index] = (nil == value ? NULL : value)
+
+ value
+ end
+
+ private
+
+ # When the local goes out of scope, clean up that slot across all locals currently assigned.
+ def local_finalizer(index)
+ proc do
+ free_index(index)
+ end
+ end
+
+ # When a thread/fiber goes out of scope, remove the array from @all_arrays.
+ def thread_fiber_finalizer(array_object_id)
+ proc do
+ weak_synchronize do
+ @all_arrays.delete(array_object_id)
+ end
+ end
+ end
+
+ # Returns the locals for the current scope, or nil if none exist.
+ def locals
+ raise NotImplementedError
+ end
+
+ # Returns the locals for the current scope, creating them if necessary.
+ def locals!
+ raise NotImplementedError
+ end
+ end
+
+ # @!visibility private
+ # @!macro internal_implementation_note
+ # An array-backed storage of indexed variables per thread.
+ class ThreadLocals < AbstractLocals
+ def locals
+ Thread.current.thread_variable_get(:concurrent_thread_locals)
+ end
+
+ def locals!
+ thread = Thread.current
+ locals = thread.thread_variable_get(:concurrent_thread_locals)
+
+ unless locals
+ locals = thread.thread_variable_set(:concurrent_thread_locals, [])
+ weak_synchronize do
+ @all_arrays[locals.object_id] = locals
+ end
+ # When the thread goes out of scope, we should delete the associated locals:
+ ObjectSpace.define_finalizer(thread, thread_fiber_finalizer(locals.object_id))
+ end
+
+ locals
+ end
+ end
+
+ # @!visibility private
+ # @!macro internal_implementation_note
+ # An array-backed storage of indexed variables per fiber.
+ class FiberLocals < AbstractLocals
+ def locals
+ Thread.current[:concurrent_fiber_locals]
+ end
+
+ def locals!
+ thread = Thread.current
+ locals = thread[:concurrent_fiber_locals]
+
+ unless locals
+ locals = thread[:concurrent_fiber_locals] = []
+ weak_synchronize do
+ @all_arrays[locals.object_id] = locals
+ end
+ # When the fiber goes out of scope, we should delete the associated locals:
+ ObjectSpace.define_finalizer(Fiber.current, thread_fiber_finalizer(locals.object_id))
+ end
+
+ locals
+ end
+ end
+
+ private_constant :AbstractLocals, :ThreadLocals, :FiberLocals
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/atomic/lock_local_var.rb b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/atomic/lock_local_var.rb
new file mode 100644
index 0000000..ebf23a2
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/atomic/lock_local_var.rb
@@ -0,0 +1,28 @@
+require 'concurrent/utility/engine'
+require_relative 'fiber_local_var'
+require_relative 'thread_local_var'
+
+module Concurrent
+ # @!visibility private
+ def self.mutex_owned_per_thread?
+ return false if Concurrent.on_jruby? || Concurrent.on_truffleruby?
+
+ mutex = Mutex.new
+ # Lock the mutex:
+ mutex.synchronize do
+ # Check if the mutex is still owned in a child fiber:
+ Fiber.new { mutex.owned? }.resume
+ end
+ end
+
+ if mutex_owned_per_thread?
+ LockLocalVar = ThreadLocalVar
+ else
+ LockLocalVar = FiberLocalVar
+ end
+
+ # Either {FiberLocalVar} or {ThreadLocalVar} depending on whether Mutex (and Monitor)
+ # are held, respectively, per Fiber or per Thread.
+ class LockLocalVar
+ end
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/atomic/mutex_atomic_boolean.rb b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/atomic/mutex_atomic_boolean.rb
new file mode 100644
index 0000000..015996b
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/atomic/mutex_atomic_boolean.rb
@@ -0,0 +1,68 @@
+require 'concurrent/synchronization/safe_initialization'
+
+module Concurrent
+
+ # @!macro atomic_boolean
+ # @!visibility private
+ # @!macro internal_implementation_note
+ class MutexAtomicBoolean
+ extend Concurrent::Synchronization::SafeInitialization
+
+ # @!macro atomic_boolean_method_initialize
+ def initialize(initial = false)
+ super()
+ @Lock = ::Mutex.new
+ @value = !!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 synchronize
+ if @Lock.owned?
+ yield
+ else
+ @Lock.synchronize { yield }
+ end
+ end
+
+ private
+
+ # @!visibility private
+ def ns_make_value(value)
+ old = @value
+ @value = value
+ old != @value
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/atomic/mutex_atomic_fixnum.rb b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/atomic/mutex_atomic_fixnum.rb
new file mode 100644
index 0000000..0ca3955
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/atomic/mutex_atomic_fixnum.rb
@@ -0,0 +1,81 @@
+require 'concurrent/synchronization/safe_initialization'
+require 'concurrent/utility/native_integer'
+
+module Concurrent
+
+ # @!macro atomic_fixnum
+ # @!visibility private
+ # @!macro internal_implementation_note
+ class MutexAtomicFixnum
+ extend Concurrent::Synchronization::SafeInitialization
+
+ # @!macro atomic_fixnum_method_initialize
+ def initialize(initial = 0)
+ super()
+ @Lock = ::Mutex.new
+ ns_set(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 synchronize
+ if @Lock.owned?
+ yield
+ else
+ @Lock.synchronize { yield }
+ end
+ 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/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/atomic/mutex_count_down_latch.rb b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/atomic/mutex_count_down_latch.rb
new file mode 100644
index 0000000..29aa1ca
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/atomic/mutex_count_down_latch.rb
@@ -0,0 +1,44 @@
+require 'concurrent/synchronization/lockable_object'
+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/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/atomic/mutex_semaphore.rb b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/atomic/mutex_semaphore.rb
new file mode 100644
index 0000000..4347289
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/atomic/mutex_semaphore.rb
@@ -0,0 +1,131 @@
+require 'concurrent/synchronization/lockable_object'
+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)
+ end
+
+ return unless block_given?
+
+ begin
+ yield
+ ensure
+ release(permits)
+ 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
+
+ acquired = synchronize do
+ if timeout.nil?
+ try_acquire_now(permits)
+ else
+ try_acquire_timed(permits, timeout)
+ end
+ end
+
+ return acquired unless block_given?
+ return unless acquired
+
+ begin
+ yield
+ ensure
+ release(permits)
+ 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/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/atomic/read_write_lock.rb b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/atomic/read_write_lock.rb
new file mode 100644
index 0000000..b26bd17
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/atomic/read_write_lock.rb
@@ -0,0 +1,255 @@
+require 'thread'
+require 'concurrent/atomic/atomic_fixnum'
+require 'concurrent/errors'
+require 'concurrent/synchronization/object'
+require 'concurrent/synchronization/lock'
+
+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/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/atomic/reentrant_read_write_lock.rb b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/atomic/reentrant_read_write_lock.rb
new file mode 100644
index 0000000..6d72a3a
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/atomic/reentrant_read_write_lock.rb
@@ -0,0 +1,379 @@
+require 'thread'
+require 'concurrent/atomic/atomic_reference'
+require 'concurrent/atomic/atomic_fixnum'
+require 'concurrent/errors'
+require 'concurrent/synchronization/object'
+require 'concurrent/synchronization/lock'
+require 'concurrent/atomic/lock_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 = LockLocalVar.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 held > 0 && @Counter.compare_and_set(1, c+RUNNING_WRITER)
+ # If we are the only one reader and successfully swap the RUNNING_WRITER bit on, then we can go ahead
+ @HeldCount.value = held + WRITE_LOCK_HELD
+ return true
+ 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/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/atomic/semaphore.rb b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/atomic/semaphore.rb
new file mode 100644
index 0000000..f0799f0
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/atomic/semaphore.rb
@@ -0,0 +1,163 @@
+require 'concurrent/atomic/mutex_semaphore'
+
+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
+
+ # @!macro semaphore_method_acquire
+ #
+ # Acquires the given number of permits from this semaphore,
+ # blocking until all are available. If a block is given,
+ # yields to it and releases the permits afterwards.
+ #
+ # @param [Fixnum] permits Number of permits to acquire
+ #
+ # @raise [ArgumentError] if `permits` is not an integer or is less than zero
+ #
+ # @return [nil, BasicObject] Without a block, `nil` is returned. If a block
+ # is given, its return value is returned.
+
+ # @!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. If a block is given, yields to it if the permits
+ # were successfully acquired, and releases them afterward, returning the
+ # block's return value.
+ #
+ # @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 zero
+ #
+ # @return [true, false, nil, BasicObject] `false` if no permits are
+ # available, `true` when acquired a permit. If a block is given, the
+ # block's return value is returned if the permits were acquired; if not,
+ # `nil` is returned.
+
+ # @!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 zero
+ #
+ # @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 = if Concurrent.on_jruby?
+ require 'concurrent/utility/native_extension_loader'
+ 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.
+ # Alternatively, permits may be acquired within a block, and automatically
+ # released after the block finishes executing.
+ #
+ # @!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
+ #
+ # @example
+ # semaphore = Concurrent::Semaphore.new(1)
+ #
+ # puts semaphore.available_permits
+ # semaphore.acquire do
+ # puts semaphore.available_permits
+ # end
+ # puts semaphore.available_permits
+ #
+ # # prints:
+ # # 1
+ # # 0
+ # # 1
+ class Semaphore < SemaphoreImplementation
+ end
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/atomic/thread_local_var.rb b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/atomic/thread_local_var.rb
new file mode 100644
index 0000000..3b7e12b
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/atomic/thread_local_var.rb
@@ -0,0 +1,111 @@
+require 'concurrent/constants'
+require_relative 'locals'
+
+module Concurrent
+
+ # 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.
+ #
+ # This is similar to Ruby's built-in thread-local variables (`Thread#thread_variable_get`),
+ # but with these major advantages:
+ # * `ThreadLocalVar` has its own identity, it doesn't need a Symbol.
+ # * Each Ruby's built-in thread-local variable leaks some memory forever (it's a Symbol held forever on the thread),
+ # so it's only OK to create a small amount of them.
+ # `ThreadLocalVar` has no such issue and it is fine to create many of them.
+ # * Ruby's built-in thread-local variables leak forever the value set on each thread (unless set to nil explicitly).
+ # `ThreadLocalVar` automatically removes the mapping for each thread once the `ThreadLocalVar` instance is GC'd.
+ #
+ # @!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
+ class ThreadLocalVar
+ LOCALS = ThreadLocals.new
+
+ # 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
+ 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
+
+ @index = LOCALS.next_index(self)
+ end
+
+ # Returns the value in the current thread's copy of this thread-local variable.
+ #
+ # @return [Object] the current value
+ def value
+ LOCALS.fetch(@index) { default }
+ end
+
+ # 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
+ def value=(value)
+ LOCALS.set(@index, value)
+ end
+
+ # 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
+ def bind(value)
+ if block_given?
+ old_value = self.value
+ self.value = value
+ begin
+ yield
+ ensure
+ self.value = old_value
+ end
+ end
+ end
+
+ protected
+
+ # @!visibility private
+ def default
+ if @default_block
+ self.value = @default_block.call
+ else
+ @default
+ end
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/atomic_reference/atomic_direct_update.rb b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/atomic_reference/atomic_direct_update.rb
new file mode 100644
index 0000000..5d2d7ed
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/atomic_reference/atomic_direct_update.rb
@@ -0,0 +1,37 @@
+require 'concurrent/errors'
+
+module Concurrent
+
+ # Define update methods that use direct paths
+ #
+ # @!visibility private
+ # @!macro internal_implementation_note
+ module AtomicDirectUpdate
+ def update
+ true until compare_and_set(old_value = get, new_value = yield(old_value))
+ new_value
+ end
+
+ def try_update
+ old_value = get
+ new_value = yield old_value
+
+ return unless compare_and_set old_value, new_value
+
+ new_value
+ end
+
+ 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
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/atomic_reference/mutex_atomic.rb b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/atomic_reference/mutex_atomic.rb
new file mode 100644
index 0000000..e5e2a63
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/atomic_reference/mutex_atomic.rb
@@ -0,0 +1,67 @@
+require 'concurrent/atomic_reference/atomic_direct_update'
+require 'concurrent/atomic_reference/numeric_cas_wrapper'
+require 'concurrent/synchronization/safe_initialization'
+
+module Concurrent
+
+ # @!visibility private
+ # @!macro internal_implementation_note
+ class MutexAtomicReference
+ extend Concurrent::Synchronization::SafeInitialization
+ include AtomicDirectUpdate
+ include AtomicNumericCompareAndSetWrapper
+ alias_method :compare_and_swap, :compare_and_set
+
+ # @!macro atomic_reference_method_initialize
+ def initialize(value = nil)
+ super()
+ @Lock = ::Mutex.new
+ @value = 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
+
+ # @!visibility private
+ def synchronize
+ if @Lock.owned?
+ yield
+ else
+ @Lock.synchronize { yield }
+ end
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/atomic_reference/numeric_cas_wrapper.rb b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/atomic_reference/numeric_cas_wrapper.rb
new file mode 100644
index 0000000..709a382
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/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/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/atomics.rb b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/atomics.rb
new file mode 100644
index 0000000..16cbe66
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/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/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/collection/copy_on_notify_observer_set.rb b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/collection/copy_on_notify_observer_set.rb
new file mode 100644
index 0000000..7c700bd
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/collection/copy_on_notify_observer_set.rb
@@ -0,0 +1,107 @@
+require 'concurrent/synchronization/lockable_object'
+
+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/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/collection/copy_on_write_observer_set.rb b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/collection/copy_on_write_observer_set.rb
new file mode 100644
index 0000000..bcb6750
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/collection/copy_on_write_observer_set.rb
@@ -0,0 +1,111 @@
+require 'concurrent/synchronization/lockable_object'
+
+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/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/collection/java_non_concurrent_priority_queue.rb b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/collection/java_non_concurrent_priority_queue.rb
new file mode 100644
index 0000000..2be9e43
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/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/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/collection/lock_free_stack.rb b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/collection/lock_free_stack.rb
new file mode 100644
index 0000000..3704410
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/collection/lock_free_stack.rb
@@ -0,0 +1,160 @@
+require 'concurrent/synchronization/object'
+
+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/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/collection/map/mri_map_backend.rb b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/collection/map/mri_map_backend.rb
new file mode 100644
index 0000000..e0cf999
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/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, &default_proc)
+ super(options, &default_proc)
+ @write_lock = Mutex.new
+ end
+
+ def []=(key, value)
+ @write_lock.synchronize { super }
+ end
+
+ def compute_if_absent(key)
+ if NULL != (stored_value = @backend.fetch(key, NULL)) # 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/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/collection/map/non_concurrent_map_backend.rb b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/collection/map/non_concurrent_map_backend.rb
new file mode 100644
index 0000000..ca5fd9b
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/collection/map/non_concurrent_map_backend.rb
@@ -0,0 +1,148 @@
+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, &default_proc)
+ validate_options_hash!(options) if options.kind_of?(::Hash)
+ set_backend(default_proc)
+ @default_proc = default_proc
+ 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(get_or_default(key, nil)))
+ 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 = get_or_default(key, nil)
+ @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
+
+ private
+
+ def set_backend(default_proc)
+ if default_proc
+ @backend = ::Hash.new { |_h, key| default_proc.call(self, key) }
+ else
+ @backend = {}
+ end
+ end
+
+ def initialize_copy(other)
+ super
+ set_backend(@default_proc)
+ 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/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/collection/map/synchronized_map_backend.rb b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/collection/map/synchronized_map_backend.rb
new file mode 100644
index 0000000..efa161e
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/collection/map/synchronized_map_backend.rb
@@ -0,0 +1,85 @@
+require 'concurrent/collection/map/non_concurrent_map_backend'
+
+module Concurrent
+
+ # @!visibility private
+ module Collection
+
+ # @!visibility private
+ class SynchronizedMapBackend < NonConcurrentMapBackend
+
+ def initialize(*args, &block)
+ super
+
+ # WARNING: Mutex is a non-reentrant lock, so the synchronized methods are
+ # not allowed to call each other.
+ @mutex = Mutex.new
+ end
+
+ def [](key)
+ @mutex.synchronize { super }
+ end
+
+ def []=(key, value)
+ @mutex.synchronize { super }
+ end
+
+ def compute_if_absent(key)
+ @mutex.synchronize { super }
+ end
+
+ def compute_if_present(key)
+ @mutex.synchronize { super }
+ end
+
+ def compute(key)
+ @mutex.synchronize { super }
+ end
+
+ def merge_pair(key, value)
+ @mutex.synchronize { super }
+ end
+
+ def replace_pair(key, old_value, new_value)
+ @mutex.synchronize { super }
+ end
+
+ def replace_if_exists(key, new_value)
+ @mutex.synchronize { super }
+ end
+
+ def get_and_set(key, value)
+ @mutex.synchronize { super }
+ end
+
+ def key?(key)
+ @mutex.synchronize { super }
+ end
+
+ def delete(key)
+ @mutex.synchronize { super }
+ end
+
+ def delete_pair(key, value)
+ @mutex.synchronize { super }
+ end
+
+ def clear
+ @mutex.synchronize { super }
+ end
+
+ def size
+ @mutex.synchronize { super }
+ end
+
+ def get_or_default(key, default_value)
+ @mutex.synchronize { super }
+ end
+
+ private
+ def dupped_backend
+ @mutex.synchronize { super }
+ end
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/collection/map/truffleruby_map_backend.rb b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/collection/map/truffleruby_map_backend.rb
new file mode 100644
index 0000000..68a1b38
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/collection/map/truffleruby_map_backend.rb
@@ -0,0 +1,14 @@
+module Concurrent
+
+ # @!visibility private
+ module Collection
+
+ # @!visibility private
+ class TruffleRubyMapBackend < TruffleRuby::ConcurrentMap
+ def initialize(options = nil)
+ options ||= {}
+ super(initial_capacity: options[:initial_capacity], load_factor: options[:load_factor])
+ end
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/collection/non_concurrent_priority_queue.rb b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/collection/non_concurrent_priority_queue.rb
new file mode 100644
index 0000000..694cd7a
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/collection/non_concurrent_priority_queue.rb
@@ -0,0 +1,143 @@
+require 'concurrent/utility/engine'
+require 'concurrent/collection/java_non_concurrent_priority_queue'
+require 'concurrent/collection/ruby_non_concurrent_priority_queue'
+
+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/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/collection/ruby_non_concurrent_priority_queue.rb b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/collection/ruby_non_concurrent_priority_queue.rb
new file mode 100644
index 0000000..322b4ac
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/collection/ruby_non_concurrent_priority_queue.rb
@@ -0,0 +1,160 @@
+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) || swim(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)
+ success = false
+
+ while (j = (2 * k)) <= @length do
+ j += 1 if j < @length && ! ordered?(j, j+1)
+ break if ordered?(k, j)
+ swap(k, j)
+ success = true
+ k = j
+ end
+
+ success
+ end
+
+ # Percolate up to maintain heap invariant.
+ #
+ # @param [Integer] k the index at which to start the percolation
+ #
+ # @!visibility private
+ def swim(k)
+ success = false
+
+ while k > 1 && ! ordered?(k/2, k) do
+ swap(k, k/2)
+ k = k/2
+ success = true
+ end
+
+ success
+ end
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/concern/deprecation.rb b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/concern/deprecation.rb
new file mode 100644
index 0000000..35ae4b2
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/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/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/concern/dereferenceable.rb b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/concern/dereferenceable.rb
new file mode 100644
index 0000000..dc172ba
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/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/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/concern/logging.rb b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/concern/logging.rb
new file mode 100644
index 0000000..d1aae81
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/concern/logging.rb
@@ -0,0 +1,121 @@
+require 'concurrent/atomic/atomic_reference'
+
+module Concurrent
+ module Concern
+
+ # Include where logging is needed
+ #
+ # @!visibility private
+ module Logging
+ # The same as Logger::Severity but we copy it here to avoid a dependency on the logger gem just for these 7 constants
+ DEBUG, INFO, WARN, ERROR, FATAL, UNKNOWN = 0, 1, 2, 3, 4, 5
+ SEV_LABEL = %w[DEBUG INFO WARN ERROR FATAL ANY].freeze
+
+ # Logs through {Concurrent.global_logger}, it can be overridden by setting @logger
+ # @param [Integer] level one of Concurrent::Concern::Logging 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)
+ logger = if defined?(@logger) && @logger
+ @logger
+ else
+ Concurrent.global_logger
+ end
+ logger.call level, progname, message, &block
+ rescue => error
+ $stderr.puts "`Concurrent.global_logger` failed to log #{[level, progname, message, block]}\n" +
+ "#{error.message} (#{error.class})\n#{error.backtrace.join "\n"}"
+ end
+ end
+ end
+end
+
+module Concurrent
+ extend Concern::Logging
+
+ # Create a simple logger with provided level and output.
+ def self.create_simple_logger(level = :FATAL, output = $stderr)
+ level = Concern::Logging.const_get(level) unless level.is_a?(Integer)
+
+ # 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'),
+ Concern::Logging::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 = :FATAL, output = $stderr)
+ Concurrent.global_logger = create_simple_logger level, output
+ end
+
+ # Create a stdlib logger with provided level and output.
+ # If you use this deprecated method you might need to add logger to your Gemfile to avoid warnings from Ruby 3.3.5+.
+ # @deprecated
+ def self.create_stdlib_logger(level = :FATAL, output = $stderr)
+ require 'logger'
+ 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 = :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(:WARN))
+ private_constant :GLOBAL_LOGGER
+
+ def self.global_logger
+ GLOBAL_LOGGER.value
+ end
+
+ def self.global_logger=(value)
+ GLOBAL_LOGGER.value = value
+ end
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/concern/obligation.rb b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/concern/obligation.rb
new file mode 100644
index 0000000..2c9ac12
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/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/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/concern/observable.rb b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/concern/observable.rb
new file mode 100644
index 0000000..b513271
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/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/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/concurrent_ruby.jar b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/concurrent_ruby.jar
new file mode 100644
index 0000000..d5b8d27
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/concurrent_ruby.jar
Binary files differ
diff --git a/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/configuration.rb b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/configuration.rb
new file mode 100644
index 0000000..5571d39
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/configuration.rb
@@ -0,0 +1,105 @@
+require 'thread'
+require 'concurrent/delay'
+require 'concurrent/errors'
+require 'concurrent/concern/deprecation'
+require 'concurrent/executor/immediate_executor'
+require 'concurrent/executor/fixed_thread_pool'
+require 'concurrent/executor/cached_thread_pool'
+require 'concurrent/utility/processor_counter'
+
+module Concurrent
+ extend Concern::Deprecation
+
+ autoload :Options, 'concurrent/options'
+ autoload :TimerSet, 'concurrent/executor/timer_set'
+ autoload :ThreadPoolExecutor, 'concurrent/executor/thread_pool_executor'
+
+ # @!visibility private
+ GLOBAL_FAST_EXECUTOR = Delay.new { Concurrent.new_fast_executor }
+ private_constant :GLOBAL_FAST_EXECUTOR
+
+ # @!visibility private
+ GLOBAL_IO_EXECUTOR = Delay.new { Concurrent.new_io_executor }
+ private_constant :GLOBAL_IO_EXECUTOR
+
+ # @!visibility private
+ GLOBAL_TIMER_SET = Delay.new { TimerSet.new }
+ 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.
+ # @deprecated Has no effect since it is no longer needed, see https://github.com/ruby-concurrency/concurrent-ruby/pull/841.
+ #
+ def self.disable_at_exit_handlers!
+ deprecated "Method #disable_at_exit_handlers! has no effect since it is no longer needed, see https://github.com/ruby-concurrency/concurrent-ruby/pull/841."
+ 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
+ name: "fast"
+ )
+ 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
+ name: "io"
+ )
+ end
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/constants.rb b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/constants.rb
new file mode 100644
index 0000000..676c2af
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/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/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/dataflow.rb b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/dataflow.rb
new file mode 100644
index 0000000..d55f19d
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/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/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/delay.rb b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/delay.rb
new file mode 100644
index 0000000..0d6d91a
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/delay.rb
@@ -0,0 +1,199 @@
+require 'thread'
+require 'concurrent/concern/obligation'
+require 'concurrent/executor/immediate_executor'
+require 'concurrent/synchronization/lockable_object'
+
+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 operation 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 exception 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/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/errors.rb b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/errors.rb
new file mode 100644
index 0000000..74f1fc3
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/errors.rb
@@ -0,0 +1,74 @@
+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
+
+ # @!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
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/exchanger.rb b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/exchanger.rb
new file mode 100644
index 0000000..a5405d2
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/exchanger.rb
@@ -0,0 +1,353 @@
+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?
+ require 'concurrent/utility/native_extension_loader'
+
+ # @!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/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/executor/abstract_executor_service.rb b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/executor/abstract_executor_service.rb
new file mode 100644
index 0000000..ac42953
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/executor/abstract_executor_service.rb
@@ -0,0 +1,131 @@
+require 'concurrent/errors'
+require 'concurrent/concern/deprecation'
+require 'concurrent/executor/executor_service'
+require 'concurrent/synchronization/lockable_object'
+
+module Concurrent
+
+ # @!macro abstract_executor_service_public_api
+ # @!visibility private
+ class AbstractExecutorService < Synchronization::LockableObject
+ include ExecutorService
+ include Concern::Deprecation
+
+ # 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
+
+ attr_reader :name
+
+ # Create a new thread pool.
+ def initialize(opts = {}, &block)
+ super(&nil)
+ synchronize do
+ @auto_terminate = opts.fetch(:auto_terminate, true)
+ @name = opts.fetch(:name) if opts.key?(:name)
+ ns_initialize(opts, &block)
+ end
+ end
+
+ def to_s
+ name ? "#{super[0..-2]} name: #{name}>" : super
+ 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 { @auto_terminate }
+ end
+
+ # @!macro executor_service_method_auto_terminate_setter
+ def auto_terminate=(value)
+ deprecated "Method #auto_terminate= has no effect. Set :auto_terminate option when executor is initialized."
+ end
+
+ private
+
+ # Returns an action which executes the `fallback_policy` once the queue
+ # size reaches `max_queue`. The reason for the indirection of an action
+ # is so that the work can be deferred outside of synchronization.
+ #
+ # @param [Array] args the arguments to the task which is being handled.
+ #
+ # @!visibility private
+ def fallback_action(*args)
+ case fallback_policy
+ when :abort
+ lambda { raise RejectedExecutionError }
+ when :discard
+ lambda { false }
+ when :caller_runs
+ lambda {
+ begin
+ yield(*args)
+ rescue => ex
+ # let it fail
+ log DEBUG, ex
+ end
+ true
+ }
+ else
+ lambda { 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
+
+ end
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/executor/cached_thread_pool.rb b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/executor/cached_thread_pool.rb
new file mode 100644
index 0000000..de50ed1
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/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(
+ DaemonThreadFactory.new(ns_auto_terminate?))
+ @executor.setRejectedExecutionHandler(FALLBACK_POLICY_CLASSES[@fallback_policy].new)
+ @executor.setKeepAliveTime(opts.fetch(:idletime, DEFAULT_THREAD_IDLETIMEOUT), java.util.concurrent.TimeUnit::SECONDS)
+ end
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/executor/executor_service.rb b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/executor/executor_service.rb
new file mode 100644
index 0000000..7e34491
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/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.
+ # @deprecated Has no effect
+ # @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/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/executor/fixed_thread_pool.rb b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/executor/fixed_thread_pool.rb
new file mode 100644
index 0000000..8324c06
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/executor/fixed_thread_pool.rb
@@ -0,0 +1,224 @@
+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_constant_default_synchronous
+ # Default value of the :synchronous option.
+
+ # @!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_method_active_count
+ # The number of threads that are actively executing tasks.
+ # @return [Integer] The number of threads that are actively executing tasks.
+
+ # @!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_synchronous
+ # Whether or not a value of 0 for :max_queue option means the queue must perform direct hand-off or rather unbounded queue.
+ # @return [true, false]
+
+ # @!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_method_prune_pool
+ # Prune the thread pool of unneeded threads
+ #
+ # What is being pruned is controlled by the min_threads and idletime
+ # parameters passed at pool creation time
+ #
+ # This is a no-op on some pool implementation (e.g. the Java one). The Ruby
+ # pool will auto-prune each time a new job is posted. You will need to call
+ # this method explicitly in case your application post jobs in bursts (a
+ # lot of jobs and then nothing for long periods)
+
+ # @!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
+ #
+ # @!method prune_pool
+ # @!macro thread_pool_executor_method_prune_pool
+
+
+
+
+ # @!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.
+ # * `name`: The name of the executor (optional). Printed in the executor's `#to_s` output and
+ # a `<name>-worker-<id>` name is given to its threads if supported by used Ruby
+ # implementation. `<id>` is uniq for each thread.
+ # * `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), the threads started will be marked as daemon.
+ # * `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 threads can be marked as daemon according to the
+ # `:auto_terminate` option.
+ #
+ # ```ruby
+ # pool1 = Concurrent::FixedThreadPool.new(5) # threads will be marked as daemon
+ # pool2 = Concurrent::FixedThreadPool.new(5, auto_terminate: false) # mark threads as non-daemon
+ # ```
+ #
+ # @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 https://docs.oracle.com/javase/8/docs/api/java/lang/Thread.html#setDaemon-boolean-
+
+
+
+
+
+ # @!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/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/executor/immediate_executor.rb b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/executor/immediate_executor.rb
new file mode 100644
index 0000000..282df7a
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/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/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/executor/indirect_immediate_executor.rb b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/executor/indirect_immediate_executor.rb
new file mode 100644
index 0000000..4f9769f
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/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/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/executor/java_executor_service.rb b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/executor/java_executor_service.rb
new file mode 100644
index 0000000..b2bc69a
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/executor/java_executor_service.rb
@@ -0,0 +1,100 @@
+require 'concurrent/utility/engine'
+
+if Concurrent.on_jruby?
+ require 'concurrent/errors'
+ 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 post(*args, &task)
+ raise ArgumentError.new('no block given') unless block_given?
+ return fallback_action(*args, &task).call 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
+ @executor.shutdown
+ nil
+ end
+ end
+
+ def kill
+ synchronize do
+ @executor.shutdownNow
+ nil
+ end
+ end
+
+ private
+
+ def ns_running?
+ !(ns_shuttingdown? || ns_shutdown?)
+ end
+
+ def ns_shuttingdown?
+ @executor.isShutdown && !@executor.isTerminated
+ end
+
+ def ns_shutdown?
+ @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
+
+ class DaemonThreadFactory
+ # hide include from YARD
+ send :include, java.util.concurrent.ThreadFactory
+
+ def initialize(daemonize = true)
+ @daemonize = daemonize
+ @java_thread_factory = java.util.concurrent.Executors.defaultThreadFactory
+ end
+
+ def newThread(runnable)
+ thread = @java_thread_factory.newThread(runnable)
+ thread.setDaemon(@daemonize)
+ return thread
+ end
+ end
+
+ private_constant :DaemonThreadFactory
+
+ end
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/executor/java_single_thread_executor.rb b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/executor/java_single_thread_executor.rb
new file mode 100644
index 0000000..7aa24f2
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/executor/java_single_thread_executor.rb
@@ -0,0 +1,30 @@
+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(
+ DaemonThreadFactory.new(ns_auto_terminate?)
+ )
+ @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)
+ end
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/executor/java_thread_pool_executor.rb b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/executor/java_thread_pool_executor.rb
new file mode 100644
index 0000000..598a5f9
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/executor/java_thread_pool_executor.rb
@@ -0,0 +1,145 @@
+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_constant_default_synchronous
+ DEFAULT_SYNCHRONOUS = false
+
+ # @!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_attr_reader_synchronous
+ attr_reader :synchronous
+
+ # @!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_method_active_count
+ def active_count
+ @executor.getActiveCount
+ 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
+
+ # @!macro thread_pool_executor_method_prune_pool
+ def prune_pool
+ 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
+ @synchronous = opts.fetch(:synchronous, DEFAULT_SYNCHRONOUS)
+ @fallback_policy = opts.fetch(:fallback_policy, :abort)
+
+ raise ArgumentError.new("`synchronous` cannot be set unless `max_queue` is 0") if @synchronous && @max_queue > 0
+ 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
+ if @synchronous
+ queue = java.util.concurrent.SynchronousQueue.new
+ else
+ queue = java.util.concurrent.LinkedBlockingQueue.new
+ end
+ 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,
+ DaemonThreadFactory.new(ns_auto_terminate?),
+ FALLBACK_POLICY_CLASSES[@fallback_policy].new)
+
+ end
+ end
+
+ end
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/executor/ruby_executor_service.rb b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/executor/ruby_executor_service.rb
new file mode 100644
index 0000000..1f7301b
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/executor/ruby_executor_service.rb
@@ -0,0 +1,82 @@
+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?
+ deferred_action = synchronize {
+ if running?
+ ns_execute(*args, &task)
+ else
+ fallback_action(*args, &task)
+ end
+ }
+ if deferred_action
+ deferred_action.call
+ else
+ true
+ end
+ end
+
+ def shutdown
+ synchronize do
+ break unless running?
+ stop_event.set
+ ns_shutdown_execution
+ end
+ true
+ end
+
+ def kill
+ synchronize do
+ break if shutdown?
+ 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/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/executor/ruby_single_thread_executor.rb b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/executor/ruby_single_thread_executor.rb
new file mode 100644
index 0000000..916337d
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/executor/ruby_single_thread_executor.rb
@@ -0,0 +1,21 @@
+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),
+ )
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/executor/ruby_thread_pool_executor.rb b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/executor/ruby_thread_pool_executor.rb
new file mode 100644
index 0000000..9375acf
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/executor/ruby_thread_pool_executor.rb
@@ -0,0 +1,373 @@
+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_constant_default_synchronous
+ DEFAULT_SYNCHRONOUS = false
+
+ # @!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_attr_reader_synchronous
+ attr_reader :synchronous
+
+ # @!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 thread_pool_executor_method_active_count
+ def active_count
+ synchronize do
+ @pool.length - @ready.length
+ end
+ 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, last_message)
+ synchronize { ns_ready_worker worker, last_message }
+ 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
+
+ # @!macro thread_pool_executor_method_prune_pool
+ def prune_pool
+ synchronize { ns_prune_pool }
+ 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
+ @synchronous = opts.fetch(:synchronous, DEFAULT_SYNCHRONOUS)
+ @fallback_policy = opts.fetch(:fallback_policy, :abort)
+
+ raise ArgumentError.new("`synchronous` cannot be set unless `max_queue` is 0") if @synchronous && @max_queue > 0
+ 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
+
+ @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
+ @workers_counter = 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
+ return fallback_action(*args, &task)
+ end
+
+ ns_prune_pool if @next_gc_time < Concurrent.monotonic_time
+ nil
+ 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)
+ return false if @synchronous
+
+ 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, Concurrent.monotonic_time, 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
+
+ @workers_counter += 1
+ @pool << (worker = Worker.new(self, @workers_counter))
+ @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, last_message, 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?
+ raise unless last_message
+ @ready.push([worker, last_message])
+ else
+ worker.stop
+ end
+ end
+ 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
+ now = Concurrent.monotonic_time
+ stopped_workers = 0
+ while !@ready.empty? && (@pool.size - stopped_workers > @min_length)
+ worker, last_message = @ready.first
+ if now - last_message > self.idletime
+ stopped_workers += 1
+ @ready.shift
+ worker << :stop
+ else break
+ end
+ end
+
+ @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
+ @workers_counter = 0
+ @ruby_pid = $$
+ end
+ end
+
+ # @!visibility private
+ class Worker
+ include Concern::Logging
+
+ def initialize(pool, id)
+ # 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
+
+ if @thread.respond_to?(:name=)
+ @thread.name = [pool.name, 'worker', id].compact.join('-')
+ end
+ 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|
+ catch(:stop) do
+ loop do
+
+ case message = my_queue.pop
+ when :stop
+ my_pool.remove_busy_worker(self)
+ throw :stop
+
+ else
+ task, args = message
+ run_task my_pool, task, args
+ my_pool.ready_worker(self, Concurrent.monotonic_time)
+ 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/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/executor/safe_task_executor.rb b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/executor/safe_task_executor.rb
new file mode 100644
index 0000000..f796b85
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/executor/safe_task_executor.rb
@@ -0,0 +1,35 @@
+require 'concurrent/synchronization/lockable_object'
+
+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)
+ success = true
+ value = reason = nil
+
+ synchronize do
+ begin
+ value = @task.call(*args)
+ success = true
+ rescue @exception_class => ex
+ reason = ex
+ success = false
+ end
+ end
+
+ [success, value, reason]
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/executor/serial_executor_service.rb b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/executor/serial_executor_service.rb
new file mode 100644
index 0000000..f1c38ec
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/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/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/executor/serialized_execution.rb b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/executor/serialized_execution.rb
new file mode 100644
index 0000000..4db7c7f
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/executor/serialized_execution.rb
@@ -0,0 +1,107 @@
+require 'concurrent/errors'
+require 'concurrent/concern/logging'
+require 'concurrent/synchronization/lockable_object'
+
+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<Array(ExecutorService, Array<Object>, 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/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/executor/serialized_execution_delegator.rb b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/executor/serialized_execution_delegator.rb
new file mode 100644
index 0000000..8197781
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/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/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/executor/simple_executor_service.rb b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/executor/simple_executor_service.rb
new file mode 100644
index 0000000..0bc62af
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/executor/simple_executor_service.rb
@@ -0,0 +1,103 @@
+require 'concurrent/atomic/atomic_boolean'
+require 'concurrent/atomic/atomic_fixnum'
+require 'concurrent/atomic/event'
+require 'concurrent/executor/executor_service'
+require 'concurrent/executor/ruby_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(*args)
+ @running = Concurrent::AtomicBoolean.new(true)
+ @stopped = Concurrent::Event.new
+ @count = Concurrent::AtomicFixnum.new(0)
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/executor/single_thread_executor.rb b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/executor/single_thread_executor.rb
new file mode 100644
index 0000000..220eb0f
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/executor/single_thread_executor.rb
@@ -0,0 +1,57 @@
+require 'concurrent/utility/engine'
+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 behaviors. 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/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/executor/thread_pool_executor.rb b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/executor/thread_pool_executor.rb
new file mode 100644
index 0000000..253d46a
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/executor/thread_pool_executor.rb
@@ -0,0 +1,88 @@
+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
+ # @option opts [Boolean] :synchronous (DEFAULT_SYNCHRONOUS) whether or not a value of 0
+ # for :max_queue means the queue must perform direct hand-off rather than unbounded.
+ # @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/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/executor/timer_set.rb b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/executor/timer_set.rb
new file mode 100644
index 0000000..759dce0
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/executor/timer_set.rb
@@ -0,0 +1,176 @@
+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/errors'
+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<Object>] 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
+ 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 }
+ begin
+ task.executor.post { task.process_task }
+ rescue RejectedExecutionError
+ # ignore and continue
+ end
+ else
+ @condition.wait([diff, 60].min)
+ end
+ end
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/executors.rb b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/executors.rb
new file mode 100644
index 0000000..eb1972c
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/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/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/future.rb b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/future.rb
new file mode 100644
index 0000000..1af182e
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/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/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/hash.rb b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/hash.rb
new file mode 100644
index 0000000..db0208e
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/hash.rb
@@ -0,0 +1,52 @@
+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/Hash.html Ruby standard library `Hash`
+
+ # @!macro internal_implementation_note
+ HashImplementation = case
+ when Concurrent.on_cruby?
+ # Hash is not fully thread-safe on CRuby, see
+ # https://bugs.ruby-lang.org/issues/19237
+ # https://github.com/ruby/ruby/commit/ffd52412ab
+ # https://github.com/ruby-concurrency/concurrent-ruby/issues/929
+ # So we will need to add synchronization here (similar to Concurrent::Map).
+ ::Hash
+
+ when Concurrent.on_jruby?
+ require 'jruby/synchronized'
+
+ class JRubyHash < ::Hash
+ include JRuby::Synchronized
+ end
+ JRubyHash
+
+ 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/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/immutable_struct.rb b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/immutable_struct.rb
new file mode 100644
index 0000000..48462e8
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/immutable_struct.rb
@@ -0,0 +1,101 @@
+require 'concurrent/synchronization/abstract_struct'
+require 'concurrent/synchronization/lockable_object'
+
+module Concurrent
+
+ # A thread-safe, immutable variation of Ruby's standard `Struct`.
+ #
+ # @see http://ruby-doc.org/core/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
+
+ private
+
+ # @!visibility private
+ def initialize_copy(original)
+ super(original)
+ ns_initialize_copy
+ 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/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/ivar.rb b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/ivar.rb
new file mode 100644
index 0000000..4165038
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/ivar.rb
@@ -0,0 +1,208 @@
+require 'concurrent/constants'
+require 'concurrent/errors'
+require 'concurrent/collection/copy_on_write_observer_set'
+require 'concurrent/concern/obligation'
+require 'concurrent/concern/observable'
+require 'concurrent/executor/safe_task_executor'
+require 'concurrent/synchronization/lockable_object'
+
+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/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/map.rb b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/map.rb
new file mode 100644
index 0000000..b263f83
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/map.rb
@@ -0,0 +1,350 @@
+require 'thread'
+require 'concurrent/constants'
+require 'concurrent/utility/engine'
+
+module Concurrent
+ # @!visibility private
+ module Collection
+
+ # @!visibility private
+ MapImplementation = case
+ when Concurrent.on_jruby?
+ require 'concurrent/utility/native_extension_loader'
+ # noinspection RubyResolve
+ JRubyMapBackend
+ when Concurrent.on_cruby?
+ require 'concurrent/collection/map/mri_map_backend'
+ MriMapBackend
+ when Concurrent.on_truffleruby?
+ if defined?(::TruffleRuby::ConcurrentMap)
+ require 'concurrent/collection/map/truffleruby_map_backend'
+ TruffleRubyMapBackend
+ else
+ require 'concurrent/collection/map/synchronized_map_backend'
+ SynchronizedMapBackend
+ end
+ 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 []=(key, value)
+ # Set a value with key
+ # @param [Object] key
+ # @param [Object] value
+ # @return [Object] the new value
+
+ # @!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
+
+ # NonConcurrentMapBackend handles default_proc natively
+ unless defined?(Collection::NonConcurrentMapBackend) and self < Collection::NonConcurrentMapBackend
+
+ # @param [Hash, nil] options options to set the :initial_capacity or :load_factor. Ignored on some Rubies.
+ # @param [Proc] default_proc Optional block to compute the default value if the key is not set, like `Hash#default_proc`
+ def initialize(options = nil, &default_proc)
+ if options.kind_of?(::Hash)
+ validate_options_hash!(options)
+ else
+ options = nil
+ end
+
+ super(options)
+ @default_proc = default_proc
+ 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 incorrect +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
+ end
+
+ alias_method :get, :[]
+ 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
+ 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 previous value when key was present or nil when there was no key
+ 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<Object>] keys
+ def keys
+ arr = []
+ each_pair { |k, v| arr << k }
+ arr
+ end unless method_defined?(:keys)
+
+ # All values
+ # @return [::Array<Object>] 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)
+
+ # 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/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/maybe.rb b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/maybe.rb
new file mode 100644
index 0000000..317c82b
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/maybe.rb
@@ -0,0 +1,229 @@
+require 'concurrent/synchronization/object'
+
+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 #=> #<Errno::ENOENT: No such file or directory @ rb_sysopen - bogus.file>
+ #
+ # 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 #=> #<Client id: 10, first_name: "Ryan">
+ #
+ # # -- 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<Object>] args Zero or more arguments to pass to the block.
+ # @yield The block from which to create a new `Maybe`.
+ # @yieldparam [Array<Object>] 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/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/mutable_struct.rb b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/mutable_struct.rb
new file mode 100644
index 0000000..5d0e9b9
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/mutable_struct.rb
@@ -0,0 +1,239 @@
+require 'concurrent/synchronization/abstract_struct'
+require 'concurrent/synchronization/lockable_object'
+
+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/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/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
+
+ private
+
+ # @!visibility private
+ def initialize_copy(original)
+ synchronize do
+ super(original)
+ ns_initialize_copy
+ end
+ 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/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/mvar.rb b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/mvar.rb
new file mode 100644
index 0000000..dfc4195
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/mvar.rb
@@ -0,0 +1,242 @@
+require 'concurrent/concern/dereferenceable'
+require 'concurrent/synchronization/object'
+
+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/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/options.rb b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/options.rb
new file mode 100644
index 0000000..bdd22a9
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/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/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/promise.rb b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/promise.rb
new file mode 100644
index 0000000..c717f9b
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/promise.rb
@@ -0,0 +1,580 @@
+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/promise'
+ # ```
+ #
+ # 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 initialization 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 #=> "#<StandardError: Here comes the Boom!>"
+ # ```
+ #
+ # #### 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 #=> #<RuntimeError: Boom!>
+ # ```
+ #
+ # 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
+ compare_and_set_state(:pending, :unscheduled)
+ @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<Promise>] promises
+ #
+ # @overload zip(*promises, opts)
+ # @param [Array<Promise>] 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<Array>]
+ 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<Promise>] others
+ #
+ # @overload zip(*promises, opts)
+ # @param [Array<Promise>] 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<Array>]
+ 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/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/promises.rb b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/promises.rb
new file mode 100644
index 0000000..c5df8fe
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/promises.rb
@@ -0,0 +1,2178 @@
+require 'concurrent/synchronization/object'
+require 'concurrent/atomic/atomic_boolean'
+require 'concurrent/atomic/atomic_fixnum'
+require 'concurrent/collection/lock_free_stack'
+require 'concurrent/configuration'
+require 'concurrent/errors'
+require 'concurrent/re_include'
+require 'concurrent/utility/monotonic_time'
+
+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 @yield 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
+
+ # Creates a resolvable event, user is responsible for resolving the event once
+ # by calling {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 a 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 a resolved future with will be either fulfilled with the given value or rejected 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 a resolved future which 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 a resolved future which 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 a 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 a 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 a new future which is resolved after all futures_and_or_events are resolved.
+ # Its value is an array of zipped future values. Its reason is an 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 a 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 a new future which is resolved after the first futures_and_or_events is resolved.
+ # Its result equals the 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 a new future which is resolved after the first futures_and_or_events is fulfilled.
+ # Its result equals the result of the first resolved future or if all futures_and_or_events reject,
+ # it has reason of the last rejected 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 [Event]
+ def any_event(*futures_and_or_events)
+ any_event_on default_executor, *futures_and_or_events
+ end
+
+ # Creates a new event which becomes resolved after the first 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, executor, 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<AbstractPromise>]
+ 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
+ if timeout
+ start = Concurrent.monotonic_time
+ until resolved?
+ break if @Condition.wait(@Lock, timeout) == nil # nil means timeout
+ timeout -= (Concurrent.monotonic_time - start)
+ break if timeout <= 0
+ end
+ else
+ until resolved?
+ @Condition.wait(@Lock, timeout)
+ end
+ 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)
+ raise 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, executor, 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, executor, 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 returned 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, event_or_future, index)
+ (event_or_future.is_a?(Event) ? event_or_future.resolved? : event_or_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/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/re_include.rb b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/re_include.rb
new file mode 100644
index 0000000..600bc6a
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/re_include.rb
@@ -0,0 +1,60 @@
+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
+ #
+ # @!visibility private
+ 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/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/scheduled_task.rb b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/scheduled_task.rb
new file mode 100644
index 0000000..efe9e19
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/scheduled_task.rb
@@ -0,0 +1,331 @@
+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/scheduled_task'
+ # require 'csv'
+ # require 'open-uri'
+ #
+ # class Ticker
+ # def get_year_end_closing(symbol, year, api_key)
+ # uri = "https://www.alphavantage.co/query?function=TIME_SERIES_MONTHLY&symbol=#{symbol}&apikey=#{api_key}&datatype=csv"
+ # data = []
+ # csv = URI.parse(uri).read
+ # if csv.include?('call frequency')
+ # return :rate_limit_exceeded
+ # end
+ # CSV.parse(csv, headers: true) do |row|
+ # data << row['close'].to_f if row['timestamp'].include?(year.to_s)
+ # end
+ # year_end = data.first
+ # year_end
+ # rescue => e
+ # p e
+ # end
+ # end
+ #
+ # api_key = ENV['ALPHAVANTAGE_KEY']
+ # abort(error_message) unless api_key
+ #
+ # # Future
+ # price = Concurrent::Future.execute{ Ticker.new.get_year_end_closing('TWTR', 2013, api_key) }
+ # price.state #=> :pending
+ # price.pending? #=> true
+ # price.value(0) #=> nil (does not block)
+ #
+ # sleep(1) # do other stuff
+ #
+ # price.value #=> 63.65 (after blocking if necessary)
+ # price.state #=> :fulfilled
+ # price.fulfilled? #=> true
+ # price.value #=> 63.65
+ #
+ # @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 #=> #<StandardError: Call me maybe?>
+ #
+ # @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 instantiation.
+ #
+ # @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/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/set.rb b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/set.rb
new file mode 100644
index 0000000..eee4eff
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/set.rb
@@ -0,0 +1,64 @@
+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?
+ # The CRuby implementation of Set is written in Ruby itself and is
+ # not thread safe for certain methods.
+ require 'monitor'
+ require 'concurrent/thread_safe/util/data_structures'
+
+ class CRubySet < ::Set
+ end
+
+ ThreadSafe::Util.make_synchronized_on_cruby CRubySet
+ CRubySet
+
+ when Concurrent.on_jruby?
+ require 'jruby/synchronized'
+
+ class JRubySet < ::Set
+ include JRuby::Synchronized
+ end
+
+ JRubySet
+
+ when Concurrent.on_truffleruby?
+ require 'concurrent/thread_safe/util/data_structures'
+
+ class TruffleRubySet < ::Set
+ end
+
+ ThreadSafe::Util.make_synchronized_on_truffleruby 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/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/settable_struct.rb b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/settable_struct.rb
new file mode 100644
index 0000000..99b8561
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/settable_struct.rb
@@ -0,0 +1,139 @@
+require 'concurrent/errors'
+require 'concurrent/synchronization/abstract_struct'
+require 'concurrent/synchronization/lockable_object'
+
+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/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
+
+ private
+
+ # @!visibility private
+ def initialize_copy(original)
+ synchronize do
+ super(original)
+ ns_initialize_copy
+ end
+ 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/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/synchronization.rb b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/synchronization.rb
new file mode 100644
index 0000000..6d8cf4b
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/synchronization.rb
@@ -0,0 +1,13 @@
+require 'concurrent/utility/native_extension_loader' # load native parts first
+
+require 'concurrent/synchronization/object'
+require 'concurrent/synchronization/lockable_object'
+require 'concurrent/synchronization/condition'
+require 'concurrent/synchronization/lock'
+
+module Concurrent
+ # @!visibility private
+ module Synchronization
+ end
+end
+
diff --git a/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/synchronization/abstract_lockable_object.rb b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/synchronization/abstract_lockable_object.rb
new file mode 100644
index 0000000..d9050b3
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/synchronization/abstract_lockable_object.rb
@@ -0,0 +1,102 @@
+require 'concurrent/utility/native_extension_loader' # load native parts first
+require 'concurrent/utility/monotonic_time'
+require 'concurrent/synchronization/object'
+
+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/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/synchronization/abstract_object.rb b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/synchronization/abstract_object.rb
new file mode 100644
index 0000000..7cd2dec
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/synchronization/abstract_object.rb
@@ -0,0 +1,22 @@
+module Concurrent
+ module Synchronization
+
+ # @!visibility private
+ # @!macro internal_implementation_note
+ class AbstractObject
+ def initialize
+ # nothing to do
+ 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/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/synchronization/abstract_struct.rb b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/synchronization/abstract_struct.rb
new file mode 100644
index 0000000..28816c5
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/synchronization/abstract_struct.rb
@@ -0,0 +1,171 @@
+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 =~ /^#<Class:/) == 0) ? '' : " #{self.class}"
+ "#<#{struct}#{clazz} #{ns_to_h}>"
+ 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 ns_initialize_copy
+ @values = @values.map do |val|
+ begin
+ val.clone
+ rescue TypeError
+ val
+ end
+ end
+ 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(false).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/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/synchronization/condition.rb b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/synchronization/condition.rb
new file mode 100644
index 0000000..5daa68b
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/synchronization/condition.rb
@@ -0,0 +1,62 @@
+require 'concurrent/synchronization/lockable_object'
+
+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/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/synchronization/full_memory_barrier.rb b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/synchronization/full_memory_barrier.rb
new file mode 100644
index 0000000..139e08d
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/synchronization/full_memory_barrier.rb
@@ -0,0 +1,29 @@
+require 'concurrent/utility/native_extension_loader' # load native parts first
+
+module Concurrent
+ module Synchronization
+ case
+ when Concurrent.on_cruby?
+ def self.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
+
+ when Concurrent.on_jruby?
+ require 'concurrent/utility/native_extension_loader'
+ def self.full_memory_barrier
+ JRubyAttrVolatile.full_memory_barrier
+ end
+
+ when Concurrent.on_truffleruby?
+ def self.full_memory_barrier
+ TruffleRuby.full_memory_barrier
+ end
+
+ else
+ warn 'Possibly unsupported Ruby implementation'
+ def self.full_memory_barrier
+ end
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/synchronization/jruby_lockable_object.rb b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/synchronization/jruby_lockable_object.rb
new file mode 100644
index 0000000..7693046
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/synchronization/jruby_lockable_object.rb
@@ -0,0 +1,15 @@
+require 'concurrent/utility/native_extension_loader' # load native parts first
+
+module Concurrent
+ module Synchronization
+
+ if Concurrent.on_jruby?
+
+ # @!visibility private
+ # @!macro internal_implementation_note
+ class JRubyLockableObject < AbstractLockableObject
+
+ end
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/synchronization/lock.rb b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/synchronization/lock.rb
new file mode 100644
index 0000000..f90e0b5
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/synchronization/lock.rb
@@ -0,0 +1,38 @@
+require 'concurrent/synchronization/lockable_object'
+
+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/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/synchronization/lockable_object.rb b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/synchronization/lockable_object.rb
new file mode 100644
index 0000000..08d2ff6
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/synchronization/lockable_object.rb
@@ -0,0 +1,75 @@
+require 'concurrent/utility/engine'
+require 'concurrent/synchronization/abstract_lockable_object'
+require 'concurrent/synchronization/mutex_lockable_object'
+require 'concurrent/synchronization/jruby_lockable_object'
+
+module Concurrent
+ module Synchronization
+
+ # @!visibility private
+ # @!macro internal_implementation_note
+ LockableObjectImplementation = case
+ when Concurrent.on_cruby?
+ MutexLockableObject
+ when Concurrent.on_jruby?
+ JRubyLockableObject
+ 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/Thread.html#method-i-wakeup)
+ # and [`Thread#raise`](http://ruby-doc.org/core/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/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/synchronization/mutex_lockable_object.rb b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/synchronization/mutex_lockable_object.rb
new file mode 100644
index 0000000..acc9745
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/synchronization/mutex_lockable_object.rb
@@ -0,0 +1,89 @@
+require 'concurrent/synchronization/abstract_lockable_object'
+
+module Concurrent
+ 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
+ super()
+ @__Lock__ = ::Mutex.new
+ @__Condition__ = ::ConditionVariable.new
+ end
+
+ def initialize_copy(other)
+ super
+ @__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
+ super()
+ @__Lock__ = ::Monitor.new
+ @__Condition__ = @__Lock__.new_cond
+ end
+
+ def initialize_copy(other)
+ super
+ @__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/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/synchronization/object.rb b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/synchronization/object.rb
new file mode 100644
index 0000000..5921907
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/synchronization/object.rb
@@ -0,0 +1,151 @@
+require 'concurrent/utility/native_extension_loader' # load native parts first
+
+require 'concurrent/synchronization/safe_initialization'
+require 'concurrent/synchronization/volatile'
+require 'concurrent/atomic/atomic_reference'
+
+module Concurrent
+ module Synchronization
+
+ # 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}
+ # @!visibility private
+ class Object < AbstractObject
+ include Volatile
+
+ # 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<Symbol>] names of the instance variables to be volatile
+ # @return [::Array<Symbol>] names of defined method names
+
+ # Has to be called by children.
+ def initialize
+ super
+ __initialize_atomic_fields__
+ end
+
+ def self.safe_initialization!
+ extend SafeInitialization unless safe_initialization?
+ end
+
+ def self.safe_initialization?
+ self.singleton_class < SafeInitialization
+ 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 only 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<Symbol>] names of the instance variables to be volatile with CAS.
+ # @return [::Array<Symbol>] 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<Symbol>] 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/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/synchronization/safe_initialization.rb b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/synchronization/safe_initialization.rb
new file mode 100644
index 0000000..f785e35
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/synchronization/safe_initialization.rb
@@ -0,0 +1,36 @@
+require 'concurrent/synchronization/full_memory_barrier'
+
+module Concurrent
+ module Synchronization
+
+ # @!visibility private
+ # @!macro internal_implementation_note
+ #
+ # By extending this module, a class 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.
+ #
+ # Due to using Kernel#extend, the module is not included again if already present in the ancestors,
+ # which avoids extra overhead.
+ #
+ # @example
+ # class AClass < Concurrent::Synchronization::Object
+ # extend Concurrent::Synchronization::SafeInitialization
+ #
+ # def initialize
+ # @AFinalValue = 'value' # published safely, #foo will never return nil
+ # end
+ #
+ # def foo
+ # @AFinalValue
+ # end
+ # end
+ module SafeInitialization
+ def new(*args, &block)
+ super(*args, &block)
+ ensure
+ Concurrent::Synchronization.full_memory_barrier
+ end
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/synchronization/volatile.rb b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/synchronization/volatile.rb
new file mode 100644
index 0000000..46e8ba6
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/synchronization/volatile.rb
@@ -0,0 +1,101 @@
+require 'concurrent/utility/native_extension_loader' # load native parts first
+require 'concurrent/utility/engine'
+require 'concurrent/synchronization/full_memory_barrier'
+
+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
+ #
+ # @!visibility private
+ module Volatile
+ def self.included(base)
+ base.extend(ClassMethods)
+ end
+
+ def full_memory_barrier
+ Synchronization.full_memory_barrier
+ end
+
+ module ClassMethods
+ if Concurrent.on_cruby?
+ 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
+
+ elsif Concurrent.on_jruby?
+ def attr_volatile(*names)
+ names.each do |name|
+ ivar = :"@volatile_#{name}"
+
+ class_eval <<-RUBY, __FILE__, __LINE__ + 1
+ def #{name}
+ ::Concurrent::Synchronization::JRubyAttrVolatile.instance_variable_get_volatile(self, :#{ivar})
+ end
+
+ def #{name}=(value)
+ ::Concurrent::Synchronization::JRubyAttrVolatile.instance_variable_set_volatile(self, :#{ivar}, value)
+ end
+ RUBY
+
+ end
+ names.map { |n| [n, :"#{n}="] }.flatten
+ end
+
+ else
+ warn 'Possibly unsupported Ruby implementation' unless Concurrent.on_truffleruby?
+
+ def attr_volatile(*names)
+ names.each do |name|
+ ivar = :"@volatile_#{name}"
+
+ class_eval <<-RUBY, __FILE__, __LINE__ + 1
+ def #{name}
+ ::Concurrent::Synchronization.full_memory_barrier
+ #{ivar}
+ end
+
+ def #{name}=(value)
+ #{ivar} = value
+ ::Concurrent::Synchronization.full_memory_barrier
+ end
+ RUBY
+ end
+
+ names.map { |n| [n, :"#{n}="] }.flatten
+ end
+ end
+ end
+
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/thread_safe/synchronized_delegator.rb b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/thread_safe/synchronized_delegator.rb
new file mode 100644
index 0000000..019d843
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/thread_safe/synchronized_delegator.rb
@@ -0,0 +1,47 @@
+require 'delegate'
+require 'monitor'
+
+module Concurrent
+ # 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
diff --git a/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/thread_safe/util.rb b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/thread_safe/util.rb
new file mode 100644
index 0000000..c67084a
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/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/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/thread_safe/util/adder.rb b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/thread_safe/util/adder.rb
new file mode 100644
index 0000000..852b403
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/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.LongAdder 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/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/thread_safe/util/data_structures.rb b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/thread_safe/util/data_structures.rb
new file mode 100644
index 0000000..01eb98f
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/thread_safe/util/data_structures.rb
@@ -0,0 +1,52 @@
+require 'concurrent/thread_safe/util'
+require 'concurrent/utility/engine'
+
+# 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_cruby(klass)
+ klass.class_eval do
+ def initialize(*args, &block)
+ @_monitor = Monitor.new
+ super
+ end
+
+ def initialize_copy(other)
+ # make sure a copy is not sharing a monitor with the original object!
+ @_monitor = Monitor.new
+ super
+ end
+ end
+
+ klass.superclass.instance_methods(false).each do |method|
+ 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
+
+ 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/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/thread_safe/util/power_of_two_tuple.rb b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/thread_safe/util/power_of_two_tuple.rb
new file mode 100644
index 0000000..b54be39
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/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/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/thread_safe/util/striped64.rb b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/thread_safe/util/striped64.rb
new file mode 100644
index 0000000..4169c3d
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/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 :padding_0, :padding_1, :padding_2, :padding_3, :padding_4, :padding_5, :padding_6, :padding_7, :padding_8, :padding_9, :padding_10, :padding_11
+ 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/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/thread_safe/util/volatile.rb b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/thread_safe/util/volatile.rb
new file mode 100644
index 0000000..cdac2a3
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/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/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/thread_safe/util/xor_shift_random.rb b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/thread_safe/util/xor_shift_random.rb
new file mode 100644
index 0000000..c231d18
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/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-locally 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<<b; 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<<b; 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/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/timer_task.rb b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/timer_task.rb
new file mode 100644
index 0000000..dd2037f
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/timer_task.rb
@@ -0,0 +1,361 @@
+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.
+ #
+ # 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.
+ #
+ # A `TimerTask` supports two different types of interval calculations.
+ # A fixed delay will always wait the same amount of time between the
+ # completion of one task and the start of the next. A fixed rate will
+ # attempt to maintain a constant rate of execution regardless of the
+ # duration of the task. For example, if a fixed rate task is scheduled
+ # to run every 60 seconds but the task itself takes 10 seconds to
+ # complete, the next task will be scheduled to run 50 seconds after
+ # the start of the previous task. If the task takes 70 seconds to
+ # complete, the next task will be start immediately after the previous
+ # task completes. Tasks will not be executed concurrently.
+ #
+ # 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).
+ #
+ # @!macro copy_options
+ #
+ # @example Basic usage
+ # task = Concurrent::TimerTask.new{ puts 'Boom!' }
+ # task.execute
+ #
+ # task.execution_interval #=> 60 (default)
+ #
+ # # wait 60 seconds...
+ # #=> 'Boom!'
+ #
+ # task.shutdown #=> true
+ #
+ # @example Configuring `:execution_interval`
+ # task = Concurrent::TimerTask.new(execution_interval: 5) do
+ # puts 'Boom!'
+ # end
+ #
+ # task.execution_interval #=> 5
+ #
+ # @example Immediate execution with `:run_now`
+ # task = Concurrent::TimerTask.new(run_now: true){ puts 'Boom!' }
+ # task.execute
+ #
+ # #=> 'Boom!'
+ #
+ # @example Configuring `:interval_type` with either :fixed_delay or :fixed_rate, default is :fixed_delay
+ # task = Concurrent::TimerTask.new(execution_interval: 5, interval_type: :fixed_rate) do
+ # puts 'Boom!'
+ # end
+ # task.interval_type #=> :fixed_rate
+ #
+ # @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.to_i.times{ print 'Boom! ' }
+ # print "\n"
+ # task.execution_interval += 1
+ # if task.execution_interval > 5
+ # puts 'Stopping...'
+ # task.shutdown
+ # end
+ # end
+ #
+ # timer_task.execute
+ # #=> 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"
+ # else
+ # print "(#{time}) Execution failed with error #{ex}\n"
+ # end
+ # end
+ # end
+ #
+ # task = Concurrent::TimerTask.new(execution_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){ 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
+
+ # Maintain the interval between the end of one execution and the start of the next execution.
+ FIXED_DELAY = :fixed_delay
+
+ # Maintain the interval between the start of one execution and the start of the next.
+ # If execution time exceeds the interval, the next execution will start immediately
+ # after the previous execution finishes. Executions will not run concurrently.
+ FIXED_RATE = :fixed_rate
+
+ # Default `:interval_type`
+ DEFAULT_INTERVAL_TYPE = FIXED_DELAY
+
+ # Create a new TimerTask with the given task and configuration.
+ #
+ # @!macro timer_task_initialize
+ # @param [Hash] opts the options defining task execution.
+ # @option opts [Float] :execution_interval number of seconds between
+ # task executions (default: EXECUTION_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)
+ # @options opts [Symbol] :interval_type method to calculate the interval
+ # between executions, can be either :fixed_rate or :fixed_delay.
+ # (default: :fixed_delay)
+ # @option opts [Executor] executor, default is `global_io_executor`
+ #
+ # @!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 [r] interval_type
+ # @return [Symbol] method to calculate the interval between executions
+ attr_reader :interval_type
+
+ # @!attribute [rw] timeout_interval
+ # @return [Fixnum] Number of seconds the task can run before it is
+ # considered to have failed.
+ def timeout_interval
+ warn 'TimerTask timeouts are now ignored as these were not able to be implemented correctly'
+ 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)
+ warn 'TimerTask timeouts are now ignored as these were not able to be implemented correctly'
+ end
+
+ private :post, :<<
+
+ private
+
+ def ns_initialize(opts, &task)
+ set_deref_options(opts)
+
+ self.execution_interval = opts[:execution] || opts[:execution_interval] || EXECUTION_INTERVAL
+ if opts[:interval_type] && ![FIXED_DELAY, FIXED_RATE].include?(opts[:interval_type])
+ raise ArgumentError.new('interval_type must be either :fixed_delay or :fixed_rate')
+ end
+ if opts[:timeout] || opts[:timeout_interval]
+ warn 'TimeTask timeouts are now ignored as these were not able to be implemented correctly'
+ end
+
+ @run_now = opts[:now] || opts[:run_now]
+ @interval_type = opts[:interval_type] || DEFAULT_INTERVAL_TYPE
+ @task = Concurrent::SafeTaskExecutor.new(task)
+ @executor = opts[:executor] || Concurrent.global_io_executor
+ @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, executor: @executor, args: [Concurrent::Event.new], &method(:execute_task))
+ nil
+ end
+
+ # @!visibility private
+ def execute_task(completion)
+ return nil unless @running.true?
+ start_time = Concurrent.monotonic_time
+ _success, value, reason = @task.execute(self)
+ if completion.try?
+ self.value = value
+ schedule_next_task(calculate_next_interval(start_time))
+ time = Time.now
+ observers.notify_observers do
+ [time, self.value, reason]
+ end
+ end
+ nil
+ end
+
+ # @!visibility private
+ def calculate_next_interval(start_time)
+ if @interval_type == FIXED_RATE
+ run_time = Concurrent.monotonic_time - start_time
+ [execution_interval - run_time, 0].max
+ else # FIXED_DELAY
+ execution_interval
+ end
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/tuple.rb b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/tuple.rb
new file mode 100644
index 0000000..56212cf
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/tuple.rb
@@ -0,0 +1,82 @@
+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
+
+ # 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 = ::Array.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/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/tvar.rb b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/tvar.rb
new file mode 100644
index 0000000..5d02ef0
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/tvar.rb
@@ -0,0 +1,222 @@
+require 'set'
+require 'concurrent/synchronization/object'
+
+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
+ @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_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
+
+ # @!visibility private
+ class Transaction
+
+ ABORTED = ::Object.new
+
+ OpenEntry = Struct.new(:value, :modified)
+
+ AbortError = Class.new(StandardError)
+ LeaveError = Class.new(StandardError)
+
+ def initialize
+ @open_tvars = {}
+ end
+
+ def read(tvar)
+ entry = open(tvar)
+ entry.value
+ end
+
+ def write(tvar, value)
+ entry = open(tvar)
+ entry.modified = true
+ entry.value = value
+ end
+
+ def open(tvar)
+ entry = @open_tvars[tvar]
+
+ unless entry
+ unless tvar.unsafe_lock.try_lock
+ Concurrent::abort_transaction
+ end
+
+ entry = OpenEntry.new(tvar.unsafe_value, false)
+ @open_tvars[tvar] = entry
+ end
+
+ entry
+ end
+
+ def abort
+ unlock
+ end
+
+ def commit
+ @open_tvars.each do |tvar, entry|
+ if entry.modified
+ tvar.unsafe_value = entry.value
+ end
+ end
+
+ unlock
+ end
+
+ def unlock
+ @open_tvars.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/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/utility/engine.rb b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/utility/engine.rb
new file mode 100644
index 0000000..0c574b2
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/utility/engine.rb
@@ -0,0 +1,45 @@
+module Concurrent
+ # @!visibility private
+ module Utility
+
+ # @!visibility private
+ module EngineDetector
+ def on_cruby?
+ RUBY_ENGINE == 'ruby'
+ end
+
+ def on_jruby?
+ RUBY_ENGINE == 'jruby'
+ 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_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/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/utility/monotonic_time.rb b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/utility/monotonic_time.rb
new file mode 100644
index 0000000..1c987d8
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/utility/monotonic_time.rb
@@ -0,0 +1,19 @@
+module Concurrent
+
+ # @!macro monotonic_get_time
+ #
+ # Returns the current time as tracked by the application monotonic clock.
+ #
+ # @param [Symbol] unit the time unit to be returned, can be either
+ # :float_second, :float_millisecond, :float_microsecond, :second,
+ # :millisecond, :microsecond, or :nanosecond default to :float_second.
+ #
+ # @return [Float] The current monotonic time since some unspecified
+ # starting point
+ #
+ # @!macro monotonic_clock_warning
+ def monotonic_time(unit = :float_second)
+ Process.clock_gettime(Process::CLOCK_MONOTONIC, unit)
+ end
+ module_function :monotonic_time
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/utility/native_extension_loader.rb b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/utility/native_extension_loader.rb
new file mode 100644
index 0000000..bf7bab3
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/utility/native_extension_loader.rb
@@ -0,0 +1,77 @@
+require 'concurrent/utility/engine'
+# Synchronization::AbstractObject must be defined before loading the extension
+require 'concurrent/synchronization/abstract_object'
+
+module Concurrent
+ # @!visibility private
+ 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 load_native_extensions
+ 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 java_extensions_loaded?
+ defined?(@java_extensions_loaded) && @java_extensions_loaded
+ 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
+
+Concurrent.load_native_extensions
diff --git a/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/utility/native_integer.rb b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/utility/native_integer.rb
new file mode 100644
index 0000000..de1cdc3
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/utility/native_integer.rb
@@ -0,0 +1,54 @@
+module Concurrent
+ # @!visibility private
+ 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/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/utility/processor_counter.rb b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/utility/processor_counter.rb
new file mode 100644
index 0000000..2489cbd
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/utility/processor_counter.rb
@@ -0,0 +1,220 @@
+require 'etc'
+require 'rbconfig'
+require 'concurrent/delay'
+
+module Concurrent
+ # @!visibility private
+ module Utility
+
+ # @!visibility private
+ class ProcessorCounter
+ def initialize
+ @processor_count = Delay.new { compute_processor_count }
+ @physical_processor_count = Delay.new { compute_physical_processor_count }
+ @cpu_quota = Delay.new { compute_cpu_quota }
+ @cpu_shares = Delay.new { compute_cpu_shares }
+ end
+
+ def processor_count
+ @processor_count.value
+ end
+
+ def physical_processor_count
+ @physical_processor_count.value
+ end
+
+ def available_processor_count
+ cpu_count = processor_count.to_f
+ quota = cpu_quota
+
+ return cpu_count if quota.nil?
+
+ # cgroup cpus quotas have no limits, so they can be set to higher than the
+ # real count of cores.
+ if quota > cpu_count
+ cpu_count
+ else
+ quota
+ end
+ end
+
+ def cpu_quota
+ @cpu_quota.value
+ end
+
+ def cpu_shares
+ @cpu_shares.value
+ end
+
+ private
+
+ def compute_processor_count
+ if Concurrent.on_jruby?
+ java.lang.Runtime.getRuntime.availableProcessors
+ else
+ Etc.nprocessors
+ end
+ end
+
+ def compute_physical_processor_count
+ ppc = case RbConfig::CONFIG["target_os"]
+ when /darwin\d\d/
+ 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/
+ # Get-CimInstance introduced in PowerShell 3 or earlier: https://learn.microsoft.com/en-us/previous-versions/powershell/module/cimcmdlets/get-ciminstance?view=powershell-3.0
+ result = run('powershell -command "Get-CimInstance -ClassName Win32_Processor -Property NumberOfCores | Select-Object -Property NumberOfCores"')
+ if !result || $?.exitstatus != 0
+ # fallback to deprecated wmic for older systems
+ result = run("wmic cpu get NumberOfCores")
+ end
+ if !result || $?.exitstatus != 0
+ # Bail out if both commands returned something unexpected
+ processor_count
+ else
+ # powershell: "\nNumberOfCores\n-------------\n 4\n\n\n"
+ # wmic: "NumberOfCores \n\n4 \n\n\n\n"
+ result.scan(/\d+/).map(&:to_i).reduce(:+)
+ end
+ else
+ processor_count
+ end
+ # fall back to logical count if physical info is invalid
+ ppc > 0 ? ppc : processor_count
+ rescue
+ return 1
+ end
+
+ def run(command)
+ IO.popen(command, &:read)
+ rescue Errno::ENOENT
+ end
+
+ def compute_cpu_quota
+ if RbConfig::CONFIG["target_os"].include?("linux")
+ if File.exist?("/sys/fs/cgroup/cpu.max")
+ # cgroups v2: https://docs.kernel.org/admin-guide/cgroup-v2.html#cpu-interface-files
+ cpu_max = File.read("/sys/fs/cgroup/cpu.max")
+ return nil if cpu_max.start_with?("max ") # no limit
+ max, period = cpu_max.split.map(&:to_f)
+ max / period
+ elsif File.exist?("/sys/fs/cgroup/cpu,cpuacct/cpu.cfs_quota_us")
+ # cgroups v1: https://kernel.googlesource.com/pub/scm/linux/kernel/git/glommer/memcg/+/cpu_stat/Documentation/cgroups/cpu.txt
+ max = File.read("/sys/fs/cgroup/cpu,cpuacct/cpu.cfs_quota_us").to_i
+ # If the cpu.cfs_quota_us is -1, cgroup does not adhere to any CPU time restrictions
+ # https://docs.kernel.org/scheduler/sched-bwc.html#management
+ return nil if max <= 0
+ period = File.read("/sys/fs/cgroup/cpu,cpuacct/cpu.cfs_period_us").to_f
+ max / period
+ end
+ end
+ end
+
+ def compute_cpu_shares
+ if RbConfig::CONFIG["target_os"].include?("linux")
+ if File.exist?("/sys/fs/cgroup/cpu.weight")
+ # cgroups v2: https://docs.kernel.org/admin-guide/cgroup-v2.html#cpu-interface-files
+ # Ref: https://github.com/kubernetes/enhancements/tree/master/keps/sig-node/2254-cgroup-v2#phase-1-convert-from-cgroups-v1-settings-to-v2
+ weight = File.read("/sys/fs/cgroup/cpu.weight").to_f
+ ((((weight - 1) * 262142) / 9999) + 2) / 1024
+ elsif File.exist?("/sys/fs/cgroup/cpu/cpu.shares")
+ # cgroups v1: https://kernel.googlesource.com/pub/scm/linux/kernel/git/glommer/memcg/+/cpu_stat/Documentation/cgroups/cpu.txt
+ File.read("/sys/fs/cgroup/cpu/cpu.shares").to_f / 1024
+ end
+ end
+ end
+ end
+ end
+
+ # create the default ProcessorCounter on load
+ @processor_counter = Utility::ProcessorCounter.new
+ singleton_class.send :attr_reader, :processor_counter
+
+ # 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." We still memoize this value once under
+ # JRuby.
+ #
+ # Otherwise Ruby's Etc.nprocessors will be used.
+ #
+ # @return [Integer] number of processors seen by the OS or Java runtime
+ #
+ # @see http://docs.oracle.com/javase/6/docs/api/java/lang/Runtime.html#availableProcessors()
+ def self.processor_count
+ processor_counter.processor_count
+ 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 self.physical_processor_count
+ processor_counter.physical_processor_count
+ end
+
+ # Number of processors cores available for process scheduling.
+ # This method takes in account the CPU quota if the process is inside a cgroup with a
+ # dedicated CPU quota (typically Docker).
+ # Otherwise it returns the same value as #processor_count but as a Float.
+ #
+ # For performance reasons the calculated value will be memoized on the first
+ # call.
+ #
+ # @return [Float] number of available processors
+ def self.available_processor_count
+ processor_counter.available_processor_count
+ end
+
+ # The maximum number of processors cores available for process scheduling.
+ # Returns `nil` if there is no enforced limit, or a `Float` if the
+ # process is inside a cgroup with a dedicated CPU quota (typically Docker).
+ #
+ # Note that nothing prevents setting a CPU quota higher than the actual number of
+ # cores on the system.
+ #
+ # For performance reasons the calculated value will be memoized on the first
+ # call.
+ #
+ # @return [nil, Float] Maximum number of available processors as set by a cgroup CPU quota, or nil if none set
+ def self.cpu_quota
+ processor_counter.cpu_quota
+ end
+
+ # The CPU shares requested by the process. For performance reasons the calculated
+ # value will be memoized on the first call.
+ #
+ # @return [Float, nil] CPU shares requested by the process, or nil if not set
+ def self.cpu_shares
+ processor_counter.cpu_shares
+ end
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/version.rb b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/version.rb
new file mode 100644
index 0000000..f773e44
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/version.rb
@@ -0,0 +1,3 @@
+module Concurrent
+ VERSION = '1.3.5'
+end