Enhance logging and task management; enable trace facility, add scheduler state logging, and improve shutdown handling

This commit is contained in:
Lukas Höppner
2026-08-12 14:12:43 +02:00
parent b623e5cf0a
commit b04ba14891
11 changed files with 187 additions and 119 deletions
+1 -1
View File
@@ -12,7 +12,7 @@
#define configMINIMAL_STACK_SIZE ( ( unsigned short ) 128 )
#define configTOTAL_HEAP_SIZE ( ( size_t ) ( 64 * 1024 ) )
#define configMAX_TASK_NAME_LEN ( 16 )
#define configUSE_TRACE_FACILITY 0
#define configUSE_TRACE_FACILITY 1
#define configUSE_16_BIT_TICKS 0
#define configIDLE_SHOULD_YIELD 1
+2
View File
@@ -7,4 +7,6 @@ void nova64_log(const char *format, ...);
const nova64_io_interface_t *nova64_get_io_interface(void);
void nova64_log_scheduler_state(void);
#endif // NOVA64_INTERNAL_H
+3
View File
@@ -32,4 +32,7 @@ bool wasm_runner_start_program(
void wasm_runner_terminate(void);
uint32_t wasm_runner_get_active_slots
(void);
#endif // WASM_RUNNER_H
+2 -4
View File
@@ -17,16 +17,14 @@ static void native_yield(wasm_exec_env_t exec_env, uint32_t timeout_ms) {
static void native_log(wasm_exec_env_t exec_env, const char *message) {
(void)exec_env;
nova64_log("[WASM LOG] ");
const char *text = message ? message : "";
size_t len = strlen(text);
bool has_newline = len > 0 && text[len - 1] == '\n';
if (has_newline) {
nova64_log("%s", text);
nova64_log("[WASM LOG] %s", text);
} else {
nova64_log("%s\n", text);
nova64_log("[WASM LOG] %s\n", text);
}
}
+113 -99
View File
@@ -6,6 +6,7 @@
#include "wasm_runner.h"
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#if defined(ESP_PLATFORM)
#include "esp_pm.h"
@@ -19,19 +20,23 @@
#include <time.h>
#endif
static bool is_initialized = false;
#if defined(_WIN32)
#include <windows.h>
#define nova64_host_sleep_ms(ms) Sleep(ms)
#elif defined(__unix__) || defined(__APPLE__)
#include <unistd.h>
#define nova64_host_sleep_ms(ms) usleep((ms) * 1000)
#else
#define nova64_host_sleep_ms(ms) ((void)0)
#endif
volatile bool is_initialized = false;
volatile bool shutdown_requested = false;
static nova64_io_interface_t g_io_interface_storage;
static const nova64_io_interface_t *g_io_interface = NULL;
static QueueHandle_t event_queue = NULL;
static TaskHandle_t boot_task_handle = NULL;
static TaskHandle_t init_task_handle = NULL;
static TaskHandle_t heartbeat_task_handle = NULL;
#if defined(NOVA64_SHUTDOWN_AVAILABLE)
static TaskHandle_t shutdown_task_handle = NULL;
volatile bool shutdown_requested = false;
#endif
static const char *const kInternalInitPath = "SYS:/init.wasm";
#if defined(_WIN32)
#include <windows.h>
@@ -92,16 +97,16 @@ static bool initialize_wasm_runtime(void) {
return true;
}
static void start_init_from_internal_storage(void *params) {
static void nova64_init_task(void *params) {
(void)params;
wasm_runner_start_program(kInternalInitPath, NULL);
}
static bool start_init_task(void) {
BaseType_t result =
xTaskCreate(start_init_from_internal_storage, "Nova64Init",
4096 / sizeof(StackType_t), NULL, tskIDLE_PRIORITY + 1,
&init_task_handle);
xTaskCreate(nova64_init_task, "Nova64Init", 4096 / sizeof(StackType_t),
NULL, tskIDLE_PRIORITY + 1,
/*&init_task_handle*/ NULL);
if (result != pdPASS) {
nova64_log("Failed to create init task.\n");
@@ -112,98 +117,31 @@ static bool start_init_task(void) {
return true;
}
#if defined(NOVA64_SHUTDOWN_AVAILABLE)
static void nova64_shutdown_task(void *params) {
(void)params;
nova64_log("Shutdown task started.\n");
shutdown_requested = false;
for (;;) {
ulTaskNotifyTake(pdTRUE,
pdMS_TO_TICKS(50)); // Wait for notification or timeout
if (shutdown_requested) {
break;
}
}
nova64_log("Shutdown requested; terminating WASM runtime.\n");
wasm_runner_terminate();
if (heartbeat_task_handle != NULL) {
vTaskDelete(heartbeat_task_handle);
heartbeat_task_handle = NULL;
}
vTaskDelay(pdMS_TO_TICKS(100)); // Allow time for tasks to clean up
shutdown_task_handle = NULL;
#if defined(_WIN32)
ExitThread(0);
#elif defined(__unix__) || defined(__APPLE__)
pthread_exit(NULL);
#else
vTaskDelete(NULL);
#endif
}
static bool start_shutdown_task(void) {
BaseType_t result = xTaskCreate(nova64_shutdown_task, "Nova64Shutdown",
2048 / sizeof(StackType_t), NULL,
tskIDLE_PRIORITY + 2, &shutdown_task_handle);
if (result != pdPASS) {
nova64_log("Failed to create shutdown task.\n");
return false;
}
nova64_log("Shutdown task created successfully.\n");
return true;
}
#endif
void heartbeat_task(void *pvParameters) {
void nova64_heartbeat_task(void *pvParameters) {
TickType_t xLastWakeTime = xTaskGetTickCount();
const TickType_t xFrequency = pdMS_TO_TICKS(1000);
uint32_t seconds = 0;
for (;;) {
vTaskDelayUntil(&xLastWakeTime, xFrequency);
while (!shutdown_requested) {
// vTaskDelayUntil(&xLastWakeTime, xFrequency);
ulTaskNotifyTake(pdTRUE, xFrequency);
seconds++;
nova64_log("[Heartbeat] System running for %lu s | FreeRTOS Tick: %lu\n",
(unsigned long)seconds, (unsigned long)xTaskGetTickCount());
}
heartbeat_task_handle = NULL;
vTaskDelete(NULL); // Delete this task when shutdown is requested
}
void start_heartbeat_task(void) {
xTaskCreate(heartbeat_task, "heartbeat", 2048, NULL, 1,
xTaskCreate(nova64_heartbeat_task, "heartbeat", 2048, NULL, 1,
&heartbeat_task_handle);
}
static void nova64_boot_task(void *params) {
(void)params;
nova64_log("Boot task started.\n");
if (!initialize_wasm_runtime()) {
nova64_log("WASM runtime initialization failed.\n");
vTaskDelete(NULL);
return;
}
if (!start_init_task()) {
nova64_log("Init startup failed.\n");
vTaskDelete(NULL);
return;
}
start_heartbeat_task();
nova64_log("Boot process completed.\n");
vTaskDelete(NULL);
}
const nova64_io_interface_t *nova64_get_io_interface(void) {
return g_io_interface;
}
@@ -224,20 +162,17 @@ NOVA64_API bool nova64_init(const nova64_io_interface_t *io_interface) {
return false;
}
#if defined(NOVA64_SHUTDOWN_AVAILABLE)
if (!start_shutdown_task()) {
nova64_log("Initializing...\n");
if (!initialize_wasm_runtime()) {
nova64_log("WASM runtime initialization failed.\n");
return false;
}
#endif
nova64_log("Starting boot process...\n");
BaseType_t created =
xTaskCreate(nova64_boot_task, "Nova64Boot", 4096 / sizeof(StackType_t),
NULL, tskIDLE_PRIORITY + 2, &boot_task_handle);
if (created != pdPASS) {
nova64_log("Failed to create boot task.\n");
if (!start_init_task()) {
nova64_log("Init startup failed.\n");
return false;
}
start_heartbeat_task();
is_initialized = true;
return true;
@@ -267,15 +202,30 @@ NOVA64_API bool nova64_shutdown(void) {
nova64_log("Scheduler shutdown requested from host.\n");
shutdown_requested = true;
if (shutdown_task_handle != NULL) {
xTaskNotifyGive(shutdown_task_handle);
return true;
if (heartbeat_task_handle) {
// vTaskDelete(heartbeat_task_handle);
// heartbeat_task_handle = NULL;
xTaskNotifyGive(heartbeat_task_handle);
}
wasm_runner_terminate();
nova64_host_sleep_ms(100); // Allow time for tasks to clean up
nova64_log_scheduler_state();
is_initialized = false;
return true;
}
#endif
NOVA64_API bool nova64_is_finalized(void) {
bool heartbeat_finished = (heartbeat_task_handle == NULL);
bool wasm_finished = (wasm_runner_get_active_slots() == 0);
return heartbeat_finished && wasm_finished;
}
NOVA64_API uint32_t nova64_get_version(void) {
return 0x010000; // v1.0.0
}
@@ -311,4 +261,68 @@ void vApplicationIdleHook(void) {
nanosleep(&ts, NULL);
#endif
}
void nova64_log_scheduler_state() {
/* Log currently active tasks for debugging shutdown issues. If FreeRTOS was
* built with trace facility, enumerate tasks and print their names and
* states. Otherwise, at least log the current total task count and known
* handles. */
#if configUSE_TRACE_FACILITY == 1
{
UBaseType_t task_count = uxTaskGetNumberOfTasks();
TaskStatus_t *tasks =
(TaskStatus_t *)pvPortMalloc(task_count * sizeof(TaskStatus_t));
if (tasks) {
uint32_t total_run_time = 0;
UBaseType_t fetched =
uxTaskGetSystemState(tasks, task_count, &total_run_time);
nova64_log("Active tasks: %u (fetched %u)\n", (unsigned)task_count,
(unsigned)fetched);
for (UBaseType_t i = 0; i < fetched; i++) {
const char *state_str = "UNKNOWN";
switch (tasks[i].eCurrentState) {
case eRunning:
state_str = "Running";
break;
case eReady:
state_str = "Ready";
break;
case eBlocked:
state_str = "Blocked";
break;
case eSuspended:
state_str = "Suspended";
break;
case eDeleted:
state_str = "Deleted";
break;
default:
state_str = "Unknown";
break;
}
nova64_log(" - %s (handle=%p) state=%s priority=%u\n",
tasks[i].pcTaskName, (void *)tasks[i].xHandle, state_str,
(unsigned)tasks[i].uxBasePriority);
}
vPortFree(tasks);
} else {
nova64_log("Failed to allocate task list buffer.\n");
}
}
#else
{
UBaseType_t task_count = uxTaskGetNumberOfTasks();
nova64_log("Task count: %u\n", (unsigned)task_count);
if (boot_task_handle)
nova64_log("Boot task: %s\n", pcTaskGetName(boot_task_handle));
// if (init_task_handle)
// nova64_log("Init task: %s\n", pcTaskGetName(init_task_handle));
if (heartbeat_task_handle)
nova64_log("Heartbeat task: %s\n", pcTaskGetName(heartbeat_task_handle));
if (shutdown_task_handle)
nova64_log("Shutdown task: %s\n", pcTaskGetName(shutdown_task_handle));
}
#endif
}
+50 -6
View File
@@ -14,7 +14,7 @@ typedef enum { SLOT_FREE = 0, SLOT_RUNNING, SLOT_STOPPING } slot_state_t;
typedef struct {
int id;
slot_state_t state;
// TaskHandle_t freertos_handle;
TaskHandle_t freertos_handle;
wasm_module_inst_t module_inst;
wasm_exec_env_t exec_env;
} wasm_task_slot_t;
@@ -29,7 +29,7 @@ bool wasm_runner_initialize(void) {
for (int i = 0; i < MAX_WASM_TASKS; i++) {
g_wasm_slots[i].id = i;
g_wasm_slots[i].state = SLOT_FREE;
// g_wasm_slots[i].freertos_handle = NULL;
g_wasm_slots[i].freertos_handle = NULL;
g_wasm_slots[i].module_inst = NULL;
}
@@ -45,6 +45,16 @@ bool wasm_runner_destroy(void) {
return true;
}
uint32_t wasm_runner_get_active_slots() {
uint32_t result = 0;
for (int i = 0; i < MAX_WASM_TASKS; i++) {
if (g_wasm_slots[i].state != SLOT_FREE) {
result++;
}
}
return result;
}
bool wasm_runner_start_program(
const char *module_path,
const wasm_runner_framebuffer_config_t *framebuffer_config) {
@@ -111,6 +121,10 @@ bool wasm_runner_start_program(
slot->state = SLOT_RUNNING;
/* Record the FreeRTOS task handle for this slot so the task can be
* referenced or force-deleted later if necessary. */
slot->freertos_handle = xTaskGetCurrentTaskHandle();
if (wasm_runtime_call_wasm(slot->exec_env, start_func, 0, NULL)) {
nova64_log("WASM module executed successfully.\n");
} else {
@@ -129,6 +143,10 @@ bool wasm_runner_start_program(
slot->module_inst = NULL;
slot->exec_env = NULL;
slot->state = SLOT_FREE;
nova64_log("WASM module execution completed and resources cleaned up.\n");
nova64_log("WASM task slot %d is now free. Deleting task %p...\n", slot->id,
(void *)xTaskGetCurrentTaskHandle());
slot->freertos_handle = NULL;
vTaskDelete(NULL);
return true;
@@ -138,12 +156,17 @@ void wasm_runner_terminate(void) {
nova64_log("Stopping all WASM tasks...\n");
for (int i = 0; i < MAX_WASM_TASKS; i++) {
if (g_wasm_slots[i].state == SLOT_RUNNING && g_wasm_slots[i].module_inst) {
if (g_wasm_slots[i].state == SLOT_RUNNING) {
g_wasm_slots[i].state = SLOT_STOPPING;
wasm_runtime_terminate(g_wasm_slots[i].module_inst);
if (g_wasm_slots[i].module_inst) {
wasm_runtime_terminate(g_wasm_slots[i].module_inst);
}
}
}
/* Wait a short grace period for tasks to exit on their own. */
const TickType_t grace = pdMS_TO_TICKS(2000);
TickType_t start = xTaskGetTickCount();
bool any_running = true;
while (any_running) {
any_running = false;
@@ -153,9 +176,30 @@ void wasm_runner_terminate(void) {
break;
}
}
if (any_running) {
vTaskDelay(pdMS_TO_TICKS(10));
if (!any_running) {
break;
}
if ((xTaskGetTickCount() - start) > grace) {
/* Force-delete any remaining FreeRTOS tasks */
for (int i = 0; i < MAX_WASM_TASKS; i++) {
if (g_wasm_slots[i].state != SLOT_FREE &&
g_wasm_slots[i].freertos_handle != NULL) {
nova64_log("Force-deleting WASM task %d\n", i);
vTaskDelete(g_wasm_slots[i].freertos_handle);
g_wasm_slots[i].freertos_handle = NULL;
}
/* Mark slot free even if we couldn't fully clean up module state to
* avoid blocking shutdown; it's better to free the native thread. */
g_wasm_slots[i].module_inst = NULL;
g_wasm_slots[i].exec_env = NULL;
g_wasm_slots[i].state = SLOT_FREE;
}
break;
}
vTaskDelay(pdMS_TO_TICKS(10));
}
nova64_log("All WASM tasks successfully stopped.\n");