Binding goes like this: the binding stack pointer (bsp) is bumped, old value and symbol are stored at bsp - 1, new value is stored in symbol's value slot or the tls.
Unbinding: the symbol's value is restored from bsp - 1, value and symbol at bsp - 1 are set to zero, and finally bsp is decremented.
The UNBIND-TO-HERE
VOP assists in unwinding the stack. It
iterates over the bindings on the binding stack until it reaches the
prescribed point. For each binding with a non-zero symbol it does an
UNBIND
.
How can a binding's symbol be zero? BIND
is not pseudo atomic
(for performance reasons) and it can be interrupted by a signal. If
the signal hits after the bsp is incremented but before the values on
the stack are set the symbol is zero because a thread starts with a
zeroed tls plus UNBIND
and UNBIND-TO-HERE
both zero the
binding being unbound.
Zeroing the binding's symbol would not be enough as the binding's
value can be moved or garbage collected and if the above interrupt
initiates gc (or be SIG_STOP_FOR_GC
) it will be greeted by a
garbage pointer.
Furthermore, BIND
must always write the value to the binding
stack first and the symbol second because the symbol being non-zero
means validity to UNBIND-TO-HERE
. For similar reasons
UNBIND
also zeroes the symbol first. But if it is interrupted
by a signal that does an async unwind then UNBIND-TO-HERE
can
be triggered when the symbol is zeroed but the value is not. In this
case UNBIND-TO-HERE
must zero out the value to avoid leaving
garbage around that may wreck the ship on the next BIND
.
In other words, the invariant is that the binding stack above bsp only
contains zeros. This makes BIND
safe in face of gc triggered at
any point during its execution.