make hash emptying non-atomic
authorDavid Mitchell <davem@iabyn.com>
Tue, 3 May 2011 09:51:17 +0000 (10:51 +0100)
committerDavid Mitchell <davem@iabyn.com>
Thu, 19 May 2011 13:49:42 +0000 (14:49 +0100)
commit3b37eb248baf5af8026a979135e4af0a077172d4
tree43fb2acbe3e4b661f69f024f0316e55ab7e46a99
parentf8d50d94322d5861315980980d36590a8caca5c6
make hash emptying non-atomic

Currently, when empting a hash of its elements (e.g. via
undef(%h), or %h=()), HvARRAY field is temporarily zeroed, so that
any destructors called on the freed elements see an empty hash.

Change this so that they see any remaining elements. Thus,
%h=() becomes more like C<delete $h{$_} for keys %h>.

The atomic behaviour was introduced (by me) in 2003 with commit
2f86008e34264, to fix RT #3096. This concerned element destructors
that messed with the hash being undeffed, causing unrefed var errors
and the like.

At the time, simply setting HvARRAY to null for the duration seemed like a
simple fix. However, it didn't take account of destructors adding new
elements to the list, thus re-creating HvARRAY. This was subsequently
fixed. Then, the HvAUX structure was invented, which meant that optional
hash fields were hidden away at the end of HvARRAY. This meant that
hfreeentries() acquired a whole bunch of extra code to copy these fields
around between the original HvARRAY and the new HvARRAY and then back
again, and temporarily squirrelling the backref array in backref magic
rather than in HvAUX.

In short, hfreeentries() became a 200 line sprawling mess.
This commit reduces it back to 70, and makes everything conceptually
simpler.

It does however change user-level visible behaviour (back to pre-2003),
but note that the new behaviour now matches the behaviour that arrays have
always had (i.e. destructors see a partially-emptied array).

Note that backref magic for HVs is now always stored in HvAUX
hv.c
t/op/undef.t