/* * Copyright (C) 2019 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ #include "bh_platform.h" #include "app_manager_export.h" #include "../app-manager/module_wasm_app.h" #include "timer_native_api.h" typedef struct { bh_list_link l; timer_ctx_t timer_ctx; } timer_ctx_node_t; static bool timer_thread_run = true; static bh_list g_timer_ctx_list; static korp_cond g_timer_ctx_list_cond; static korp_mutex g_timer_ctx_list_mutex; void wasm_timer_callback(timer_id_t id, unsigned int mod_id) { module_data* module = module_data_list_lookup_id(mod_id); if (module == NULL) return; // !!! the length parameter must be 0, so the receiver will // not free the payload pointer. bh_post_msg(module->queue, TIMER_EVENT_WASM, (char *)(uintptr_t)id, 0); } /** * why we create a separate link for module timer contexts * rather than traverse the module list? * It helps to reduce the lock frequency for the module list. * Also when we lock the module list and then call the callback for * timer expire, the callback is request the list lock again for lookup * the module from module id. It is for avoiding that situation. */ void * thread_modulers_timer_check(void * arg) { uint32 ms_to_expiry; uint64 us_to_wait; while (timer_thread_run) { ms_to_expiry = (uint32)-1; os_mutex_lock(&g_timer_ctx_list_mutex); timer_ctx_node_t* elem = (timer_ctx_node_t*) bh_list_first_elem(&g_timer_ctx_list); while (elem) { uint32 next = check_app_timers(elem->timer_ctx); if (next != (uint32)-1) { if (ms_to_expiry == (uint32)-1 || ms_to_expiry > next) ms_to_expiry = next; } elem = (timer_ctx_node_t*) bh_list_elem_next(elem); } os_mutex_unlock(&g_timer_ctx_list_mutex); if (ms_to_expiry == (uint32)-1) us_to_wait = BHT_WAIT_FOREVER; else us_to_wait = (uint64)ms_to_expiry * 1000; os_mutex_lock(&g_timer_ctx_list_mutex); os_cond_reltimedwait(&g_timer_ctx_list_cond, &g_timer_ctx_list_mutex, us_to_wait); os_mutex_unlock(&g_timer_ctx_list_mutex); } return NULL; } void wakeup_modules_timer_thread(timer_ctx_t ctx) { os_mutex_lock(&g_timer_ctx_list_mutex); os_cond_signal(&g_timer_ctx_list_cond); os_mutex_unlock(&g_timer_ctx_list_mutex); } void init_wasm_timer() { korp_tid tm_tid; bh_list_init(&g_timer_ctx_list); os_cond_init(&g_timer_ctx_list_cond); /* temp solution for: thread_modulers_timer_check thread would recursive lock the mutex */ os_recursive_mutex_init(&g_timer_ctx_list_mutex); os_thread_create(&tm_tid, thread_modulers_timer_check, NULL, BH_APPLET_PRESERVED_STACK_SIZE); } void exit_wasm_timer() { timer_thread_run = false; } timer_ctx_t create_wasm_timer_ctx(unsigned int module_id, int prealloc_num) { timer_ctx_t ctx = create_timer_ctx(wasm_timer_callback, wakeup_modules_timer_thread, prealloc_num, module_id); if (ctx == NULL) return NULL; timer_ctx_node_t * node = (timer_ctx_node_t*) wasm_runtime_malloc(sizeof(timer_ctx_node_t)); if (node == NULL) { destroy_timer_ctx(ctx); return NULL; } memset(node, 0, sizeof(*node)); node->timer_ctx = ctx; os_mutex_lock(&g_timer_ctx_list_mutex); bh_list_insert(&g_timer_ctx_list, node); os_mutex_unlock(&g_timer_ctx_list_mutex); return ctx; } void destroy_module_timer_ctx(unsigned int module_id) { timer_ctx_node_t* elem; os_mutex_lock(&g_timer_ctx_list_mutex); elem = (timer_ctx_node_t*) bh_list_first_elem(&g_timer_ctx_list); while (elem) { if (timer_ctx_get_owner(elem->timer_ctx) == module_id) { bh_list_remove(&g_timer_ctx_list, elem); destroy_timer_ctx(elem->timer_ctx); wasm_runtime_free(elem); break; } elem = (timer_ctx_node_t*) bh_list_elem_next(elem); } os_mutex_unlock(&g_timer_ctx_list_mutex); } timer_ctx_t get_wasm_timer_ctx(wasm_module_inst_t module_inst) { module_data * m = app_manager_get_module_data(Module_WASM_App, module_inst); if (m == NULL) return NULL; return m->timer_ctx; } timer_id_t wasm_create_timer(wasm_exec_env_t exec_env, int interval, bool is_period, bool auto_start) { wasm_module_inst_t module_inst = get_module_inst(exec_env); timer_ctx_t timer_ctx = get_wasm_timer_ctx(module_inst); bh_assert(timer_ctx); return sys_create_timer(timer_ctx, interval, is_period, auto_start); } void wasm_timer_destroy(wasm_exec_env_t exec_env, timer_id_t timer_id) { wasm_module_inst_t module_inst = get_module_inst(exec_env); timer_ctx_t timer_ctx = get_wasm_timer_ctx(module_inst); bh_assert(timer_ctx); sys_timer_destroy(timer_ctx, timer_id); } void wasm_timer_cancel(wasm_exec_env_t exec_env, timer_id_t timer_id) { wasm_module_inst_t module_inst = get_module_inst(exec_env); timer_ctx_t timer_ctx = get_wasm_timer_ctx(module_inst); bh_assert(timer_ctx); sys_timer_cancel(timer_ctx, timer_id); } void wasm_timer_restart(wasm_exec_env_t exec_env, timer_id_t timer_id, int interval) { wasm_module_inst_t module_inst = get_module_inst(exec_env); timer_ctx_t timer_ctx = get_wasm_timer_ctx(module_inst); bh_assert(timer_ctx); sys_timer_restart(timer_ctx, timer_id, interval); } uint32 wasm_get_sys_tick_ms(wasm_exec_env_t exec_env) { return (uint32)bh_get_tick_ms(); }