--- /dev/null
+/*
+ * PASS
+ *
+ * Copyright (c) 2022 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 <threads.h>
+#include <time.h>
+
+enum thread_type {
+ THREAD_TYPE_WORKER,
+ THREAD_TYPE_DAEMON,
+ THREAD_TYPE_TIMER,
+};
+
+enum thread_state {
+ THREAD_STATE_RUNNING,
+ THREAD_STATE_STOPPED,
+ THREAD_STATE_TERMINATED,
+};
+
+enum thread_return {
+ THREAD_RETURN_ERROR = -EXIT_FAILURE,
+ THREAD_RETURN_DONE = EXIT_SUCCESS,
+ THREAD_RETURN_CONTINUE,
+};
+
+struct thread_context {
+ enum thread_state state;
+ struct timespec timer;
+ int (*func)(void *arg, void **result);
+ void *arg;
+ void *result;
+ mtx_t lock;
+ cnd_t wait;
+};
+
+struct thread {
+ thrd_t id;
+ struct thread_context *ctx;
+};
+
+int create_daemon_thread(struct thread *thread, int (*func)(void *, void **), void *arg);
+int create_timer_thread(struct thread *thread, u_int32_t timer_expire_msec,
+ int (*func)(void *, void **), void *arg);
+int create_worker_thread(struct thread *thread, int (*func)(void *, void **), void *arg);
+void destroy_thread(struct thread *thread);
+void suspend_thread(struct thread *thread);
+void resume_thread(struct thread *thread);
+
+#define schedule_worker resume_thread
+int wait_for_completion(struct thread *thread, void **result);
--- /dev/null
+/*
+ * PASS
+ *
+ * Copyright (c) 2022 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 <util/thread.h>
+#include <util/log.h>
+
+static void __thread_loop_main(void *_ctx)
+{
+ enum thread_return ret = THREAD_RETURN_DONE;
+ struct thread_context *ctx = _ctx;
+ void *result;
+
+ while (ctx->state != THREAD_STATE_TERMINATED) {
+ if (ctx->timer.tv_sec || ctx->timer.tv_nsec)
+ 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;
+
+ ret = ctx->func(ctx->arg, &result);
+
+ if (ret != THREAD_RETURN_CONTINUE) {
+ ctx->state = THREAD_STATE_TERMINATED;
+ ctx->result = result;
+ }
+ mtx_unlock(&ctx->lock);
+ }
+
+ 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;
+}
+
+static int do_create_thread(struct thread *thread,
+ enum thread_type type,
+ u_int32_t timer_expire_msec,
+ int (*func)(void *, void **), void *arg)
+{
+ struct thread_context *ctx;
+ thrd_t tid;
+ int ret;
+
+ if (!thread || !func)
+ return -EINVAL;
+
+ ctx = calloc(1, sizeof(struct thread_context));
+ if (!ctx)
+ return -ENOMEM;
+
+ mtx_init(&ctx->lock, mtx_plain);
+ cnd_init(&ctx->wait);
+ ctx->func = func;
+ ctx->arg = arg;
+
+ 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;
+ }
+
+ thread->id = tid;
+ thread->ctx = ctx;
+
+ return 0;
+
+err:
+ cnd_destroy(&ctx->wait);
+ mtx_destroy(&ctx->lock);
+ free(ctx);
+
+ return ret;
+}
+
+int create_daemon_thread(struct thread *thread,
+ int (*func)(void *, void **), void *arg)
+{
+ return do_create_thread(thread, THREAD_TYPE_DAEMON, 0, func, arg);
+}
+
+int create_timer_thread(struct thread *thread,
+ u_int32_t timer_expire_msec,
+ int (*func)(void *, void **), void *arg)
+{
+ return do_create_thread(thread, THREAD_TYPE_TIMER,
+ timer_expire_msec, func, arg);
+}
+
+int create_worker_thread(struct thread *thread,
+ int (*func)(void *, void **), void *arg)
+{
+ return do_create_thread(thread, THREAD_TYPE_WORKER, 0, func, 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;
+
+ *result = ctx->result;
+
+ cnd_destroy(&ctx->wait);
+ mtx_destroy(&ctx->lock);
+
+ free(thread->ctx);
+ thread->ctx = NULL;
+
+ return 0;
+}