/**
* @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;
}