Enhance logging and task management; enable trace facility, add scheduler state logging, and improve shutdown handling
This commit is contained in:
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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
|
||||
}
|
||||
@@ -312,3 +262,67 @@ void vApplicationIdleHook(void) {
|
||||
|
||||
#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
@@ -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");
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
+2
-1
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+5
-1
@@ -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);
|
||||
}
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
extern "nova64" fn log(message: [*]const u8) void;
|
||||
|
||||
pub fn main() void {
|
||||
log(&"Hello from Zig!"[0]);
|
||||
}
|
||||
Reference in New Issue
Block a user