-
Couldn't load subscription status.
- Fork 78
Description
#1075 removed disable/enable_collection and replaced it with Collection::is_collection_enabled. However, it seems a binding cannot implement enable/disable collection in a thread-safe way with the latter API.
Consider a scenario like this:
- GC is enabled by default.
- Thread 1 tries to allocate and polls for GC. The poll is allowed, thus MMTk schedules work packets for the GC. However, none of the work packets has been executed yet (including
StopMutatorsandCollection::stop_all_mutators). - Thread 2 tries to disable GC. It sets a binding-side flag, and future
Collection::is_collection_enabledwill return this flag value. Thread 2 further invokes a GC safe point to make sure there is no ongoing GC request yet. Asstop_all_mutatorshas not been called yet, the binding does not know there is a GC and the safe point simply returns. - Thread 2 starts to execute code that is supposed to run only when GC is disabled.
StopMutatorsgets executed by MMTk, and it asks the binding to stop mutators.- Thread 2 runs to a GC safe point, and gets stopped at a place where GC is not supposed to happen.
Though the document for is_collection_enabled states that 'any synchronization involving enabling and disabling collections by mutator threads should be implemented by the VM', there seems to be no way for the VM to do this.
The core issue is that there exists a window between when MMTk polls for GC and when it executes stop_all_mutators.
During this window:
- MMTk already considers a GC in progress.
- The binding, however, cannot detect that GC has been triggered.
If the binding disables GC in that interval, the two states diverge -- leading to GC happening when the VM assumes it cannot.
The previous enable/disable_collection API did not have this problem. When the binding called these methods, MMTk could internally check whether a GC was ongoing and block the call until the GC completed, ensuring proper synchronization.