backports 3.18.0 → 3.23.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (70) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +435 -292
  3. data/Gemfile +3 -16
  4. data/README.md +306 -156
  5. data/backports.gemspec +1 -1
  6. data/lib/backports/2.0.0.rb +1 -1
  7. data/lib/backports/2.1.0/module/singleton_class.rb +8 -0
  8. data/lib/backports/2.1.0.rb +1 -1
  9. data/lib/backports/2.2.0/string/unicode_normalize.rb +3 -3
  10. data/lib/backports/2.2.0.rb +1 -1
  11. data/lib/backports/2.3.0/queue/close.rb +48 -0
  12. data/lib/backports/2.3.0/string.rb +3 -0
  13. data/lib/backports/2.3.0/struct/dig.rb +2 -0
  14. data/lib/backports/2.3.0.rb +1 -1
  15. data/lib/backports/2.4.0/bignum/dup.rb +5 -0
  16. data/lib/backports/2.4.0/bignum.rb +3 -0
  17. data/lib/backports/2.4.0/string/unpack1.rb +7 -0
  18. data/lib/backports/2.4.0.rb +1 -1
  19. data/lib/backports/2.5.0/dir/children.rb +4 -0
  20. data/lib/backports/2.5.0/dir/each_child.rb +7 -0
  21. data/lib/backports/2.5.0/hash/transform_keys.rb +10 -3
  22. data/lib/backports/2.5.0/string/undump.rb +2 -2
  23. data/lib/backports/2.5.0.rb +1 -1
  24. data/lib/backports/2.5.rb +1 -1
  25. data/lib/backports/2.6.0/enumerable/chain.rb +2 -0
  26. data/lib/backports/2.6.0.rb +2 -2
  27. data/lib/backports/2.6.rb +1 -1
  28. data/lib/backports/2.7.0/complex/{comparision.rb → comparison.rb} +0 -0
  29. data/lib/backports/2.7.0/enumerable/tally.rb +4 -3
  30. data/lib/backports/2.7.0/symbol/end_with.rb +9 -0
  31. data/lib/backports/2.7.0/symbol.rb +3 -0
  32. data/lib/backports/2.7.0.rb +2 -2
  33. data/lib/backports/3.0.0/env/except.rb +10 -0
  34. data/lib/backports/3.0.0/env.rb +3 -0
  35. data/lib/backports/3.0.0/hash/except.rb +10 -0
  36. data/lib/backports/3.0.0/hash/transform_keys.rb +48 -0
  37. data/lib/backports/3.0.0/hash.rb +3 -0
  38. data/lib/backports/3.0.0/ractor.rb +19 -0
  39. data/lib/backports/3.0.0/symbol/name.rb +21 -0
  40. data/lib/backports/3.0.0/symbol.rb +3 -0
  41. data/lib/backports/3.0.0.rb +3 -0
  42. data/lib/backports/3.0.rb +1 -0
  43. data/lib/backports/3.1.0/array/intersect.rb +16 -0
  44. data/lib/backports/3.1.0/array.rb +3 -0
  45. data/lib/backports/3.1.0/class/descendants.rb +11 -0
  46. data/lib/backports/3.1.0/class/subclasses.rb +11 -0
  47. data/lib/backports/3.1.0/class.rb +3 -0
  48. data/lib/backports/3.1.0/enumerable/compact.rb +5 -0
  49. data/lib/backports/3.1.0/enumerable/tally.rb +18 -0
  50. data/lib/backports/3.1.0/enumerable.rb +3 -0
  51. data/lib/backports/3.1.0/file/dirname.rb +16 -0
  52. data/lib/backports/3.1.0/file.rb +3 -0
  53. data/lib/backports/3.1.0/integer/try_convert.rb +7 -0
  54. data/lib/backports/3.1.0/integer.rb +3 -0
  55. data/lib/backports/3.1.0/match_data/match.rb +5 -0
  56. data/lib/backports/3.1.0/match_data/match_length.rb +6 -0
  57. data/lib/backports/3.1.0/match_data.rb +3 -0
  58. data/lib/backports/3.1.0/struct/keyword_init.rb +5 -0
  59. data/lib/backports/3.1.0/struct.rb +3 -0
  60. data/lib/backports/3.1.0.rb +3 -0
  61. data/lib/backports/3.1.rb +1 -0
  62. data/lib/backports/latest.rb +1 -1
  63. data/lib/backports/ractor/cloner.rb +103 -0
  64. data/lib/backports/ractor/errors.rb +20 -0
  65. data/lib/backports/ractor/filtered_queue.rb +205 -0
  66. data/lib/backports/ractor/queues.rb +66 -0
  67. data/lib/backports/ractor/ractor.rb +272 -0
  68. data/lib/backports/ractor/sharing.rb +97 -0
  69. data/lib/backports/version.rb +1 -1
  70. metadata +51 -8
@@ -0,0 +1,103 @@
1
+ # shareable_constant_value: literal
2
+
3
+ using ::RubyNext if defined?(::RubyNext)
4
+
5
+ module Backports
6
+ class Ractor
7
+ class Cloner
8
+ class << self
9
+ def deep_clone(obj)
10
+ return obj if Ractor.ractor_shareable_self?(obj, false) { false }
11
+
12
+ new.deep_clone(obj)
13
+ end
14
+
15
+ private :new
16
+ end
17
+
18
+ def initialize
19
+ @processed = {}.compare_by_identity
20
+ @changed = nil
21
+ end
22
+
23
+ def deep_clone(obj)
24
+ result = process(obj) do |r|
25
+ copy_contents(r)
26
+ end
27
+ return result if result
28
+
29
+ Ractor.ractor_mark_set_shareable(@processed)
30
+ obj
31
+ end
32
+
33
+ # Yields a deep copy.
34
+ # If no deep copy is needed, `obj` is returned and
35
+ # nothing is yielded
36
+ private def clone_deeper(obj)
37
+ return obj if Ractor.ractor_shareable_self?(obj, false) { false }
38
+
39
+ result = process(obj) do |r|
40
+ copy_contents(r)
41
+ end
42
+ return obj unless result
43
+
44
+ yield result if block_given?
45
+ result
46
+ end
47
+
48
+ # Yields if `obj` is a new structure
49
+ # Returns the deep copy, or `false` if no deep copy is needed
50
+ private def process(obj)
51
+ @processed.fetch(obj) do
52
+ # For recursive structures, assume that we'll need a duplicate.
53
+ # If that's not the case, we will have duplicated the whole structure
54
+ # for nothing...
55
+ @processed[obj] = result = obj.dup
56
+ changed = track_change { yield result }
57
+ return false if obj.frozen? && !changed
58
+
59
+ @changed = true
60
+ result.freeze if obj.frozen?
61
+
62
+ result
63
+ end
64
+ end
65
+
66
+ # returns if the block called `deep clone` and that the deep copy was needed
67
+ private def track_change
68
+ prev = @changed
69
+ @changed = false
70
+ yield
71
+ @changed
72
+ ensure
73
+ @changed = prev
74
+ end
75
+
76
+ # modifies in place `obj` by calling `deep clone` on its contents
77
+ private def copy_contents(obj)
78
+ case obj
79
+ when ::Hash
80
+ if obj.default
81
+ clone_deeper(obj.default) do |copy|
82
+ obj.default = copy
83
+ end
84
+ end
85
+ obj.transform_keys! { |key| clone_deeper(key) }
86
+ obj.transform_values! { |value| clone_deeper(value) }
87
+ when ::Array
88
+ obj.map! { |item| clone_deeper(item) }
89
+ when ::Struct
90
+ obj.each_pair do |key, item|
91
+ clone_deeper(item) { |copy| obj[key] = copy }
92
+ end
93
+ end
94
+ obj.instance_variables.each do |var|
95
+ clone_deeper(obj.instance_variable_get(var)) do |copy|
96
+ obj.instance_variable_set(var, copy)
97
+ end
98
+ end
99
+ end
100
+ end
101
+ private_constant :Cloner
102
+ end
103
+ end
@@ -0,0 +1,20 @@
1
+ # shareable_constant_value: literal
2
+
3
+ module Backports
4
+ class Ractor
5
+ class ClosedError < ::StopIteration
6
+ end
7
+
8
+ class Error < ::StandardError
9
+ end
10
+
11
+ class RemoteError < Error
12
+ attr_reader :ractor
13
+
14
+ def initialize(message = nil)
15
+ @ractor = Ractor.current
16
+ super
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,205 @@
1
+ # shareable_constant_value: literal
2
+
3
+ module Backports
4
+ # Like ::Queue, but with
5
+ # - filtering
6
+ # - timeout
7
+ # - raises on closed queues
8
+ #
9
+ # Independent from other Ractor related backports.
10
+ class FilteredQueue
11
+ CONSUME_ON_ESCAPE = true
12
+
13
+ class ClosedQueueError < ::ClosedQueueError
14
+ end
15
+
16
+ class TimeoutError < ::ThreadError
17
+ end
18
+
19
+ class Message
20
+ # Not using Struct as we want comparision by identity
21
+ attr_reader :value
22
+ attr_accessor :reserved
23
+
24
+ def initialize(value)
25
+ @value = value
26
+ @reserved = false
27
+ end
28
+ end
29
+ private_constant :Message
30
+
31
+ attr_reader :num_waiting
32
+
33
+ # Timeout processing based on https://spin.atomicobject.com/2017/06/28/queue-pop-with-timeout-fixed/
34
+ def initialize
35
+ @mutex = ::Mutex.new
36
+ @queue = []
37
+ @closed = false
38
+ @received = ::ConditionVariable.new
39
+ @num_waiting = 0
40
+ end
41
+
42
+ def close
43
+ @mutex.synchronize do
44
+ @closed = true
45
+ @received.broadcast
46
+ end
47
+ self
48
+ end
49
+
50
+ def closed?
51
+ @closed
52
+ end
53
+
54
+ def <<(x)
55
+ @mutex.synchronize do
56
+ ensure_open
57
+ @queue << Message.new(x)
58
+ @received.signal
59
+ end
60
+ self
61
+ end
62
+ alias_method :push, :<<
63
+
64
+ def clear
65
+ @mutex.synchronize do
66
+ @queue.clear
67
+ end
68
+ self
69
+ end
70
+
71
+ def pop(timeout: nil, &block)
72
+ msg = nil
73
+ exclude = [] if block # exclusion list of messages rejected by this call
74
+ timeout_time = timeout + Time.now.to_f if timeout
75
+ while true do # rubocop:disable Style/InfiniteLoop, Style/WhileUntilDo
76
+ @mutex.synchronize do
77
+ reenter if reentrant?
78
+ msg = acquire!(timeout_time, exclude)
79
+ return consume!(msg).value unless block
80
+ end
81
+ return msg.value if filter?(msg, &block)
82
+ end
83
+ end
84
+
85
+ def empty?
86
+ avail = @mutex.synchronize do
87
+ available!
88
+ end
89
+
90
+ !avail
91
+ end
92
+
93
+ protected def timeout_value
94
+ raise self.class::TimeoutError, "timeout elapsed"
95
+ end
96
+
97
+ protected def closed_queue_value
98
+ ensure_open
99
+ end
100
+
101
+ # @return if outer message should be consumed or not
102
+ protected def reenter
103
+ true
104
+ end
105
+
106
+ ### private section
107
+ #
108
+ # bang methods require synchronization
109
+
110
+ # @returns:
111
+ # * true if message consumed (block result truthy or due to reentrant call)
112
+ # * false if rejected
113
+ private def filter?(msg)
114
+ consume = self.class::CONSUME_ON_ESCAPE
115
+ begin
116
+ reentered = consume_on_reentry(msg) do
117
+ consume = !!(yield msg.value)
118
+ end
119
+ reentered ? reenter : consume
120
+ ensure
121
+ commit(msg, consume) unless reentered
122
+ end
123
+ end
124
+
125
+ # @returns msg
126
+ private def consume!(msg)
127
+ @queue.delete(msg)
128
+ end
129
+
130
+ private def reject!(msg)
131
+ msg.reserved = false
132
+ @received.broadcast
133
+ end
134
+
135
+ private def commit(msg, consume)
136
+ @mutex.synchronize do
137
+ if consume
138
+ consume!(msg)
139
+ else
140
+ reject!(msg)
141
+ end
142
+ end
143
+ end
144
+
145
+ private def consume_on_reentry(msg)
146
+ q_map = current_filtered_queues
147
+ if (outer_msg = q_map[self])
148
+ commit(outer_msg, reenter)
149
+ end
150
+ q_map[self] = msg
151
+ begin
152
+ yield
153
+ ensure
154
+ reentered = !q_map.delete(self)
155
+ end
156
+ reentered
157
+ end
158
+
159
+ private def reentrant?
160
+ !!current_filtered_queues[self]
161
+ end
162
+
163
+ # @returns Hash { FilteredQueue => Message }
164
+ private def current_filtered_queues
165
+ t = Thread.current
166
+ t.thread_variable_get(:backports_currently_filtered_queues) or
167
+ t.thread_variable_set(:backports_currently_filtered_queues, {}.compare_by_identity)
168
+ end
169
+
170
+ # private methods assume @mutex synchonized
171
+ # adds to exclude list
172
+ private def acquire!(timeout_time, exclude = nil)
173
+ while true do # rubocop:disable Style/InfiniteLoop, Style/WhileUntilDo
174
+ if (msg = available!(exclude))
175
+ msg.reserved = true
176
+ exclude << msg if exclude
177
+ return msg
178
+ end
179
+ return closed_queue_value if @closed
180
+ # wait for element or timeout
181
+ if timeout_time
182
+ remaining_time = timeout_time - ::Time.now.to_f
183
+ return timeout_value if remaining_time <= 0
184
+ end
185
+ begin
186
+ @num_waiting += 1
187
+ @received.wait(@mutex, remaining_time)
188
+ ensure
189
+ @num_waiting -= 1
190
+ end
191
+ end
192
+ end
193
+
194
+ private def available!(exclude = nil)
195
+ @queue.find do |msg|
196
+ next if exclude && exclude.include?(msg)
197
+ !msg.reserved
198
+ end
199
+ end
200
+
201
+ private def ensure_open
202
+ raise self.class::ClosedQueueError, 'queue closed' if @closed
203
+ end
204
+ end
205
+ end
@@ -0,0 +1,66 @@
1
+ # shareable_constant_value: literal
2
+
3
+ require_relative 'filtered_queue'
4
+
5
+ module Backports
6
+ class Ractor
7
+ # Standard ::Queue but raises if popping and closed
8
+ class BaseQueue < FilteredQueue
9
+ ClosedQueueError = Ractor::ClosedError
10
+
11
+ # yields message (if any)
12
+ def pop_non_blocking
13
+ yield pop(timeout: 0)
14
+ rescue TimeoutError
15
+ nil
16
+ end
17
+ end
18
+
19
+ class IncomingQueue < BaseQueue
20
+ TYPE = :incoming
21
+
22
+ protected def reenter
23
+ raise Ractor::Error, 'Can not reenter'
24
+ end
25
+ end
26
+
27
+ # * Wraps exception
28
+ # * Add `ack: ` to push (blocking)
29
+ class OutgoingQueue < BaseQueue
30
+ TYPE = :outgoing
31
+
32
+ WrappedException = ::Struct.new(:exception, :ractor)
33
+
34
+ def initialize
35
+ @ack_queue = ::Queue.new
36
+ super
37
+ end
38
+
39
+ def pop(timeout: nil, ack: true)
40
+ r = super(timeout: timeout)
41
+ @ack_queue << :done if ack
42
+ raise r.exception if WrappedException === r
43
+
44
+ r
45
+ end
46
+
47
+ def close(how = :hard)
48
+ super()
49
+ return if how == :soft
50
+
51
+ clear
52
+ @ack_queue.close
53
+ end
54
+
55
+ def push(obj, ack:)
56
+ super(obj)
57
+ if ack
58
+ r = @ack_queue.pop # block until popped
59
+ raise ClosedError, "The #{self.class::TYPE}-port is already closed" unless r == :done
60
+ end
61
+ self
62
+ end
63
+ end
64
+ private_constant :BaseQueue, :OutgoingQueue, :IncomingQueue
65
+ end
66
+ end
@@ -0,0 +1,272 @@
1
+ # shareable_constant_value: literal
2
+
3
+ # Ruby 2.0+ backport of `Ractor` class
4
+ # Extra private methods and instance variables all start with `ractor_`
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
53
+ end
54
+
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
63
+ begin
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)
73
+ end
74
+ end
75
+ end
76
+
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
90
+ end
91
+
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
99
+
100
+ def take
101
+ ractor_outgoing_queue.pop(ack: true)
102
+ end
103
+
104
+ def name
105
+ @ractor_name
106
+ end
107
+
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
128
+
129
+ def close_incoming
130
+ r = ractor_incoming_queue.closed?
131
+ ractor_incoming_queue.close
132
+ r
133
+ end
134
+
135
+ def close_outgoing
136
+ r = ractor_outgoing_queue.closed?
137
+ ractor_outgoing_queue.close
138
+ r
139
+ end
140
+
141
+ private def receive
142
+ ractor_incoming_queue.pop
143
+ end
144
+
145
+ private def receive_if(&block)
146
+ raise ::ArgumentError, 'no block given' unless block
147
+ ractor_incoming_queue.pop(&block)
148
+ end
149
+
150
+ def [](key)
151
+ Ractor.current.ractor_locals[key]
152
+ end
153
+
154
+ def []=(key, value)
155
+ Ractor.current.ractor_locals[key] = value
156
+ end
157
+
158
+ # @api private
159
+ def ractor_locals
160
+ @ractor_locals ||= {}.compare_by_identity
161
+ end
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'
169
+ end
170
+
171
+ def receive
172
+ current.__send__(:receive)
173
+ end
174
+ alias_method :recv, :receive
175
+
176
+ def receive_if(&block)
177
+ current.__send__(:receive_if, &block)
178
+ end
179
+
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`'
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
206
+
207
+ sleep(0.001)
208
+ end
209
+ end
210
+
211
+ def make_shareable(obj)
212
+ return obj if ractor_check_shareability?(obj, true)
213
+
214
+ raise Ractor::Error, '#freeze does not freeze object correctly'
215
+ end
216
+
217
+ def shareable?(obj)
218
+ ractor_check_shareability?(obj, false)
219
+ end
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
225
+
226
+ def count
227
+ ::ObjectSpace.each_object(Ractor).count(&:ractor_live?)
228
+ end
229
+
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)
235
+
236
+ th.kill
237
+ th.join
238
+ end
239
+ Ractor.current.ractor_incoming_queue.clear
240
+ end
241
+
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
255
+
256
+ private def ractor_find_current
257
+ RactorThreadGroups[Thread.current.group]
258
+ end
259
+ end
260
+
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
266
+
267
+ # @api private
268
+ attr_reader :ractor_outgoing_queue, :ractor_incoming_queue, :ractor_thread
269
+
270
+ ractor_init
271
+ end
272
+ end