farce 0.4.1 → 0.5.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.
- 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/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: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Konstantin Haase
|
|
@@ -114,6 +114,7 @@ files:
|
|
|
114
114
|
- lib/farce/abstract/pool.rb
|
|
115
115
|
- lib/farce/abstract/port.rb
|
|
116
116
|
- lib/farce/abstract/priority_queue.rb
|
|
117
|
+
- lib/farce/abstract/publisher.rb
|
|
117
118
|
- lib/farce/abstract/queue.rb
|
|
118
119
|
- lib/farce/abstract/scheduler.rb
|
|
119
120
|
- lib/farce/abstract/set.rb
|
|
@@ -131,6 +132,14 @@ files:
|
|
|
131
132
|
- lib/farce/backports.rb
|
|
132
133
|
- lib/farce/class_mirror.rb
|
|
133
134
|
- lib/farce/clock.rb
|
|
135
|
+
- lib/farce/concurrent.rb
|
|
136
|
+
- lib/farce/concurrent/count_down_latch.rb
|
|
137
|
+
- lib/farce/concurrent/cyclic_barrier.rb
|
|
138
|
+
- lib/farce/concurrent/event.rb
|
|
139
|
+
- lib/farce/concurrent/fiber_local_var.rb
|
|
140
|
+
- lib/farce/concurrent/local_var.rb
|
|
141
|
+
- lib/farce/concurrent/thread_local_var.rb
|
|
142
|
+
- lib/farce/concurrent/tuple.rb
|
|
134
143
|
- lib/farce/config.rb
|
|
135
144
|
- lib/farce/counter.rb
|
|
136
145
|
- lib/farce/deduper.rb
|
|
@@ -298,6 +307,7 @@ files:
|
|
|
298
307
|
- lib/farce/internal/mutable_ordered_key_lock_map.rb
|
|
299
308
|
- lib/farce/internal/noncopyable.rb
|
|
300
309
|
- lib/farce/internal/ordered_key_lock_map.rb
|
|
310
|
+
- lib/farce/internal/pool_settings.rb
|
|
301
311
|
- lib/farce/internal/pool_supervisor.rb
|
|
302
312
|
- lib/farce/internal/pool_worker.rb
|
|
303
313
|
- lib/farce/internal/portable_transaction.rb
|
|
@@ -349,6 +359,7 @@ files:
|
|
|
349
359
|
- lib/farce/local/molecule.rb
|
|
350
360
|
- lib/farce/local/pool.rb
|
|
351
361
|
- lib/farce/local/priority_queue.rb
|
|
362
|
+
- lib/farce/local/publisher.rb
|
|
352
363
|
- lib/farce/local/queue.rb
|
|
353
364
|
- lib/farce/local/scheduler.rb
|
|
354
365
|
- lib/farce/local/scoped.rb
|
|
@@ -365,6 +376,7 @@ files:
|
|
|
365
376
|
- lib/farce/lock.rb
|
|
366
377
|
- lib/farce/lru_map.rb
|
|
367
378
|
- lib/farce/map.rb
|
|
379
|
+
- lib/farce/memoize.rb
|
|
368
380
|
- lib/farce/mode_manager.rb
|
|
369
381
|
- lib/farce/mode_manager/smart_rule.rb
|
|
370
382
|
- lib/farce/molecule.rb
|
|
@@ -377,6 +389,7 @@ files:
|
|
|
377
389
|
- lib/farce/proxy/register.rb
|
|
378
390
|
- lib/farce/proxy/supervisor.rb
|
|
379
391
|
- lib/farce/proxy/wrapper.rb
|
|
392
|
+
- lib/farce/publisher.rb
|
|
380
393
|
- lib/farce/queue.rb
|
|
381
394
|
- lib/farce/ractor.rb
|
|
382
395
|
- lib/farce/read_write_lock.rb
|
|
@@ -404,6 +417,7 @@ files:
|
|
|
404
417
|
- lib/farce/strict/molecule.rb
|
|
405
418
|
- lib/farce/strict/port.rb
|
|
406
419
|
- lib/farce/strict/priority_queue.rb
|
|
420
|
+
- lib/farce/strict/publisher.rb
|
|
407
421
|
- lib/farce/strict/queue.rb
|
|
408
422
|
- lib/farce/strict/set.rb
|
|
409
423
|
- lib/farce/strict/sorted_set.rb
|
|
@@ -415,6 +429,8 @@ files:
|
|
|
415
429
|
- lib/farce/strict/weak_map.rb
|
|
416
430
|
- lib/farce/strict/weak_set.rb
|
|
417
431
|
- lib/farce/strict/weak_value_map.rb
|
|
432
|
+
- lib/farce/subscription.rb
|
|
433
|
+
- lib/farce/subscription/definitions.rb
|
|
418
434
|
- lib/farce/system.rb
|
|
419
435
|
- lib/farce/task.rb
|
|
420
436
|
- lib/farce/thread_executor.rb
|
|
@@ -452,6 +468,7 @@ files:
|
|
|
452
468
|
- lib/farce/unshared/molecule.rb
|
|
453
469
|
- lib/farce/unshared/pool.rb
|
|
454
470
|
- lib/farce/unshared/priority_queue.rb
|
|
471
|
+
- lib/farce/unshared/publisher.rb
|
|
455
472
|
- lib/farce/unshared/queue.rb
|
|
456
473
|
- lib/farce/unshared/scheduler.rb
|
|
457
474
|
- lib/farce/unshared/set.rb
|