51
51
import java .util .Objects ;
52
52
import java .util .logging .Level ;
53
53
54
+ import com .oracle .graal .python .util .PythonUtils ;
54
55
import org .graalvm .collections .EconomicMap ;
55
56
56
57
import com .oracle .graal .python .PythonLanguage ;
70
71
import com .oracle .graal .python .builtins .objects .frame .PFrame ;
71
72
import com .oracle .graal .python .builtins .objects .function .PArguments ;
72
73
import com .oracle .graal .python .builtins .objects .function .Signature ;
74
+ import com .oracle .graal .python .nodes .IndirectCallNode ;
73
75
import com .oracle .graal .python .nodes .PRootNode ;
74
76
import com .oracle .graal .python .nodes .call .GenericInvokeNode ;
75
77
import com .oracle .graal .python .runtime .AsyncHandler ;
76
78
import com .oracle .graal .python .runtime .ExecutionContext .CalleeContext ;
79
+ import com .oracle .graal .python .runtime .ExecutionContext .IndirectCallContext ;
77
80
import com .oracle .graal .python .runtime .PythonContext ;
78
81
import com .oracle .graal .python .runtime .PythonOptions ;
79
82
import com .oracle .truffle .api .CompilerAsserts ;
@@ -95,6 +98,8 @@ public final class CApiContext extends CExtContext {
95
98
96
99
public static final long REFERENCE_COUNT_BITS = Integer .SIZE ;
97
100
public static final long REFERENCE_COUNT_MARKER = (1L << REFERENCE_COUNT_BITS );
101
+ /* a random number between 1 and 20 */
102
+ private static final int MAX_COLLECTION_RETRIES = 17 ;
98
103
99
104
/** Total amount of allocated native memory (in bytes). */
100
105
private long allocatedMemory = 0 ;
@@ -108,7 +113,6 @@ public final class CApiContext extends CExtContext {
108
113
private Map <Object , AllocInfo > freedNativeMemory ;
109
114
110
115
@ CompilationFinal private RootCallTarget referenceCleanerCallTarget ;
111
- @ CompilationFinal private RootCallTarget triggerAsyncActionsCallTarget ;
112
116
113
117
/**
114
118
* This cache is used to cache native wrappers for frequently used primitives. This is strictly
@@ -208,14 +212,6 @@ private RootCallTarget getReferenceCleanerCallTarget() {
208
212
return referenceCleanerCallTarget ;
209
213
}
210
214
211
- RootCallTarget getTriggerAsyncActionsCallTarget () {
212
- if (triggerAsyncActionsCallTarget == null ) {
213
- CompilerDirectives .transferToInterpreterAndInvalidate ();
214
- triggerAsyncActionsCallTarget = Truffle .getRuntime ().createCallTarget (new TriggerAsyncActionsRootNode (getContext ()));
215
- }
216
- return triggerAsyncActionsCallTarget ;
217
- }
218
-
219
215
public TraceMallocDomain getTraceMallocDomain (int domainIdx ) {
220
216
return traceMallocDomains [domainIdx ];
221
217
}
@@ -419,43 +415,6 @@ public void execute(PythonContext context) {
419
415
}
420
416
}
421
417
422
- private static final class TriggerAsyncActionsRootNode extends PRootNode {
423
- private static final Signature SIGNATURE = new Signature (-1 , false , -1 , false , new String [0 ], new String [0 ]);
424
-
425
- @ Child private CalleeContext calleeContext = CalleeContext .create ();
426
-
427
- private final ConditionProfile customLocalsProfile = ConditionProfile .createBinaryProfile ();
428
- private final BranchProfile asyncProfile = BranchProfile .create ();
429
- private final PythonContext context ;
430
-
431
- protected TriggerAsyncActionsRootNode (PythonContext context ) {
432
- super (context .getLanguage ());
433
- this .context = context ;
434
- }
435
-
436
- @ Override
437
- public Object execute (VirtualFrame frame ) {
438
- CalleeContext .enter (frame , customLocalsProfile );
439
- try {
440
- doGc ();
441
- context .triggerAsyncActions (frame , asyncProfile );
442
- } finally {
443
- calleeContext .exit (frame , this );
444
- }
445
- return 0 ;
446
- }
447
-
448
- @ Override
449
- public Signature getSignature () {
450
- return SIGNATURE ;
451
- }
452
-
453
- @ Override
454
- public boolean isPythonInternal () {
455
- return true ;
456
- }
457
- }
458
-
459
418
public NativeObjectReference lookupNativeObjectReference (int idx ) {
460
419
return nativeObjectWrapperList .get (idx );
461
420
}
@@ -615,47 +574,53 @@ public boolean isAllocated(Object ptr) {
615
574
return true ;
616
575
}
617
576
618
- public void increaseMemoryPressure (GenericInvokeNode invokeNode , long size ) {
577
+ public void increaseMemoryPressure (long size ) {
619
578
if (allocatedMemory <= getContext ().getOption (PythonOptions .MaxNativeMemory )) {
620
579
allocatedMemory += size ;
621
580
return ;
622
581
}
623
-
624
- // Triggering the GC and the async actions is hidden behind a call target to keep this
625
- // method slim.
626
- invokeNode .execute (getTriggerAsyncActionsCallTarget (), PArguments .create ());
627
-
628
- if (allocatedMemory + size > getContext ().getOption (PythonOptions .MaxNativeMemory )) {
629
- throw new OutOfMemoryError ("native memory" );
630
- }
631
- allocatedMemory += size ;
582
+ triggerGC (size );
632
583
}
633
584
634
- public void increaseMemoryPressure (VirtualFrame frame , long size , BranchProfile asyncProfile ) {
635
- if (allocatedMemory <= getContext ().getOption (PythonOptions .MaxNativeMemory )) {
585
+ public void increaseMemoryPressure (VirtualFrame frame , PythonContext context , IndirectCallNode caller , long size ) {
586
+ if (allocatedMemory + size <= getContext ().getOption (PythonOptions .MaxNativeMemory )) {
636
587
allocatedMemory += size ;
637
588
return ;
638
589
}
639
590
640
- doGc ();
641
- getContext ().triggerAsyncActions (frame , asyncProfile );
591
+ Object savedState = IndirectCallContext .enter (frame , context , caller );
592
+ try {
593
+ triggerGC (size );
594
+ } finally {
595
+ IndirectCallContext .exit (frame , context , savedState );
596
+ }
597
+ }
642
598
643
- if (allocatedMemory + size > getContext ().getOption (PythonOptions .MaxNativeMemory )) {
644
- throw new OutOfMemoryError ("native memory" );
599
+ @ TruffleBoundary
600
+ private void triggerGC (long size ) {
601
+ long delay = 0 ;
602
+ for (int retries = 0 ; retries < MAX_COLLECTION_RETRIES ; retries ++) {
603
+ delay += 50 ;
604
+ doGc (delay );
605
+ getContext ().triggerAsyncActions (null , BranchProfile .getUncached ());
606
+ if (allocatedMemory + size <= getContext ().getOption (PythonOptions .MaxNativeMemory )) {
607
+ allocatedMemory += size ;
608
+ return ;
609
+ }
645
610
}
646
- allocatedMemory += size ;
611
+ throw new OutOfMemoryError ( "native memory" ) ;
647
612
}
648
613
649
614
public void reduceMemoryPressure (long size ) {
650
615
allocatedMemory -= size ;
651
616
}
652
617
653
618
@ TruffleBoundary
654
- private static void doGc () {
619
+ private static void doGc (long millis ) {
655
620
LOGGER .fine ("full GC due to native memory" );
656
- System . gc ();
621
+ PythonUtils . forceFullGC ();
657
622
try {
658
- Thread .sleep (50 );
623
+ Thread .sleep (millis );
659
624
} catch (InterruptedException x ) {
660
625
// Restore interrupt status
661
626
Thread .currentThread ().interrupt ();
0 commit comments