Skip to content

Commit

Permalink
plugin: Optimize module hooks by caching the functions called for each.
Browse files Browse the repository at this point in the history
... or is it? There has always been a measurable performance drop caused by
iterating through the whole list of functions and comparing each and every
function name. With the "thread_exit" function, this has become even worse.

Building a JSON array of function pointer for each hook in advance removes this
drop. Thanks to Jansson's reference counting, this even comes at no additional
cost in memory (as if that ever mattered). And as a side effect, this also
guarantees that the execution order in mod_func_run() at least follows the
order of DLL loading, even though the order of functions *inside* a DLL remains
undefined.
  • Loading branch information
nmlgc committed Aug 4, 2014
1 parent 3efc0fe commit 705a8da
Show file tree
Hide file tree
Showing 2 changed files with 51 additions and 25 deletions.
68 changes: 45 additions & 23 deletions thcrap/src/plugin.c
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include "thcrap.h"

static json_t *funcs = NULL;
static json_t *mod_funcs = NULL;
static json_t *plugins = NULL;

void* func_get(const char *name)
Expand All @@ -24,9 +25,20 @@ int plugin_init(HMODULE hMod)
if(!funcs) {
funcs = json_object();
}
if(!mod_funcs) {
mod_funcs = json_object();
}
if(!ret) {
mod_func_run(funcs_new, "init", NULL);
mod_func_run(funcs_new, "detour", NULL);
json_t *mod_funcs_new = mod_func_build(funcs_new);
const char *key;
json_t *val;
mod_func_run(mod_funcs_new, "init", NULL);
mod_func_run(mod_funcs_new, "detour", NULL);
json_object_foreach(mod_funcs_new, key, val) {
json_t *funcs_old = json_object_get_create(mod_funcs, key, JSON_ARRAY);
json_array_extend(funcs_old, val);
}
json_decref(mod_funcs_new);
}
json_object_merge(funcs, funcs_new);
json_decref(funcs_new);
Expand Down Expand Up @@ -68,6 +80,7 @@ int plugins_close(void)
json_t *val;

funcs = json_decref_safe(funcs);
mod_funcs = json_decref_safe(mod_funcs);

log_printf("Removing plug-ins...\n");
json_object_foreach(plugins, key, val) {
Expand All @@ -80,33 +93,42 @@ int plugins_close(void)
return 0;
}

void mod_func_run(json_t *funcs, const char *pattern, void *param)
json_t* mod_func_build(json_t *funcs)
{
if(json_is_object(funcs) && pattern) {
STRLEN_DEC(pattern);
size_t suffix_len = strlen("_mod_%s") + pattern_len;
VLA(char, suffix, suffix_len);
const char *key;
json_t *val;
suffix_len = snprintf(suffix, suffix_len, "_mod_%s", pattern);
json_object_foreach(funcs, key, val) {
size_t key_len = strlen(key);
const char *key_suffix = key + (key_len - suffix_len);
if(
key_len > suffix_len
&& !memcmp(key_suffix, suffix, suffix_len)
) {
mod_call_type func = (mod_call_type)json_integer_value(val);
if(func) {
func(param);
}
json_t *ret = NULL;
const char *infix = "_mod_";
size_t infix_len = strlen(infix);
const char *key;
json_t *val;
json_object_foreach(funcs, key, val) {
const char *p = strstr(key, infix);
if(p) {
json_t *arr = NULL;
p += infix_len;
if(!ret) {
ret = json_object();
}
arr = json_object_get_create(ret, p, JSON_ARRAY);
json_array_append(arr, val);
}
}
return ret;
}

void mod_func_run(json_t *mod_funcs, const char *pattern, void *param)
{
json_t *func_array = json_object_get(mod_funcs, pattern);
size_t i;
json_t *val;
json_array_foreach(func_array, i, val) {
mod_call_type func = (mod_call_type)json_integer_value(val);
if(func) {
func(param);
}
VLA_FREE(suffix);
}
}

void mod_func_run_all(const char *pattern, void *param)
{
mod_func_run(funcs, pattern, param);
mod_func_run(mod_funcs, pattern, param);
}
8 changes: 6 additions & 2 deletions thcrap/src/plugin.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,12 @@ void* func_get(const char *name);
// Module function type.
typedef void (*mod_call_type)(void *param);

// Runs every exported function ending in "*_mod_[suffix]" across the
// functions in [funcs]. The order of execution is undefined.
// Builds a JSON object all module hook functions in [funcs].
json_t* mod_func_build(json_t *funcs);

// Runs every module hook function for [suffix] in [mod_funcs]. The execution
// order of the hook functions follows the order their DLLs were originally
// loaded in, but is undefined within the functions of a single DLL.
void mod_func_run(json_t *funcs, const char *suffix, void *param);

// Calls mod_fun_run() with all registered functions from all thcrap DLLs.
Expand Down

0 comments on commit 705a8da

Please sign in to comment.