@@ -231,6 +231,7 @@ private RubyThread getOrCreateForeignThread(RubyContext context, Thread thread)
231
231
232
232
@ CompilationFinal public boolean singleContext = true ;
233
233
@ CompilationFinal public Optional <RubyContext > contextIfSingleContext ;
234
+ private int numberOfContexts = 0 ;
234
235
235
236
public final CyclicAssumption traceFuncUnusedAssumption = new CyclicAssumption ("set_trace_func is not used" );
236
237
@@ -245,8 +246,10 @@ private RubyThread getOrCreateForeignThread(RubyContext context, Thread thread)
245
246
public final SymbolTable symbolTable ;
246
247
public final KeywordArgumentsDescriptorManager keywordArgumentsDescriptorManager = new KeywordArgumentsDescriptorManager ();
247
248
public final FrozenStringLiterals frozenStringLiterals ;
248
- public final Cleaner cleaner = Cleaner .create ();
249
249
250
+ // GR-44025: We store the cleanerThread explicitly here to make it a clear image building failure if it would still be set.
251
+ public Thread cleanerThread = null ;
252
+ @ CompilationFinal public Cleaner cleaner = null ;
250
253
251
254
public volatile ValueWrapperManager .HandleBlockWeakReference [] handleBlockSharedMap = new ValueWrapperManager .HandleBlockWeakReference [0 ];
252
255
public final ValueWrapperManager .HandleBlockAllocator handleBlockAllocator = new ValueWrapperManager .HandleBlockAllocator ();
@@ -422,6 +425,9 @@ public RubyContext createContext(Env env) {
422
425
Metrics .initializeOption ();
423
426
424
427
synchronized (this ) {
428
+ numberOfContexts ++;
429
+ setupCleaner ();
430
+
425
431
if (this .options == null ) { // First context
426
432
this .allocationReporter = env .lookup (AllocationReporter .class );
427
433
this .options = new LanguageOptions (env , env .getOptions (), singleContext );
@@ -461,8 +467,12 @@ protected void initializeContext(RubyContext context) {
461
467
try {
462
468
Metrics .printTime ("before-initialize-context" );
463
469
context .initialize ();
470
+
464
471
if (context .isPreInitializing ()) {
465
- setRubyHome (context .getEnv (), null );
472
+ synchronized (this ) {
473
+ setRubyHome (context .getEnv (), null );
474
+ resetCleaner ();
475
+ }
466
476
}
467
477
Metrics .printTime ("after-initialize-context" );
468
478
} catch (Throwable e ) {
@@ -493,7 +503,10 @@ protected boolean patchContext(RubyContext context, Env newEnv) {
493
503
return false ;
494
504
}
495
505
496
- setRubyHome (newEnv , findRubyHome ());
506
+ synchronized (this ) {
507
+ setRubyHome (newEnv , findRubyHome ());
508
+ setupCleaner ();
509
+ }
497
510
498
511
boolean patched = context .patchContext (newEnv );
499
512
Metrics .printTime ("after-patch-context" );
@@ -514,6 +527,14 @@ protected void disposeContext(RubyContext context) {
514
527
if (options .COVERAGE_GLOBAL ) {
515
528
coverageManager .print (this , System .out );
516
529
}
530
+
531
+ synchronized (this ) {
532
+ // GR-28354: Workaround for no "before CacheStore hook"
533
+ numberOfContexts --;
534
+ if (numberOfContexts == 0 && !singleContext ) {
535
+ resetCleaner ();
536
+ }
537
+ }
517
538
}
518
539
519
540
public static RubyContext getCurrentContext () {
@@ -653,6 +674,19 @@ protected Object getScope(RubyContext context) {
653
674
return context .getTopScopeObject ();
654
675
}
655
676
677
+ private void setupCleaner () {
678
+ assert Thread .holdsLock (this );
679
+ if (cleaner == null ) {
680
+ cleaner = Cleaner .create (runnable -> this .cleanerThread = new Thread (runnable , "Ruby-Cleaner" ));
681
+ }
682
+ }
683
+
684
+ private void resetCleaner () {
685
+ assert Thread .holdsLock (this );
686
+ cleanerThread = null ;
687
+ cleaner = null ;
688
+ }
689
+
656
690
public String getRubyHome () {
657
691
return rubyHome ;
658
692
}
@@ -671,6 +705,7 @@ public String getPathRelativeToHome(String path) {
671
705
}
672
706
673
707
private void setRubyHome (Env env , String home ) {
708
+ assert Thread .holdsLock (this );
674
709
rubyHome = home ;
675
710
setRubyHomeTruffleFile (env , home );
676
711
}
0 commit comments