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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +13 -0
- data/Gemfile +1 -1
- data/README.md +285 -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/integer/try_convert.rb +5 -0
- data/lib/backports/3.1.0/integer.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 +225 -191
- data/lib/backports/ractor/sharing.rb +75 -71
- data/lib/backports/version.rb +1 -1
- 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
|
-
|
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
|
-
|
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
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
result =
|
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
|
-
|
60
|
-
rescue
|
61
|
-
|
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
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
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
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
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
|
-
|
93
|
-
|
94
|
-
|
100
|
+
def take
|
101
|
+
ractor_outgoing_queue.pop(ack: true)
|
102
|
+
end
|
95
103
|
|
96
|
-
|
97
|
-
|
98
|
-
|
104
|
+
def name
|
105
|
+
@ractor_name
|
106
|
+
end
|
99
107
|
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
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
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
129
|
+
def close_incoming
|
130
|
+
r = ractor_incoming_queue.closed?
|
131
|
+
ractor_incoming_queue.close
|
132
|
+
r
|
133
|
+
end
|
126
134
|
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
135
|
+
def close_outgoing
|
136
|
+
r = ractor_outgoing_queue.closed?
|
137
|
+
ractor_outgoing_queue.close
|
138
|
+
r
|
139
|
+
end
|
132
140
|
|
133
|
-
|
134
|
-
|
135
|
-
|
141
|
+
private def receive
|
142
|
+
ractor_incoming_queue.pop
|
143
|
+
end
|
136
144
|
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
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
|
-
|
143
|
-
|
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
|
151
|
-
current.
|
154
|
+
def []=(key, value)
|
155
|
+
Ractor.current.ractor_locals[key] = value
|
152
156
|
end
|
153
|
-
alias_method :recv, :receive
|
154
157
|
|
155
|
-
|
156
|
-
|
158
|
+
# @api private
|
159
|
+
def ractor_locals
|
160
|
+
@ractor_locals ||= {}.compare_by_identity
|
157
161
|
end
|
158
162
|
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
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
|
-
|
165
|
-
|
166
|
-
|
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
|
-
|
172
|
-
|
173
|
-
|
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
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
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
|
-
|
207
|
+
sleep(0.001)
|
208
|
+
end
|
187
209
|
end
|
188
|
-
end
|
189
210
|
|
190
|
-
|
191
|
-
|
211
|
+
def make_shareable(obj)
|
212
|
+
return obj if ractor_check_shareability?(obj, true)
|
192
213
|
|
193
|
-
|
194
|
-
|
214
|
+
raise Ractor::Error, '#freeze does not freeze object correctly'
|
215
|
+
end
|
195
216
|
|
196
|
-
|
197
|
-
|
198
|
-
|
217
|
+
def shareable?(obj)
|
218
|
+
ractor_check_shareability?(obj, false)
|
219
|
+
end
|
199
220
|
|
200
|
-
|
201
|
-
|
202
|
-
|
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
|
-
|
205
|
-
|
206
|
-
|
226
|
+
def count
|
227
|
+
::ObjectSpace.each_object(Ractor).count(&:ractor_live?)
|
228
|
+
end
|
207
229
|
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
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
|
-
|
215
|
-
|
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
|
-
|
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
|
-
|
223
|
-
|
224
|
-
|
256
|
+
private def ractor_find_current
|
257
|
+
RactorThreadGroups[Thread.current.group]
|
258
|
+
end
|
225
259
|
end
|
226
|
-
end
|
227
260
|
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
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
|
-
|
235
|
-
|
267
|
+
# @api private
|
268
|
+
attr_reader :ractor_outgoing_queue, :ractor_incoming_queue, :ractor_thread
|
236
269
|
|
237
|
-
|
270
|
+
ractor_init
|
271
|
+
end
|
238
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.0" unless Backports.constants.include? :VERSION # the guard is against a redefinition warning that happens on Travis
|
3
3
|
end
|