Bug #18485
closedEven though init a blocking Fiber with Fiber.new(blocking: true) but scheduler is invoked
Description
For example:
require 'fiber'
require 'io/nonblock'
class SimpleScheduler
def initialize
@readable = {}
@writable = {}
@waiting = {}
@ready = []
@blocking = 0
@urgent = IO.pipe
end
def run
p 'aaaaaaa'
while @readable.any? or @writable.any? or @waiting.any? or @blocking.positive? or @ready.any?
p 'bbbbbbb'
readable, writable = IO.select(@readable.keys + [@urgent.first], @writable.keys, [], 0)
readable&.each do |io|
if fiber = @readable.delete(io)
fiber.resume
end
end
writable&.each do |io|
if fiber = @writable.delete(io)
fiber.resume
end
end
@waiting.keys.each do |fiber|
if current_time > @waiting[fiber]
@waiting.delete(fiber)
fiber.resume
end
end
ready, @ready = @ready, []
ready.each do |fiber|
fiber.resume
end
end
end
def io_wait(io, events, timeout)
p 'ccccccc'
unless (events & IO::READABLE).zero?
@readable[io] = Fiber.current
end
unless (events & IO::WRITABLE).zero?
@writable[io] = Fiber.current
end
Fiber.yield
return events
end
def kernel_sleep(duration = nil)
p 'ddddddd'
block(:sleep, duration)
return true
end
def block(blocker, timeout = nil)
p 'eeeeeeee'
if timeout
@waiting[Fiber.current] = current_time + timeout
begin
Fiber.yield
ensure
@waiting.delete(Fiber.current)
end
else
@blocking += 1
begin
Fiber.yield
ensure
@blocking -= 1
end
end
end
def unblock(blocker, fiber)
p 'ffffffffff'
@ready << fiber
io = @urgent.last
io.write_nonblock('.')
end
def close
p 'ggggggggg'
run
@urgent.each(&:close)
@urgent = nil
end
private
def current_time
p 'hhhhhhhh'
Process.clock_gettime(Process::CLOCK_MONOTONIC)
end
end
scheduler = SimpleScheduler.new
Fiber.set_scheduler(scheduler)
f = Fiber.new(blocking: true) do
p '1111111'
sleep(1)
p '2222222'
end
f.resume
Output:
"1111111"
"2222222"
"ggggggggg"
"aaaaaaa"
You can see the SimpleScheduler.run
and SimpleScheduler.close
is invoked.
If I write my own gem using IO.read and IO.write. User will track in my gem and my code.
I can not avoid it but to Fiber.set_scheduler(nil)
. By this way, I would break users' code clearing their scheduler.
How strong intrusive it is!
Here's the running result I expected:
"1111111"
"2222222"
I don't want any Fiber.scheduler hooking into the origin implementation as IO.read
and IO.write
did before.
Composition over inheritance.
I hope Fiber.scheduler could be a standalone class like Dispatcher
, which can check readable and writable with interface IDispatcher
, and user can adapt event with Channel.read
instead of IO.read
.
Updated by mame (Yusuke Endoh) almost 3 years ago
- Assignee set to ioquatix (Samuel Williams)
Updated by ioquatix (Samuel Williams) almost 3 years ago
- Status changed from Open to Closed
This is working as intended.
f = Fiber.new(blocking: true) do
p '1111111'
sleep(1)
p '2222222'
end
does not invoke the kernel_sleep
hook. Nor does it invoke any IO output hooks (and thus performs blocking IO).