@@ -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
@@ -248,7 +249,7 @@ private RubyThread getOrCreateForeignThread(RubyContext context, Thread thread)
248
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.
250
251
public Thread cleanerThread = null ;
251
- @ CompilationFinal public Cleaner cleaner = newCleaner () ;
252
+ @ CompilationFinal public Cleaner cleaner = null ;
252
253
253
254
public volatile ValueWrapperManager .HandleBlockWeakReference [] handleBlockSharedMap = new ValueWrapperManager .HandleBlockWeakReference [0 ];
254
255
public final ValueWrapperManager .HandleBlockAllocator handleBlockAllocator = new ValueWrapperManager .HandleBlockAllocator ();
@@ -424,6 +425,9 @@ public RubyContext createContext(Env env) {
424
425
Metrics .initializeOption ();
425
426
426
427
synchronized (this ) {
428
+ numberOfContexts ++;
429
+ setupCleaner ();
430
+
427
431
if (this .options == null ) { // First context
428
432
this .allocationReporter = env .lookup (AllocationReporter .class );
429
433
this .options = new LanguageOptions (env , env .getOptions (), singleContext );
@@ -465,9 +469,10 @@ protected void initializeContext(RubyContext context) {
465
469
context .initialize ();
466
470
467
471
if (context .isPreInitializing ()) {
468
- setRubyHome (context .getEnv (), null );
469
- this .cleanerThread = null ;
470
- this .cleaner = null ;
472
+ synchronized (this ) {
473
+ setRubyHome (context .getEnv (), null );
474
+ resetCleaner ();
475
+ }
471
476
}
472
477
Metrics .printTime ("after-initialize-context" );
473
478
} catch (Throwable e ) {
@@ -498,8 +503,10 @@ protected boolean patchContext(RubyContext context, Env newEnv) {
498
503
return false ;
499
504
}
500
505
501
- setRubyHome (newEnv , findRubyHome ());
502
- this .cleaner = newCleaner ();
506
+ synchronized (this ) {
507
+ setRubyHome (newEnv , findRubyHome ());
508
+ setupCleaner ();
509
+ }
503
510
504
511
boolean patched = context .patchContext (newEnv );
505
512
Metrics .printTime ("after-patch-context" );
@@ -520,6 +527,14 @@ protected void disposeContext(RubyContext context) {
520
527
if (options .COVERAGE_GLOBAL ) {
521
528
coverageManager .print (this , System .out );
522
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
+ }
523
538
}
524
539
525
540
public static RubyContext getCurrentContext () {
@@ -659,8 +674,17 @@ protected Object getScope(RubyContext context) {
659
674
return context .getTopScopeObject ();
660
675
}
661
676
662
- private Cleaner newCleaner () {
663
- return Cleaner .create (runnable -> this .cleanerThread = new Thread (runnable , "Ruby-Cleaner" ));
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 ;
664
688
}
665
689
666
690
public String getRubyHome () {
@@ -681,6 +705,7 @@ public String getPathRelativeToHome(String path) {
681
705
}
682
706
683
707
private void setRubyHome (Env env , String home ) {
708
+ assert Thread .holdsLock (this );
684
709
rubyHome = home ;
685
710
setRubyHomeTruffleFile (env , home );
686
711
}
0 commit comments