Class: Rage::FiberScheduler
- Inherits:
-
Object
- Object
- Rage::FiberScheduler
- Defined in:
- lib/rage/fiber_scheduler.rb
Defined Under Namespace
Modules: BlockingOperationWait
Instance Method Summary collapse
-
#address_resolve(hostname) ⇒ Object
Resolve a hostname to IP addresses, caching results for 60 seconds.
-
#block(_blocker, timeout = nil) ⇒ Object
Block the current fiber until unblocked or timeout.
-
#close ⇒ Object
Clean up by closing the worker pool and Iodine scheduler.
-
#fiber(&block) ⇒ Object
Create and schedule a new non-blocking fiber, handling request and user-spawned fibers differently.
-
#fiber_interrupt(fiber, exception) ⇒ Object
Interrupt a fiber by incrementing its generation and raising an exception.
-
#initialize ⇒ FiberScheduler
constructor
Initialize the scheduler, storing the root fiber and an empty DNS cache.
- #io_read(io, buffer, length, offset = 0) ⇒ Object
-
#io_wait(io, events, timeout = nil) ⇒ Object
Wait for I/O events on a file descriptor, yielding the fiber until ready or timeout.
- #io_write(io, buffer, length, offset = 0) ⇒ Object
-
#kernel_sleep(duration = nil) ⇒ Object
Pause the current fiber for the specified duration.
-
#process_wait(pid, flags) ⇒ Object
Wait for a specified process.
- #timeout_after(duration, exception_class = Timeout::Error, *exception_arguments, &block) ⇒ Object
-
#unblock(_blocker, fiber) ⇒ Object
Unblock a fiber previously blocked by
#block.
Constructor Details
#initialize ⇒ FiberScheduler
Initialize the scheduler, storing the root fiber and an empty DNS cache.
16 17 18 19 20 |
# File 'lib/rage/fiber_scheduler.rb', line 16 def initialize @root_fiber = Fiber.current @dns_cache = {} @blocked = {} end |
Instance Method Details
#address_resolve(hostname) ⇒ Object
Resolve a hostname to IP addresses, caching results for 60 seconds.
112 113 114 115 116 117 118 119 120 |
# File 'lib/rage/fiber_scheduler.rb', line 112 def address_resolve(hostname) @dns_cache[hostname] ||= begin ::Iodine.run_after(60_000) do @dns_cache[hostname] = nil end Resolv.getaddresses(hostname) end end |
#block(_blocker, timeout = nil) ⇒ Object
Block the current fiber until unblocked or timeout.
123 124 125 126 127 128 129 130 131 132 133 134 135 |
# File 'lib/rage/fiber_scheduler.rb', line 123 def block(_blocker, timeout = nil) f = Fiber.current gen = (f.__wait_generation += 1) @blocked[f] = gen if timeout ::Iodine.run_after((timeout * 1000).to_i) { unblock(nil, f) if gen == f.__wait_generation } end Fiber.yield ensure @blocked.delete(f) end |
#close ⇒ Object
Clean up by closing the worker pool and Iodine scheduler.
211 212 213 214 |
# File 'lib/rage/fiber_scheduler.rb', line 211 def close @worker_pool&.close ::Iodine::Scheduler.close end |
#fiber(&block) ⇒ Object
Create and schedule a new non-blocking fiber, handling request and user-spawned fibers differently.
179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 |
# File 'lib/rage/fiber_scheduler.rb', line 179 def fiber(&block) parent = Fiber.current fiber = if parent == @root_fiber # the fiber to wrap a request in Fiber.new(blocking: false) do Fiber.current.__set_id Rage::Telemetry.tracer.span_core_fiber_dispatch do Fiber.current.__set_result(block.call) end end else # the fiber was created in the user code Fiber.new(blocking: false) do Rage::Telemetry.tracer.span_core_fiber_spawn(parent:) do Fiber.current.__set_result(block.call) end # send a message for `Fiber.await` to work Iodine.publish(parent.__await_channel, "", Iodine::PubSub::PROCESS) if parent.__await_channel rescue Exception => e Fiber.current.__set_err(e) Iodine.publish(parent.__await_channel, Fiber::AWAIT_ERROR_MESSAGE, Iodine::PubSub::PROCESS) if parent.__await_channel end end fiber.__wait_generation = 0 fiber.resume fiber end |
#fiber_interrupt(fiber, exception) ⇒ Object
Interrupt a fiber by incrementing its generation and raising an exception.
148 149 150 151 |
# File 'lib/rage/fiber_scheduler.rb', line 148 def fiber_interrupt(fiber, exception) fiber.__wait_generation += 1 fiber.raise(exception) end |
#io_read(io, buffer, length, offset = 0) ⇒ Object
41 42 43 |
# File 'lib/rage/fiber_scheduler.rb', line 41 def io_read(io, buffer, offset, length) ::Iodine::Scheduler.read(io.fileno, buffer, length, offset) end |
#io_wait(io, events, timeout = nil) ⇒ Object
Wait for I/O events on a file descriptor, yielding the fiber until ready or timeout.
23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
# File 'lib/rage/fiber_scheduler.rb', line 23 def io_wait(io, events, timeout = nil) f = Fiber.current gen = (f.__wait_generation += 1) ::Iodine::Scheduler.attach(io.fileno, events, timeout&.ceil) do |err| f.resume(err) if f.alive? && gen == f.__wait_generation end err = Fiber.defer(io.fileno) if err == false || (err && err < 0) err else events end end |
#io_write(io, buffer, length, offset = 0) ⇒ Object
74 75 76 |
# File 'lib/rage/fiber_scheduler.rb', line 74 def io_write(io, buffer, offset, length) ::Iodine::Scheduler.write(io.fileno, buffer, length, offset) end |
#kernel_sleep(duration = nil) ⇒ Object
Pause the current fiber for the specified duration.
88 89 90 91 |
# File 'lib/rage/fiber_scheduler.rb', line 88 def kernel_sleep(duration = nil) block(nil, duration || 0) Fiber.pause if duration.nil? || duration < 1 end |
#process_wait(pid, flags) ⇒ Object
Wait for a specified process.
174 175 176 |
# File 'lib/rage/fiber_scheduler.rb', line 174 def process_wait(pid, flags) Thread.new { Process::Status.wait(pid, flags) }.value end |
#timeout_after(duration, exception_class = Timeout::Error, *exception_arguments, &block) ⇒ Object
94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 |
# File 'lib/rage/fiber_scheduler.rb', line 94 def timeout_after(duration, exception_class = Timeout::Error, *exception_arguments, &block) f, fulfilled = Fiber.current, false ::Iodine.run_after((duration * 1000).to_i) do if !fulfilled && f.alive? fulfilled = true f.__wait_generation += 1 f.raise(exception_class, *exception_arguments) end end block.call ensure fulfilled = true end |
#unblock(_blocker, fiber) ⇒ Object
Unblock a fiber previously blocked by #block.
138 139 140 141 142 143 144 145 |
# File 'lib/rage/fiber_scheduler.rb', line 138 def unblock(_blocker, fiber) gen = @blocked.delete(fiber) return unless gen ::Iodine.defer do fiber.resume if fiber.alive? && gen == fiber.__wait_generation end end |