--- /dev/null
+/*
+ * Copyright (c) 2025 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <unistd.h>
+#include <stdbool.h>
+#include <dlfcn.h>
+#include <dirent.h>
+#include <errno.h>
+
+#include <glib.h>
+#include <glib-object.h>
+#include <glib-unix.h>
+#include <rpc-port-internal.h>
+#include <bundle_internal.h>
+
+#include "hal-common.h"
+#include "hal-api-conf.h"
+#include "common.h"
+
+#include "thread.h"
+
+#define RPC_STUB_PROC_NAME "d::HalBackendService"
+
+struct hal_backend_service_data {
+ enum hal_module module;
+ struct thread *thread;
+ gboolean is_initialized;
+};
+
+enum hal_backend_service_func {
+ HAL_BACKEND_SERVICE_FUNC_UNKNOWN = 0,
+ HAL_BACKEND_SERVICE_FUNC_GET_NAME,
+ HAL_BACKEND_SERVICE_FUNC_INIT,
+ HAL_BACKEND_SERVICE_FUNC_EXIT,
+};
+
+extern char *program_invocation_name;
+
+static GMainLoop *g_g_main_loop;
+static GSList *g_hal_backend_service_data_list = NULL;
+G_LOCK_DEFINE_STATIC(hal_backend_service_lock);
+
+static inline const char* get_backend_service_library_name(struct __hal_module_info *info)
+{
+ if (!info)
+ return NULL;
+
+#if defined(__aarch64__) || defined(__x86_64__) || defined (__riscv)
+ return info->backend_service_library_name_64bit;
+#else
+ return info->backend_service_library_name;
+#endif
+}
+
+static int __open_backend_service(struct __hal_module_info *info)
+{
+ const char *backend_service_library_name = NULL;
+ int ret;
+
+ if (info->backend_service && info->backend_service_handle)
+ return 0;
+
+ backend_service_library_name = get_backend_service_library_name(info);
+ if (!backend_service_library_name) {
+ _E("%s: Failed to get backend service library name\n",
+ info->module_name);
+ ret = -EINVAL;
+ goto err;
+ } else if (access(backend_service_library_name, F_OK) == -1) {
+ _I("%s: There is no backend service library\n",
+ info->module_name);
+ ret = -ENOENT;
+ goto err;
+ }
+
+ _I("%s: Prepare to open HAL backend Plugin: library(%s)/count(%d) by %s\n",
+ info->module_name, get_backend_service_library_name(info), info->backend_service_usage_count,
+ program_invocation_name);
+
+ if (!info->backend_service_symbol_name) {
+ _E("%s: Failed to get backend symbol name\n",
+ info->module_name);
+ ret = -EINVAL;
+ goto err;
+ }
+
+ info->backend_service_handle = dlopen(backend_service_library_name, RTLD_LAZY);
+ if (!info->backend_service_handle) {
+ _E("%s: Failed to load backend service library (%s)\n",
+ info->module_name, dlerror());
+ ret = -EINVAL;
+ goto err;
+ }
+
+ info->backend_service = dlsym(info->backend_service_handle,
+ info->backend_service_symbol_name);
+ if (!info->backend_service) {
+ _E("%s: Failed to find backend service data (%s)\n",
+ info->module_name, dlerror());
+ ret = -EINVAL;
+ goto err_dlclose;
+ }
+
+ return 0;
+
+err_dlclose:
+ dlclose(info->backend_service_handle);
+err:
+ info->backend_service = NULL;
+ info->backend_service_handle = NULL;
+
+ return ret;
+}
+
+static void __close_backend_service(struct __hal_module_info *info)
+{
+ if (!info->backend_service || !info->backend_service_handle)
+ return;
+
+ if (info->backend_service_handle)
+ dlclose(info->backend_service_handle);
+
+ info->backend_service = NULL;
+ info->backend_service_handle = NULL;
+}
+
+static int __do_backend_service_func(enum hal_module module,
+ enum hal_backend_service_func func,
+ void *user_data,
+ char *name, int name_size)
+{
+ struct __hal_module_info *info = NULL;
+ int ret = 0, ret_error = 0, len;
+
+ if (module <= HAL_MODULE_UNKNOWN || module >= HAL_MODULE_END) {
+ _E("Invalid parameter of HAL module (%d)\n", module);
+ return 0;
+ }
+
+ G_LOCK(hal_backend_service_lock);
+
+ if (_hal_api_conf_init()) {
+ ret = -ENODATA;
+ goto err_unlock;
+ }
+
+ info = _hal_api_conf_get_module_info(module, NULL);
+ if (info == NULL) {
+ _E("Failed to get HAL module(%d) information\n", module);
+ ret = -ENODATA;
+ goto err_conf_exit;
+ }
+
+ if (!info->hal_api) {
+ ret = -ENOENT;
+ goto err_conf_exit;
+ }
+
+ if (!info->backend_service_handle || !info->backend_service) {
+ _E("%s hal-backend-servicd doesn't be opended\n", info->module_name);
+ ret = -EINVAL;
+ goto err_conf_exit;
+ }
+
+ /* Return name of hal_backend_service structure */
+ switch (func) {
+ case HAL_BACKEND_SERVICE_FUNC_GET_NAME:
+ if (info->backend_service->name== 0 || !name || name_size == 0) {
+ _E("%s: Invalid size of name[] array\n", info->module_name);
+ ret = -EINVAL;
+ goto err_conf_exit;
+ }
+
+ len = strlen(info->backend_service->name);
+ if (!info->backend_service->name || (len + 1 > name_size)) {
+ _E("%s: Invalid size of name[] array\n", info->module_name);
+ ret = -EINVAL;
+ goto err_conf_exit;
+ }
+ strncpy(name, info->backend_service->name, name_size);
+ break;
+ case HAL_BACKEND_SERVICE_FUNC_INIT:
+ if (info->backend_service->early_init) {
+ ret = info->backend_service->early_init(user_data);
+ if (ret != 0) {
+ _E("Failed to early initialize %s: ret(%d)",
+ info->backend_service->name, ret);
+ goto err_conf_exit;
+ }
+ }
+
+ if (info->backend_service->init) {
+ ret = info->backend_service->init(user_data);
+ if (ret != 0) {
+ _E("Failed to initialize %s: ret(%d)",
+ info->backend_service->name, ret);
+ goto err_conf_exit;
+ }
+ }
+ break;
+ case HAL_BACKEND_SERVICE_FUNC_EXIT:
+ if (info->backend_service->exit) {
+ ret = info->backend_service->exit(user_data);
+ if (ret != 0) {
+ _W("Cannot exit %s: ret(%d)",
+ info->backend_service->name, ret);
+ ret_error = ret;
+ }
+ }
+
+ if (info->backend_service->late_exit) {
+ ret = info->backend_service->late_exit(user_data);
+ if (ret != 0) {
+ _W("Cannot late exit %s: ret(%d)",
+ info->backend_service->name, ret);
+ ret_error = ret;
+ }
+ }
+ break;
+ default:
+ _E("%s: Failed to get backend data\n", info->module_name);
+ ret = -EINVAL;
+ goto err_conf_exit;
+ }
+
+ if (ret_error != 0)
+ ret = ret_error;
+ else
+ ret = 0;
+
+err_conf_exit:
+ _hal_api_conf_exit();
+err_unlock:
+ G_UNLOCK(hal_backend_service_lock);
+ return ret;
+}
+
+static int __get_backend_service(enum hal_module module,
+ void **data, void *user_data,
+ const char *library_name)
+{
+ struct __hal_module_info *info = NULL;
+ int ret;
+
+ if (module <= HAL_MODULE_UNKNOWN || module >= HAL_MODULE_END) {
+ _E("Invalid parameter of HAL module (%d)\n", module);
+ return -EINVAL;
+ }
+
+ G_LOCK(hal_backend_service_lock);
+ if (_hal_api_conf_init()) {
+ ret = -EINVAL;
+ goto err;
+ }
+
+ info = _hal_api_conf_get_module_info(module, library_name);
+ if (info == NULL) {
+ if (!library_name)
+ _E("Failed to get HAL module(%d) information\n", module);
+ else
+ _E("Failed to get HAL module(%d) information (%s)\n",
+ module, library_name);
+ ret = -EINVAL;
+ goto err_conf_exit;
+ }
+
+ if (!info->hal_api) {
+ ret = -ENOENT;
+ goto err_conf_exit;
+ }
+
+ ret = __open_backend_service(info);
+ if (ret < 0)
+ goto err_conf_exit;
+
+ info->backend_service_usage_count++;
+
+ _I("%s: Get HAL backend Plugin: name(%s)/library(%s)/count(%d) by %s\n",
+ info->module_name, info->backend_service->name,
+ get_backend_service_library_name(info), info->backend_service_usage_count,
+ program_invocation_name);
+
+ G_UNLOCK(hal_backend_service_lock);
+ return 0;
+
+err_conf_exit:
+ _hal_api_conf_exit();
+err:
+ G_UNLOCK(hal_backend_service_lock);
+ return ret;
+}
+
+static int __put_backend_service(enum hal_module module,
+ void *data, void *user_data,
+ const char *library_name)
+{
+ struct __hal_module_info *info = NULL;
+ int ret;
+
+ /* Check parameter whether is valid or not */
+ if (module <= HAL_MODULE_UNKNOWN || module >= HAL_MODULE_END) {
+ _E("Invalid parameter of HAL module (%d)\n", module);
+ return -EINVAL;
+ }
+
+ G_LOCK(hal_backend_service_lock);
+
+ info = _hal_api_conf_get_module_info(module, library_name);
+ if (info == NULL) {
+ _E("Failed to get HAL module(%d) information\n", module);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ if (!info->hal_api) {
+ ret = 0;
+ goto out;
+ }
+
+ if (!info->handle || !info->backend) {
+ _I("%s: Has not yet dlopend backend\n", info->module_name);
+ ret = 0;
+ goto out;
+ }
+
+ if (!info->backend_service_usage_count) {
+ _I("%s: Already fully put for HAL module\n", info->module_name);
+ ret = 0;
+ goto out;
+ }
+
+ _I("%s: Prepare to exit HAL backend plugin: name(%s)/library(%s)/count(%d) by %s\n",
+ info->module_name, info->backend_service->name,
+ get_backend_service_library_name(info), info->backend_service_usage_count,
+ program_invocation_name);
+
+ info->backend_service_usage_count--;
+
+ _I("%s: Exit and Prepare to put HAL backend: name(%s)/library(%s)/count(%d) by %s\n",
+ info->module_name, info->backend_service->name,
+ get_backend_service_library_name(info), info->backend_service_usage_count,
+ program_invocation_name);
+
+ if (info->backend_service_usage_count > 0) {
+ ret = 0;
+ goto out;
+ }
+
+ __close_backend_service(info);
+
+ _I("%s: Put HAL backend: library(%s)/count(%d) by %s\n",
+ info->module_name, get_backend_service_library_name(info), info->backend_service_usage_count,
+ program_invocation_name);
+
+ _hal_api_conf_exit();
+
+ ret = 0;
+
+out:
+ G_UNLOCK(hal_backend_service_lock);
+
+ return ret;
+}
+
+EXPORT
+int hal_common_get_backend_service(enum hal_module module, void *data)
+{
+ return __get_backend_service(module, data, NULL, NULL);
+}
+
+EXPORT
+int hal_common_put_backend_service(enum hal_module module, void *data)
+{
+ return __put_backend_service(module, data, NULL, NULL);
+}
+
+EXPORT
+int hal_common_init_backend_service(enum hal_module module, void *user_data)
+{
+ return __do_backend_service_func(module, HAL_BACKEND_SERVICE_FUNC_INIT,
+ user_data, NULL, 0);
+}
+
+EXPORT
+int hal_common_exit_backend_service(enum hal_module module, void *user_data)
+{
+ return __do_backend_service_func(module, HAL_BACKEND_SERVICE_FUNC_EXIT,
+ user_data, NULL, 0);
+}
+
+static int add_hal_backend_service(enum hal_module module)
+{
+ struct hal_backend_service_data *hal_backend_service_data;
+
+ if (module <= HAL_MODULE_UNKNOWN || module >= HAL_MODULE_END) {
+ _E("Invalid parameter of HAL module (%d)\n", module);
+ return -EINVAL;
+ }
+
+ hal_backend_service_data = g_malloc(sizeof(*hal_backend_service_data));
+ if (hal_backend_service_data == NULL) {
+ _E("Failed to allocate memory for hal_backend_service");
+ return -EINVAL;
+ }
+
+ hal_backend_service_data->module = module;
+ hal_backend_service_data->thread = NULL;
+ hal_backend_service_data->is_initialized = FALSE;
+
+ g_hal_backend_service_data_list = g_slist_prepend(g_hal_backend_service_data_list,
+ (gpointer)hal_backend_service_data);
+
+ return 0;
+}
+
+static gint compare_hal_backend_service(
+ struct hal_backend_service_data *hal_backend_service_data,
+ enum hal_module module)
+{
+ return (hal_backend_service_data->module == module) ? 0 : 1;
+}
+
+static int delete_hal_backend_service(
+ enum hal_module module)
+{
+ if (module <= HAL_MODULE_UNKNOWN || module >= HAL_MODULE_END) {
+ _E("Invalid parameter of HAL module (%d)\n", module);
+ return -EINVAL;
+ }
+
+ GSList *slist_hal_backend_service = g_slist_find_custom(
+ g_hal_backend_service_data_list, (gpointer)module,
+ (GCompareFunc)compare_hal_backend_service);
+ if (slist_hal_backend_service == NULL) {
+ _E("No such hal_backend_service is registered: hal_module(%d)",
+ module);
+ return -EINVAL;
+ }
+
+ g_hal_backend_service_data_list
+ = g_slist_remove(g_hal_backend_service_data_list,
+ (gpointer)(slist_hal_backend_service->data));
+
+ return 0;
+}
+
+static int hal_backend_service_open_and_add_plugin(void)
+{
+ enum hal_module module;
+ int ret;
+
+ for (module = HAL_MODULE_UNKNOWN + 1 ; module < HAL_MODULE_END; module++) {
+ ret = hal_common_get_backend_service(module, NULL);
+ if (ret == -ENOENT)
+ continue;
+ else if (ret < 0) {
+ _W("Failed to get hal-backend-service\n");
+ continue;
+ }
+
+ ret = add_hal_backend_service(module);
+ if (ret < 0) {
+ _W("Failed to add hal-backend-service\n");
+ continue;
+ }
+ }
+
+ return 0;
+}
+
+static int hal_backend_service_delete_and_close_plugin(void)
+{
+ enum hal_module module;
+ int ret;
+
+ for (module = HAL_MODULE_END - 1 ; module > 0; module--) {
+ ret = delete_hal_backend_service(module);
+ if (ret < 0) {
+ _W("Failed to delete hal-backend-service\n");
+ continue;
+ }
+
+ ret = hal_common_put_backend_service(module, NULL);
+ if (ret < 0) {
+ _W("Failed to put hal-backend-service\n");
+ continue;
+ }
+ }
+
+ return 0;
+}
+
+static int hal_backend_service_thread_func(void *data, void **user_data)
+{
+ struct hal_backend_service_data *service_data = NULL;
+ int ret = 0;
+
+ if (data == NULL) {
+ _E("Invalid hal_backend_service(NULL)");
+ return THREAD_RETURN_ERROR;
+ }
+ service_data = (struct hal_backend_service_data *)data;
+
+ if (service_data->module <= HAL_MODULE_UNKNOWN || service_data->module >= HAL_MODULE_END) {
+ _E("Invalid parameter of HAL module (%d)\n", service_data->module);
+ return THREAD_RETURN_ERROR;
+ }
+
+ ret = hal_common_init_backend_service(service_data->module, NULL);
+ if (ret < 0) {
+ _E("Failed to initialize hal backend service: ret(%d)", ret);
+ return THREAD_RETURN_ERROR;
+ }
+
+ suspend_thread(service_data->thread);
+
+ return THREAD_RETURN_CONTINUE;
+}
+
+static int hal_backend_thread_finalize_func(void *data)
+{
+ struct hal_backend_service_data *service_data = NULL;
+ int ret = 0;
+
+ if (data == NULL) {
+ _E("Invalid hal_backend_service(NULL)");
+ return THREAD_RETURN_ERROR;
+ }
+ service_data = (struct hal_backend_service_data *)data;
+
+ if (service_data->module <= HAL_MODULE_UNKNOWN || service_data->module >= HAL_MODULE_END) {
+ _E("Invalid parameter of HAL module (%d)\n", service_data->module);
+ return THREAD_RETURN_ERROR;
+ }
+
+ ret = hal_common_exit_backend_service(service_data->module, NULL);
+ if (ret < 0) {
+ _E("Failed to exit hal backend service: ret(%d)", ret);
+ return THREAD_RETURN_ERROR;
+ }
+
+ _D("Exit done.");
+
+ return THREAD_RETURN_DONE;
+}
+
+static void create_hal_backend_service_thread(gpointer data, gpointer user_data)
+{
+ struct hal_backend_service_data *service_data;
+ size_t *failed_count;
+ struct thread *thread;
+ int ret = 0;
+
+ if (data == NULL || user_data == NULL) {
+ _E("Invalid parameter: data(%p), user_data(%p)", data, user_data);
+ return;
+ }
+
+ service_data = (struct hal_backend_service_data *)data;
+ failed_count = (size_t *)user_data;
+
+ ret = create_daemon_thread(&thread,
+ hal_backend_service_thread_func, service_data,
+ hal_backend_thread_finalize_func, service_data);
+ if (ret < 0) {
+ _E("Failed to create hal_backend_service thread: ret(%d)", ret);
+ (*failed_count)++;
+ return;
+ }
+ service_data->thread = thread;
+
+ _D("Thread created for hal_module(%d)", service_data->module);
+}
+
+static void delete_hal_backend_service_thread(gpointer data, gpointer user_data)
+{
+ struct hal_backend_service_data *service_data;
+
+ if (data == NULL) {
+ _E("Invalid parameter: data(NULL)");
+ return;
+ }
+ service_data = (struct hal_backend_service_data *)data;
+
+ destroy_thread(service_data->thread);
+ service_data->thread = NULL;
+
+ _D("Thread destroyed for hal_module(%d)", service_data->module);
+}
+
+static void hal_backend_service_start(void)
+{
+ size_t failed_count = 0;
+
+ /* Create threads for hal-backend-service and then tie them */
+ g_slist_foreach(g_hal_backend_service_data_list,
+ create_hal_backend_service_thread,
+ &failed_count);
+ if (failed_count != 0)
+ _W("Cannot register %zu service(s)", failed_count);
+}
+
+static void hal_backend_service_stop(void)
+{
+ g_slist_foreach(g_hal_backend_service_data_list,
+ delete_hal_backend_service_thread, NULL);
+}
+
+/**
+ * Signal Handler
+ */
+static gboolean handle_signal_glib(gpointer data)
+{
+ int signal_number = (int)data;
+
+ _D("Received signal(%d)", signal_number);
+ if (g_g_main_loop != NULL)
+ g_main_loop_quit(g_g_main_loop);
+
+ return G_SOURCE_REMOVE;
+}
+
+static void handle_signal_std(int signal_number)
+{
+ _W("Received signal(%d)", signal_number);
+ raise(SIGTERM);
+}
+
+static void attach_signal_handlers(void)
+{
+ g_unix_signal_add(SIGINT, handle_signal_glib, (gpointer)SIGINT);
+ g_unix_signal_add(SIGHUP, handle_signal_glib, (gpointer)SIGHUP);
+ g_unix_signal_add(SIGTERM, handle_signal_glib, (gpointer)SIGTERM);
+
+ signal(SIGQUIT, handle_signal_std);
+ signal(SIGABRT, handle_signal_std);
+}
+
+int main(int argc, char **argv)
+{
+ _D("Initialize hal-backend-service");
+
+ g_g_main_loop = g_main_loop_new(NULL, FALSE);
+
+ rpc_port_register_proc_info(RPC_STUB_PROC_NAME, NULL);
+
+ /* Load hal-backend-service plugin */
+ hal_backend_service_open_and_add_plugin();
+ hal_backend_service_start();
+
+ attach_signal_handlers();
+
+ g_main_loop_run(g_g_main_loop);
+
+ /* Un-load hal-backend-service plugin */
+ hal_backend_service_stop();
+ hal_backend_service_delete_and_close_plugin();
+
+ _D("Exit hal-backend-service");
+
+ return 0;
+}
--- /dev/null
+/*
+ * Copyright (c) 2025 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdlib.h>
+#include <errno.h>
+
+#include "thread.h"
+
+static void __thread_loop_main(void *_ctx)
+{
+ enum thread_return ret = THREAD_RETURN_DONE;
+ enum thread_return finalize_ret = THREAD_RETURN_DONE;
+ struct thread_context *ctx = _ctx;
+ void *result = NULL;
+
+ mtx_lock(&ctx->lock);
+ while (ctx->state != THREAD_STATE_TERMINATED) {
+ if (ctx->timer.tv_sec || ctx->timer.tv_nsec) {
+ mtx_unlock(&ctx->lock);
+ thrd_sleep(&ctx->timer, NULL);
+ mtx_lock(&ctx->lock);
+ }
+
+ while (ctx->state == THREAD_STATE_STOPPED)
+ cnd_wait(&ctx->wait, &ctx->lock);
+ if (ctx->state == THREAD_STATE_TERMINATED)
+ break;
+
+ mtx_unlock(&ctx->lock);
+ ret = ctx->func(ctx->func_arg, &result);
+ mtx_lock(&ctx->lock);
+
+ if (ret != THREAD_RETURN_CONTINUE) {
+ ctx->state = THREAD_STATE_TERMINATED;
+ ctx->result = result;
+ }
+ }
+ mtx_unlock(&ctx->lock);
+
+ if (ctx->finalize != NULL)
+ finalize_ret = ctx->finalize(ctx->finalize_arg);
+
+ if (finalize_ret != THREAD_RETURN_DONE)
+ ret = finalize_ret;
+
+ thrd_exit(ret);
+}
+
+static void do_destroy_thread(struct thread *thread)
+{
+ struct thread_context *ctx = thread->ctx;
+
+ mtx_lock(&ctx->lock);
+ ctx->state = THREAD_STATE_TERMINATED;
+ mtx_unlock(&ctx->lock);
+
+ /* try to wake up thread whether it is sleeping or not */
+ cnd_signal(&ctx->wait);
+
+ thrd_join(thread->id, NULL);
+
+ cnd_destroy(&ctx->wait);
+ mtx_destroy(&ctx->lock);
+}
+
+void destroy_thread(struct thread *thread)
+{
+ do_destroy_thread(thread);
+ free(thread->ctx);
+ thread->ctx = NULL;
+
+ free(thread);
+}
+
+static int do_create_thread(struct thread **thread, enum thread_type type,
+ u_int32_t timer_expire_msec,
+ int (*func)(void *, void **), void *func_arg,
+ int (*finalize)(void *), void *finalize_arg)
+{
+ struct thread *new_thread;
+ struct thread_context *ctx;
+ thrd_t tid;
+ int ret;
+
+ if (!thread || !func)
+ return -EINVAL;
+
+ new_thread = malloc(sizeof(struct thread));
+ if (!new_thread)
+ return -ENOMEM;
+
+ ctx = calloc(1, sizeof(struct thread_context));
+ if (!ctx) {
+ ret = -ENOMEM;
+ goto err_malloc;
+ }
+
+ mtx_init(&ctx->lock, mtx_plain);
+ cnd_init(&ctx->wait);
+
+ ctx->func = func;
+ ctx->func_arg = func_arg;
+ ctx->finalize = finalize;
+ ctx->finalize_arg = finalize_arg;
+
+ mtx_lock(&ctx->lock);
+ switch (type) {
+ case THREAD_TYPE_WORKER:
+ ctx->state = THREAD_STATE_STOPPED;
+ break;
+ case THREAD_TYPE_DAEMON:
+ ctx->state = THREAD_STATE_RUNNING;
+ break;
+ case THREAD_TYPE_TIMER:
+ ctx->state = THREAD_STATE_RUNNING;
+ ctx->timer = (struct timespec) {
+ .tv_sec = timer_expire_msec / 1000,
+ .tv_nsec = (timer_expire_msec % 1000) * 1000000,
+ };
+ break;
+ default:
+ ret = -EINVAL;
+ goto err;
+ }
+
+ ret = thrd_create(&tid, (thrd_start_t) __thread_loop_main, (void *)ctx);
+ if (ret == thrd_error) {
+ ret = -EINVAL;
+ goto err;
+ }
+
+ new_thread->id = tid;
+ new_thread->ctx = ctx;
+
+ *thread = new_thread;
+ mtx_unlock(&ctx->lock);
+
+ return 0;
+
+err:
+ mtx_unlock(&ctx->lock);
+ cnd_destroy(&ctx->wait);
+ mtx_destroy(&ctx->lock);
+ free(ctx);
+err_malloc:
+ free(new_thread);
+
+ return ret;
+}
+
+int create_daemon_thread(struct thread **thread,
+ int (*func)(void *, void **), void *func_arg,
+ int (*finalize)(void *), void *finalize_arg)
+{
+ return do_create_thread(thread, THREAD_TYPE_DAEMON, 0, func, func_arg,
+ finalize, finalize_arg);
+}
+
+int create_timer_thread(struct thread **thread, u_int32_t timer_expire_msec,
+ int (*func)(void *, void **), void *func_arg,
+ int (*finalize)(void *), void *finalize_arg)
+{
+ return do_create_thread(thread, THREAD_TYPE_TIMER, timer_expire_msec,
+ func, func_arg, finalize, finalize_arg);
+}
+
+int create_worker_thread(struct thread **thread,
+ int (*func)(void *, void **), void *arg,
+ int (*finalize)(void *), void *finalize_arg)
+{
+ return do_create_thread(thread, THREAD_TYPE_WORKER, 0, func, arg,
+ finalize, finalize_arg);
+}
+
+void suspend_thread(struct thread *thread)
+{
+ struct thread_context *ctx = thread->ctx;
+
+ mtx_lock(&ctx->lock);
+ ctx->state = THREAD_STATE_STOPPED;
+ mtx_unlock(&ctx->lock);
+}
+
+void resume_thread(struct thread *thread)
+{
+ struct thread_context *ctx = thread->ctx;
+
+ mtx_lock(&ctx->lock);
+ ctx->state = THREAD_STATE_RUNNING;
+ mtx_unlock(&ctx->lock);
+ cnd_signal(&ctx->wait);
+}
+
+int wait_for_completion(struct thread *thread, void **result)
+{
+ struct thread_context *ctx = thread->ctx;
+ int ret;
+
+ thrd_join(thread->id, &ret);
+ if (ret == THREAD_RETURN_ERROR)
+ return ret;
+
+ if (result)
+ *result = ctx->result;
+
+ cnd_destroy(&ctx->wait);
+ mtx_destroy(&ctx->lock);
+
+ free(thread->ctx);
+ thread->ctx = NULL;
+
+ free(thread);
+
+ return 0;
+}