feat(module.c, stk.c): implement pending queue and cascade dependency resolution
This commit is contained in:
+131
@@ -47,6 +47,19 @@ static char stk_mod_deps_sym[STK_MOD_FUNC_NAME_BUFFER] = "stk_mod_deps";
|
|||||||
|
|
||||||
size_t module_count = 0;
|
size_t module_count = 0;
|
||||||
|
|
||||||
|
static char (*stk_pending)[STK_PATH_MAX_OS] = NULL;
|
||||||
|
static size_t stk_pending_count = 0;
|
||||||
|
|
||||||
|
void stk_pending_free(void)
|
||||||
|
{
|
||||||
|
if (stk_pending) {
|
||||||
|
free(stk_pending);
|
||||||
|
stk_pending = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
stk_pending_count = 0;
|
||||||
|
}
|
||||||
|
|
||||||
static stk_version_t stk_parse_version(const char *str)
|
static stk_version_t stk_parse_version(const char *str)
|
||||||
{
|
{
|
||||||
stk_version_t v;
|
stk_version_t v;
|
||||||
@@ -448,6 +461,7 @@ void stk_module_free_memory(void)
|
|||||||
stk_modules = NULL;
|
stk_modules = NULL;
|
||||||
}
|
}
|
||||||
module_count = 0;
|
module_count = 0;
|
||||||
|
stk_pending_free();
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned char stk_module_init_memory(size_t capacity)
|
unsigned char stk_module_init_memory(size_t capacity)
|
||||||
@@ -506,6 +520,123 @@ void stk_module_unload_all(void)
|
|||||||
stk_module_free_memory();
|
stk_module_free_memory();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void stk_pending_add(const char *path)
|
||||||
|
{
|
||||||
|
char (*new_pending)[STK_PATH_MAX_OS];
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
new_pending = malloc((stk_pending_count + 1) * sizeof(*stk_pending));
|
||||||
|
if (!new_pending)
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (i = 0; i < stk_pending_count; i++)
|
||||||
|
memcpy(new_pending[i], stk_pending[i], STK_PATH_MAX_OS);
|
||||||
|
|
||||||
|
free(stk_pending);
|
||||||
|
stk_pending = new_pending;
|
||||||
|
|
||||||
|
strncpy(stk_pending[stk_pending_count], path, STK_PATH_MAX_OS - 1);
|
||||||
|
stk_pending[stk_pending_count][STK_PATH_MAX_OS - 1] = '\0';
|
||||||
|
stk_pending_count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
void stk_pending_remove(const char *id)
|
||||||
|
{
|
||||||
|
size_t i, write;
|
||||||
|
char pending_id[STK_MOD_ID_BUFFER];
|
||||||
|
|
||||||
|
if (!stk_pending_count)
|
||||||
|
return;
|
||||||
|
|
||||||
|
write = 0;
|
||||||
|
for (i = 0; i < stk_pending_count; i++) {
|
||||||
|
extract_module_id(stk_pending[i], pending_id);
|
||||||
|
if (strncmp(pending_id, id, STK_MOD_ID_BUFFER) == 0)
|
||||||
|
continue;
|
||||||
|
if (write != i)
|
||||||
|
memcpy(stk_pending[write], stk_pending[i],
|
||||||
|
STK_PATH_MAX_OS);
|
||||||
|
write++;
|
||||||
|
}
|
||||||
|
stk_pending_count = write;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t stk_pending_retry(void)
|
||||||
|
{
|
||||||
|
size_t i, d, loaded = 0;
|
||||||
|
unsigned char deps_satisfied;
|
||||||
|
unsigned char result;
|
||||||
|
void *handle;
|
||||||
|
union {
|
||||||
|
void *obj;
|
||||||
|
const char *(*meta_func)(void);
|
||||||
|
} u;
|
||||||
|
const stk_dep_t *deps;
|
||||||
|
size_t dep_count;
|
||||||
|
int found;
|
||||||
|
|
||||||
|
if (!stk_pending_count)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
for (i = 0; i < stk_pending_count; i++) {
|
||||||
|
handle = platform_load_library(stk_pending[i]);
|
||||||
|
if (!handle)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
u.obj = platform_get_symbol(handle, stk_mod_deps_sym);
|
||||||
|
if (!u.obj) {
|
||||||
|
platform_unload_library(handle);
|
||||||
|
goto attempt_load;
|
||||||
|
}
|
||||||
|
|
||||||
|
deps = (const stk_dep_t *)u.obj;
|
||||||
|
dep_count = 0;
|
||||||
|
while (deps[dep_count].id[0] != '\0')
|
||||||
|
dep_count++;
|
||||||
|
|
||||||
|
deps_satisfied = 1;
|
||||||
|
for (d = 0; d < dep_count; d++) {
|
||||||
|
found = is_mod_loaded(deps[d].id);
|
||||||
|
if (found < 0) {
|
||||||
|
deps_satisfied = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (deps[d].version[0] &&
|
||||||
|
!stk_validate_constraint(
|
||||||
|
deps[d].version, stk_modules[found].version)) {
|
||||||
|
deps_satisfied = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
platform_unload_library(handle);
|
||||||
|
|
||||||
|
if (!deps_satisfied)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
attempt_load:
|
||||||
|
if (stk_module_realloc_memory(module_count + 1) != 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
result = stk_module_load(stk_pending[i], module_count);
|
||||||
|
if (result != STK_MOD_INIT_SUCCESS)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
module_count++;
|
||||||
|
loaded++;
|
||||||
|
|
||||||
|
memcpy(stk_pending[i], stk_pending[stk_pending_count - 1],
|
||||||
|
STK_PATH_MAX_OS);
|
||||||
|
stk_pending_count--;
|
||||||
|
i--;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stk_pending_count == 0)
|
||||||
|
stk_pending_free();
|
||||||
|
|
||||||
|
return loaded;
|
||||||
|
}
|
||||||
|
|
||||||
static void stk_set_fn_name(char *dst, const char *name)
|
static void stk_set_fn_name(char *dst, const char *name)
|
||||||
{
|
{
|
||||||
if (!name || (stk_flags & STK_FLAG_INITIALIZED))
|
if (!name || (stk_flags & STK_FLAG_INITIALIZED))
|
||||||
|
|||||||
@@ -53,6 +53,9 @@ void stk_module_unload(size_t index);
|
|||||||
void stk_module_unload_all(void);
|
void stk_module_unload_all(void);
|
||||||
unsigned char stk_validate_dependencies(size_t count);
|
unsigned char stk_validate_dependencies(size_t count);
|
||||||
unsigned char stk_topo_sort(size_t count, size_t *order);
|
unsigned char stk_topo_sort(size_t count, size_t *order);
|
||||||
|
void stk_pending_add(const char *path);
|
||||||
|
void stk_pending_remove(const char *id);
|
||||||
|
size_t stk_pending_retry(void);
|
||||||
|
|
||||||
static void build_path(char *dest, size_t dest_size, const char *dir,
|
static void build_path(char *dest, size_t dest_size, const char *dir,
|
||||||
const char *file)
|
const char *file)
|
||||||
@@ -185,10 +188,21 @@ unsigned char stk_init(void)
|
|||||||
|
|
||||||
dep_result = stk_validate_dependencies(module_count);
|
dep_result = stk_validate_dependencies(module_count);
|
||||||
if (dep_result != STK_MOD_INIT_SUCCESS) {
|
if (dep_result != STK_MOD_INIT_SUCCESS) {
|
||||||
stk_log(STK_LOG_ERROR, "Dependency validation failed: %s",
|
size_t j;
|
||||||
stk_error_string(dep_result));
|
char mod_tmp_path[STK_PATH_MAX_OS];
|
||||||
stk_module_unload_all();
|
stk_log(STK_LOG_WARN,
|
||||||
return dep_result;
|
"Some modules have unmet dependencies, deferring");
|
||||||
|
for (j = 0; j < module_count; j++) {
|
||||||
|
if (stk_modules[j].dep_count > 0) {
|
||||||
|
build_path(mod_tmp_path, sizeof(mod_tmp_path),
|
||||||
|
stk_tmp_dir, stk_modules[j].id);
|
||||||
|
strncat(mod_tmp_path, STK_MODULE_EXT,
|
||||||
|
sizeof(mod_tmp_path) -
|
||||||
|
strlen(mod_tmp_path) - 1);
|
||||||
|
stk_pending_add(mod_tmp_path);
|
||||||
|
stk_module_unload(j);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
order = malloc(module_count * sizeof(size_t));
|
order = malloc(module_count * sizeof(size_t));
|
||||||
@@ -340,6 +354,7 @@ begin_operations:
|
|||||||
for (i = 0; i < unload_count; ++i) {
|
for (i = 0; i < unload_count; ++i) {
|
||||||
stk_log(STK_LOG_INFO, "Unloaded module: %s",
|
stk_log(STK_LOG_INFO, "Unloaded module: %s",
|
||||||
stk_modules[unloaded_mod_indices[i]].id);
|
stk_modules[unloaded_mod_indices[i]].id);
|
||||||
|
stk_pending_remove(stk_modules[unloaded_mod_indices[i]].id);
|
||||||
stk_module_unload(unloaded_mod_indices[i]);
|
stk_module_unload(unloaded_mod_indices[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -366,8 +381,6 @@ begin_operations:
|
|||||||
stk_log(STK_LOG_ERROR, "Failed to reload module %s: %s",
|
stk_log(STK_LOG_ERROR, "Failed to reload module %s: %s",
|
||||||
file_list[file_index],
|
file_list[file_index],
|
||||||
stk_error_string(load_result));
|
stk_error_string(load_result));
|
||||||
else
|
|
||||||
stk_log_module(mod_index);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
holes_to_fill = (load_count < unload_count) ? load_count : unload_count;
|
holes_to_fill = (load_count < unload_count) ? load_count : unload_count;
|
||||||
@@ -392,8 +405,6 @@ begin_operations:
|
|||||||
stk_log(STK_LOG_ERROR, "Failed to load module %s: %s",
|
stk_log(STK_LOG_ERROR, "Failed to load module %s: %s",
|
||||||
file_list[file_index],
|
file_list[file_index],
|
||||||
stk_error_string(load_result));
|
stk_error_string(load_result));
|
||||||
else
|
|
||||||
stk_log_module(target_index);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (load_count > unload_count)
|
if (load_count > unload_count)
|
||||||
@@ -427,7 +438,6 @@ append_modules:
|
|||||||
file_list[file_index],
|
file_list[file_index],
|
||||||
stk_error_string(load_result));
|
stk_error_string(load_result));
|
||||||
} else {
|
} else {
|
||||||
stk_log_module(module_count + successful_appends);
|
|
||||||
successful_appends++;
|
successful_appends++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -460,13 +470,64 @@ validate_deps:
|
|||||||
if (module_count == 0)
|
if (module_count == 0)
|
||||||
goto free_poll;
|
goto free_poll;
|
||||||
|
|
||||||
dep_result = stk_validate_dependencies(module_count);
|
{
|
||||||
if (dep_result != STK_MOD_INIT_SUCCESS) {
|
size_t cascade_indices[STK_PATH_MAX];
|
||||||
stk_log(STK_LOG_ERROR, "Dependency validation failed: %s",
|
size_t cascade_count;
|
||||||
stk_error_string(dep_result));
|
char cascade_tmp_path[STK_PATH_MAX_OS];
|
||||||
goto free_poll;
|
size_t j, k, cascade_write;
|
||||||
|
|
||||||
|
do {
|
||||||
|
cascade_count = 0;
|
||||||
|
|
||||||
|
for (j = 0; j < module_count; j++) {
|
||||||
|
if (stk_modules[j].dep_count == 0)
|
||||||
|
continue;
|
||||||
|
for (k = 0; k < stk_modules[j].dep_count; k++) {
|
||||||
|
if (is_mod_loaded(
|
||||||
|
stk_modules[j].deps[k].id) <
|
||||||
|
0) {
|
||||||
|
cascade_indices
|
||||||
|
[cascade_count++] = j;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cascade_count == 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
for (j = 0; j < cascade_count; j++) {
|
||||||
|
size_t idx = cascade_indices[j];
|
||||||
|
stk_log(STK_LOG_WARN,
|
||||||
|
"Unloading '%s': unmet dependencies",
|
||||||
|
stk_modules[idx].id);
|
||||||
|
build_path(cascade_tmp_path,
|
||||||
|
sizeof(cascade_tmp_path),
|
||||||
|
stk_tmp_dir, stk_modules[idx].id);
|
||||||
|
strncat(cascade_tmp_path, STK_MODULE_EXT,
|
||||||
|
sizeof(cascade_tmp_path) -
|
||||||
|
strlen(cascade_tmp_path) - 1);
|
||||||
|
stk_pending_add(cascade_tmp_path);
|
||||||
|
stk_module_unload(idx);
|
||||||
|
}
|
||||||
|
|
||||||
|
cascade_write = 0;
|
||||||
|
for (j = 0; j < module_count; j++) {
|
||||||
|
if (stk_modules[j].handle != NULL) {
|
||||||
|
if (cascade_write != j)
|
||||||
|
stk_modules[cascade_write] =
|
||||||
|
stk_modules[j];
|
||||||
|
cascade_write++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
module_count = cascade_write;
|
||||||
|
|
||||||
|
} while (cascade_count > 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (module_count > 0)
|
||||||
|
stk_module_realloc_memory(module_count);
|
||||||
|
|
||||||
order = malloc(module_count * sizeof(size_t));
|
order = malloc(module_count * sizeof(size_t));
|
||||||
if (order) {
|
if (order) {
|
||||||
dep_result = stk_topo_sort(module_count, order);
|
dep_result = stk_topo_sort(module_count, order);
|
||||||
@@ -476,6 +537,11 @@ validate_deps:
|
|||||||
free(order);
|
free(order);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
stk_pending_retry();
|
||||||
|
|
||||||
|
if (module_count > 0)
|
||||||
|
stk_log_modules();
|
||||||
|
|
||||||
free_poll:
|
free_poll:
|
||||||
free(reloaded_mod_indices);
|
free(reloaded_mod_indices);
|
||||||
free(reloaded_mod_file_indices);
|
free(reloaded_mod_file_indices);
|
||||||
|
|||||||
Reference in New Issue
Block a user