farce 0.1.0-aarch64-linux-gnu → 0.2.0-aarch64-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 -1
- data/README.md +72 -16
- data/docs/gems/dry-types.md +8 -8
- data/docs/modes.md +67 -10
- data/ext/farce/atom.c +37 -22
- data/ext/farce/bounded_map.c +17 -13
- data/ext/farce/containers.h +15 -0
- data/ext/farce/counter.c +13 -18
- data/ext/farce/exchanger.c +5 -1
- data/ext/farce/farce.c +1 -1
- data/ext/farce/flag.c +39 -3
- data/ext/farce/io.c +3 -3
- data/ext/farce/lock.c +17 -23
- data/ext/farce/map.c +88 -66
- data/ext/farce/priority_queue.c +55 -43
- data/ext/farce/queue.c +10 -4
- data/ext/farce/reactor.c +52 -25
- data/ext/farce/reactor.h +1 -1
- data/ext/farce/signal.c +5 -1
- data/ext/farce/transaction.c +20 -24
- data/ext/farce/tree_map.c +33 -18
- data/ext/farce/trie.c +81 -43
- data/ext/farce/unshareable.c +168 -19
- data/ext/farce/unshared_io_pool.h +21 -12
- data/ext/farce/unshared_signal.c +13 -2
- data/ext/farce/unshared_wait.h +3 -0
- data/ext/farce/vector.c +82 -18
- data/ext/farce/weak_map.c +75 -63
- data/lib/farce/_yard/macros.rb +18 -3
- data/lib/farce/abstract/flag.rb +73 -0
- data/lib/farce/abstract/queue.rb +4 -3
- data/lib/farce/abstract/scheduler.rb +3 -3
- data/lib/farce/abstract/set.rb +17 -5
- data/lib/farce/abstract/sorted_set.rb +2 -2
- data/lib/farce/abstract/timer_queue.rb +49 -10
- data/lib/farce/abstract/vector.rb +510 -28
- data/lib/farce/atom.rb +1 -1
- data/lib/farce/class_mirror.rb +20 -7
- data/lib/farce/config.rb +7 -1
- data/lib/farce/engine/jvm/flag.rb +15 -3
- 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/require_hook.rb +18 -0
- data/lib/farce/engine/ruby.rb +7 -0
- data/lib/farce/engine/shared/unshared_vector.rb +31 -4
- data/lib/farce/engine/shared/vector.rb +8 -0
- data/lib/farce/engine/shared/weak_map/index.rb +14 -1
- data/lib/farce/engine/truffleruby/native/flag.rb +15 -3
- data/lib/farce/envelope.rb +54 -7
- data/lib/farce/exchanger.rb +1 -1
- data/lib/farce/flag.rb +2 -0
- data/lib/farce/integrations/active_support.rb +1 -1
- data/lib/farce/integrations/bson.rb +1 -1
- data/lib/farce/integrations/cbor.rb +1 -1
- data/lib/farce/integrations/concurrent.rb +3 -3
- data/lib/farce/integrations/date.rb +8 -0
- data/lib/farce/integrations/dry_types.rb +7 -4
- data/lib/farce/integrations/json.rb +2 -2
- data/lib/farce/integrations/msgpack.rb +1 -1
- data/lib/farce/integrations/oj.rb +3 -4
- data/lib/farce/integrations/psych.rb +1 -1
- data/lib/farce/integrations/ractor_sharing.rb +1 -1
- data/lib/farce/integrations/sorted_set.rb +1 -1
- data/lib/farce/integrations/weakref.rb +9 -1
- data/lib/farce/integrations/yajl.rb +3 -3
- data/lib/farce/integrations.rb +33 -23
- data/lib/farce/internal/flag_waiting.rb +30 -0
- data/lib/farce/internal/freeze.rb +8 -3
- data/lib/farce/internal/map_value_modes.rb +1 -1
- data/lib/farce/internal/scheduler_lifecycle.rb +1 -1
- data/lib/farce/lazy.rb +4 -4
- data/lib/farce/lazy_ref.rb +3 -3
- data/lib/farce/lfu_map.rb +1 -1
- data/lib/farce/local/flag.rb +4 -0
- data/lib/farce/local/scoped.rb +1 -1
- data/lib/farce/lru_map.rb +1 -1
- data/lib/farce/map.rb +2 -2
- data/lib/farce/mode_manager/smart_rule.rb +138 -0
- data/lib/farce/mode_manager.rb +12 -3
- data/lib/farce/molecule.rb +2 -2
- data/lib/farce/pool.rb +1 -1
- data/lib/farce/port.rb +4 -4
- data/lib/farce/priority_queue.rb +1 -1
- data/lib/farce/proxy/register.rb +6 -0
- data/lib/farce/proxy/supervisor.rb +1 -1
- data/lib/farce/proxy.rb +7 -2
- data/lib/farce/queue.rb +1 -1
- data/lib/farce/scheduler.rb +2 -2
- data/lib/farce/set.rb +1 -1
- data/lib/farce/sorted_set.rb +1 -1
- data/lib/farce/timer_queue.rb +20 -6
- data/lib/farce/transaction/vector.rb +43 -9
- data/lib/farce/transaction/wrapper.rb +14 -0
- data/lib/farce/tree_map.rb +1 -1
- data/lib/farce/unsafe/lfu_map.rb +2 -0
- data/lib/farce/unsafe/lru_map.rb +2 -0
- data/lib/farce/unsafe/tree_map.rb +2 -0
- data/lib/farce/unshareable.rb +8 -19
- data/lib/farce/unshared/atom.rb +3 -0
- data/lib/farce/unshared/lfu_map.rb +2 -0
- data/lib/farce/unshared/lru_map.rb +2 -0
- data/lib/farce/unshared/map.rb +3 -0
- data/lib/farce/unshared/set.rb +1 -0
- data/lib/farce/unshared/sorted_set.rb +1 -0
- data/lib/farce/unshared/tree_map.rb +5 -1
- data/lib/farce/unshared/vector.rb +3 -0
- data/lib/farce/unshared/weak_atom.rb +5 -1
- data/lib/farce/unshared/weak_key_map.rb +3 -0
- data/lib/farce/unshared/weak_map.rb +3 -0
- data/lib/farce/unshared/weak_set.rb +1 -0
- data/lib/farce/unshared/weak_value_map.rb +3 -0
- data/lib/farce/vector.rb +44 -19
- data/lib/farce/version.rb +1 -1
- data/lib/farce.rb +11 -8
- metadata +5 -1
data/lib/farce/timer_queue.rb
CHANGED
|
@@ -4,14 +4,14 @@
|
|
|
4
4
|
|
|
5
5
|
module Farce
|
|
6
6
|
# A shareable timer queue with transfer modes for unshareable values.
|
|
7
|
-
class TimerQueue < Abstract::TimerQueue
|
|
7
|
+
class TimerQueue < Farce::Abstract::TimerQueue
|
|
8
8
|
include Internal::ManagedQueue
|
|
9
9
|
include Shareable::Unfreezable
|
|
10
10
|
|
|
11
11
|
# (see Farce::Abstract::TimerQueue#initialize)
|
|
12
12
|
# @!macro modes
|
|
13
13
|
# @param mode [Symbol] the transfer mode for unshareable values
|
|
14
|
-
def initialize(capacity: nil, mode: :
|
|
14
|
+
def initialize(capacity: nil, mode: :smart, track_age: false)
|
|
15
15
|
@manager = ModeManager.new(mode:)
|
|
16
16
|
super(capacity:, track_age:)
|
|
17
17
|
end
|
|
@@ -20,16 +20,20 @@ module Farce
|
|
|
20
20
|
# @!macro modes
|
|
21
21
|
# @param mode [Symbol] the transfer mode for unshareable values
|
|
22
22
|
def push(value, non_block = false, mode: nil, timeout: nil, **time_options) # rubocop:disable Style/OptionalBooleanParameter
|
|
23
|
-
at
|
|
24
|
-
|
|
23
|
+
at = Clock.parse(time_options)
|
|
24
|
+
value = @manager.wrap(value, mode:)
|
|
25
|
+
entry = Entry.new(value, at, self)
|
|
26
|
+
push_to_storage(at, non_block, value, timeout:) && entry
|
|
25
27
|
end
|
|
26
28
|
|
|
27
29
|
# (see Farce::Abstract::TimerQueue#try_push)
|
|
28
30
|
# @!macro modes
|
|
29
31
|
# @param mode [Symbol] the transfer mode for unshareable values
|
|
30
32
|
def try_push(value, mode: nil, **time_options)
|
|
31
|
-
at
|
|
32
|
-
|
|
33
|
+
at = Clock.parse(time_options)
|
|
34
|
+
value = @manager.wrap(value, mode:)
|
|
35
|
+
entry = Entry.new(value, at, self)
|
|
36
|
+
return entry if @queue.push(at, value)
|
|
33
37
|
block_given? ? yield : false
|
|
34
38
|
end
|
|
35
39
|
|
|
@@ -62,5 +66,15 @@ module Farce
|
|
|
62
66
|
return @manager.unwrap(value) unless empty
|
|
63
67
|
yield if block_given?
|
|
64
68
|
end
|
|
69
|
+
|
|
70
|
+
private
|
|
71
|
+
|
|
72
|
+
def delete_from_storage(priority, value, compare_by_identity:)
|
|
73
|
+
if compare_by_identity && @manager.managed_envelope?(value)
|
|
74
|
+
@queue.delete_identity(priority, value)
|
|
75
|
+
else
|
|
76
|
+
super
|
|
77
|
+
end
|
|
78
|
+
end
|
|
65
79
|
end
|
|
66
80
|
end
|
|
@@ -18,7 +18,12 @@ module Farce
|
|
|
18
18
|
|
|
19
19
|
# (see Abstract::Vector#[])
|
|
20
20
|
# @see Abstract::Vector#[]
|
|
21
|
-
def [](index
|
|
21
|
+
def [](index, length = UNDEFINED)
|
|
22
|
+
access do
|
|
23
|
+
next unwrap_stored(@working[index]) if length.equal?(UNDEFINED) && !index.is_a?(Range)
|
|
24
|
+
slice(index, length)
|
|
25
|
+
end
|
|
26
|
+
end
|
|
22
27
|
|
|
23
28
|
# (see Abstract::Vector#get)
|
|
24
29
|
# @see Abstract::Vector#get
|
|
@@ -34,8 +39,12 @@ module Farce
|
|
|
34
39
|
|
|
35
40
|
# (see Abstract::Vector#[]=)
|
|
36
41
|
# @see Abstract::Vector#[]=
|
|
37
|
-
def []=(index, value)
|
|
38
|
-
|
|
42
|
+
def []=(index, value, replacement = UNDEFINED)
|
|
43
|
+
if replacement.equal?(UNDEFINED) && !index.is_a?(Range)
|
|
44
|
+
store(index, value)
|
|
45
|
+
else
|
|
46
|
+
write { assign_vector_slice(index, value, replacement) }
|
|
47
|
+
end
|
|
39
48
|
end
|
|
40
49
|
|
|
41
50
|
# (see Abstract::Vector#store)
|
|
@@ -52,15 +61,24 @@ module Farce
|
|
|
52
61
|
|
|
53
62
|
# (see Abstract::Vector#push)
|
|
54
63
|
# @see Abstract::Vector#push
|
|
55
|
-
def push(value, mode: nil)
|
|
56
|
-
write
|
|
64
|
+
def push(value = UNDEFINED, *rest, mode: nil)
|
|
65
|
+
write do
|
|
66
|
+
if rest.empty? && !value.equal?(UNDEFINED)
|
|
67
|
+
@working.push(wrap(value, mode:))
|
|
68
|
+
else
|
|
69
|
+
push_values(value.equal?(UNDEFINED) ? rest : [value, *rest], mode:)
|
|
70
|
+
end
|
|
71
|
+
end
|
|
57
72
|
self
|
|
58
73
|
end
|
|
59
|
-
|
|
74
|
+
|
|
75
|
+
def <<(value) = push(value)
|
|
60
76
|
|
|
61
77
|
# (see Abstract::Vector#pop)
|
|
62
78
|
# @see Abstract::Vector#pop
|
|
63
|
-
def pop =
|
|
79
|
+
def pop(count = UNDEFINED)
|
|
80
|
+
write { count.equal?(UNDEFINED) ? unwrap_stored(@working.pop) : pop_values(count) }
|
|
81
|
+
end
|
|
64
82
|
|
|
65
83
|
# (see Abstract::Vector#clear)
|
|
66
84
|
# @see Abstract::Vector#clear
|
|
@@ -120,6 +138,17 @@ module Farce
|
|
|
120
138
|
def internal_vector = @working
|
|
121
139
|
def logical_value(value) = unwrap_stored(value)
|
|
122
140
|
|
|
141
|
+
def deletion_comparison(value)
|
|
142
|
+
return value unless @manager
|
|
143
|
+
@manager.wrap(value, mode: compare_by_identity? ? :local : :copy)
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
def deletion_match?(stored, expected)
|
|
147
|
+
return super unless @manager
|
|
148
|
+
unwrap_stored(stored)
|
|
149
|
+
@manager.same_value?(stored, expected, identity: compare_by_identity?)
|
|
150
|
+
end
|
|
151
|
+
|
|
123
152
|
# Slice helpers return independent data rather than another participant.
|
|
124
153
|
# Use an ordinary vector that remains usable after the attempt ends.
|
|
125
154
|
def build_derived_vector(values)
|
|
@@ -130,10 +159,15 @@ module Farce
|
|
|
130
159
|
|
|
131
160
|
def inspect_value(inspector, value) = super(inspector, value, @manager)
|
|
132
161
|
def derived_storage(value) = wrap(value)
|
|
162
|
+
def prepare_vector_value(value, mode: nil) = wrap(value, mode:)
|
|
133
163
|
|
|
134
|
-
Wrapper.inherit(self, :each_index, :reverse_each, :to_a, :deconstruct, :to_h, :join, :dig, :assoc,
|
|
164
|
+
Wrapper.inherit(self, :each_index, :reverse_each, :to_a, :to_ary, :deconstruct, :to_h, :join, :dig, :assoc,
|
|
135
165
|
:rassoc, :at, :fetch, :fetch_values, :values_at, :slice, :rfind, :bsearch, :bsearch_index, :pack,
|
|
136
|
-
:length, :count, :compare_by_identity?, :shareable_values
|
|
166
|
+
:length, :count, :compare_by_identity?, :shareable_values?, :flatten, :transpose, :product,
|
|
167
|
+
:combination, :repeated_combination, :permutation, :repeated_permutation, :<=>,
|
|
168
|
+
writes: %i[delete delete_if map! collect! keep_if select! filter! reject! compact! uniq!
|
|
169
|
+
reverse! rotate! shuffle! sort! sort_by! flatten! shift unshift prepend insert delete_at slice!
|
|
170
|
+
replace fill append])
|
|
137
171
|
end
|
|
138
172
|
end
|
|
139
173
|
end
|
|
@@ -15,6 +15,20 @@ module Farce
|
|
|
15
15
|
module Wrapper
|
|
16
16
|
include Internal::MarshalSupport::Reject
|
|
17
17
|
|
|
18
|
+
# Freezing an attempt view must not freeze its staged data or participant.
|
|
19
|
+
module Unfreezable
|
|
20
|
+
def append_features(mod)
|
|
21
|
+
super
|
|
22
|
+
if mod.is_a?(Class)
|
|
23
|
+
mod.class_eval { undef freeze if method_defined?(:freeze) }
|
|
24
|
+
else
|
|
25
|
+
mod.extend(Unfreezable)
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
private_constant :Unfreezable
|
|
30
|
+
extend Unfreezable
|
|
31
|
+
|
|
18
32
|
# Wrappers inherit abstract container classes for their interfaces, but
|
|
19
33
|
# must not initialize another ordinary container. This constructor ends
|
|
20
34
|
# Unshareable's super chain after binding the existing transaction snapshot.
|
data/lib/farce/tree_map.rb
CHANGED
|
@@ -20,7 +20,7 @@ module Farce
|
|
|
20
20
|
# @param entries [Hash, Array<Array(BasicObject, BasicObject)>, Abstract::Map, #each, nil]
|
|
21
21
|
# Optional initial entries for the map.
|
|
22
22
|
# @param mode [Symbol] The default value transfer mode.
|
|
23
|
-
def initialize(entries = nil, mode: :
|
|
23
|
+
def initialize(entries = nil, mode: :smart, normalize_keys: nil, **keyword_entries)
|
|
24
24
|
unless keyword_entries.empty?
|
|
25
25
|
raise ArgumentError, "entries given as both positional and keyword arguments" unless entries.nil?
|
|
26
26
|
entries = keyword_entries
|
data/lib/farce/unsafe/lfu_map.rb
CHANGED
|
@@ -10,6 +10,7 @@ module Farce
|
|
|
10
10
|
# does not coordinate competing callers, including callers loading the same key.
|
|
11
11
|
class LFUMap < Abstract::LFUMap
|
|
12
12
|
include Unshareable
|
|
13
|
+
include Internal::Freeze::Delegated
|
|
13
14
|
|
|
14
15
|
# @!method store_if_absent(key)
|
|
15
16
|
# Read an existing value or construct and store a value for an absent key.
|
|
@@ -29,6 +30,7 @@ module Farce
|
|
|
29
30
|
private
|
|
30
31
|
|
|
31
32
|
def new_bounded_map(...) = Internal::LFUMap.new(...)
|
|
33
|
+
def freeze_backend = @map
|
|
32
34
|
def new_key_locks(**) = nil
|
|
33
35
|
def with_key_lock(_key) = yield
|
|
34
36
|
end
|
data/lib/farce/unsafe/lru_map.rb
CHANGED
|
@@ -10,6 +10,7 @@ module Farce
|
|
|
10
10
|
# does not coordinate competing callers, including callers loading the same key.
|
|
11
11
|
class LRUMap < Abstract::LRUMap
|
|
12
12
|
include Unshareable
|
|
13
|
+
include Internal::Freeze::Delegated
|
|
13
14
|
|
|
14
15
|
# @!method store_if_absent(key)
|
|
15
16
|
# Read an existing value or construct and store a value for an absent key.
|
|
@@ -29,6 +30,7 @@ module Farce
|
|
|
29
30
|
private
|
|
30
31
|
|
|
31
32
|
def new_bounded_map(...) = Internal::LRUMap.new(...)
|
|
33
|
+
def freeze_backend = @map
|
|
32
34
|
def new_key_locks(**) = nil
|
|
33
35
|
def with_key_lock(_key) = yield
|
|
34
36
|
end
|
|
@@ -8,12 +8,14 @@ module Farce
|
|
|
8
8
|
# A {Abstract::TreeMap tree map} that is not thread-safe and cannot be shared between Ractors.
|
|
9
9
|
class TreeMap < Abstract::TreeMap
|
|
10
10
|
include Unshareable
|
|
11
|
+
include Internal::Freeze::Delegated
|
|
11
12
|
|
|
12
13
|
undef_method :transaction_wrapper
|
|
13
14
|
|
|
14
15
|
private
|
|
15
16
|
|
|
16
17
|
def new_tree_map(...) = Internal::UnsafeTreeMap.new(...)
|
|
18
|
+
def freeze_backend = @map
|
|
17
19
|
def new_key_locks = nil
|
|
18
20
|
def with_key_lock(_key) = yield
|
|
19
21
|
end
|
data/lib/farce/unshareable.rb
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
module Farce
|
|
6
6
|
# Mixin that makes sure instances cannot be shared between Ractors.
|
|
7
|
-
#
|
|
7
|
+
# Instances may be frozen without becoming Ractor-shareable.
|
|
8
8
|
#
|
|
9
9
|
# @example
|
|
10
10
|
# class MyClass
|
|
@@ -12,26 +12,10 @@ module Farce
|
|
|
12
12
|
# end
|
|
13
13
|
#
|
|
14
14
|
# object = MyClass.new
|
|
15
|
-
# object.freeze #
|
|
15
|
+
# object.freeze # Preserves the Ractor restrictions.
|
|
16
16
|
#
|
|
17
17
|
# Ractor.shareable?(object) # => false
|
|
18
18
|
module Unshareable
|
|
19
|
-
# @api private
|
|
20
|
-
module UndefFreeze # :nodoc: all
|
|
21
|
-
# @api private
|
|
22
|
-
def append_features(mod)
|
|
23
|
-
super
|
|
24
|
-
if mod.is_a?(Class)
|
|
25
|
-
mod.class_eval { undef freeze if method_defined?(:freeze) }
|
|
26
|
-
else
|
|
27
|
-
mod.extend(UndefFreeze)
|
|
28
|
-
end
|
|
29
|
-
end
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
private_constant :UndefFreeze
|
|
33
|
-
extend UndefFreeze
|
|
34
|
-
|
|
35
19
|
# Mixin for classes that cannot be shared across Ractors, but may be moved between them.
|
|
36
20
|
module Movable
|
|
37
21
|
include Unshareable
|
|
@@ -58,7 +42,12 @@ module Farce
|
|
|
58
42
|
def pin_unshareable
|
|
59
43
|
return unless Internal.native_ractors?
|
|
60
44
|
case self
|
|
61
|
-
when Movable
|
|
45
|
+
when Movable
|
|
46
|
+
if is_a?(Copyable)
|
|
47
|
+
Internal::Unshareable.prevent_shareable(self)
|
|
48
|
+
else
|
|
49
|
+
Internal::Unshareable.prevent_copyable(self)
|
|
50
|
+
end
|
|
62
51
|
when Copyable then Internal::Unshareable.prevent_movable(self)
|
|
63
52
|
else Internal::Unshareable.pin_to_current_ractor(self)
|
|
64
53
|
end
|
data/lib/farce/unshared/atom.rb
CHANGED
|
@@ -12,6 +12,7 @@ module Farce
|
|
|
12
12
|
# atom.update { |items| items + [:job] }
|
|
13
13
|
class Atom < Abstract::Atom
|
|
14
14
|
include Unshareable
|
|
15
|
+
include Internal::Freeze::Delegated
|
|
15
16
|
|
|
16
17
|
# @param value [BasicObject, nil] the initial value
|
|
17
18
|
# @param compare_by_identity [Boolean] whether comparisons use object identity
|
|
@@ -19,6 +20,8 @@ module Farce
|
|
|
19
20
|
@atom = Internal::UnsharedAtom.new(value, compare_by_identity:)
|
|
20
21
|
super()
|
|
21
22
|
end
|
|
23
|
+
|
|
24
|
+
private def freeze_backend = @atom
|
|
22
25
|
end
|
|
23
26
|
end
|
|
24
27
|
end
|
|
@@ -7,10 +7,12 @@ module Farce
|
|
|
7
7
|
# A thread-safe LFU map for mutable keys and values used within one Ractor.
|
|
8
8
|
class LFUMap < Abstract::LFUMap
|
|
9
9
|
include Unshareable
|
|
10
|
+
include Internal::Freeze::Delegated
|
|
10
11
|
|
|
11
12
|
private
|
|
12
13
|
|
|
13
14
|
def new_bounded_map(...) = Internal::LFUMap.new(...)
|
|
15
|
+
def freeze_backend = @map
|
|
14
16
|
|
|
15
17
|
def new_key_locks(compare_keys_by_identity:)
|
|
16
18
|
Internal::KeyLockMap.new(registry_class: Farce::Unshared::Map, compare_keys_by_identity:)
|
|
@@ -7,10 +7,12 @@ module Farce
|
|
|
7
7
|
# A thread-safe LRU map for mutable keys and values used within one Ractor.
|
|
8
8
|
class LRUMap < Abstract::LRUMap
|
|
9
9
|
include Unshareable
|
|
10
|
+
include Internal::Freeze::Delegated
|
|
10
11
|
|
|
11
12
|
private
|
|
12
13
|
|
|
13
14
|
def new_bounded_map(...) = Internal::LRUMap.new(...)
|
|
15
|
+
def freeze_backend = @map
|
|
14
16
|
|
|
15
17
|
def new_key_locks(compare_keys_by_identity:)
|
|
16
18
|
Internal::KeyLockMap.new(registry_class: Farce::Unshared::Map, compare_keys_by_identity:)
|
data/lib/farce/unshared/map.rb
CHANGED
|
@@ -12,6 +12,7 @@ module Farce
|
|
|
12
12
|
# `dup` and `clone` copy entries into independent storage while sharing stored values.
|
|
13
13
|
class Map < Abstract::ConcurrentMap
|
|
14
14
|
include Unshareable
|
|
15
|
+
include Internal::Freeze::Delegated
|
|
15
16
|
|
|
16
17
|
# @!macro map_initialization
|
|
17
18
|
#
|
|
@@ -44,6 +45,8 @@ module Farce
|
|
|
44
45
|
end
|
|
45
46
|
|
|
46
47
|
Internal.prepare_map_access(self, :unshared)
|
|
48
|
+
|
|
49
|
+
private def freeze_backend = @map
|
|
47
50
|
end
|
|
48
51
|
end
|
|
49
52
|
end
|
data/lib/farce/unshared/set.rb
CHANGED
|
@@ -14,8 +14,12 @@ module Farce
|
|
|
14
14
|
# map[2].equal?(value) # => true
|
|
15
15
|
class TreeMap < Abstract::TreeMap
|
|
16
16
|
include Unshareable
|
|
17
|
+
include Internal::Freeze::Delegated
|
|
17
18
|
|
|
18
|
-
private
|
|
19
|
+
private
|
|
20
|
+
|
|
21
|
+
def new_tree_map(...) = Internal::TreeMap.new(...)
|
|
22
|
+
def freeze_backend = @map
|
|
19
23
|
end
|
|
20
24
|
end
|
|
21
25
|
end
|
|
@@ -7,6 +7,7 @@ module Farce
|
|
|
7
7
|
# A thread-safe vector for mutable values used within one Ractor.
|
|
8
8
|
class Vector < Abstract::Vector
|
|
9
9
|
include Unshareable
|
|
10
|
+
include Internal::Freeze::Delegated
|
|
10
11
|
|
|
11
12
|
# @param source [Array, nil] Initial values. Values are retained directly.
|
|
12
13
|
# @param compare_by_identity [Boolean] Whether values are compared by identity.
|
|
@@ -14,6 +15,8 @@ module Farce
|
|
|
14
15
|
@vector = Internal::UnsharedVector.new(source, compare_by_identity:)
|
|
15
16
|
super()
|
|
16
17
|
end
|
|
18
|
+
|
|
19
|
+
private def freeze_backend = @vector
|
|
17
20
|
end
|
|
18
21
|
end
|
|
19
22
|
end
|
|
@@ -24,8 +24,12 @@ module Farce
|
|
|
24
24
|
# atom.value.equal?(replacement) # => true
|
|
25
25
|
class WeakAtom < Abstract::WeakAtom
|
|
26
26
|
include Unshareable
|
|
27
|
+
include Internal::Freeze::Delegated
|
|
27
28
|
|
|
28
|
-
private
|
|
29
|
+
private
|
|
30
|
+
|
|
31
|
+
def internal_atom_class = Internal::UnsharedWeakAtom
|
|
32
|
+
def freeze_backend = @atom
|
|
29
33
|
end
|
|
30
34
|
end
|
|
31
35
|
end
|
|
@@ -16,6 +16,7 @@ module Farce
|
|
|
16
16
|
# owner = nil
|
|
17
17
|
class WeakKeyMap < Abstract::WeakKeyMap
|
|
18
18
|
include Unshareable
|
|
19
|
+
include Internal::Freeze::Delegated
|
|
19
20
|
|
|
20
21
|
def initialize(
|
|
21
22
|
initial_mapping = nil,
|
|
@@ -39,6 +40,8 @@ module Farce
|
|
|
39
40
|
Internal::KeyNormalizer.install_concurrent(self, normalizer) if restoring
|
|
40
41
|
super()
|
|
41
42
|
end
|
|
43
|
+
|
|
44
|
+
private def freeze_backend = @map
|
|
42
45
|
end
|
|
43
46
|
end
|
|
44
47
|
end
|
|
@@ -16,6 +16,7 @@ module Farce
|
|
|
16
16
|
# value = nil
|
|
17
17
|
class WeakMap < Abstract::WeakMap
|
|
18
18
|
include Unshareable
|
|
19
|
+
include Internal::Freeze::Delegated
|
|
19
20
|
|
|
20
21
|
def initialize(
|
|
21
22
|
initial_mapping = nil,
|
|
@@ -39,6 +40,8 @@ module Farce
|
|
|
39
40
|
Internal::KeyNormalizer.install_concurrent(self, normalizer) if restoring
|
|
40
41
|
super()
|
|
41
42
|
end
|
|
43
|
+
|
|
44
|
+
private def freeze_backend = @map
|
|
42
45
|
end
|
|
43
46
|
end
|
|
44
47
|
end
|
|
@@ -7,6 +7,7 @@ module Farce
|
|
|
7
7
|
# A thread-safe set that retains mutable elements weakly within one Ractor.
|
|
8
8
|
class WeakSet < Farce::Abstract::WeakSet
|
|
9
9
|
include Unshareable
|
|
10
|
+
include Internal::Freeze::Delegated
|
|
10
11
|
|
|
11
12
|
private def new_map(...) = Farce::Unshared::WeakMap.new(...)
|
|
12
13
|
end
|
|
@@ -15,6 +15,7 @@ module Farce
|
|
|
15
15
|
# value = nil
|
|
16
16
|
class WeakValueMap < Abstract::WeakValueMap
|
|
17
17
|
include Unshareable
|
|
18
|
+
include Internal::Freeze::Delegated
|
|
18
19
|
|
|
19
20
|
def initialize(
|
|
20
21
|
initial_mapping = nil,
|
|
@@ -38,6 +39,8 @@ module Farce
|
|
|
38
39
|
Internal::KeyNormalizer.install_concurrent(self, normalizer) if restoring
|
|
39
40
|
super()
|
|
40
41
|
end
|
|
42
|
+
|
|
43
|
+
private def freeze_backend = @map
|
|
41
44
|
end
|
|
42
45
|
end
|
|
43
46
|
end
|
data/lib/farce/vector.rb
CHANGED
|
@@ -10,9 +10,9 @@ module Farce
|
|
|
10
10
|
# vector = Farce::Vector.new([[]], mode: :make_shareable)
|
|
11
11
|
# vector.update(0) { |jobs| jobs + [:finished] } # => [:finished]
|
|
12
12
|
#
|
|
13
|
-
# @!method append(
|
|
14
|
-
# Append
|
|
15
|
-
# @param
|
|
13
|
+
# @!method append(*values, mode: nil, timeout: nil)
|
|
14
|
+
# Append values using the same options as {#push}.
|
|
15
|
+
# @param values [Array<BasicObject>] The values to append.
|
|
16
16
|
# @param mode [Symbol, nil] The transfer mode, or nil to use the default.
|
|
17
17
|
# @param timeout [Numeric, nil] The maximum wait in seconds. Nil waits indefinitely.
|
|
18
18
|
# @return [self, false] Self on success, or false on timeout.
|
|
@@ -23,7 +23,7 @@ module Farce
|
|
|
23
23
|
# @param source [Array, nil] Initial values. 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
|
-
def initialize(source = nil, mode: :
|
|
26
|
+
def initialize(source = nil, mode: :smart, compare_by_identity: false)
|
|
27
27
|
raise TypeError, "source must be an Array" unless source.nil? || source.is_a?(Array)
|
|
28
28
|
unless true.equal?(compare_by_identity) || false.equal?(compare_by_identity)
|
|
29
29
|
raise ArgumentError, "compare_by_identity must be true or false"
|
|
@@ -64,13 +64,20 @@ module Farce
|
|
|
64
64
|
def shareable_values? = true
|
|
65
65
|
|
|
66
66
|
# (see Farce::Abstract::Vector#[])
|
|
67
|
-
def [](index
|
|
67
|
+
def [](index, length = UNDEFINED)
|
|
68
|
+
return @manager.unwrap(@vector[index]) if length.equal?(UNDEFINED) && !index.is_a?(Range)
|
|
69
|
+
slice(index, length)
|
|
70
|
+
end
|
|
68
71
|
|
|
69
72
|
# (see Farce::Abstract::Vector#[]=)
|
|
70
|
-
def []=(index, value)
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
73
|
+
def []=(index, value, replacement = UNDEFINED)
|
|
74
|
+
if replacement.equal?(UNDEFINED) && !index.is_a?(Range)
|
|
75
|
+
check_frozen!
|
|
76
|
+
@vector[index] = @manager.wrap(value)
|
|
77
|
+
value
|
|
78
|
+
else
|
|
79
|
+
assign_vector_slice(index, value, replacement)
|
|
80
|
+
end
|
|
74
81
|
end
|
|
75
82
|
|
|
76
83
|
# (see Farce::Abstract::Vector#get)
|
|
@@ -85,13 +92,21 @@ module Farce
|
|
|
85
92
|
|
|
86
93
|
# (see Farce::Abstract::Vector#push)
|
|
87
94
|
# @param mode [Symbol, nil] The transfer mode, or nil to use the default.
|
|
88
|
-
def push(value, mode: nil, timeout: nil)
|
|
89
|
-
|
|
90
|
-
|
|
95
|
+
def push(value = UNDEFINED, *rest, mode: nil, timeout: nil)
|
|
96
|
+
if rest.empty? && !value.equal?(UNDEFINED)
|
|
97
|
+
check_frozen!
|
|
98
|
+
stored = mode.nil? ? @manager.wrap(value) : @manager.wrap(value, mode:)
|
|
99
|
+
completed = timeout.nil? ? @vector.push(stored) : @vector.push(stored, timeout:)
|
|
100
|
+
return completed ? self : false
|
|
101
|
+
end
|
|
102
|
+
push_values(value.equal?(UNDEFINED) ? rest : [value, *rest], mode:, timeout:)
|
|
91
103
|
end
|
|
92
104
|
|
|
93
105
|
# (see Farce::Abstract::Vector#pop)
|
|
94
|
-
def pop(
|
|
106
|
+
def pop(count = UNDEFINED, timeout: nil)
|
|
107
|
+
return @manager.unwrap(timeout.nil? ? @vector.pop : @vector.pop(timeout:)) if count.equal?(UNDEFINED)
|
|
108
|
+
pop_values(count, timeout:)
|
|
109
|
+
end
|
|
95
110
|
|
|
96
111
|
# (see Farce::Abstract::Vector#swap)
|
|
97
112
|
# @!macro modes
|
|
@@ -183,7 +198,9 @@ module Farce
|
|
|
183
198
|
|
|
184
199
|
protected
|
|
185
200
|
|
|
186
|
-
def logical_value(value)
|
|
201
|
+
def logical_value(value) = @manager.unwrap(value)
|
|
202
|
+
def deletion_comparison(value) = wrap_comparison(value)
|
|
203
|
+
def deletion_match?(stored, expected) = values_equal?(stored, expected)
|
|
187
204
|
|
|
188
205
|
def build_derived_vector(values)
|
|
189
206
|
self.class.allocate.initialize_derived_vector(values, @manager, @compare_by_identity)
|
|
@@ -206,11 +223,19 @@ module Farce
|
|
|
206
223
|
|
|
207
224
|
private
|
|
208
225
|
|
|
209
|
-
def inspect_value(inspector, value)
|
|
210
|
-
def derived_storage(value)
|
|
211
|
-
def
|
|
212
|
-
|
|
213
|
-
|
|
226
|
+
def inspect_value(inspector, value) = super(inspector, value, @manager)
|
|
227
|
+
def derived_storage(value) = @manager.wrap(value)
|
|
228
|
+
def prepare_vector_value(value, mode: nil) = @manager.wrap(value, mode:)
|
|
229
|
+
|
|
230
|
+
# Expanded descendants have no independent envelope to reuse. Copy them rather than
|
|
231
|
+
# moving them out of a retained parent Array or another sequence's storage.
|
|
232
|
+
def prepare_sequence_value(value)
|
|
233
|
+
@manager.wrap(value, mode: mode == :move ? :copy : nil)
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
def freeze_backend = @vector
|
|
237
|
+
def wrap_comparison(value) = @manager.wrap(value, mode: compare_by_identity? ? :local : :copy)
|
|
238
|
+
def values_equal?(left, right) = @manager.same_value?(left, right, identity: compare_by_identity?)
|
|
214
239
|
|
|
215
240
|
def timeout_deadline(timeout)
|
|
216
241
|
return if timeout.nil?
|
data/lib/farce/version.rb
CHANGED
data/lib/farce.rb
CHANGED
|
@@ -54,7 +54,10 @@ module Farce
|
|
|
54
54
|
|
|
55
55
|
# The valid transfer modes for values that are not Ractor-shareable.
|
|
56
56
|
# @return [::Set<Symbol>]
|
|
57
|
-
MODES = ::Set[
|
|
57
|
+
MODES = ::Set[
|
|
58
|
+
:copy, :marshal, :move, :local, :make_shareable, :mutable, :raise, :shareable_copy, :dedup, :proxy,
|
|
59
|
+
:smart, :smart_raise, :smart_move, :smart_copy,
|
|
60
|
+
].freeze
|
|
58
61
|
|
|
59
62
|
UNDEFINED = Internal::Undefined.new("UNDEFINED")
|
|
60
63
|
|
|
@@ -171,7 +174,7 @@ module Farce
|
|
|
171
174
|
DEDUPER.dedup(object, **)
|
|
172
175
|
end
|
|
173
176
|
|
|
174
|
-
# @overload enfarce(object, freeze: nil, mode: :
|
|
177
|
+
# @overload enfarce(object, freeze: nil, mode: :smart)
|
|
175
178
|
# Converts vanilla Ruby objects into their {Farce} equivalents.
|
|
176
179
|
#
|
|
177
180
|
# @!macro modes
|
|
@@ -396,7 +399,7 @@ module Farce
|
|
|
396
399
|
# # Hi from the current ractor!
|
|
397
400
|
# # Hi again from the main ractor!
|
|
398
401
|
#
|
|
399
|
-
# @overload on_main(*args, mode: :
|
|
402
|
+
# @overload on_main(*args, mode: :smart)
|
|
400
403
|
# @param args [Array] The arguments to be passed to the block.
|
|
401
404
|
# @param mode [Symbol] The argument transfer mode when called from another ractor.
|
|
402
405
|
# @yield [*args] The block to be executed on the main ractor.
|
|
@@ -418,7 +421,7 @@ module Farce
|
|
|
418
421
|
if Ractor.main?
|
|
419
422
|
yield(*args)
|
|
420
423
|
else
|
|
421
|
-
mode = :
|
|
424
|
+
mode = :smart if UNDEFINED.equal?(mode)
|
|
422
425
|
Internal::MainScheduler.execute(*args, mode:, auto_local: false, &)
|
|
423
426
|
end
|
|
424
427
|
|
|
@@ -438,7 +441,7 @@ module Farce
|
|
|
438
441
|
# Farce.in_parallel("hello") { |message| puts message.upcase }
|
|
439
442
|
# Farce.in_parallel.schedule { puts "another task" }
|
|
440
443
|
#
|
|
441
|
-
# @overload in_parallel(*args, mode: :
|
|
444
|
+
# @overload in_parallel(*args, mode: :smart)
|
|
442
445
|
# @param args [Array<Object>] Arguments passed to the block.
|
|
443
446
|
# @param mode [Symbol] Argument transfer mode. Ignored on JRuby and TruffleRuby.
|
|
444
447
|
# @yield [*args] The task to schedule.
|
|
@@ -455,12 +458,12 @@ module Farce
|
|
|
455
458
|
return Internal::ParallelScheduler
|
|
456
459
|
end
|
|
457
460
|
|
|
458
|
-
mode = :
|
|
461
|
+
mode = :smart if UNDEFINED.equal?(mode)
|
|
459
462
|
Internal::ParallelScheduler.schedule(*args, mode:, &)
|
|
460
463
|
nil
|
|
461
464
|
end
|
|
462
465
|
|
|
463
|
-
# @overload schedule(*args, mode: :
|
|
466
|
+
# @overload schedule(*args, mode: :smart, auto_local: true, **kwargs)
|
|
464
467
|
# Schedules work to be executed out of band.
|
|
465
468
|
#
|
|
466
469
|
# If the `mode` is set to `:local`, it will use the current thread's fiber scheduler to schedule the task.
|
|
@@ -486,7 +489,7 @@ module Farce
|
|
|
486
489
|
# @param kwargs [Hash] Additional keyword arguments passed to the scheduler.
|
|
487
490
|
# @yield [*args] The task to schedule.
|
|
488
491
|
# @return [nil]
|
|
489
|
-
def self.schedule(*, mode: :
|
|
492
|
+
def self.schedule(*, mode: :smart, auto_local: true, **, &)
|
|
490
493
|
raise LocalJumpError, "no block given" unless block_given?
|
|
491
494
|
|
|
492
495
|
if auto_local || mode == :local
|