backports 3.20.1 → 3.22.1
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +6 -0
- data/Gemfile +1 -1
- data/README.md +281 -158
- data/lib/backports/2.1.0/module/singleton_class.rb +8 -0
- data/lib/backports/2.3.0/struct/dig.rb +2 -0
- data/lib/backports/2.4.0/string/unpack1.rb +7 -0
- data/lib/backports/2.5.0/dir/children.rb +4 -0
- data/lib/backports/2.5.0/dir/each_child.rb +7 -0
- data/lib/backports/2.5.0/integer/sqrt.rb +1 -1
- data/lib/backports/2.7.0/complex/{comparision.rb → comparison.rb} +0 -0
- data/lib/backports/2.7.0/enumerable/tally.rb +4 -3
- data/lib/backports/3.0.0/ractor.rb +15 -1
- data/lib/backports/3.1.0/array/intersect.rb +16 -0
- data/lib/backports/3.1.0/array.rb +3 -0
- data/lib/backports/3.1.0/class/descendants.rb +11 -0
- data/lib/backports/3.1.0/class/subclasses.rb +11 -0
- data/lib/backports/3.1.0/class.rb +3 -0
- data/lib/backports/3.1.0/enumerable/compact.rb +5 -0
- data/lib/backports/3.1.0/enumerable/tally.rb +18 -0
- data/lib/backports/3.1.0/enumerable.rb +3 -0
- data/lib/backports/3.1.0/file/dirname.rb +16 -0
- data/lib/backports/3.1.0/file.rb +3 -0
- data/lib/backports/3.1.0/match_data/match.rb +5 -0
- data/lib/backports/3.1.0/match_data/match_length.rb +6 -0
- data/lib/backports/3.1.0/match_data.rb +3 -0
- data/lib/backports/3.1.0/struct/keyword_init.rb +5 -0
- data/lib/backports/3.1.0/struct.rb +3 -0
- data/lib/backports/3.1.0.rb +3 -0
- data/lib/backports/3.1.rb +1 -0
- data/lib/backports/latest.rb +1 -1
- data/lib/backports/ractor/cloner.rb +81 -69
- data/lib/backports/ractor/errors.rb +14 -10
- data/lib/backports/{tools → ractor}/filtered_queue.rb +11 -8
- data/lib/backports/ractor/queues.rb +50 -46
- data/lib/backports/ractor/ractor.rb +224 -213
- data/lib/backports/ractor/sharing.rb +75 -71
- data/lib/backports/version.rb +1 -1
- 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
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
@
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
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
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
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
|
-
|
64
|
-
rescue
|
65
|
-
|
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
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
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
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
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
|
-
|
126
|
-
|
127
|
-
|
128
|
-
r
|
129
|
-
end
|
100
|
+
def take
|
101
|
+
ractor_outgoing_queue.pop(ack: true)
|
102
|
+
end
|
130
103
|
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
r
|
135
|
-
end
|
104
|
+
def name
|
105
|
+
@ractor_name
|
106
|
+
end
|
136
107
|
|
137
|
-
|
138
|
-
|
139
|
-
|
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
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
129
|
+
def close_incoming
|
130
|
+
r = ractor_incoming_queue.closed?
|
131
|
+
ractor_incoming_queue.close
|
132
|
+
r
|
133
|
+
end
|
145
134
|
|
146
|
-
|
147
|
-
|
148
|
-
|
135
|
+
def close_outgoing
|
136
|
+
r = ractor_outgoing_queue.closed?
|
137
|
+
ractor_outgoing_queue.close
|
138
|
+
r
|
139
|
+
end
|
149
140
|
|
150
|
-
|
151
|
-
|
152
|
-
|
141
|
+
private def receive
|
142
|
+
ractor_incoming_queue.pop
|
143
|
+
end
|
153
144
|
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
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
|
-
|
160
|
-
|
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
|
168
|
-
current.
|
154
|
+
def []=(key, value)
|
155
|
+
Ractor.current.ractor_locals[key] = value
|
169
156
|
end
|
170
|
-
alias_method :recv, :receive
|
171
157
|
|
172
|
-
|
173
|
-
|
158
|
+
# @api private
|
159
|
+
def ractor_locals
|
160
|
+
@ractor_locals ||= {}.compare_by_identity
|
174
161
|
end
|
175
162
|
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
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
|
-
|
182
|
-
|
183
|
-
|
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
|
-
|
189
|
-
|
190
|
-
|
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
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
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
|
-
|
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
|
-
|
208
|
-
|
211
|
+
def make_shareable(obj)
|
212
|
+
return obj if ractor_check_shareability?(obj, true)
|
209
213
|
|
210
|
-
|
211
|
-
|
214
|
+
raise Ractor::Error, '#freeze does not freeze object correctly'
|
215
|
+
end
|
212
216
|
|
213
|
-
|
214
|
-
|
215
|
-
|
217
|
+
def shareable?(obj)
|
218
|
+
ractor_check_shareability?(obj, false)
|
219
|
+
end
|
216
220
|
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
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
|
-
|
223
|
-
|
224
|
-
|
226
|
+
def count
|
227
|
+
::ObjectSpace.each_object(Ractor).count(&:ractor_live?)
|
228
|
+
end
|
225
229
|
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
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
|
-
|
233
|
-
|
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
|
-
|
242
|
+
# @api private
|
243
|
+
def ractor_next_id
|
244
|
+
@id ||= 0
|
245
|
+
@id += 1
|
246
|
+
end
|
239
247
|
|
240
|
-
|
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
|
-
|
247
|
-
|
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
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
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
|
-
|
258
|
-
|
267
|
+
# @api private
|
268
|
+
attr_reader :ractor_outgoing_queue, :ractor_incoming_queue, :ractor_thread
|
259
269
|
|
260
|
-
|
270
|
+
ractor_init
|
271
|
+
end
|
261
272
|
end
|
@@ -1,92 +1,96 @@
|
|
1
|
-
|
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
|
-
|
8
|
-
|
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
|
-
|
11
|
-
|
12
|
-
visited = {}
|
10
|
+
Cloner.deep_clone(val)
|
11
|
+
end
|
13
12
|
|
14
|
-
|
13
|
+
private def ractor_check_shareability?(obj, freeze_all)
|
14
|
+
ractor_shareable_self?(obj, freeze_all) do
|
15
|
+
visited = {}
|
15
16
|
|
16
|
-
|
17
|
+
return false unless ractor_shareable_parts?(obj, freeze_all, visited)
|
17
18
|
|
18
|
-
|
19
|
-
end
|
20
|
-
end
|
19
|
+
ractor_mark_set_shareable(visited)
|
21
20
|
|
22
|
-
|
23
|
-
|
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
|
-
|
38
|
-
|
39
|
-
|
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
|
-
|
44
|
-
|
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
|
-
|
47
|
-
visited.each do |key|
|
48
|
-
@ractor_shareable[key] = Ractor
|
46
|
+
true
|
49
47
|
end
|
50
|
-
end
|
51
48
|
|
52
|
-
|
53
|
-
|
54
|
-
|
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
|
-
|
74
|
-
|
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
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
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
|
data/lib/backports/version.rb
CHANGED
@@ -1,3 +1,3 @@
|
|
1
1
|
module Backports
|
2
|
-
VERSION = "3.
|
2
|
+
VERSION = "3.22.1" unless Backports.constants.include? :VERSION # the guard is against a redefinition warning that happens on Travis
|
3
3
|
end
|