backports 3.20.0 → 3.22.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.
Files changed (41) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +13 -0
  3. data/Gemfile +1 -1
  4. data/README.md +285 -158
  5. data/lib/backports/2.1.0/module/singleton_class.rb +8 -0
  6. data/lib/backports/2.3.0/struct/dig.rb +2 -0
  7. data/lib/backports/2.4.0/string/unpack1.rb +7 -0
  8. data/lib/backports/2.5.0/dir/children.rb +4 -0
  9. data/lib/backports/2.5.0/dir/each_child.rb +7 -0
  10. data/lib/backports/2.5.0/integer/sqrt.rb +1 -1
  11. data/lib/backports/2.7.0/complex/{comparision.rb → comparison.rb} +0 -0
  12. data/lib/backports/2.7.0/enumerable/tally.rb +4 -3
  13. data/lib/backports/3.0.0/ractor.rb +15 -1
  14. data/lib/backports/3.1.0/array/intersect.rb +16 -0
  15. data/lib/backports/3.1.0/array.rb +3 -0
  16. data/lib/backports/3.1.0/class/descendants.rb +11 -0
  17. data/lib/backports/3.1.0/class/subclasses.rb +11 -0
  18. data/lib/backports/3.1.0/class.rb +3 -0
  19. data/lib/backports/3.1.0/enumerable/compact.rb +5 -0
  20. data/lib/backports/3.1.0/enumerable/tally.rb +18 -0
  21. data/lib/backports/3.1.0/enumerable.rb +3 -0
  22. data/lib/backports/3.1.0/file/dirname.rb +16 -0
  23. data/lib/backports/3.1.0/file.rb +3 -0
  24. data/lib/backports/3.1.0/integer/try_convert.rb +5 -0
  25. data/lib/backports/3.1.0/integer.rb +3 -0
  26. data/lib/backports/3.1.0/match_data/match.rb +5 -0
  27. data/lib/backports/3.1.0/match_data/match_length.rb +6 -0
  28. data/lib/backports/3.1.0/match_data.rb +3 -0
  29. data/lib/backports/3.1.0/struct/keyword_init.rb +5 -0
  30. data/lib/backports/3.1.0/struct.rb +3 -0
  31. data/lib/backports/3.1.0.rb +3 -0
  32. data/lib/backports/3.1.rb +1 -0
  33. data/lib/backports/latest.rb +1 -1
  34. data/lib/backports/ractor/cloner.rb +81 -69
  35. data/lib/backports/ractor/errors.rb +14 -10
  36. data/lib/backports/{tools → ractor}/filtered_queue.rb +11 -8
  37. data/lib/backports/ractor/queues.rb +50 -46
  38. data/lib/backports/ractor/ractor.rb +225 -191
  39. data/lib/backports/ractor/sharing.rb +75 -71
  40. data/lib/backports/version.rb +1 -1
  41. metadata +25 -4
@@ -1,238 +1,272 @@
1
+ # shareable_constant_value: literal
2
+
1
3
  # Ruby 2.0+ backport of `Ractor` class
2
4
  # Extra private methods and instance variables all start with `ractor_`
3
- class Ractor
4
- require_relative '../tools/arguments'
5
-
6
- require_relative 'cloner'
7
- require_relative 'errors'
8
- require_relative 'queues'
9
- require_relative 'sharing'
10
-
11
- # Implementation notes
12
- #
13
- # Uses one `Thread` for each `Ractor`, as well as queues for communication
14
- #
15
- # The incoming queue is strict: contrary to standard queue, you can't pop from an empty closed queue.
16
- # Since standard queues return `nil` is those conditions, we wrap/unwrap `nil` values and consider
17
- # all `nil` values to be results of closed queues. `ClosedQueueError` are re-raised as `Ractor::ClosedError`
18
- #
19
- # The outgoing queue is strict and blocking. Same wrapping / raising as incoming,
20
- # with an extra queue to acknowledge when a value has been read (or if the port is closed while waiting).
21
- #
22
- # The last result is a bit tricky as it needs to be pushed on the outgoing queue but can not be blocking.
23
- # For this, we "soft close" the outgoing port.
24
-
25
- def initialize(*args, &block)
26
- @ractor_incoming_queue = IncomingQueue.new
27
- @ractor_outgoing_queue = OutgoingQueue.new
28
- raise ArgumentError, 'must be called with a block' unless block
29
-
30
- kw = args.last
31
- if kw.is_a?(Hash) && kw.size == 1 && kw.key?(:name)
32
- args.pop
33
- name = kw[:name]
34
- end
35
- @ractor_name = name && Backports.coerce_to_str(name)
36
-
37
- if Ractor.main == nil # then initializing main Ractor
38
- @ractor_thread = ::Thread.current
39
- @ractor_origin = nil
40
- @ractor_thread.thread_variable_set(:ractor, self)
41
- else
42
- @ractor_origin = caller(1, 1).first.split(':in `').first
43
-
44
- args.map! { |a| Ractor.ractor_isolate(a, false) }
45
- ractor_thread_start(args, block)
5
+ module Backports
6
+ class Ractor
7
+ require_relative '../tools/arguments'
8
+
9
+ require_relative 'cloner'
10
+ require_relative 'errors'
11
+ require_relative 'queues'
12
+ require_relative 'sharing'
13
+
14
+ RactorThreadGroups = ::ObjectSpace::WeakMap.new # ThreadGroup => Ractor
15
+ private_constant :RactorThreadGroups
16
+ # Implementation notes
17
+ #
18
+ # Uses one `Thread` for each `Ractor`, as well as queues for communication
19
+ #
20
+ # The incoming queue is strict: contrary to standard queue, you can't pop from an empty closed queue.
21
+ # Since standard queues return `nil` is those conditions, we wrap/unwrap `nil` values and consider
22
+ # all `nil` values to be results of closed queues. `ClosedQueueError` are re-raised as `Ractor::ClosedError`
23
+ #
24
+ # The outgoing queue is strict and blocking. Same wrapping / raising as incoming,
25
+ # with an extra queue to acknowledge when a value has been read (or if the port is closed while waiting).
26
+ #
27
+ # The last result is a bit tricky as it needs to be pushed on the outgoing queue but can not be blocking.
28
+ # For this, we "soft close" the outgoing port.
29
+
30
+ def initialize(*args, &block)
31
+ @ractor_incoming_queue = IncomingQueue.new
32
+ @ractor_outgoing_queue = OutgoingQueue.new
33
+ raise ::ArgumentError, 'must be called with a block' unless block
34
+
35
+ kw = args.last
36
+ if kw.is_a?(::Hash) && kw.size == 1 && kw.key?(:name)
37
+ args.pop
38
+ name = kw[:name]
39
+ end
40
+ @ractor_name = name && Backports.coerce_to_str(name)
41
+
42
+ @id = Ractor.ractor_next_id
43
+ if Ractor.main == nil # then initializing main Ractor
44
+ @ractor_thread = ::Thread.current
45
+ @ractor_origin = nil
46
+ @ractor_thread.thread_variable_set(:backports_ractor, self)
47
+ else
48
+ @ractor_origin = caller(1, 1).first.split(':in `').first
49
+
50
+ args.map! { |a| Ractor.ractor_isolate(a, false) }
51
+ ractor_thread_start(args, block)
52
+ end
46
53
  end
47
- end
48
54
 
49
- private def ractor_thread_start(args, block)
50
- Thread.new do
51
- @ractor_thread = Thread.current
52
- @ractor_thread_group = ThreadGroup.new.add(@ractor_thread)
53
- ::Thread.current.thread_variable_set(:ractor, self)
54
- result = nil
55
- begin
56
- result = instance_exec(*args, &block)
57
- rescue ::Exception => err # rubocop:disable Lint/RescueException
55
+ private def ractor_thread_start(args, block)
56
+ ::Thread.new do
57
+ @ractor_thread = ::Thread.current
58
+ @ractor_thread_group = ::ThreadGroup.new
59
+ RactorThreadGroups[@ractor_thread_group] = self
60
+ @ractor_thread_group.add(@ractor_thread)
61
+ ::Thread.current.thread_variable_set(:backports_ractor, self)
62
+ result = nil
58
63
  begin
59
- raise RemoteError, "thrown by remote Ractor: #{err.message}"
60
- rescue RemoteError => e # Hack to create exception with `cause`
61
- result = OutgoingQueue::WrappedException.new(e)
64
+ result = instance_exec(*args, &block)
65
+ rescue ::Exception => err # rubocop:disable Lint/RescueException
66
+ begin
67
+ raise RemoteError, "thrown by remote Ractor: #{err.message}"
68
+ rescue RemoteError => e # Hack to create exception with `cause`
69
+ result = OutgoingQueue::WrappedException.new(e)
70
+ end
71
+ ensure
72
+ ractor_thread_terminate(result)
62
73
  end
63
- ensure
64
- ractor_thread_terminate(result)
65
74
  end
66
75
  end
67
- end
68
76
 
69
- private def ractor_thread_terminate(result)
70
- begin
71
- ractor_outgoing_queue.push(result, ack: false) unless ractor_outgoing_queue.closed?
72
- rescue ClosedQueueError
73
- return # ignore
74
- end
75
- ractor_incoming_queue.close
76
- ractor_outgoing_queue.close(:soft)
77
- ensure
78
- # TODO: synchronize?
79
- @ractor_thread_group.list.each do |thread|
80
- thread.kill unless thread == Thread.current
77
+ private def ractor_thread_terminate(result)
78
+ begin
79
+ ractor_outgoing_queue.push(result, ack: false) unless ractor_outgoing_queue.closed?
80
+ rescue ::ClosedQueueError
81
+ return # ignore
82
+ end
83
+ ractor_incoming_queue.close
84
+ ractor_outgoing_queue.close(:soft)
85
+ ensure
86
+ # TODO: synchronize?
87
+ @ractor_thread_group.list.each do |thread|
88
+ thread.kill unless thread == Thread.current
89
+ end
81
90
  end
82
- end
83
91
 
84
- def send(obj, move: false)
85
- ractor_incoming_queue << Ractor.ractor_isolate(obj, move)
86
- self
87
- rescue ::ClosedQueueError
88
- raise ClosedError, 'The incoming-port is already closed'
89
- end
90
- alias_method :<<, :send
92
+ def send(obj, move: false)
93
+ ractor_incoming_queue << Ractor.ractor_isolate(obj, move)
94
+ self
95
+ rescue ::ClosedQueueError
96
+ raise ClosedError, 'The incoming-port is already closed'
97
+ end
98
+ alias_method :<<, :send
91
99
 
92
- def take
93
- ractor_outgoing_queue.pop(ack: true)
94
- end
100
+ def take
101
+ ractor_outgoing_queue.pop(ack: true)
102
+ end
95
103
 
96
- def name
97
- @ractor_name
98
- end
104
+ def name
105
+ @ractor_name
106
+ end
99
107
 
100
- RACTOR_STATE = {
101
- 'sleep' => 'blocking',
102
- 'run' => 'running',
103
- 'aborting' => 'aborting',
104
- false => 'terminated',
105
- nil => 'terminated',
106
- }.freeze
107
- private_constant :RACTOR_STATE
108
-
109
- def inspect
110
- state = RACTOR_STATE[@ractor_thread ? @ractor_thread.status : 'run']
111
- info = [
112
- 'Ractor:#1',
113
- name,
114
- @ractor_origin,
115
- state
116
- ].compact.join(' ')
117
-
118
- "#<#{info}>"
119
- end
108
+ RACTOR_STATE = {
109
+ 'sleep' => 'blocking',
110
+ 'run' => 'running',
111
+ 'aborting' => 'aborting',
112
+ false => 'terminated',
113
+ nil => 'terminated',
114
+ }.freeze
115
+ private_constant :RACTOR_STATE
116
+
117
+ def inspect
118
+ state = RACTOR_STATE[@ractor_thread ? @ractor_thread.status : 'run']
119
+ info = [
120
+ "Ractor:##{@id}",
121
+ name,
122
+ @ractor_origin,
123
+ state,
124
+ ].compact.join(' ')
125
+
126
+ "#<#{info}>"
127
+ end
120
128
 
121
- def close_incoming
122
- r = ractor_incoming_queue.closed?
123
- ractor_incoming_queue.close
124
- r
125
- end
129
+ def close_incoming
130
+ r = ractor_incoming_queue.closed?
131
+ ractor_incoming_queue.close
132
+ r
133
+ end
126
134
 
127
- def close_outgoing
128
- r = ractor_outgoing_queue.closed?
129
- ractor_outgoing_queue.close
130
- r
131
- end
135
+ def close_outgoing
136
+ r = ractor_outgoing_queue.closed?
137
+ ractor_outgoing_queue.close
138
+ r
139
+ end
132
140
 
133
- private def receive
134
- ractor_incoming_queue.pop
135
- end
141
+ private def receive
142
+ ractor_incoming_queue.pop
143
+ end
136
144
 
137
- private def receive_if(&block)
138
- raise ArgumentError, 'no block given' unless block
139
- ractor_incoming_queue.pop(&block)
140
- end
145
+ private def receive_if(&block)
146
+ raise ::ArgumentError, 'no block given' unless block
147
+ ractor_incoming_queue.pop(&block)
148
+ end
141
149
 
142
- class << self
143
- def yield(value, move: false)
144
- value = ractor_isolate(value, move)
145
- current.ractor_outgoing_queue.push(value, ack: true)
146
- rescue ClosedQueueError
147
- raise ClosedError, 'The outgoing-port is already closed'
150
+ def [](key)
151
+ Ractor.current.ractor_locals[key]
148
152
  end
149
153
 
150
- def receive
151
- current.__send__(:receive)
154
+ def []=(key, value)
155
+ Ractor.current.ractor_locals[key] = value
152
156
  end
153
- alias_method :recv, :receive
154
157
 
155
- def receive_if(&block)
156
- current.__send__(:receive_if, &block)
158
+ # @api private
159
+ def ractor_locals
160
+ @ractor_locals ||= {}.compare_by_identity
157
161
  end
158
162
 
159
- def select(*ractors, yield_value: not_given = true, move: false)
160
- cur = Ractor.current
161
- queues = ractors.map do |r|
162
- r == cur ? r.ractor_incoming_queue : r.ractor_outgoing_queue
163
+ class << self
164
+ def yield(value, move: false)
165
+ value = ractor_isolate(value, move)
166
+ current.ractor_outgoing_queue.push(value, ack: true)
167
+ rescue ::ClosedQueueError
168
+ raise ClosedError, 'The outgoing-port is already closed'
163
169
  end
164
- if !not_given
165
- out = current.ractor_outgoing_queue
166
- yield_value = ractor_isolate(yield_value, move)
167
- elsif ractors.empty?
168
- raise ArgumentError, 'specify at least one ractor or `yield_value`'
170
+
171
+ def receive
172
+ current.__send__(:receive)
169
173
  end
174
+ alias_method :recv, :receive
170
175
 
171
- while true # rubocop:disable Style/InfiniteLoop
172
- # Don't `loop`, in case of `ClosedError` (not that there should be any)
173
- queues.each_with_index do |q, i|
174
- q.pop_non_blocking do |val|
175
- r = ractors[i]
176
- return [r == cur ? :receive : r, val]
177
- end
178
- end
176
+ def receive_if(&block)
177
+ current.__send__(:receive_if, &block)
178
+ end
179
179
 
180
- if out && out.num_waiting > 0
181
- # Not quite atomic...
182
- out.push(yield_value, ack: true)
183
- return [:yield, nil]
180
+ def select(*ractors, yield_value: not_given = true, move: false)
181
+ cur = Ractor.current
182
+ queues = ractors.map do |r|
183
+ r == cur ? r.ractor_incoming_queue : r.ractor_outgoing_queue
184
184
  end
185
+ if !not_given
186
+ out = current.ractor_outgoing_queue
187
+ yield_value = ractor_isolate(yield_value, move)
188
+ elsif ractors.empty?
189
+ raise ::ArgumentError, 'specify at least one ractor or `yield_value`'
190
+ end
191
+
192
+ while true # rubocop:disable Style/InfiniteLoop
193
+ # Don't `loop`, in case of `ClosedError` (not that there should be any)
194
+ queues.each_with_index do |q, i|
195
+ q.pop_non_blocking do |val|
196
+ r = ractors[i]
197
+ return [r == cur ? :receive : r, val]
198
+ end
199
+ end
200
+
201
+ if out && out.num_waiting > 0
202
+ # Not quite atomic...
203
+ out.push(yield_value, ack: true)
204
+ return [:yield, nil]
205
+ end
185
206
 
186
- sleep(0.001)
207
+ sleep(0.001)
208
+ end
187
209
  end
188
- end
189
210
 
190
- def make_shareable(obj)
191
- return obj if ractor_check_shareability?(obj, true)
211
+ def make_shareable(obj)
212
+ return obj if ractor_check_shareability?(obj, true)
192
213
 
193
- raise Ractor::Error, '#freeze does not freeze object correctly'
194
- end
214
+ raise Ractor::Error, '#freeze does not freeze object correctly'
215
+ end
195
216
 
196
- def shareable?(obj)
197
- ractor_check_shareability?(obj, false)
198
- end
217
+ def shareable?(obj)
218
+ ractor_check_shareability?(obj, false)
219
+ end
199
220
 
200
- def current
201
- Thread.current.thread_variable_get(:ractor)
202
- end
221
+ def current
222
+ ::Thread.current.thread_variable_get(:backports_ractor) ||
223
+ ::Thread.current.thread_variable_set(:backports_ractor, ractor_find_current)
224
+ end
203
225
 
204
- def count
205
- ObjectSpace.each_object(Ractor).count(&:ractor_live?)
206
- end
226
+ def count
227
+ ::ObjectSpace.each_object(Ractor).count(&:ractor_live?)
228
+ end
207
229
 
208
- # @api private
209
- def ractor_reset
210
- ObjectSpace.each_object(Ractor).each do |r|
211
- next if r == Ractor.current
212
- next unless (th = r.ractor_thread)
230
+ # @api private
231
+ def ractor_reset
232
+ ::ObjectSpace.each_object(Ractor).each do |r|
233
+ next if r == Ractor.current
234
+ next unless (th = r.ractor_thread)
213
235
 
214
- th.kill
215
- th.join
236
+ th.kill
237
+ th.join
238
+ end
239
+ Ractor.current.ractor_incoming_queue.clear
216
240
  end
217
- Ractor.current.ractor_incoming_queue.clear
218
- end
219
241
 
220
- attr_reader :main
242
+ # @api private
243
+ def ractor_next_id
244
+ @id ||= 0
245
+ @id += 1
246
+ end
247
+
248
+ attr_reader :main
249
+
250
+ private def ractor_init
251
+ @ractor_shareable = ::ObjectSpace::WeakMap.new
252
+ @main = Ractor.new { nil }
253
+ RactorThreadGroups[::ThreadGroup::Default] = @main
254
+ end
221
255
 
222
- private def ractor_init
223
- @ractor_shareable = ::ObjectSpace::WeakMap.new
224
- @main = Ractor.new { nil }
256
+ private def ractor_find_current
257
+ RactorThreadGroups[Thread.current.group]
258
+ end
225
259
  end
226
- end
227
260
 
228
- # @api private
229
- def ractor_live?
230
- !defined?(@ractor_thread) || # May happen if `count` is called from another thread before `initialize` has completed
231
- @ractor_thread.status
232
- end
261
+ # @api private
262
+ def ractor_live?
263
+ !defined?(@ractor_thread) || # May happen if `count` is called from another thread before `initialize` has completed
264
+ @ractor_thread.status
265
+ end
233
266
 
234
- # @api private
235
- attr_reader :ractor_outgoing_queue, :ractor_incoming_queue, :ractor_thread
267
+ # @api private
268
+ attr_reader :ractor_outgoing_queue, :ractor_incoming_queue, :ractor_thread
236
269
 
237
- ractor_init
270
+ ractor_init
271
+ end
238
272
  end
@@ -1,92 +1,96 @@
1
- class Ractor
2
- class << self
3
- # @api private
4
- def ractor_isolate(val, move = false)
5
- return val if move
1
+ # shareable_constant_value: literal
6
2
 
7
- Cloner.deep_clone(val)
8
- end
3
+ module Backports
4
+ class Ractor
5
+ class << self
6
+ # @api private
7
+ def ractor_isolate(val, move = false)
8
+ return val if move
9
9
 
10
- private def ractor_check_shareability?(obj, freeze_all)
11
- ractor_shareable_self?(obj, freeze_all) do
12
- visited = {}
10
+ Cloner.deep_clone(val)
11
+ end
13
12
 
14
- return false unless ractor_shareable_parts?(obj, freeze_all, visited)
13
+ private def ractor_check_shareability?(obj, freeze_all)
14
+ ractor_shareable_self?(obj, freeze_all) do
15
+ visited = {}
15
16
 
16
- ractor_mark_set_shareable(visited)
17
+ return false unless ractor_shareable_parts?(obj, freeze_all, visited)
17
18
 
18
- true
19
- end
20
- end
19
+ ractor_mark_set_shareable(visited)
21
20
 
22
- # yield if shareability can't be determined without looking at its parts
23
- def ractor_shareable_self?(obj, freeze_all)
24
- return true if @ractor_shareable.key?(obj)
25
- return true if ractor_shareable_by_nature?(obj, freeze_all)
26
- if obj.frozen? || (freeze_all && obj.freeze)
27
- yield
28
- else
29
- false
21
+ true
22
+ end
30
23
  end
31
- end
32
-
33
- private def ractor_shareable_parts?(obj, freeze_all, visited)
34
- return true if visited.key?(obj)
35
- visited[obj] = true
36
24
 
37
- ractor_traverse(obj) do |part|
38
- return false unless ractor_shareable_self?(part, freeze_all) do
39
- ractor_shareable_parts?(part, freeze_all, visited)
25
+ # yield if shareability can't be determined without looking at its parts
26
+ def ractor_shareable_self?(obj, freeze_all)
27
+ return true if @ractor_shareable.key?(obj)
28
+ return true if ractor_shareable_by_nature?(obj, freeze_all)
29
+ if obj.frozen? || (freeze_all && obj.freeze)
30
+ yield
31
+ else
32
+ false
40
33
  end
41
34
  end
42
35
 
43
- true
44
- end
36
+ private def ractor_shareable_parts?(obj, freeze_all, visited)
37
+ return true if visited.key?(obj)
38
+ visited[obj] = true
39
+
40
+ ractor_traverse(obj) do |part|
41
+ return false unless ractor_shareable_self?(part, freeze_all) do
42
+ ractor_shareable_parts?(part, freeze_all, visited)
43
+ end
44
+ end
45
45
 
46
- def ractor_mark_set_shareable(visited)
47
- visited.each do |key|
48
- @ractor_shareable[key] = Ractor
46
+ true
49
47
  end
50
- end
51
48
 
52
- private def ractor_traverse(obj, &block)
53
- case obj
54
- when ::Hash
55
- Hash obj.default
56
- yield obj.default_proc
57
- obj.each do |key, value|
58
- yield key
59
- yield value
49
+ def ractor_mark_set_shareable(visited)
50
+ visited.each do |key|
51
+ @ractor_shareable[key] = Ractor
60
52
  end
61
- when ::Range
62
- yield obj.begin
63
- yield obj.end
64
- when ::Array, ::Struct
65
- obj.each(&block)
66
- when ::Complex
67
- yield obj.real
68
- yield obj.imaginary
69
- when ::Rational
70
- yield obj.numerator
71
- yield obj.denominator
72
53
  end
73
- obj.instance_variables.each do |var|
74
- yield obj.instance_variable_get(var)
54
+
55
+ private def ractor_traverse(obj, &block)
56
+ case obj
57
+ when ::Hash
58
+ Hash obj.default
59
+ yield obj.default_proc
60
+ obj.each do |key, value|
61
+ yield key
62
+ yield value
63
+ end
64
+ when ::Range
65
+ yield obj.begin
66
+ yield obj.end
67
+ when ::Array, ::Struct
68
+ obj.each(&block)
69
+ when ::Complex
70
+ yield obj.real
71
+ yield obj.imaginary
72
+ when ::Rational
73
+ yield obj.numerator
74
+ yield obj.denominator
75
+ end
76
+ obj.instance_variables.each do |var|
77
+ yield obj.instance_variable_get(var)
78
+ end
75
79
  end
76
- end
77
80
 
78
- private def ractor_shareable_by_nature?(obj, freeze_all)
79
- case obj
80
- when ::Module, ::Ractor
81
- true
82
- when ::Regexp, ::Range, ::Numeric
83
- !freeze_all # Assume that these are literals that would have been frozen in 3.0
84
- # unless we're making them shareable, in which case we might as well
85
- # freeze them for real.
86
- when ::Symbol, false, true, nil # Were only frozen in Ruby 2.3+
87
- true
88
- else
89
- false
81
+ private def ractor_shareable_by_nature?(obj, freeze_all)
82
+ case obj
83
+ when ::Module, Ractor
84
+ true
85
+ when ::Regexp, ::Range, ::Numeric
86
+ !freeze_all # Assume that these are literals that would have been frozen in 3.0
87
+ # unless we're making them shareable, in which case we might as well
88
+ # freeze them for real.
89
+ when ::Symbol, false, true, nil # Were only frozen in Ruby 2.3+
90
+ true
91
+ else
92
+ false
93
+ end
90
94
  end
91
95
  end
92
96
  end
@@ -1,3 +1,3 @@
1
1
  module Backports
2
- VERSION = "3.20.0" unless Backports.constants.include? :VERSION # the guard is against a redefinition warning that happens on Travis
2
+ VERSION = "3.22.0" unless Backports.constants.include? :VERSION # the guard is against a redefinition warning that happens on Travis
3
3
  end