Due to graph mutation during an incremental cycle collection, objects in the CC graph
may end up with more things pointing to them than they have a ref count. However, these
objects should never become garbage.
Any object that has been stored away somewhere in the middle of incremental graph
building must be treated as live, because we can't trust that the CC graph has
accurate information about it. If such an object is truly garbage, we'll unlink it
in the next cycle collection instead.
ICC uses this to track objects that have been AddRef'd during ICC graph building.
For those objects, we may not have the proper information for them, so treat them
as live.
When an object dies during an incremental cycle collection, we null out
its mParticipant, so we must add various null checks to avoid crashing
when we reach the CC graph representation of an object that has died.
If we purge snow white objects while ICC is in progress, we need to
make sure to remove anything from the CC graph to avoid dangling pointers.
We don't need to do that after shutdown.
Now that all of MarkRoots's state is stored on the heap, it can be run
incrementally. Like with Collect, it takes a budget to determine how
long it can run. Any residual budget will be available to the caller.
One difference is that Collect calls checkOverBudget() which always checks
the time, but MarkRoots uses isOverBudget() to determine if there is
any time remaining. This only checks the current time every
kNumNodesBetweenTimeChecks nodes, to reduce the overhead of checking.
To make nsCycleCollector::MarkRoots incremental, we have to store all of its state on
the heap, so we can resume it. The only remaining state to convert is the NodePool
enumerator.
This patch makes it so that Collect takes a time budget that describes
how much longer the collection can be run for. Then we run the current phase.
Once this is done, we check whether we have exceeded our time budget or
if we have finished a collection. If neither of those have happened, we
run the cycle collector some more.
If we're a manually triggered CC, and we were in the middle of an ICC when
the CC started, then once the current CC is complete, we start a new CC
immediately. This is needed to ensure that a manually specified listener
is used, and to ensure that any garbage objects the caller expects to be
collected are in fact collected.
Note that in this patch we are always passing in an unlimited budget to
Collect, so cycle collections will always be run to completion.
Cycle collection protects against reentrancy by setting a flag to indicate a collection
is in progress. With synchronous CC, it is okay to set this in BeginCollection, and
clear it in CleanupAfterCollection. With ICC, this must be set and cleared in every
slice, so I moved the fixing of it to Collect. I also changed the name of the variable,
because we can be in the middle of an ICC without the CC being actively running,
and it is only the latter we are worried about here.