Greets,
I'm thinking about trying out an experiment on a new git branch,
`cfish-obj-as-perl-sv`. So you folks know where I'm going with this when the
commit emails start coming in, here's the idea...
Here's the struct layout for our current base object, Clownfish::Obj:
struct cfish_Obj {
cfish_VTable *vtable;
union { void *host_obj; int32_t count; } ref;
};
Python objects start out like so (in non-debugging builds):
struct PyObject {
Py_ssize_t ob_refcnt;
PyTypeObject *ob_type;
};
For our Python build, I'd like Clownfish::Obj to extend PyObject.
struct cfish_Obj {
Py_ssize_t ob_refcnt;
PyTypeObject *ob_type;
cfish_VTable *vtable;
};
Making this change will allow any Clownfish object to be used in a PyObject
context. The refcount will be shared.
Similarly, in our Perl build, I'd like Clownfish::Obj to extend Perl's SV.
struct cfish_Obj {
void *sv_any;
U32 sv_refcnt;
U32 sv_flags;
union { void *ptr; IV iv; } sv_union;
cfish_VTable *vtable;
};
The motivations for this experiment are:
* If possible, ditch our current refcounting/host-object-caching mechanism,
which is kludgy and hard to explain.
* Reduce per-object memory costs and make those costs more predictable.
Right now costs are small until we cache a host object -- which
unfortunately means that memory costs can grow unexpectedly if a large
number of small objects suddenly gain a cached host object.
Some brainstorming code which might potentially end up in perl/xs/XSBind.c is
below. There are some issues which need to be solved regarding immutable
objects (such as VTables) and refcounting, but the current system has worse
bugs. :)
I'm not sure whether this experiment is going to work out, but I didn't want
to do too much work in isolation before bringing it to the dev list.
Marvin Humphrey
cfish_Obj*
cfish_VTable_make_obj(cfish_VTable *self) {
// Allocate an empty, undef SV but with a greater size.
char *raw;
Newxz(raw, self->obj_alloc_size, char); // Newxz to memzero object.
SV *obj_as_sv = (SV*)raw;
SvREFCNT(obj_as_sv) = 1;
SvANY(obj_as_sv) = 0;
SvFLAGS(obj_as_sv) = 0;
// Make the object into an inner Perl object SV.
SvOBJECT_on(obj_as_sv);
PL_sv_objcount++;
SvUPGRADE(obj_as_sv, SVt_PVMG);
sv_setiv(obj_as_sv, PTR2IV(obj_as_sv)); // circular ref not a problem
// Connect class association.
// TODO: cache stash pointer as a member of the VTable.
cfish_CharBuf *class_name = Cfish_VTable_Get_Name(self);
HV *stash = gv_stashpvn((char*)Cfish_CB_Get_Ptr8(class_name),
Cfish_CB_Get_Size(class_name), TRUE);
SvSTASH_set(obj_as_sv, (HV*)SvREFCNT_inc(stash));
// Add Clownfish VTable pointer.
cfish_Obj *obj = (cfish_Obj*)obj_as_sv;
obj->vtable = self;
return obj;
}
uint32_t
cfish_Obj_get_refcount(cfish_Obj *self) {
return SvREFCNT((SV*)self);
}
cfish_Obj*
cfish_Obj_inc_refcount(cfish_Obj *self) {
SvREFCNT_inc_simple_void_NN((SV*)self);
return self;
}
uint32_t
cfish_Obj_dec_refcount(cfish_Obj *self) {
modified_refcount = SvREFCNT((SV*)self->ref.host_obj) - 1;
// If the SV's refcount falls to 0, DESTROY will be invoked from
// Perl-space.
SvREFCNT_dec((SV*)self->ref.host_obj);
return modified_refcount;
}
void*
cfish_Obj_to_host(cfish_Obj *self) {
return newRV_inc((SV*)self);
}
void
cfish_Obj_destroy(cfish_Obj *self) {
// no-op, because Perl is going to take care of freeing `self`.
}