//------------------------------------------------------------------------------
tOplkError timeru_exit(void)
{
    tTimeruData*     pTimer;

    /* cancel thread */
    pthread_cancel(timeruInstance_g.processThread);
    DEBUG_LVL_TIMERU_TRACE("%s() Waiting for thread to exit...\n", __func__);

    /* wait for thread to terminate */
    pthread_join(timeruInstance_g.processThread, NULL);
    DEBUG_LVL_TIMERU_TRACE("%s()Thread exited\n", __func__);

    /* free up timer list */
    resetTimerList();
    while ((pTimer = getNextTimer()) != NULL)
    {
        removeTimer(pTimer);
        OPLK_FREE(pTimer);
    }

    pthread_mutex_destroy(&timeruInstance_g.mutex);

    timeruInstance_g.pFirstTimer = NULL;
    timeruInstance_g.pLastTimer = NULL;

    return kErrorOk;
}
//------------------------------------------------------------------------------
static void* processThread(void* pArgument_p)
{
    tTimeruData*    pTimer;
    sigset_t        awaitedSignal;
    siginfo_t       signalInfo;

    UNUSED_PARAMETER(pArgument_p);

    // Uncomment to show the thread ID on Linux (include must also be uncommented)!
    // DEBUG_LVL_TIMERU_TRACE("%s() ThreadId:%d\n", __func__, syscall(SYS_gettid));

    sigemptyset(&awaitedSignal);
    sigaddset(&awaitedSignal, SIGRTMIN);
    pthread_sigmask(SIG_BLOCK, &awaitedSignal, NULL);

    /* loop forever until thread will be canceled */
    while (1)
    {
        if (sigwaitinfo(&awaitedSignal, &signalInfo) > 0)
        {
            pTimer = (tTimeruData*)signalInfo.si_value.sival_ptr;
            if (pTimer != NULL)
                /* call callback function of timer */
                cbTimer((ULONG)pTimer);
            else
            {
                DEBUG_LVL_ERROR_TRACE("%s() sival_ptr==NULL code=%d\n", __func__,
                                      signalInfo.si_code);
            }
        }
    }

    DEBUG_LVL_TIMERU_TRACE("%s() Exiting!\n", __func__);
    return NULL;
}
//------------------------------------------------------------------------------
tOplkError timeru_setTimer(tTimerHdl* pTimerHdl_p, ULONG timeInMs_p, tTimerArg argument_p)
{
    tTimeruData*        pData;
    struct itimerspec   relTime;
    tHrtimerSig         sig;

    if (pTimerHdl_p == NULL)
        return kErrorTimerInvalidHandle;

    pData = (tTimeruData*)OPLK_MALLOC(sizeof(tTimeruData));
    if (pData == NULL)
    {
        DEBUG_LVL_ERROR_TRACE("error allocating user timer memory!\n");
        return kErrorNoResource;
    }

    OPLK_MEMCPY(&pData->timerArg, &argument_p, sizeof(tTimerArg));

    addTimer(pData);

    sig.sigType = kHrtimerSigMsgQueue;
    sig.sigParam.m_signalMq.msgQueue = timeruInstance_l.msgQueue;
    sig.sigParam.m_signalMq.m_sigData = (ULONG)pData;

    if (hrtimer_create(CLOCK_MONOTONIC, &sig, &pData->timer) != 0)
    {
        DEBUG_LVL_ERROR_TRACE("%s() Error hrtimer_create!\n", __func__);
        return kErrorNoResource;
    }

    if (timeInMs_p >= 1000)
    {
        relTime.it_value.tv_sec = (timeInMs_p / 1000);
        relTime.it_value.tv_nsec = (timeInMs_p % 1000) * 1000000;
    }
    else
    {
        relTime.it_value.tv_sec = 0;
        relTime.it_value.tv_nsec = timeInMs_p * 1000000;
    }

    relTime.it_interval.tv_sec = 0;
    relTime.it_interval.tv_nsec = 0;

    DEBUG_LVL_TIMERU_TRACE("%s() Set timer:%08x timeInMs_p=%ld\n",
                           __func__, *pData, timeInMs_p);

    if (hrtimer_settime(pData->timer, 0, &relTime, NULL) < 0)
    {
        DEBUG_LVL_ERROR_TRACE("%s() Error hrtimer_settime!\n", __func__);
        return kErrorTimerNoTimerCreated;
    }

    *pTimerHdl_p = (tTimerHdl)pData;

    return kErrorOk;
}
//------------------------------------------------------------------------------
tOplkError timeru_modifyTimer(tTimerHdl* pTimerHdl_p,
                              ULONG timeInMs_p,
                              const tTimerArg* pArgument_p)
{
    tTimeruData*        pData;
    struct itimerspec   relTime;

    if (pTimerHdl_p == NULL)
        return kErrorTimerInvalidHandle;

    // check handle itself, i.e. was the handle initialized before
    if (*pTimerHdl_p == 0)
        return timeru_setTimer(pTimerHdl_p, timeInMs_p, pArgument_p);

    pData = (tTimeruData*)*pTimerHdl_p;

    if (timeInMs_p >= 1000)
    {
        relTime.it_value.tv_sec = (timeInMs_p / 1000);
        relTime.it_value.tv_nsec = (timeInMs_p % 1000) * 1000000;
    }
    else
    {
        relTime.it_value.tv_sec = 0;
        relTime.it_value.tv_nsec = timeInMs_p * 1000000;
    }

    DEBUG_LVL_TIMERU_TRACE("%s() Modify timer:%08x timeInMs_p=%ld\n",
                           __func__,
                           *pTimerHdl_p,
                           timeInMs_p);

    relTime.it_interval.tv_sec = 0;
    relTime.it_interval.tv_nsec = 0;

    if (hrtimer_settime(pData->timer, 0, &relTime, NULL) != 0)
    {
        DEBUG_LVL_ERROR_TRACE("%s() Error timer_settime!\n", __func__);
        return kErrorTimerNoTimerCreated;
    }

    // copy the TimerArg after the timer is restarted,
    // so that a timer occurred immediately before hrtimer_settime
    // won't use the new timerArg and
    // therefore the old timer cannot be distinguished from the new one.
    // But if the new timer is too fast, it may get lost.
    OPLK_MEMCPY(&pData->timerArg, pArgument_p, sizeof(tTimerArg));

    return kErrorOk;
}