farce 0.1.0-arm-linux-gnu → 0.2.0-arm-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.
Files changed (116) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +80 -1
  3. data/README.md +72 -16
  4. data/docs/gems/dry-types.md +8 -8
  5. data/docs/modes.md +67 -10
  6. data/ext/farce/atom.c +37 -22
  7. data/ext/farce/bounded_map.c +17 -13
  8. data/ext/farce/containers.h +15 -0
  9. data/ext/farce/counter.c +13 -18
  10. data/ext/farce/exchanger.c +5 -1
  11. data/ext/farce/farce.c +1 -1
  12. data/ext/farce/flag.c +39 -3
  13. data/ext/farce/io.c +3 -3
  14. data/ext/farce/lock.c +17 -23
  15. data/ext/farce/map.c +88 -66
  16. data/ext/farce/priority_queue.c +55 -43
  17. data/ext/farce/queue.c +10 -4
  18. data/ext/farce/reactor.c +52 -25
  19. data/ext/farce/reactor.h +1 -1
  20. data/ext/farce/signal.c +5 -1
  21. data/ext/farce/transaction.c +20 -24
  22. data/ext/farce/tree_map.c +33 -18
  23. data/ext/farce/trie.c +81 -43
  24. data/ext/farce/unshareable.c +168 -19
  25. data/ext/farce/unshared_io_pool.h +21 -12
  26. data/ext/farce/unshared_signal.c +13 -2
  27. data/ext/farce/unshared_wait.h +3 -0
  28. data/ext/farce/vector.c +82 -18
  29. data/ext/farce/weak_map.c +75 -63
  30. data/lib/farce/_yard/macros.rb +18 -3
  31. data/lib/farce/abstract/flag.rb +73 -0
  32. data/lib/farce/abstract/queue.rb +4 -3
  33. data/lib/farce/abstract/scheduler.rb +3 -3
  34. data/lib/farce/abstract/set.rb +17 -5
  35. data/lib/farce/abstract/sorted_set.rb +2 -2
  36. data/lib/farce/abstract/timer_queue.rb +49 -10
  37. data/lib/farce/abstract/vector.rb +510 -28
  38. data/lib/farce/atom.rb +1 -1
  39. data/lib/farce/class_mirror.rb +20 -7
  40. data/lib/farce/config.rb +7 -1
  41. data/lib/farce/engine/jvm/flag.rb +15 -3
  42. data/lib/farce/engine/ruby/3.4/farce.so +0 -0
  43. data/lib/farce/engine/ruby/4.0/farce.so +0 -0
  44. data/lib/farce/engine/ruby/shared/require_hook.rb +18 -0
  45. data/lib/farce/engine/ruby.rb +7 -0
  46. data/lib/farce/engine/shared/unshared_vector.rb +31 -4
  47. data/lib/farce/engine/shared/vector.rb +8 -0
  48. data/lib/farce/engine/shared/weak_map/index.rb +14 -1
  49. data/lib/farce/engine/truffleruby/native/flag.rb +15 -3
  50. data/lib/farce/envelope.rb +54 -7
  51. data/lib/farce/exchanger.rb +1 -1
  52. data/lib/farce/flag.rb +2 -0
  53. data/lib/farce/integrations/active_support.rb +1 -1
  54. data/lib/farce/integrations/bson.rb +1 -1
  55. data/lib/farce/integrations/cbor.rb +1 -1
  56. data/lib/farce/integrations/concurrent.rb +3 -3
  57. data/lib/farce/integrations/date.rb +8 -0
  58. data/lib/farce/integrations/dry_types.rb +7 -4
  59. data/lib/farce/integrations/json.rb +2 -2
  60. data/lib/farce/integrations/msgpack.rb +1 -1
  61. data/lib/farce/integrations/oj.rb +3 -4
  62. data/lib/farce/integrations/psych.rb +1 -1
  63. data/lib/farce/integrations/ractor_sharing.rb +1 -1
  64. data/lib/farce/integrations/sorted_set.rb +1 -1
  65. data/lib/farce/integrations/weakref.rb +9 -1
  66. data/lib/farce/integrations/yajl.rb +3 -3
  67. data/lib/farce/integrations.rb +33 -23
  68. data/lib/farce/internal/flag_waiting.rb +30 -0
  69. data/lib/farce/internal/freeze.rb +8 -3
  70. data/lib/farce/internal/map_value_modes.rb +1 -1
  71. data/lib/farce/internal/scheduler_lifecycle.rb +1 -1
  72. data/lib/farce/lazy.rb +4 -4
  73. data/lib/farce/lazy_ref.rb +3 -3
  74. data/lib/farce/lfu_map.rb +1 -1
  75. data/lib/farce/local/flag.rb +4 -0
  76. data/lib/farce/local/scoped.rb +1 -1
  77. data/lib/farce/lru_map.rb +1 -1
  78. data/lib/farce/map.rb +2 -2
  79. data/lib/farce/mode_manager/smart_rule.rb +138 -0
  80. data/lib/farce/mode_manager.rb +12 -3
  81. data/lib/farce/molecule.rb +2 -2
  82. data/lib/farce/pool.rb +1 -1
  83. data/lib/farce/port.rb +4 -4
  84. data/lib/farce/priority_queue.rb +1 -1
  85. data/lib/farce/proxy/register.rb +6 -0
  86. data/lib/farce/proxy/supervisor.rb +1 -1
  87. data/lib/farce/proxy.rb +7 -2
  88. data/lib/farce/queue.rb +1 -1
  89. data/lib/farce/scheduler.rb +2 -2
  90. data/lib/farce/set.rb +1 -1
  91. data/lib/farce/sorted_set.rb +1 -1
  92. data/lib/farce/timer_queue.rb +20 -6
  93. data/lib/farce/transaction/vector.rb +43 -9
  94. data/lib/farce/transaction/wrapper.rb +14 -0
  95. data/lib/farce/tree_map.rb +1 -1
  96. data/lib/farce/unsafe/lfu_map.rb +2 -0
  97. data/lib/farce/unsafe/lru_map.rb +2 -0
  98. data/lib/farce/unsafe/tree_map.rb +2 -0
  99. data/lib/farce/unshareable.rb +8 -19
  100. data/lib/farce/unshared/atom.rb +3 -0
  101. data/lib/farce/unshared/lfu_map.rb +2 -0
  102. data/lib/farce/unshared/lru_map.rb +2 -0
  103. data/lib/farce/unshared/map.rb +3 -0
  104. data/lib/farce/unshared/set.rb +1 -0
  105. data/lib/farce/unshared/sorted_set.rb +1 -0
  106. data/lib/farce/unshared/tree_map.rb +5 -1
  107. data/lib/farce/unshared/vector.rb +3 -0
  108. data/lib/farce/unshared/weak_atom.rb +5 -1
  109. data/lib/farce/unshared/weak_key_map.rb +3 -0
  110. data/lib/farce/unshared/weak_map.rb +3 -0
  111. data/lib/farce/unshared/weak_set.rb +1 -0
  112. data/lib/farce/unshared/weak_value_map.rb +3 -0
  113. data/lib/farce/vector.rb +44 -19
  114. data/lib/farce/version.rb +1 -1
  115. data/lib/farce.rb +11 -8
  116. metadata +5 -1
@@ -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: :copy, track_age: false)
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 = Clock.parse(time_options)
24
- push_to_storage(at, non_block, @manager.wrap(value, mode:), timeout:)
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 = Clock.parse(time_options)
32
- return true if @queue.push(at, @manager.wrap(value, mode:))
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) = access { unwrap_stored(@working[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
- store(index, value)
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 { @working.push(wrap(value, mode:)) }
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
- alias << push
74
+
75
+ def <<(value) = push(value)
60
76
 
61
77
  # (see Abstract::Vector#pop)
62
78
  # @see Abstract::Vector#pop
63
- def pop = write { unwrap_stored(@working.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.
@@ -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: :copy, normalize_keys: nil, **keyword_entries)
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
@@ -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
@@ -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
@@ -4,7 +4,7 @@
4
4
 
5
5
  module Farce
6
6
  # Mixin that makes sure instances cannot be shared between Ractors.
7
- # @note Mixin in this module will remove the `freeze` method from the class.
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 # this would normally make it shareable
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 then Internal::Unshareable.prevent_copyable(self) unless is_a?(Copyable)
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
@@ -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:)
@@ -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
@@ -7,6 +7,7 @@ module Farce
7
7
  # A thread-safe set for mutable elements used within one Ractor.
8
8
  class Set < Farce::Abstract::Set
9
9
  include Unshareable
10
+ include Internal::Freeze::Delegated
10
11
 
11
12
  private def new_map(...) = Farce::Unshared::Map.new(...)
12
13
  end
@@ -7,6 +7,7 @@ module Farce
7
7
  # A thread-safe set maintained in ascending order within one Ractor.
8
8
  class SortedSet < Farce::Abstract::SortedSet
9
9
  include Unshareable
10
+ include Internal::Freeze::Delegated
10
11
 
11
12
  class MutableTreeMap < Farce::Unshared::TreeMap # :nodoc:
12
13
  private
@@ -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 def new_tree_map(...) = Internal::TreeMap.new(...)
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 def internal_atom_class = Internal::UnsharedWeakAtom
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(value, mode: nil, timeout: nil)
14
- # Append one value using the same options as {#push}.
15
- # @param value [BasicObject] The value to append.
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: :copy, compare_by_identity: false)
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) = @manager.unwrap(@vector[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
- check_frozen!
72
- @vector[index] = @manager.wrap(value)
73
- value
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
- check_frozen!
90
- @vector.push(@manager.wrap(value, mode:), timeout:) ? self : false
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(timeout: nil) = @manager.unwrap(@vector.pop(timeout:))
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) = @manager.unwrap(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) = super(inspector, value, @manager)
210
- def derived_storage(value) = @manager.wrap(value)
211
- def freeze_backend = @vector
212
- def wrap_comparison(value) = @manager.wrap(value, mode: compare_by_identity? ? :local : :copy)
213
- def values_equal?(left, right) = @manager.same_value?(left, right, identity: compare_by_identity?)
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
@@ -4,5 +4,5 @@
4
4
 
5
5
  module Farce
6
6
  # @return [String] the current version of Farce
7
- VERSION = "0.1.0"
7
+ VERSION = "0.2.0"
8
8
  end
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[:copy, :move, :local, :make_shareable, :mutable, :raise, :shareable_copy, :dedup, :proxy].freeze
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: :copy)
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: :copy)
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 = :copy if UNDEFINED.equal?(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: :copy)
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 = :copy if UNDEFINED.equal?(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: :copy, auto_local: true, **kwargs)
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: :copy, auto_local: true, **, &)
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