-
Notifications
You must be signed in to change notification settings - Fork 113
Open
Description
I have started a branch symbols in my repos:
https://github.com/Neopallium/cr/tree/symbols
It has added support for:
- Re-loadable custom plugin symbols
cr_symbol. It is an opaque structure that holds a pointer to a symbol from the plugin. When the plugin reloads, all re-loadable symbols are updated. - Support for safely calling plugin callbacks.
cr_closure - Nested
setjmpprotected calls (Allows nesting of plugins). This is only needed on linux/macOS, windows uses__try/__exceptwhich doesn't use a global and "should" be nesting safe?
Limits:
- Don't call into the same plugin more then once in a call chain (i.e. Plugin A -> Plugin B -> Plugin A "crash", would cause Plugin A to possible reload while it is still on the call stack.)
TODOS:
- Doesn't free
cr_symbolorcr_closure. I plan to add reference counting ofcr_symbol - Always creates a new
cr_symbol, should check if one already exists for the requested symbol. - Tests
- Untested on windows/macOS.
- Add
cr_closure_freeto release allocatedcr_closureand release reference tocr_symbol - Add
cr_closure_initto allow embeddingcr_closureinside the callback'suserdataobject. - Try adding a "protected call" counter to
cr_pluginto prevent reload/rollback while a protected call is active for that plugin. Or try tolongjmp/throwback to the first protected call.
New public API:
struct cr_symbol *cr_plugin_get_symbol(struct cr_plugin *ctx, const char *name)
Returns a reference to a re-loadable plugin symbol.
struct cr_closure *cr_plugin_new_closure(struct cr_symbol *symbol, void *userdata)
Used to wrap the plugin's callback and userdata, so it can be safely unwrapped and called from host-side "trampoline" function.
Example (See working example in repos: samples/fake_gui.*, samples/cb_host.*, samples/cb_guest.c):
- Fake GUI event callback API
typedef int (*cb_mouse_event_func)(void *userdata, int x, int y, int buttons);
int fake_register_mouse_events(cb_mouse_event_func cb, void *userdata);
int fake_send_mouse_event(int x, int y, int buttons);- Host-side support
// We need a "trampoline" function that will never be unloaded/reloaded.
// Only need one for each kind of callback function (typedef)
// This can be defined in a small common shared library that is dynamically linked into the host and plugins.
int host_trampoline_mouse_events(void *userdata, int x, int y, int buttons) {
CR_CLOSURE_CALL_START(cb_mouse_event_func, userdata, true)
if (cb_func) {
return cb_func(closure->userdata, x, y, buttons);
} else {
fprintf(stdout, "No plugin callback. Can happen when the plugin is closed `cr_plugin_close`\n");
}
CR_CLOSURE_CALL_END
return 0;
}
///// Normal host-side code follows.....- Guest plugin callback
static struct cr_symbol * CR_STATE g_plugin_mouse_cb = NULL;
static void register_mouse_events(struct cr_plugin *ctx, void *userdata) {
if (!g_plugin_mouse_cb) {
// Only need to initialize `g_plugin_mouse_cb` on the first `CR_LOAD`
g_plugin_mouse_cb = cr_plugin_get_symbol(ctx, "plugin_mouse_events");
// We can create any number of `cr_closure` as needed and re-use `cr_symbol`
struct cr_closure *closure = cr_symbol_new_closure(g_plugin_mouse_cb, userdata);
fake_register_mouse_events(host_trampoline_mouse_events, closure);
}
}
// Plugin callback for mouse events.
CR_EXPORT int plugin_mouse_events(void *userdata, int x, int y, int buttons) {
struct cr_plugin *ctx = (struct cr_plugin *)userdata;
fprintf(stdout, "Guest: mouse event: (%d, %d, 0x%x): ver=%d\n", x, y, buttons);
return 0;
}
CR_EXPORT int cr_main(struct cr_plugin *ctx, enum cr_op operation) {
if (operation == CR_LOAD) {
// Reload/rollback unsafe register of mouse events callback
//fake_register_mouse_events(plugin_mouse_events, ctx);
// Reload/rollback safe.
register_mouse_events(ctx, ctx); // We are just using `ctx` as our plugin userdata.
}
return 0;
}rokups
Metadata
Metadata
Assignees
Labels
No labels