/*
 * Copyright (c) 1994, 2021 Alex Leigh, All Rights Reserved
 *
 * THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF ALEX LEIGH The copyright
 * notice above does not evidence any actual or intended publication of such
 * source code.
 *
 * Disclosure, copying, distribution, or transmission of this code without
 * express permission is prohibited.
 */

#include <mecha.h>
#include <time.h>
#include <assert.h>
#include "thr_posix.h"

/**
 * Attempt to sleep the calling thread for at least the number of milliseconds indicated. Upon return there is
 * no guarantee with this implementation that the thread actually slept for that amount.
 */
void mFthr_sleep(int ms) {
    struct timespec tim, tim2;
    tim.tv_sec = ms / 1000;
    tim.tv_nsec = (ms % 1000) * 1000000;
    nanosleep(&tim, &tim2);
}

/**
 * Create a new thread of execution.
 *
 * Returns MECHA_OK on success, otherwise, returns MECHA_ERR.
 */
int mFthr_create(struct mSthr *thread, void (*func)(void *)) {
    // This is OK to cast, because we never process the return value.
    return pthread_create(&thread->tid, NULL, (void *(*)(void *)) func, NULL);
}

/**
 * Free resources associated with the given thread.
 *
 * Calling this function will not terminate the thread, if it is running.
 */
void mFthr_free(__attribute__((unused)) struct mSthr *thread) {
    // NOP
}

/**
 * Wait for the given thread to terminate.
 *
 * Returns MECHA_OK on success, otherwise, returns MECHA_ERR. If MECHA_ERR is returned, the state of the joined
 * thread is indeterminate.
 *
 * @param thread
 * @return MECHA_OK on success, MECHA_ERR on error.
 */
int mFthr_join(struct mSthr *thread) {
    assert(thread != NULL);

    if (pthread_join(thread->tid, NULL) == 0) {
        return MECHA_OK;
    } else {
        return MECHA_ERR;
    }
}

int mFthr_mutex_init(struct mSthr_mutex *mutex) {
    assert(mutex != NULL);

    if (pthread_mutex_init(&mutex->mutex, NULL) == 0) {
        return MECHA_OK;
    } else {
        return MECHA_ERR;
    }
}

int mFthr_mutex_lock(struct mSthr_mutex *mutex) {
    assert(mutex != NULL);

    if (pthread_mutex_lock(&mutex->mutex) != 0) {
        return MECHA_ERR;
    }

    return MECHA_OK;
}

int mFthr_mutex_unlock(struct mSthr_mutex *mutex) {
    assert(mutex != NULL);

    if (pthread_mutex_unlock(&mutex->mutex) != 0) {
        return MECHA_ERR;
    }

    return MECHA_OK;
}

void mFthr_mutex_free(struct mSthr_mutex *mutex) {
    pthread_mutex_destroy(&mutex->mutex);
}