Choose the ragged stop strategy when the GC kind is determined, so that
we do so with respect to a single measurement of pending unavailable
bytes.
Also remove assert in heap_should_mark_while_stopping, as it can be
called after stopping too, when evacuation is enabled.
The counters are unsigned, so that they can overflow. (Is that really
necessary though?) In any case try_pop can decrement a counter, leading
to a situation where you can think you have (size_t)-1 elements; not
good. Instead when computing the queue size, use a signed value.
Limits total queue size to half the unsigned space; fine.
We don't need a pin bit: we just need to mark pinned objects before
evacuation starts. This way we can remove the stopping / marking race
so that we can always mark while stopping.
The collector now has an abstract interface onto the embedder. The
embedder has to supply some functionality, such as tracing and
forwarding. This is a pretty big change in terms of lines but it's
supposed to have no functional or performance change.
We're targetting systems that need to be able to inspect the kind of an
object, so this information has to be somewhere. If it's out-of-line,
we might save memory, but we would lose locality. Concretely in Guile
the tag bits are in the object itself.
Firstly, we add a priority evacuation reserve to prioritize having a few
evacuation blocks on hand. Otherwise if we give them all to big
allocations first and we have a fragmented heap, we won't be able to
evacuate that fragmented heap to give more blocks to the large
allocations.
Secondly, we remove `enum gc_reason`. The issue is that with multiple
mutator threads, the precise thread triggering GC does not provide much
information. Instead we should make choices on how to collect based on
the state of the heap.
Finally, we move detection of out-of-memory inside the collector,
instead of the allocator.
Together, these changes let mt-gcbench (with fragmentation) operate in
smaller heaps.