Script 'mail_helper' called by obssrc
Hello community,
here is the log from the commit of package rubygem-timeout for openSUSE:Factory
checked in at 2026-07-15 16:34:19
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/rubygem-timeout (Old)
and /work/SRC/openSUSE:Factory/.rubygem-timeout.new.1991 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "rubygem-timeout"
Wed Jul 15 16:34:19 2026 rev:3 rq:1365338 version:0.6.1
Changes:
--------
--- /work/SRC/openSUSE:Factory/rubygem-timeout/rubygem-timeout.changes
2025-02-06 22:05:48.619068730 +0100
+++
/work/SRC/openSUSE:Factory/.rubygem-timeout.new.1991/rubygem-timeout.changes
2026-07-15 16:52:16.364220617 +0200
@@ -1,0 +2,18 @@
+Mon Jul 13 11:31:37 UTC 2026 - Aleksei Burlakov <[email protected]>
+
+- 0.6.1:
+
+## What's Changed
+ * add test case for string argument by @t-mangoe in #90
+ * Improve Timeout.timeout documentation formatting and typos by @ybiquitous
in #92
+ * [DOC] document the private instance method by @nobu in #94
+ * Compatibility with Fiber scheduler. by @ioquatix in #97
+ * Remove warnings by @etiennebarrie in #99
+
+New Contributors
+ * @t-mangoe made their first contribution in #90
+ * @ybiquitous made their first contribution in #92
+ * @ioquatix made their first contribution in #97
+ * @etiennebarrie made their first contribution in #99
+
+-------------------------------------------------------------------
Old:
----
timeout-0.4.3.gem
New:
----
timeout-0.6.1.gem
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Other differences:
------------------
++++++ rubygem-timeout.spec ++++++
--- /var/tmp/diff_new_pack.MtlEdi/_old 2026-07-15 16:52:19.832338455 +0200
+++ /var/tmp/diff_new_pack.MtlEdi/_new 2026-07-15 16:52:19.836338590 +0200
@@ -1,7 +1,7 @@
#
# spec file for package rubygem-timeout
#
-# Copyright (c) 2025 SUSE LLC
+# Copyright (c) 2026 SUSE LLC and contributors
#
# All modifications and additions to the file contributed by third parties
# remain the property of their copyright owners, unless otherwise agreed
@@ -24,7 +24,7 @@
#
Name: rubygem-timeout
-Version: 0.4.3
+Version: 0.6.1
Release: 0
%define mod_name timeout
%define mod_full_name %{mod_name}-%{version}
++++++ timeout-0.4.3.gem -> timeout-0.6.1.gem ++++++
Binary files old/checksums.yaml.gz and new/checksums.yaml.gz differ
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/lib/timeout.rb new/lib/timeout.rb
--- old/lib/timeout.rb 2024-12-16 04:53:10.000000000 +0100
+++ new/lib/timeout.rb 2026-03-09 23:03:33.000000000 +0100
@@ -20,9 +20,9 @@
module Timeout
# The version
- VERSION = "0.4.3"
+ VERSION = "0.6.1"
- # Internal error raised to when a timeout is triggered.
+ # Internal exception raised to when a timeout is triggered.
class ExitException < Exception
def exception(*) # :nodoc:
self
@@ -44,12 +44,101 @@
end
# :stopdoc:
- CONDVAR = ConditionVariable.new
- QUEUE = Queue.new
- QUEUE_MUTEX = Mutex.new
- TIMEOUT_THREAD_MUTEX = Mutex.new
- @timeout_thread = nil
- private_constant :CONDVAR, :QUEUE, :QUEUE_MUTEX, :TIMEOUT_THREAD_MUTEX
+
+ # We keep a private reference so that time mocking libraries won't break
Timeout.
+ GET_TIME = Process.method(:clock_gettime)
+ if defined?(Ractor.make_shareable)
+ # Ractor.make_shareable(Method) only works on Ruby 4+
+ Ractor.make_shareable(GET_TIME) rescue nil
+ end
+ private_constant :GET_TIME
+
+ class State
+ def initialize
+ @condvar = ConditionVariable.new
+ @queue = Queue.new
+ @queue_mutex = Mutex.new
+
+ @timeout_thread = nil
+ @timeout_thread_mutex = Mutex.new
+ end
+
+ if defined?(Ractor.store_if_absent) && defined?(Ractor.shareable?) &&
Ractor.shareable?(GET_TIME)
+ # Ractor support if
+ # 1. Ractor.store_if_absent is available
+ # 2. Method object can be shareable (4.0~)
+ def self.instance
+ Ractor.store_if_absent :timeout_gem_state do
+ State.new
+ end
+ end
+ else
+ GLOBAL_STATE = State.new
+
+ def self.instance
+ GLOBAL_STATE
+ end
+ end
+
+ def create_timeout_thread
+ # Threads unexpectedly inherit the interrupt mask:
https://github.com/ruby/timeout/issues/41
+ # So reset the interrupt mask to the default one for the timeout thread
+ Thread.handle_interrupt(Object => :immediate) do
+ watcher = Thread.new do
+ requests = []
+ while true
+ until @queue.empty? and !requests.empty? # wait to have at least
one request
+ req = @queue.pop
+ requests << req unless req.done?
+ end
+ closest_deadline = requests.min_by(&:deadline).deadline
+
+ now = 0.0
+ @queue_mutex.synchronize do
+ while (now = GET_TIME.call(Process::CLOCK_MONOTONIC)) <
closest_deadline and @queue.empty?
+ @condvar.wait(@queue_mutex, closest_deadline - now)
+ end
+ end
+
+ requests.each do |req|
+ req.interrupt if req.expired?(now)
+ end
+ requests.reject!(&:done?)
+ end
+ end
+
+ if !watcher.group.enclosed? && (!defined?(Ractor.main?) ||
Ractor.main?)
+ ThreadGroup::Default.add(watcher)
+ end
+
+ watcher.name = "Timeout stdlib thread"
+ watcher.thread_variable_set(:"\0__detached_thread__", true)
+ watcher
+ end
+ end
+
+ def ensure_timeout_thread_created
+ unless @timeout_thread&.alive?
+ # If the Mutex is already owned we are in a signal handler.
+ # In that case, just return and let the main thread create the Timeout
thread.
+ return if @timeout_thread_mutex.owned?
+
+ Sync.synchronize @timeout_thread_mutex do
+ unless @timeout_thread&.alive?
+ @timeout_thread = create_timeout_thread
+ end
+ end
+ end
+ end
+
+ def add_request(request)
+ Sync.synchronize @queue_mutex do
+ @queue << request
+ @condvar.signal
+ end
+ end
+ end
+ private_constant :State
class Request
attr_reader :deadline
@@ -64,6 +153,7 @@
@done = false # protected by @mutex
end
+ # Only called by the timeout thread, so does not need Sync.synchronize
def done?
@mutex.synchronize do
@done
@@ -74,6 +164,7 @@
now >= @deadline
end
+ # Only called by the timeout thread, so does not need Sync.synchronize
def interrupt
@mutex.synchronize do
unless @done
@@ -84,61 +175,36 @@
end
def finished
- @mutex.synchronize do
+ Sync.synchronize @mutex do
@done = true
end
end
end
private_constant :Request
- def self.create_timeout_thread
- watcher = Thread.new do
- requests = []
- while true
- until QUEUE.empty? and !requests.empty? # wait to have at least one
request
- req = QUEUE.pop
- requests << req unless req.done?
- end
- closest_deadline = requests.min_by(&:deadline).deadline
-
- now = 0.0
- QUEUE_MUTEX.synchronize do
- while (now = GET_TIME.call(Process::CLOCK_MONOTONIC)) <
closest_deadline and QUEUE.empty?
- CONDVAR.wait(QUEUE_MUTEX, closest_deadline - now)
- end
- end
-
- requests.each do |req|
- req.interrupt if req.expired?(now)
- end
- requests.reject!(&:done?)
- end
- end
- ThreadGroup::Default.add(watcher) unless watcher.group.enclosed?
- watcher.name = "Timeout stdlib thread"
- watcher.thread_variable_set(:"\0__detached_thread__", true)
- watcher
- end
- private_class_method :create_timeout_thread
-
- def self.ensure_timeout_thread_created
- unless @timeout_thread and @timeout_thread.alive?
- TIMEOUT_THREAD_MUTEX.synchronize do
- unless @timeout_thread and @timeout_thread.alive?
- @timeout_thread = create_timeout_thread
- end
+ module Sync
+ # Calls mutex.synchronize(&block) but if that fails on CRuby due to being
in a trap handler,
+ # run mutex.synchronize(&block) in a separate Thread instead.
+ def self.synchronize(mutex, &block)
+ begin
+ mutex.synchronize(&block)
+ rescue ThreadError => e
+ raise e unless e.message == "can't be called from trap context"
+ # Workaround CRuby issue https://bugs.ruby-lang.org/issues/19473
+ # which raises on Mutex#synchronize in trap handler.
+ # It's expensive to create a Thread just for this,
+ # but better than failing.
+ Thread.new {
+ mutex.synchronize(&block)
+ }.join
end
end
end
-
- # We keep a private reference so that time mocking libraries won't break
- # Timeout.
- GET_TIME = Process.method(:clock_gettime)
- private_constant :GET_TIME
+ private_constant :Sync
# :startdoc:
- # Perform an operation in a block, raising an error if it takes longer than
+ # Perform an operation in a block, raising an exception if it takes longer
than
# +sec+ seconds to complete.
#
# +sec+:: Number of seconds to wait for the block to terminate. Any
non-negative number
@@ -146,45 +212,91 @@
# value of 0 or +nil+ will execute the block without any timeout.
# Any negative number will raise an ArgumentError.
# +klass+:: Exception Class to raise if the block fails to terminate
- # in +sec+ seconds. Omitting will use the default, Timeout::Error
+ # in +sec+ seconds. Omitting will use the default, Timeout::Error.
# +message+:: Error message to raise with Exception Class.
- # Omitting will use the default, "execution expired"
+ # Omitting will use the default, <tt>"execution expired"</tt>.
#
# Returns the result of the block *if* the block completed before
- # +sec+ seconds, otherwise throws an exception, based on the value of
+klass+.
+ # +sec+ seconds, otherwise raises an exception, based on the value of
+klass+.
#
- # The exception thrown to terminate the given block cannot be rescued inside
- # the block unless +klass+ is given explicitly. However, the block can use
- # ensure to prevent the handling of the exception. For that reason, this
- # method cannot be relied on to enforce timeouts for untrusted blocks.
+ # The exception raised to terminate the given block is the given +klass+, or
+ # Timeout::ExitException if +klass+ is not given. The reason for that
behavior
+ # is that Timeout::Error inherits from RuntimeError and might be caught
unexpectedly by +rescue+.
+ # Timeout::ExitException inherits from Exception so it will only be rescued
by <tt>rescue Exception</tt>.
+ # Note that the Timeout::ExitException is translated to a Timeout::Error
once it reaches the Timeout.timeout call,
+ # so outside that call it will be a Timeout::Error.
+ #
+ # In general, be aware that the code block may rescue the exception, and in
such a case not respect the timeout.
+ # Also, the block can use +ensure+ to prevent the handling of the exception.
+ # For those reasons, this method cannot be relied on to enforce timeouts for
untrusted blocks.
#
# If a scheduler is defined, it will be used to handle the timeout by
invoking
- # Scheduler#timeout_after.
+ # Fiber::Scheduler#timeout_after.
#
# Note that this is both a method of module Timeout, so you can <tt>include
# Timeout</tt> into your classes so they have a #timeout method, as well as
# a module method, so you can call it directly as Timeout.timeout().
- def timeout(sec, klass = nil, message = nil, &block) #:yield: +sec+
+ #
+ # ==== Ensuring the exception does not fire inside ensure blocks
+ #
+ # When using Timeout.timeout, it can be desirable to ensure the timeout
exception does not fire inside an +ensure+ block.
+ # The simplest and best way to do so is to put the Timeout.timeout call
inside the body of the +begin+/+ensure+/+end+:
+ #
+ # begin
+ # Timeout.timeout(sec) { some_long_operation }
+ # ensure
+ # cleanup # safe, cannot be interrupted by timeout
+ # end
+ #
+ # If that is not feasible, e.g. if there are +ensure+ blocks inside
+some_long_operation+,
+ # they need to not be interrupted by timeout, and it's not possible to move
these ensure blocks outside,
+ # one can use Thread.handle_interrupt to delay the timeout exception like so:
+ #
+ # Thread.handle_interrupt(Timeout::Error => :never) {
+ # Timeout.timeout(sec, Timeout::Error) do
+ # setup # timeout cannot happen here, no matter how long it takes
+ # Thread.handle_interrupt(Timeout::Error => :immediate) {
+ # some_long_operation # timeout can happen here
+ # }
+ # ensure
+ # cleanup # timeout cannot happen here, no matter how long it takes
+ # end
+ # }
+ #
+ # An important thing to note is the need to pass an exception +klass+ to
Timeout.timeout,
+ # otherwise it does not work. Specifically, using
<tt>Thread.handle_interrupt(Timeout::ExitException => ...)</tt>
+ # is unsupported and causes subtle errors like raising the wrong exception
outside the block, do not use that.
+ #
+ # Note that Thread.handle_interrupt is somewhat dangerous because if setup
or cleanup hangs
+ # then the current thread will hang too and the timeout will never fire.
+ # Also note the block might run for longer than +sec+ seconds:
+ # e.g. +some_long_operation+ executes for +sec+ seconds + whatever time
cleanup takes.
+ #
+ # If you want the timeout to only happen on blocking operations, one can use
+:on_blocking+
+ # instead of +:immediate+. However, that means if the block uses no blocking
operations after +sec+ seconds,
+ # the block will not be interrupted.
+ def self.timeout(sec, klass = nil, message = nil, &block) #:yield: +sec+
return yield(sec) if sec == nil or sec.zero?
raise ArgumentError, "Timeout sec must be a non-negative number" if 0 > sec
message ||= "execution expired"
if Fiber.respond_to?(:current_scheduler) && (scheduler =
Fiber.current_scheduler)&.respond_to?(:timeout_after)
- return scheduler.timeout_after(sec, klass || Error, message, &block)
- end
-
- Timeout.ensure_timeout_thread_created
- perform = Proc.new do |exc|
- request = Request.new(Thread.current, sec, exc, message)
- QUEUE_MUTEX.synchronize do
- QUEUE << request
- CONDVAR.signal
+ perform = Proc.new do |exc|
+ scheduler.timeout_after(sec, exc, message, &block)
end
- begin
- return yield(sec)
- ensure
- request.finished
+ else
+ state = State.instance
+ state.ensure_timeout_thread_created
+
+ perform = Proc.new do |exc|
+ request = Request.new(Thread.current, sec, exc, message)
+ state.add_request(request)
+ begin
+ return yield(sec)
+ ensure
+ request.finished
+ end
end
end
@@ -194,5 +306,9 @@
Error.handle_timeout(message, &perform)
end
end
- module_function :timeout
+
+ # See Timeout.timeout
+ private def timeout(*args, &block)
+ Timeout.timeout(*args, &block)
+ end
end
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/metadata new/metadata
--- old/metadata 2024-12-16 04:53:10.000000000 +0100
+++ new/metadata 2026-03-09 23:03:33.000000000 +0100
@@ -1,14 +1,13 @@
--- !ruby/object:Gem::Specification
name: timeout
version: !ruby/object:Gem::Version
- version: 0.4.3
+ version: 0.6.1
platform: ruby
authors:
- Yukihiro Matsumoto
-autorequire:
bindir: bin
cert_chain: []
-date: 2024-12-16 00:00:00.000000000 Z
+date: 2026-03-09 00:00:00.000000000 Z
dependencies: []
description: Auto-terminate potentially long-running operations in Ruby.
email:
@@ -32,7 +31,6 @@
homepage_uri: https://github.com/ruby/timeout
source_code_uri: https://github.com/ruby/timeout
changelog_uri: https://github.com/ruby/timeout/releases
-post_install_message:
rdoc_options: []
require_paths:
- lib
@@ -47,8 +45,7 @@
- !ruby/object:Gem::Version
version: '0'
requirements: []
-rubygems_version: 3.5.11
-signing_key:
+rubygems_version: 3.6.9
specification_version: 4
summary: Auto-terminate potentially long-running operations in Ruby.
test_files: []