ractorize 0.0.7 → 0.0.8
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 +10 -0
- data/src/ractorize/base_ractor.rb +2 -0
- data/src/ractorize/garbage_collection/tracker.rb +35 -0
- data/src/ractorize/garbage_collection.rb +81 -0
- data/src/ractorize/ractorized_object.rb +61 -35
- data/src/ractorize/thunk/thunk_ractor.rb +23 -0
- data/src/ractorize/thunk.rb +12 -21
- data/src/ractorize.rb +60 -30
- metadata +5 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 52f6214e369ed8e100b4af65d8cebeeadbfc5ec288b5c93d896f3feb277944fb
|
|
4
|
+
data.tar.gz: 463beced042a7da641a824450a00447ba359e942a4a42a1e99723250611a808d
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 1e79f4cd57dc32ef2f113620fabc60df2b0811f1e68b7975e3c3109fc210fa2ef24c8b439b3e0664b8619401ee07191ce66a7de7dafc6c606c05426c6fcee088
|
|
7
|
+
data.tar.gz: 2927ab40a74c984c46110b3ace84158621dddceb2d45b5125d42f4dcdc9aadd846f364a712329d48054fb645e918abd64d760a6ee21026309c7414211b600647
|
data/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,13 @@
|
|
|
1
|
+
## [0.0.8] - 2026-06-24
|
|
2
|
+
|
|
3
|
+
- Add GarbageCollector to close abandoned RactorizedObject ractors and Thunk ractors
|
|
4
|
+
- Switch thunks to work off of Ractor instead of Ractor::Port due to port/ractor leaks
|
|
5
|
+
- Block on #hash and delegate to super in #==/#!=
|
|
6
|
+
- Significant test suite improvements:
|
|
7
|
+
- Remove awkward RACTORIZE_PROC tests and instead use the shmactor gem to get to 100% branch coverage
|
|
8
|
+
- Parallelize the build and run a shmactor build to get full coverage of ractor procs
|
|
9
|
+
- Check for leaked ractors/ports after test suite and fail the build on any memory leaks
|
|
10
|
+
|
|
1
11
|
## [0.0.7] - 2026-06-06
|
|
2
12
|
|
|
3
13
|
- Closed Ractor::Ports sometimes give IOError instead of Ractor::ClosedError so handle both
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
module Ractorize
|
|
2
|
+
module GarbageCollection
|
|
3
|
+
class Tracker
|
|
4
|
+
attr_accessor :ractorized_object_id_to_ractor,
|
|
5
|
+
:thunk_id_to_ractor
|
|
6
|
+
|
|
7
|
+
def initialize
|
|
8
|
+
self.ractorized_object_id_to_ractor = ObjectSpace::WeakMap.new
|
|
9
|
+
self.thunk_id_to_ractor = ObjectSpace::WeakMap.new
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def track_ractorized_object(ractorized_object)
|
|
13
|
+
ractorized_object_id_to_ractor[ractorized_object.__object_id__] = ractorized_object.__ractor__
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def cleanup_after_ractorized_object(ractorized_object_id)
|
|
17
|
+
ractor = ractorized_object_id_to_ractor.delete(ractorized_object_id)
|
|
18
|
+
ractor&.<<(:__close__)
|
|
19
|
+
rescue Ractor::ClosedError
|
|
20
|
+
# do nothing
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def track_thunk(thunk_id, ractor)
|
|
24
|
+
thunk_id_to_ractor[thunk_id] = ractor
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def cleanup_after_thunk(thunk_id)
|
|
28
|
+
ractor = thunk_id_to_ractor.delete(thunk_id)
|
|
29
|
+
ractor&.<<(:__close__)
|
|
30
|
+
rescue Ractor::ClosedError
|
|
31
|
+
# do nothing
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
module Ractorize
|
|
2
|
+
module GarbageCollection
|
|
3
|
+
class TrackingRactor < BaseRactor; end
|
|
4
|
+
|
|
5
|
+
class << self
|
|
6
|
+
def track_ractorized_object(ractorized_object)
|
|
7
|
+
# We have to define the finalizer here, not in the tracker, because it's not frozen yet
|
|
8
|
+
ObjectSpace.define_finalizer(ractorized_object, &finalize_proc)
|
|
9
|
+
Object.instance_method(:freeze).bind_call(ractorized_object)
|
|
10
|
+
|
|
11
|
+
begin
|
|
12
|
+
TRACKING_RACTOR << [:track_ractorized_object, ractorized_object].freeze
|
|
13
|
+
rescue TrackingRactor::ClosedError
|
|
14
|
+
# do nothing
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def track_thunk(thunk)
|
|
19
|
+
# We have to define the finalizer here, not in the tracker, because it's not frozen yet
|
|
20
|
+
ObjectSpace.define_finalizer(thunk, &finalize_thunk_proc)
|
|
21
|
+
|
|
22
|
+
begin
|
|
23
|
+
TRACKING_RACTOR << [:track_thunk, thunk.__object_id__, thunk.__thunk_ractor__].freeze
|
|
24
|
+
rescue TrackingRactor::ClosedError
|
|
25
|
+
# do nothing
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def cleanup_after_ractorized_object(ractorized_object_id)
|
|
30
|
+
TRACKING_RACTOR << [:cleanup_after_ractorized_object, ractorized_object_id].freeze
|
|
31
|
+
rescue Ractor::ClosedError
|
|
32
|
+
# do nothing
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def cleanup_after_thunk(thunk_id)
|
|
36
|
+
TRACKING_RACTOR << [:cleanup_after_thunk, thunk_id].freeze
|
|
37
|
+
rescue Ractor::ClosedError
|
|
38
|
+
# do nothing
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
private
|
|
42
|
+
|
|
43
|
+
def finalize_proc
|
|
44
|
+
proc do |ractorized_object_id|
|
|
45
|
+
::Ractorize::GarbageCollection.cleanup_after_ractorized_object(ractorized_object_id)
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def finalize_thunk_proc
|
|
50
|
+
proc do |thunk_id|
|
|
51
|
+
::Ractorize::GarbageCollection.cleanup_after_thunk(thunk_id)
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
TRACKING_RACTOR = TrackingRactor.new do
|
|
57
|
+
tracker = Tracker.new
|
|
58
|
+
|
|
59
|
+
loop do
|
|
60
|
+
# SimpleCov branch coverage doesn't like that we aren't testing not matching anything
|
|
61
|
+
# but this does result in an error unlike case/when so no point in checking that.
|
|
62
|
+
# :nocov:
|
|
63
|
+
case receive
|
|
64
|
+
# :nocov:
|
|
65
|
+
in :track_ractorized_object, ractorized_object
|
|
66
|
+
tracker.track_ractorized_object(ractorized_object)
|
|
67
|
+
in :cleanup_after_ractorized_object, ractorized_object_id
|
|
68
|
+
tracker.cleanup_after_ractorized_object(ractorized_object_id)
|
|
69
|
+
in :track_thunk, thunk_id, thunk_ractor
|
|
70
|
+
tracker.track_thunk(thunk_id, thunk_ractor)
|
|
71
|
+
in :cleanup_after_thunk, thunk_id
|
|
72
|
+
tracker.cleanup_after_thunk(thunk_id)
|
|
73
|
+
end
|
|
74
|
+
rescue TrackingRactor::ClosedError
|
|
75
|
+
# do nothing
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
private_constant :TRACKING_RACTOR
|
|
80
|
+
end
|
|
81
|
+
end
|
|
@@ -3,22 +3,34 @@ require_relative "thunk"
|
|
|
3
3
|
|
|
4
4
|
module Ractorize
|
|
5
5
|
class RactorizedObject < BasicObject
|
|
6
|
+
class RactorizedRactor < ::BaseRactor; end
|
|
7
|
+
|
|
8
|
+
class << self
|
|
9
|
+
def method_should_use_thunk?(method_symbol)
|
|
10
|
+
method_symbol != :== && method_symbol != :! && method_symbol != :!= &&
|
|
11
|
+
method_symbol != :inspect && method_symbol != :to_s &&
|
|
12
|
+
!method_symbol.end_with?("?") && method_symbol != :hash
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
attr_reader :__object_id__, :__ractor__
|
|
17
|
+
|
|
6
18
|
def initialize(mode, *args, **opts, &block)
|
|
7
|
-
@
|
|
19
|
+
@__ractor__ = RactorizedRactor.new(name: "#{args.first}<#{args.first.object_id}>", &RACTOR_PROC)
|
|
8
20
|
|
|
9
21
|
case mode
|
|
10
22
|
when :object
|
|
11
|
-
@
|
|
23
|
+
@__ractor__ << :object
|
|
12
24
|
|
|
13
25
|
outside_object = args.first
|
|
14
26
|
|
|
15
27
|
@__target_class__ = outside_object.class
|
|
16
28
|
|
|
17
29
|
if ::Ractor.shareable?(outside_object)
|
|
18
|
-
@
|
|
30
|
+
@__ractor__ << outside_object
|
|
19
31
|
else
|
|
20
32
|
::Ractorize.resolve_all_thunks(outside_object)
|
|
21
|
-
@
|
|
33
|
+
@__ractor__.send(outside_object, move: true)
|
|
22
34
|
end
|
|
23
35
|
when :class
|
|
24
36
|
klass, *args = args
|
|
@@ -28,29 +40,29 @@ module Ractorize
|
|
|
28
40
|
to_move = ::Ractorize.prepare_args(@__target_class__, args, opts)
|
|
29
41
|
|
|
30
42
|
if to_move&.any?
|
|
31
|
-
@
|
|
32
|
-
@
|
|
43
|
+
@__ractor__ << :class_arg_by_arg
|
|
44
|
+
@__ractor__ << klass
|
|
33
45
|
|
|
34
46
|
args.each do |arg|
|
|
35
|
-
@
|
|
36
|
-
@
|
|
47
|
+
@__ractor__ << :arg
|
|
48
|
+
@__ractor__.send(arg, move: to_move.include?(arg))
|
|
37
49
|
end
|
|
38
50
|
|
|
39
51
|
opts.each_pair do |name, value|
|
|
40
|
-
@
|
|
41
|
-
@
|
|
42
|
-
@
|
|
52
|
+
@__ractor__ << :kwarg
|
|
53
|
+
@__ractor__ << name
|
|
54
|
+
@__ractor__.send(value, move: to_move.include?(value))
|
|
43
55
|
end
|
|
44
56
|
|
|
45
57
|
if block
|
|
46
|
-
@
|
|
47
|
-
@
|
|
58
|
+
@__ractor__ << :block
|
|
59
|
+
@__ractor__ << block
|
|
48
60
|
end
|
|
49
61
|
|
|
50
|
-
@
|
|
62
|
+
@__ractor__ << :done
|
|
51
63
|
else
|
|
52
|
-
@
|
|
53
|
-
@
|
|
64
|
+
@__ractor__ << :class
|
|
65
|
+
@__ractor__ << [klass, args.freeze, opts.dup.freeze, block].freeze
|
|
54
66
|
end
|
|
55
67
|
else
|
|
56
68
|
# :nocov:
|
|
@@ -58,30 +70,35 @@ module Ractorize
|
|
|
58
70
|
# :nocov:
|
|
59
71
|
end
|
|
60
72
|
|
|
61
|
-
::Object.instance_method(:
|
|
73
|
+
@__object_id__ = ::Object.instance_method(:object_id).bind_call(self)
|
|
74
|
+
|
|
75
|
+
::Ractorize::GarbageCollection.track_ractorized_object(self)
|
|
62
76
|
end
|
|
63
77
|
|
|
64
78
|
def __close__ = method_missing(:__close__)
|
|
65
79
|
|
|
66
80
|
def __join__
|
|
67
81
|
__close__
|
|
68
|
-
@
|
|
82
|
+
@__ractor__.join
|
|
69
83
|
self
|
|
70
84
|
end
|
|
71
85
|
|
|
72
86
|
def method_missing(method_name, *args, **opts, &block)
|
|
73
|
-
if @
|
|
87
|
+
if @__ractor__.default_port.closed?
|
|
74
88
|
::Kernel.raise ::Ractor::ClosedError,
|
|
75
89
|
"You already closed this Ractorized instance of #{@__target_class__}!\n" \
|
|
76
90
|
"No more methods can be sent to it but you sent #{method_name}"
|
|
77
91
|
end
|
|
78
92
|
|
|
79
|
-
return_port = ::
|
|
93
|
+
return_port = ::Ractorize::RactorizedObject::RactorizedRactor::Port.new
|
|
94
|
+
thunk_ractor = if !block && RactorizedObject.method_should_use_thunk?(method_name)
|
|
95
|
+
::Ractorize::Thunk::ThunkRactor.new
|
|
96
|
+
end
|
|
80
97
|
|
|
81
98
|
to_move = ::Ractorize.prepare_args(@__target_class__, args, opts)
|
|
82
99
|
|
|
83
100
|
if to_move&.any?
|
|
84
|
-
@
|
|
101
|
+
@__ractor__ << [:__invoke_arg_by_arg__, [].freeze, {}.freeze, return_port, thunk_ractor, !!block]
|
|
85
102
|
|
|
86
103
|
args_port = return_port.receive
|
|
87
104
|
args_port << method_name
|
|
@@ -99,7 +116,7 @@ module Ractorize
|
|
|
99
116
|
|
|
100
117
|
args_port << :done
|
|
101
118
|
else
|
|
102
|
-
@
|
|
119
|
+
@__ractor__ << [method_name, args.dup.freeze, opts.dup.freeze, return_port, thunk_ractor, !!block].freeze
|
|
103
120
|
end
|
|
104
121
|
|
|
105
122
|
if block
|
|
@@ -121,28 +138,38 @@ module Ractorize
|
|
|
121
138
|
# TODO: yielded_block likely won't work when actually used
|
|
122
139
|
# so we should probably instead just raise an exception
|
|
123
140
|
# TODO: handle break and also raise in the block
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
141
|
+
begin
|
|
142
|
+
broke = true
|
|
143
|
+
block_result = block.call(*yielded_args.freeze, **yielded_opts.freeze, &yielded_block)
|
|
144
|
+
broke = false
|
|
145
|
+
ensure
|
|
146
|
+
block_result = block_result.__value__ while ::Ractorize::Thunk === block_result
|
|
147
|
+
|
|
148
|
+
block_result_port << if broke
|
|
149
|
+
# TODO: handle error situation
|
|
150
|
+
:break
|
|
151
|
+
else
|
|
152
|
+
[:normal, block_result].freeze
|
|
153
|
+
end
|
|
154
|
+
end
|
|
129
155
|
end
|
|
130
156
|
end
|
|
131
157
|
|
|
132
158
|
value
|
|
133
159
|
# Let's assume the user would rather block on all predicate methods than
|
|
134
160
|
# incorrectly get a non-truthy value (thunk is always truthy even if it evaluates as nil/false)
|
|
135
|
-
elsif
|
|
136
|
-
|
|
161
|
+
elsif thunk_ractor
|
|
162
|
+
return_port.close
|
|
163
|
+
Thunk.new(thunk_ractor)
|
|
164
|
+
else
|
|
137
165
|
value = return_port.receive
|
|
166
|
+
return_port.close
|
|
138
167
|
|
|
139
168
|
# :nocov:
|
|
140
169
|
::Kernel.raise ::Ractorize::Thunk::EscapingRactorError if ::Ractorize::Thunk === value
|
|
141
170
|
# :nocov:
|
|
142
171
|
|
|
143
172
|
value
|
|
144
|
-
else
|
|
145
|
-
Thunk.new(return_port)
|
|
146
173
|
end
|
|
147
174
|
end
|
|
148
175
|
|
|
@@ -159,11 +186,10 @@ module Ractorize
|
|
|
159
186
|
method_missing(:respond_to?, method_name, include_all)
|
|
160
187
|
end
|
|
161
188
|
|
|
162
|
-
def ==(other) = method_missing(:==, other)
|
|
163
|
-
def !=(other) = method_missing(:==, other)
|
|
189
|
+
def ==(other) = method_missing(:==, other) || super
|
|
190
|
+
def !=(other) = method_missing(:==, other) || super
|
|
164
191
|
def ! = method_missing(:!)
|
|
165
|
-
def equal?(other) = method_missing(:equal?, other)
|
|
166
|
-
|
|
192
|
+
# def equal?(other) = method_missing(:equal?, other) || super
|
|
167
193
|
def to_s = inspect
|
|
168
194
|
|
|
169
195
|
def inspect
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
require_relative "../base_ractor"
|
|
2
|
+
|
|
3
|
+
module Ractorize
|
|
4
|
+
class Thunk < BasicObject
|
|
5
|
+
class ThunkRactor < ::BaseRactor
|
|
6
|
+
class << self
|
|
7
|
+
def new
|
|
8
|
+
super do
|
|
9
|
+
# SimpleCov seems to want us to handle the case where nothing matches but that would be an error
|
|
10
|
+
# :nocov:
|
|
11
|
+
case receive
|
|
12
|
+
# :nocov:
|
|
13
|
+
in :__close__
|
|
14
|
+
# do nothing
|
|
15
|
+
in :success, value
|
|
16
|
+
value
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
data/src/ractorize/thunk.rb
CHANGED
|
@@ -2,14 +2,18 @@ module Ractorize
|
|
|
2
2
|
class Thunk < BasicObject
|
|
3
3
|
class EscapingRactorError < ::StandardError; end
|
|
4
4
|
|
|
5
|
-
attr_accessor :
|
|
5
|
+
attr_accessor :__thunk_ractor__, :__object_id__
|
|
6
6
|
|
|
7
|
-
def initialize(
|
|
8
|
-
self.
|
|
9
|
-
self.
|
|
7
|
+
def initialize(return_value_portlike)
|
|
8
|
+
self.__thunk_ractor__ = return_value_portlike
|
|
9
|
+
self.__object_id__ = ::Object.instance_method(:object_id).bind_call(self)
|
|
10
|
+
::Ractorize::GarbageCollection.track_thunk(self)
|
|
10
11
|
end
|
|
11
12
|
|
|
12
13
|
def initialize_clone(...)
|
|
14
|
+
# :nocov:
|
|
15
|
+
raise "CAREFUL! THUNK CLONED!!"
|
|
16
|
+
# :nocov:
|
|
13
17
|
# is this actually necessary?? Seems so?
|
|
14
18
|
end
|
|
15
19
|
|
|
@@ -24,24 +28,11 @@ module Ractorize
|
|
|
24
28
|
def __value__
|
|
25
29
|
return @__value__ if defined?(@__value__)
|
|
26
30
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
# :nocov:
|
|
31
|
-
::Kernel.raise EscapingRactorError,
|
|
32
|
-
"Somehow this thunk was passed between ractors but wasn't resolved first."
|
|
33
|
-
# :nocov:
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
# :nocov:
|
|
37
|
-
::Kernel.raise EscapingRactorError if ::Ractorize::Thunk === value
|
|
38
|
-
# :nocov:
|
|
39
|
-
|
|
40
|
-
@__value__ = value
|
|
41
|
-
|
|
42
|
-
::Object.instance_method(:freeze).bind(self).call
|
|
31
|
+
@__value__ = __thunk_ractor__.join.value
|
|
32
|
+
self.__thunk_ractor__ = nil
|
|
33
|
+
::Object.instance_method(:freeze).bind_call(self)
|
|
43
34
|
|
|
44
|
-
|
|
35
|
+
@__value__
|
|
45
36
|
end
|
|
46
37
|
|
|
47
38
|
def ! = !__value__
|
data/src/ractorize.rb
CHANGED
|
@@ -64,6 +64,7 @@ module Ractorize
|
|
|
64
64
|
# Not sure why that is but we need to handle that case.
|
|
65
65
|
def resolve_all_thunks(structure)
|
|
66
66
|
each_thunk(structure, &:__value__)
|
|
67
|
+
nil
|
|
67
68
|
end
|
|
68
69
|
|
|
69
70
|
def to_move(target_class, args)
|
|
@@ -147,28 +148,8 @@ module Ractorize
|
|
|
147
148
|
to_move(target_class, args)
|
|
148
149
|
end
|
|
149
150
|
|
|
150
|
-
def each_thunk(structure, seen = Set.new, &
|
|
151
|
-
|
|
152
|
-
return if seen.include?(structure)
|
|
153
|
-
|
|
154
|
-
seen << structure
|
|
155
|
-
|
|
156
|
-
case structure
|
|
157
|
-
when Array
|
|
158
|
-
structure.each { each_thunk(it, seen, &block) }
|
|
159
|
-
when Hash
|
|
160
|
-
each_thunk(structure.keys, seen, &block)
|
|
161
|
-
each_thunk(structure.values, seen, &block)
|
|
162
|
-
when Struct
|
|
163
|
-
each_thunk(structure.values, seen, &block)
|
|
164
|
-
else
|
|
165
|
-
ivarsget = ::Object.instance_method(:instance_variables)
|
|
166
|
-
iget = ::Object.instance_method(:instance_variable_get)
|
|
167
|
-
|
|
168
|
-
ivarsget.bind(structure).call.each do |var|
|
|
169
|
-
each_thunk(iget.bind(structure).call(var), seen, &block)
|
|
170
|
-
end
|
|
171
|
-
end
|
|
151
|
+
def each_thunk(structure, seen = Set.new, &)
|
|
152
|
+
each_instance_of(Thunk, structure, seen, 0, &)
|
|
172
153
|
end
|
|
173
154
|
|
|
174
155
|
def extract_args(port_like)
|
|
@@ -200,11 +181,43 @@ module Ractorize
|
|
|
200
181
|
|
|
201
182
|
[args, opts, block]
|
|
202
183
|
end
|
|
184
|
+
|
|
185
|
+
private
|
|
186
|
+
|
|
187
|
+
def each_instance_of(klass, structure, seen = Set.new, depth = 0, &block)
|
|
188
|
+
depth += 1
|
|
189
|
+
if klass === structure
|
|
190
|
+
block.call(structure)
|
|
191
|
+
end
|
|
192
|
+
return if seen.include?(structure)
|
|
193
|
+
|
|
194
|
+
seen << structure
|
|
195
|
+
|
|
196
|
+
case structure
|
|
197
|
+
when Array
|
|
198
|
+
structure.each { each_instance_of(klass, it, seen, depth, &block) }
|
|
199
|
+
when Hash
|
|
200
|
+
each_instance_of(klass, structure.keys, seen, depth, &block)
|
|
201
|
+
each_instance_of(klass, structure.values, seen, depth, &block)
|
|
202
|
+
when Struct
|
|
203
|
+
each_instance_of(klass, structure.values, seen, depth, &block)
|
|
204
|
+
else
|
|
205
|
+
ivarsget = ::Object.instance_method(:instance_variables)
|
|
206
|
+
iget = ::Object.instance_method(:instance_variable_get)
|
|
207
|
+
|
|
208
|
+
ivarsget.bind(structure).call.each do |var|
|
|
209
|
+
value = iget.bind(structure).call(var)
|
|
210
|
+
each_instance_of(klass, value, seen, depth, &block)
|
|
211
|
+
end
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
nil
|
|
215
|
+
end
|
|
203
216
|
end
|
|
204
217
|
|
|
205
218
|
# Putting this in a constant so we can get test coverage on it since not sure how to get coverage
|
|
206
219
|
# on something inside a ractor.
|
|
207
|
-
RACTOR_PROC =
|
|
220
|
+
RACTOR_PROC = Ractor.shareable_proc do
|
|
208
221
|
mode = receive
|
|
209
222
|
|
|
210
223
|
object = case mode
|
|
@@ -230,16 +243,25 @@ module Ractorize
|
|
|
230
243
|
end
|
|
231
244
|
|
|
232
245
|
loop do
|
|
233
|
-
|
|
246
|
+
# rubocop:disable Lint/UselessAssignment
|
|
247
|
+
value = method_name = method_args = opts = return_port = thunk_ractor = block_given = nil
|
|
248
|
+
# rubocop:enable Lint/UselessAssignment
|
|
249
|
+
method_name, method_args, opts, return_port, thunk_ractor, block_given = receive
|
|
234
250
|
|
|
235
251
|
case method_name
|
|
236
252
|
when :__close__
|
|
237
|
-
|
|
253
|
+
begin
|
|
254
|
+
thunk_ractor&.send([:success, object].freeze, move: true)
|
|
255
|
+
rescue RactorizedRactor::ClosedError
|
|
256
|
+
# do nothing
|
|
257
|
+
end
|
|
258
|
+
|
|
259
|
+
object = nil
|
|
238
260
|
close
|
|
239
261
|
break
|
|
240
262
|
else
|
|
241
263
|
if method_name == :__invoke_arg_by_arg__
|
|
242
|
-
args_port =
|
|
264
|
+
args_port = Ractorize::RactorizedObject::RactorizedRactor::Port.new
|
|
243
265
|
return_port << args_port
|
|
244
266
|
|
|
245
267
|
method_name = args_port.receive
|
|
@@ -247,7 +269,7 @@ module Ractorize
|
|
|
247
269
|
end
|
|
248
270
|
|
|
249
271
|
if block_given
|
|
250
|
-
block_result_port =
|
|
272
|
+
block_result_port = Ractorize::RactorizedObject::RactorizedRactor::Port.new
|
|
251
273
|
|
|
252
274
|
value = object.__send__(method_name, *method_args, **opts) do |*args, **opts, &b|
|
|
253
275
|
Ractorize.prepare_args(target_class, args, opts, skip_move: true)
|
|
@@ -260,7 +282,8 @@ module Ractorize
|
|
|
260
282
|
when :normal
|
|
261
283
|
return_value
|
|
262
284
|
when :break
|
|
263
|
-
|
|
285
|
+
# TODO: handle error situation
|
|
286
|
+
break
|
|
264
287
|
else
|
|
265
288
|
# :nocov:
|
|
266
289
|
raise "Not sure how to handle outcome_type #{outcome_type}"
|
|
@@ -274,7 +297,11 @@ module Ractorize
|
|
|
274
297
|
value = value.__value__ while Ractorize::Thunk === value
|
|
275
298
|
|
|
276
299
|
begin
|
|
277
|
-
|
|
300
|
+
if thunk_ractor
|
|
301
|
+
thunk_ractor.send([:success, value].freeze)
|
|
302
|
+
else
|
|
303
|
+
return_port << value
|
|
304
|
+
end
|
|
278
305
|
rescue IOError => e
|
|
279
306
|
# Unclear why this sometimes manifests as this error instead of ClosedError but
|
|
280
307
|
# need to handle them both.
|
|
@@ -288,9 +315,12 @@ module Ractorize
|
|
|
288
315
|
end
|
|
289
316
|
end
|
|
290
317
|
end
|
|
318
|
+
|
|
319
|
+
nil
|
|
291
320
|
end
|
|
292
321
|
|
|
293
|
-
|
|
322
|
+
nil
|
|
323
|
+
# object
|
|
294
324
|
rescue => e
|
|
295
325
|
# :nocov:
|
|
296
326
|
puts
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: ractorize
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.0.
|
|
4
|
+
version: 0.0.8
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Miles Georgi
|
|
@@ -21,9 +21,13 @@ files:
|
|
|
21
21
|
- README.md
|
|
22
22
|
- lib/ractorize.rb
|
|
23
23
|
- src/ractorize.rb
|
|
24
|
+
- src/ractorize/base_ractor.rb
|
|
25
|
+
- src/ractorize/garbage_collection.rb
|
|
26
|
+
- src/ractorize/garbage_collection/tracker.rb
|
|
24
27
|
- src/ractorize/ractorized_class.rb
|
|
25
28
|
- src/ractorize/ractorized_object.rb
|
|
26
29
|
- src/ractorize/thunk.rb
|
|
30
|
+
- src/ractorize/thunk/thunk_ractor.rb
|
|
27
31
|
homepage: https://github.com/ractor-shack/ractorize
|
|
28
32
|
licenses:
|
|
29
33
|
- MPL-2.0
|