From: "mame (Yusuke Endoh)" Date: 2022-01-14T04:38:14+00:00 Subject: [ruby-core:107120] [Ruby master Bug#18485] Even though init a blocking Fiber with Fiber.new(blocking: true) but scheduler is invoked Issue #18485 has been updated by mame (Yusuke Endoh). Assignee set to ioquatix (Samuel Williams) ---------------------------------------- Bug #18485: Even though init a blocking Fiber with Fiber.new(blocking: true) but scheduler is invoked https://bugs.ruby-lang.org/issues/18485#change-95965 * Author: jakit (Jakit Liang) * Status: Open * Priority: Normal * Assignee: ioquatix (Samuel Williams) * ruby -v: ruby 3.1.0p0 (2021-12-25 revision fb4df44d16) [arm64-darwin20] * Backport: 2.6: UNKNOWN, 2.7: UNKNOWN, 3.0: UNKNOWN, 3.1: UNKNOWN ---------------------------------------- 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`. -- https://bugs.ruby-lang.org/ Unsubscribe: