backports 3.20.1 → 3.22.1

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