@@ -123,7 +123,7 @@ def ensure_timeout_thread_created
123123 # In that case, just return and let the main thread create the Timeout thread.
124124 return if @timeout_thread_mutex . owned?
125125
126- @timeout_thread_mutex . synchronize do
126+ Sync . synchronize @timeout_thread_mutex do
127127 unless @timeout_thread &.alive?
128128 @timeout_thread = create_timeout_thread
129129 end
@@ -132,7 +132,7 @@ def ensure_timeout_thread_created
132132 end
133133
134134 def add_request ( request )
135- @queue_mutex . synchronize do
135+ Sync . synchronize @queue_mutex do
136136 @queue << request
137137 @condvar . signal
138138 end
@@ -153,6 +153,7 @@ def initialize(thread, timeout, exception_class, message)
153153 @done = false # protected by @mutex
154154 end
155155
156+ # Only called by the timeout thread, so does not need Sync.synchronize
156157 def done?
157158 @mutex . synchronize do
158159 @done
@@ -163,6 +164,7 @@ def expired?(now)
163164 now >= @deadline
164165 end
165166
167+ # Only called by the timeout thread, so does not need Sync.synchronize
166168 def interrupt
167169 @mutex . synchronize do
168170 unless @done
@@ -173,13 +175,33 @@ def interrupt
173175 end
174176
175177 def finished
176- @mutex . synchronize do
178+ Sync . synchronize @mutex do
177179 @done = true
178180 end
179181 end
180182 end
181183 private_constant :Request
182184
185+ module Sync
186+ # Calls mutex.synchronize(&block) but if that fails on CRuby due to being in a trap handler,
187+ # run mutex.synchronize(&block) in a separate Thread instead.
188+ def self . synchronize ( mutex , &block )
189+ begin
190+ mutex . synchronize ( &block )
191+ rescue ThreadError => e
192+ raise e unless e . message == "can't be called from trap context"
193+ # Workaround CRuby issue https://bugs.ruby-lang.org/issues/19473
194+ # which raises on Mutex#synchronize in trap handler.
195+ # It's expensive to create a Thread just for this,
196+ # but better than failing.
197+ Thread . new {
198+ mutex . synchronize ( &block )
199+ } . join
200+ end
201+ end
202+ end
203+ private_constant :Sync
204+
183205 # :startdoc:
184206
185207 # Perform an operation in a block, raising an exception if it takes longer than
0 commit comments