util: Add common thread utility 70/270170/3
authorDongwoo Lee <dwoo08.lee@samsung.com>
Mon, 24 Jan 2022 12:35:42 +0000 (21:35 +0900)
committerDongwoo Lee <dwoo08.lee@samsung.com>
Wed, 26 Jan 2022 09:28:12 +0000 (18:28 +0900)
To support multi-thread by common utility, now three types of threads
are provided as like 'daemon', 'worker', and 'timer'.

Change-Id: Ie2ac65187cab0ce9ec41a5610fc0b99d08f9e42a
Signed-off-by: Dongwoo Lee <dwoo08.lee@samsung.com>
CMakeLists.txt
include/util/thread.h [new file with mode: 0644]
src/util/thread.c [new file with mode: 0644]

index cbef703..0d8de97 100644 (file)
@@ -30,6 +30,7 @@ SET(SRCS
        src/util/resource.c
        src/util/gdbus-util.c
        src/util/timer.c
+       src/util/thread.c
        src/main.c
        #Generated by a custom command 'gdbus-codegen' below
        src/pass/pass-dbus-stub.c
diff --git a/include/util/thread.h b/include/util/thread.h
new file mode 100644 (file)
index 0000000..0f13200
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * 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);
diff --git a/src/util/thread.c b/src/util/thread.c
new file mode 100644 (file)
index 0000000..50b1bc0
--- /dev/null
@@ -0,0 +1,193 @@
+/*
+ * 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;
+}