Module: FiberScheduler::Compatibility

Defined in:
lib/fiber_scheduler/compatibility.rb

Constant Summary collapse

Close =
Class.new(RuntimeError)

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.internal?Boolean

Returns:

  • (Boolean)


92
93
94
# File 'lib/fiber_scheduler/compatibility.rb', line 92

def self.internal?
  Thread.current[:_fiber_scheduler]
end

.set_internal!Object



88
89
90
# File 'lib/fiber_scheduler/compatibility.rb', line 88

def self.set_internal!
  Thread.current[:_fiber_scheduler] = true # Sets a FIBER local var!
end

Instance Method Details

#_volatileObject



84
85
86
# File 'lib/fiber_scheduler/compatibility.rb', line 84

def _volatile
  @_volatile ||= {}
end

#closeObject

#close and #_volatile handle a complexity in Async::Scheduler#close, more specifically this line: github.com/socketry/async/blob/456df488d801572821eaf5ec2fda10e3b9744a5f/lib/async/scheduler.rb#L55



69
70
71
72
73
74
75
76
77
78
79
80
81
82
# File 'lib/fiber_scheduler/compatibility.rb', line 69

def close
  super
rescue
  if _volatile.empty?
    Kernel.raise
  else
    # #dup is used because #_volatile is modified during iteration.
    _volatile.dup.each do |fiber, _|
      fiber.raise(Close)
    end

    super # retry
  end
end

#fiber(*args, **opts, &block) ⇒ Object



5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
# File 'lib/fiber_scheduler/compatibility.rb', line 5

def fiber(*args, **opts, &block)
  return super unless Compatibility.internal?

  # This is `Fiber.schedule` call inside `FiberScheduler { ... }` block.
  type = args.first
  case type
  when :blocking
    Fiber.new(blocking: true) {
      Compatibility.set_internal!
      yield
    }.tap(&:resume)

  when :waiting
    parent = Fiber.current
    finished = false # prevents races
    blocking = false # prevents #unblock-ing a fiber that never blocked

    # Don't pass *args and **opts to an unknown fiber scheduler class.
    fiber = super() do
      Compatibility.set_internal!
      yield
    ensure
      finished = true
      unblock(nil, parent) if blocking
    end

    unless finished
      blocking = true
      block(nil, nil)
    end

    fiber

  when :volatile
    # Transfer to current fiber some time after a volatile fiber yields.
    unblock(nil, Fiber.current)
    # Alternative to #unblock: Fiber.scheduler.push(Fiber.current)

    fiber = Fiber.new(blocking: false) do
      Compatibility.set_internal!
      yield
    rescue Close
      # Fiber scheduler is closing.
    ensure
      _volatile.delete(Fiber.current)
    end
    _volatile[fiber] = nil
    fiber.tap(&:transfer)

  when nil
    # Don't pass *args and **opts to an unknown fiber scheduler class.
    super() do
      Compatibility.set_internal!
      yield
    end

  else
    raise "Unknown type"
  end
end