Class: Farce::Local::Scheduler
- Inherits:
-
Abstract::Scheduler
- Object
- Abstract::Executor
- Abstract::Scheduler
- Farce::Local::Scheduler
- Includes:
- Scoped, Shareable::Unfreezable, Internal::LocalExecutorMethods
- Defined in:
- lib/farce/local/scheduler.rb
Overview
A shareable handle with an independent task scheduler in each scope.
new leaves each scheduler unstarted. Launch it with Abstract::Scheduler#launch_thread, or
install it with Fiber.set_scheduler. Scheduler.create starts a worker thread in
each scope when that scope is first used. The default scope is :ractor.
Tasks retain their blocks and argument references. Abstract::Executor#execute runs directly in the calling fiber. A fiber scheduler constructor must be convertible to a shareable proc for use in each Ractor. #close closes the current scope. #close_all closes all scopes and prevents new schedulers from starting. Scheduler.current returns an Unshared::Scheduler.
Instance Attribute Summary
Attributes included from Scoped
Class Method Summary collapse
-
.create(executor = Thread, name: nil, priority: nil) ⇒ Scheduler
Starts a worker in this scope and retains the launch settings for other scopes.
-
.current ⇒ Unshared::Scheduler?
Returns the current thread's unshared scheduler, or nil when none is installed.
Instance Method Summary collapse
-
#alive? ⇒ Boolean
Whether the current scope is dispatching tasks.
-
#capacity ⇒ BasicObject
Pending-task capacity in this scope.
-
#capacity=(value) ⇒ BasicObject
Resize the queue in this scope without starting a worker.
-
#close ⇒ self
Closes the current scope and rejects new submissions there.
-
#close_all ⇒ self
Requests shutdown in every scope and Ractor and rejects future scopes.
-
#closed? ⇒ Boolean
Whether the current scope rejects new tasks.
-
#error ⇒ Exception?
The current scope's scheduler error.
-
#external_scheduler ⇒ Object?
The fiber scheduler in the current scope, when supplied by a constructor.
-
#initialize(capacity: 1024, backend: Farce.config.io_backend, scope: :ractor, &constructor) ⇒ Scheduler
constructor
Creates a handle whose scoped schedulers remain unstarted.
-
#launch(executor = Thread, name: nil, priority: nil) ⇒ Thread, Object
Starts a worker for the current scope.
-
#owner ⇒ Ractor
The current scope's owning Ractor.
- #owner=(value) ⇒ BasicObject
-
#schedule(*args, mode: :smart, auto_local: true, timeout: nil) ⇒ self?
Enqueue a task in the current scope, preserving its block and arguments.
-
#state ⇒ Symbol
The current scope's scheduler state.
-
#try_schedule(*args, mode: :smart, auto_local: true) ⇒ self?
Enqueues a task without waiting for queue space.
-
#wraps_external? ⇒ Boolean
Whether a fiber scheduler constructor was supplied.
Methods included from Shareable
Methods inherited from Abstract::Scheduler
#launch_ractor, #launch_thread, #local?
Methods inherited from Abstract::Executor
#execute, #local?, #ractor_shareable?
Methods included from Internal::Noncopyable
Constructor Details
#initialize(capacity: 1024, backend: Farce.config.io_backend, scope: :ractor, &constructor) ⇒ Scheduler
Creates a handle whose scoped schedulers remain unstarted. The constructor block builds the fiber scheduler when a scope is launched or installed. Valid scopes are:
:ractor- The value is shared by all threads within the same Ractor.:thread_group- The value is shared by all threads within the same thread group.:thread- The value is shared by all fibers within the same thread.:fiber_storage- The value is shared by all fibers using the same storage. Unless explicitly specified, storage is inherited from the parent fiber for blocking fibers, but not for non-blocking fibers (such as those created by #schedule).:fiber- Each fiber has its own independent value.
56 57 58 59 60 61 62 63 64 65 66 67 |
# File 'lib/farce/local/scheduler.rb', line 56 def initialize(capacity: 1024, backend: Internal::FROZEN_CONFIG.io_backend, scope: :ractor, worker: nil, &constructor) @constructor = prepare_constructor(constructor || Internal::FROZEN_CONFIG.fiber_scheduler_constructor) @capacity = capacity && Integer(capacity) @backend = backend @worker = worker @schedulers = Strict::Map.new @closed_all = Internal::Flag.new(false) @initialization_key = Object.new.freeze @registration_key = Object.new.freeze super(scope:, capacity: capacity && Integer(capacity), backend:) end |
Class Method Details
.create(executor = Thread, name: nil, priority: nil) ⇒ Scheduler
Starts a worker in this scope and retains the launch settings for other scopes. Additional options and the block are forwarded to #initialize.
42 43 44 45 46 |
# File 'lib/farce/local/scheduler.rb', line 42 def self.create(executor = Thread, name: nil, priority: nil, **, &) raise ArgumentError, "Local schedulers cannot launch Ractor workers" if executor && executor <= Ractor worker = [executor || Thread, name && -String(name), priority && Integer(priority)].freeze new(**, worker:, &) end |
.current ⇒ Unshared::Scheduler?
Returns the current thread's unshared scheduler, or nil when none is installed.
33 |
# File 'lib/farce/local/scheduler.rb', line 33 def self.current(**) = Unshared::Scheduler.current(**) |
Instance Method Details
#alive? ⇒ Boolean
Returns Whether the current scope is dispatching tasks.
133 |
# File 'lib/farce/local/scheduler.rb', line 133 def alive? = current_scheduler&.alive? || false |
#capacity ⇒ BasicObject
Pending-task capacity in this scope. New scopes retain the constructor value.
70 |
# File 'lib/farce/local/scheduler.rb', line 70 def capacity = (scheduler = current_scheduler) ? scheduler.capacity : @capacity |
#capacity=(value) ⇒ BasicObject
Resize the queue in this scope without starting a worker.
73 74 75 76 |
# File 'lib/farce/local/scheduler.rb', line 73 def capacity=(value) raise SchedulerClosedError, "cannot configure a closed scheduler" if @closed_all.value scoped_value.capacity = value end |
#close ⇒ self
Closes the current scope and rejects new submissions there.
104 105 106 107 |
# File 'lib/farce/local/scheduler.rb', line 104 def close current_scheduler&.close self end |
#close_all ⇒ self
Requests shutdown in every scope and Ractor and rejects future scopes. Does not wait for queued or running tasks to finish.
112 113 114 115 116 117 118 119 120 121 |
# File 'lib/farce/local/scheduler.rb', line 112 def close_all return self unless @closed_all.compare_and_set(false, true) @schedulers.each_pair do |scheduler, queue| queue.push([scheduler, :close].freeze, clock: 0) rescue ClosedQueueError # The owning Ractor may already have shut down its supervisor. nil end close end |
#closed? ⇒ Boolean
Returns Whether the current scope rejects new tasks.
130 |
# File 'lib/farce/local/scheduler.rb', line 130 def closed? = @closed_all.value || scoped_value.closed? |
#error ⇒ Exception?
Returns The current scope's scheduler error.
127 |
# File 'lib/farce/local/scheduler.rb', line 127 def error = current_scheduler&.error |
#external_scheduler ⇒ Object?
Returns The fiber scheduler in the current scope, when supplied by a constructor.
148 |
# File 'lib/farce/local/scheduler.rb', line 148 def external_scheduler = current_scheduler&.external_scheduler |
#launch(executor = Thread, name: nil, priority: nil) ⇒ Thread, Object
Starts a worker for the current scope. Other scopes retain their creation behavior.
96 97 98 99 100 |
# File 'lib/farce/local/scheduler.rb', line 96 def launch(executor = Thread, name: nil, priority: nil) raise ArgumentError, "Local schedulers cannot launch Ractor workers" if executor && executor <= Ractor raise "Scheduler may not be reused" if @closed_all.value active_scoped_value.launch(executor, name:, priority:) end |
#owner ⇒ Ractor
Returns The current scope's owning Ractor.
136 |
# File 'lib/farce/local/scheduler.rb', line 136 def owner = Ractor.current |
#owner=(value) ⇒ BasicObject
140 141 142 |
# File 'lib/farce/local/scheduler.rb', line 140 def owner=(value) raise ArgumentError, "owner must be the current Ractor" unless value == Ractor.current end |
#schedule(*args, mode: :smart, auto_local: true, timeout: nil) ⇒ self?
Enqueue a task in the current scope, preserving its block and arguments.
mode is ignored when auto_local is true. Otherwise, only local and smart modes are accepted.
83 |
# File 'lib/farce/local/scheduler.rb', line 83 def schedule(...) = enqueue_task(false, ...) |
#state ⇒ Symbol
Returns The current scope's scheduler state.
124 |
# File 'lib/farce/local/scheduler.rb', line 124 def state = current_scheduler&.state || :closed |
#try_schedule(*args, mode: :smart, auto_local: true) ⇒ self?
Enqueues a task without waiting for queue space.
Arguments and transfer modes follow #schedule. With auto_local enabled,
it still waits for ownership to be assigned.
86 87 88 |
# File 'lib/farce/local/scheduler.rb', line 86 def try_schedule(*, mode: :smart, auto_local: true, &) enqueue_task(true, *, mode:, auto_local:, &) end |
#wraps_external? ⇒ Boolean
Returns Whether a fiber scheduler constructor was supplied.
145 |
# File 'lib/farce/local/scheduler.rb', line 145 def wraps_external? = !@constructor.nil? |