15
15
import java .util .ArrayList ;
16
16
17
17
import org .truffleruby .RubyContext ;
18
+ import org .truffleruby .core .queue .UnsizedQueue ;
18
19
19
20
import com .oracle .truffle .api .CompilerDirectives .TruffleBoundary ;
20
21
import com .oracle .truffle .api .object .DynamicObject ;
@@ -79,16 +80,176 @@ public ReferenceProcessingService<MarkerReference> service() {
79
80
}
80
81
}
81
82
83
+ public static class MarkRunnerReference extends WeakReference <Object > implements ReferenceProcessingService .ProcessingReference <MarkRunnerReference > {
84
+
85
+ private final MarkRunnerService service ;
86
+ private MarkRunnerReference next = null ;
87
+ private MarkRunnerReference prev = null ;
88
+
89
+ public MarkRunnerReference (Object object , ReferenceQueue <? super Object > queue , MarkRunnerService service ) {
90
+ super (object , queue );
91
+ this .service = service ;
92
+ }
93
+
94
+ public MarkRunnerReference getPrevious () {
95
+ return prev ;
96
+ }
97
+
98
+ public void setPrevious (MarkRunnerReference previous ) {
99
+ prev = previous ;
100
+ }
101
+
102
+ public MarkRunnerReference getNext () {
103
+ return next ;
104
+ }
105
+
106
+ public void setNext (MarkRunnerReference next ) {
107
+ this .next = next ;
108
+ }
109
+
110
+ public ReferenceProcessingService <MarkRunnerReference > service () {
111
+ return service ;
112
+ }
113
+ }
114
+
115
+ public static class MarkRunnerService extends ReferenceProcessingService <MarkingService .MarkRunnerReference > {
116
+
117
+ private final MarkingService markingService ;
118
+
119
+ public MarkRunnerService (RubyContext context , ReferenceProcessor referenceProcessor , MarkingService markingService ) {
120
+ super (context , referenceProcessor );
121
+ this .markingService = markingService ;
122
+ }
123
+
124
+ @ Override
125
+ protected void processReference (ProcessingReference <?> reference ) {
126
+ ArrayList <Object []> keptObjectLists = new ArrayList <>();
127
+ Object [] list ;
128
+ while (true ) {
129
+ list = (Object []) markingService .keptObjectQueue .poll ();
130
+ if (list == null ) {
131
+ break ;
132
+ } else {
133
+ keptObjectLists .add (list );
134
+ }
135
+ }
136
+ if (!keptObjectLists .isEmpty ()) {
137
+ runAllMarkers ();
138
+ }
139
+ super .processReference (reference );
140
+ keptObjectLists .clear ();
141
+ }
142
+
143
+ @ TruffleBoundary
144
+ public void runAllMarkers () {
145
+ MarkerReference currentMarker = markingService .getFirst ();
146
+ MarkerReference nextMarker ;
147
+ while (currentMarker != null ) {
148
+ nextMarker = currentMarker .next ;
149
+ markingService .runMarker (currentMarker );
150
+ if (nextMarker == currentMarker ) {
151
+ throw new Error ("The MarkerReference linked list structure has become broken." );
152
+ }
153
+ currentMarker = nextMarker ;
154
+ }
155
+ }
156
+ }
157
+
82
158
private final int cacheSize ;
83
159
84
- private final ThreadLocal <MarkerStack > stackPreservation = ThreadLocal . withInitial (() -> new MarkerStack ()) ;
160
+ private final ThreadLocal <MarkerThreadLocalData > threadLocalData ;
85
161
86
- private Object [] keptObjects ;
87
162
private final ArrayDeque <Object []> oldKeptObjects = new ArrayDeque <>();
163
+ private MarkRunnerService runnerService ;
164
+
165
+ private UnsizedQueue keptObjectQueue = new UnsizedQueue ();
166
+
167
+ public static class MarkerThreadLocalData {
168
+ private final MarkerKeptObjects keptObjects ;
169
+ private final MarkerStack preservationStack ;
170
+
171
+ public MarkerThreadLocalData (MarkingService service ) {
172
+ this .preservationStack = new MarkerStack ();
173
+ this .keptObjects = new MarkerKeptObjects (service );
174
+ }
175
+
176
+ public MarkerKeptObjects getKeptObjects () {
177
+ return keptObjects ;
178
+ }
179
+
180
+ public MarkerStack getPreservationStack () {
181
+ return preservationStack ;
182
+ }
183
+ }
184
+
185
+ public static class MarkerKeptObjects {
186
+ private final MarkingService service ;
187
+ protected Object [] objects ;
188
+ protected int counter ;
189
+
190
+ protected MarkerKeptObjects (MarkingService service ) {
191
+ objects = new Object [service .cacheSize ];
192
+ counter = 0 ;
193
+ this .service = service ;
194
+ }
88
195
89
- private int counter = 0 ;
196
+ public void keepObject (Object object ) {
197
+ /*
198
+ * It is important to get the ordering of events correct to avoid references being
199
+ * garbage collected too soon. If we are attempting to add a handle to a native
200
+ * structure then that consists of two events. First we create the handle, and then the
201
+ * handle is stored in the struct. If we run mark functions immediate after adding the
202
+ * handle to the list of kept objects then the mark function will be run before that
203
+ * handle is stored in the structure, and since it will be removed from the list of kept
204
+ * objects it could be collected before the mark functions are run again.
205
+ *
206
+ * Instead we check for the kept list being full before adding an object to it, as those
207
+ * handles are already stored in structs by this point.
208
+ */
209
+ if (isFull ()) {
210
+ queueAndReset ();
211
+ }
212
+ objects [counter ++] = object ;
213
+ }
214
+
215
+ private void queueAndReset () {
216
+ service .queueForMarking (objects );
217
+ objects = new Object [service .cacheSize ];
218
+ counter = 0 ;
219
+ }
90
220
91
- protected class MarkerStackEntry {
221
+ private boolean isFull () {
222
+ return counter == service .cacheSize ;
223
+ }
224
+
225
+ public void keepObjects (MarkerKeptObjects otherObjects ) {
226
+ if (isFull ()) {
227
+ queueAndReset ();
228
+ }
229
+ if (otherObjects .isFull ()) {
230
+ service .queueForMarking (otherObjects .objects );
231
+ return ;
232
+ } else if (otherObjects .counter == 0 ) {
233
+ return ;
234
+ } else if (otherObjects .counter + counter <= service .cacheSize ) {
235
+ System .arraycopy (otherObjects .objects , 0 , objects , counter , otherObjects .counter );
236
+ counter += otherObjects .counter ;
237
+ return ;
238
+ } else {
239
+ int overflowLength = counter + otherObjects .counter - service .cacheSize ;
240
+ int initialLength = otherObjects .counter - overflowLength ;
241
+ System .arraycopy (otherObjects .objects , 0 , objects , counter , initialLength );
242
+ counter = service .cacheSize ;
243
+ queueAndReset ();
244
+ System .arraycopy (otherObjects .objects , initialLength , objects , 0 , overflowLength );
245
+ counter = overflowLength ;
246
+ return ;
247
+ }
248
+ }
249
+
250
+ }
251
+
252
+ protected static class MarkerStackEntry {
92
253
protected final MarkerStackEntry previous ;
93
254
protected final ArrayList <Object > entries = new ArrayList <>();
94
255
@@ -97,7 +258,7 @@ protected MarkerStackEntry(MarkerStackEntry previous) {
97
258
}
98
259
}
99
260
100
- public class MarkerStack {
261
+ public static class MarkerStack {
101
262
protected MarkerStackEntry current = new MarkerStackEntry (null );
102
263
103
264
public ArrayList <Object > get () {
@@ -113,42 +274,48 @@ public void push() {
113
274
}
114
275
}
115
276
277
+ @ TruffleBoundary
278
+ public MarkerThreadLocalData getThreadLocalData () {
279
+ return threadLocalData .get ();
280
+ }
281
+
116
282
@ TruffleBoundary
117
283
public MarkerStack getStackFromThreadLocal () {
118
- return stackPreservation .get ();
284
+ return threadLocalData .get ().getPreservationStack ();
285
+ }
286
+
287
+ @ TruffleBoundary
288
+ public MarkerKeptObjects getKeptObjectsFromThreadLocal () {
289
+ return threadLocalData .get ().getKeptObjects ();
119
290
}
120
291
121
292
public MarkingService (RubyContext context , ReferenceProcessor referenceProcessor ) {
122
293
super (context , referenceProcessor );
123
294
cacheSize = context .getOptions ().CEXTS_MARKING_CACHE ;
124
- keptObjects = new Object [cacheSize ];
295
+ threadLocalData = ThreadLocal .withInitial (() -> new MarkerThreadLocalData (this ));
296
+ runnerService = new MarkRunnerService (context , referenceProcessor , this );
125
297
}
126
298
127
- synchronized void addToKeptObjects (Object object ) {
128
- /*
129
- * It is important to get the ordering of events correct to avoid references being garbage
130
- * collected too soon. If we are attempting to add a handle to a native structure then that
131
- * consists of two events. First we create the handle, and then the handle is stored in the
132
- * struct. If we run mark functions immediate after adding the handle to the list of kept
133
- * objects then the mark function will be run before that handle is stored in the structure,
134
- * and since it will be removed from the list of kept objects it could be collected before
135
- * the mark functions are run again.
136
- *
137
- * Instead we check for the kept list being full before adding an object to it, as those
138
- * handles are already stored in structs by this point.
139
- */
140
- if (counter == cacheSize ) {
141
- runAllMarkers ();
142
- }
143
- keptObjects [counter ++] = object ;
299
+ @ TruffleBoundary
300
+ public void makeThreadLocalData () {
301
+ MarkerThreadLocalData data = new MarkerThreadLocalData (this );
302
+ MarkerKeptObjects keptObjects = data .getKeptObjects ();
303
+ context .getFinalizationService ().addFinalizer (data , null , MarkingService .class , () -> getThreadLocalData ().keptObjects .keepObjects (keptObjects ), null );
304
+ }
305
+
306
+ @ TruffleBoundary
307
+ public void queueForMarking (Object [] objects ) {
308
+ keptObjectQueue .add (objects );
309
+ runnerService .add (new MarkRunnerReference (new Object (), referenceProcessor .processingQueue , runnerService ));
144
310
}
145
311
146
312
@ TruffleBoundary
147
- public synchronized void runAllMarkers () {
148
- counter = 0 ;
149
- oldKeptObjects .push (keptObjects );
313
+ public void runAllMarkers () {
314
+ MarkerKeptObjects objects = getKeptObjectsFromThreadLocal ();
315
+ objects .counter = 0 ;
316
+ oldKeptObjects .push (objects .objects );
150
317
try {
151
- keptObjects = new Object [cacheSize ];
318
+ objects . objects = new Object [cacheSize ];
152
319
MarkerReference currentMarker = getFirst ();
153
320
MarkerReference nextMarker ;
154
321
while (currentMarker != null ) {
0 commit comments