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,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
@@ -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
@@ -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
@@ -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
@@ -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