farce 0.4.1-x86_64-linux-gnu → 0.5.0-x86_64-linux-gnu
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +80 -2
- data/README.md +3 -1
- data/docs/benchmarks.md +37 -21
- data/docs/compatibility.md +13 -0
- data/docs/data-structures.md +37 -0
- data/docs/integrations.md +1 -0
- data/docs/introduction.md +19 -0
- data/docs/modes.md +2 -0
- data/docs/scheduling.md +35 -3
- data/docs/scopes.md +3 -1
- data/docs/shims.md +53 -0
- data/docs/utilities.md +31 -0
- data/docs/variants.md +9 -0
- data/ext/farce/farce.c +9 -0
- data/ext/farce/map.c +14 -3
- data/ext/farce/priority_queue.c +46 -1
- data/ext/farce/queue.c +141 -7
- data/ext/farce/vector.c +3 -3
- data/ext/farce/weak_map.c +12 -3
- data/ext/java/org/farce/PriorityQueue.java +18 -2
- data/lib/farce/abstract/concurrent_map.rb +122 -0
- data/lib/farce/abstract/counter.rb +35 -1
- data/lib/farce/abstract/executor.rb +30 -7
- data/lib/farce/abstract/flag.rb +12 -0
- data/lib/farce/abstract/pool.rb +158 -47
- data/lib/farce/abstract/priority_queue.rb +1 -1
- data/lib/farce/abstract/publisher.rb +189 -0
- data/lib/farce/abstract/queue.rb +29 -3
- data/lib/farce/abstract/scheduler.rb +41 -14
- data/lib/farce/abstract/timer_queue.rb +3 -3
- data/lib/farce/abstract/vector.rb +3 -1
- data/lib/farce/atom.rb +9 -2
- data/lib/farce/clock.rb +12 -0
- data/lib/farce/concurrent/count_down_latch.rb +37 -0
- data/lib/farce/concurrent/cyclic_barrier.rb +109 -0
- data/lib/farce/concurrent/event.rb +60 -0
- data/lib/farce/concurrent/fiber_local_var.rb +31 -0
- data/lib/farce/concurrent/local_var.rb +65 -0
- data/lib/farce/concurrent/thread_local_var.rb +28 -0
- data/lib/farce/concurrent/tuple.rb +74 -0
- data/lib/farce/concurrent.rb +50 -0
- data/lib/farce/config.rb +112 -2
- data/lib/farce/counter.rb +0 -16
- data/lib/farce/engine/jruby/map.rb +8 -6
- data/lib/farce/engine/jruby/signal.rb +2 -2
- data/lib/farce/engine/jvm/farce.jar +0 -0
- data/lib/farce/engine/jvm/priority_queue.rb +14 -4
- data/lib/farce/engine/ruby/3.4/farce.so +0 -0
- data/lib/farce/engine/ruby/4.0/farce.so +0 -0
- data/lib/farce/engine/ruby/shared/parallel_scheduler.rb +9 -1
- data/lib/farce/engine/ruby/shared/ractor_selector.rb +1 -4
- data/lib/farce/engine/ruby/shared/weak_map.rb +9 -12
- data/lib/farce/engine/shared/exchanger.rb +2 -2
- data/lib/farce/engine/shared/queue.rb +43 -5
- data/lib/farce/engine/shared/ractor_selector.rb +1 -1
- data/lib/farce/engine/shared/strict_map.rb +5 -0
- data/lib/farce/engine/shared/unshared_atom.rb +1 -1
- data/lib/farce/engine/shared/unshared_vector.rb +8 -7
- data/lib/farce/engine/shared/unshared_weak_map.rb +8 -6
- data/lib/farce/engine/shared/weak_atom/base.rb +5 -10
- data/lib/farce/engine/shared/weak_map/cell.rb +1 -1
- data/lib/farce/engine/truffleruby/map.rb +8 -6
- data/lib/farce/engine/truffleruby/native/priority_queue.rb +18 -4
- data/lib/farce/engine/truffleruby/signal.rb +1 -1
- data/lib/farce/integrations/concurrent.rb +16 -3
- data/lib/farce/internal/blocking_priority_queue.rb +4 -9
- data/lib/farce/internal/flag_waiting.rb +1 -1
- data/lib/farce/internal/lease_initialization.rb +1 -1
- data/lib/farce/internal/lease_pool_state.rb +1 -1
- data/lib/farce/internal/lease_state.rb +2 -2
- data/lib/farce/internal/local_executor_methods.rb +3 -3
- data/lib/farce/internal/map_value_modes.rb +9 -2
- data/lib/farce/internal/pool_settings.rb +9 -0
- data/lib/farce/internal/pool_worker.rb +34 -21
- data/lib/farce/internal/reservation_waiting.rb +1 -1
- data/lib/farce/internal/scheduler_lifecycle.rb +2 -2
- data/lib/farce/internal/weak_map_value_modes.rb +5 -0
- data/lib/farce/internal.rb +8 -8
- data/lib/farce/lazy.rb +3 -0
- data/lib/farce/lazy_ref.rb +3 -0
- data/lib/farce/local/lease.rb +2 -9
- data/lib/farce/local/lease_map.rb +2 -9
- data/lib/farce/local/pool.rb +38 -5
- data/lib/farce/local/publisher.rb +30 -0
- data/lib/farce/local/scheduler.rb +18 -5
- data/lib/farce/local/vector.rb +3 -2
- data/lib/farce/memoize.rb +314 -0
- data/lib/farce/pool.rb +5 -0
- data/lib/farce/priority_queue.rb +1 -1
- data/lib/farce/publisher.rb +57 -0
- data/lib/farce/signal.rb +1 -1
- data/lib/farce/strict/publisher.rb +31 -0
- data/lib/farce/strict/vector.rb +3 -2
- data/lib/farce/subscription/definitions.rb +27 -0
- data/lib/farce/subscription.rb +121 -0
- data/lib/farce/transaction/map.rb +35 -1
- data/lib/farce/unshared/pool.rb +1 -1
- data/lib/farce/unshared/publisher.rb +25 -0
- data/lib/farce/unshared/scheduler.rb +3 -3
- data/lib/farce/unshared/vector.rb +3 -2
- data/lib/farce/vector.rb +12 -9
- data/lib/farce/version.rb +1 -1
- data/lib/farce/walker/definitions.rb +13 -2
- data/lib/farce/weak_atom.rb +5 -2
- data/lib/farce.rb +3 -1
- metadata +18 -1
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# shareable_constant_value: literal
|
|
3
|
+
# warn_indent: true
|
|
4
|
+
|
|
5
|
+
module Farce
|
|
6
|
+
# A message multiplexer.
|
|
7
|
+
# Implements the publisher-subscriber protocol.
|
|
8
|
+
#
|
|
9
|
+
# @example
|
|
10
|
+
# publisher = Farce::Publisher.new
|
|
11
|
+
#
|
|
12
|
+
# # Count how many messages have been received
|
|
13
|
+
# counter = Farce::Counter.new
|
|
14
|
+
# publisher.subscribe(counter)
|
|
15
|
+
#
|
|
16
|
+
# # Trigger a callback whenever a message is published
|
|
17
|
+
# publisher.subscribe do |message|
|
|
18
|
+
# puts "Received message: #{message}"
|
|
19
|
+
# end
|
|
20
|
+
#
|
|
21
|
+
# # Send messages to a queue
|
|
22
|
+
# queue = Farce::Queue.new
|
|
23
|
+
# publisher.subscribe(queue)
|
|
24
|
+
#
|
|
25
|
+
# # Keep track of the last published value
|
|
26
|
+
# atom = Farce::Atom.new
|
|
27
|
+
# publisher.subscribe(atom)
|
|
28
|
+
#
|
|
29
|
+
# # Send messages to a Ractor
|
|
30
|
+
# ractor = Ractor.new do
|
|
31
|
+
# while message = Ractor.receive
|
|
32
|
+
# puts "Received message in Ractor: #{message}"
|
|
33
|
+
# end
|
|
34
|
+
# end
|
|
35
|
+
# publisher.subscribe(ractor)
|
|
36
|
+
#
|
|
37
|
+
# # Publish some messages
|
|
38
|
+
# publisher.publish("Hello, world!")
|
|
39
|
+
# publisher.publish("Another message")
|
|
40
|
+
#
|
|
41
|
+
# queue.pop # => "Hello, world!"
|
|
42
|
+
# atom.value # => "Another message"
|
|
43
|
+
# counter.value # => 2
|
|
44
|
+
class Publisher < Farce::Abstract::Publisher
|
|
45
|
+
include Shareable::Unfreezable
|
|
46
|
+
|
|
47
|
+
# @overload initialize(*subscribers)
|
|
48
|
+
# (see Farce::Abstract#initialize)
|
|
49
|
+
def initialize(*subscribers)
|
|
50
|
+
@map = Strict::Map.new
|
|
51
|
+
super()
|
|
52
|
+
subscribers.each { subscribe(it) }
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
private attr_reader :map
|
|
56
|
+
end
|
|
57
|
+
end
|
data/lib/farce/signal.rb
CHANGED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# shareable_constant_value: literal
|
|
3
|
+
# warn_indent: true
|
|
4
|
+
|
|
5
|
+
module Farce
|
|
6
|
+
module Strict
|
|
7
|
+
# Strict variant of {Farce::Publisher}.
|
|
8
|
+
class Publisher < Farce::Abstract::Publisher
|
|
9
|
+
include Shareable::Unfreezable
|
|
10
|
+
|
|
11
|
+
# @overload initialize(*subscribers)
|
|
12
|
+
# (see Farce::Abstract#initialize)
|
|
13
|
+
def initialize(*subscribers)
|
|
14
|
+
@map = Strict::Map.new
|
|
15
|
+
super()
|
|
16
|
+
subscribers.each { subscribe(it) }
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# (see Farce::Abstract::Publisher#publish)
|
|
20
|
+
def publish(message = nil)
|
|
21
|
+
return super if Ractor.shareable?(message)
|
|
22
|
+
raise Ractor::IsolationError, "value must be Ractor-shareable"
|
|
23
|
+
end
|
|
24
|
+
alias send publish
|
|
25
|
+
alias << publish
|
|
26
|
+
alias broadcast publish
|
|
27
|
+
|
|
28
|
+
private attr_reader :map
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
data/lib/farce/strict/vector.rb
CHANGED
|
@@ -8,10 +8,11 @@ module Farce
|
|
|
8
8
|
class Vector < Abstract::Vector
|
|
9
9
|
include Shareable::Delegated
|
|
10
10
|
|
|
11
|
-
# @param source [Array, nil] Initial values. The source array is not retained.
|
|
11
|
+
# @param source [Array, Integer, nil] Initial values or a size filled with nil. The source array is not retained.
|
|
12
12
|
# @param compare_by_identity [Boolean] Whether values are compared by identity.
|
|
13
|
+
# @raise [ArgumentError] If the size is negative.
|
|
13
14
|
def initialize(source = nil, compare_by_identity: false)
|
|
14
|
-
@vector = Internal::Vector.new(source, compare_by_identity:)
|
|
15
|
+
@vector = Internal::Vector.new(normalize_vector_source(source), compare_by_identity:)
|
|
15
16
|
super()
|
|
16
17
|
end
|
|
17
18
|
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# shareable_constant_value: literal
|
|
3
|
+
# warn_indent: true
|
|
4
|
+
|
|
5
|
+
module Farce
|
|
6
|
+
class Subscription
|
|
7
|
+
Farce.on_main do
|
|
8
|
+
ractors = Internal.native_ractors? ? [::Ractor] : [Internal::FakeRactor::Main, Internal::FakeRactor::NotMain]
|
|
9
|
+
|
|
10
|
+
define(Internal::BasePort, *ractors) { subscriber.send(it) }
|
|
11
|
+
define(Abstract::Publisher) { subscriber.publish(it) }
|
|
12
|
+
define(Abstract::Queue) { subscriber.try_push(it) }
|
|
13
|
+
define(Internal::Counter, Local::Counter) { |_| subscriber.increment }
|
|
14
|
+
define(Internal::Flag, Local::Flag) { |_| subscriber.set }
|
|
15
|
+
define(Abstract::Atom) { subscriber.value = it }
|
|
16
|
+
define(Abstract::Set, Abstract::Vector) { subscriber << it }
|
|
17
|
+
define(Signal) { |_| subscriber.broadcast }
|
|
18
|
+
|
|
19
|
+
define(::Thread::Queue) { subscriber.push(it) }
|
|
20
|
+
define(::Thread::SizedQueue) do |payload|
|
|
21
|
+
subscriber.push(payload, true)
|
|
22
|
+
rescue ThreadError
|
|
23
|
+
false
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# shareable_constant_value: literal
|
|
3
|
+
# warn_indent: true
|
|
4
|
+
|
|
5
|
+
module Farce
|
|
6
|
+
# A publisher subscription that can be paused or permanently canceled.
|
|
7
|
+
# Disabling skips new messages. Unsubscribing drains an existing callback queue before stopping it.
|
|
8
|
+
class Subscription
|
|
9
|
+
include Internal::Freeze::Unfreezable
|
|
10
|
+
include Internal::Noncopyable
|
|
11
|
+
|
|
12
|
+
# Raised when an operation is attempted on an unsubscribed subscription.
|
|
13
|
+
UnsubscribedError = Class.new(StandardError)
|
|
14
|
+
|
|
15
|
+
REGISTER = ClassMirror.new(self)
|
|
16
|
+
private_constant :REGISTER
|
|
17
|
+
|
|
18
|
+
class << self
|
|
19
|
+
protected :new
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# Register delivery behavior for subscriber classes from the main Ractor.
|
|
23
|
+
# Subclasses inherit the nearest registered delivery method.
|
|
24
|
+
#
|
|
25
|
+
# @example
|
|
26
|
+
# Farce::Subscription.define(MyInbox) { |payload| subscriber.push(payload) }
|
|
27
|
+
#
|
|
28
|
+
# @param classes [Array<Class>] the subscriber classes to support
|
|
29
|
+
# @yieldparam payload [BasicObject] the published payload
|
|
30
|
+
# @yieldreceiver [Subscription] the subscription whose {#subscriber} receives the payload
|
|
31
|
+
# @note The block must be convertible to a shareable proc.
|
|
32
|
+
# @return [void]
|
|
33
|
+
def self.define(*classes, &)
|
|
34
|
+
raise LocalJumpError, "no block given" unless block_given?
|
|
35
|
+
definition = Internal.prepare_method_definition(&)
|
|
36
|
+
classes.each do |klass|
|
|
37
|
+
REGISTER.define(klass) do
|
|
38
|
+
define_method(:deliver, definition)
|
|
39
|
+
private :deliver
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# @api private
|
|
45
|
+
def self.build(publisher, subscriber, ...)
|
|
46
|
+
handler = REGISTER[subscriber.class]
|
|
47
|
+
unless handler.private_method_defined?(:deliver)
|
|
48
|
+
raise ArgumentError, "Unsupported subscriber type: #{subscriber.class}"
|
|
49
|
+
end
|
|
50
|
+
handler.new(publisher, subscriber, ...)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# @return [BasicObject] the subscriber associated with this subscription
|
|
54
|
+
attr_reader :subscriber
|
|
55
|
+
|
|
56
|
+
# @return [Farce::Abstract::Publisher] the publisher associated with this subscription
|
|
57
|
+
attr_reader :publisher
|
|
58
|
+
|
|
59
|
+
# @api private
|
|
60
|
+
attr_reader :key
|
|
61
|
+
|
|
62
|
+
# @!visibility private
|
|
63
|
+
def initialize(publisher, subscriber, map, callback: false, source: nil)
|
|
64
|
+
# Keep Proc identities alive without making their captured state shareable.
|
|
65
|
+
@source = source && Envelope.new(source, mode: :local)
|
|
66
|
+
@callback = callback
|
|
67
|
+
@enabled = Flag.new(true)
|
|
68
|
+
@key = (source || subscriber).__id__
|
|
69
|
+
@map = map
|
|
70
|
+
@publisher = publisher
|
|
71
|
+
@subscribed = Flag.new(true)
|
|
72
|
+
@subscriber = subscriber
|
|
73
|
+
super()
|
|
74
|
+
__freeze__
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# @api private
|
|
78
|
+
def publish(payload)
|
|
79
|
+
return unless enabled?
|
|
80
|
+
deliver(payload)
|
|
81
|
+
true
|
|
82
|
+
rescue ::ClosedQueueError, Ractor::ClosedError, FrozenError
|
|
83
|
+
unsubscribe
|
|
84
|
+
false
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# @return [Boolean] whether the subscription is currently enabled
|
|
88
|
+
def enabled? = @subscribed.value && @enabled.value
|
|
89
|
+
|
|
90
|
+
# @return [Boolean] whether the subscription has not been permanently canceled
|
|
91
|
+
def subscribed? = @subscribed.value
|
|
92
|
+
|
|
93
|
+
# Enables the subscription, allowing it to receive published payloads.
|
|
94
|
+
# @raise [UnsubscribedError] if the subscription has been unsubscribed
|
|
95
|
+
# @return [void]
|
|
96
|
+
def enable
|
|
97
|
+
raise UnsubscribedError, "Cannot enable a subscription that has been unsubscribed" unless @subscribed.value
|
|
98
|
+
@enabled.value = true
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
# Disables the subscription, preventing it from receiving published payloads.
|
|
102
|
+
# @return [void]
|
|
103
|
+
def disable = @enabled.value = false
|
|
104
|
+
|
|
105
|
+
# Removes the subscription permanently, allowing it and the subscriber to be garbage collected.
|
|
106
|
+
# @return [BasicObject, nil] the subscriber, or nil if already unsubscribed
|
|
107
|
+
def unsubscribe
|
|
108
|
+
return unless @subscribed.compare_and_set(true, false)
|
|
109
|
+
@map.delete_pair(key, self)
|
|
110
|
+
subscriber.seal if @callback
|
|
111
|
+
subscriber
|
|
112
|
+
end
|
|
113
|
+
alias cancel unsubscribe
|
|
114
|
+
|
|
115
|
+
private
|
|
116
|
+
|
|
117
|
+
define_method(:__freeze__, Kernel.instance_method(:freeze))
|
|
118
|
+
|
|
119
|
+
require "farce/subscription/definitions"
|
|
120
|
+
end
|
|
121
|
+
end
|
|
@@ -16,8 +16,42 @@ module Farce
|
|
|
16
16
|
class Map < Abstract::ConcurrentMap
|
|
17
17
|
include MapOperations
|
|
18
18
|
|
|
19
|
+
# (see Abstract::ConcurrentMap#store_if_present)
|
|
20
|
+
# @param mode [Symbol, nil] The result's transfer mode, or nil to use the default.
|
|
21
|
+
def store_if_present(key, mode: nil)
|
|
22
|
+
require_block!(block_given?)
|
|
23
|
+
write do
|
|
24
|
+
canonical = prepare(key)
|
|
25
|
+
next unless @working.key?(canonical)
|
|
26
|
+
replacement = yield(unwrap_stored(@working[canonical]))
|
|
27
|
+
unwrap_stored(store_value(canonical, wrap(replacement, mode:)))
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# (see Abstract::ConcurrentMap#replace_if_exists)
|
|
32
|
+
# @param mode [Symbol, nil] The replacement's transfer mode, or nil to use the default.
|
|
33
|
+
def replace_if_exists(key, replacement, mode: nil)
|
|
34
|
+
write do
|
|
35
|
+
canonical = prepare(key)
|
|
36
|
+
next unless @working.key?(canonical)
|
|
37
|
+
previous = @working[canonical]
|
|
38
|
+
store_value(canonical, wrap(replacement, mode:))
|
|
39
|
+
unwrap_stored(previous)
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# (see Abstract::ConcurrentMap#delete_pair)
|
|
44
|
+
# A failed comparison invalidates the transaction attempt.
|
|
45
|
+
def delete_pair(key, expected)
|
|
46
|
+
removed = modify_entry(key) do |present, value|
|
|
47
|
+
present && values_equal?(value, expected) ? Internal::MAP_DELETE : Internal::MAP_KEEP
|
|
48
|
+
end
|
|
49
|
+
compared(removed)
|
|
50
|
+
end
|
|
51
|
+
|
|
19
52
|
Wrapper.inherit(self, *MapOperations::READ_HELPERS, :delete_if, :reject!, :compact!, :keep_if,
|
|
20
|
-
:select!, :filter!, :transform_values!, :merge
|
|
53
|
+
:select!, :filter!, :transform_values!, :merge!, :put, :get_and_set, :compute_if_absent,
|
|
54
|
+
:compute_if_present, :replace_pair, :compute, :merge_pair, :fetch_or_store, :put_if_absent)
|
|
21
55
|
|
|
22
56
|
private def enlist(backend) = @transaction.enlist(backend, per_key: true)
|
|
23
57
|
end
|
data/lib/farce/unshared/pool.rb
CHANGED
|
@@ -36,7 +36,7 @@ module Farce
|
|
|
36
36
|
Internal::LocalScheduledTask.new(args, block)
|
|
37
37
|
end
|
|
38
38
|
|
|
39
|
-
def schedule_scaler(started, delay =
|
|
39
|
+
def schedule_scaler(started, delay = settings.grow_after)
|
|
40
40
|
Internal::ThreadPoolSupervisor.current.schedule(self, started, delay)
|
|
41
41
|
end
|
|
42
42
|
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# shareable_constant_value: literal
|
|
3
|
+
# warn_indent: true
|
|
4
|
+
|
|
5
|
+
module Farce
|
|
6
|
+
module Unshared
|
|
7
|
+
# Unshared variant of {Farce::Publisher}.
|
|
8
|
+
class Publisher < Abstract::Publisher
|
|
9
|
+
include Unshareable
|
|
10
|
+
|
|
11
|
+
# @overload initialize(*subscribers)
|
|
12
|
+
# (see Farce::Abstract#initialize)
|
|
13
|
+
def initialize(...)
|
|
14
|
+
@map = Unshared::Map.new
|
|
15
|
+
super
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
private
|
|
19
|
+
|
|
20
|
+
attr_reader :map
|
|
21
|
+
|
|
22
|
+
def default_mode = :local
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -20,12 +20,12 @@ module Farce
|
|
|
20
20
|
# results.pop # => :done
|
|
21
21
|
# scheduler.close
|
|
22
22
|
#
|
|
23
|
-
# @!method schedule(*args, mode: :smart, auto_local: true)
|
|
23
|
+
# @!method schedule(*args, mode: :smart, auto_local: true, timeout: nil)
|
|
24
24
|
# Enqueue a task while retaining its block and argument references.
|
|
25
25
|
# `mode` is ignored when `auto_local` is true. Otherwise, only local and
|
|
26
26
|
# smart modes are accepted.
|
|
27
27
|
# @param (see Farce::Abstract::Executor#schedule)
|
|
28
|
-
# @return [self]
|
|
28
|
+
# @return [self, nil] self when submitted, or nil when the scheduling timeout expires.
|
|
29
29
|
class Scheduler < Abstract::Scheduler
|
|
30
30
|
include Unshareable
|
|
31
31
|
include Internal::LocalExecutorMethods
|
|
@@ -80,7 +80,7 @@ module Farce
|
|
|
80
80
|
def new_queue(capacity:) = Internal::UnsharedQueue.new(capacity:)
|
|
81
81
|
def launch_callback = ->(_scheduler, priority, name) { launch!(name, priority) }
|
|
82
82
|
|
|
83
|
-
def prepare_task(args, block, mode, auto_local)
|
|
83
|
+
def prepare_task(args, block, mode, auto_local, **)
|
|
84
84
|
validate_local_mode(mode, auto_local)
|
|
85
85
|
Internal::LocalScheduledTask.new(args, block)
|
|
86
86
|
end
|
|
@@ -9,10 +9,11 @@ module Farce
|
|
|
9
9
|
include Unshareable
|
|
10
10
|
include Internal::Freeze::Delegated
|
|
11
11
|
|
|
12
|
-
# @param source [Array, nil] Initial values. Values are retained directly.
|
|
12
|
+
# @param source [Array, Integer, nil] Initial values or a size filled with nil. Values are retained directly.
|
|
13
13
|
# @param compare_by_identity [Boolean] Whether values are compared by identity.
|
|
14
|
+
# @raise [ArgumentError] If the size is negative.
|
|
14
15
|
def initialize(source = nil, compare_by_identity: false)
|
|
15
|
-
@vector = Internal::UnsharedVector.new(source, compare_by_identity:)
|
|
16
|
+
@vector = Internal::UnsharedVector.new(normalize_vector_source(source), compare_by_identity:)
|
|
16
17
|
super()
|
|
17
18
|
end
|
|
18
19
|
|
data/lib/farce/vector.rb
CHANGED
|
@@ -20,10 +20,12 @@ module Farce
|
|
|
20
20
|
include Shareable::Delegated
|
|
21
21
|
|
|
22
22
|
# @!macro modes
|
|
23
|
-
# @param source [Array, nil] Initial values. The source array is not retained.
|
|
23
|
+
# @param source [Array, Integer, nil] Initial values or a size filled with nil. The source array is not retained.
|
|
24
24
|
# @param mode [Symbol] The default value transfer mode.
|
|
25
25
|
# @param compare_by_identity [Boolean] Whether values are compared by identity.
|
|
26
|
+
# @raise [ArgumentError] If the size is negative.
|
|
26
27
|
def initialize(source = nil, mode: :smart, compare_by_identity: false)
|
|
28
|
+
source = normalize_vector_source(source)
|
|
27
29
|
raise TypeError, "source must be an Array" unless source.nil? || source.is_a?(Array)
|
|
28
30
|
unless true.equal?(compare_by_identity) || false.equal?(compare_by_identity)
|
|
29
31
|
raise ArgumentError, "compare_by_identity must be true or false"
|
|
@@ -176,11 +178,18 @@ module Farce
|
|
|
176
178
|
replacement = @manager.wrap(replacement, mode:)
|
|
177
179
|
wrapped = true
|
|
178
180
|
end
|
|
179
|
-
return true if @vector.compare_and_set(index, current, replacement, timeout:
|
|
181
|
+
return true if @vector.compare_and_set(index, current, replacement, timeout: Clock.remaining(deadline))
|
|
180
182
|
return false if nil.equal?(current) || (deadline && Clock.now >= deadline)
|
|
181
183
|
end
|
|
182
184
|
end
|
|
183
185
|
|
|
186
|
+
# (see Farce::Abstract::Vector#wait_until)
|
|
187
|
+
def wait_until(index, timeout: nil)
|
|
188
|
+
raise LocalJumpError, "no block given" unless block_given?
|
|
189
|
+
result = Internal.wait_until(@vector, index, timeout:) { |current| yield(@manager.unwrap(current)) }
|
|
190
|
+
@manager.unwrap(result)
|
|
191
|
+
end
|
|
192
|
+
|
|
184
193
|
# (see Farce::Abstract::Vector#wait_until_changed)
|
|
185
194
|
def wait_until_changed(index, expected, timeout: nil)
|
|
186
195
|
deadline = timeout_deadline(timeout)
|
|
@@ -189,7 +198,7 @@ module Farce
|
|
|
189
198
|
current = @vector[index]
|
|
190
199
|
return @manager.unwrap(current) unless values_equal?(current, expected)
|
|
191
200
|
return if deadline && Clock.now >= deadline
|
|
192
|
-
@vector.wait_until_changed(index, current, timeout:
|
|
201
|
+
@vector.wait_until_changed(index, current, timeout: Clock.remaining(deadline))
|
|
193
202
|
end
|
|
194
203
|
end
|
|
195
204
|
|
|
@@ -243,11 +252,5 @@ module Farce
|
|
|
243
252
|
raise ArgumentError, "timeout must be finite and non-negative" unless timeout.finite? && !timeout.negative?
|
|
244
253
|
Clock.now + timeout
|
|
245
254
|
end
|
|
246
|
-
|
|
247
|
-
def remaining_timeout(deadline)
|
|
248
|
-
return unless deadline
|
|
249
|
-
remaining = deadline - Clock.now
|
|
250
|
-
remaining.negative? ? 0 : remaining
|
|
251
|
-
end
|
|
252
255
|
end
|
|
253
256
|
end
|
data/lib/farce/version.rb
CHANGED
|
@@ -118,8 +118,19 @@ module Farce
|
|
|
118
118
|
if !key.equal?(new_key)
|
|
119
119
|
entries << [key, new_key, result]
|
|
120
120
|
elsif !value.equal?(result)
|
|
121
|
-
|
|
122
|
-
|
|
121
|
+
loop do
|
|
122
|
+
retry_update = false
|
|
123
|
+
send_to(target, :modify_entry, key, canonical: true) do |present, current|
|
|
124
|
+
next Internal::MAP_KEEP unless present
|
|
125
|
+
if Internal.value_changed?(current, value, true)
|
|
126
|
+
value = current
|
|
127
|
+
retry_update = true
|
|
128
|
+
next Internal::MAP_KEEP
|
|
129
|
+
end
|
|
130
|
+
result
|
|
131
|
+
end
|
|
132
|
+
break unless retry_update
|
|
133
|
+
# Traverse replacements after releasing the key's update reservation.
|
|
123
134
|
result = walker.visit(value)
|
|
124
135
|
end
|
|
125
136
|
end
|
data/lib/farce/weak_atom.rb
CHANGED
|
@@ -100,14 +100,17 @@ module Farce
|
|
|
100
100
|
matched
|
|
101
101
|
end
|
|
102
102
|
|
|
103
|
+
# (see Abstract::Atom#wait_until)
|
|
104
|
+
def wait_until(timeout: nil, &) = Internal.wait_until(@atom, timeout:, &)
|
|
105
|
+
|
|
103
106
|
# (see Abstract::Atom#wait_until_changed)
|
|
104
107
|
def wait_until_changed(expected, timeout: nil, &fallback)
|
|
105
108
|
deadline = Internal.timeout_deadline(timeout)
|
|
106
109
|
while true
|
|
107
|
-
current = @atom.get(timeout:
|
|
110
|
+
current = @atom.get(timeout: Clock.remaining(deadline)) { TIMED_OUT }
|
|
108
111
|
return fallback&.call if TIMED_OUT.equal?(current)
|
|
109
112
|
return current unless values_equal?(current, expected)
|
|
110
|
-
result = @atom.wait_until_changed(current, timeout:
|
|
113
|
+
result = @atom.wait_until_changed(current, timeout: Clock.remaining(deadline)) { TIMED_OUT }
|
|
111
114
|
return fallback&.call if TIMED_OUT.equal?(result)
|
|
112
115
|
end
|
|
113
116
|
end
|
data/lib/farce.rb
CHANGED
|
@@ -429,7 +429,9 @@ module Farce
|
|
|
429
429
|
end
|
|
430
430
|
|
|
431
431
|
# Schedules work without waiting for the block to finish.
|
|
432
|
-
# On CRuby, uses a shared Ractor pool with at most {System.cpu_count} workers.
|
|
432
|
+
# On CRuby, uses a shared Ractor pool with at most {System.cpu_count} workers by default.
|
|
433
|
+
# Set {Config}'s `parallel_pool_*` options before creating a scheduler to configure
|
|
434
|
+
# its worker limits, growth and shrink delays, per-worker task limit, and queue capacity.
|
|
433
435
|
# On JRuby and TruffleRuby, starts a new thread for each task.
|
|
434
436
|
#
|
|
435
437
|
# Without a block, returns the shared scheduler. The CRuby pool starts workers
|
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.
|
|
4
|
+
version: 0.5.0
|
|
5
5
|
platform: x86_64-linux-gnu
|
|
6
6
|
authors:
|
|
7
7
|
- Konstantin Haase
|
|
@@ -112,6 +112,7 @@ files:
|
|
|
112
112
|
- lib/farce/abstract/pool.rb
|
|
113
113
|
- lib/farce/abstract/port.rb
|
|
114
114
|
- lib/farce/abstract/priority_queue.rb
|
|
115
|
+
- lib/farce/abstract/publisher.rb
|
|
115
116
|
- lib/farce/abstract/queue.rb
|
|
116
117
|
- lib/farce/abstract/scheduler.rb
|
|
117
118
|
- lib/farce/abstract/set.rb
|
|
@@ -129,6 +130,14 @@ files:
|
|
|
129
130
|
- lib/farce/backports.rb
|
|
130
131
|
- lib/farce/class_mirror.rb
|
|
131
132
|
- lib/farce/clock.rb
|
|
133
|
+
- lib/farce/concurrent.rb
|
|
134
|
+
- lib/farce/concurrent/count_down_latch.rb
|
|
135
|
+
- lib/farce/concurrent/cyclic_barrier.rb
|
|
136
|
+
- lib/farce/concurrent/event.rb
|
|
137
|
+
- lib/farce/concurrent/fiber_local_var.rb
|
|
138
|
+
- lib/farce/concurrent/local_var.rb
|
|
139
|
+
- lib/farce/concurrent/thread_local_var.rb
|
|
140
|
+
- lib/farce/concurrent/tuple.rb
|
|
132
141
|
- lib/farce/config.rb
|
|
133
142
|
- lib/farce/counter.rb
|
|
134
143
|
- lib/farce/deduper.rb
|
|
@@ -300,6 +309,7 @@ files:
|
|
|
300
309
|
- lib/farce/internal/mutable_ordered_key_lock_map.rb
|
|
301
310
|
- lib/farce/internal/noncopyable.rb
|
|
302
311
|
- lib/farce/internal/ordered_key_lock_map.rb
|
|
312
|
+
- lib/farce/internal/pool_settings.rb
|
|
303
313
|
- lib/farce/internal/pool_supervisor.rb
|
|
304
314
|
- lib/farce/internal/pool_worker.rb
|
|
305
315
|
- lib/farce/internal/portable_transaction.rb
|
|
@@ -351,6 +361,7 @@ files:
|
|
|
351
361
|
- lib/farce/local/molecule.rb
|
|
352
362
|
- lib/farce/local/pool.rb
|
|
353
363
|
- lib/farce/local/priority_queue.rb
|
|
364
|
+
- lib/farce/local/publisher.rb
|
|
354
365
|
- lib/farce/local/queue.rb
|
|
355
366
|
- lib/farce/local/scheduler.rb
|
|
356
367
|
- lib/farce/local/scoped.rb
|
|
@@ -367,6 +378,7 @@ files:
|
|
|
367
378
|
- lib/farce/lock.rb
|
|
368
379
|
- lib/farce/lru_map.rb
|
|
369
380
|
- lib/farce/map.rb
|
|
381
|
+
- lib/farce/memoize.rb
|
|
370
382
|
- lib/farce/mode_manager.rb
|
|
371
383
|
- lib/farce/mode_manager/smart_rule.rb
|
|
372
384
|
- lib/farce/molecule.rb
|
|
@@ -379,6 +391,7 @@ files:
|
|
|
379
391
|
- lib/farce/proxy/register.rb
|
|
380
392
|
- lib/farce/proxy/supervisor.rb
|
|
381
393
|
- lib/farce/proxy/wrapper.rb
|
|
394
|
+
- lib/farce/publisher.rb
|
|
382
395
|
- lib/farce/queue.rb
|
|
383
396
|
- lib/farce/ractor.rb
|
|
384
397
|
- lib/farce/read_write_lock.rb
|
|
@@ -406,6 +419,7 @@ files:
|
|
|
406
419
|
- lib/farce/strict/molecule.rb
|
|
407
420
|
- lib/farce/strict/port.rb
|
|
408
421
|
- lib/farce/strict/priority_queue.rb
|
|
422
|
+
- lib/farce/strict/publisher.rb
|
|
409
423
|
- lib/farce/strict/queue.rb
|
|
410
424
|
- lib/farce/strict/set.rb
|
|
411
425
|
- lib/farce/strict/sorted_set.rb
|
|
@@ -417,6 +431,8 @@ files:
|
|
|
417
431
|
- lib/farce/strict/weak_map.rb
|
|
418
432
|
- lib/farce/strict/weak_set.rb
|
|
419
433
|
- lib/farce/strict/weak_value_map.rb
|
|
434
|
+
- lib/farce/subscription.rb
|
|
435
|
+
- lib/farce/subscription/definitions.rb
|
|
420
436
|
- lib/farce/system.rb
|
|
421
437
|
- lib/farce/task.rb
|
|
422
438
|
- lib/farce/thread_executor.rb
|
|
@@ -454,6 +470,7 @@ files:
|
|
|
454
470
|
- lib/farce/unshared/molecule.rb
|
|
455
471
|
- lib/farce/unshared/pool.rb
|
|
456
472
|
- lib/farce/unshared/priority_queue.rb
|
|
473
|
+
- lib/farce/unshared/publisher.rb
|
|
457
474
|
- lib/farce/unshared/queue.rb
|
|
458
475
|
- lib/farce/unshared/scheduler.rb
|
|
459
476
|
- lib/farce/unshared/set.rb
|