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: []

Reply via email to