52
52
import java .util .Objects ;
53
53
import java .util .logging .Level ;
54
54
55
- import com .oracle .graal .python .builtins .objects .cext .CExtNodes ;
56
- import com .oracle .graal .python .builtins .objects .cext .NativeCAPISymbols ;
57
55
import org .graalvm .collections .EconomicMap ;
58
56
59
57
import com .oracle .graal .python .PythonLanguage ;
60
58
import com .oracle .graal .python .builtins .objects .PNone ;
61
59
import com .oracle .graal .python .builtins .objects .cext .CAPIConversionNodeSupplier ;
62
60
import com .oracle .graal .python .builtins .objects .cext .CApiGuards ;
61
+ import com .oracle .graal .python .builtins .objects .cext .CExtNodes ;
63
62
import com .oracle .graal .python .builtins .objects .cext .CExtNodes .AddRefCntNode ;
64
63
import com .oracle .graal .python .builtins .objects .cext .CExtNodes .GetRefCntNode ;
65
- import com .oracle .graal .python .builtins .objects .cext .CExtNodes .SubRefCntNode ;
66
- import com .oracle .graal .python .builtins .objects .cext .CExtNodesFactory .SubRefCntNodeGen ;
64
+ import com .oracle .graal .python .builtins .objects .cext .CExtNodes .PCallCapiFunction ;
67
65
import com .oracle .graal .python .builtins .objects .cext .DynamicObjectNativeWrapper .PrimitiveNativeWrapper ;
66
+ import com .oracle .graal .python .builtins .objects .cext .NativeCAPISymbols ;
68
67
import com .oracle .graal .python .builtins .objects .cext .PythonAbstractNativeObject ;
68
+ import com .oracle .graal .python .builtins .objects .cext .capi .NativeObjectReferenceArrayWrapper .PointerArrayWrapper ;
69
+ import com .oracle .graal .python .builtins .objects .cext .capi .NativeObjectReferenceArrayWrapper .RefCountArrayWrapper ;
69
70
import com .oracle .graal .python .builtins .objects .cext .common .CExtContext ;
70
71
import com .oracle .graal .python .builtins .objects .frame .PFrame ;
71
72
import com .oracle .graal .python .builtins .objects .function .PArguments ;
@@ -152,7 +153,7 @@ public CApiContext(PythonContext context, Object hpyLibrary) {
152
153
Thread .currentThread ().interrupt ();
153
154
}
154
155
155
- ArrayDeque <NativeObjectReference > refs = new ArrayDeque <>();
156
+ ArrayList <NativeObjectReference > refs = new ArrayList <>();
156
157
do {
157
158
if (reference instanceof NativeObjectReference ) {
158
159
refs .add ((NativeObjectReference ) reference );
@@ -162,7 +163,7 @@ public CApiContext(PythonContext context, Object hpyLibrary) {
162
163
} while (reference != null );
163
164
164
165
if (!refs .isEmpty ()) {
165
- return new CApiReferenceCleanerAction (refs );
166
+ return new CApiReferenceCleanerAction (refs . toArray ( new NativeObjectReference [ 0 ]) );
166
167
}
167
168
168
169
return null ;
@@ -299,35 +300,76 @@ private static final class CApiReferenceCleanerRootNode extends PRootNode {
299
300
private static final Signature SIGNATURE = new Signature (-1 , false , -1 , false , new String []{"ptr" , "managedRefCount" }, new String [0 ]);
300
301
private static final TruffleLogger LOGGER = PythonLanguage .getLogger (CApiReferenceCleanerRootNode .class );
301
302
302
- @ Child private SubRefCntNode refCntNode ;
303
- @ Child private CalleeContext calleeContext = CalleeContext .create ();
303
+ @ Child private CalleeContext calleeContext ;
304
+ @ Child private InteropLibrary pointerObjectLib ;
305
+ @ Child private PCallCapiFunction callBulkSubref ;
304
306
305
307
private final ConditionProfile customLocalsProfile = ConditionProfile .createBinaryProfile ();
306
308
private final CApiContext cApiContext ;
307
309
308
310
protected CApiReferenceCleanerRootNode (PythonContext context ) {
309
311
super (context .getLanguage ());
310
- refCntNode = SubRefCntNodeGen .create ();
311
312
this .cApiContext = context .getCApiContext ();
313
+ this .calleeContext = CalleeContext .create ();
314
+ this .callBulkSubref = PCallCapiFunction .create ();
312
315
}
313
316
314
317
@ Override
315
- @ SuppressWarnings ("unchecked" )
316
318
public Object execute (VirtualFrame frame ) {
317
319
CalleeContext .enter (frame , customLocalsProfile );
318
320
try {
319
- ArrayDeque < NativeObjectReference > nativeObjectReferences = (ArrayDeque < NativeObjectReference > ) PArguments .getArgument (frame , 0 );
321
+ NativeObjectReference [] nativeObjectReferences = (NativeObjectReference [] ) PArguments .getArgument (frame , 0 );
320
322
NativeObjectReference nativeObjectReference ;
321
- while ((nativeObjectReference = pollFirst (nativeObjectReferences )) != null ) {
322
- if (!nativeObjectReference .resurrect ) {
323
- TruffleObject pointerObject = nativeObjectReference .getPtrObject ();
323
+ int cleaned = 0 ;
324
+ long allocatedNativeMem = cApiContext .allocatedMemory ;
325
+ long startTime = 0 ;
326
+ long middleTime = 0 ;
327
+ final int n = nativeObjectReferences .length ;
328
+ boolean loggable = LOGGER .isLoggable (Level .FINE );
329
+
330
+ if (loggable ) {
331
+ startTime = System .currentTimeMillis ();
332
+ }
333
+
334
+ if (LOGGER .isLoggable (Level .FINER )) {
335
+ // it's not an OSR loop, so we do this before the loop
336
+ if (n > 0 && pointerObjectLib == null ) {
337
+ CompilerDirectives .transferToInterpreterAndInvalidate ();
338
+ pointerObjectLib = insert (InteropLibrary .getFactory ().create (nativeObjectReferences [0 ].ptrObject ));
339
+ }
324
340
341
+ for (int i = 0 ; i < n ; i ++) {
342
+ nativeObjectReference = nativeObjectReferences [i ];
325
343
cApiContext .nativeObjectWrapperList .remove (nativeObjectReference .id );
326
- if (LOGGER .isLoggable (Level .FINE )) {
327
- LOGGER .fine (() -> "Cleaning native object reference to " + CApiContext .asHex (pointerObject ));
344
+ Object pointerObject = nativeObjectReference .ptrObject ;
345
+ if (!nativeObjectReference .resurrect && !pointerObjectLib .isNull (pointerObject )) {
346
+ cApiContext .checkAccess (pointerObject , pointerObjectLib );
347
+ LOGGER .finer (() -> "Cleaning native object reference to " + CApiContext .asHex (pointerObject ));
348
+ cleaned ++;
328
349
}
329
- refCntNode .execute (pointerObject , nativeObjectReference .managedRefCount );
330
350
}
351
+ } else {
352
+ for (int i = 0 ; i < n ; i ++) {
353
+ cApiContext .nativeObjectWrapperList .remove (nativeObjectReferences [i ].id );
354
+ }
355
+ }
356
+
357
+ if (loggable ) {
358
+ middleTime = System .currentTimeMillis ();
359
+ }
360
+
361
+ callBulkSubref .call (NativeCAPISymbols .FUN_BULK_SUBREF , new PointerArrayWrapper (nativeObjectReferences ), new RefCountArrayWrapper (nativeObjectReferences ), (long ) n );
362
+
363
+ if (loggable ) {
364
+ final long countDuration = middleTime - startTime ;
365
+ final long duration = System .currentTimeMillis () - middleTime ;
366
+ final int finalCleaned = cleaned ;
367
+ final long freedNativeMemory = allocatedNativeMem - cApiContext .allocatedMemory ;
368
+ LOGGER .fine (() -> "Total queued references: " + n );
369
+ LOGGER .fine (() -> "Cleaned references: " + finalCleaned );
370
+ LOGGER .fine (() -> "Free'd native memory: " + freedNativeMemory );
371
+ LOGGER .fine (() -> "Count duration: " + countDuration );
372
+ LOGGER .fine (() -> "Duration: " + duration );
331
373
}
332
374
} finally {
333
375
calleeContext .exit (frame , this );
@@ -340,24 +382,50 @@ private static NativeObjectReference pollFirst(ArrayDeque<NativeObjectReference>
340
382
return deque .pollFirst ();
341
383
}
342
384
385
+ @ TruffleBoundary
386
+ private static int size (ArrayList <NativeObjectReference > deque ) {
387
+ return deque .size ();
388
+ }
389
+
390
+ @ TruffleBoundary
391
+ private static NativeObjectReference get (ArrayList <NativeObjectReference > deque , int i ) {
392
+ return deque .get (i );
393
+ }
394
+
395
+ @ TruffleBoundary
396
+ private static NativeObjectReference clear (ArrayList <NativeObjectReference > deque , int i ) {
397
+ return deque .set (i , null );
398
+ }
399
+
343
400
@ Override
344
401
public Signature getSignature () {
345
402
return SIGNATURE ;
346
403
}
347
404
405
+ @ Override
406
+ public String getName () {
407
+ return "native_reference_cleaner" ;
408
+ }
409
+
410
+ @ Override
411
+ public boolean isInternal () {
412
+ return false ;
413
+ }
414
+
348
415
@ Override
349
416
public boolean isPythonInternal () {
350
- return true ;
417
+ return false ;
351
418
}
352
419
}
353
420
354
421
/**
355
422
* Reference cleaner action that will be executed by the {@link AsyncHandler}.
356
423
*/
357
424
private static final class CApiReferenceCleanerAction implements AsyncHandler .AsyncAction {
358
- private final ArrayDeque <NativeObjectReference > nativeObjectReferences ;
359
425
360
- public CApiReferenceCleanerAction (ArrayDeque <NativeObjectReference > nativeObjectReferences ) {
426
+ private final NativeObjectReference [] nativeObjectReferences ;
427
+
428
+ public CApiReferenceCleanerAction (NativeObjectReference [] nativeObjectReferences ) {
361
429
this .nativeObjectReferences = nativeObjectReferences ;
362
430
}
363
431
0 commit comments