diff --git a/src/lpm_log.h b/src/lpm_log.h new file mode 100644 index 0000000..09b8ab4 --- /dev/null +++ b/src/lpm_log.h @@ -0,0 +1,22 @@ +#ifndef LUA_PLUGIN_MANAGE_LPM_LOG_H +#define LUA_PLUGIN_MANAGE_LPM_LOG_H + +#include + +/* 简单定义一个日志输出, 可根据实际的日志输出调整定义 */ +/* define some basic log macros */ +#define LOG(level, format, ...) printf("[%s][%s]%d: " format "\n", #level, __FUNCTION__, __LINE__, ##__VA_ARGS__); +#define LOGDEBUG(...) LOG(DEBUG, __VA_ARGS__) +// #define LOGDEBUG(...) +#define LOGINFO(...) LOG(INFO, __VA_ARGS__) +// #define LOGINFO(...) +#define LOGNOTICE(...) LOG(INFO, __VA_ARGS__) +// #define LOGNOTICE(...) +#define LOGWARN(...) LOG(WARNING, __VA_ARGS__) +// #define LOGWARN(...) +#define LOGERROR(...) LOG(ERROR, __VA_ARGS__) +// #define LOGERROR(...) +#define LOGFATAL(...) LOG(FALT, __VA_ARGS__) +// #define LOGFATAL(...) + +#endif \ No newline at end of file diff --git a/src/lua_plugin_binding.c b/src/lua_plugin_binding.c new file mode 100644 index 0000000..bcf5039 --- /dev/null +++ b/src/lua_plugin_binding.c @@ -0,0 +1,529 @@ +/************************************************************************* + > File Name: lua_plugin_binding.c + > Author: + > Created Time: 2024-08 + > Encoding : UTF-8 + ************************************************************************/ + +/************************************************************************* + * version + * [ v0.1 ] + * 08 -02 + * 1. 实现函数 + * int lua_cbinding_function; + * int lua_cbinding_function_remove; + * int lua_cbinding_data; + * int lua_cbinding_data_remove; + * int lua_cbinding_functions; + * int lua_cbinding_datas; + * int lua_plugin_manage_session_regist; + * ** 注册函数lua_plugin_manage_session_regist还需要补充 + ************************************************************************/ +#include "lua_plugin_manage_internal.h" + +#include +#include + +/* 需要注册至lua中供lua调用的所有函数原型 */ +int lua_plugin_manage_session_regist(lua_State * state); + +/* 需要注册至状态机中的函数定义在链表中, 会依次完成注册 */ +struct lua_binding_function lua_bind_functions[] = { + {lua_plugin_manage_session_regist, "register", "plugin_manage"}, + {NULL, NULL, NULL}, +}; + +/* 需要注册至状态机中的数据定义在链表中, 会依次完成注册 */ +/* 为了实现注册过程的统一, 所有数据类型均使用string作为value格式 */ +struct lua_binding_data lua_bind_datas[] = { + {DATATYPE_BOOL, "true", "test_data_bool", "plugin_manage"}, + {DATATYPE_INT, "1000", "test_data_int", "plugin_manage"}, + {DATATYPE_NUM, "100.001", "test_data_double", "plugin_manage"}, + {DATATYPE_STRING, "this is a string test", "test_data_string", "plugin_manage"}, + {DATATYPE_END, NULL, NULL, NULL}, +}; + +/* + * Function: lua_cbinding_function + * Input: | lua_State * | state | 需要注册函数的状态机 + * | lua_CFunction | bind_function | 需要注册的函数 + * | const char * | function_name | 注册函数的名称 + * | const char * | space_name | 注册函数的命名空间 + * Output: + * Return: | 0 | 注册成功 + * | -1 | 传入的参数有错误 + * | -2 | 命名空间已经存在, 且命名空间不是table类型, 无法新增成员 + * | -3 | 命名空间中已经存在函数名称对应成员, 无法完成覆盖 + * | -4 | 存在全局的函数名称, 无法完成注册 + * Description: 将一个C函数注册至lua中 + */ +int lua_cbinding_function( + lua_State *state, + lua_CFunction bind_function, + const char *function_name, + const char *space_name) +{ + if (__glibc_unlikely(!state || !bind_function || !function_name)) + return -1; + + if (space_name) + { + /* 包含space_name, 调用时为 space_name.function_name */ + lua_getglobal(state, space_name); + if (lua_gettop(state) == 0 || lua_type(state, -1) == LUA_TNIL) + { + /* 没有该命名空间, 创建一个 */ + lua_newtable(state); + lua_setglobal(state, space_name); + lua_settop(state, 0); + lua_getglobal(state, space_name); + } + else + { + /* 该全局已经存在 */ + if (lua_type(state, -1) != LUA_TTABLE) + { + /* spacename已经被占用, 并且不是table, 无法插入数据 */ + lua_settop(state, 0); + return -2; + } + + lua_getfield(state, -1, function_name); + if (lua_type(state, -1) != LUA_TNIL) + { + /* 在待插入的global table中存在与function_name重名的成员 */ + lua_settop(state, 0); + return -3; + } + lua_pop(state, 1); /* 正确的情况下此时栈顶为nil, 弹出一个nil值 */ + } + + lua_pushcfunction(state, bind_function); + lua_setfield(state, -2, function_name); + } + else + { + lua_getglobal(state, function_name); + if (lua_type(state, -1) != LUA_TNIL) + { + /* 存在与function_name重名的全局成员 */ + lua_settop(state, 0); + return -4; + } + lua_pop(state, 1); + + /* 不包含space_name, 调用时直接使用function_name调用 */ + lua_pushcfunction(state, bind_function); + lua_setglobal(state, function_name); + } + + lua_settop(state, 0); /* 操作完成, 弹出所有元素 */ + return 0; +} + +/* + * Function: lua_cbinding_function_remove + * Input: | lua_State * | state | 需要删除注册函数的状态机 + * | const char * | function_name | 需要删除的函数名称 + * | const char * | space_name | 需要删除的命名空间名称 + * Output: + * Return: | 0 | 删除成功 + * | -1 | 参数错误 + * | -2 | 命名空间存在但不是一个table + * | -3 | 命名空间中存在函数名称对应成员, 但是类型不是函数 + * | -4 | 函数名称对应的成员不是一个函数 + * Description: 从状态机中删除一个C注册的函数, 删除过程中将该函数在lua状态的引用置为nil + */ +int lua_cbinding_function_remove( + lua_State *state, + const char *function_name, + const char *space_name) +{ + if (__glibc_unlikely(!state || !function_name)) + return -1; + + if (space_name) + { + /* 检查该命名空间是否存在 */ + lua_getglobal(state, space_name); + if (lua_type(state, -1) != LUA_TTABLE) + { + /* 命名空间存在且不是table */ + lua_settop(state, 0); + return -2; + } + /* 检查该命名空间内是否存在function_name */ + lua_getfield(state, -1, function_name); + if (lua_type(state, -1) != LUA_TFUNCTION) + { + /* 命名空间存在内部元素名称为function_name, 但是类型不符 */ + lua_settop(state, 0); + return -3; + } + /* 删除该函数 */ + lua_pop(state, 1); + lua_pushnil(state); + lua_setfield(state, -2, function_name); + } + else + { + /* 获取全局数据, 检查function_name */ + lua_getglobal(state, function_name); + if (lua_type(state, -1) != LUA_TFUNCTION) + { + /* 类型不符 */ + lua_settop(state, 0); + return -4; + } + /* 删除该函数 */ + lua_pop(state, 1); + lua_pushnil(state); + lua_setglobal(state, function_name); + } + + lua_settop(state, 0); /* 操作完成, 弹出所有元素 */ + return 0; +} + +/* + * Function: lua_cbinding_data + * Input: | lua_State * | state | 需要注册数据的状态机 + * | struct lua_binding_data * | data | 需要注册至状态机中的数据 + * Output: + * Return: | 0 | 注册成功 + * | -1 | 传入的参数有错误 + * | -2 | 命名空间已经存在, 且命名空间不是table类型, 无法新增成员 + * | -3 | 命名空间中已经存在函数名称对应成员, 无法完成覆盖 + * | -4 | 无法识别data的数据类型 + * | -5 | 全局变量名称已经有成员, 不为空 + * Description: 将一个变量注册至lua状态机中 + */ +int lua_cbinding_data( + lua_State *state, + struct lua_binding_data *data) +{ + if (__glibc_unlikely(!state || !data)) + { + return -1; + } + enum DATATYPE data_type = data->binding_data_type; + char *value = data->binding_data_value; + char *data_name = data->binding_data_name; + char *space_name = data->binding_data_space_name; + if (__glibc_unlikely(!value || !data_name)) + return -1; + + if (space_name) + { + /* 包含space_name */ + lua_getglobal(state, space_name); + if (lua_gettop(state) == 0 || lua_type(state, -1) == LUA_TNIL) + { + /* 没有该命名空间, 创建一个 */ + lua_newtable(state); + lua_setglobal(state, space_name); + lua_settop(state, 0); + lua_getglobal(state, space_name); + } + else + { + /* 该全局已经存在 */ + if (lua_type(state, -1) != LUA_TTABLE) + { + /* spacename已经被占用, 并且不是table, 无法插入数据 */ + lua_settop(state, 0); + return -2; + } + + lua_getfield(state, -1, data_name); + if (lua_type(state, -1) != LUA_TNIL) + { + /* 在待插入的global table中存在与function_name重名的成员 */ + lua_settop(state, 0); + return -3; + } + lua_pop(state, 1); /* 正确的情况下此时栈顶为nil, 弹出一个nil值 */ + } + + /* 不同类型需要使用不同的入栈函数 */ + switch (data_type) + { + case DATATYPE_BOOL: + if (strstr(value, "true") || strstr(value, "TRUE")) + lua_pushboolean(state, 1); + else if (strstr(value, "false") || strstr(value, "false")) + lua_pushboolean(state, 0); + break; + case DATATYPE_INT: + lua_pushinteger(state, atoi(value)); + break; + case DATATYPE_NUM: + lua_pushnumber(state, (lua_Number)atof(value)); + break; + case DATATYPE_STRING: + lua_pushstring(state, (const char *)value); + break; + default: + /* 未识别的类型 */ + lua_settop(state, 0); + return -4; + } + lua_setfield(state, -2, data_name); + } + else + { + lua_getglobal(state, data_name); + if (lua_type(state, -1) != LUA_TNIL) + { + lua_settop(state, 0); + /* 存在与data_name重名的全局成员 */ + return -5; + } + lua_pop(state, 1); + + /* 不同类型需要使用不同的入栈函数 */ + switch (data_type) + { + case DATATYPE_BOOL: + if (strstr(value, "true") || strstr(value, "TRUE")) + lua_pushboolean(state, 1); + else if (strstr(value, "false") || strstr(value, "false")) + lua_pushboolean(state, 0); + break; + case DATATYPE_INT: + lua_pushinteger(state, atoi(value)); + break; + case DATATYPE_NUM: + lua_pushnumber(state, (lua_Number)atof(value)); + break; + case DATATYPE_STRING: + lua_pushstring(state, (const char *)value); + break; + default: + /* 未识别的类型 */ + lua_settop(state, 0); + return -4; + } + lua_setglobal(state, data_name); + } + + lua_settop(state, 0); /* 操作完成, 弹出所有元素 */ + return 0; +} + +/* + * Function: lua_cbinding_data_remove + * Input: | lua_State * | state | 需要删除注册数据的状态机 + * | const char * | data_name | 需要删除的数据名称 + * | const char * | space_name | 需要删除的命名空间名称 + * Output: + * Return: | 0 | 删除成功 + * | -1 | 参数错误 + * | -2 | 命名空间存在但不是一个table + * | -3 | 命名空间中存在数据名称对应成员, 但是类型不符 + * | -4 | 函数名称对应的成员类型不符 + * Description: 从状态机中删除一个C注册的数据, 删除过程中将该名称在lua状态的引用置为nil + */ +int lua_cbinding_data_remove( + lua_State *state, + const char *data_name, + const char *space_name) +{ + if (__glibc_unlikely(!state || !data_name)) + return -1; + + if (space_name) + { + /* 检查该命名空间是否存在 */ + lua_getglobal(state, space_name); + if (lua_type(state, -1) != LUA_TTABLE) + { + /* 命名空间存在且不是table */ + lua_settop(state, 0); + return -2; + } + /* 检查该命名空间内是否存在data_name */ + lua_getfield(state, -1, data_name); + int data_type = lua_type(state, -1); + if (data_type != LUA_TSTRING && data_type != LUA_TNUMBER && data_type != LUA_TBOOLEAN) + { + /* 命名空间存在内部元素名称为data_name, 但是类型不符 */ + lua_settop(state, 0); + return -3; + } + else if (data_type == LUA_TNIL) + { + /* 没有该名称的元素 */ + lua_settop(state, 0); + return 0; + } + /* 删除该函数 */ + lua_pop(state, 1); + lua_pushnil(state); + lua_setfield(state, -2, data_name); + } + else + { + /* 获取全局数据, 检查function_name */ + lua_getglobal(state, data_name); + int data_type = lua_type(state, -1); + if (data_type != LUA_TSTRING && data_type != LUA_TNUMBER && data_type != LUA_TBOOLEAN) + { + /* 类型不符 */ + lua_settop(state, 0); + return -4; + } + else if (data_type == LUA_TNIL) + { + /* 没有该名称的元素 */ + lua_settop(state, 0); + return 0; + } + /* 删除该函数 */ + lua_pop(state, 1); + lua_pushnil(state); + lua_setglobal(state, data_name); + } + + lua_settop(state, 0); /* 操作完成, 弹出所有元素 */ + return 0; +} + +/* + * Function: lua_cbinding_functions + * Input: | lua_State * | state | 需要绑定函数的状态机实例 + * Output: + * Return: | -1 | 参数错误 + * | 0 | 成功 + * | >0 | 绑定有错的函数数量 + * Description: 绑定所有需要在lua中调用的C函数 + */ +int lua_cbinding_functions(lua_State *state) +{ + if (__glibc_unlikely(!state)) + return -1; + int bind_function_count = sizeof(lua_bind_functions) / sizeof(struct lua_binding_function); + + int bind_ret = 0; + int failed_count = 0; + for (int i = 0; i < bind_function_count; ++i) + { + if (lua_bind_functions[i].binding_function && lua_bind_functions[i].binding_function_name) + { + bind_ret = lua_cbinding_function(state, + lua_bind_functions[i].binding_function, + (const char *)lua_bind_functions[i].binding_function_name, + (const char *)lua_bind_functions[i].binding_function_space_name); + if (bind_ret) + { + LOGERROR("binding function failed, ret is %d, function name is %s\n", bind_ret, lua_bind_functions[i].binding_function_name); + failed_count += 1; + } + } + } + return failed_count; +} + +/* + * Function: lua_cbinding_datas + * Input: | lua_State * | state | 需要绑定数据的状态机实例 + * Output: + * Return: | -1 | 参数错误 + * | 0 | 成功 + * | >0 | 绑定出错的数据数量 + * Description: 绑定所有data数据 + */ +int lua_cbinding_datas(lua_State *state) +{ + if (__glibc_unlikely(!state)) + return -1; + int bind_data_count = sizeof(lua_bind_datas) / sizeof(struct lua_binding_data); + + int bind_ret = 0; + int failed_count = 0; + for (int i = 0; i < bind_data_count; ++i) + { + if (lua_bind_datas[i].binding_data_value && lua_bind_datas[i].binding_data_name) + { + bind_ret = lua_cbinding_data(state, &lua_bind_datas[i]); + if (bind_ret) + { + LOGERROR("binding data failed, ret is %d, data is [%s]%s", bind_ret, lua_bind_datas[i].binding_data_name, lua_bind_datas[i].binding_data_value); + failed_count += 1; + } + } + } + return failed_count; +} + +/* + * Function: + * Input: + * Output: + * Return: + * Description: + */ +int lua_plugin_manage_session_regist(lua_State *state) +{ + /* 参数个数检查 */ + if (lua_gettop(state) != 4) + { + lua_settop(state, 0); + return 0; + } + + /* 参数类型检查 */ + if (lua_type(state, -1) != LUA_TLIGHTUSERDATA || lua_type(state, -2) != LUA_TFUNCTION || + lua_type(state, -3) != LUA_TFUNCTION || lua_type(state, -4) != LUA_TLIGHTUSERDATA) + { + lua_settop(state, 0); + return 0; + } + + /* 取出处理第四个参数 */ + struct lua_plugin_env *plugin_env = (struct lua_plugin_env *)lua_topointer(state, -1); + if (!plugin_env) + { + lua_settop(state, 0); + return 0; + } + lua_pop(state, 1); + + /* 取出处理第三个参数 */ + int ctx_free_id = luaL_ref(state, LUA_REGISTRYINDEX); + if (ctx_free_id == LUA_REFNIL) + { + lua_settop(state, 0); + return 0; + } + lua_pop(state, 1); + + /* 取出处理第二个参数 */ + int ctx_new_id = luaL_ref(state, LUA_REGISTRYINDEX); + if (ctx_new_id == LUA_REFNIL) + { + lua_settop(state, 0); + return 0; + } + lua_pop(state, 1); +#if 0 + /* 取出处理第一个参数 */ + struct stellar * st = (struct stellar *)lua_topointer(state, -1); + if ( !st ) { + lua_settop(state, 0); + return 0; + } + lua_pop(state, 1); + /* 在stellar中注册, 获取注册id */ + int plugin_id = stellar_session_plugin_register(st, lpm_ctx_new_func, lpm_ctx_free_func, (void *)plugin_env); + + /* 将注册完成的新插件插入到队列中 */ + struct lua_session_plugin session_plugin; + memset(&session_plugin, 0, sizeof(session_plugin)); + session_plugin_instance_init(&session_plugin, state, plugin_id, ctx_new_id, ctx_free_id); + + utarray_push_back(plugin_env->lua_plugin_env_plugin_array, &session_plugin); + lua_pushinteger(state, plugin_id); +#endif + return 1; +} diff --git a/src/lua_plugin_manage.c b/src/lua_plugin_manage.c new file mode 100644 index 0000000..df155ff --- /dev/null +++ b/src/lua_plugin_manage.c @@ -0,0 +1,649 @@ +/************************************************************************* + > File Name: lua_plugin_manage.c + > Author: + > Created Time: 2024-08 + > Encoding : UTF-8 + ************************************************************************/ + +/************************************************************************* + * version + * [ v0.1 ] + * 08-05 + * 1. 实现函数 + * void specific_instance_copy; + * void specific_instance_destory; + * int script_instance_init_byname; + * int script_instance_init_byrefid; + * void script_instance_clean; + * int script_execute; (暂未实现) + * int session_plugin_instance_init; + * void session_plugin_instance_destory; + * int plugin_env_instance_init; + * void plugin_env_instance_destory; + * int thread_state_instance_init; + * void thread_state_instance_destory; + * int thread_state_instance_load; + * int lua_plugin_manage_config_load; + * int lua_plugin_manage_thread_load; + ************************************************************************/ +#include "lua_plugin_manage_internal.h" +#include "lpm_log.h" + +#include +#include +#include +#include + +#include + +static UT_icd specific_icd = {sizeof(struct lua_config_specific), NULL, specific_instance_copy, specific_instance_destory}; +static UT_icd session_plugin_icd = {sizeof(struct lua_session_plugin), NULL, NULL, session_plugin_instance_destory}; +static UT_icd plugin_env_icd = {sizeof(struct lua_plugin_env), NULL, NULL, plugin_env_instance_destory}; +static UT_icd thread_state_icd = {sizeof(struct lua_thread_state), NULL, NULL, thread_state_instance_destory}; + +/* + * Function: specific_instance_copy + * Input: | void * | dst | 拷贝数据目的地址 + * | void * | src | 拷贝数据源地址 + * Output: + * Return: + * Description: 配置的插件信息拷贝函数, 由于可能存在多线程, 将数据完整拷贝一次 + */ +void specific_instance_copy(void *dst, const void *src) +{ + struct lua_config_specific *dst_spec = (struct lua_config_specific *)dst; + struct lua_config_specific *src_spec = (struct lua_config_specific *)src; + if (__glibc_unlikely(!dst_spec || !src_spec)) + return; + dst_spec->lua_config_specific_file = strdup(src_spec->lua_config_specific_file); + // dst_spec->lua_config_specific_name = strdup(src_spec->lua_config_specific_name); + dst_spec->lua_config_specific_load_func = strdup(src_spec->lua_config_specific_load_func); + dst_spec->lua_config_specific_unload_func = strdup(src_spec->lua_config_specific_unload_func); + return; +} + +/* + * Function: specific_instance_destory + * Input: | void * | elt | 待销毁的specific实例 + * Output: + * Return: + * Description: 销毁一个specific实例, 并释放内部占用的内存 + */ +void specific_instance_destory(void *elt) +{ + struct lua_config_specific *element = (struct lua_config_specific *)elt; + if (!element) + return; + if (element->lua_config_specific_file) + free(element->lua_config_specific_file); + // if (element->lua_config_specific_name) + // free(element->lua_config_specific_name); + if (element->lua_config_specific_load_func) + free(element->lua_config_specific_load_func); + if (element->lua_config_specific_unload_func) + free(element->lua_config_specific_unload_func); + return; +} + +/* + * Function: script_instance_init_byname + * Input: | struct lua_script * | script | 需要进行初始化的脚本结构 + * | lua_State * | state | 脚本注册使用的状态机 + * | const char * | filepath | 脚本加载的文件路径 + * | const char * | funcname | 脚本加载的函数名称 + * Output: + * Return: | 0 | 加载成功 + * | -1 | 参数错误 + * | -3 | 状态机读取文件失败 + * | -4 | 状态机加载文件后运行失败 + * | -5 | 加载函数过程中未找到该函数 + * | -6 | 创建函数引用ID失败 + * Description: 将一个lua文件中的函数创建成一个lua_script结构, 方便调用运行 + */ +int script_instance_init_byname( + struct lua_script *script, + lua_State *state, + const char *filepath, + const char *funcname) +{ + if (__glibc_unlikely(!script || !state || !filepath || !funcname)) + return -1; + +#if 0 + /* 在加载配置文件的时候检查过了, 这里不检查 */ + if (access(filepath, F_OK)) + /* 文件不存在 */ + return -2; +#endif + if (luaL_loadfile(state, filepath)) + /* 加载文件失败 */ + return -3; + /* 仅加载不运行 */ + // lua_settop(state, 0); + /* Bugfix: 这里还是需要运行一次, 否则可能加载不完整 */ + if (lua_pcall(state, 0, 0, 0)) + { + /* 文件加载完成后检查存在错误 */ + lua_settop(state, 0); + return -4; + } + lua_settop(state, 0); + + /* 加载全局变量, 找到函数后进行类型判断 */ + lua_getglobal(state, funcname); + if (lua_type(state, -1) != LUA_TFUNCTION) + { + /* 在已经加载的文件中未找到对应的函数名称 */ + lua_settop(state, 0); + return -5; + } + /* 创建索引 */ + int ref_id = luaL_ref(state, LUA_REGISTRYINDEX); + if (ref_id == LUA_REFNIL) + { + /* 索引创建失败 */ + lua_settop(state, 0); + return -6; + } + + script_instance_init_byrefid(script, state, ref_id); + lua_settop(state, 0); + return 0; +} + +/* + * Function: script_instance_init_byrefid + * Input: | struct lua_script * | script | 需要进行初始化的脚本结构 + * | lua_State * | state | 脚本注册使用的状态机 + * | int | ref_id | 代码已经在状态机中生成的refid + * Output: + * Return: | 0 | 初始化成功 + * | -1 | 参数错误 + * Description: 根据已经生成的refid初始化一个脚本结构实例 + */ +int script_instance_init_byrefid( + struct lua_script *script, + lua_State *state, + int ref_id) +{ + if (__glibc_unlikely(!script || !state || ref_id == LUA_REFNIL)) + return -1; + script->script_state = state; + script->script_ref_id = ref_id; + script->script_run_success = 0; + script->script_run_failed = 0; + script->script_total_ms = 0; + return 0; +} + +/* + * Function: script_instance_clean + * Input: | struct lua_script * | script | 需要清理的脚本实例 + * Output: + * Return: + * Description: 清理一个脚本, 并在状态机中释放该脚本的引用 + */ +void script_instance_clean(struct lua_script *script) +{ + if (__glibc_unlikely(!script)) + return; + + lua_State *state = script->script_state; + luaL_unref(state, LUA_REGISTRYINDEX, script->script_ref_id); + lua_settop(state, 0); + return; +} + +/* + * Function: + * Input: + * Output: + * Return: + * Description: + */ +int script_execute(struct lua_script *script) +{ + if (!script) + return -1; + return 0; +} + +/* + * Function: session_plugin_instance_init + * Input: | struct lua_session_plugin * | session_plugin | 需要进行初始化的插件实例 + * | lua_State * | state | 插件注册使用的状态机 + * | int | plugin_id | 插件注册完成后得到的插件编号 + * | int | new_refid | ctx_new函数在状态机中的引用 + * | int | free_refid | ctx_free函数在状态机中的引用 + * Output: + * Return: | 0 | 初始化成功 + * | -1 | 参数错误 + * Description: 初始化一个插件实例 + */ +int session_plugin_instance_init( + struct lua_session_plugin *session_plugin, + lua_State *state, + int plugin_id, + int new_refid, + int free_refid) +{ + if (__glibc_unlikely(!session_plugin || !state)) + return -1; + session_plugin->plugin_id = plugin_id; + script_instance_init_byrefid(&session_plugin->plugin_ctx_new_script, state, new_refid); + script_instance_init_byrefid(&session_plugin->plugin_ctx_free_script, state, free_refid); + return 0; +} + +/* + * Function: session_plugin_instance_destory + * Input: | void * | elt | 待销毁的插件实例 + * Output: + * Return: + * Description: 销毁一个插件实例, 并将插件中引用的函数在状态机中释放 + */ +void session_plugin_instance_destory(void *elt) +{ + if (__glibc_unlikely(!elt)) + return; + struct lua_session_plugin *session_plugin = (struct lua_session_plugin *)elt; + script_instance_clean(&session_plugin->plugin_ctx_new_script); + script_instance_clean(&session_plugin->plugin_ctx_free_script); + return; +} + +/* + * Function: plugin_env_instance_init + * Input: | struct lua_plugin_env * | plugin_env | 插件的运行环境实例 + * | lua_State * | state | 插件注册的状态机 + * | struct lua_config_specific * | specific | 插件需要加载的配置实例 + * Output: + * Return: | 0 | 初始化成功 + * | -1 | 参数错误 + * | -2 | 加载函数加载错误 + * | -3 | 卸载函数加载错误 + * | -4 | 创建全局函数变量失败 + * Description: 初始化一个插件的运行环境变量 + */ +int plugin_env_instance_init( + struct lua_plugin_env *plugin_env, + lua_State *state, + struct lua_config_specific *specific) +{ + if (__glibc_unlikely(!plugin_env || !state || !specific)) + return -1; + + memset(plugin_env, 0, sizeof(struct lua_plugin_env)); + plugin_env->lua_plugin_env_state = state; + plugin_env->lua_plugin_env_plugin_array = NULL; + // plugin_env->lua_plugin_env_name = strdup(specific->lua_config_specific_name); + /* 从文件中加载load函数, 但此时不调用 */ + if (script_instance_init_byname(&plugin_env->lua_plugin_env_load_func, + state, + specific->lua_config_specific_file, + specific->lua_config_specific_load_func)) + { + return -2; + } + /* 从文件中加载unload函数 */ + if (script_instance_init_byname(&plugin_env->lua_plugin_env_unload_func, + state, + specific->lua_config_specific_file, + specific->lua_config_specific_unload_func)) + { + script_instance_clean(&plugin_env->lua_plugin_env_load_func); + return -3; + } + /* 创建私有数据空间 */ + lua_newtable(plugin_env->lua_plugin_env_state); + int ref_id = luaL_ref(plugin_env->lua_plugin_env_state, LUA_REGISTRYINDEX); + if (ref_id == LUA_REFNIL) + return -4; + plugin_env->lua_plugin_env_ref_id = ref_id; + + return 0; +} + +/* + * Function: plugin_env_instance_destory + * Input: | void * | elt | 待销毁的插件运行环境实例 + * Output: + * Return: + * Description: 销毁一个插件运行环境实例, 并在状态机中释放其引用 + */ +void plugin_env_instance_destory(void *elt) +{ + if (__glibc_unlikely(!elt)) + return; + struct lua_plugin_env *plugin_env = (struct lua_plugin_env *)elt; + /* 释放已经注册的插件 */ + if (plugin_env->lua_plugin_env_plugin_array) + utarray_free(plugin_env->lua_plugin_env_plugin_array); + /* 释放插件的加载函数与卸载函数 */ + script_instance_clean(&plugin_env->lua_plugin_env_load_func); + script_instance_clean(&plugin_env->lua_plugin_env_unload_func); + /* 将lua中的私有环境数据取消引用 */ + if (plugin_env->lua_plugin_env_ref_id) + luaL_unref(plugin_env->lua_plugin_env_state, LUA_REGISTRYINDEX, plugin_env->lua_plugin_env_ref_id); + return; +} + +/* + * Function: thread_state_instance_init + * Input: | struct lua_thread_state * | thread_state | 待初始化的线程状态机 + * | int | state_id | 线程的ID号 + * Output: + * Return: | 0 | 初始化成功 + * | -1 | 参数错误 + * | -2 | 创建新的状态机失败 + * Description: 在线程中创建一个状态机, 并进行初始化 + */ +int thread_state_instance_init( + struct lua_thread_state *thread_state, + int state_id) +{ + if (__glibc_unlikely(!thread_state)) + return -1; + + lua_State *new_state = luaL_newstate(); + if (__glibc_unlikely(!new_state)) + return -2; + luaL_openlibs(new_state); + /* 绑定所有注册函数 */ + lua_cbinding_functions(new_state); + /* 绑定所有全局变量 */ + lua_cbinding_datas(new_state); + lua_settop(new_state, 0); + + memset(thread_state, 0, sizeof(struct lua_thread_state)); + thread_state->lua_thread_id = state_id; + thread_state->lua_thread_plugin_count = 0; + thread_state->lua_thread_state = new_state; + thread_state->lua_thread_env_array = NULL; + thread_state->lua_thread_begin_time = time(NULL); + + return 0; +} + +/* + * Function: thread_state_instance_destory + * Input: | void * | elt | 待销毁的状态机 + * Output: + * Return: + * Description: 销毁一个线程状态机 + */ +void thread_state_instance_destory(void *elt) +{ + if (__glibc_unlikely(!elt)) + return; + struct lua_thread_state *thread_state = (struct lua_thread_state *)elt; + /* 卸载所有已经注册的插件 */ + if (thread_state->lua_thread_env_array) + utarray_free(thread_state->lua_thread_env_array); + /* 关闭状态机 */ + if (thread_state->lua_thread_state) + lua_close(thread_state->lua_thread_state); + return; +} + +/* + * Function: thread_state_instance_load + * Input: | struct lua_thread_state * | thread_state | 进行初始化加载的状态机 + * | struct lua_plugin_manage_schema * | schema | 已经完成配置读取的全局实例 + * Output: + * Return: | 0 | 加载成功 + * | -1 | 参数错误 + * | -2 | 根据加载的配置初始化插件失败 + * Description: 在线程的状态机中依次加载插件 + */ +int thread_state_instance_load( + struct lua_thread_state *thread_state, + struct lua_plugin_manage_schema *schema) +{ + if (__glibc_unlikely(!thread_state || !schema)) + return -1; + + lua_State *state = thread_state->lua_thread_state; + utarray_new(thread_state->lua_thread_env_array, &plugin_env_icd); + utarray_reserve(thread_state->lua_thread_env_array, schema->lua_config_specific_count); + struct lua_config_specific *specific = NULL; + + /* 读取所有插件并加入插件列表中 */ + struct lua_plugin_env new_plugin_env; + while ((specific = utarray_next(schema->lua_config_specific_array, specific))) + { + memset(&new_plugin_env, 0, sizeof(new_plugin_env)); + if (plugin_env_instance_init(&new_plugin_env, state, specific)) + return -2; + utarray_push_back(thread_state->lua_thread_env_array, &new_plugin_env); + } + + /* 依次执行插件加载函数, 调用lua中load函数 */ + struct lua_plugin_env *plugin_env = NULL; + while ((plugin_env = utarray_next(thread_state->lua_thread_env_array, plugin_env))) + { + utarray_new(plugin_env->lua_plugin_env_plugin_array, &session_plugin_icd); + lua_rawgeti(state, LUA_REGISTRYINDEX, plugin_env->lua_plugin_env_ref_id); + /* 在lua中的全局参数中加入plugin_env指针 */ + lua_pushlightuserdata(state, plugin_env); + lua_setfield(state, -2, PLUGIN_ENV_DEFAULT_KEY); + lua_settop(state, 0); + + script_execute(&plugin_env->lua_plugin_env_load_func); + } + + return 0; +} + +/* + * Function: specific_check + * Input: | struct lua_config_specific * | specific | 待减产的配置项 + * Output: + * Return: | 0 | 检查无问题 + * | -1 | 参数错误 + * | -2 | 配置中的插件文件不存在 + * Description: 检查配置是否有明显问题 + * TODO: 目前仅检查文件是否存在, 后续逐步补充, 比如文件内是否有不应该使用的函数等 + */ +static int specific_check(struct lua_config_specific *specific) +{ + if (__glibc_unlikely(!specific)) + return -1; + + if (access(specific->lua_config_specific_file, F_OK)) + return -2; + + return 0; +} + +/* + * Function: lua_plugin_manage_config_load + * Input: | struct lua_plugin_manage_schema * | scheme | 加载插件的全局变量 + * | const char * | config_file_name | 插件配置文件 + * Output: + * Return: | >=0 | 加载成功, 返回读取的配置插件数量 + * | -1 | 参数错误 + * | -2 | 配置文件不存在或打开文件错误 + * | -3 | toml加载失败 + * | -4 | 获取插件列表失败 + * | -5 | 插件配置项错误 + * | -6 | 插件配置检查出现问题 + * Description: 从配置文件中读取所有插件配置 + */ +int lua_plugin_manage_config_load( + struct lua_plugin_manage_schema *schema, + const char *config_file_name) +{ + if (__glibc_unlikely(!schema || !config_file_name)) + return -1; + int specific_num = 0; + char errbuff[256] = {0}; + + if (access(config_file_name, F_OK)) + return -2; + FILE *fp = fopen(config_file_name, "r"); + if (!fp) + return -2; + + toml_table_t *conf = toml_parse_file(fp, errbuff, sizeof(errbuff)); + if (fp) + fclose(fp); + if (!conf) + { + LOGERROR("parse config file failed, filename %s, err %s\n", config_file_name, errbuff); + return -3; + } + + toml_array_t *plugin_array = toml_array_in(conf, "plugin"); + if (!plugin_array) + return -4; + + /* 获取配置中配置的插件数量 */ + specific_num = toml_array_nelem(plugin_array); + utarray_new(schema->lua_config_specific_array, &specific_icd); + utarray_reserve(schema->lua_config_specific_array, specific_num); + struct lua_config_specific *specific = (struct lua_config_specific *)calloc(sizeof(struct lua_config_specific), 1); + + for (int i = 0; i < specific_num; ++i) + { + toml_table_t *plugin = toml_table_at(plugin_array, i); + const char *raw_filepath = toml_raw_in(plugin, "path"); + // const char *raw_name = toml_raw_in(plugin, "name"); + const char *raw_load_func_name = toml_raw_in(plugin, "init"); + const char *raw_unload_func_name = toml_raw_in(plugin, "exit"); + + /* 从配置中拷贝字符串到specific实例中 */ + if (toml_rtos(raw_filepath, &specific->lua_config_specific_file) || + // toml_rtos(raw_name, &specific->lua_config_specific_name) || + toml_rtos(raw_load_func_name, &specific->lua_config_specific_load_func) || + toml_rtos(raw_unload_func_name, &specific->lua_config_specific_unload_func)) + { + toml_free(conf); + free(specific); + return -5; + } + + /* 插件配置检查 */ + int spec_check_ret = specific_check(specific); + if (spec_check_ret) + { + LOGERROR("check specific failed, ret is %d\n", spec_check_ret); + toml_free(conf); + free(specific); + return -6; + } + + /* 插入到队列中 */ + utarray_push_back(schema->lua_config_specific_array, specific); + specific_instance_destory(specific); + } + free(specific); + + /* 返回加载的插件数量 */ + return specific_num; +} + +/* + * Function: lua_plugin_manage_thread_load + * Input: | struct lua_plugin_manage_schema * | schema | 初始化的全局变量 + * | int | thread_count | 初始化的线程数量 + * Output: + * Return: | >= 0 | 创建完成, 并返回创建成功数量 + * | -1 | 参数错误 + * | -2 | 线程状态机初始化失败 + * | -3 | 线程状态机加载过程失败 + * Description: 依次创建好每个线程中的状态机 + */ +int lua_plugin_manage_thread_load( + struct lua_plugin_manage_schema *schema, + int thread_count) +{ + if (__glibc_unlikely(!schema || thread_count <= 0 || thread_count >= LUA_PLUGIN_MANAGE_MAX_THREAD_COUNT)) + return -1; + + utarray_new(schema->lua_plugin_thread_array, &thread_state_icd); + utarray_reserve(schema->lua_plugin_thread_array, thread_count); + struct lua_thread_state new_thread_state; + for (int i = 1; i <= thread_count; ++i) + { + memset(&new_thread_state, 0, sizeof(new_thread_state)); + if (thread_state_instance_init(&new_thread_state, i)) + return -2; + utarray_push_back(schema->lua_plugin_thread_array, &new_thread_state); + } + + struct lua_thread_state *thread_state = NULL; + while ((thread_state = utarray_next(schema->lua_plugin_thread_array, thread_state))) + { + if (thread_state_instance_load(thread_state, schema)) + return -3; + } + + return thread_count; +} + +/* + * Function: + * Input: + * Output: + * Return: + * Description: + */ +struct lua_plugin_manage_schema *lua_plugin_manage_init( + struct stellar *st, + const char *config_file_path) +{ + if (__glibc_unlikely(!st || !config_file_path)) + return NULL; + + struct lua_plugin_manage_schema *new_schema = (struct lua_plugin_manage_schema *)calloc(1, sizeof(struct lua_plugin_manage_schema)); + if (__glibc_unlikely(!new_schema)) + return NULL; + memset(new_schema, 0, sizeof(struct lua_plugin_manage_schema)); + + new_schema->lua_plugin_schema_st = st; + /* 从配置文件中加载插件 */ + new_schema->lua_config_specific_count = lua_plugin_manage_config_load(new_schema, config_file_path); + LOGDEBUG("load config finish, load count is %d, file path is %s", new_schema->lua_config_specific_count, config_file_path); + if (new_schema->lua_config_specific_count < 0) + { + /* 可以没有插件配置, 但是不能在加载过程中出错 */ + free(new_schema); + return NULL; + } +#ifdef LUAPLUGIN_BASIC_UNITTEST + struct lua_config_specific *specific = NULL; + while ((specific = utarray_next(new_schema->lua_config_specific_array, specific))) + { + printf("path is %s\n", specific->lua_config_specific_file); + printf("load is %s\n", specific->lua_config_specific_load_func); + printf("unload is %s\n", specific->lua_config_specific_unload_func); + } +#endif + + int thread_count = 1; + /* 依次创建线程状态机, 并加入链表 */ + new_schema->lua_plugin_thread_count = lua_plugin_manage_thread_load(new_schema, thread_count); + + return new_schema; +} + +/* + * Function: + * Input: + * Output: + * Return: + * Description: + */ +void lua_plugin_manage_exit(struct lua_plugin_manage_schema *lua_plug_mgr) +{ + if (__glibc_unlikely(!lua_plug_mgr)) + return; + if (lua_plug_mgr->lua_config_specific_array) + utarray_free(lua_plug_mgr->lua_config_specific_array); + if (lua_plug_mgr->lua_plugin_thread_array) + utarray_free(lua_plug_mgr->lua_plugin_thread_array); + return; +} + + diff --git a/src/lua_plugin_manage_internal.h b/src/lua_plugin_manage_internal.h new file mode 100644 index 0000000..ea98f08 --- /dev/null +++ b/src/lua_plugin_manage_internal.h @@ -0,0 +1,272 @@ +/************************************************************************* + > File Name: lua_plugin_manage_internal.h + > Author: + > Created Time: 2024-08 + > Encoding : UTF-8 + ************************************************************************/ + +/************************************************************************* + * version + * [ v0.1 ] + * 08-02 + * 完成函数注册及数据注册功能 + * 1. 声明及定义结构 + * struct lua_binding_function; + * enum BINDING_DATATYPE; + * struct lua_binding_data; + * 2. 声明函数 + * int lua_cbinding_function; + * int lua_cbinding_function_remove; + * int lua_cbinding_data; + * int lua_cbinding_data_remove; + * int lua_cbinding_functions; + * int lua_cbinding_datas; + * + * 08-05 + * 基本完成lua_plugin_manage中主流程 + * 1. 声明及定义结构 + * struct lua_config_specific; + * struct lua_script; + * struct lua_session_plugin; + * struct lua_plugin_env; + * struct lua_thread_state; + * struct lua_plugin_manage_schema; + * 2. 声明函数 + * void specific_instance_copy; + * void specific_instance_destory; + * int script_instance_init_byname; + * int script_instance_init_byrefid; + * void script_instance_clean; + * int script_execute; (暂未实现) + * int session_plugin_instance_init; + * void session_plugin_instance_destory; + * int plugin_env_instance_init; + * void plugin_env_instance_destory; + * int thread_state_instance_init; + * void thread_state_instance_destory; + * int thread_state_instance_load; + * int lua_plugin_manage_config_load; + * int lua_plugin_manage_thread_load; + ************************************************************************/ +#ifndef LUA_PLUGIN_MANAGE_INTERNAL_H +#define LUA_PLUGIN_MANAGE_INTERNAL_H + +#include "lua_plugin_manage.h" +#include "lpm_log.h" + +#include +#include +#include +#include + +#include + +/* ***** ***** ***** ***** ***** ***** */ + +/* 需要注册至lua中的函数 */ +struct lua_binding_function +{ + /* 注册函数原型 */ + lua_CFunction binding_function; + /* 注册至lua中的函数名称 */ + char *binding_function_name; + /* 注册至lua中的命名空间名称 */ + char *binding_function_space_name; +}; + +/* 定义在lua_plugin_binding.c文件中 */ +/* 所有需要注册至lua状态机中供lua调用的函数 */ +extern struct lua_binding_function lua_bind_functions[]; + +/* 向lua状态机中注册一个函数 */ +int lua_cbinding_function(lua_State *state, lua_CFunction bind_function, const char *function_name, const char *space_name); +/* 从lua状态机中移除一个已经注册的函数 */ +int lua_cbinding_function_remove(lua_State *state, const char *function_name, const char *space_name); + +/* 暂时支持前四种数据类型提前注册至lua状态机中 */ +/* 其余类型用于运行lua函数等场景 */ +enum DATATYPE +{ + DATATYPE_BEGIN = 0, + /* bool类型 */ + DATATYPE_BOOL, + /* int类型 */ + DATATYPE_INT, + /* double类型 */ + DATATYPE_NUM, + /* 字符串类型 */ + DATATYPE_STRING, + /* 以下类型不能用于全局变量注册, 仅用于函数等场景 */ + /* table类型 */ + DATATYPE_TABLE, + /* 指针类型 */ + DATATYPE_POINTER, + DATATYPE_END +}; + +/* 需要注册至lua状态机中的数据 */ +struct lua_binding_data +{ + /* 注册的数据类型 */ + enum DATATYPE binding_data_type; + /* 注册数数据值 */ + char *binding_data_value; + /* 注册的数据名称 */ + char *binding_data_name; + /* 注册至lua中的命名空间名称 */ + char *binding_data_space_name; +}; + +/* 定义在lua_plugin_binding.c中 */ +/* 所有需要注册至lua状态机中的全局数据 */ +extern struct lua_binding_data lua_bind_datas[]; + +/* 将一个全局数据注册至状态机中 */ +int lua_cbinding_data(lua_State *state, struct lua_binding_data *data); +/* 从状态机中移除一个已经注册的全局数据 */ +int lua_cbinding_data_remove(lua_State *state, const char *data_name, const char *space_name); + +/* 将lua_bind_functions中提前注册的所有函数注册至state状态机中 */ +int lua_cbinding_functions(lua_State *state); +/* 将lua_bind_datas中提前注册的所有数据注册至state状态机中 */ +int lua_cbinding_datas(lua_State *state); + +/* ***** ***** ***** ***** ***** ***** */ + + + +/* ***** ***** ***** ***** ***** ***** */ + +struct lua_config_specific; +struct lua_script; +struct lua_session_plugin; +struct lua_plugin_env; +struct lua_thread_state; +struct lua_plugin_manage_schema; + +/* 根据配置文件加载过程中保存插件信息的临时结构 */ +struct lua_config_specific +{ + /* 插件需要使用的文件名 */ + char *lua_config_specific_file; + /* 插件名称 */ + // char *lua_config_specific_name; + /* 加载插件需要调用的函数名称 */ + char *lua_config_specific_load_func; + /* 卸载插件需要调用的函数名称 */ + char *lua_config_specific_unload_func; +}; + +void specific_instance_copy(void *dst, const void *src); +void specific_instance_destory(void *elt); + +/* 一个可以运行的lua函数 */ +/* 加载过程中生成引用, 后续使用过程中使用引用编号进行调用 */ +struct lua_script +{ + /* 该函数注册时注册至的状态机, 运行过程中使用该状态机调用函数 */ + lua_State *script_state; + /* 该函数的引用ID */ + int script_ref_id; + + /* 运行成功次数 */ + int script_run_success; + /* 运行失败次数 */ + int script_run_failed; + /* 最后一次运行开始时间 */ + clock_t script_last_ms_start; + /* 最后一次运行结束时间 */ + clock_t script_last_ms_end; + /* 运行总用时 */ + clock_t script_total_ms; +}; + +int script_instance_init_byname(struct lua_script *script, lua_State *state, const char *filepath, const char *funcname); +int script_instance_init_byrefid(struct lua_script *script, lua_State *state, int ref_id); +void script_instance_clean(struct lua_script *script); +int script_execute(struct lua_script *script); + +/* 每一个插件的函数信息 */ +struct lua_session_plugin +{ + /* 插件注册完成后得到的插件编号 */ + int plugin_id; + + /* ctx_new函数 */ + struct lua_script plugin_ctx_new_script; + /* ctx_free函数 */ + struct lua_script plugin_ctx_free_script; + /* on_message函数 */ + // struct lua_script plugin_on_message_script; +}; + +int session_plugin_instance_init(struct lua_session_plugin *session_plugin, lua_State *state, int plugin_id, int new_refid, int free_refid); +void session_plugin_instance_destory(void *elt); + +#define PLUGIN_ENV_DEFAULT_KEY "penv_pointer" + +/* 调用插件加载函数时得到的运行环境数据 */ +struct lua_plugin_env +{ + /* 插件注册的状态机 */ + lua_State *lua_plugin_env_state; + /* 该环境数据中包含的所有插件列表 */ + UT_array *lua_plugin_env_plugin_array; + + /* 加载插件的lua函数 */ + struct lua_script lua_plugin_env_load_func; + /* 卸载插件的lua函数 */ + struct lua_script lua_plugin_env_unload_func; + + /* 该插件运行环境数据的名称, 在创建时同时在状态机中创建该名称的命名空间 */ + /* 插件申请lua内的全局变量可以保存在该命名空间内, 防止被其他内容覆盖 */ + // char * lua_plugin_env_name; + + /* 在lua中保存运行数据的引用ID */ + int lua_plugin_env_ref_id; /* plugin_env */ +}; + +/* 此时不创建session_array, 在调用load函数过程中创建 */ +int plugin_env_instance_init(struct lua_plugin_env *plugin_env, lua_State *state, struct lua_config_specific *specific); +void plugin_env_instance_destory(void *elt); + +/* 每一个线程中保存一个状态机数据 */ +struct lua_thread_state +{ + /* 创建状态机的线程ID */ + int lua_thread_id; + /* 已经插入的插件数量 */ + int lua_thread_plugin_count; + /* 生成的状态机 */ + lua_State *lua_thread_state; + // struct lua_pl_state_private * lua_thread_private; + /* 该线程状态机中注册的插件列表 */ + UT_array *lua_thread_env_array; + /* 状态机的启动时间 */ + time_t lua_thread_begin_time; +}; + +int thread_state_instance_init(struct lua_thread_state *thread_state, int state_id); +void thread_state_instance_destory(void *elt); +int thread_state_instance_load(struct lua_thread_state *thread_state, struct lua_plugin_manage_schema *schema); + +struct lua_plugin_manage_schema +{ + struct stellar *lua_plugin_schema_st; + int lua_config_specific_count; + UT_array *lua_config_specific_array; + int lua_plugin_thread_count; + UT_array *lua_plugin_thread_array; +}; + +/* 防止参数错误, 暂时定一个上限 */ +#define LUA_PLUGIN_MANAGE_MAX_THREAD_COUNT 10 + +void *lpm_ctx_new_func(struct session *sess, void *plugin_env); +void lpm_ctx_free_func(struct session *sess, void *sess_ctx, void *plugin_env); +void lpm_on_session_msg_func(struct session *sess, int topic_id, const void *msg, void *sess_ctx, void *plugin_env); + +int lua_plugin_manage_config_load(struct lua_plugin_manage_schema *schema, const char *config_file_name); +int lua_plugin_manage_thread_load(struct lua_plugin_manage_schema *schema, int thread_count); + +#endif \ No newline at end of file