@@ -32,6 +32,7 @@ struct objspace {
3232 unsigned long live_ractor_cache_count ;
3333
3434 pthread_mutex_t mutex ;
35+ rb_atomic_t mutator_blocking_count ;
3536 bool world_stopped ;
3637 pthread_cond_t cond_world_stopped ;
3738 pthread_cond_t cond_world_started ;
@@ -131,7 +132,9 @@ rb_mmtk_block_for_gc(MMTk_VMMutatorThread mutator)
131132 struct objspace * objspace = rb_gc_get_objspace ();
132133
133134 size_t starting_gc_count = objspace -> gc_count ;
135+ RUBY_ATOMIC_INC (objspace -> mutator_blocking_count );
134136 int lock_lev = RB_GC_VM_LOCK ();
137+ RUBY_ATOMIC_DEC (objspace -> mutator_blocking_count );
135138 int err ;
136139 if ((err = pthread_mutex_lock (& objspace -> mutex )) != 0 ) {
137140 rb_bug ("ERROR: cannot lock objspace->mutex: %s" , strerror (err ));
@@ -1049,7 +1052,25 @@ rb_gc_impl_before_fork(void *objspace_ptr)
10491052{
10501053 struct objspace * objspace = objspace_ptr ;
10511054
1055+ retry :
10521056 objspace -> fork_hook_vm_lock_lev = RB_GC_VM_LOCK ();
1057+ rb_gc_vm_barrier ();
1058+
1059+ /* At this point, we know that all the Ractors are paused because of the
1060+ * rb_gc_vm_barrier above. Since rb_mmtk_block_for_gc is a barrier point,
1061+ * one or more Ractors could be paused there. However, mmtk_before_fork is
1062+ * not compatible with that because it assumes that the MMTk workers are idle,
1063+ * but the workers are not idle because they are busy working on a GC.
1064+ *
1065+ * This essentially implements a trylock. It will optimistically lock but will
1066+ * release the lock if it detects that any other Ractors are waiting in
1067+ * rb_mmtk_block_for_gc.
1068+ */
1069+ rb_atomic_t mutator_blocking_count = RUBY_ATOMIC_LOAD (objspace -> mutator_blocking_count );
1070+ if (mutator_blocking_count != 0 ) {
1071+ RB_GC_VM_UNLOCK (objspace -> fork_hook_vm_lock_lev );
1072+ goto retry ;
1073+ }
10531074
10541075 mmtk_before_fork ();
10551076}
0 commit comments