Skip to content

Commit 3234d2e

Browse files
correctly handle nested timeouts
1 parent cd55d8b commit 3234d2e

1 file changed

Lines changed: 23 additions & 57 deletions

File tree

lib/rage/fiber_scheduler.rb

Lines changed: 23 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,7 @@ def initialize
1010
@root_fiber = Fiber.current
1111
@dns_cache = {}
1212

13-
@alive_fibers = {}
14-
@timeout_mutex = Mutex.new
13+
@alive_fibers = Hash.new { |h, k| h[k] = {} }
1514

1615
start_timeout_worker
1716
end
@@ -20,13 +19,6 @@ def io_wait(io, events, timeout = nil)
2019
f = Fiber.current
2120
::Iodine::Scheduler.attach(io.fileno, events, timeout&.ceil) { |err| f.resume(err) }
2221

23-
timeout_deadline = Process.clock_gettime(Process::CLOCK_MONOTONIC) + timeout
24-
@alive_fibers[f.__get_id] = {
25-
fiber: f,
26-
timeout_deadline: timeout_deadline,
27-
exception_class: RageTimeout,
28-
}
29-
3022
err = Fiber.defer(io.fileno)
3123
if err == false || (err && err < 0)
3224
err
@@ -79,41 +71,21 @@ def kernel_sleep(duration = nil)
7971
Fiber.pause if duration.nil? || duration < 1
8072
end
8173

82-
# TODO: GC works a little strange with this closure;
83-
#
84-
# def timeout_after(duration, exception_class = Timeout::Error, *exception_arguments, &block)
85-
# fiber, block_status = Fiber.current, :running
86-
# ::Iodine.run_after((duration * 1000).to_i) do
87-
# fiber.raise(exception_class, exception_arguments) if block_status == :running
88-
# end
89-
90-
# result = block.call
91-
# block_status = :finished
92-
93-
# result
94-
# end
9574
def timeout_after(duration, exception_class = Timeout::Error, *exception_arguments, &block)
9675
fiber = Fiber.current
9776
timeout_deadline = Process.clock_gettime(Process::CLOCK_MONOTONIC) + duration
9877

99-
p "duration #{duration}"
100-
p "fiber id #{fiber.__get_id}"
101-
102-
@timeout_mutex.synchronize do
103-
@alive_fibers[fiber.__get_id] = {
104-
fiber: fiber,
105-
timeout_deadline: timeout_deadline,
106-
exception_class: exception_class,
107-
exception_arguments: exception_arguments,
108-
}
109-
end
78+
@alive_fibers[fiber.__get_id][timeout_deadline] = {
79+
fiber: fiber,
80+
timeout_deadline: timeout_deadline,
81+
exception_class: exception_class,
82+
exception_arguments: exception_arguments,
83+
}
11084

11185
begin
11286
block.call
11387
ensure
114-
@timeout_mutex.synchronize do
115-
@alive_fibers.delete(fiber.__get_id)
116-
end
88+
@alive_fibers[fiber.__get_id].delete(timeout_deadline)
11789
end
11890
end
11991

@@ -189,36 +161,30 @@ def start_timeout_worker
189161
return unless ::Iodine.running?
190162

191163
::Iodine.run_every(Rage::FiberScheduler::TIMEOUT_WORKER_INTERVAL) do
192-
@timeout_mutex.synchronize do
193-
check_timeouts
194-
end
164+
check_timeouts
195165
end
196166
end
197167

198168
def check_timeouts
199-
p @alive_fibers.count
169+
@alive_fibers.delete_if do |fiber_id, timeouts|
170+
timeouts.delete_if do |timeout_key, fiber_hash|
171+
current_time = Process.clock_gettime(Process::CLOCK_MONOTONIC)
200172

201-
@alive_fibers.delete_if do |_, fiber_hash|
202-
current_time = Process.clock_gettime(Process::CLOCK_MONOTONIC)
173+
return false if current_time < fiber_hash[:timeout_deadline]
203174

204-
p current_time
205-
p "deadline #{fiber_hash[:timeout_deadline]}"
206-
p "fiber id #{fiber_hash[:fiber].__get_id}"
175+
fiber = fiber_hash[:fiber]
176+
unblock(nil, fiber)
207177

208-
return false if current_time < fiber_hash[:timeout_deadline]
178+
if fiber.alive?
179+
fiber.raise(RageTimeout)
180+
else
181+
timeouts.delete(timeout_key)
182+
end
209183

210-
p 'after'
211-
212-
fiber = fiber_hash[:fiber]
213-
# unblock(nil, fiber)
214-
215-
# if fiber.alive?
216-
fiber.raise(RageTimeout)
217-
# else
218-
# fiber.kill
219-
# end
184+
true
185+
end
220186

221-
true
187+
timeouts.length == 0
222188
end
223189
end
224190
end

0 commit comments

Comments
 (0)