@@ -112,11 +112,19 @@ struct rb_mmtk_xmalloc_accounting{
112112 size_t malloc_total ;
113113} rb_mmtk_xmalloc_accounting_t ;
114114
115+ // States for disabling GC
116+ struct RubyMMTKGCDisablingState {
117+ // This mutex only protects the `gc_is_disabled` field.
118+ pthread_mutex_t mutex ;
119+ bool gc_is_disabled ;
120+ };
121+
115122struct RubyMMTKGlobal {
116123 pthread_mutex_t mutex ;
117124 pthread_cond_t cond_world_stopped ;
118125 pthread_cond_t cond_world_started ;
119126 rb_atomic_t mutator_blocking_count ;
127+ struct RubyMMTKGCDisablingState gc_disabling_state ;
120128 unsigned int fork_hook_vm_lock_lev ;
121129 bool world_stopped ;
122130 size_t start_the_world_count ;
@@ -125,6 +133,10 @@ struct RubyMMTKGlobal {
125133 .cond_world_stopped = PTHREAD_COND_INITIALIZER ,
126134 .cond_world_started = PTHREAD_COND_INITIALIZER ,
127135 .mutator_blocking_count = 0 ,
136+ .gc_disabling_state = (struct RubyMMTKGCDisablingState ) {
137+ .mutex = PTHREAD_MUTEX_INITIALIZER ,
138+ .gc_is_disabled = false,
139+ },
128140 .fork_hook_vm_lock_lev = 0 ,
129141 .world_stopped = false,
130142 .start_the_world_count = 0 ,
@@ -1802,7 +1814,83 @@ rb_mmtk_block_for_gc_internal(void *unused)
18021814 RUBY_DEBUG_LOG ("GC finished." );
18031815}
18041816
1805- static void
1817+ ////////////////////////////////////////////////////////////////////////////////
1818+ // Disabling / enabling GC
1819+ ////////////////////////////////////////////////////////////////////////////////
1820+
1821+ void
1822+ rb_mmtk_disable_collection ()
1823+ {
1824+ // Only mutator threads can disable GC.
1825+ rb_mmtk_assert_mutator ();
1826+
1827+ struct RubyMMTKGCDisablingState * state = & rb_mmtk_global .gc_disabling_state ;
1828+
1829+ for (;;) {
1830+ pthread_mutex_lock (& state -> mutex );
1831+ bool gc_is_disabled = state -> gc_is_disabled ;
1832+ if (!gc_is_disabled ) {
1833+ // GC is not disabled, yet. We try once to disable it.
1834+ bool successful = mmtk_disable_collection ();
1835+ if (successful ) {
1836+ // If it is successful, we have disabled GC. We set the state value.
1837+ state -> gc_is_disabled = true;
1838+ gc_is_disabled = true;
1839+ }
1840+ }
1841+ pthread_mutex_unlock (& state -> mutex );
1842+
1843+ if (gc_is_disabled ) {
1844+ // Either GC has already been disabled before this call,
1845+ // or we successfully disabled GC. We can return.
1846+ return ;
1847+ } else {
1848+ // Otherwise, another thread must have triggered GC, and GC will start soon.
1849+ // We block until the next GC finishes and try again.
1850+ rb_mmtk_block_for_gc ((MMTk_VMMutatorThread )GET_RACTOR ());
1851+ }
1852+ }
1853+ }
1854+
1855+ void
1856+ rb_mmtk_enable_collection ()
1857+ {
1858+ // Only mutator threads can enable GC.
1859+ rb_mmtk_assert_mutator ();
1860+
1861+ struct RubyMMTKGCDisablingState * state = & rb_mmtk_global .gc_disabling_state ;
1862+
1863+ pthread_mutex_lock (& state -> mutex );
1864+ bool gc_is_disabled = state -> gc_is_disabled ;
1865+ if (gc_is_disabled ) {
1866+ // GC is already disabled. We enable GC. This is non-blocking.
1867+ mmtk_enable_collection ();
1868+ state -> gc_is_disabled = false;
1869+ }
1870+ pthread_mutex_unlock (& state -> mutex );
1871+
1872+ }
1873+
1874+ bool
1875+ rb_mmtk_is_collection_enabled ()
1876+ {
1877+ // Only mutator threads can enable GC.
1878+ rb_mmtk_assert_mutator ();
1879+
1880+ struct RubyMMTKGCDisablingState * state = & rb_mmtk_global .gc_disabling_state ;
1881+
1882+ pthread_mutex_lock (& state -> mutex );
1883+ bool gc_is_disabled = state -> gc_is_disabled ;
1884+ pthread_mutex_unlock (& state -> mutex );
1885+
1886+ return !gc_is_disabled ;
1887+ }
1888+
1889+ ////////////////////////////////////////////////////////////////////////////////
1890+ // Blocking for GC
1891+ ////////////////////////////////////////////////////////////////////////////////
1892+
1893+ void
18061894rb_mmtk_block_for_gc (MMTk_VMMutatorThread tls )
18071895{
18081896 rb_mmtk_assert_mutator ();
0 commit comments