On 25/03/2012 21:22, Marvin Humphrey wrote:
On Sun, Mar 25, 2012 at 12:16 PM, Nick Wellnhofer<[email protected]> wrote:
It's not the extensions that need the struct definitions, it's the code in
parcel.c:
/* Define the variable which holds this class's class name.
*/
static cfish_ZombieCharBuf LUCY_QUERY_CLASS_NAME = {
CFISH_ZOMBIECHARBUF,
{1}, /* ref.count */
"Lucy::Search::Query",
19,
0
};
When building Lucy, CharBuf and VTable are automatically included with
struct definitions. But in extensions this is not the case.
Got it.
I believe that the solution is to define VTables using a bootstrapping
function at startup rather than define them statically at compile-time.
The attached patch contains are a rough attempt to initialize the
VTables at run-time. I also tried to malloc the VTables dynamically, but
this broke RAWPOSTING_BLANK where a VTable is used to initialize a
global struct.
The patch also moves the code to register the VTables from boot.c to
parcel.c which seemed like the right place, because this initialization
code does not depend on the host language.
Nick
>From c23e3ed798967d5588e293434c6ec528326abc6f Mon Sep 17 00:00:00 2001
From: Nick Wellnhofer <[email protected]>
Date: Tue, 10 Apr 2012 01:23:46 +0200
Subject: [PATCH] Initialize VTables dynamically during boot
---
clownfish/perl/lib/Clownfish/CFC.xs | 4 +-
clownfish/src/CFCBindAliases.c | 1 +
clownfish/src/CFCBindClass.c | 160 ++++++++++++++++-------------------
clownfish/src/CFCBindClass.h | 15 +++-
clownfish/src/CFCBindCore.c | 40 ++++++---
clownfish/src/CFCPerl.c | 10 +--
core/Lucy/Object/VTable.c | 32 +++++++
core/Lucy/Object/VTable.cfh | 5 ++
8 files changed, 158 insertions(+), 109 deletions(-)
diff --git a/clownfish/perl/lib/Clownfish/CFC.xs
b/clownfish/perl/lib/Clownfish/CFC.xs
index daafb38..662c17b 100644
--- a/clownfish/perl/lib/Clownfish/CFC.xs
+++ b/clownfish/perl/lib/Clownfish/CFC.xs
@@ -1816,10 +1816,10 @@ CODE:
OUTPUT: RETVAL
SV*
-to_c(self)
+to_c_data(self)
CFCBindClass *self;
CODE:
- RETVAL = S_sv_eat_c_string(CFCBindClass_to_c(self));
+ RETVAL = S_sv_eat_c_string(CFCBindClass_to_c_data(self));
OUTPUT: RETVAL
SV*
diff --git a/clownfish/src/CFCBindAliases.c b/clownfish/src/CFCBindAliases.c
index 01d800f..10f0ecd 100644
--- a/clownfish/src/CFCBindAliases.c
+++ b/clownfish/src/CFCBindAliases.c
@@ -95,6 +95,7 @@ struct alias aliases[] = {
{"cfish_VTable", "lucy_VTable"},
{"CFISH_VTABLE", "LUCY_VTABLE"},
+ {"cfish_VTable_bootstrap", "lucy_VTable_bootstrap"},
{"cfish_VTable_add_to_registry", "lucy_VTable_add_to_registry"},
{"cfish_VTable_add_alias_to_registry",
"lucy_VTable_add_alias_to_registry"},
{"cfish_VTable_offset_of_parent", "lucy_VTable_offset_of_parent"},
diff --git a/clownfish/src/CFCBindClass.c b/clownfish/src/CFCBindClass.c
index 814a34f..84364a2 100644
--- a/clownfish/src/CFCBindClass.c
+++ b/clownfish/src/CFCBindClass.c
@@ -34,6 +34,7 @@ struct CFCBindClass {
CFCBase base;
CFCClass *client;
char *full_callbacks_var;
+ char *full_methods_var;
char *full_name_var;
char *short_names_macro;
};
@@ -50,12 +51,6 @@ S_to_c_header_dynamic(CFCBindClass *self);
static char*
S_struct_definition(CFCBindClass *self);
-/* C code defining the ZombieCharBuf which contains the class name for this
- * class.
- */
-static char*
-S_name_var_definition(CFCBindClass *self);
-
// Return C code defining the class's VTable.
static char*
S_vtable_definition(CFCBindClass *self);
@@ -112,9 +107,11 @@ CFCBindClass_init(CFCBindClass *self, CFCClass *client) {
const char *full_vtable_var = CFCClass_full_vtable_var(client);
const char *PREFIX = CFCClass_get_PREFIX(client);
self->full_callbacks_var = (char*)MALLOCATE(strlen(full_vtable_var) + 20);
+ self->full_methods_var = (char*)MALLOCATE(strlen(full_vtable_var) + 20);
self->full_name_var = (char*)MALLOCATE(strlen(full_vtable_var) + 20);
self->short_names_macro = (char*)MALLOCATE(strlen(PREFIX) + 20);
sprintf(self->full_callbacks_var, "%s_CALLBACKS", full_vtable_var);
+ sprintf(self->full_methods_var, "%s_METHODS", full_vtable_var);
sprintf(self->full_name_var, "%s_CLASS_NAME", full_vtable_var);
sprintf(self->short_names_macro, "%sUSE_SHORT_NAMES", PREFIX);
@@ -124,6 +121,7 @@ CFCBindClass_init(CFCBindClass *self, CFCClass *client) {
void
CFCBindClass_destroy(CFCBindClass *self) {
FREEMEM(self->full_callbacks_var);
+ FREEMEM(self->full_methods_var);
FREEMEM(self->full_name_var);
FREEMEM(self->short_names_macro);
CFCBase_decref((CFCBase*)self->client);
@@ -290,7 +288,7 @@ S_to_c_header_dynamic(CFCBindClass *self) {
}
char*
-CFCBindClass_to_c(CFCBindClass *self) {
+CFCBindClass_to_c_data(CFCBindClass *self) {
CFCClass *client = self->client;
if (CFCClass_inert(client)) {
@@ -300,9 +298,8 @@ CFCBindClass_to_c(CFCBindClass *self) {
const char *include_h = CFCClass_include_h(client);
const char *autocode = CFCClass_get_autocode(client);
const char *vt_type = CFCClass_full_vtable_type(client);
+ const char *vt_hidden = CFCClass_full_vtable_hidden(client);
const char *cnick = CFCClass_get_cnick(client);
- char *class_name_def = S_name_var_definition(self);
- char *vtable_def = S_vtable_definition(self);
CFCMethod **methods = CFCClass_methods(client);
CFCMethod **fresh_methods = CFCClass_fresh_methods(client);
@@ -319,6 +316,10 @@ CFCBindClass_to_c(CFCBindClass *self) {
cb_var = CFCUtil_cat(cb_var, "cfish_Callback *", self->full_callbacks_var,
"[] = {\n ", NULL);
+ char *method_var = CFCUtil_strdup("");
+ method_var = CFCUtil_cat(method_var, "cfish_method_t ",
+ self->full_methods_var, "[] = {\n ", NULL);
+
for (int meth_num = 0; methods[meth_num] != NULL; meth_num++) {
CFCMethod *method = methods[meth_num];
int method_is_fresh = S_method_is_fresh(method, fresh_methods);
@@ -365,11 +366,18 @@ CFCBindClass_to_c(CFCBindClass *self) {
cb_var = CFCUtil_cat(cb_var, "&", full_cb_sym, ",\n ", NULL);
}
+ // Define method implementations.
+ const char *implementing_sym = CFCMethod_implementing_func_sym(method);
+ method_var = CFCUtil_cat(method_var, "(cfish_method_t)",
+ implementing_sym, ",\n ", NULL);
+
FREEMEM(offset_str);
}
// Close callbacks variable definition.
cb_var = CFCUtil_cat(cb_var, "NULL\n};\n", NULL);
+ // Close methods variable definition.
+ method_var = CFCUtil_cat(method_var, "NULL\n};\n", NULL);
const char pattern[] =
"#include \"%s\"\n"
@@ -398,7 +406,7 @@ CFCBindClass_to_c(CFCBindClass *self) {
"\n"
"%s\n"
"\n"
- "/* Define the variable which holds this class's class name.\n"
+ "/* Define this class's method implementations.\n"
" */\n"
"\n"
"%s\n"
@@ -406,7 +414,7 @@ CFCBindClass_to_c(CFCBindClass *self) {
"/* Define this class's VTable.\n"
" */\n"
"\n"
- "%s\n"
+ "%s %s;\n"
"\n"
"/* Include auxilary automatically generated code for this class.\n"
" */\n"
@@ -419,21 +427,21 @@ CFCBindClass_to_c(CFCBindClass *self) {
+ strlen(cb_funcs)
+ strlen(cb_objects)
+ strlen(cb_var)
- + strlen(class_name_def)
- + strlen(vtable_def)
+ + strlen(method_var)
+ + strlen(vt_type)
+ + strlen(vt_hidden)
+ strlen(autocode)
+ 100;
char *code = (char*)MALLOCATE(size);
sprintf(code, pattern, include_h, offsets, cb_funcs, cb_objects, cb_var,
- class_name_def, vtable_def, autocode);
+ method_var, vt_type, vt_hidden, autocode);
+ FREEMEM(method_var);
FREEMEM(fresh_methods);
FREEMEM(offsets);
FREEMEM(cb_funcs);
FREEMEM(cb_objects);
FREEMEM(cb_var);
- FREEMEM(class_name_def);
- FREEMEM(vtable_def);
return code;
}
@@ -464,38 +472,17 @@ S_struct_definition(CFCBindClass *self) {
return struct_def;
}
-static char*
-S_name_var_definition(CFCBindClass *self) {
- const char *class_name = CFCClass_get_class_name(self->client);
- unsigned class_name_len = (unsigned)strlen(class_name);
-
- const char pattern[] =
- "static cfish_ZombieCharBuf %s = {\n"
- " CFISH_ZOMBIECHARBUF,\n"
- " {1}, /* ref.count */\n"
- " \"%s\",\n"
- " %u,\n"
- " 0\n"
- "};\n\n";
- size_t size = sizeof(pattern)
- + strlen(self->full_name_var)
- + class_name_len
- + 15 // class_name_len
- + 20;
- char *name_var_def = (char*)MALLOCATE(size);
- sprintf(name_var_def, pattern, self->full_name_var, class_name,
- class_name_len);
+// Return C code bootstrapping the class's VTable.
+char*
+CFCBindClass_to_vtable_bootstrap(CFCBindClass *self) {
+ CFCClass *client = self->client;
- return name_var_def;
-}
+ if (CFCClass_inert(client)) {
+ return CFCUtil_strdup("");
+ }
-// Return C code defining the class's VTable.
-static char*
-S_vtable_definition(CFCBindClass *self) {
- CFCClass *client = self->client;
CFCClass *parent = CFCClass_get_parent(client);
- CFCMethod **methods = CFCClass_methods(client);
- const char *vt_type = CFCClass_full_vtable_type(client);
+ const char *class_name = CFCClass_get_class_name(client);
const char *vt_var = CFCClass_full_vtable_var(client);
const char *struct_sym = CFCClass_full_struct_sym(client);
@@ -504,56 +491,57 @@ S_vtable_definition(CFCBindClass *self) {
? CFCClass_full_vtable_var(parent)
: "NULL"; // No parent, e.g. Obj or inert classes.
- // Spec functions which implement the methods, casting to quiet compiler.
- char *method_str = CFCUtil_strdup("");
- int num_methods = 0;
- for (; methods[num_methods] != NULL; num_methods++) {
- CFCMethod *method = methods[num_methods];
- const char *implementing_sym = CFCMethod_implementing_func_sym(method);
- size_t size = strlen(method_str) + strlen(implementing_sym) + 50;
- method_str = (char*)REALLOCATE(method_str, size);
- if (strlen(method_str)) {
- strcat(method_str, ",\n");
- }
- strcat(method_str, " (cfish_method_t)");
- strcat(method_str, implementing_sym);
- }
-
char pattern[] =
- "\n"
- "%s %s_vt = {\n"
- " CFISH_VTABLE, /* vtable vtable */\n"
- " {1}, /* ref.count */\n"
- " %s, /* parent */\n"
- " (cfish_CharBuf*)&%s,\n"
- " 0, /* flags */\n"
- " NULL, /* \"void *x\" member reserved for future use */\n"
- " sizeof(%s), /* obj_alloc_size */\n"
- " offsetof(cfish_VTable, methods)\n"
- " + %d * sizeof(cfish_method_t), /* vt_alloc_size */\n"
- " &%s, /* callbacks */\n"
- " {\n"
- "%s\n"
- " }\n"
- "};\n\n";
+ " cfish_VTable_bootstrap(\n"
+ " %s, /* vtable */\n"
+ " %s, /* parent */\n"
+ " \"%s\", /* name */\n"
+ " 0, /* flags */\n"
+ " NULL, /* \"void *x\" member reserved for future use */\n"
+ " sizeof(%s), /* obj_alloc_size */\n"
+ " %s, /* callbacks */\n"
+ " %s /* methods */\n"
+ " );\n";
size_t size = sizeof(pattern)
- + strlen(vt_type)
+ strlen(vt_var)
+ strlen(parent_ref)
- + strlen(self->full_name_var)
+ + strlen(class_name)
+ strlen(struct_sym)
- + 10 /* num_methods */
+ strlen(self->full_callbacks_var)
- + strlen(method_str)
+ + strlen(self->full_methods_var)
+ 40;
- char *vtable_def = (char*)MALLOCATE(size);
- sprintf(vtable_def, pattern, vt_type, vt_var, parent_ref,
- self->full_name_var, struct_sym, num_methods,
- self->full_callbacks_var, method_str);
+ char *code = (char*)MALLOCATE(size);
+ sprintf(code, pattern, vt_var, parent_ref, class_name, struct_sym,
+ self->full_callbacks_var, self->full_methods_var);
+
+ return code;
+}
+
+// Return C code registering the class's VTable.
+char*
+CFCBindClass_to_vtable_register(CFCBindClass *self) {
+ CFCClass *client = self->client;
+
+ if (CFCClass_inert(client)) {
+ return CFCUtil_strdup("");
+ }
+
+ const char *vt_var = CFCClass_full_vtable_var(client);
+
+ // Ignore return value from VTable_add_to_registry, since it's OK if
+ // multiple threads contend for adding these permanent VTables and some
+ // fail.
+ char pattern[] =
+ " cfish_VTable_add_to_registry(%s);\n";
- FREEMEM(method_str);
- return vtable_def;
+ size_t size = sizeof(pattern)
+ + strlen(vt_var)
+ + 20;
+ char *code = (char*)MALLOCATE(size);
+ sprintf(code, pattern, vt_var);
+
+ return code;
}
// Declare cfish_Callback objects.
diff --git a/clownfish/src/CFCBindClass.h b/clownfish/src/CFCBindClass.h
index c53af53..6199e8c 100644
--- a/clownfish/src/CFCBindClass.h
+++ b/clownfish/src/CFCBindClass.h
@@ -49,11 +49,20 @@ CFCBindClass_destroy(CFCBindClass *self);
char*
CFCBindClass_to_c_header(CFCBindClass *self);
-/** Return the .c file which contains autogenerated C code necessary for the
- * class to function properly.
+/** Return the C data definitions necessary for the class to function properly.
*/
char*
-CFCBindClass_to_c(CFCBindClass *self);
+CFCBindClass_to_c_data(CFCBindClass *self);
+
+/** Return the autogenerated C code necessary to bootstrap the class.
+ */
+char*
+CFCBindClass_to_vtable_bootstrap(CFCBindClass *self);
+
+/** Return the autogenerated C code necessary to register class.
+ */
+char*
+CFCBindClass_to_vtable_register(CFCBindClass *self);
#ifdef __cplusplus
}
diff --git a/clownfish/src/CFCBindCore.c b/clownfish/src/CFCBindCore.c
index 3ea0f50..9f64fd2 100644
--- a/clownfish/src/CFCBindCore.c
+++ b/clownfish/src/CFCBindCore.c
@@ -205,6 +205,9 @@ S_write_parcel_h(CFCBindCore *self) {
" size_t offset;\n"
"} cfish_Callback;\n"
"\n"
+ "void\n"
+ "cfish_bootstrap_parcel();\n"
+ "\n"
"#ifdef __cplusplus\n"
"}\n"
"#endif\n"
@@ -241,9 +244,11 @@ S_write_parcel_c(CFCBindCore *self) {
CFCHierarchy *hierarchy = self->hierarchy;
// Aggregate C code from all files.
- char *content = CFCUtil_strdup("");
char *privacy_syms = CFCUtil_strdup("");
char *includes = CFCUtil_strdup("");
+ char *c_data = CFCUtil_strdup("");
+ char *vt_bootstrap = CFCUtil_strdup("");
+ char *vt_register = CFCUtil_strdup("");
CFCFile **files = CFCHierarchy_files(hierarchy);
for (int i = 0; files[i] != NULL; i++) {
CFCFile *file = files[i];
@@ -258,9 +263,15 @@ S_write_parcel_c(CFCBindCore *self) {
if (CFCClass_included(klass)) { continue; }
CFCBindClass *class_binding = CFCBindClass_new(klass);
- char *c_code = CFCBindClass_to_c(class_binding);
- content = CFCUtil_cat(content, c_code, "\n", NULL);
- FREEMEM(c_code);
+ char *class_c_data = CFCBindClass_to_c_data(class_binding);
+ c_data = CFCUtil_cat(c_data, class_c_data, "\n", NULL);
+ FREEMEM(class_c_data);
+ char *vt_boot = CFCBindClass_to_vtable_bootstrap(class_binding);
+ vt_bootstrap = CFCUtil_cat(vt_bootstrap, vt_boot, NULL);
+ FREEMEM(vt_boot);
+ char *vt_reg = CFCBindClass_to_vtable_register(class_binding);
+ vt_register = CFCUtil_cat(vt_register, vt_reg, NULL);
+ FREEMEM(vt_reg);
CFCBase_decref((CFCBase*)class_binding);
const char *privacy_sym = CFCClass_privacy_symbol(klass);
privacy_syms = CFCUtil_cat(privacy_syms, "#define ",
@@ -274,27 +285,33 @@ S_write_parcel_c(CFCBindCore *self) {
char pattern[] =
"%s\n"
"\n"
- "#define C_LUCY_ZOMBIECHARBUF\n"
- "#define C_LUCY_VTABLE\n"
"%s\n"
"#include \"parcel.h\"\n"
- "#include \"Lucy/Object/CharBuf.h\"\n"
"#include \"Lucy/Object/VTable.h\"\n"
"%s\n"
"\n"
"%s\n"
"\n"
+ "void\n"
+ "cfish_bootstrap_parcel() {\n"
+ "%s"
+ "\n"
+ "%s"
+ "}\n"
+ "\n"
"%s\n";
size_t size = sizeof(pattern)
+ strlen(self->header)
+ strlen(privacy_syms)
+ strlen(includes)
- + strlen(content)
+ + strlen(c_data)
+ + strlen(vt_bootstrap)
+ + strlen(vt_register)
+ strlen(self->footer)
+ 50;
char *file_content = (char*)MALLOCATE(size);
sprintf(file_content, pattern, self->header, privacy_syms, includes,
- content, self->footer);
+ c_data, vt_bootstrap, vt_register, self->footer);
// Unlink then open file.
const char *src_dest = CFCHierarchy_get_source_dest(hierarchy);
@@ -306,7 +323,8 @@ S_write_parcel_c(CFCBindCore *self) {
FREEMEM(privacy_syms);
FREEMEM(includes);
- FREEMEM(content);
- FREEMEM(file_content);
+ FREEMEM(c_data);
+ FREEMEM(vt_bootstrap);
+ FREEMEM(vt_register);
}
diff --git a/clownfish/src/CFCPerl.c b/clownfish/src/CFCPerl.c
index 8a07206..376ffb7 100644
--- a/clownfish/src/CFCPerl.c
+++ b/clownfish/src/CFCPerl.c
@@ -249,13 +249,6 @@ S_write_boot_c(CFCPerl *self) {
if (CFCClass_inert(klass)) { continue; }
- // Ignore return value from VTable_add_to_registry, since it's OK if
- // multiple threads contend for adding these permanent VTables and some
- // fail.
- registrations
- = CFCUtil_cat(registrations, " cfish_VTable_add_to_registry(",
- CFCClass_full_vtable_var(klass), ");\n", NULL);
-
// Add aliases for selected KinoSearch classes which allow old indexes
// to be read.
CFCPerlClass *class_binding = CFCPerlClass_singleton(class_name);
@@ -314,6 +307,9 @@ S_write_boot_c(CFCPerl *self) {
"%s() {\n"
" AV *isa;\n"
" cfish_ZombieCharBuf *alias = CFISH_ZCB_WRAP_STR(\"\", 0);\n"
+ "\n"
+ " cfish_bootstrap_parcel();\n"
+ "\n"
"%s\n"
"%s\n"
"}\n"
diff --git a/core/Lucy/Object/VTable.c b/core/Lucy/Object/VTable.c
index 62cded2..65ced8f 100644
--- a/core/Lucy/Object/VTable.c
+++ b/core/Lucy/Object/VTable.c
@@ -16,6 +16,7 @@
#define C_LUCY_VTABLE
#define C_LUCY_OBJ
+#define C_LUCY_VIEWCHARBUF
#define LUCY_USE_SHORT_NAMES
#define CHY_USE_SHORT_NAMES
@@ -39,6 +40,37 @@ S_scrunch_charbuf(CharBuf *source, CharBuf *target);
LockFreeRegistry *VTable_registry = NULL;
+#include <stdio.h>
+
+VTable*
+VTable_bootstrap(VTable *self, VTable *parent, const char *name, int flags,
+ void *x, size_t obj_alloc_size, void *callbacks,
+ cfish_method_t *methods) {
+ self->vtable = CFISH_VTABLE;
+ self->ref.count = 1;
+ self->parent = parent;
+ self->flags = flags;
+ self->x = x;
+ self->obj_alloc_size = obj_alloc_size;
+ self->callbacks = callbacks;
+
+ ViewCharBuf *vcb =
+ (ViewCharBuf*)Memory_wrapped_calloc(sizeof(ViewCharBuf), 1);
+ vcb->vtable = VIEWCHARBUF;
+ vcb->ref.count = 1;
+ ViewCB_init(vcb, name, strlen(name));
+ self->name = (CharBuf*)vcb;
+
+ int n;
+ for (n = 0; methods[n]; ++n) {
+ self->methods[n] = methods[n];
+ }
+ self->vt_alloc_size = offsetof(cfish_VTable, methods)
+ + n * sizeof(cfish_method_t);
+
+ return self;
+}
+
void
VTable_destroy(VTable *self) {
THROW(ERR, "Insane attempt to destroy VTable for class '%o'", self->name);
diff --git a/core/Lucy/Object/VTable.cfh b/core/Lucy/Object/VTable.cfh
index a3c413f..593ba4c 100644
--- a/core/Lucy/Object/VTable.cfh
+++ b/core/Lucy/Object/VTable.cfh
@@ -37,6 +37,11 @@ class Lucy::Object::VTable inherits Lucy::Object::Obj {
inert LockFreeRegistry *registry;
inert size_t offset_of_parent;
+ inert VTable*
+ bootstrap(VTable *self, VTable *parent, const char *name, int flags,
+ void *x, size_t obj_alloc_size, void *callbacks,
+ cfish_method_t *methods);
+
/** Return a singleton. If a VTable can be found in the registry based on
* the supplied class name, it will be returned. Otherwise, a new VTable
* will be created using [parent] as a base.
--
1.7.9.5