farce 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/lib/farce/task.rb ADDED
@@ -0,0 +1,285 @@
1
+ # frozen_string_literal: true
2
+ # shareable_constant_value: literal
3
+ # warn_indent: true
4
+
5
+ module Farce
6
+ # A unit of work that executes once and retains its result or failure.
7
+ #
8
+ # ```ruby
9
+ # # You can create and schedule a task with one call:
10
+ # task = Farce::Task.schedule { 1 + 2 }
11
+ # task.value # => 3
12
+ #
13
+ # # There is also a shorthand for running a task on the global Ractor pool:
14
+ # task = Farce::Task.in_parallel { sleep 1 }
15
+ # task.join
16
+ # task.alive? # => false
17
+ #
18
+ # # schedule accepts a custom scheduler:
19
+ # scheduler = Farce::Scheduler.create
20
+ # task = Farce::Task.schedule(scheduler:) { 1 + 2 }
21
+ #
22
+ # # or alternatively you can manually create a task and hand it to the scheduler
23
+ # task = Farce::Task.new { 1 + 2 }
24
+ # scheduler.schedule(&task)
25
+ #
26
+ # # or execute it manually
27
+ # task = Farce::Task.new { Ractor.current.name.upcase }
28
+ # Ractor.new(task, name: "Herbert") { it.call }
29
+ # task.value # => "HERBERT"
30
+ # ```
31
+ class Task
32
+ include Abstract::Value
33
+ include Internal::Freeze::Delegated
34
+ include Internal::MarshalSupport::Reject
35
+
36
+ # Raised when an operation is attempted on a task that is in an incorrect state.
37
+ InvalidStateError = Class.new(RuntimeError)
38
+
39
+ # @!macro [new] task_schedule
40
+ # @!macro modes
41
+ # @param args [Array] the arguments to pass to the task
42
+ # @param mode [Symbol] the mode for arguments and the successful result
43
+ # Arguments can be overridden by {#add_argument}.
44
+ # @yield [*args] the block to execute with the given arguments
45
+ # @yieldparam args [Array] the arguments passed to the block
46
+ # @yieldreturn [Object] the result of the block execution, will become the task's {#value}
47
+ # @raise ArgumentError if the provided mode is invalid
48
+ # @raise LocalJumpError if no callback is provided
49
+
50
+ # @overload in_parallel(*args, mode: :smart)
51
+ # Creates a task and schedules it for execution on the {Farce.in_parallel globally shared executor pool}.
52
+ # @!macro task_schedule
53
+ # @return [Task] the created task
54
+ def self.in_parallel(*, **, &) = enqueue(false, Farce.in_parallel, *, auto_local: false, **, &)
55
+
56
+ # @overload on_main(*args, mode: :smart, auto_local: true)
57
+ # Creates a task and schedules it for execution on the {Farce.on_main main scheduler}.
58
+ # If `auto_local` is true (default), `mode` will be set to `:local` if the method is called from the main Ractor.
59
+ #
60
+ # @!macro task_schedule
61
+ # @param auto_local [Boolean] whether to automatically switch to local mode if called from the main Ractor
62
+ # @return [Task] the created task
63
+ def self.on_main(*, **, &) = enqueue(false, Farce.on_main, *, **, &)
64
+
65
+ # @overload schedule(*args, mode: :smart, auto_local: true, scheduler: nil, &)
66
+ # Creates a task and schedules it for execution.
67
+ # A scheduler may optionally be provided.
68
+ #
69
+ # If no scheduler is provided, then it will look for a Ractor-local scheduler if mode is `:local` or `auto_local`
70
+ # is true. If none is found, one will be created automatically if `:mode` is `:local`, otherwise the
71
+ # {Farce.in_parallel globally shared executor pool} will be used.
72
+ #
73
+ # The scheduler may be a {Abstract::Executor executor}, or a Fiber scheduler.
74
+ #
75
+ # @!macro task_schedule
76
+ # @param scheduler [Abstract::Executor, Fiber::Scheduler, nil] the scheduler to use for execution
77
+ # @return [Task] the created task
78
+ def self.schedule(*, scheduler: nil, **, &) = enqueue(false, scheduler, *, **, &)
79
+
80
+ # @overload try_schedule(*args, mode: :smart, auto_local: true, scheduler: nil, &)
81
+ # Creates a task and attempts to schedule it for execution.
82
+ #
83
+ # Behaves exactly like {.schedule}, except it uses {Abstract::Executor#try_schedule} instead of
84
+ # {Abstract::Executor#schedule} if the scheduler is an executor, and that it returns `nil` if it fails to
85
+ # schedule the task.
86
+ #
87
+ # @!macro task_schedule
88
+ # @return [Task, nil] the created task, or `nil` if it failed to schedule
89
+ def self.try_schedule(*, scheduler: nil, **, &) = enqueue(true, scheduler, *, **, &)
90
+
91
+ # @api private
92
+ def self.enqueue(non_block, scheduler, *, mode: :smart, auto_local: true, &) # :nodoc:
93
+ unless scheduler
94
+ scheduler = Internal.local_scheduler(create: mode == :local) if auto_local || mode == :local
95
+ unless scheduler
96
+ scheduler = Internal::ParallelScheduler
97
+ auto_local = false
98
+ end
99
+ end
100
+
101
+ if scheduler.is_a?(Abstract::Executor)
102
+ mode = :local if auto_local && scheduler.local?(wait: false)
103
+ task = new(*, mode:, &)
104
+
105
+ if non_block
106
+ return unless scheduler.try_schedule(mode:, auto_local: false, &task)
107
+ else
108
+ scheduler.schedule(mode:, auto_local: false, &task)
109
+ end
110
+ else
111
+ task = new(*, mode: :local, &)
112
+ scheduler.fiber(&task)
113
+ end
114
+
115
+ task
116
+ end
117
+ private_class_method :enqueue
118
+
119
+ # @return [Symbol] the default mode used for task arguments and the return value
120
+ attr_reader :mode
121
+
122
+ # Creates a new task with the given arguments and callback.
123
+ #
124
+ # This does not schedule the task for execution, nor does it associate it with any executor.
125
+ #
126
+ # In fact, the task object itself is a proc-like object, that can be {call called} like a regular proc,
127
+ # but will track its execution state and result.
128
+ #
129
+ # @example Using a Task without an Executor
130
+ # task = Farce::Task.new(1, 2) { |a, b| a + b }
131
+ #
132
+ # task.alive? # => true
133
+ # task.call == task # => true
134
+ # task.alive? # => false
135
+ # task.value # => 3
136
+ #
137
+ # @example Passing a task to an executor
138
+ # task = Farce::Task.new { puts "Hello, world!" }
139
+ # Farce.in_parallel(&task)
140
+ # task.join # waits for the task to complete
141
+ #
142
+ # @!macro task_schedule
143
+ def initialize(*args, mode: :smart, &callback)
144
+ raise ArgumentError, "invalid mode: #{mode}" unless MODES.include?(mode)
145
+ raise LocalJumpError, "no callback provided" unless block_given?
146
+
147
+ @mode = mode
148
+ @ractor = Strict::Atom.new
149
+ @state = Strict::Atom.new(:initialized)
150
+
151
+ if mode == :local
152
+ @arguments = args
153
+ @callback = callback
154
+ @manager = nil
155
+ @ractor.value = Ractor.current
156
+ @result = Unshared::Atom.new
157
+ else
158
+ @arguments = Strict::Vector.new
159
+ @callback = Ractor.shareable?(callback) ? callback : Ractor.shareable_proc(&callback)
160
+ @manager = ModeManager.new(mode: mode)
161
+ @result = Strict::Atom.new
162
+ args.each { add_argument(it) }
163
+ end
164
+
165
+ __freeze__
166
+ end
167
+
168
+ # Adds an argument to the task.
169
+ # Can only be called before the task is frozen and before {#call} is invoked.
170
+ #
171
+ # Allows for arguments to use different modes than the return value (or other arguments).
172
+ # Mode is ignored for {#local? local} tasks.
173
+ #
174
+ # For non-local tasks, `mode` may only be set to `:local` for arguments added by the Ractor that will
175
+ # {#call execute} the task.
176
+ #
177
+ # @example A task using `move` for the return value, but `copy` for its argument
178
+ # task = Farce::Task.new(mode: :move) { |arg| arg.upcase }
179
+ # task.add_argument("hello", mode: :copy)
180
+ # Farce.in_parallel(&task)
181
+ # task.value # => "HELLO"
182
+ #
183
+ # @param argument [BasicObject] the argument to add to the task
184
+ # @param mode [Symbol, nil] the mode to use for the argument, or `nil` to use the task's default mode
185
+ # @raise ArgumentError if the provided mode is invalid
186
+ # @raise FrozenError if the task has already been frozen or called
187
+ # @raise InvalidStateError if trying to add a local argument from more than one ractor
188
+ # @return [self] the task itself, for chaining
189
+ def add_argument(argument, mode: nil)
190
+ check_frozen!
191
+
192
+ unless local?
193
+ raise ArgumentError, "invalid mode: #{mode}" unless mode.nil? || MODES.include?(mode)
194
+ raise InvalidStateError, "cannot add local argument to a non-local task" if mode == :local && !claim_for_ractor?
195
+ argument = @manager.wrap(argument, mode:)
196
+ end
197
+
198
+ @arguments << argument
199
+ self
200
+ end
201
+
202
+ alias << add_argument
203
+
204
+ # @return [Boolean] whether the task is currently alive (initialized or running)
205
+ def alive? = alive_state?(@state.value)
206
+
207
+ # Executes the task by invoking its callback with the added arguments.
208
+ # @raise InvalidStateError if the task has already been claimed by another ractor or has already been called
209
+ # @return [self] the task itself, for chaining
210
+ def call
211
+ raise InvalidStateError, "task already claimed by another ractor" unless claim_for_ractor?
212
+ raise InvalidStateError, "call may only be called once" unless @state.compare_and_set(:initialized, :running)
213
+ begin
214
+ arguments = local? ? @arguments.freeze : @arguments.freeze.to_a.map! { @manager.unwrap(it) }
215
+ result = @callback.call(*arguments)
216
+ @result.value = local? ? result : @manager.wrap(result)
217
+ @state.value = :finished
218
+ rescue Exception => e # rubocop:disable Lint/RescueException
219
+ # Every callback failure must release waiters, including ScriptError and SystemExit.
220
+ begin
221
+ @result.value = e
222
+ rescue Ractor::Error
223
+ @result.value = [RuntimeError, -"#{e.class}: #{e.message}", Ractor.make_shareable(e.backtrace)].freeze
224
+ end
225
+ @state.value = :error
226
+ ensure
227
+ if @state.value == :running
228
+ @result.value = [RuntimeError, -"task execution aborted"].freeze
229
+ @state.value = :error
230
+ end
231
+ end
232
+
233
+ self
234
+ end
235
+
236
+ # Waits for the task to finish, with an optional timeout.
237
+ # @param timeout [Numeric, nil] the maximum time to wait, or `nil` to wait indefinitely
238
+ # @return [self, nil] the task itself if it finished within the timeout, or `nil` otherwise
239
+ def join(timeout = nil)
240
+ return unless @state.wait_while(timeout:) { alive_state?(it) }
241
+ self
242
+ end
243
+
244
+ # @return [Boolean] whether the task is local (i.e., its mode is `:local`)
245
+ def local? = @mode == :local
246
+
247
+ # Waits for the task to finish and returns its value, with an optional timeout.
248
+ # If the task raises an error, it will be propagated.
249
+ #
250
+ # @param timeout [Numeric, nil] the maximum time to wait, or `nil` to wait indefinitely
251
+ # @return [BasicObject, nil]
252
+ # the value returned by the task's callback, or `nil` if the task did not finish within the timeout
253
+ # @raise Exception if the task raises an error
254
+ # @raise RuntimeError with the original class, message, and backtrace if the exception cannot be shared
255
+ # @raise RuntimeError if execution is aborted before producing a result or exception
256
+ def value(timeout = nil)
257
+ case @state.wait_while(timeout:) { alive_state?(it) }
258
+ when :finished then local? ? @result.value : @manager.unwrap(@result.value)
259
+ when :error then raise(*@result.value)
260
+ end
261
+ end
262
+
263
+ # Converts the task to a Proc object.
264
+ # If the task is Ractor-shareable, then so is the returned Proc.
265
+ # @return [Proc] a Proc that calls the task's `call` method
266
+ def to_proc
267
+ return method(:call).to_proc if local?
268
+ task = self
269
+ Ractor.make_shareable(-> { task.call })
270
+ end
271
+
272
+ private
273
+
274
+ def alive_state?(state) = state == :initialized || state == :running
275
+
276
+ def claim_for_ractor?
277
+ current_ractor = Ractor.current
278
+ @ractor.compare_and_set(nil, current_ractor)
279
+ @ractor.value == current_ractor
280
+ end
281
+
282
+ define_method(:__freeze__, Kernel.instance_method(:freeze))
283
+ def freeze_backend = @arguments
284
+ end
285
+ end
@@ -3,11 +3,11 @@
3
3
  # warn_indent: true
4
4
 
5
5
  module Farce
6
- # A scheduler that starts a new thread for each scheduled task. Executes synchronous tasks inline.
6
+ # A task executor that starts a new thread for each scheduled task. Executes synchronous tasks inline.
7
7
  # Creating an instance starts no threads. Blocks and arguments stay local to the caller.
8
- # Tasks are not tracked, and closing this stateless scheduler has no effect.
8
+ # Tasks are not tracked, and closing this stateless executor has no effect.
9
9
  # Default instances support Marshal. Instances with a custom thread factory reject dumping.
10
- class ThreadScheduler < Farce::Abstract::Scheduler
10
+ class ThreadExecutor < Farce::Abstract::Executor
11
11
  include Shareable::Immutable
12
12
  include Internal::Inspect
13
13
 
@@ -18,7 +18,7 @@ module Farce
18
18
 
19
19
  # @api private
20
20
  def marshal_dump
21
- raise TypeError, "scheduler factories cannot be marshaled" if @factory
21
+ raise TypeError, "thread factories cannot be marshaled" if @factory
22
22
  [1]
23
23
  end
24
24
 
@@ -32,7 +32,7 @@ module Farce
32
32
  #
33
33
  # @overload schedule(*args, **options)
34
34
  # @param args [Array<Object>] Arguments passed to the block.
35
- # @param options [Hash] Scheduler options are ignored. All tasks use local arguments.
35
+ # @param options [Hash] Execution options are ignored. All tasks use local arguments.
36
36
  # @yield [*args] The task to run.
37
37
  # @return [self]
38
38
  def schedule(*, **, &)
@@ -45,7 +45,7 @@ module Farce
45
45
  #
46
46
  # @overload execute(*args, **options)
47
47
  # @param args [Array<Object>] Arguments passed to the block.
48
- # @param options [Hash] Scheduler options are ignored. All tasks use local arguments.
48
+ # @param options [Hash] Execution options are ignored. All tasks use local arguments.
49
49
  # @yield [*args] The task to run.
50
50
  # @return [self]
51
51
  def execute(*, **)
@@ -57,7 +57,7 @@ module Farce
57
57
  # @return [self] Does not wait for or stop task threads.
58
58
  def close = self
59
59
 
60
- # @return [false] This stateless scheduler is always available.
60
+ # @return [false] This stateless executor is always available.
61
61
  def closed? = false
62
62
 
63
63
  # @return [Symbol] Always `:running`.
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+ # shareable_constant_value: literal
3
+ # warn_indent: true
4
+
5
+ module Farce
6
+ module Unshared
7
+ # An elastic pool of thread workers within one Ractor.
8
+ #
9
+ # Uses the same scaling and fiber schedulers as {Farce::Pool}. Tasks keep their
10
+ # blocks and argument references. `auto_local: true` ignores `mode`. With
11
+ # `auto_local: false`, only `:local` and smart modes are accepted.
12
+ # The pool cannot be copied or moved to another Ractor.
13
+ # A scheduler constructor may capture objects from this Ractor.
14
+ #
15
+ # @example Submit work using local state
16
+ # results = Farce::Unshared::Queue.new
17
+ # pool = Farce::Unshared::Pool.new(max_size: 2)
18
+ # pool.schedule { results.push(:done) }
19
+ # results.pop # => :done
20
+ # pool.close
21
+ class Pool < Abstract::Pool
22
+ include Unshareable
23
+ include Internal::LocalExecutorMethods
24
+
25
+ private
26
+
27
+ def publish_pool = pin_unshareable
28
+ def prepare_constructor(constructor) = constructor
29
+ def new_queue(capacity:) = Internal::UnsharedQueue.new(capacity:, track_age: true)
30
+ def new_worker = Internal::UnsharedPoolWorker.new(self)
31
+ def new_scheduler(**, &) = Scheduler.new(**, &)
32
+ def launch_scheduler(scheduler) = scheduler.launch_thread
33
+
34
+ def prepare_task(args, block, mode, auto_local)
35
+ validate_local_mode(mode, auto_local)
36
+ Internal::LocalScheduledTask.new(args, block)
37
+ end
38
+
39
+ def schedule_scaler(started, delay = @grow_after)
40
+ Internal::ThreadPoolSupervisor.current.schedule(self, started, delay)
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,89 @@
1
+ # frozen_string_literal: true
2
+ # shareable_constant_value: literal
3
+ # warn_indent: true
4
+
5
+ module Farce
6
+ module Unshared
7
+ # A task scheduler that runs work within its creating Ractor.
8
+ #
9
+ # Use {.create} to start a background thread, or install an instance with
10
+ # `Fiber.set_scheduler` on the current thread. Tasks retain their blocks and
11
+ # argument references. The scheduler cannot be copied or moved to another Ractor.
12
+ #
13
+ # `auto_local: true` ignores `mode`. With `auto_local: false`, only `:local`
14
+ # and smart modes are accepted. All accepted modes keep tasks local.
15
+ #
16
+ # @example Run tasks with captured local state
17
+ # results = Farce::Unshared::Queue.new
18
+ # scheduler = Farce::Unshared::Scheduler.create
19
+ # scheduler.schedule { results.push(:done) }
20
+ # results.pop # => :done
21
+ # scheduler.close
22
+ #
23
+ # @!method schedule(*args, mode: :smart, auto_local: true)
24
+ # Enqueue a task while retaining its block and argument references.
25
+ # `mode` is ignored when `auto_local` is true. Otherwise, only local and
26
+ # smart modes are accepted.
27
+ # @param (see Farce::Abstract::Executor#schedule)
28
+ # @return [self]
29
+ class Scheduler < Abstract::Scheduler
30
+ include Unshareable
31
+ include Internal::LocalExecutorMethods
32
+
33
+ # Start a worker thread in this Ractor. Returns the running scheduler.
34
+ # Additional options and the block are forwarded to {#initialize}.
35
+ # @param executor [Class, nil] Thread or a custom executor that stays in this Ractor.
36
+ # @param name [String, nil] Optional worker name.
37
+ # @param priority [Integer, nil] Optional worker thread priority.
38
+ # @yieldreturn [Object] Fiber scheduler to install in the worker.
39
+ # @return [Scheduler]
40
+ def self.create(executor = Thread, name: nil, priority: nil, **, &) = super
41
+
42
+ # Create a scheduler without starting a worker.
43
+ # A constructor block builds the fiber scheduler when launched or installed.
44
+ # It may capture objects from this Ractor.
45
+ #
46
+ # @overload initialize(capacity: 1024, backend: Farce.config.io_backend, &constructor)
47
+ # @param capacity [Integer, nil] Pending-task capacity, or nil for an unbounded queue.
48
+ # @param backend [Symbol] IO backend for the built-in scheduler.
49
+ # @yieldreturn [Object] Fiber scheduler to install in the worker.
50
+ def initialize(...)
51
+ super
52
+ self.owner = Ractor.current
53
+ end
54
+
55
+ # Start dispatching tasks in a new thread or a custom executor in this Ractor.
56
+ # Rejects Ractor executors before changing the scheduler's state.
57
+ # @param executor [Class, nil] Thread or a custom executor that stays in this Ractor.
58
+ # @param name [String, nil] Optional worker name.
59
+ # @param priority [Integer, nil] Optional worker thread priority.
60
+ # @return [Thread, Object] The worker.
61
+ # @raise [ArgumentError] If a Ractor executor is supplied.
62
+ def launch(executor = Thread, name: nil, priority: nil)
63
+ raise ArgumentError, "Unshared schedulers cannot launch Ractor workers" if executor && executor <= Ractor
64
+ super(executor || Thread, name:, priority:)
65
+ end
66
+
67
+ # @raise [ArgumentError] Unshared schedulers cannot launch Ractor workers.
68
+ def launch_ractor(...) = launch(Ractor, ...)
69
+
70
+ # @param value [Ractor] The creating Ractor.
71
+ # @raise [ArgumentError] If another Ractor is supplied or the assigned owner changes.
72
+ def owner=(value)
73
+ raise ArgumentError, "owner must be the current Ractor" unless value == Ractor.current
74
+ super
75
+ end
76
+
77
+ private
78
+
79
+ def prepare_constructor(constructor) = constructor
80
+ def new_queue(capacity:) = Internal::UnsharedQueue.new(capacity:)
81
+ def launch_callback = ->(_scheduler, priority, name) { launch!(name, priority) }
82
+
83
+ def prepare_task(args, block, mode, auto_local)
84
+ validate_local_mode(mode, auto_local)
85
+ Internal::LocalScheduledTask.new(args, block)
86
+ end
87
+ end
88
+ end
89
+ end
data/lib/farce/version.rb CHANGED
@@ -4,5 +4,5 @@
4
4
 
5
5
  module Farce
6
6
  # @return [String] the current version of Farce
7
- VERSION = "0.2.0"
7
+ VERSION = "0.3.0"
8
8
  end
data/lib/farce.rb CHANGED
@@ -360,7 +360,7 @@ module Farce
360
360
  # This allows scheduling without blocking the current thread.
361
361
  #
362
362
  # On JRuby and TruffleRuby, blocks run inline because there is no native Ractor isolation.
363
- # The returned {ThreadScheduler} starts a new thread for each scheduled task.
363
+ # The returned {ThreadExecutor} starts a new thread for each scheduled task.
364
364
  #
365
365
  # @example
366
366
  # $results = []
@@ -408,9 +408,9 @@ module Farce
408
408
  #
409
409
  # @overload on_main
410
410
  # Returns a scheduler that executes tasks on the main ractor.
411
- # @return [Abstract::Scheduler]
411
+ # @return [Abstract::Executor]
412
412
  #
413
- # @return [Abstract::Scheduler, nil]
413
+ # @return [Abstract::Executor, nil]
414
414
  # @see .in_parallel
415
415
  def self.on_main(*args, mode: UNDEFINED, &)
416
416
  unless block_given?
@@ -448,9 +448,9 @@ module Farce
448
448
  # @return [nil]
449
449
  #
450
450
  # @overload in_parallel
451
- # @return [Abstract::Scheduler] The shared scheduler.
451
+ # @return [Abstract::Executor] The shared scheduler.
452
452
  #
453
- # @return [Abstract::Scheduler, nil]
453
+ # @return [Abstract::Executor, nil]
454
454
  # @see .on_main
455
455
  def self.in_parallel(*args, mode: UNDEFINED, &)
456
456
  unless block_given?
@@ -493,22 +493,14 @@ module Farce
493
493
  raise LocalJumpError, "no block given" unless block_given?
494
494
 
495
495
  if auto_local || mode == :local
496
- if Fiber.respond_to?(:scheduler) && fiber_scheduler = Fiber.scheduler
497
- fiber_scheduler.fiber(**) { yield(*) }
496
+ scheduler = Internal.local_scheduler(create: mode == :local)
497
+ if scheduler && !scheduler.is_a?(Abstract::Executor)
498
+ scheduler.fiber(**) { yield(*) }
498
499
  return
499
500
  end
500
- scheduler = Ractor.main? ? Internal::MainScheduler : Internal::Storage[:local_scheduler]
501
501
  end
502
502
 
503
- scheduler ||=
504
- if mode == :local
505
- Internal::Storage.store_if_absent(:local_scheduler) do
506
- Internal.native_ractors? ? Scheduler.create(Thread) : ThreadScheduler.new
507
- end
508
- else
509
- Internal::ParallelScheduler
510
- end
511
-
503
+ scheduler ||= Internal::ParallelScheduler
512
504
  scheduler.schedule(*, mode:, auto_local:, **, &)
513
505
  nil
514
506
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: farce
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Konstantin Haase
@@ -85,6 +85,7 @@ files:
85
85
  - lib/farce/abstract/counter.rb
86
86
  - lib/farce/abstract/duplicable_map.rb
87
87
  - lib/farce/abstract/exchanger.rb
88
+ - lib/farce/abstract/executor.rb
88
89
  - lib/farce/abstract/flag.rb
89
90
  - lib/farce/abstract/lazy.rb
90
91
  - lib/farce/abstract/lease.rb
@@ -94,6 +95,7 @@ files:
94
95
  - lib/farce/abstract/lru_map.rb
95
96
  - lib/farce/abstract/map.rb
96
97
  - lib/farce/abstract/molecule.rb
98
+ - lib/farce/abstract/pool.rb
97
99
  - lib/farce/abstract/port.rb
98
100
  - lib/farce/abstract/priority_queue.rb
99
101
  - lib/farce/abstract/queue.rb
@@ -262,6 +264,8 @@ files:
262
264
  - lib/farce/internal/lease_pool_state.rb
263
265
  - lib/farce/internal/lease_state.rb
264
266
  - lib/farce/internal/lease_waiting.rb
267
+ - lib/farce/internal/local_executor_methods.rb
268
+ - lib/farce/internal/local_scheduled_task.rb
265
269
  - lib/farce/internal/managed_queue.rb
266
270
  - lib/farce/internal/map_value_modes.rb
267
271
  - lib/farce/internal/marshal_support.rb
@@ -287,12 +291,14 @@ files:
287
291
  - lib/farce/internal/strict_lease_map.rb
288
292
  - lib/farce/internal/strict_lease_pool.rb
289
293
  - lib/farce/internal/thread_pool.rb
294
+ - lib/farce/internal/thread_pool_supervisor.rb
290
295
  - lib/farce/internal/transaction_conflict.rb
291
296
  - lib/farce/internal/transaction_freeze_guard.rb
292
297
  - lib/farce/internal/transaction_map_snapshot.rb
293
298
  - lib/farce/internal/undefined.rb
294
299
  - lib/farce/internal/unshared_lease.rb
295
300
  - lib/farce/internal/unshared_lease_pool.rb
301
+ - lib/farce/internal/unshared_pool_worker.rb
296
302
  - lib/farce/internal/unshared_queue_waiting.rb
297
303
  - lib/farce/internal/value_serialization.rb
298
304
  - lib/farce/internal/weak_map_value_modes.rb
@@ -316,8 +322,10 @@ files:
316
322
  - lib/farce/local/lru_map.rb
317
323
  - lib/farce/local/map.rb
318
324
  - lib/farce/local/molecule.rb
325
+ - lib/farce/local/pool.rb
319
326
  - lib/farce/local/priority_queue.rb
320
327
  - lib/farce/local/queue.rb
328
+ - lib/farce/local/scheduler.rb
321
329
  - lib/farce/local/scoped.rb
322
330
  - lib/farce/local/set.rb
323
331
  - lib/farce/local/sorted_set.rb
@@ -382,7 +390,8 @@ files:
382
390
  - lib/farce/strict/weak_set.rb
383
391
  - lib/farce/strict/weak_value_map.rb
384
392
  - lib/farce/system.rb
385
- - lib/farce/thread_scheduler.rb
393
+ - lib/farce/task.rb
394
+ - lib/farce/thread_executor.rb
386
395
  - lib/farce/timer_queue.rb
387
396
  - lib/farce/transaction.rb
388
397
  - lib/farce/transaction/atom.rb
@@ -415,8 +424,10 @@ files:
415
424
  - lib/farce/unshared/lru_map.rb
416
425
  - lib/farce/unshared/map.rb
417
426
  - lib/farce/unshared/molecule.rb
427
+ - lib/farce/unshared/pool.rb
418
428
  - lib/farce/unshared/priority_queue.rb
419
429
  - lib/farce/unshared/queue.rb
430
+ - lib/farce/unshared/scheduler.rb
420
431
  - lib/farce/unshared/set.rb
421
432
  - lib/farce/unshared/sorted_set.rb
422
433
  - lib/farce/unshared/timer_queue.rb