backports 3.20.0 → 3.22.0

Sign up to get free protection for your applications and to get access to all the features.
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