In a bootimage=true build, we create allocate certain objects as
"immortal fixies", which means they will never been deallocated at
runtime and should only be visited if/when they point to objects which
might move during garbage collection. However, there was a bug in the
following case:
1. immortal fixie F is updated to point to a movable object M and
thus F is added to the list of fixies to visit during the next minor
collection (but not the next major one, since all reachable objects
are visited during a major collection, and there's no point in
visiting an unreachable object, whereas during a minor collection we
have to visit F because we don't know if it's reachable or not)
2. a major collection occurs, but F is not reachable and thus is not
visited, whereas M is moved
3. a minor collection occurs, and since F is still in the list, it is
visited, but since it contains a stale pointer to M's old location,
we crash
The solution is to ensure unreachable immortal fixies are removed from
the above list after each major collection, thus guaranteeing they
won't be visited on any subsequent collection.
GCC is a lot more sensitive about -Werror=unused-variable, to the
point that stuff declared in header files but unused in a given
compilation unit is flagged. This may be due to the way we're
here's the fix.
At first, it might look like the atomicIncrement operations here,
since they resolve to OSAtomicCompareAndSwap32Barrier, ought to
provide all the memory barrier guarantees we need; however, it turns
out it's not quite sufficient.
Here's why: Apple's OSAtomic*Barrier operations guarantee that memory
operations *before* the atomic op won't be reordered with memory
operations *after* the atomic op - but makes no guarantee that the
atomic op itself won't be reordered with other operations before or
after it. The key is that the atomic operation is not really atomic,
but rather consists of separate read, check and write steps - in a
loop, no less. Here, presumably, the read of t->m->exclusive is
hoisted by the out-of-order processor to between the read and write
steps of the "atomic" increment of t->m->activeCount.
Longer term, it probably makes sense to replace this with the c11
<stdatomic.h> operations or the c++11 <atomic> types. We ought to
actually use the atomic increment operations provided there. As it
is, our atomicIncrement looks substantially less efficient than those,
since it's actually implemented on arm64 as two nested loops (one in
atomicIncrement and one in the compare-and-swap) instead of one. We
should also evaluate whether the various instances of atomic
operations actually need as strong of barriers as we're giving them.
FWIW, the gcc __sync_* builtins seem to have the same problem, at
least on arm64.
such as %s, %d, %x... also width, left justify, zerofill flags are
implemented. Many of the other formats do not work, see the comments in
avian.FormatString javadoc and look for FIXME and TODO items for more
information on features that are known to not work correctly.
This would theoretically break compatibility with apps using embedded
classpaths, on big-endian architectures - because of the size type
extension. However, we don't currently support any big-endian
architectures, so it shouldn't be a problem.