/*
 * Copyright (c) 2020-2021 Huawei Device Co., Ltd.
 *
 * HDF is dual licensed: you can use it either under the terms of
 * the GPL, or the BSD license, at your option.
 * See the LICENSE file in the root of this repository for complete details.
 */

#include "osal_mutex.h"
#include <errno.h>
#include <pthread.h>
#include "securec.h"
#include "hdf_log.h"
#include "osal_mem.h"

#define HDF_LOG_TAG osal_mutex
#define HDF_NANO_UNITS 1000000000

int32_t OsalMutexInit(struct OsalMutex *mutex)
{
    pthread_mutex_t *mutexTmp = NULL;
    int32_t ret;

    if (mutex == NULL) {
        HDF_LOGE("%s invalid param", __func__);
        return HDF_ERR_INVALID_PARAM;
    }

    mutex->realMutex = NULL;

    mutexTmp = (pthread_mutex_t *)OsalMemCalloc(sizeof(pthread_mutex_t));
    if (mutexTmp == NULL) {
        HDF_LOGE("%s malloc fail", __func__);
        return HDF_ERR_MALLOC_FAIL;
    }

    ret = pthread_mutex_init(mutexTmp, NULL);
    if (ret != 0) {
        HDF_LOGE("%s fail %d %d", __func__, ret, __LINE__);
        OsalMemFree(mutexTmp);
        return HDF_FAILURE;
    }

    mutex->realMutex = (void *)mutexTmp;

    return HDF_SUCCESS;
}

int32_t OsalMutexDestroy(struct OsalMutex *mutex)
{
    int32_t ret;

    if (mutex == NULL || mutex->realMutex == NULL) {
        HDF_LOGE("%s invalid param", __func__);
        return HDF_ERR_INVALID_PARAM;
    }

    ret = pthread_mutex_destroy((pthread_mutex_t *)mutex->realMutex);
    if (ret != 0) {
        HDF_LOGE("%s fail %d %d", __func__, ret, __LINE__);
        return HDF_FAILURE;
    }

    OsalMemFree(mutex->realMutex);
    mutex->realMutex = NULL;

    return HDF_SUCCESS;
}

int32_t OsalMutexLock(struct OsalMutex *mutex)
{
    if (mutex == NULL || mutex->realMutex == NULL) {
        HDF_LOGE("%s invalid param", __func__);
        return HDF_ERR_INVALID_PARAM;
    }
    return OsalMutexTimedLock(mutex, HDF_WAIT_FOREVER);
}

int32_t OsalMutexTimedLock(struct OsalMutex *mutex, uint32_t ms)
{
    int32_t ret;

    if (mutex == NULL || mutex->realMutex == NULL) {
        HDF_LOGE("%s invalid param", __func__);
        return HDF_ERR_INVALID_PARAM;
    }

    if (ms == HDF_WAIT_FOREVER) {
        ret = pthread_mutex_lock((pthread_mutex_t *)mutex->realMutex);
        if (ret != 0) {
            HDF_LOGE("pthread_mutex_lock fail %d", ret);
            return HDF_FAILURE;
        }
    } else {
        struct timespec time;
        (void)memset_s(&time, sizeof(time), 0, sizeof(time));
        clock_gettime(CLOCK_REALTIME, &time);
        time.tv_sec += (time_t)ms / HDF_KILO_UNIT;
        time.tv_nsec += (time_t)(ms % HDF_KILO_UNIT) * HDF_KILO_UNIT * HDF_KILO_UNIT;
        if (time.tv_nsec >= HDF_NANO_UNITS) {
            time.tv_nsec -= HDF_NANO_UNITS;
            time.tv_sec += 1;
        }
        ret = pthread_mutex_timedlock((pthread_mutex_t *)mutex->realMutex, &time);
        if (ret != 0) {
            if (ret == ETIMEDOUT) {
                return HDF_ERR_TIMEOUT;
            } else {
                HDF_LOGE("%s time_out time:%u ret:%d", __func__, ms, ret);
                return HDF_FAILURE;
            }
        }
    }

    return HDF_SUCCESS;
}

int32_t OsalMutexUnlock(struct OsalMutex *mutex)
{
    int32_t ret;

    if (mutex == NULL || mutex->realMutex == NULL) {
        HDF_LOGE("%s invalid param", __func__);
        return HDF_ERR_INVALID_PARAM;
    }

    ret = pthread_mutex_unlock((pthread_mutex_t *)mutex->realMutex);
    if (ret != 0) {
        HDF_LOGE("%s fail %d %d", __func__, ret, __LINE__);
        return HDF_FAILURE;
    }

    return HDF_SUCCESS;
}