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.
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
data/lib/farce/config.rb CHANGED
@@ -24,7 +24,7 @@ module Farce
24
24
  class Config
25
25
  # Automatically load integrations when their dependencies are loaded. Defaults to true.
26
26
  # Can also be set via `FARCE_AUTOLOAD_INTEGRATIONS` (`true`/`false` or `1`/`0`).
27
- # Set before requiring Farce to also disable startup integration loading.
27
+ # Set before requiring Farce to disable startup integration loading and require hooks.
28
28
  # @return [Boolean]
29
29
  attr_reader :autoload_integrations
30
30
 
@@ -63,6 +63,7 @@ module Farce
63
63
  end
64
64
 
65
65
  # Enable or disable automatic integration loading. Already loaded integrations remain active.
66
+ # Enabling this option installs require hooks if they are not already installed.
66
67
  # @param value [Boolean, String] true, false, or their environment variable representations.
67
68
  # @raise [ArgumentError] If the value is not a supported boolean.
68
69
  def autoload_integrations=(value)
@@ -71,6 +72,11 @@ module Farce
71
72
  when false, "false", "0" then false
72
73
  else raise ArgumentError, "autoload_integrations must be a boolean"
73
74
  end
75
+ if @autoload_integrations && Farce.const_defined?(:Integrations, false) &&
76
+ !Farce.autoload?(:Integrations) && Farce.config.equal?(self)
77
+ Integrations.install_require_hooks
78
+ end
79
+ @autoload_integrations
74
80
  end
75
81
 
76
82
  # Default fiber scheduler for Pool and Scheduler.
@@ -8,23 +8,30 @@ module Farce
8
8
  # @!visibility private
9
9
  module Internal # :nodoc: all
10
10
  class Flag
11
+ include FlagWaiting
12
+
11
13
  def initialize(value = false) # rubocop:disable Style/OptionalBooleanParameter
12
14
  raise "Flag is already initialized" if defined?(@reference)
13
15
  check_frozen!
16
+ @signal = Signal.new
14
17
  @reference = JVMContainers::AtomicBoolean.new(validate_boolean(value, "initial value"))
15
18
  end
16
19
 
20
+ def change_signal = @signal
21
+
17
22
  def value = @reference.get
18
23
 
19
24
  def set # rubocop:disable Naming/PredicateMethod
20
25
  check_frozen!
21
26
  @reference.set(true)
27
+ @signal.broadcast
22
28
  true
23
29
  end
24
30
 
25
31
  def unset # rubocop:disable Naming/PredicateMethod
26
32
  check_frozen!
27
33
  @reference.set(false)
34
+ @signal.broadcast
28
35
  false
29
36
  end
30
37
 
@@ -32,20 +39,21 @@ module Farce
32
39
  check_frozen!
33
40
  value = validate_boolean(value, "value")
34
41
  @reference.set(value)
42
+ @signal.broadcast
35
43
  value
36
44
  end
37
45
 
38
46
  def swap(value)
39
47
  check_frozen!
40
48
  value = validate_boolean(value, "value")
41
- @reference.getAndSet(value)
49
+ @reference.getAndSet(value).tap { @signal.broadcast }
42
50
  end
43
51
 
44
52
  def compare_and_set(expected_value, replacement_value)
45
53
  check_frozen!
46
54
  expected_value = validate_boolean(expected_value, "expected value")
47
55
  replacement_value = validate_boolean(replacement_value, "replacement value")
48
- @reference.compareAndSet(expected_value, replacement_value)
56
+ @reference.compareAndSet(expected_value, replacement_value).tap { |changed| @signal.broadcast if changed }
49
57
  end
50
58
 
51
59
  def toggle
@@ -53,7 +61,10 @@ module Farce
53
61
  loop do
54
62
  current = @reference.get
55
63
  replacement = !current
56
- return replacement if @reference.compareAndSet(current, replacement)
64
+ if @reference.compareAndSet(current, replacement)
65
+ @signal.broadcast
66
+ return replacement
67
+ end
57
68
  end
58
69
  end
59
70
 
@@ -72,6 +83,7 @@ module Farce
72
83
  end
73
84
 
74
85
  def initialize_copy(other)
86
+ @signal = Signal.new
75
87
  @reference = JVMContainers::AtomicBoolean.new(other.value)
76
88
  end
77
89
  end
Binary file
Binary file
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+ # shareable_constant_value: literal
3
+ # warn_indent: true
4
+
5
+ # Ruby 3.4.11 and 4.0 prepend a Ractor-aware require to Kernel on activation.
6
+ # Detect that wrapper so versions that install it on Object need no relocation.
7
+ # A later Kernel alias captures that wrapper and loses its super target.
8
+ # Keep the original implementation above Object so aliases capture Kernel's
9
+ # underlying require while secondary Ractors still use Ruby's loader service.
10
+ ractor_require = Kernel.ancestors.find { it.name == "<RactorRequire>" }
11
+ if ractor_require
12
+ replacement = Module.new
13
+ replacement.set_temporary_name("<FarceRactorRequire>")
14
+ replacement.define_method(:require, ractor_require.instance_method(:require))
15
+ replacement.send(:private, :require)
16
+ Object.prepend(replacement)
17
+ ractor_require.remove_method(:require)
18
+ end
@@ -14,6 +14,13 @@ module Farce
14
14
  ENGINE_PATH = "farce/engine/ruby/#{RUBY_VERSION[/^\d+\.\d+/]}".freeze
15
15
  private_constant :ENGINE_PATH
16
16
 
17
+ require "farce/engine/ruby/shared/require_hook"
18
+ # Older Ractor activation defines a public Object#require. Keep Ruby's
19
+ # ordinary private visibility even when integration hooks are disabled.
20
+ if Object.instance_method(:require).source_location&.first == "<internal:ractor>"
21
+ Object.class_eval { private :require }
22
+ end
23
+
17
24
  begin
18
25
  require "#{ENGINE_PATH}/rebind"
19
26
  require "#{ENGINE_PATH}/farce"
@@ -252,6 +252,28 @@ module Farce
252
252
  finish_update if reservation
253
253
  end
254
254
 
255
+ # Yield private storage and publish it only after the block finishes.
256
+ # The yielded Array is never retained. Callbacks run outside the mutex.
257
+ def batch_update(timeout: nil)
258
+ raise LocalJumpError, "no block given" unless block_given?
259
+ check_frozen!
260
+ completed, values = reserve(timeout_deadline(timeout)) do
261
+ snapshot = @values.dup
262
+ @updating = true
263
+ snapshot
264
+ end
265
+ return false unless completed
266
+ yield values
267
+ replacement = values.dup
268
+ @mutex.synchronize do
269
+ check_frozen_locked!
270
+ @values = replacement
271
+ end
272
+ true
273
+ ensure
274
+ finish_update if completed
275
+ end
276
+
255
277
  def wait_until_changed(index, expected, timeout: nil)
256
278
  wait_for_value(convert_index(index), expected, timeout_deadline(timeout), non_nil: false)
257
279
  end
@@ -272,11 +294,16 @@ module Farce
272
294
 
273
295
  def with_available(deadline)
274
296
  while true
275
- generation = @signal.generation
276
- completed, result = @mutex.synchronize do
277
- @updating ? [false, nil] : [true, yield]
297
+ generation = nil
298
+ result = @mutex.synchronize do
299
+ if @updating
300
+ generation = @signal.generation
301
+ nil
302
+ else
303
+ [true, yield]
304
+ end
278
305
  end
279
- return [true, result] if completed
306
+ return result if result
280
307
  return [false, nil] unless wait_for_signal(generation, deadline)
281
308
  end
282
309
  end
@@ -72,6 +72,14 @@ module Farce
72
72
  end
73
73
  end
74
74
 
75
+ def batch_update(**)
76
+ raise LocalJumpError, "no block given" unless block_given?
77
+ super do |values|
78
+ yield values
79
+ values.each { check_value(it) }
80
+ end
81
+ end
82
+
75
83
  private
76
84
 
77
85
  def check_value(value)
@@ -184,7 +184,20 @@ module Farce
184
184
 
185
185
  def data_for(key) = Internal.garbage_collectable?(key) ? @data : @immediate
186
186
  def key_reference(key) = UnsharedWeakMapWeakReference.for(key)
187
- def registry_enumerator = @entries.to_enum(:each_value)
187
+
188
+ def registry_enumerator
189
+ entries = @entries
190
+ Enumerator.new do |yielder|
191
+ # Compaction can rehash a weak registry and repeat earlier entries.
192
+ # Tokens have no back-reference to cells, keys, or values.
193
+ seen = {}.compare_by_identity
194
+ entries.each_value do |entry|
195
+ next if seen.key?(entry.token)
196
+ seen[entry.token] = true
197
+ yielder << entry
198
+ end
199
+ end
200
+ end
188
201
 
189
202
  def register(entry)
190
203
  @registry_mutex.synchronize do
@@ -6,23 +6,30 @@ module Farce
6
6
  # @!visibility private
7
7
  module Internal # :nodoc: all
8
8
  class Flag
9
+ include FlagWaiting
10
+
9
11
  def initialize(value = false) # rubocop:disable Style/OptionalBooleanParameter
10
12
  raise "Flag is already initialized" if defined?(@reference)
11
13
  check_frozen!
14
+ @signal = Signal.new
12
15
  @reference = TruffleRuby::AtomicReference.new(validate_boolean(value, "initial value"))
13
16
  end
14
17
 
18
+ def change_signal = @signal
19
+
15
20
  def value = @reference.get
16
21
 
17
22
  def set # rubocop:disable Naming/PredicateMethod
18
23
  check_frozen!
19
24
  @reference.set(true)
25
+ @signal.broadcast
20
26
  true
21
27
  end
22
28
 
23
29
  def unset # rubocop:disable Naming/PredicateMethod
24
30
  check_frozen!
25
31
  @reference.set(false)
32
+ @signal.broadcast
26
33
  false
27
34
  end
28
35
 
@@ -30,20 +37,21 @@ module Farce
30
37
  check_frozen!
31
38
  value = validate_boolean(value, "value")
32
39
  @reference.set(value)
40
+ @signal.broadcast
33
41
  value
34
42
  end
35
43
 
36
44
  def swap(value)
37
45
  check_frozen!
38
46
  value = validate_boolean(value, "value")
39
- @reference.get_and_set(value)
47
+ @reference.get_and_set(value).tap { @signal.broadcast }
40
48
  end
41
49
 
42
50
  def compare_and_set(expected_value, replacement_value)
43
51
  check_frozen!
44
52
  expected_value = validate_boolean(expected_value, "expected value")
45
53
  replacement_value = validate_boolean(replacement_value, "replacement value")
46
- @reference.compare_and_set(expected_value, replacement_value)
54
+ @reference.compare_and_set(expected_value, replacement_value).tap { |changed| @signal.broadcast if changed }
47
55
  end
48
56
 
49
57
  def toggle
@@ -51,7 +59,10 @@ module Farce
51
59
  loop do
52
60
  current = @reference.get
53
61
  replacement = !current
54
- return replacement if @reference.compare_and_set(current, replacement)
62
+ if @reference.compare_and_set(current, replacement)
63
+ @signal.broadcast
64
+ return replacement
65
+ end
55
66
  end
56
67
  end
57
68
 
@@ -70,6 +81,7 @@ module Farce
70
81
  end
71
82
 
72
83
  def initialize_copy(other)
84
+ @signal = Signal.new
73
85
  @reference = TruffleRuby::AtomicReference.new(other.value)
74
86
  end
75
87
  end
@@ -5,8 +5,7 @@
5
5
  module Farce
6
6
  # An envelope can be used to wrap an unshareable value in a shareable object.
7
7
  #
8
- # Once created, the envelope can be shared between Ractors without any objects being moved or copied until the
9
- # envelope is opened.
8
+ # Once created, the envelope can be shared between Ractors. Its payload is retrieved when the envelope is opened.
10
9
  #
11
10
  # When a Ractor attempts to open an envelope, it attempts "claim" it. Some envelopes may only be claimed by a single
12
11
  # Ractor, others can be claimed by any Ractor. Claims cannot be taken back.
@@ -111,6 +110,53 @@ module Farce
111
110
  def vault_key = defined?(@vault_key) ? @vault_key : self
112
111
  end
113
112
 
113
+ # An envelope that serializes its contents with Ruby's Marshal protocol.
114
+ # The snapshot is created immediately and reconstructed once per Ractor that opens it.
115
+ # Farce containers rebuild their storage from logical snapshots.
116
+ # Contents must support Marshal. Nested shareable values may be reconstructed too.
117
+ #
118
+ # @example Transfer an Unshared map
119
+ # source = Farce::Unshared::Map.new({ jobs: [] })
120
+ # envelope = Farce::Envelope.new(source, mode: :marshal)
121
+ # envelope.value[:jobs] << :ready
122
+ # source[:jobs] # => []
123
+ class Marshal < Farce::Envelope
124
+ include Copyable
125
+
126
+ # @param value [BasicObject] the value to serialize
127
+ # @param auto_unwrap [ModeManager, nil] the manager that may automatically open this envelope
128
+ # @raise [TypeError] if the value cannot be marshaled
129
+ def initialize(value, auto_unwrap = nil)
130
+ @dump = ::Marshal.dump(value).freeze
131
+ super
132
+ end
133
+
134
+ # @api private
135
+ def marshal_dump = [1, value, auto_unwrap]
136
+
137
+ # @api private
138
+ def marshal_load(data)
139
+ value, manager = Internal::MarshalSupport.payload(data, 2)
140
+ initialize(value, manager)
141
+ Internal::Storage.ractor[self] = value
142
+ end
143
+
144
+ # (see Envelope#claim)
145
+ def claim = self
146
+
147
+ # (see Envelope#claimed?)
148
+ def claimed? = true
149
+
150
+ # (see Envelope#owned?)
151
+ def owned? = true
152
+
153
+ protected
154
+
155
+ # The bytes are created by this envelope from its caller's value.
156
+ # rubocop:disable-next Security/MarshalLoad
157
+ def retrieve = ::Marshal.load(@dump)
158
+ end
159
+
114
160
  # An envelope that moves its contents. Can only be opened by a single Ractor.
115
161
  # The value will be moved once when the envelope is created, and then once when the envelope is opened.
116
162
  #
@@ -253,7 +299,7 @@ module Farce
253
299
  #
254
300
  # @overload new(value, mode:)
255
301
  # @param value [BasicObject] The value to wrap in the envelope.
256
- # @param mode [Symbol, nil] The type of envelope to create. Can be `:move`, `:copy`, or `:local`.
302
+ # @param mode [Symbol, nil] The type of envelope to create. Can be `:move`, `:copy`, `:marshal`, or `:local`.
257
303
  # @return [Envelope] A new envelope wrapping the given value.
258
304
  #
259
305
  # @return [Envelope] A new envelope wrapping the given value.
@@ -265,10 +311,11 @@ module Farce
265
311
 
266
312
  def self.subclass_for(move: false, local: false, mode: nil)
267
313
  case mode
268
- when :move then Move
269
- when :copy then Copy
270
- when :local then Local
271
- when nil then local ? Local : (move ? Move : Copy)
314
+ when :move then Move
315
+ when :copy then Copy
316
+ when :marshal then Marshal
317
+ when :local then Local
318
+ when nil then local ? Local : (move ? Move : Copy)
272
319
  else raise ArgumentError, "invalid mode: #{mode.inspect}"
273
320
  end
274
321
  end
@@ -15,7 +15,7 @@ module Farce
15
15
 
16
16
  # @!macro modes
17
17
  # @param mode [Symbol] the default mode used to transfer values between Ractors
18
- def initialize(mode: :copy)
18
+ def initialize(mode: :smart)
19
19
  @manager = ModeManager.new(mode:)
20
20
  @exchanger = Internal::Exchanger.new
21
21
  super()
data/lib/farce/flag.rb CHANGED
@@ -23,5 +23,7 @@ module Farce
23
23
  class Flag < Internal::Flag
24
24
  include Abstract::Flag
25
25
  include Shareable::Native
26
+
27
+ private :change_signal
26
28
  end
27
29
  end
@@ -2,7 +2,7 @@
2
2
  # shareable_constant_value: literal
3
3
  # warn_indent: true
4
4
 
5
- require "farce"
5
+ require "farce" unless defined?(Farce::Abstract)
6
6
  require "active_support"
7
7
  require "active_support/core_ext"
8
8
 
@@ -3,7 +3,7 @@
3
3
  # warn_indent: true
4
4
 
5
5
  # @!group BSON Integration
6
- require "farce"
6
+ require "farce" unless defined?(Farce::Abstract)
7
7
  require "bson"
8
8
 
9
9
  module Farce
@@ -3,7 +3,7 @@
3
3
  # warn_indent: true
4
4
 
5
5
  # @!group CBOR Integration
6
- require "farce"
6
+ require "farce" unless defined?(Farce::Abstract)
7
7
  require "cbor"
8
8
 
9
9
  module Farce
@@ -3,9 +3,9 @@
3
3
  # warn_indent: true
4
4
 
5
5
  # @!group concurrent-ruby Integration
6
- require "farce"
7
- require "concurrent/map"
8
- require "concurrent/tvar"
6
+ require "farce" unless defined?(Farce::Abstract)
7
+ require "concurrent/map" unless defined?(Concurrent::Map)
8
+ require "concurrent/tvar" unless defined?(Concurrent::TVar)
9
9
 
10
10
  module Farce
11
11
  module Integrations
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+ # shareable_constant_value: literal
3
+ # warn_indent: true
4
+
5
+ # @!group Date Integration
6
+ require "farce" unless defined?(Farce::ModeManager)
7
+ require "date" unless defined?(Date)
8
+ Farce::ModeManager::SmartRule.define(Date, :make_shareable)
@@ -3,7 +3,7 @@
3
3
  # warn_indent: true
4
4
 
5
5
  # @!group Dry Types Integration
6
- require "farce"
6
+ require "farce" unless defined?(Farce::Abstract)
7
7
  require "dry/types"
8
8
 
9
9
  module Farce
@@ -70,7 +70,7 @@ module Farce
70
70
  when :shared
71
71
  return {} unless %i[vector map set atom].include?(kind)
72
72
 
73
- { mode: UNDEFINED.equal?(mode) ? :copy : mode }
73
+ { mode: UNDEFINED.equal?(mode) ? :smart : mode }
74
74
  when :local then { scope: UNDEFINED.equal?(scope) ? :ractor : scope }
75
75
  else {}
76
76
  end
@@ -101,8 +101,11 @@ module Farce
101
101
 
102
102
  if variant == :shared
103
103
  raise ArgumentError, "scope is only supported by the local variant" unless UNDEFINED.equal?(scope)
104
- raise ArgumentError, "mode :move is not supported by dry-types constructors" if mode == :move
105
- return if UNDEFINED.equal?(mode) || %i[copy local make_shareable raise shareable_copy].include?(mode)
104
+ if %i[move smart_move].include?(mode)
105
+ raise ArgumentError, "mode #{mode.inspect} is not supported by dry-types constructors"
106
+ end
107
+ allowed = %i[smart smart_raise smart_copy copy local make_shareable raise shareable_copy]
108
+ return if UNDEFINED.equal?(mode) || allowed.include?(mode)
106
109
 
107
110
  raise ArgumentError, "invalid mode: #{mode.inspect}"
108
111
  end
@@ -2,6 +2,6 @@
2
2
  # shareable_constant_value: literal
3
3
  # warn_indent: true
4
4
 
5
- require "farce"
6
- require "json" unless defined?(JSON::State)
5
+ require "farce" unless defined?(Farce::Abstract)
6
+ require "json" unless defined?(JSON::State)
7
7
  require_relative "shared/to_json"
@@ -3,7 +3,7 @@
3
3
  # warn_indent: true
4
4
 
5
5
  # @!group ActiveSupport Integration
6
- require "farce"
6
+ require "farce" unless defined?(Farce::Abstract)
7
7
  require "msgpack"
8
8
 
9
9
  module Farce
@@ -3,10 +3,9 @@
3
3
  # warn_indent: true
4
4
 
5
5
  # @!group Oj Integration
6
- require "farce"
7
- require "oj" unless defined?(Oj::VERSION)
8
-
9
- require "json" unless [].respond_to?(:to_json)
6
+ require "farce" unless defined?(Farce::Abstract)
7
+ require "oj" unless defined?(Oj::VERSION)
8
+ require "json" unless [].respond_to?(:to_json)
10
9
  require_relative "shared/to_json"
11
10
 
12
11
  module Farce
@@ -3,7 +3,7 @@
3
3
  # warn_indent: true
4
4
 
5
5
  # @!group Psych Integration
6
- require "farce"
6
+ require "farce" unless defined?(Farce::Abstract)
7
7
  require "psych" unless defined?(Psych::Coder)
8
8
 
9
9
  module Farce
@@ -3,7 +3,7 @@
3
3
  # warn_indent: true
4
4
 
5
5
  # @!group ractor-sharing Integration
6
- require "farce"
6
+ require "farce" unless defined?(Farce::Abstract)
7
7
  require "ractor/tvar"
8
8
  require "ractor/lockvar"
9
9
  require "ractor/lockhash"
@@ -3,7 +3,7 @@
3
3
  # warn_indent: true
4
4
 
5
5
  # @!group SortedSet Integration
6
- require "farce"
6
+ require "farce" unless defined?(Farce::Abstract)
7
7
  require "sorted_set"
8
8
 
9
9
  module Farce
@@ -3,7 +3,7 @@
3
3
  # warn_indent: true
4
4
 
5
5
  # @!group WeakRef Integration
6
- require "farce" unless defined?(Farce::Walker)
6
+ require "farce" unless defined?(Farce::Abstract)
7
7
  require "weakref" unless defined?(WeakRef)
8
8
 
9
9
  module Farce
@@ -21,6 +21,14 @@ module Farce
21
21
  end
22
22
  end
23
23
 
24
+ Farce::ModeManager::SmartRule.define(::WeakRef) do |value, mode_manager, mode|
25
+ ref = value.__getobj__
26
+ value = Farce::WeakRef.new(ref)
27
+ super(value, mode_manager, mode)
28
+ rescue ::WeakRef::RefError
29
+ Farce::WeakRef::RECYCLED
30
+ end
31
+
24
32
  Internal::Converter.define(::WeakRef, WeakRef) do
25
33
  def convert!(ref)
26
34
  @seen.fetch(ref) do
@@ -4,7 +4,7 @@
4
4
 
5
5
  # @!group Yajl Integration
6
6
  # Yajl 1.4.3 cannot encode or parse inside non-main native Ractors.
7
- require "farce"
8
- require "yajl" unless defined?(Yajl::Encoder)
9
- require "json" unless [].respond_to?(:to_json)
7
+ require "farce" unless defined?(Farce::Abstract)
8
+ require "yajl" unless defined?(Yajl::Encoder)
9
+ require "json" unless [].respond_to?(:to_json)
10
10
  require_relative "shared/to_json"