diff --git a/gc/mmtk/src/api.rs b/gc/mmtk/src/api.rs index ef0d7bf..80b0a43 100644 --- a/gc/mmtk/src/api.rs +++ b/gc/mmtk/src/api.rs @@ -305,9 +305,13 @@ pub extern "C" fn mmtk_handle_user_collection_request( crate::mmtk().handle_user_collection_request(tls, force, exhaustive); if gc_was_disabled { - crate::mmtk() - .disable_collection() - .unwrap_or_else(|_| panic!("failed to re-disable GC after GC")); + // While GC was enabled, another Ractor may have requested a GC that hasn't run yet. + // MMTk can't disable GC until that GC runs, and it needs this thread to stop first. + // So block for it and try again. Since a GC is already requested, this waits for that + // GC instead of starting a new one. + while crate::mmtk().disable_collection().is_err() { + crate::mmtk().handle_user_collection_request(tls, true, false); + } } } diff --git a/test/mmtk/test_gc.rb b/test/mmtk/test_gc.rb new file mode 100644 index 0000000..abbe531 --- /dev/null +++ b/test/mmtk/test_gc.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +require_relative "helper" + +module MMTk + class TestGC < TestCase + def test_start_from_ractors_while_disabled + assert_separately([], <<~RUBY) + Warning[:experimental] = false + + GC.disable + ractors = 4.times.map do + Ractor.new do + 10.times { GC.start } + end + end + ractors.each(&:join) + + assert_equal(true, GC.enable) + RUBY + end + end +end