Newer
Older
libkc / modules / src / kc_threads.c
Nomura Kei on 29 May 5 KB update
/**
 * @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;
}