Newer
Older
libkc / modules / src / kc_threads_win.c
/**
 * @file  kc_thread.c
 * @brief スレッドモジュール
 * @copyright  2020 - 2024  Nomura Kei
 */
#include <stdio.h>

#include <kc_threads.h>
#include <kc_threads_win.h>

#if (KC_IS_WINDOWS)

int thrd_create(thrd_t *thr, thrd_start_t func, void *arg)
{
    if ((thr == NULL) || (func == NULL))
    {
        return thrd_error;
    }

    thr->handle = CreateThread(
        NULL,                         // セキュリティ属性
        0,                            // スタックサイズ
        (LPTHREAD_START_ROUTINE)func, // スレッド関数
        arg,                          // 引数
        0,                            // 作成フラグ
        &(thr->thread_id)             // スレッドID
    );

    if (thr->handle == NULL)
    {
        return thrd_error;
    }
    return thrd_success;
}
int thrd_join(thrd_t thr, int *res)
{
    if (WaitForSingleObject(thr.handle, INFINITE) != WAIT_OBJECT_0)
    {
        return thrd_error;
    }
    if (res != NULL)
    {
        DWORD retcode;
        if (GetExitCodeThread(thr.handle, &retcode) == 0)
        {
            return thrd_error;
        }
        *res = (int)retcode;
    }
    CloseHandle(thr.handle);
    return thrd_success;
}

int thrd_detach(thrd_t thr)
{
    if (thr.handle == NULL)
    {
        return thrd_error;
    }
    if (CloseHandle(thr.handle) == 0)
    {
        return thrd_error;
    }
    return thrd_success;
}

thrd_t thrd_current(void)
{
    thrd_t current;
    current.handle = GetCurrentThread();
    current.thread_id = GetThreadId(current.handle);
    return current;
}

int thrd_equal(thrd_t lhs, thrd_t rhs)
{
    return (lhs.thread_id == rhs.thread_id);
}

void thrd_yield(void)
{
    SwitchToThread();
}

int thrd_sleep(const struct timespec *duration, struct timespec *remaining)
{
    if (duration == NULL)
    {
        return thrd_error;
    }

    // Windows の Sleep は、ms 単位
    DWORD msec = (DWORD)(duration->tv_sec * 1000 + duration->tv_nsec / 1000000);
    Sleep(msec);

    // 常に成功したものとして、remaining は 0 に設定する。
    if (remaining != NULL)
    {
        remaining->tv_sec = 0;
        remaining->tv_nsec = 0;
    }
    return thrd_success;
}

/**
 * 指定されたミューテックスを初期化します。
 * Windows の場合、常に mtx_recursive が有効となります。
 *
 * @param mtx 初期化するミューテックスの識別子
 * @param type タイプ (mtx_plain, mtx_timed, mtx_plain|mtx_recursive, mtx_timed|mtx_recursive)
 * @return thrd_success/thrd_error (成功/失敗)
 */
int mtx_init(mtx_t *mtx, int type)
{
    if (mtx == NULL)
    {
        return thrd_error;
    }
    mtx->type = type;
    InitializeCriticalSection(&mtx->cs);
    return thrd_success;
}

/**
 * ミューテックスを破棄します。
 *
 * @param mtx 破棄するミューテックス識別子
 */
void mtx_destroy(mtx_t *mtx)
{
    printf("mtx_destroy: mtx->cs:%p\n", &mtx->cs);
    DeleteCriticalSection(&mtx->cs);
}

/**
 * 指定されたミューテックスがロックされるまで現在のスレッドをロックします。
 *
 * @param mtx ミューテックス識別子
 * @return thrd_success/thrd_error (成功/失敗)
 */
int mtx_lock(mtx_t *mtx)
{
    EnterCriticalSection(&mtx->cs);
    return thrd_success;
}

/**
 *
 */
int mtx_unlock(mtx_t *mtx)
{
    LeaveCriticalSection(&mtx->cs);
    return thrd_success;
}

int cnd_init(cnd_t *cond)
{
    if (cond == NULL)
    {
        return thrd_error;
    }
    InitializeConditionVariable(&cond->cond);
    return thrd_success;
}

int cnd_signal(cnd_t *cond)
{
    if (cond == NULL)
    {
        return thrd_error;
    }
    WakeConditionVariable(&cond->cond);
    return thrd_success;
}
int cnd_broadcast(cnd_t *cond)
{
    if (cond == NULL)
    {
        return thrd_error;
    }
    WakeAllConditionVariable(&cond->cond);
    return thrd_success;
}
int cnd_wait(cnd_t *cond, mtx_t *mtx)
{
    if ((cond == NULL) || (mtx == NULL))
    {
        return thrd_error;
    }
    if (SleepConditionVariableCS(&cond->cond, &mtx->cs, INFINITE))
    {
        return thrd_success;
    }
    return thrd_error;
}

int cnd_timedwait(cnd_t *cond, mtx_t *mtx, const struct timespec *ts)
{
    if ((cond == NULL) || (mtx == NULL) || (ts == NULL))
    {
        return thrd_error;
    }
    DWORD msec = (DWORD)(ts->tv_sec * 1000 + ts->tv_nsec / 1000000);
    if (SleepConditionVariableCS(&cond->cond, &mtx->cs, msec))
    {
        return thrd_timedout;
    }
    return thrd_error;
}

void cnd_destroy(cnd_t *cond)
{
    // Nothing to do
    UNUSED_VARIABLE(cond);
}

#endif