/** * @file kc_thread.c * @brief スレッドモジュール * @copyright 2020 - 2024 Nomura Kei */ #include <string.h> #include <errno.h> #include <stdatomic.h> #include <kc.h> #include <kc_memory.h> #include <kc_threads.h> /** * KcThread 管理情報 */ typedef struct { thrd_t tid; void *args; int (*run)(void *args); atomic_bool alive_state; } KcThreadInfo; // ============================================================================= // プロトタイプ宣言 // ============================================================================= static bool KcThread_is_alive(KcThread *thread); static bool KcThread_start(KcThread *thread, void *args); static bool KcThread_join(KcThread *thread); static int KcThread_run(void *thread); // ============================================================================= // new // ============================================================================= /** * スレッドを生成します。 * * @param run スレッドにて実行する関数へのポインタ */ KcThread *KcThread_new(int (*run)(void *args)) { // KcThread の管理構造 // +--------------+ // | KcThread | // | ... | // | _info -----------+ // +--------------+ | // | <_info> | <---+ // +--------------+ KcThread *thread = (KcThread *)malloc(sizeof(KcThread) + sizeof(KcThreadInfo)); if (thread != NULL) { thread->is_alive = KcThread_is_alive; thread->start = KcThread_start; thread->join = KcThread_join; thread->_info = (thread + 1); KcThreadInfo *info = (KcThreadInfo *)thread->_info; // info->tid info->run = run; atomic_init(&info->alive_state, false); } return thread; } // ============================================================================= // delete // ============================================================================= /** * Thread を破棄します。 * * @param thread 破棄するスレッド */ void KcThread_delete(KcThread *thread) { free(thread); } // ============================================================================= // sleep // ============================================================================= /** * 指定された ms 間現在のスレッドをスリープさせます。 * * @param time スリープさせるミリ秒数 * @param force true の場合、シグナルにより割り込まれた場合でも継続してスリープします。 * @return true/false (成功/中断された) */ bool KcThread_msleep(long time, bool force) { long sec = time / 1000; long msec = time - (sec * 1000); long nsec = msec * 1000 * 1000; return KcThread_sleep(sec, nsec, force); } /** * 指定された sec 秒 + nsec ナノ秒間現在のスレッドをスリープさせます。 * * @param sec スリープさせる秒数 * @param nsec スリープさせるナノ秒数 * @param force true の場合、シグナルにより割り込まれた場合でも継続してスリープします。 * @return true/false (成功/中断された) */ bool KcThread_sleep(long sec, long nsec, bool force) { struct timespec remaining = {sec, nsec}; int ret; do { errno = 0; ret = thrd_sleep(&remaining, &remaining); } while ((ret == -1) && force && (errno == EINTR)); return (ret == 0); } // ============================================================================= // is_alive // ============================================================================= /** * 指定されたスレッドが生きているか否かを返します。 * * @param thread 対象スレッド * @return true/false (スレッドが生存している/していない) */ static bool KcThread_is_alive(KcThread *thread) { KcThreadInfo *info = (KcThreadInfo *)thread->_info; return atomic_load(&info->alive_state); } // ============================================================================= // start // ============================================================================= /** * スレッドを開始します。 * * @param thread 対象スレッド * @param args スレッドに渡す情報 * @return true/false (スレッド起動成功/失敗) */ static bool KcThread_start(KcThread *thread, void *args) { int ret = thrd_error; KcThreadInfo *info = (KcThreadInfo *)thread->_info; if (!atomic_load(&info->alive_state)) { info->args = args; ret = thrd_create(&info->tid, KcThread_run, thread); } return (ret == thrd_success); } // ============================================================================= // join // ============================================================================= /** * スレッドが終了するのを待機します。 * * @param thread 対象スレッド * @return true/false (成功/失敗) */ static bool KcThread_join(KcThread *thread) { int ret = thrd_error; KcThreadInfo *info = (KcThreadInfo *)thread->_info; ret = thrd_join(info->tid, NULL); return (ret == thrd_success); } //////////////////////////////////////////////////////////////////////////////// // // 内部関数 // // ============================================================================= // run // ============================================================================= /** * スレッドで実行される関数。 * start にて関数を別スレッドにて実行します。 * * @param args スレッド情報 * @return */ int KcThread_run(void *args) { KcThread *thread = (KcThread *)args; KcThreadInfo *info = (KcThreadInfo *)thread->_info; atomic_store(&info->alive_state, true); int ret = info->run(info->args); atomic_store(&info->alive_state, false); return ret; }