Bug #18482
Updated by jakit (Jakit Liang) almost 3 years ago
class Fiber can not disable scheduler with it's parameter.
When parameter is false:
```
require 'fiber'
require 'io/nonblock'
class SimpleScheduler
def initialize
@readable = {}
@writable = {}
@waiting = {}
@ready = []
@blocking = 0
@urgent = IO.pipe
end
def run
while @readable.any? or @writable.any? or @waiting.any? or @blocking.positive? or @ready.any?
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)
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)
block(:sleep, duration)
return true
end
def block(blocker, timeout = nil)
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)
@ready << fiber
io = @urgent.last
io.write_nonblock('.')
end
def close
run
@urgent.each(&:close)
@urgent = nil
end
private
def current_time
Process.clock_gettime(Process::CLOCK_MONOTONIC)
end
end
scheduler = SimpleScheduler.new
Fiber.set_scheduler(scheduler)
puts "Go to sleep!"
f = Fiber.new(false) do
puts "Going to sleep"
sleep(1)
puts "I slept well"
end
f.resume
puts "Wakey-wakey, sleepyhead"
```
Result:
```
Go to sleep!
Going to sleep
Wakey-wakey, sleepyhead
I slept well
```
And when parameter is true:
```
require 'fiber'
require 'io/nonblock'
class SimpleScheduler
def initialize
@readable = {}
@writable = {}
@waiting = {}
@ready = []
@blocking = 0
@urgent = IO.pipe
end
def run
while @readable.any? or @writable.any? or @waiting.any? or @blocking.positive? or @ready.any?
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)
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)
block(:sleep, duration)
return true
end
def block(blocker, timeout = nil)
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)
@ready << fiber
io = @urgent.last
io.write_nonblock('.')
end
def close
run
@urgent.each(&:close)
@urgent = nil
end
private
def current_time
Process.clock_gettime(Process::CLOCK_MONOTONIC)
end
end
scheduler = SimpleScheduler.new
Fiber.set_scheduler(scheduler)
puts "Go to sleep!"
f = Fiber.new(true) do
puts "Going to sleep"
sleep(1)
puts "I slept well"
end
f.resume
puts "Wakey-wakey, sleepyhead"
```
Result (was still the same):
```
Go to sleep!
Going to sleep
Wakey-wakey, sleepyhead
I slept well
```
While make the set_scheduler line commented:
```
scheduler = SimpleScheduler.new
# Fiber.set_scheduler(scheduler) // Here is commented
puts "Go to sleep!"
f = Fiber.new(false) do
puts "Going to sleep"
sleep(1)
puts "I slept well"
end
```
Result is right:
```
Go to sleep!
Going to sleep
I slept well
Wakey-wakey, sleepyhead
```
Maybe in some situation.
I wrote my gem without Scheduler. But user defined its Scheduler for his or her logic code.
It will break the sequence of Fiber which was needed for my gem.
Also, using Fiber in the Enumerator situation will be broke down too:
```
db.with_each_row_of_result(sql_stmt) do |row|
yield row
end
```
[[https://blog.appsignal.com/2018/11/27/ruby-magic-fibers-and-enumerators-in-ruby.html]]
It will break the sequence of db rows when doing enum such like python's generator.