I think I need to change the documentation format

This commit is contained in:
udo
2026-04-22 01:32:05 +00:00
parent b53eb6e4f1
commit 8223dcc6b3
222 changed files with 1932 additions and 39753 deletions
+309 -82
View File
@@ -1,9 +1,11 @@
#include "compiler.h"
#include "compiler-parser.h"
#include "hash.h"
#include <algorithm>
#include <cstdlib>
#include <filesystem>
#include <sys/file.h>
#include <unistd.h>
namespace {
@@ -17,11 +19,14 @@ struct SharedUnitFilesystemState
{
bool source_exists = false;
bool metadata_exists = false;
bool compile_output_exists = false;
bool metadata_parsed = false;
bool abi_compatible = false;
bool input_signature_matches = false;
time_t source_time = 0;
time_t compiled_time = 0;
time_t metadata_time = 0;
time_t compile_output_time = 0;
time_t setup_template_time = 0;
time_t compiler_header_time = 0;
time_t compiler_source_time = 0;
@@ -30,8 +35,51 @@ struct SharedUnitFilesystemState
time_t required_time = 0;
u64 metadata_abi_version = 0;
u64 runtime_abi_version = UCE_UNIT_ABI_VERSION;
String metadata_content;
String compile_output_content;
String metadata_build_token;
String metadata_input_signature;
String current_input_signature;
};
struct SharedUnitCompileCheck
{
bool source_missing = false;
bool needs_compile = false;
};
bool compiler_config_truthy(String raw, bool default_value)
{
raw = to_lower(trim(raw));
if(raw == "")
return(default_value);
if(raw == "1" || raw == "true" || raw == "yes" || raw == "on")
return(true);
if(raw == "0" || raw == "false" || raw == "no" || raw == "off")
return(false);
return(default_value);
}
bool compiler_jit_compile_on_request_enabled(Request* context)
{
if(!context || !context->server)
return(true);
return(compiler_config_truthy(context->server->config["JIT_COMPILE_ON_REQUEST"], true));
}
u64 compiler_failure_retry_seconds(Request* context)
{
if(!context || !context->server)
return(10);
auto raw = trim(context->server->config["COMPILE_FAILURE_RETRY_SECONDS"]);
if(raw == "")
return(10);
auto value = int_val(raw);
if(value < 0)
return(0);
return((u64)value);
}
bool compiler_is_u64_string(String value)
{
value = trim(value);
@@ -45,11 +93,12 @@ bool compiler_is_u64_string(String value)
return(true);
}
bool compiler_parse_unit_metadata_abi(String content, u64& abi_out)
StringMap compiler_parse_unit_metadata(String content)
{
StringMap metadata;
content = trim(content);
if(content == "")
return(false);
return(metadata);
auto lines = split(content, "\n");
for(auto& raw_line : lines)
@@ -62,37 +111,97 @@ bool compiler_parse_unit_metadata_abi(String content, u64& abi_out)
continue;
auto key = trim(line.substr(0, split_pos));
auto value = trim(line.substr(split_pos + 1));
if(key != "unit_abi_version" || !compiler_is_u64_string(value))
continue;
abi_out = (u64)atoll(value.c_str());
return(true);
if(key != "")
metadata[key] = value;
}
return(false);
return(metadata);
}
String compiler_unit_metadata_text()
String compiler_unit_input_signature(Request* context, SharedUnit* su)
{
if(!context || !su || !file_exists(su->file_name))
return("");
String setup_template = context->server->config["COMPILER_SYS_PATH"] + "/" + context->server->config["SETUP_TEMPLATE"];
return(
gen_sha1(file_get_contents(su->file_name)) + ":" +
gen_sha1(file_get_contents(setup_template)) + ":" +
std::to_string(UCE_UNIT_ABI_VERSION)
);
}
String compiler_unit_build_token()
{
return(
std::to_string(getpid()) + ":" +
std::to_string((u64)(time_precise() * 1000000.0))
);
}
String compiler_unit_metadata_text(Request* context, SharedUnit* su)
{
return(
"format=uce-unit-metadata-v1\n"
"unit_abi_version=" + std::to_string(UCE_UNIT_ABI_VERSION) + "\n"
"input_signature=" + compiler_unit_input_signature(context, su) + "\n"
"build_token=" + compiler_unit_build_token() + "\n"
);
}
bool shared_unit_filesystem_requires_recompile(const SharedUnitFilesystemState& state)
SharedUnitCompileCheck shared_unit_compile_check(const SharedUnitFilesystemState& state)
{
if(!state.source_exists)
return(true);
if(state.compiled_time == 0)
return(true);
if(state.compiled_time < state.required_time)
return(true);
if(!state.metadata_exists)
return(true);
if(!state.metadata_parsed)
return(true);
if(!state.abi_compatible)
return(true);
return(false);
SharedUnitCompileCheck result;
result.source_missing = !state.source_exists;
result.needs_compile =
!state.source_exists ||
state.compiled_time == 0 ||
state.compiled_time < state.required_time ||
!state.metadata_exists ||
!state.metadata_parsed ||
!state.abi_compatible ||
!state.input_signature_matches;
return(result);
}
bool compiler_failure_retry_deferred(Request* context, SharedUnit* su, const SharedUnitFilesystemState& state)
{
if(!context || !su)
return(false);
auto retry_seconds = compiler_failure_retry_seconds(context);
if(retry_seconds == 0)
return(false);
if(!state.compile_output_exists || state.compile_output_time == 0)
return(false);
if(state.source_time == 0)
return(false);
if(state.source_time > state.compile_output_time)
return(false);
return(time() < (u64)state.compile_output_time + retry_seconds);
}
String compiler_failure_output_for_state(SharedUnit* su, const SharedUnitFilesystemState& state)
{
if(!state.compile_output_exists)
return("");
auto content = trim(state.compile_output_content);
if(content != "")
return(content);
if(su)
return(trim(su->compile_error_status));
return("");
}
void compiler_restore_persisted_failure(SharedUnit* su, const SharedUnitFilesystemState& state, String status = "compile_error")
{
if(!su)
return;
auto message = compiler_failure_output_for_state(su, state);
if(message == "")
message = "recent compilation failed";
su->compiler_messages = message;
su->compile_status = status;
su->compile_error_status = message;
su->last_error = (state.compile_output_time != 0 ? state.compile_output_time : time());
}
time_t compiler_runtime_abi_time(Request* context)
@@ -196,6 +305,16 @@ auto compiler_with_registry_lock(Request* context, TCallback callback) -> declty
return(result);
}
bool compiler_has_known_unit_cached(Request* context, String file_name)
{
if(!context)
return(false);
return(compiler_with_registry_lock(context, [&]() {
auto files = compiler_read_known_units_unlocked(context);
return(std::find(files.begin(), files.end(), file_name) != files.end());
}));
}
SharedUnitFilesystemState inspect_shared_unit_filesystem(Request* context, SharedUnit* su)
{
SharedUnitFilesystemState state;
@@ -211,27 +330,60 @@ SharedUnitFilesystemState inspect_shared_unit_filesystem(Request* context, Share
state.runtime_binary_time = file_mtime(context->server->config["COMPILER_SYS_PATH"] + "/bin/uce_fastcgi.linux.bin");
state.compiler_abi_time = compiler_runtime_abi_time(context);
state.metadata_time = file_mtime(su->meta_file_name);
state.compile_output_time = file_mtime(su->compile_output_file_name);
state.current_input_signature = compiler_unit_input_signature(context, su);
state.metadata_exists = (state.metadata_time != 0);
state.compile_output_exists = (state.compile_output_time != 0);
if(state.metadata_exists)
state.metadata_parsed = compiler_parse_unit_metadata_abi(
file_get_contents(su->meta_file_name),
state.metadata_abi_version
);
{
state.metadata_content = file_get_contents(su->meta_file_name);
auto metadata = compiler_parse_unit_metadata(state.metadata_content);
auto abi_it = metadata.find("unit_abi_version");
if(abi_it != metadata.end() && compiler_is_u64_string(abi_it->second))
{
state.metadata_abi_version = (u64)atoll(abi_it->second.c_str());
state.metadata_parsed = true;
}
auto input_it = metadata.find("input_signature");
if(input_it != metadata.end())
state.metadata_input_signature = input_it->second;
auto build_it = metadata.find("build_token");
if(build_it != metadata.end())
state.metadata_build_token = build_it->second;
}
if(state.compile_output_exists)
state.compile_output_content = file_get_contents(su->compile_output_file_name);
state.abi_compatible = (state.metadata_parsed && state.metadata_abi_version == state.runtime_abi_version);
state.input_signature_matches = (
state.metadata_parsed &&
state.metadata_input_signature != "" &&
state.current_input_signature != "" &&
state.metadata_input_signature == state.current_input_signature
);
state.required_time = std::max({state.source_time, state.setup_template_time, state.compiler_abi_time});
state.compiled_time = file_mtime(su->so_name);
return(state);
}
void compiler_record_observed_filesystem_state(SharedUnit* su, const SharedUnitFilesystemState& state)
{
if(!su)
return;
su->observed_compiled_time = state.compiled_time;
su->observed_metadata_content = state.metadata_content;
}
bool shared_unit_cache_is_stale(Request* context, SharedUnit* su)
{
if(!su)
return(true);
auto state = inspect_shared_unit_filesystem(context, su);
if(shared_unit_filesystem_requires_recompile(state))
if(shared_unit_compile_check(state).needs_compile)
return(true);
if(su->last_compiled != 0 && state.compiled_time != su->last_compiled)
if(state.compiled_time != su->observed_compiled_time)
return(true);
if(state.metadata_content != su->observed_metadata_content)
return(true);
return(false);
}
@@ -245,6 +397,45 @@ void release_shared_unit_cache_entry(Request* context, String file_name)
context->server->units.erase(it);
}
SharedUnit* compiler_cached_unit(Request* context, String file_name)
{
auto it = context->server->units.find(file_name);
if(it == context->server->units.end())
return(0);
return(it->second);
}
bool compiler_cache_mode_matches(SharedUnit* su, bool opt_so_optional)
{
return(su && su->opt_so_optional == opt_so_optional);
}
bool compiler_cached_unit_is_reusable(Request* context, SharedUnit* su, bool opt_so_optional, bool force_recompile)
{
return(
!force_recompile &&
compiler_cache_mode_matches(su, opt_so_optional) &&
!shared_unit_cache_is_stale(context, su)
);
}
SharedUnit* compiler_reusable_cached_unit(Request* context, String file_name, bool opt_so_optional, bool force_recompile)
{
auto su = compiler_cached_unit(context, file_name);
if(compiler_cached_unit_is_reusable(context, su, opt_so_optional, force_recompile))
return(su);
return(0);
}
void compiler_release_cached_unit_if_needed(Request* context, String file_name, bool opt_so_optional, bool force_recompile)
{
auto su = compiler_cached_unit(context, file_name);
if(!su)
return;
if(force_recompile || !compiler_cache_mode_matches(su, opt_so_optional) || shared_unit_cache_is_stale(context, su))
release_shared_unit_cache_entry(context, file_name);
}
String compiler_current_unit_path(Request* context)
{
if(!context)
@@ -354,11 +545,12 @@ String compiler_error_status(SharedUnit* su)
String compiler_status_from_filesystem(const SharedUnitFilesystemState& state, SharedUnit* su = 0)
{
auto compile_check = shared_unit_compile_check(state);
if(!state.source_exists)
return("missing_source");
if(state.compiled_time == 0 || !state.metadata_exists)
return("not_compiled");
if(shared_unit_filesystem_requires_recompile(state))
if(compile_check.needs_compile)
return("stale");
if(su && su->so_handle)
return("loaded");
@@ -453,13 +645,12 @@ void setup_unit_paths(Request* context, SharedUnit* su, String file_name)
su->so_name = su->bin_path + "/" + su->bin_file_name;
su->api_file_name = su->bin_path + "/" + su->src_file_name + ".exports.txt";
su->meta_file_name = su->bin_path + "/" + su->src_file_name + ".meta.txt";
su->compile_output_file_name = su->bin_path + "/" + su->src_file_name + ".compile.txt";
//su->setup_file_name = su->bin_path + "/" + su->src_file_name + ".setup.h";
}
void load_shared_unit(Request* context, SharedUnit* su, String file_name)
void load_shared_unit(Request* context, SharedUnit* su)
{
//setup_unit_paths(context, su, file_name);
su->on_render = 0;
su->on_component = 0;
su->on_websocket = 0;
@@ -474,6 +665,12 @@ void load_shared_unit(Request* context, SharedUnit* su, String file_name)
su->compile_status = "not_compiled";
return;
}
auto fs_state = inspect_shared_unit_filesystem(context, su);
if(compiler_failure_retry_deferred(context, su, fs_state))
{
compiler_restore_persisted_failure(su, fs_state);
return;
}
//printf("(i) unit file not found: %s\n", su->so_name.c_str());
su->compiler_messages = "unit file not found";
su->compile_status = "not_compiled";
@@ -525,14 +722,14 @@ void load_shared_unit(Request* context, SharedUnit* su, String file_name)
return(result);
}*/
void compile_shared_unit(Request* context, SharedUnit* su, String file_name)
void compile_shared_unit(Request* context, SharedUnit* su)
{
//setup_unit_paths(context, su, file_name);
f64 comp_start = time_precise();
if(!file_exists(su->file_name))
{
su->compiler_messages = "source file not found (" + su->file_name + ")";
file_put_contents(su->compile_output_file_name, su->compiler_messages + "\n");
compiler_untrack_known_unit(context, su->file_name);
compiler_record_compile_result(su, time_precise() - comp_start, false, "missing_source", su->compiler_messages);
return;
@@ -554,14 +751,22 @@ void compile_shared_unit(Request* context, SharedUnit* su, String file_name)
if(su->compiler_messages.length() > 0)
{
file_put_contents(su->compile_output_file_name, su->compiler_messages + "\n");
compiler_record_compile_result(su, time_precise() - comp_start, false, "compile_error", su->compiler_messages);
printf("%s \n", su->compiler_messages.c_str());
}
else
{
load_shared_unit(context, su, file_name);
load_shared_unit(context, su);
if(su->so_handle)
file_put_contents(su->meta_file_name, compiler_unit_metadata_text());
{
file_put_contents(su->meta_file_name, compiler_unit_metadata_text(context, su));
file_unlink(su->compile_output_file_name);
}
else if(trim(su->compiler_messages) != "")
{
file_put_contents(su->compile_output_file_name, su->compiler_messages + "\n");
}
compiler_record_compile_result(
su,
time_precise() - comp_start,
@@ -579,16 +784,11 @@ SharedUnit* compiler_get_shared_unit_internal(Request* context, String file_name
{
file_name = compiler_normalize_unit_path(context, file_name);
auto existing = context->server->units.find(file_name);
bool cache_mode_matches = (
existing != context->server->units.end() &&
existing->second->opt_so_optional == opt_so_optional
);
if(!force_recompile && existing != context->server->units.end() && cache_mode_matches && !shared_unit_cache_is_stale(context, existing->second))
return(existing->second);
auto cached = compiler_reusable_cached_unit(context, file_name, opt_so_optional, force_recompile);
if(cached)
return(cached);
if(existing != context->server->units.end())
release_shared_unit_cache_entry(context, file_name);
compiler_release_cached_unit_if_needed(context, file_name, opt_so_optional, force_recompile);
SharedUnit* su = new SharedUnit();
setup_unit_paths(context, su, file_name);
@@ -598,38 +798,61 @@ SharedUnit* compiler_get_shared_unit_internal(Request* context, String file_name
if(fdlock != -1)
flock(fdlock, LOCK_EX);
existing = context->server->units.find(file_name);
cache_mode_matches = (
existing != context->server->units.end() &&
existing->second->opt_so_optional == opt_so_optional
);
if(existing != context->server->units.end() && (force_recompile || !cache_mode_matches || shared_unit_cache_is_stale(context, existing->second)))
release_shared_unit_cache_entry(context, file_name);
existing = context->server->units.find(file_name);
cache_mode_matches = (
existing != context->server->units.end() &&
existing->second->opt_so_optional == opt_so_optional
);
if(!force_recompile && existing != context->server->units.end() && cache_mode_matches && !shared_unit_cache_is_stale(context, existing->second))
cached = compiler_reusable_cached_unit(context, file_name, opt_so_optional, force_recompile);
if(cached)
{
compiler_close_lock_file(fdlock);
delete su;
return(existing->second);
return(cached);
}
compiler_release_cached_unit_if_needed(context, file_name, opt_so_optional, force_recompile);
auto state = inspect_shared_unit_filesystem(context, su);
bool do_recompile = force_recompile || !state.source_exists || state.compiled_time == 0 || state.compiled_time < state.required_time;
auto compile_check = shared_unit_compile_check(state);
bool retry_deferred = compiler_failure_retry_deferred(context, su, state);
bool jit_enabled = compiler_jit_compile_on_request_enabled(context);
bool do_recompile = force_recompile || compile_check.needs_compile;
if(do_recompile)
{
compile_shared_unit(context, su, file_name);
if(!force_recompile && retry_deferred)
compiler_restore_persisted_failure(su, state);
else if(!force_recompile && !jit_enabled)
{
compiler_restore_persisted_failure(su, state, "jit_compile_disabled");
if(trim(su->compiler_messages) == "")
{
su->compiler_messages = "JIT compilation on request is disabled";
su->compile_error_status = su->compiler_messages;
}
}
else
compile_shared_unit(context, su);
}
else
{
load_shared_unit(context, su, file_name);
load_shared_unit(context, su);
if(!su->so_handle)
compile_shared_unit(context, su, file_name);
{
if(!force_recompile && retry_deferred)
compiler_restore_persisted_failure(su, state);
else if(!force_recompile && !jit_enabled)
{
compiler_restore_persisted_failure(su, state, "jit_compile_disabled");
if(trim(su->compiler_messages) == "")
{
su->compiler_messages = "JIT compilation on request is disabled";
su->compile_error_status = su->compiler_messages;
}
}
else
compile_shared_unit(context, su);
}
}
auto observed_state = inspect_shared_unit_filesystem(context, su);
compiler_record_observed_filesystem_state(su, observed_state);
compiler_close_lock_file(fdlock);
context->server->units[file_name] = su;
@@ -728,13 +951,9 @@ StringList compiler_scan_site_units(Request* context)
StringList compiler_list_known_units(Request* context)
{
auto files = compiler_with_registry_lock(context, [&]() {
return(compiler_with_registry_lock(context, [&]() {
return(compiler_read_known_units_unlocked(context));
});
context->server->known_unit_files.clear();
for(auto& file_name : files)
context->server->known_unit_files[file_name] = true;
return(files);
}));
}
void compiler_set_known_units(Request* context, StringList files)
@@ -744,24 +963,22 @@ void compiler_set_known_units(Request* context, StringList files)
compiler_write_known_units_unlocked(context, files);
return(0);
});
context->server->known_unit_files.clear();
for(auto& file_name : files)
context->server->known_unit_files[file_name] = true;
}
void compiler_track_known_unit(Request* context, String file_name)
{
file_name = compiler_normalize_unit_path(context, file_name);
if(file_name == "" || !compiler_is_known_unit_file(file_name) || context->server->known_unit_files[file_name])
if(file_name == "" || !compiler_is_known_unit_file(file_name))
return;
compiler_with_registry_lock(context, [&]() {
auto files = compiler_read_known_units_unlocked(context);
if(std::find(files.begin(), files.end(), file_name) != files.end())
return(0);
files.push_back(file_name);
compiler_write_known_units_unlocked(context, files);
return(0);
});
context->server->known_unit_files[file_name] = true;
}
void compiler_untrack_known_unit(Request* context, String file_name)
@@ -776,7 +993,6 @@ void compiler_untrack_known_unit(Request* context, String file_name)
compiler_write_known_units_unlocked(context, files);
return(0);
});
context->server->known_unit_files.erase(file_name);
}
bool compiler_unit_needs_recompile(Request* context, String file_name, bool* source_missing)
@@ -785,13 +1001,14 @@ bool compiler_unit_needs_recompile(Request* context, String file_name, bool* sou
SharedUnit su;
setup_unit_paths(context, &su, file_name);
auto state = inspect_shared_unit_filesystem(context, &su);
auto compile_check = shared_unit_compile_check(state);
if(source_missing)
*source_missing = !state.source_exists;
if(!state.source_exists)
*source_missing = compile_check.source_missing;
if(compile_check.source_missing)
return(false);
if(state.compiled_time == 0)
return(true);
return(state.compiled_time < state.required_time);
if(compiler_failure_retry_deferred(context, &su, state))
return(false);
return(compile_check.needs_compile);
}
DTree unit_info(String path)
@@ -825,6 +1042,8 @@ DTree unit_info(String path)
}
auto fs_state = inspect_shared_unit_filesystem(context, su);
if(su->compile_status == "unknown" && compiler_failure_retry_deferred(context, su, fs_state))
compiler_restore_persisted_failure(su, fs_state);
auto exports_text = compiler_unit_exports_text(su);
auto exports = compiler_unit_exports(su);
if(exports.size() == 0 && exports_text != "")
@@ -841,6 +1060,7 @@ DTree unit_info(String path)
info["so_name"] = su->so_name;
info["api_file_name"] = su->api_file_name;
info["meta_file_name"] = su->meta_file_name;
info["compile_output_file_name"] = su->compile_output_file_name;
info["compile_status"] = (su->compile_status != "unknown" ? su->compile_status : compiler_status_from_filesystem(fs_state, su));
info["compile_error_status"] = su->compile_error_status;
info["runtime_error_status"] = su->runtime_error_status;
@@ -867,11 +1087,15 @@ DTree unit_info(String path)
info["source_mtime"] = (f64)fs_state.source_time;
info["compiled_mtime"] = (f64)fs_state.compiled_time;
info["metadata_mtime"] = (f64)fs_state.metadata_time;
info["compile_output_mtime"] = (f64)fs_state.compile_output_time;
info["setup_template_mtime"] = (f64)fs_state.setup_template_time;
info["required_mtime"] = (f64)fs_state.required_time;
info["runtime_abi_version"] = (f64)fs_state.runtime_abi_version;
info["metadata_abi_version"] = (f64)fs_state.metadata_abi_version;
compiler_tree_set_bool(info, "known", context->server->known_unit_files[resolved_path]);
info["current_input_signature"] = fs_state.current_input_signature;
info["metadata_input_signature"] = fs_state.metadata_input_signature;
info["metadata_build_token"] = fs_state.metadata_build_token;
compiler_tree_set_bool(info, "known", compiler_has_known_unit_cached(context, resolved_path));
compiler_tree_set_bool(info, "current_unit", resolved_path == compiler_current_unit_path(context));
compiler_tree_set_bool(info, "loaded", su->so_handle != 0);
compiler_tree_set_bool(info, "source_exists", fs_state.source_exists);
@@ -879,7 +1103,10 @@ DTree unit_info(String path)
compiler_tree_set_bool(info, "metadata_exists", fs_state.metadata_exists);
compiler_tree_set_bool(info, "metadata_parsed", fs_state.metadata_parsed);
compiler_tree_set_bool(info, "abi_compatible", fs_state.abi_compatible);
compiler_tree_set_bool(info, "stale", shared_unit_cache_is_stale(context, su));
compiler_tree_set_bool(info, "input_signature_matches", fs_state.input_signature_matches);
compiler_tree_set_bool(info, "stale", shared_unit_compile_check(fs_state).needs_compile && !compiler_failure_retry_deferred(context, su, fs_state));
compiler_tree_set_bool(info, "retry_deferred", compiler_failure_retry_deferred(context, su, fs_state));
compiler_tree_set_bool(info, "cache_stale", shared_unit_cache_is_stale(context, su));
compiler_tree_set_bool(info, "has_render", su->on_render != 0);
compiler_tree_set_bool(info, "has_component", su->on_component != 0);
compiler_tree_set_bool(info, "has_websocket", su->on_websocket != 0);
+2 -2
View File
@@ -7,8 +7,8 @@
String preprocess_shared_unit(Request* context, SharedUnit* su);
void setup_unit_paths(Request* context, SharedUnit* su, String file_name);
void load_shared_unit(Request* context, SharedUnit* su, String file_name);
void compile_shared_unit(Request* context, SharedUnit* su, String file_name);
void load_shared_unit(Request* context, SharedUnit* su);
void compile_shared_unit(Request* context, SharedUnit* su);
SharedUnit* get_shared_unit(Request* context, String file_name, bool opt_so_optional = false);
void compiler_invoke(Request* context, String file_name);
void compiler_invoke_websocket(Request* context, String file_name);
+296 -4
View File
@@ -1,4 +1,8 @@
#include <cctype>
#include <cmath>
#include <limits>
namespace {
template <typename TreePtr>
@@ -30,6 +34,110 @@ bool dtree_key_is_index(String key, s64 expected_index = -1)
return(true);
}
String dtree_trim(String raw)
{
if(raw == "")
return(raw);
size_t start = 0;
while(start < raw.length() && isspace(raw[start]))
start += 1;
size_t end = raw.length();
while(end > start && isspace(raw[end - 1]))
end -= 1;
return(raw.substr(start, end - start));
}
String dtree_lower(String raw)
{
for(auto& c : raw)
c = (char)tolower(c);
return(raw);
}
bool dtree_string_to_bool_value(String raw, bool& value_out)
{
raw = dtree_lower(dtree_trim(raw));
if(raw == "")
return(false);
if(raw == "1" || raw == "true" || raw == "(true)" || raw == "yes" || raw == "on")
{
value_out = true;
return(true);
}
if(raw == "0" || raw == "false" || raw == "(false)" || raw == "no" || raw == "off" || raw == "null")
{
value_out = false;
return(true);
}
return(false);
}
bool dtree_string_to_f64_value(String raw, f64& value_out)
{
raw = dtree_trim(raw);
if(raw == "")
return(false);
bool bool_value = false;
if(dtree_string_to_bool_value(raw, bool_value))
{
value_out = (bool_value ? 1.0 : 0.0);
return(true);
}
char* end = 0;
value_out = strtod(raw.c_str(), &end);
if(end == raw.c_str())
return(false);
while(end && *end != 0)
{
if(!isspace(*end))
return(false);
end += 1;
}
return(std::isfinite(value_out));
}
const DTree* dtree_scalar_map_value(const DTree& tree)
{
if(tree.type != 'M' || tree._map.size() != 1)
return(0);
auto it = tree._map.begin();
if(it == tree._map.end())
return(0);
return(&it->second.deref());
}
f64 dtree_clamp_to_f64_range(long double value)
{
if(value > std::numeric_limits<f64>::max())
return(std::numeric_limits<f64>::max());
if(value < -std::numeric_limits<f64>::max())
return(-std::numeric_limits<f64>::max());
return((f64)value);
}
s64 dtree_clamp_to_s64_range(long double value)
{
if(value > (long double)std::numeric_limits<s64>::max())
return(std::numeric_limits<s64>::max());
if(value < (long double)std::numeric_limits<s64>::min())
return(std::numeric_limits<s64>::min());
return((s64)value);
}
u64 dtree_clamp_to_u64_range(long double value)
{
if(value <= 0)
return(0);
if(value > (long double)std::numeric_limits<u64>::max())
return(std::numeric_limits<u64>::max());
return((u64)value);
}
}
void DTree::each(std::function <void (DTree t, String key)> f)
@@ -98,6 +206,158 @@ String DTree::to_string()
return("");
}
s64 DTree::to_s64()
{
const DTree& target = deref();
switch(target.type)
{
case('S'):
{
f64 value = 0;
if(!dtree_string_to_f64_value(target._String, value))
return(0);
return(dtree_clamp_to_s64_range((long double)value));
}
case('F'):
return(dtree_clamp_to_s64_range((long double)target._float));
case('B'):
return(target._bool ? 1 : 0);
case('M'):
{
const DTree* item = dtree_scalar_map_value(target);
if(item)
return(const_cast<DTree*>(item)->to_s64());
return(0);
}
case('P'):
return(dtree_clamp_to_s64_range((long double)(u64)target._ptr));
case('R'):
return(0);
}
return(0);
}
u64 DTree::to_u64()
{
const DTree& target = deref();
switch(target.type)
{
case('S'):
{
f64 value = 0;
if(!dtree_string_to_f64_value(target._String, value))
return(0);
return(dtree_clamp_to_u64_range((long double)value));
}
case('F'):
return(dtree_clamp_to_u64_range((long double)target._float));
case('B'):
return(target._bool ? 1 : 0);
case('M'):
{
const DTree* item = dtree_scalar_map_value(target);
if(item)
return(const_cast<DTree*>(item)->to_u64());
return(0);
}
case('P'):
return((u64)target._ptr);
case('R'):
return(0);
}
return(0);
}
f64 DTree::to_f64()
{
const DTree& target = deref();
switch(target.type)
{
case('S'):
{
f64 value = 0;
if(!dtree_string_to_f64_value(target._String, value))
return(0);
return(value);
}
case('F'):
return(target._float);
case('B'):
return(target._bool ? 1.0 : 0.0);
case('M'):
{
const DTree* item = dtree_scalar_map_value(target);
if(item)
return(const_cast<DTree*>(item)->to_f64());
return(0);
}
case('P'):
return(dtree_clamp_to_f64_range((long double)(u64)target._ptr));
case('R'):
return(0);
}
return(0);
}
bool DTree::to_bool()
{
const DTree& target = deref();
switch(target.type)
{
case('S'):
{
bool value = false;
if(dtree_string_to_bool_value(target._String, value))
return(value);
f64 numeric_value = 0;
if(dtree_string_to_f64_value(target._String, numeric_value))
return(numeric_value != 0);
return(dtree_trim(target._String) != "");
}
case('F'):
return(target._float != 0);
case('B'):
return(target._bool);
case('M'):
{
const DTree* item = dtree_scalar_map_value(target);
if(item)
return(const_cast<DTree*>(item)->to_bool());
return(target._map.size() > 0);
}
case('P'):
return(target._ptr != 0);
case('R'):
return(false);
}
return(false);
}
StringMap DTree::to_stringmap()
{
const DTree& target = deref();
StringMap result;
switch(target.type)
{
case('M'):
for(const auto& entry : target._map)
result[entry.first] = const_cast<DTree&>(entry.second.deref()).to_string();
break;
case('S'):
if(dtree_trim(target._String) != "")
result["value"] = target._String;
break;
case('F'):
case('B'):
case('P'):
result["value"] = const_cast<DTree&>(target).to_string();
break;
case('R'):
break;
}
return(result);
}
String DTree::to_json(char quote_char)
{
const DTree& target = deref();
@@ -350,6 +610,7 @@ void DTree::set(StringMap source)
return;
}
set_type('M');
_map.clear();
_array_index = 0;
_list_mode = false;
for (auto it = source.begin(); it != source.end(); ++it)
@@ -378,12 +639,46 @@ void DTree::set_reference(DTree* target)
_ptr = target;
}
bool DTree::has(String s) const
{
const DTree& target = deref();
if(target.type != 'M')
return(false);
return(target._map.find(s) != target._map.end());
}
DTree* DTree::key(String s)
{
DTree* target = reference_target();
if(target)
return(target->key(s));
if(type != 'M')
return(0);
auto it = _map.find(s);
if(it == _map.end())
return(0);
return(&it->second);
}
const DTree* DTree::key(String s) const
{
const DTree& target = deref();
if(target.type != 'M')
return(0);
auto it = target._map.find(s);
if(it == target._map.end())
return(0);
return(&it->second);
}
DTree* DTree::get_or_create(String s)
{
DTree* target = reference_target();
if(target)
return(target->get_or_create(s));
set_type('M');
if(_list_mode && !dtree_key_is_index(s))
_list_mode = false;
return(&_map[s]);
}
@@ -391,10 +686,7 @@ DTree& DTree::operator [] (String s) {
DTree* target = reference_target();
if(target)
return((*target)[s]);
set_type('M');
if(_list_mode && !dtree_key_is_index(s))
_list_mode = false;
return(_map[s]);
return(*get_or_create(s));
}
void DTree::operator = (String v) { set(v); }
+13
View File
@@ -2,6 +2,11 @@
String json_escape(String s, char quote_char = '"');
// DTree is UCE's general-purpose structured value container.
// It stores scalar values, nested map/list-like values, and internal references.
// Numeric and boolean reads are intentionally permissive so request data,
// JSON-decoded values, and metadata trees can be consumed without repetitive
// manual parsing at each call site.
struct DTree {
char type = 'S';
@@ -18,6 +23,11 @@ struct DTree {
bool is_array();
bool is_list() const;
String to_string();
s64 to_s64();
u64 to_u64();
f64 to_f64();
bool to_bool();
StringMap to_stringmap();
String to_json(char quote_char = '"');
String get_type_name();
DTree get_by_path(String path, String delim = "/");
@@ -35,7 +45,10 @@ struct DTree {
void set(StringMap source);
void set_array();
void set_reference(DTree* target);
bool has(String s) const;
DTree* key(String s);
const DTree* key(String s) const;
DTree* get_or_create(String s);
DTree& operator [] (String s);
void operator = (String v);
void operator = (f64 v);
+166 -54
View File
@@ -31,7 +31,7 @@ String capture_backtrace_string(u32 max_frames, u32 skip_frames)
trace += "\n";
}
free(symbols);
return(trace);
return(trace);
}
String signal_name(int sig)
@@ -49,6 +49,40 @@ String signal_name(int sig)
}
}
namespace {
String time_format_expand_delta_tokens(String format, u64 timestamp, u64 now_timestamp)
{
u64 delta_seconds = 0;
if(now_timestamp > timestamp)
delta_seconds = now_timestamp - timestamp;
format = replace(format, "%deltaY", std::to_string(delta_seconds / (60 * 60 * 24 * 365)));
format = replace(format, "%deltam", std::to_string(delta_seconds / (60 * 60 * 24 * 30)));
format = replace(format, "%deltad", std::to_string(delta_seconds / (60 * 60 * 24)));
format = replace(format, "%deltaH", std::to_string(delta_seconds / (60 * 60)));
format = replace(format, "%deltaM", std::to_string(delta_seconds / 60));
format = replace(format, "%deltaS", std::to_string(delta_seconds));
return(format);
}
String time_format_shell(String format, u64 timestamp, bool use_utc)
{
String ts;
String fmt;
u64 effective_timestamp = (timestamp > 0 ? timestamp : time());
String expanded_format = time_format_expand_delta_tokens(format, effective_timestamp, time());
if(timestamp > 0)
ts = String("-d '@")+std::to_string(timestamp)+"'";
if(expanded_format != "")
fmt = String("+'")+expanded_format+"'";
return(trim(shell_exec(String("date ") + (use_utc ? "-u " : "") + ts + " " + fmt)));
}
}
String shell_exec(String cmd)
{
//printf("(i) shell_exec(%s)\n", cmd.c_str());
@@ -154,63 +188,117 @@ bool file_exists(String path)
return(std::filesystem::exists(fp));
}
String file_get_contents(String file_name)
int file_open_locked(String file_name, int open_flags, int lock_type, int create_mode)
{
/*std::ifstream ifs(file_name);
printf("stream file desc %i\n", ifs.filedesc());
String content(
(std::istreambuf_iterator<char>(ifs) ),
(std::istreambuf_iterator<char>() ) );*/
int fd = open(file_name.c_str(), open_flags, create_mode);
if(fd == -1)
return(-1);
if(flock(fd, lock_type) == -1)
{
close(fd);
return(-1);
}
return(fd);
}
void file_close_locked(int fd)
{
if(fd == -1)
return;
flock(fd, LOCK_UN);
close(fd);
}
String file_get_contents_locked_fd(int fd)
{
if(fd == -1)
return("");
char buf[512];
String content;
s32 fd = open(file_name.c_str(), O_RDONLY);
s64 bytes_read = 0;
lseek(fd, 0, SEEK_SET);
while((bytes_read = read(fd, buf, sizeof(buf))) > 0)
content.append(buf, bytes_read);
return(content);
}
namespace {
bool file_write_all(int fd, const char* data, size_t remaining)
{
while(remaining > 0)
{
auto bytes_written = write(fd, data, remaining);
if(bytes_written < 0)
return(false);
data += bytes_written;
remaining -= bytes_written;
}
return(true);
}
}
bool file_put_contents_locked_fd(int fd, String content)
{
if(fd == -1)
return(false);
lseek(fd, 0, SEEK_SET);
if(ftruncate(fd, 0) != 0)
return(false);
if(!file_write_all(fd, content.data(), content.length()))
return(false);
return(true);
}
String file_get_contents(String file_name)
{
s32 fd = file_open_locked(file_name, O_RDONLY, LOCK_SH);
if(fd == -1)
{
printf("(!) Could not read %s\n", file_name.c_str());
return("");
}
flock(fd, LOCK_SH);
s64 bytes_read = 0;
//s64 size = lseek(fd, 0, SEEK_END);
//lseek(fd, 0, SEEK_SET);
//content.reserve(size+1);
while((bytes_read = read(fd, buf, 512)) > 0)
{
content.append(buf, bytes_read);
}
flock(fd, LOCK_UN);
close(fd);
String content = file_get_contents_locked_fd(fd);
file_close_locked(fd);
return(content);
}
bool file_put_contents(String file_name, String content)
{
s32 fd = open(file_name.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0644);
s32 fd = file_open_locked(file_name, O_RDWR | O_CREAT, LOCK_EX, 0644);
if(fd == -1)
{
printf("(!) Could not write %s\n", file_name.c_str());
return(false);
}
flock(fd, LOCK_EX);
const char* data = content.data();
size_t remaining = content.length();
while(remaining > 0)
bool ok = file_put_contents_locked_fd(fd, content);
file_close_locked(fd);
if(!ok)
{
auto bytes_written = write(fd, data, remaining);
if(bytes_written < 0)
{
flock(fd, LOCK_UN);
close(fd);
printf("(!) Could not fully write %s\n", file_name.c_str());
return(false);
}
data += bytes_written;
remaining -= bytes_written;
printf("(!) Could not fully write %s\n", file_name.c_str());
return(false);
}
return(true);
}
bool file_append_contents(String file_name, String content)
{
s32 fd = file_open_locked(file_name, O_RDWR | O_CREAT, LOCK_EX, 0644);
if(fd == -1)
{
printf("(!) Could not append %s\n", file_name.c_str());
return(false);
}
lseek(fd, 0, SEEK_END);
bool ok = file_write_all(fd, content.data(), content.length());
file_close_locked(fd);
if(!ok)
{
printf("(!) Could not fully append %s\n", file_name.c_str());
return(false);
}
flock(fd, LOCK_UN);
close(fd);
return(true);
}
@@ -284,26 +372,34 @@ u64 time()
String time_format_local(String format, u64 timestamp)
{
String ts;
String fmt;
if(timestamp > 0)
ts = String("-d '@")+std::to_string(timestamp)+"'";
if(format != "")
fmt = String("+'"+format+"'");
return(trim(shell_exec("date "+ts+" "+fmt)));
return(time_format_shell(format, timestamp, false));
}
String time_format_utc(String format, u64 timestamp)
{
String ts;
String fmt;
if(timestamp > 0)
ts = String("-d '@")+std::to_string(timestamp)+"'";
if(format == "RFC1123")
format = "%a, %d %b %Y %T GMT";
if(format != "")
fmt = String("+'"+format+"'");
return(trim(shell_exec("date -u "+ts+" "+fmt)));
return(time_format_shell(format, timestamp, true));
}
String time_format_relative(u64 timestamp, String format_very_recent, u64 medium_recency_seconds, String format_medium_recent, u64 not_recent_seconds, String format_not_recent)
{
u64 now_timestamp = time();
u64 delta_seconds = 0;
if(now_timestamp > timestamp)
delta_seconds = now_timestamp - timestamp;
format_very_recent = first(format_very_recent, "just now");
medium_recency_seconds = (medium_recency_seconds > 0 ? medium_recency_seconds : 90);
format_medium_recent = first(format_medium_recent, "%deltaM minutes ago");
not_recent_seconds = (not_recent_seconds > 0 ? not_recent_seconds : 90 * 60);
format_not_recent = first(format_not_recent, "%deltaH hours ago");
if(delta_seconds < medium_recency_seconds)
return(time_format_expand_delta_tokens(format_very_recent, timestamp, now_timestamp));
if(delta_seconds < not_recent_seconds)
return(time_format_expand_delta_tokens(format_medium_recent, timestamp, now_timestamp));
return(time_format_expand_delta_tokens(format_not_recent, timestamp, now_timestamp));
}
u64 time_parse(String time_String)
@@ -511,21 +607,29 @@ int task_kill(pid_t pid, int sig)
pid_t task_pid(String key)
{
String status_file_name = context->server->config["BIN_DIRECTORY"] + "/task-" + key;
String lock_file_name = status_file_name + ".lock";
int lock_fd = file_open_locked(lock_file_name, O_RDWR | O_CREAT, LOCK_EX, 0644);
String status_file = file_get_contents(status_file_name);
pid_t p = 0;
if(status_file != "")
{
p = int_val(status_file);
if(task_kill(p, 0) == 0) // process is still running
{
file_close_locked(lock_fd);
return(p);
}
file_unlink(status_file_name);
}
file_close_locked(lock_fd);
return(p);
}
pid_t task(String key, std::function<void()> exec_after_spawn, u64 timeout)
{
String status_file_name = context->server->config["BIN_DIRECTORY"] + "/task-" + key;
String lock_file_name = status_file_name + ".lock";
int lock_fd = file_open_locked(lock_file_name, O_RDWR | O_CREAT, LOCK_EX, 0644);
String status_file = file_get_contents(status_file_name);
pid_t p;
if(status_file != "")
@@ -534,6 +638,7 @@ pid_t task(String key, std::function<void()> exec_after_spawn, u64 timeout)
if(task_kill(p, 0) == 0) // process is still running
{
printf("(P) worker process '%s' already running: PID %i\n", key.c_str(), p);
file_close_locked(lock_fd);
return(p);
}
//printf("(P) worker process '%s' had crashed: PID %i\n", key.c_str(), p);
@@ -542,27 +647,31 @@ pid_t task(String key, std::function<void()> exec_after_spawn, u64 timeout)
p = fork();
if(p == 0)
{
file_close_locked(lock_fd);
my_pid = getpid();
file_put_contents(status_file_name, std::to_string(my_pid));
close(context->resources.client_socket);
context->resources.client_socket = 0;
//printf("(C) child procress started, PID:%i\n", my_pid);
//prctl(PR_SET_PDEATHSIG, SIGHUP);
exec_after_spawn();
int exit_lock_fd = file_open_locked(lock_file_name, O_RDWR | O_CREAT, LOCK_EX, 0644);
file_unlink(status_file_name);
file_close_locked(exit_lock_fd);
printf("(P) worker process '%s' terminated: PID %i\n", key.c_str(), my_pid);
exit(0);
}
else
{
file_put_contents(status_file_name, std::to_string(p));
file_close_locked(lock_fd);
printf("(P) worker process '%s' spawned: PID %i\n", key.c_str(), p);
return(p);
}
}
#include <unistd.h>
pid_t task_repeat(String key, u64 interval, std::function<void()> exec_after_spawn, u64 timeout)
pid_t task_repeat(String key, f64 interval, std::function<void()> exec_after_spawn, u64 timeout)
{
auto repeater_function = [&]() {
while (true)
@@ -610,6 +719,9 @@ StringMap make_server_settings()
cfg["COMPILER_SYS_PATH"] = ".";
cfg["PRECOMPILE_FILES_IN"] = "";
cfg["SITE_DIRECTORY"] = "site";
cfg["JIT_COMPILE_ON_REQUEST"] = "1";
cfg["PROACTIVE_COMPILE_ENABLED"] = "1";
cfg["COMPILE_FAILURE_RETRY_SECONDS"] = std::to_string(10);
cfg["PROACTIVE_COMPILE_CHECK_INTERVAL"] = std::to_string(60);
cfg["HTTP_PORT"] = std::to_string(8080);
+11 -6
View File
@@ -1,6 +1,8 @@
#pragma once
#include <sys/file.h>
#include <signal.h>
#include <sstream>
String shell_exec(String cmd);
String shell_escape(String raw);
@@ -9,17 +11,19 @@ String dirname(String fn);
String path_join(String base, String child);
bool mkdir(String path);
bool file_exists(String path);
int file_open_locked(String file_name, int open_flags, int lock_type = LOCK_SH, int create_mode = 0644);
void file_close_locked(int fd);
String file_get_contents_locked_fd(int fd);
bool file_put_contents_locked_fd(int fd, String content);
String file_get_contents(String file_name);
bool file_put_contents(String file_name, String content);
#include <fstream>
bool file_append_contents(String file_name, String content);
template <typename... Ts>
bool file_append(String file_name, Ts... args)
{
std::ofstream fout;
fout.open(file_name.c_str(), std::ios_base::app);
((fout << args), ...);
fout.close();
return(true);
std::ostringstream out;
((out << args), ...);
return(file_append_contents(file_name, out.str()));
}
String cwd_get();
void cwd_set(String path);
@@ -32,6 +36,7 @@ f64 time_precise();
u64 time();
String time_format_local(String format = "", u64 timestamp = 0);
String time_format_utc(String format = "", u64 timestamp = 0);
String time_format_relative(u64 timestamp, String format_very_recent = "", u64 medium_recency_seconds = 0, String format_medium_recent = "", u64 not_recent_seconds = 0, String format_not_recent = "");
u64 time_parse(String time_String);
u64 socket_connect(String host, short port);
+6
View File
@@ -1,3 +1,4 @@
#include <sys/file.h>
#include <stdlib.h>
#include <unistd.h>
#include <iostream>
@@ -97,6 +98,11 @@ Request::~Request()
delete stream;
ob_stack.clear();
ob = 0;
if(session_lock_fd != -1)
{
flock(session_lock_fd, LOCK_UN);
close(session_lock_fd);
}
for(auto& sockfd : resources.sockets)
close(sockfd);
}
+6 -1
View File
@@ -66,6 +66,7 @@ struct SharedUnit {
String so_name;
String api_file_name;
String meta_file_name;
String compile_output_file_name;
String setup_file_name;
StringList api_declarations;
std::map<String, void*> api_functions;
@@ -89,9 +90,11 @@ struct SharedUnit {
String compile_error_status = "";
String runtime_error_status = "";
time_t last_compiled = 0;
time_t observed_compiled_time = 0;
time_t last_loaded = 0;
time_t last_rendered = 0;
time_t last_error = 0;
String observed_metadata_content = "";
u64 request_count = 0;
u64 invoke_count = 0;
@@ -125,7 +128,6 @@ struct UploadedFile {
struct ServerState {
std::map<String, SharedUnit*> units;
std::map<String, bool> known_unit_files;
StringMap config;
u32 request_count = 0;
@@ -153,6 +155,9 @@ struct Request {
StringMap post;
StringMap cookies;
StringMap session;
String session_file_name = "";
String session_serialized = "";
int session_lock_fd = -1;
DTree var;
DTree cfg;
+53 -1
View File
@@ -542,27 +542,70 @@ String session_file_path(String session_id)
return(context->server->config["SESSION_PATH"] + "/" + session_id);
}
namespace {
void session_release_lock(Request* request)
{
if(!request)
return;
if(request->session_lock_fd != -1)
file_close_locked(request->session_lock_fd);
request->session_lock_fd = -1;
request->session_file_name = "";
request->session_serialized = "";
}
}
StringMap load_session_data(String session_id)
{
String session_path = session_file_path(session_id);
if(session_path == "")
return(StringMap());
if(context && context->session_lock_fd != -1 && context->session_file_name == session_path)
return(parse_query(file_get_contents_locked_fd(context->session_lock_fd)));
return(parse_query(file_get_contents(session_path)));
}
void save_session_data(String session_id, StringMap data)
{
String session_path = session_file_path(session_id);
String encoded = encode_query(data);
if(session_path == "")
{
printf("(!) Refusing to save invalid session id\n");
return;
}
file_put_contents(session_path, encode_query(data));
if(context && context->session_lock_fd != -1 && context->session_file_name == session_path)
{
if(encoded == context->session_serialized)
return;
if(file_put_contents_locked_fd(context->session_lock_fd, encoded))
context->session_serialized = encoded;
return;
}
int fd = file_open_locked(session_path, O_RDWR | O_CREAT, LOCK_EX, 0644);
if(fd == -1)
{
printf("(!) Refusing to save unreadable session file %s\n", session_path.c_str());
return;
}
if(file_get_contents_locked_fd(fd) != encoded)
file_put_contents_locked_fd(fd, encoded);
file_close_locked(fd);
}
String session_start(String session_name)
{
if(context->session_lock_fd != -1 && context->session_name == session_name && context->session_id != "")
return(context->session_id);
session_release_lock(context);
context->session.clear();
context->session_serialized = "";
context->session_file_name = "";
context->session_id = "";
context->session_name = "";
String session_id = context->cookies[session_name];
if(!is_valid_session_id(session_id))
session_id = "";
@@ -574,7 +617,15 @@ String session_start(String session_name)
}
context->session_id = session_id;
context->session_name = session_name;
context->session_file_name = session_file_path(context->session_id);
if(context->session_file_name != "")
{
context->session_lock_fd = file_open_locked(context->session_file_name, O_RDWR | O_CREAT, LOCK_EX, 0644);
if(context->session_lock_fd == -1)
printf("(!) Could not lock session file %s\n", context->session_file_name.c_str());
}
context->session = load_session_data(context->session_id);
context->session_serialized = encode_query(context->session);
return(context->session_id);
}
@@ -585,6 +636,7 @@ void session_destroy(String session_name)
set_cookie(session_name, "", time() - int_val(context->server->config["SESSION_TIME"]));
context->session.clear();
save_session_data(context->session_id, context->session);
session_release_lock(context);
context->session_id = "";
}
}