/*
 * Copyright (c) 2013-2019 Huawei Technologies Co., Ltd. All rights reserved.
 * Copyright (c) 2020-2021 Huawei Device Co., Ltd. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this list of
 *    conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright notice, this list
 *    of conditions and the following disclaimer in the documentation and/or other materials
 *    provided with the distribution.
 *
 * 3. Neither the name of the copyright holder nor the names of its contributors may be used
 *    to endorse or promote products derived from this software without specific prior written
 *    permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "pprivate.h"
#include "pthread.h"
#include "sched.h"

#include "stdio.h"
#include "map_error.h"
#include "los_process_pri.h"
#include "los_sched_pri.h"


/*
 * Array of pthread control structures. A pthread_t object is
 * "just" an index into this array.
 */
STATIC _pthread_data g_pthreadData[LOSCFG_BASE_CORE_TSK_LIMIT + 1];

/* Count of number of threads that have exited and not been reaped. */
STATIC INT32 g_pthreadsExited = 0;

/* this is to protect the pthread data */
STATIC pthread_mutex_t g_pthreadsDataMutex = PTHREAD_MUTEX_INITIALIZER;

/* pointed to by PTHREAD_CANCELED */
UINTPTR g_pthreadCanceledDummyVar;

/*
 * Private version of pthread_self() that returns a pointer to our internal
 * control structure.
 */
_pthread_data *pthread_get_self_data(void)
{
    UINT32 runningTaskPID = ((LosTaskCB *)(OsCurrTaskGet()))->taskID;
    _pthread_data *data = &g_pthreadData[runningTaskPID];

    return data;
}

_pthread_data *pthread_get_data(pthread_t id)
{
    _pthread_data *data = NULL;

    if (OS_TID_CHECK_INVALID(id)) {
        return NULL;
    }

    data = &g_pthreadData[id];
    /* Check that this is a valid entry */
    if ((data->state == PTHREAD_STATE_FREE) || (data->state == PTHREAD_STATE_EXITED)) {
        return NULL;
    }

    /* Check that the entry matches the id */
    if (data->id != id) {
        return NULL;
    }

    /* Return the pointer */
    return data;
}

/*
 * Check whether there is a cancel pending and if so, whether
 * cancellations are enabled. We do it in this order to reduce the
 * number of tests in the common case - when no cancellations are
 * pending. We make this inline so it can be called directly below for speed
 */
STATIC INT32 CheckForCancel(VOID)
{
    _pthread_data *self = pthread_get_self_data();
    if (self->canceled && (self->cancelstate == PTHREAD_CANCEL_ENABLE)) {
        return 1;
    }
    return 0;
}

STATIC VOID ProcessUnusedStatusTask(_pthread_data *data)
{
    data->state = PTHREAD_STATE_FREE;
    (VOID)memset_s(data, sizeof(_pthread_data), 0, sizeof(_pthread_data));
}

/*
 * This function is called to tidy up and dispose of any threads that have
 * exited. This work must be done from a thread other than the one exiting.
 * Note: this function must be called with pthread_mutex locked.
 */
STATIC VOID PthreadReap(VOID)
{
    UINT32 i;
    _pthread_data *data = NULL;
    /*
     * Loop over the thread table looking for exited threads. The
     * g_pthreadsExited counter springs us out of this once we have
     * found them all (and keeps us out if there are none to do).
     */
    for (i = 0; g_pthreadsExited && (i < g_taskMaxNum); i++) {
        data = &g_pthreadData[i];
        if (data->state == PTHREAD_STATE_EXITED) {
            /* the Huawei LiteOS not delete the dead TCB,so need to delete the TCB */
            (VOID)LOS_TaskDelete(data->task->taskID);
            if (data->task->taskStatus & OS_TASK_STATUS_UNUSED) {
                ProcessUnusedStatusTask(data);
                g_pthreadsExited--;
            }
        }
    }
}

STATIC VOID SetPthreadAttr(const _pthread_data *self, const pthread_attr_t *attr, pthread_attr_t *outAttr)
{
    /*
     * Set use_attr to the set of attributes we are going to
     * actually use. Either those passed in, or the default set.
     */
    if (attr == NULL) {
        (VOID)pthread_attr_init(outAttr);
    } else {
        (VOID)memcpy_s(outAttr, sizeof(pthread_attr_t), attr, sizeof(pthread_attr_t));
    }

    /*
     * If the stack size is not valid, we can assume that it is at
     * least PTHREAD_STACK_MIN bytes.
     */
    if (!outAttr->stacksize_set) {
        outAttr->stacksize = LOSCFG_BASE_CORE_TSK_DEFAULT_STACK_SIZE;
    }
    if (outAttr->inheritsched == PTHREAD_INHERIT_SCHED) {
        if (self->task == NULL) {
            outAttr->schedparam.sched_priority = ((LosTaskCB *)(OsCurrTaskGet()))->priority;
        } else {
            outAttr->schedpolicy = self->attr.schedpolicy;
            outAttr->schedparam  = self->attr.schedparam;
            outAttr->scope       = self->attr.scope;
        }
    }
}

STATIC VOID SetPthreadDataAttr(const pthread_attr_t *userAttr, const pthread_t threadID,
                               LosTaskCB *taskCB, _pthread_data *created)
{
    created->attr         = *userAttr;
    created->id           = threadID;
    created->task         = taskCB;
    created->state        = (userAttr->detachstate == PTHREAD_CREATE_JOINABLE) ?
                            PTHREAD_STATE_RUNNING : PTHREAD_STATE_DETACHED;
    /* need to confirmation */
    created->cancelstate  = PTHREAD_CANCEL_ENABLE;
    created->canceltype   = PTHREAD_CANCEL_DEFERRED;
    created->cancelbuffer = NULL;
    created->canceled     = 0;
    created->freestack    = 0; /* no use default : 0 */
    created->stackmem     = taskCB->topOfStack;
    created->thread_data  = NULL;
}

STATIC UINT32 InitPthreadData(pthread_t threadID, pthread_attr_t *userAttr,
                              const CHAR name[], size_t len)
{
    errno_t err;
    UINT32 ret = LOS_OK;
    LosTaskCB *taskCB = OS_TCB_FROM_TID(threadID);
    _pthread_data *created = &g_pthreadData[threadID];

    err = strncpy_s(created->name, sizeof(created->name), name, len);
    if (err != EOK) {
        PRINT_ERR("%s: %d, err: %d\n", __FUNCTION__, __LINE__, err);
        return LOS_NOK;
    }
    userAttr->stacksize   = taskCB->stackSize;
    err = OsSetTaskName(taskCB, created->name, FALSE);
    if (err != LOS_OK) {
        PRINT_ERR("%s: %d, err: %d\n", __FUNCTION__, __LINE__, err);
        return LOS_NOK;
    }
#ifdef LOSCFG_KERNEL_SMP
    if (userAttr->cpuset.__bits[0] > 0) {
        taskCB->cpuAffiMask = (UINT16)userAttr->cpuset.__bits[0];
    }
#endif

    SetPthreadDataAttr(userAttr, threadID, taskCB, created);
    return ret;
}

int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
                   void *(*startRoutine)(void *), void *arg)
{
    pthread_attr_t userAttr;
    UINT32 ret;
    CHAR name[PTHREAD_DATA_NAME_MAX] = {0};
    STATIC UINT16 pthreadNumber = 1;
    TSK_INIT_PARAM_S taskInitParam = {0};
    UINT32 taskHandle;
    _pthread_data *self = pthread_get_self_data();

    if ((thread == NULL) || (startRoutine == NULL)) {
        return EINVAL;
    }

    SetPthreadAttr(self, attr, &userAttr);

    (VOID)snprintf_s(name, sizeof(name), sizeof(name) - 1, "pth%02d", pthreadNumber);
    pthreadNumber++;

    taskInitParam.pcName       = name;
    taskInitParam.pfnTaskEntry = (TSK_ENTRY_FUNC)startRoutine;
    taskInitParam.auwArgs[0]   = (UINTPTR)arg;
    taskInitParam.usTaskPrio   = (UINT16)userAttr.schedparam.sched_priority;
    taskInitParam.uwStackSize  = userAttr.stacksize;
    if (OsProcessIsUserMode(OsCurrProcessGet())) {
        taskInitParam.processID = OsGetKernelInitProcessID();
    } else {
        taskInitParam.processID = OsCurrProcessGet()->processID;
    }
    if (userAttr.detachstate == PTHREAD_CREATE_DETACHED) {
        taskInitParam.uwResved = LOS_TASK_STATUS_DETACHED;
    } else {
        /* Set the pthread default joinable */
        taskInitParam.uwResved = LOS_TASK_ATTR_JOINABLE;
    }

    PthreadReap();
    ret = LOS_TaskCreateOnly(&taskHandle, &taskInitParam);
    if (ret == LOS_OK) {
        *thread = (pthread_t)taskHandle;
        ret = InitPthreadData(*thread, &userAttr, name, PTHREAD_DATA_NAME_MAX);
        if (ret != LOS_OK) {
            goto ERROR_OUT_WITH_TASK;
        }
        (VOID)LOS_SetTaskScheduler(taskHandle, SCHED_RR, taskInitParam.usTaskPrio);
    }

    if (ret == LOS_OK) {
        return ENOERR;
    } else {
        goto ERROR_OUT;
    }

ERROR_OUT_WITH_TASK:
    (VOID)LOS_TaskDelete(taskHandle);
ERROR_OUT:
    *thread = (pthread_t)-1;

    return map_errno(ret);
}

void pthread_exit(void *retVal)
{
    _pthread_data *self = pthread_get_self_data();
    UINT32 intSave;

    if (pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, (int *)0) != ENOERR) {
        PRINT_ERR("%s: %d failed\n", __FUNCTION__, __LINE__);
    }

    if (pthread_mutex_lock(&g_pthreadsDataMutex) != ENOERR) {
        PRINT_ERR("%s: %d failed\n", __FUNCTION__, __LINE__);
    }

    self->task->joinRetval = retVal;
    /*
     * If we are already detached, go to EXITED state, otherwise
     * go into JOIN state.
     */
    if (self->state == PTHREAD_STATE_DETACHED) {
        self->state = PTHREAD_STATE_EXITED;
        g_pthreadsExited++;
    } else {
        self->state = PTHREAD_STATE_JOIN;
    }

    if (pthread_mutex_unlock(&g_pthreadsDataMutex) != ENOERR) {
        PRINT_ERR("%s: %d failed\n", __FUNCTION__, __LINE__);
    }
    SCHEDULER_LOCK(intSave);
    /* If the thread is the highest thread,it can't schedule in LOS_SemPost. */
    OsTaskJoinPostUnsafe(self->task);
    if (self->task->taskStatus & OS_TASK_STATUS_RUNNING) {
        OsSchedResched();
    }
    SCHEDULER_UNLOCK(intSave);
}

STATIC INT32 ProcessByJoinState(_pthread_data *joined)
{
    UINT32 intSave;
    INT32 err = 0;
    UINT32 ret;
    switch (joined->state) {
        case PTHREAD_STATE_RUNNING:
            /* The thread is still running, we must wait for it. */
            SCHEDULER_LOCK(intSave);
            ret = OsTaskJoinPendUnsafe(joined->task);
            SCHEDULER_UNLOCK(intSave);
            if (ret != LOS_OK) {
                err = (INT32)ret;
                break;
            }

            joined->state = PTHREAD_STATE_ALRDY_JOIN;
            break;
           /*
            * The thread has become unjoinable while we waited, so we
            * fall through to complain.
            */
        case PTHREAD_STATE_FREE:
        case PTHREAD_STATE_DETACHED:
        case PTHREAD_STATE_EXITED:
            /* None of these may be joined. */
            err = EINVAL;
            break;
        case PTHREAD_STATE_ALRDY_JOIN:
            err = EINVAL;
            break;
        case PTHREAD_STATE_JOIN:
            break;
        default:
            PRINT_ERR("state: %u is not supported\n", (UINT32)joined->state);
            break;
    }
    return err;
}

int pthread_join(pthread_t thread, void **retVal)
{
    INT32 err;
    UINT8 status;
    _pthread_data *self = NULL;
    _pthread_data *joined = NULL;

    /* Check for cancellation first. */
    pthread_testcancel();

    /* Dispose of any dead threads */
    (VOID)pthread_mutex_lock(&g_pthreadsDataMutex);
    PthreadReap();
    (VOID)pthread_mutex_unlock(&g_pthreadsDataMutex);

    self   = pthread_get_self_data();
    joined = pthread_get_data(thread);
    if (joined == NULL) {
        return ESRCH;
    }
    status = joined->state;

    if (joined == self) {
        return EDEADLK;
    }

    err = ProcessByJoinState(joined);
    (VOID)pthread_mutex_lock(&g_pthreadsDataMutex);

    if (!err) {
        /*
         * Here, we know that joinee is a thread that has exited and is
         * ready to be joined.
         */
        if (retVal != NULL) {
            /* Get the retVal */
            *retVal = joined->task->joinRetval;
        }

        /* Set state to exited. */
        joined->state = PTHREAD_STATE_EXITED;
        g_pthreadsExited++;

        /* Dispose of any dead threads */
        PthreadReap();
    } else {
        joined->state = status;
    }

    (VOID)pthread_mutex_unlock(&g_pthreadsDataMutex);
    /* Check for cancellation before returning */
    pthread_testcancel();

    return err;
}

/*
 * Set the detachstate of the thread to "detached". The thread then does not
 * need to be joined and its resources will be freed when it exits.
 */
int pthread_detach(pthread_t thread)
{
    int ret = 0;
    UINT32 intSave;

    _pthread_data *detached = NULL;

    if (pthread_mutex_lock(&g_pthreadsDataMutex) != ENOERR) {
        ret = ESRCH;
    }
    detached = pthread_get_data(thread);
    if (detached == NULL) {
        ret = ESRCH; /* No such thread */
    } else if (detached->state == PTHREAD_STATE_DETACHED) {
        ret = EINVAL; /* Already detached! */
    } else if (detached->state == PTHREAD_STATE_JOIN) {
        detached->state = PTHREAD_STATE_EXITED;
        g_pthreadsExited++;
    } else {
        /* Set state to detached and kick any joinees to make them return. */
        SCHEDULER_LOCK(intSave);
        if (!(detached->task->taskStatus & OS_TASK_STATUS_EXIT)) {
            ret = OsTaskSetDetachUnsafe(detached->task);
            if (ret == ESRCH) {
                ret = LOS_OK;
            } else if (ret == LOS_OK) {
                detached->state = PTHREAD_STATE_DETACHED;
            }
        } else {
            detached->state = PTHREAD_STATE_EXITED;
            g_pthreadsExited++;
        }
        SCHEDULER_UNLOCK(intSave);
    }

    /* Dispose of any dead threads */
    PthreadReap();
    if (pthread_mutex_unlock(&g_pthreadsDataMutex) != ENOERR) {
        ret = ESRCH;
    }

    return ret;
}

int pthread_setschedparam(pthread_t thread, int policy, const struct sched_param *param)
{
    _pthread_data *data = NULL;
    int ret;

    if ((param == NULL) || (param->sched_priority > OS_TASK_PRIORITY_LOWEST)) {
        return EINVAL;
    }

    if (policy != SCHED_RR) {
        return EINVAL;
    }

    /* The parameters seem OK, change the thread. */
    ret = pthread_mutex_lock(&g_pthreadsDataMutex);
    if (ret != ENOERR) {
        return ret;
    }

    data = pthread_get_data(thread);
    if (data == NULL) {
        ret = pthread_mutex_unlock(&g_pthreadsDataMutex);
        if (ret != ENOERR) {
            return ret;
        }
        return ESRCH;
    }

    /* Only support one policy now */
    data->attr.schedpolicy = SCHED_RR;
    data->attr.schedparam  = *param;

    ret = pthread_mutex_unlock(&g_pthreadsDataMutex);
    if (ret != ENOERR) {
        return ret;
    }
    (VOID)LOS_TaskPriSet((UINT32)thread, (UINT16)param->sched_priority);

    return ENOERR;
}

int pthread_getschedparam(pthread_t thread, int *policy, struct sched_param *param)
{
    _pthread_data *data = NULL;
    int ret;

    if ((policy == NULL) || (param == NULL)) {
        return EINVAL;
    }

    ret = pthread_mutex_lock(&g_pthreadsDataMutex);
    if (ret != ENOERR) {
        return ret;
    }

    data = pthread_get_data(thread);
    if (data == NULL) {
        goto ERR_OUT;
    }

    *policy = data->attr.schedpolicy;
    *param = data->attr.schedparam;

    ret = pthread_mutex_unlock(&g_pthreadsDataMutex);
    return ret;
ERR_OUT:
    ret = pthread_mutex_unlock(&g_pthreadsDataMutex);
    if (ret != ENOERR) {
        return ret;
    }
    return ESRCH;
}

/* Call initRoutine just the once per control variable. */
int pthread_once(pthread_once_t *onceControl, void (*initRoutine)(void))
{
    pthread_once_t old;
    int ret;

    if ((onceControl == NULL) || (initRoutine == NULL)) {
        return EINVAL;
    }

    /* Do a test and set on the onceControl object. */
    ret = pthread_mutex_lock(&g_pthreadsDataMutex);
    if (ret != ENOERR) {
        return ret;
    }

    old = *onceControl;
    *onceControl = 1;

    ret = pthread_mutex_unlock(&g_pthreadsDataMutex);
    if (ret != ENOERR) {
        return ret;
    }
    /* If the onceControl was zero, call the initRoutine(). */
    if (!old) {
        initRoutine();
    }

    return ENOERR;
}

/* Thread specific data */
int pthread_key_create(pthread_key_t *key, void (*destructor)(void *))
{
    (VOID)key;
    (VOID)destructor;
    PRINT_ERR("[%s] is not support.\n", __FUNCTION__);
    return 0;
}

/* Store the pointer value in the thread-specific data slot addressed by the key. */
int pthread_setspecific(pthread_key_t key, const void *pointer)
{
    (VOID)key;
    (VOID)pointer;
    PRINT_ERR("[%s] is not support.\n", __FUNCTION__);
    return 0;
}

/* Retrieve the pointer value in the thread-specific data slot addressed by the key. */
void *pthread_getspecific(pthread_key_t key)
{
    (VOID)key;
    PRINT_ERR("[%s] is not support.\n", __FUNCTION__);
    return NULL;
}

/*
 * Set cancel state of current thread to ENABLE or DISABLE.
 * Returns old state in *oldState.
 */
int pthread_setcancelstate(int state, int *oldState)
{
    _pthread_data *self = NULL;
    int ret;

    if ((state != PTHREAD_CANCEL_ENABLE) && (state != PTHREAD_CANCEL_DISABLE)) {
        return EINVAL;
    }

    ret = pthread_mutex_lock(&g_pthreadsDataMutex);
    if (ret != ENOERR) {
        return ret;
    }

    self = pthread_get_self_data();

    if (oldState != NULL) {
        *oldState = self->cancelstate;
    }

    self->cancelstate = (UINT8)state;

    ret = pthread_mutex_unlock(&g_pthreadsDataMutex);
    if (ret != ENOERR) {
        return ret;
    }

    return ENOERR;
}

/*
 * Set cancel type of current thread to ASYNCHRONOUS or DEFERRED.
 * Returns old type in *oldType.
 */
int pthread_setcanceltype(int type, int *oldType)
{
    _pthread_data *self = NULL;
    int ret;

    if ((type != PTHREAD_CANCEL_ASYNCHRONOUS) && (type != PTHREAD_CANCEL_DEFERRED)) {
        return EINVAL;
    }

    ret = pthread_mutex_lock(&g_pthreadsDataMutex);
    if (ret != ENOERR) {
        return ret;
    }

    self = pthread_get_self_data();
    if (oldType != NULL) {
        *oldType = self->canceltype;
    }

    self->canceltype = (UINT8)type;

    ret = pthread_mutex_unlock(&g_pthreadsDataMutex);
    if (ret != ENOERR) {
        return ret;
    }

    return ENOERR;
}

STATIC UINT32 DoPthreadCancel(_pthread_data *data)
{
    UINT32 ret = LOS_OK;
    UINT32 intSave;
    LOS_TaskLock();
    data->canceled = 0;
    if ((data->task->taskStatus & OS_TASK_STATUS_EXIT) || (LOS_TaskSuspend(data->task->taskID) != ENOERR)) {
        ret = LOS_NOK;
        goto OUT;
    }

    if (data->task->taskStatus & OS_TASK_FLAG_PTHREAD_JOIN) {
        SCHEDULER_LOCK(intSave);
        OsTaskJoinPostUnsafe(data->task);
        SCHEDULER_UNLOCK(intSave);
        g_pthreadCanceledDummyVar = (UINTPTR)PTHREAD_CANCELED;
        data->task->joinRetval = (VOID *)g_pthreadCanceledDummyVar;
    } else if (data->state && !(data->task->taskStatus & OS_TASK_STATUS_UNUSED)) {
        data->state = PTHREAD_STATE_EXITED;
        g_pthreadsExited++;
        PthreadReap();
    } else {
        ret = LOS_NOK;
    }
OUT:
    LOS_TaskUnlock();
    return ret;
}

int pthread_cancel(pthread_t thread)
{
    _pthread_data *data = NULL;

    if (pthread_mutex_lock(&g_pthreadsDataMutex) != ENOERR) {
        PRINT_ERR("%s: %d failed\n", __FUNCTION__, __LINE__);
    }

    data = pthread_get_data(thread);
    if (data == NULL) {
        if (pthread_mutex_unlock(&g_pthreadsDataMutex) != ENOERR) {
            PRINT_ERR("%s: %d failed\n", __FUNCTION__, __LINE__);
        }
        return ESRCH;
    }

    data->canceled = 1;

    if ((data->cancelstate == PTHREAD_CANCEL_ENABLE) &&
        (data->canceltype == PTHREAD_CANCEL_ASYNCHRONOUS)) {
        /*
         * If the thread has cancellation enabled, and it is in
         * asynchronous mode, suspend it and set corresponding thread's status.
         * We also release the thread out of any current wait to make it wake up.
         */
        if (DoPthreadCancel(data) == LOS_NOK) {
            goto ERROR_OUT;
        }
    }

    /*
     * Otherwise the thread has cancellation disabled, in which case
     * it is up to the thread to enable cancellation
     */
    if (pthread_mutex_unlock(&g_pthreadsDataMutex) != ENOERR) {
        PRINT_ERR("%s: %d failed\n", __FUNCTION__, __LINE__);
    }

    return ENOERR;
ERROR_OUT:
    if (pthread_mutex_unlock(&g_pthreadsDataMutex) != ENOERR) {
        PRINT_ERR("%s: %d failed\n", __FUNCTION__, __LINE__);
    }
    return ESRCH;
}

/*
 * Test for a pending cancellation for the current thread and terminate
 * the thread if there is one.
 */
void pthread_testcancel(void)
{
    if (CheckForCancel()) {
        /*
         * If we have cancellation enabled, and there is a cancellation
         * pending, then go ahead and do the deed.
         * Exit now with special retVal. pthread_exit() calls the
         * cancellation handlers implicitly.
         */
        pthread_exit((void *)PTHREAD_CANCELED);
    }
}

/* Get current thread id. */
pthread_t pthread_self(void)
{
    _pthread_data *data = pthread_get_self_data();

    return data->id;
}

/* Compare two thread identifiers. */
int pthread_equal(pthread_t thread1, pthread_t thread2)
{
    return thread1 == thread2;
}

void pthread_cleanup_push_inner(struct pthread_cleanup_buffer *buffer,
                                void (*routine)(void *), void *arg)
{
    (VOID)buffer;
    (VOID)routine;
    (VOID)arg;
    PRINT_ERR("[%s] is not support.\n", __FUNCTION__);
    return;
}

void pthread_cleanup_pop_inner(struct pthread_cleanup_buffer *buffer, int execute)
{
    (VOID)buffer;
    (VOID)execute;
    PRINT_ERR("[%s] is not support.\n", __FUNCTION__);
    return;
}

/*
 * Set the cpu affinity mask for the thread
 */
int pthread_setaffinity_np(pthread_t thread, size_t cpusetsize, const cpu_set_t* cpuset)
{
    INT32 ret = sched_setaffinity(thread, cpusetsize, cpuset);
    if (ret == -1) {
        return errno;
    } else {
        return ENOERR;
    }
}

/*
 * Get the cpu affinity mask from the thread
 */
int pthread_getaffinity_np(pthread_t thread, size_t cpusetsize, cpu_set_t* cpuset)
{
    INT32 ret = sched_getaffinity(thread, cpusetsize, cpuset);
    if (ret == -1) {
        return errno;
    } else {
        return ENOERR;
    }
}

