In my Perl-RPM module, I have a recurring problem. I have tied hashes that
have a struct attached via ~ magic, and the struct includes an HV* into
which I cache the key/value pairs (these generally only need to be computed
the first time through, but the hash has to stay tied). Unfortunately, I'm
pretty much bleeding memory as if from a gaping head wound. The reason?
When I store a value on the (inner) HV*, I originally did not mortalize
it. When I retrieved a value, I made sure that the new copy was
mortalized. In the case of simple function return values, I let the XS glue
handle it. Other places, I would explicitly sv_2mortal(). But what was
happening was this: I would either (a) get a ton of "attempt to free
unreferenced scalar" error messages, or (b) not see my object destructors
until the final clean-up phase of the program.
This is harder to illustrate, since I was never certain where the offense
actually occurs. My typemap entry for the tied datatypes:
RPM::Database O_RPM_Tied
RPM::Header O_RPM_Tied
OUTPUT
O_RPM_Tied
if ($var)
{
$arg = sv_bless(sv_2mortal(newRV_noinc((SV*)$var)),
gv_stashpv(\"${(my $ntt=$ntype)=~s/_/::/g;\$ntt}\",
TRUE));
}
else
{
$arg = newSVsv(&PL_sv_undef);
}
INPUT
O_RPM_Tied
if (sv_isobject($arg) && (SvTYPE(SvRV($arg)) == SVt_PVHV))
$var = (HV*)SvRV($arg);
else
{
rpm_error(aTHX_ RPMERR_BADARG,
\"${Package}::$func_name: not a blessed HV reference\");
XSRETURN_UNDEF;
}
The return value for the XS typing of RPM::Database::FETCH is
RPM::Header. The return for RPM::Header::FETCH is SV*. I know that I'm
leaking RPM::Header objects that are stored within the inner hash of an
RPM::Database object, but I may well be leaking SV*'s from the inner hash
of each RPM::Header, as well.
(As a side note, the OUTPUT typemap seems inefficient, since XS will
initialized ST(0) with sv_newmortal(), then overwrite this with my
assignment. Is there a more memory-efficient way of doing the OUTPUT
mapping such that it directly assigns the reference into $arg, rather than
creating a new, mortal RV?)
I suspect the problem comes from the fact that I implement
RPM::Database::FETCH essentially by calling the internal rpmhdr_FETCH C
routine (which is what the RPM::Header::FETCH XS stub encapsulates) and
storing the resulting value on the inner hash. I do this something like so:
I start out with:
/* Step 1: Check to see if this has already been requested and is
thus cached on the hash itself */
svp = hv_fetch(dbstruct->storage, (char *)name, namelen, FALSE);
if (svp && SvROK(*svp))
{
FETCH = (RPM__Header)(*svp);
return (RPM__Header)SvREFCNT_inc((SV *)FETCH);
}
(dbstruct is my ~-magic struct, and storage is a HV*.) Thus, if I've
previously fetched this and it is on the inner hash, I just return the
value that hv_fetch gave me (the inner hash isn't tied). When I actually
have had to get a new value:
FETCH = rpmhdr_TIEHASH(aTHX_ "RPM::Header",
sv_2mortal(newSViv((unsigned)hdr)),
RPM_HEADER_FROM_REF | RPM_HEADER_READONLY);
/* If name is no longer NULL, it means our vector in was a string
(key), so put the result back into the hash-cache. */
if (name != NULL)
{
hv_store(dbstruct->storage, (char *)name, namelen,
(SV *)FETCH, FALSE);
SvREFCNT_inc((SV *)FETCH);
}
The value "hdr" is an internal struct ptr managed by the RPM API
itself. This will return a value of type RPM__Header, which is typedef'd to
HV*. It will have been tied and blessed before rpmhdr_TIEHASH
returns. Here, I've cast it to SV* in order to store it on the inner
hash. Note that the earlier code snippet re-casts it and returns the
RPM__Header type. The OUTPUT typemap picks up from that point. If I removed
*either* of those SvREFCNT_inc() calls, let alone both, I will get
intermittent "attempt to free unreferenced" errors. Not always in my test
suite, but in more involved applications for certain.
Any help appreciated. I imagine the if I can solve this for this
object-case, I'll be able to clean up any cruft in the RPM::Header::FETCH
routine as well.
Randy
--
-------------------------------------------------------------------------------
Randy J. Ray | Programming is a Dark Art [...] The programmer is fighting
[EMAIL PROTECTED] | against the two most destructive forces in the universe:
415-777-9810 x246 | entropy and human stupidity. --Dr. Damian Conway