Return component ONCE slots with handler resolution
This commit is contained in:
@@ -69,6 +69,7 @@ if [[ "$action" == "run" ]]; then
|
|||||||
scripts/test_dependency_invalidation.sh
|
scripts/test_dependency_invalidation.sh
|
||||||
scripts/test_cold_component_deadline.sh
|
scripts/test_cold_component_deadline.sh
|
||||||
scripts/test_nested_component_props.sh
|
scripts/test_nested_component_props.sh
|
||||||
|
scripts/test_component_once_prefetch.sh
|
||||||
scripts/test_relative_component_cache.sh
|
scripts/test_relative_component_cache.sh
|
||||||
scripts/test_password_hashing.sh
|
scripts/test_password_hashing.sh
|
||||||
scripts/test_mysql_epoch_refresh.sh
|
scripts/test_mysql_epoch_refresh.sh
|
||||||
|
|||||||
Executable
+39
@@ -0,0 +1,39 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
cd "$(dirname "$0")/.."
|
||||||
|
|
||||||
|
test_name="component-once-prefetch-test-$$"
|
||||||
|
site_directory="${UCE_TEST_SITE_DIRECTORY:-site}"
|
||||||
|
if [[ -z "${UCE_TEST_SITE_DIRECTORY:-}" && -r /etc/uce/settings.cfg ]]; then
|
||||||
|
configured_site_directory=$(awk -F= '/^[[:space:]]*SITE_DIRECTORY[[:space:]]*=/ {gsub(/^[[:space:]]+|[[:space:]]+$/, "", $2); print $2; exit}' /etc/uce/settings.cfg)
|
||||||
|
if [[ -n "${configured_site_directory:-}" ]]; then
|
||||||
|
site_directory="$configured_site_directory"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
source_dir="$site_directory/$test_name"
|
||||||
|
|
||||||
|
cleanup() {
|
||||||
|
rm -rf "$source_dir"
|
||||||
|
}
|
||||||
|
trap cleanup EXIT
|
||||||
|
mkdir -p "$source_dir"
|
||||||
|
|
||||||
|
printf '%s\n' \
|
||||||
|
'CLI(Request& context) {' \
|
||||||
|
' String first = component("child", context);' \
|
||||||
|
' String second = component("child", context);' \
|
||||||
|
' DValue perf = request_perf();' \
|
||||||
|
' print(first, ",", second, ",", perf["component_resolve_count"].to_u64(), ",", perf["component_loaded_reuse_count"].to_u64());' \
|
||||||
|
'}' >"$source_dir/entry.uce"
|
||||||
|
|
||||||
|
printf '%s\n' \
|
||||||
|
'ONCE(Request& context) { context.call["once"] = context.call["once"].to_u64() + 1; }' \
|
||||||
|
'COMPONENT(Request& context) { print(context.call["once"].to_u64()); }' >"$source_dir/child.uce"
|
||||||
|
|
||||||
|
output=$(scripts/uce-cli "/$test_name/entry.uce")
|
||||||
|
if [[ "$output" != "1,1,1,0" ]]; then
|
||||||
|
echo "component ONCE slot was not returned with the primary handler: $output" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "component ONCE prefetch passed"
|
||||||
+15
-4
@@ -466,7 +466,8 @@ extern "C" int32_t uce_host_component_resolve(
|
|||||||
const char* target, size_t target_len,
|
const char* target, size_t target_len,
|
||||||
const char* handler, size_t handler_len,
|
const char* handler, size_t handler_len,
|
||||||
const char* current_unit, size_t current_unit_len,
|
const char* current_unit, size_t current_unit_len,
|
||||||
char* resolved_buf, size_t resolved_cap);
|
char* resolved_buf, size_t resolved_cap,
|
||||||
|
int32_t* once_slot_out);
|
||||||
|
|
||||||
// target → table slot, reset per request (workspaces die with the request,
|
// target → table slot, reset per request (workspaces die with the request,
|
||||||
// but a single workspace can render the same component many times)
|
// but a single workspace can render the same component many times)
|
||||||
@@ -554,16 +555,26 @@ static s32 wasm_resolve_target(String unit_target, String handler, String* resol
|
|||||||
return(cached->second);
|
return(cached->second);
|
||||||
}
|
}
|
||||||
char resolved[512];
|
char resolved[512];
|
||||||
|
s32 once_slot = 0;
|
||||||
s32 slot = uce_host_component_resolve(
|
s32 slot = uce_host_component_resolve(
|
||||||
unit_target.data(), unit_target.size(), handler.data(), handler.size(),
|
unit_target.data(), unit_target.size(), handler.data(), handler.size(),
|
||||||
current.data(), current.size(),
|
current.data(), current.size(),
|
||||||
resolved, sizeof(resolved));
|
resolved, sizeof(resolved), &once_slot);
|
||||||
|
String resolved_path = slot ? String(resolved, strnlen(resolved, sizeof(resolved))) : String("");
|
||||||
if(resolved_out && slot)
|
if(resolved_out && slot)
|
||||||
*resolved_out = String(resolved, strnlen(resolved, sizeof(resolved)));
|
*resolved_out = resolved_path;
|
||||||
if(!is_exists)
|
if(!is_exists)
|
||||||
{
|
{
|
||||||
wasm_component_slots[cache_key] = slot;
|
wasm_component_slots[cache_key] = slot;
|
||||||
wasm_component_paths[cache_key] = slot ? String(resolved, strnlen(resolved, sizeof(resolved))) : String("");
|
wasm_component_paths[cache_key] = resolved_path;
|
||||||
|
bool runs_once = handler == "render" || handler.rfind("render:", 0) == 0 ||
|
||||||
|
handler == "component" || handler.rfind("component:", 0) == 0;
|
||||||
|
if(slot && runs_once)
|
||||||
|
{
|
||||||
|
String once_key = current + "\t" + "once" + "\t" + resolved_path;
|
||||||
|
wasm_component_slots[once_key] = once_slot;
|
||||||
|
wasm_component_paths[once_key] = once_slot ? resolved_path : String("");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return(slot);
|
return(slot);
|
||||||
}
|
}
|
||||||
|
|||||||
+38
-32
@@ -2527,8 +2527,11 @@ private:
|
|||||||
// hostcall body: uce_host_component_resolve(unit, handler, current) → slot.
|
// hostcall body: uce_host_component_resolve(unit, handler, current) → slot.
|
||||||
// `handler` names the export ("render", "component:CARD", "cli",
|
// `handler` names the export ("render", "component:CARD", "cli",
|
||||||
// "serve_http:named", "once") or is "exists" (probe only, loads nothing).
|
// "serve_http:named", "once") or is "exists" (probe only, loads nothing).
|
||||||
int32_t component_resolve(const String& target, const String& handler, const String& current_unit, String& resolved_out)
|
int32_t component_resolve(const String& target, const String& handler, const String& current_unit,
|
||||||
|
String& resolved_out, int32_t* once_slot_out = 0)
|
||||||
{
|
{
|
||||||
|
if(once_slot_out)
|
||||||
|
*once_slot_out = 0;
|
||||||
auto probe_start = std::chrono::steady_clock::now();
|
auto probe_start = std::chrono::steady_clock::now();
|
||||||
auto record_probe = [&]() {
|
auto record_probe = [&]() {
|
||||||
component_resolve_count += 1;
|
component_resolve_count += 1;
|
||||||
@@ -2675,38 +2678,38 @@ private:
|
|||||||
component_link_total_us += (u64)std::chrono::duration_cast<std::chrono::microseconds>(
|
component_link_total_us += (u64)std::chrono::duration_cast<std::chrono::microseconds>(
|
||||||
std::chrono::steady_clock::now() - link_start).count();
|
std::chrono::steady_clock::now() - link_start).count();
|
||||||
};
|
};
|
||||||
String symbol = handler_export_symbol(handler);
|
auto link_handler = [&](const String& requested_handler) -> int32_t {
|
||||||
String slot_key = resolved + ":" + symbol;
|
String symbol = handler_export_symbol(requested_handler);
|
||||||
auto cached = handler_slots.find(slot_key);
|
String slot_key = resolved + ":" + symbol;
|
||||||
if(cached != handler_slots.end())
|
auto cached = handler_slots.find(slot_key);
|
||||||
{
|
if(cached != handler_slots.end())
|
||||||
record_link();
|
return((int32_t)cached->second);
|
||||||
record_probe();
|
auto handler_fn = unit_func(unit_index, symbol);
|
||||||
return((int32_t)cached->second);
|
if(!handler_fn)
|
||||||
}
|
{
|
||||||
auto handler_fn = unit_func(unit_index, symbol);
|
// ONCE is optional per unit; a missing __uce_once is not an error.
|
||||||
if(!handler_fn)
|
if(requested_handler != "once")
|
||||||
{
|
fprintf(stderr, "[wasm] %s does not export %s\n", resolved.c_str(), symbol.c_str());
|
||||||
// ONCE is optional per unit; a missing __uce_once is not an error
|
return(0);
|
||||||
if(handler != "once")
|
}
|
||||||
fprintf(stderr, "[wasm] %s does not export %s\n", resolved.c_str(), symbol.c_str());
|
u32 slot = 0;
|
||||||
record_link();
|
error = place_funcref(*handler_fn, slot);
|
||||||
record_probe();
|
if(error != "")
|
||||||
return(0);
|
{
|
||||||
}
|
fprintf(stderr, "[wasm] %s\n", error.c_str());
|
||||||
u32 slot = 0;
|
return(0);
|
||||||
error = place_funcref(*handler_fn, slot);
|
}
|
||||||
if(error != "")
|
handler_slots[slot_key] = slot;
|
||||||
{
|
return((int32_t)slot);
|
||||||
fprintf(stderr, "[wasm] %s\n", error.c_str());
|
};
|
||||||
record_link();
|
int32_t slot = link_handler(handler);
|
||||||
record_probe();
|
bool runs_once = handler == "render" || handler.rfind("render:", 0) == 0 ||
|
||||||
return(0);
|
handler == "component" || handler.rfind("component:", 0) == 0;
|
||||||
}
|
if(slot && once_slot_out && runs_once)
|
||||||
handler_slots[slot_key] = slot;
|
*once_slot_out = link_handler("once");
|
||||||
record_link();
|
record_link();
|
||||||
record_probe();
|
record_probe();
|
||||||
return((int32_t)slot);
|
return(slot);
|
||||||
}
|
}
|
||||||
|
|
||||||
String run_task_callback(u64 callback_id)
|
String run_task_callback(u64 callback_id)
|
||||||
@@ -4013,7 +4016,8 @@ private:
|
|||||||
self->hostcall_read(args[0].i32(), args[1].i32(), target);
|
self->hostcall_read(args[0].i32(), args[1].i32(), target);
|
||||||
self->hostcall_read(args[2].i32(), args[3].i32(), handler);
|
self->hostcall_read(args[2].i32(), args[3].i32(), handler);
|
||||||
self->hostcall_read(args[4].i32(), args[5].i32(), current);
|
self->hostcall_read(args[4].i32(), args[5].i32(), current);
|
||||||
int32_t slot = self->component_resolve(target, handler, current, resolved);
|
int32_t once_slot = 0;
|
||||||
|
int32_t slot = self->component_resolve(target, handler, current, resolved, &once_slot);
|
||||||
u32 cap = (u32)args[7].i32();
|
u32 cap = (u32)args[7].i32();
|
||||||
if(cap > 0)
|
if(cap > 0)
|
||||||
{
|
{
|
||||||
@@ -4022,6 +4026,8 @@ private:
|
|||||||
resolved.push_back('\0');
|
resolved.push_back('\0');
|
||||||
self->hostcall_write(args[6].i32(), resolved);
|
self->hostcall_write(args[6].i32(), resolved);
|
||||||
}
|
}
|
||||||
|
String once_slot_bytes((const char*)&once_slot, sizeof(once_slot));
|
||||||
|
self->hostcall_write(args[8].i32(), once_slot_bytes);
|
||||||
results[0] = Val(slot);
|
results[0] = Val(slot);
|
||||||
return(std::monostate());
|
return(std::monostate());
|
||||||
}));
|
}));
|
||||||
|
|||||||
Reference in New Issue
Block a user