On Tue, Mar 24, 2009 at 08:23:49PM -0500, Peter Karman wrote:
> I tried all the patterns thus far mentioned in the
> libswish3 Perl/XS bindings (SWISH::3) and finally (through much trial and
> error)
> ended up with the same pattern that Ferret (and I believe KS) uses: create
> host
> objects at the C boundaries, refcounting only the internal C structs, and then
> in the perl DESTROY() method doing something like:
>
> if (c_obj->ref_cnt--) {
> swish_destroy(c_obj);
> }
>
> I've chosen to mitigate the host object expense by doing all the required
> heavy
> lifting (tight loops, etc) in native C, exposing only those methods that the
> Perl user really needs.
That's a good system. And it may be that we can't improve on it in the
context of a garbage collecting host.
KS does something a little different these days, though. Instead of creating
Perl objects at the Perl/C boundary, KS creates a Perl object and a C object
at the same time, and they are mated for life.[1]
HOWEVER, despite the fact that the C object and the Perl object point at each
other, we don't have a circular reference problem because the two objects
*share* a single, unified refcount.
Obj *self = Obj_new(); // SvREFCNT((SV*)self->ref.host_obj) == 1
Obj_Inc_RefCount(self); // SvREFCNT((SV*)self->ref.host_obj) == 2
SV *pobj = Obj_To_Host(self); // SvREFCNT((SV*)self->ref.host_obj) == 3
SV *copy = newSVsv(pobj); // SvREFCNT((SV*)self->ref.host_obj) == 4
Obj_Dec_RefCount(self) // SvREFCNT((SV*)self->ref.host_obj) == 3
Obj_Dec_RefCount(self) // SvREFCNT((SV*)self->ref.host_obj) == 2
Obj_Dec_RefCount(self) // SvREFCNT((SV*)self->ref.host_obj) == 1
Obj_Dec_RefCount(self) // $obj->DESTROY invoked from Perl-space
Unlike SWISH3, KinoSearch::Obj::DESTROY knows that it will only be invoked
when the refcount is 0, so it can invoke the C destructor every time.
void
DESTROY(self)
kino_Obj *self;
PPCODE:
Kino_Obj_Destroy(self);
In fact, every time that a KinoSearch::Obj has its refcount falls to 0, we
venture
back into Perl-space. That's because Obj_Dec_RefCount() is a wrapper around
SvREFCNT_dec(), and when SvREFCNT_dec() hits 0, it invokes the Perl DESTROY
method.
The advantage of this scheme is that we create fewer objects, and thus incur
less overhead at the host/C boundary.
The question I'm trying to answer is: Is there any way to make this work in
the context of a host like Ruby or Java, which use tracing garbage collection
instead of a refcounting scheme that we can piggyback on?
Marvin Humphrey
[1] OK, I'm lying. KinoSearch classes which subclass
KinoSearch::Obj::FastObj actually behave exactly the same way as Ferret
and SWISH. It would be really nice if that were not the case, because if
even really really smart people like Peter find this hard to follow, the
system needs to be simplified.