From b04ba148915eee3ba09594034d23420ac12e4c8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20H=C3=B6ppner?= Date: Wed, 12 Aug 2026 14:12:43 +0200 Subject: [PATCH] Enhance logging and task management; enable trace facility, add scheduler state logging, and improve shutdown handling --- core/include/FreeRTOSConfig.h | 2 +- core/include/nova64_internal.h | 2 + core/include/wasm_runner.h | 3 + core/src/nova64_api.c | 6 +- core/src/nova64_core.c | 212 ++++++++++++++++++--------------- core/src/wasm_runner.c | 56 ++++++++- simulator/Nova64Core.cs | 3 + simulator/Program.cs | 8 +- software/init.zig | 3 +- software/nova64.zig | 6 +- software/tmp.zig | 5 - 11 files changed, 187 insertions(+), 119 deletions(-) delete mode 100644 software/tmp.zig diff --git a/core/include/FreeRTOSConfig.h b/core/include/FreeRTOSConfig.h index 2581c2c..309e10d 100644 --- a/core/include/FreeRTOSConfig.h +++ b/core/include/FreeRTOSConfig.h @@ -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 diff --git a/core/include/nova64_internal.h b/core/include/nova64_internal.h index 7e3b00d..cbdd5b0 100644 --- a/core/include/nova64_internal.h +++ b/core/include/nova64_internal.h @@ -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 \ No newline at end of file diff --git a/core/include/wasm_runner.h b/core/include/wasm_runner.h index 6648c8c..1103dfd 100644 --- a/core/include/wasm_runner.h +++ b/core/include/wasm_runner.h @@ -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 diff --git a/core/src/nova64_api.c b/core/src/nova64_api.c index ca53ea6..e3643a0 100644 --- a/core/src/nova64_api.c +++ b/core/src/nova64_api.c @@ -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); } } diff --git a/core/src/nova64_core.c b/core/src/nova64_core.c index 07adb2b..1f05efd 100644 --- a/core/src/nova64_core.c +++ b/core/src/nova64_core.c @@ -6,6 +6,7 @@ #include "wasm_runner.h" #include #include +#include #if defined(ESP_PLATFORM) #include "esp_pm.h" @@ -19,19 +20,23 @@ #include #endif -static bool is_initialized = false; +#if defined(_WIN32) +#include +#define nova64_host_sleep_ms(ms) Sleep(ms) +#elif defined(__unix__) || defined(__APPLE__) +#include +#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 @@ -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 } \ No newline at end of file diff --git a/core/src/wasm_runner.c b/core/src/wasm_runner.c index 34177a4..762952e 100644 --- a/core/src/wasm_runner.c +++ b/core/src/wasm_runner.c @@ -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"); diff --git a/simulator/Nova64Core.cs b/simulator/Nova64Core.cs index b9109ad..046bdfb 100644 --- a/simulator/Nova64Core.cs +++ b/simulator/Nova64Core.cs @@ -42,6 +42,9 @@ internal static class Nova64Core [DllImport(LibName, CallingConvention = CallingConvention.Cdecl)] public static extern bool nova64_shutdown(); + [DllImport(LibName, CallingConvention = CallingConvention.Cdecl)] + public static extern bool nova64_is_finalized(); + [DllImport(LibName, CallingConvention = CallingConvention.Cdecl)] public static extern uint nova64_get_version(); } diff --git a/simulator/Program.cs b/simulator/Program.cs index a42bb8c..49d5632 100644 --- a/simulator/Program.cs +++ b/simulator/Program.cs @@ -145,10 +145,14 @@ internal static class Program Console.WriteLine("[Sim] Requesting core shutdown..."); if (Nova64Core.nova64_shutdown()) { - if (!_coreThread.Join(TimeSpan.FromSeconds(5))) + int timeoutMs = 1000; + while (!Nova64Core.nova64_is_finalized() && timeoutMs > 0) { - Console.WriteLine("[Sim] Core thread did not exit in time."); + Thread.Sleep(10); + timeoutMs -= 10; } + Console.WriteLine($"[Sim] Core shutdown complete after {1000 - timeoutMs}ms."); + _coreThread = null; } else { diff --git a/software/init.zig b/software/init.zig index 4d49081..dddf0cd 100644 --- a/software/init.zig +++ b/software/init.zig @@ -4,7 +4,8 @@ pub fn main() void { _ = add(1, 2); while (true) { nova64.log("Hello from Zig!"); - // nova64.yield(1000); + // nova64.yield(); + // nova64.sleep(1000); } } diff --git a/software/nova64.zig b/software/nova64.zig index 62796d1..6ad6ea2 100644 --- a/software/nova64.zig +++ b/software/nova64.zig @@ -5,6 +5,10 @@ pub fn log(message: []const u8) void { nova64_log(message.ptr); } -pub fn yield(timeout_ms: u32) void { +pub fn sleep(timeout_ms: u32) void { nova64_yield(timeout_ms); } + +pub fn yield() void { + nova64_yield(0); +} diff --git a/software/tmp.zig b/software/tmp.zig deleted file mode 100644 index 0ec431f..0000000 --- a/software/tmp.zig +++ /dev/null @@ -1,5 +0,0 @@ -extern "nova64" fn log(message: [*]const u8) void; - -pub fn main() void { - log(&"Hello from Zig!"[0]); -}