On 11/09/2014 18:55, Marvin Humphrey wrote:
I'm skeptical about having a cast operation trigger a refcount increment.
Besides the runtime cost of the refcount manipulation (or interface object
allocation), the need to DECREF after use also makes programming more complex
and increases the likelihood of memory leaks.
static int
compare(const void *va, const void *vb) {
Comparer *a = toCOMPARER(*(Obj**)va);
Comparer *b = toCOMPARER(*(Obj**)vb);
int retval = Comparer_Compare_To(a, b); // exception here causes leak
DECREF(a);
DECREF(b);
return retval;
}
I understand that avoiding refcount manipulation narrows our implementation
options.
Originally, I didn't think of it as a cast operation but more like a
constructor for interface objects. It would be nice if the API would support
an implementation using interface objects that are allocated separately.
1. Allocate the itable array on demand. Objects of many classes are never
converted to interface types. This would only require an additional NULL
check.
Right, and the NULL check is in the cast which is better than having it in the
method dispatch routine.
We could do this and also divide the itable array into, say, 8 parts. Then
during the lookup, figure out which array to look in by performing a
mask/shift on the interface ID.
Good idea. It doesn't change the quadratic memory growth but it should give us
enough leeway to support even thousands of classes and interfaces with
reasonable memory usage.
Hey, how about we encode the interface ID and into the high bits of the method
OFFSET variable? Then there's only one global variable memory fetch needed
during method dispatch.
static inline void
Futzer_Futz(Futzer *self) {
uint32_t offset = Futzer_Futz_OFFSET;
uint32_t itable_array_slot
= (offset & ITABLE_ARRAY_MASK) >> ITABLE_ARRAY_SHIFT;
Interface **itables = &self->klass->itables + itable_array_slot;
uint32_t interface_id
= (offset & INTERFACE_ID_MASK) >> INTERFACE_ID_SHIFT;
Interface *interface = itables[interface_id];
char *ptr = (char*)interface + (offset & IMETHOD_OFFSET_MASK);
Futzer_Futz_t method = (Futzer_Futz_t)ptr;
method(self);
}
Oh, and OFFSET vars should probably be uint32_t rather than size_t. No class
is ever going to have so many methods that we need a size_t. That'll save
some space on 64-bit systems.
Yes, we could use something like a 20/12 or a 16/16 bit split. This would
limit the total number of interfaces and the number of methods per interface
to a value between 2^12 and 2^20 but this shouldn't be a problem.
Nick