This is not an existing class, but documentation of the interface that Scheduler object should comply to in order to be used as argument to Fiber.scheduler
and handle non-blocking fibers. See also the âNon-blocking fibersâ section in Fiber
class docs for explanations of some concepts.
Schedulerâs behavior and usage are expected to be as follows:
When the execution in the non-blocking Fiber
reaches some blocking operation (like sleep, wait for a process, or a non-ready I/O), it calls some of the schedulerâs hook methods, listed below.
Scheduler somehow registers what the current fiber is waiting on, and yields control to other fibers with Fiber.yield
(so the fiber would be suspended while expecting its wait to end, and other fibers in the same thread can perform)
At the end of the current thread execution, the schedulerâs method close
is called
The scheduler runs into a wait loop, checking all the blocked fibers (which it has registered on hook calls) and resuming them when the awaited resource is ready (e.g. I/O ready or sleep time elapsed).
A typical implementation would probably rely for this closing loop on a gem like EventMachine or Async.
This way concurrent execution will be achieved transparently for every individual Fiberâs code.
Hook methods are:
(the list is expanded as Ruby developers make more methods having non-blocking calls)
When not specified otherwise, the hook implementations are mandatory: if they are not implemented, the methods trying to call hook will fail. To provide backward compatibility, in the future hooks will be optional (if they are not implemented, due to the scheduler being created for the older Ruby version, the code which needs this hook will not fail, and will just behave in a blocking fashion).
It is also strongly recommended that the scheduler implements the fiber
method, which is delegated to by Fiber.schedule
.
Sample toy implementation of the scheduler can be found in Rubyâs code, in test/fiber/scheduler.rb
address_resolve(hostname) → array_of_strings or nil click to toggle source
block(blocker, timeout = nil) click to toggle source
Invoked by methods like Thread.join
, and by Mutex, to signify that current Fiber
is blocked until further notice (e.g. unblock
) or until timeout
has elapsed.
blocker
is what we are waiting on, informational only (for debugging and logging). There are no guarantee about its value.
Expected to return boolean, specifying whether the blocking operation was successful or not.
static VALUE rb_fiber_scheduler_interface_block(VALUE self) { }
close() click to toggle source
Called when the current thread exits. The scheduler is expected to implement this method in order to allow all waiting fibers to finalize their execution.
The suggested pattern is to implement the main event loop in the close
method.
static VALUE rb_fiber_scheduler_interface_close(VALUE self) { }
fiber(&block) click to toggle source
Implementation of the Fiber.schedule
. The method is expected to immediately run the given block of code in a separate non-blocking fiber, and to return that Fiber
.
Minimal suggested implementation is:
def fiber(&block) fiber = Fiber.new(blocking: false, &block) fiber.resume fiber end
static VALUE rb_fiber_scheduler_interface_fiber(VALUE self) { }
io_read(io, buffer, length) → read length or -errno click to toggle source
Invoked by IO#read
to read length
bytes from io
into a specified buffer
(see IO::Buffer
).
The length
argument is the âminimum length to be readâ. If the IO
buffer size is 8KiB, but the length
is 1024
(1KiB), up to 8KiB might be read, but at least 1KiB will be. Generally, the only case where less data than length
will be read is if there is an error reading the data.
Specifying a length
of 0 is valid and means try reading at least once and return any available data.
Suggested implementation should try to read from io
in a non-blocking manner and call io_wait
if the io
is not ready (which will yield control to other fibers).
See IO::Buffer
for an interface available to return data.
Expected to return number of bytes read, or, in case of an error, -errno
(negated number corresponding to systemâs error code).
The method should be considered experimental.
static VALUE rb_fiber_scheduler_interface_io_read(VALUE self) { }
io_wait(io, events, timeout) click to toggle source
Invoked by IO#wait
, IO#wait_readable
, IO#wait_writable
to ask whether the specified descriptor is ready for specified events within the specified timeout
.
events
is a bit mask of IO::READABLE
, IO::WRITABLE
, and IO::PRIORITY
.
Suggested implementation should register which Fiber
is waiting for which resources and immediately calling Fiber.yield
to pass control to other fibers. Then, in the close
method, the scheduler might dispatch all the I/O resources to fibers waiting for it.
Expected to return the subset of events that are ready immediately.
static VALUE rb_fiber_scheduler_interface_io_wait(VALUE self) { }
io_write(io, buffer, length) → written length or -errno click to toggle source
Invoked by IO#write
to write length
bytes to io
from from a specified buffer
(see IO::Buffer
).
The length
argument is the â(minimum) length to be writtenâ. If the IO
buffer size is 8KiB, but the length
specified is 1024 (1KiB), at most 8KiB will be written, but at least 1KiB will be. Generally, the only case where less data than length
will be written is if there is an error writing the data.
Specifying a length
of 0 is valid and means try writing at least once, as much data as possible.
Suggested implementation should try to write to io
in a non-blocking manner and call io_wait
if the io
is not ready (which will yield control to other fibers).
See IO::Buffer
for an interface available to get data from buffer efficiently.
Expected to return number of bytes written, or, in case of an error, -errno
(negated number corresponding to systemâs error code).
The method should be considered experimental.
static VALUE rb_fiber_scheduler_interface_io_write(VALUE self) { }
kernel_sleep(duration = nil) click to toggle source
Invoked by Kernel#sleep
and Mutex#sleep and is expected to provide an implementation of sleeping in a non-blocking way. Implementation might register the current fiber in some list of âwhich fiber wait until what momentâ, call Fiber.yield
to pass control, and then in close
resume the fibers whose wait period has elapsed.
static VALUE rb_fiber_scheduler_interface_kernel_sleep(VALUE self) { }
process_wait(pid, flags) click to toggle source
Invoked by Process::Status.wait
in order to wait for a specified process. See that method description for arguments description.
Suggested minimal implementation:
Thread.new do Process::Status.wait(pid, flags) end.value
This hook is optional: if it is not present in the current scheduler, Process::Status.wait
will behave as a blocking method.
Expected to return a Process::Status
instance.
static VALUE rb_fiber_scheduler_interface_process_wait(VALUE self) { }
timeout_after(duration, exception_class, *exception_arguments, &block) → result of block click to toggle source
Invoked by Timeout.timeout
to execute the given block
within the given duration
. It can also be invoked directly by the scheduler or user code.
Attempt to limit the execution time of a given block
to the given duration
if possible. When a non-blocking operation causes the block
âs execution time to exceed the specified duration
, that non-blocking operation should be interrupted by raising the specified exception_class
constructed with the given exception_arguments
.
General execution timeouts are often considered risky. This implementation will only interrupt non-blocking operations. This is by design because itâs expected that non-blocking operations can fail for a variety of unpredictable reasons, so applications should already be robust in handling these conditions and by implication timeouts.
However, as a result of this design, if the block
does not invoke any non-blocking operations, it will be impossible to interrupt it. If you desire to provide predictable points for timeouts, consider adding +sleep(0)+.
If the block is executed successfully, its result will be returned.
The exception will typically be raised using Fiber#raise
.
static VALUE rb_fiber_scheduler_interface_timeout_after(VALUE self) { }
unblock(blocker, fiber) click to toggle source
Invoked to wake up Fiber
previously blocked with block
(for example, Mutex#lock calls block
and Mutex#unlock calls unblock
). The scheduler should use the fiber
parameter to understand which fiber is unblocked.
blocker
is what was awaited for, but it is informational only (for debugging and logging), and it is not guaranteed to be the same value as the blocker
for block
.
static VALUE rb_fiber_scheduler_interface_unblock(VALUE self) { }
RetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4