Add bin and edit workflow
Gitea Actions Demo / Explore-Gitea-Actions (push) Failing after 9s

This commit is contained in:
2026-09-16 13:11:16 -06:00
parent c8ac4fcae5
commit 4cee170d66
17576 changed files with 895740 additions and 2 deletions
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -0,0 +1,55 @@
module Concurrent
module Collection
# @!visibility private
# @!macro ruby_timeout_queue
class RubyTimeoutQueue < ::Queue
def initialize(*args)
if RUBY_VERSION >= '3.2'
raise "#{self.class.name} is not needed on Ruby 3.2 or later, use ::Queue instead"
end
super(*args)
@mutex = Mutex.new
@cond_var = ConditionVariable.new
end
def push(obj)
@mutex.synchronize do
super(obj)
@cond_var.signal
end
end
alias_method :enq, :push
alias_method :<<, :push
def pop(non_block = false, timeout: nil)
if non_block && timeout
raise ArgumentError, "can't set a timeout if non_block is enabled"
end
if non_block
super(true)
elsif timeout
@mutex.synchronize do
deadline = Concurrent.monotonic_time + timeout
while (now = Concurrent.monotonic_time) < deadline && empty?
@cond_var.wait(@mutex, deadline - now)
end
begin
return super(true)
rescue ThreadError
# still empty
nil
end
end
else
super(false)
end
end
alias_method :deq, :pop
alias_method :shift, :pop
end
private_constant :RubyTimeoutQueue
end
end
@@ -0,0 +1,18 @@
module Concurrent
module Collection
# @!visibility private
# @!macro internal_implementation_note
TimeoutQueueImplementation = if RUBY_VERSION >= '3.2'
::Queue
else
require 'concurrent/collection/ruby_timeout_queue'
RubyTimeoutQueue
end
private_constant :TimeoutQueueImplementation
# @!visibility private
# @!macro timeout_queue
class TimeoutQueue < TimeoutQueueImplementation
end
end
end