Class: AsyncFutures::FiberExecutor
- Defined in:
- lib/async_futures/fiber_executor.rb
Overview
Executor implementation based on Fiber primitives.
Requires that Fiber.scheduler be set in order to work.
Several benefits of using FiberExecutor over using Fiber.schedule directly:
- By default
Fiberinstances run viaFiber.schedulehave no straightforward way of returning their final result upon completion likeThreadandRactordo (both support callingvalueto get the final result).Futuremakes this trivial to do for a scheduledFiber. Fiberinstances cannot currently be shared acrossThreadinstances (though this may change someday). However,Futureinstances can safely be shared across bothThreadsandFibers(Ractors can share neitherFibernorFutureand that is unlikely to ever change due to their design).
FiberExecutor specific details for submission:
For FiberExecutor the tasks are run immediately upon submission
using the Fiber.schedule method.
This method will return
as soon as the Fiber hits a blocking operation
or runs the Fiber to completion.
Thus it is completely possible
that the returned Future is already completed
by the time it is returned to the caller.
The FiberExecutor implementation does not guarantee
that any particular task will be run concurrently
with any other particular task;
that is dependent
on whether the submitted procs/blocks
have blocking operations that yield control
back to the Fiber::Scheduler
and whether the Fiber::Scheduler properly implements
Fiber switching for those operations.
Instance Method Summary collapse
-
#initialize(treat_as_concurrent: false, worker_name_prefix: nil) ⇒ FiberExecutor
constructor
Create a new
FiberExecutor. -
#shutdown(wait: true, cancel_futures: false) ⇒ Object
Shutdown
FiberExecutorinstance. -
#submit(*args, **kwargs, &block) ⇒ Object
Asynchronously submit a task for execution.
-
#support_concurrency? ⇒ Boolean
Return
trueiftreat_as_concurrentwas passed astrueto theFiberExecutorconstructor.
Methods inherited from Executor
Constructor Details
#initialize(treat_as_concurrent: false, worker_name_prefix: nil) ⇒ FiberExecutor
Create a new FiberExecutor.
Spawns fibers via Fiber.schedule.
Raises AsyncFutures::Error
unless Fiber.scheduler is set.
Because auto-fibers do not yield control
unless they encounter a blocking operation,
it is completely possible
that the Fiber runs to completion upon submission.
Thus, submit_concurrent fails by default
unless the parameter treat_as_concurrent is set to true.
All internal state is protected via mutex,
so it is safe to use a single FiberExecutor instance across multiple threads.
However each thread must have its own Fiber::Scheduler set
in order to successfully call submit.
The parameter worker_name_prefix can be used
to optionally add a prefix to generated worker names.
70 71 72 73 74 75 76 77 78 |
# File 'lib/async_futures/fiber_executor.rb', line 70 def initialize(treat_as_concurrent: false, worker_name_prefix: nil) raise Error.new('No Fiber.scheduler set') unless Fiber.scheduler super(worker_name_prefix: worker_name_prefix) @treat_as_concurrent = treat_as_concurrent @futures = SynchronizedDelegator.new(Set.new) at_exit { shutdown(wait: false, cancel_futures: true) } end |
Instance Method Details
#shutdown(wait: true, cancel_futures: false) ⇒ Object
Shutdown FiberExecutor instance.
See AsyncFutures::Executor.shutdown for full documentation.
116 117 118 119 120 121 122 123 124 125 126 127 128 |
# File 'lib/async_futures/fiber_executor.rb', line 116 def shutdown(wait: true, cancel_futures: false) # rubocop:disable Metrics/CyclomaticComplexity yield(self) if block_given? ensure at_first_shutdown do if wait || cancel_futures futures_dup = @futures.dup.to_set futures_dup.reject!(&:cancel) if cancel_futures # This will deadlock outside a FiberScheduler, futures_dup.reject!(&:join) if wait end end end |
#submit(*args, **kwargs, &block) ⇒ Object
Asynchronously submit a task for execution.
See AsyncFutures::Executor.submit method for full documentation.
83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 |
# File 'lib/async_futures/fiber_executor.rb', line 83 def submit(*args, **kwargs, &block) raise ArgumentError.new('No block given') unless block Future.new.tap do |future| synchronize do refute_shutdown # Need to set this immediately to ensure DeadlockError is raised appropriately. future.thread = Thread.current future.add_done_callback { |f| @futures.delete(f) } @futures.add(future) end Fiber.schedule do AsyncFutures.worker_name = new_worker_name future.complete(*args, **kwargs, &block) end end end |
#support_concurrency? ⇒ Boolean
Return true
if treat_as_concurrent was passed as true
to the FiberExecutor constructor.
Otherwise,
return false.
109 110 111 |
# File 'lib/async_futures/fiber_executor.rb', line 109 def support_concurrency? !!@treat_as_concurrent end |