mirror of
https://github.com/eclipse-openvehicle-api/openvehicle-api.git
synced 2026-07-01 21:25:11 +00:00
175 lines
5.0 KiB
C++
175 lines
5.0 KiB
C++
/********************************************************************************
|
|
* Copyright (c) 2025-2026 ZF Friedrichshafen AG
|
|
*
|
|
* This program and the accompanying materials are made available under the
|
|
* terms of the Apache License Version 2.0 which is available at
|
|
* https://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*
|
|
* Contributors:
|
|
* Erik Verhoeven - initial API and implementation
|
|
********************************************************************************/
|
|
|
|
#include "tasktimer.h"
|
|
#include <fstream>
|
|
#include <functional>
|
|
|
|
CTimer::CTimer(CTaskTimerService& rtimersvc, uint32_t uiPeriod, sdv::core::ITaskExecute* pExecute) :
|
|
m_rtimersvc(rtimersvc), m_pExecute(pExecute)
|
|
{
|
|
if (!pExecute) return;
|
|
|
|
#ifdef _WIN32
|
|
// Use a lambda function without capture as function pointer.
|
|
// NOTE: Casting is unsafe... but a necessity when working with Windows API functions.
|
|
m_uiTimerID = timeSetEvent(uiPeriod, 0, [](UINT, UINT, DWORD_PTR dwUser, DWORD_PTR, DWORD_PTR)
|
|
{
|
|
CTimer* pTimer = reinterpret_cast<CTimer*>(dwUser);
|
|
pTimer->ExecuteCallback();
|
|
}, reinterpret_cast<DWORD_PTR>(this), TIME_PERIODIC | TIME_KILL_SYNCHRONOUS);
|
|
#elif defined __unix__
|
|
// Configure the signal event for timer expiration
|
|
sigevent sev{};
|
|
sev.sigev_notify = SIGEV_THREAD;
|
|
sev.sigev_notify_function = [](sigval sv)
|
|
{
|
|
CTimer* pTimer = reinterpret_cast<CTimer*>(sv.sival_ptr);
|
|
std::unique_lock<std::mutex> lock(pTimer->m_mtxExecution);
|
|
if (!pTimer->m_bRunning) return;
|
|
sdv::core::ITaskExecute* pExecuteLocal = reinterpret_cast<sdv::core::ITaskExecute*>(pTimer->m_pExecute);
|
|
pExecuteLocal->Execute();
|
|
};
|
|
sev.sigev_value.sival_ptr = this;
|
|
sev.sigev_notify_attributes = nullptr;
|
|
|
|
// Create the timer
|
|
if (timer_create(CLOCK_MONOTONIC, &sev, &m_timerid) != 0)
|
|
return;
|
|
|
|
//split up period in full seconds and remaining nanoseconds
|
|
//find out how many full seconds period contains
|
|
uint32_t uiFullSeconds = uiPeriod / 1000;
|
|
//determine remainder in nanoseconds
|
|
uint32_t uiRemainderNS = (uiPeriod % 1000)*1000000;
|
|
|
|
// Configure the timer
|
|
itimerspec its{};
|
|
its.it_value.tv_sec = uiFullSeconds;
|
|
its.it_value.tv_nsec = uiRemainderNS;
|
|
its.it_interval.tv_sec = uiFullSeconds;
|
|
its.it_interval.tv_nsec = uiRemainderNS;
|
|
|
|
// Start the timer
|
|
if (timer_settime(m_timerid, 0, &its, nullptr) != 0)
|
|
{
|
|
timer_delete(m_timerid);
|
|
return;
|
|
}
|
|
|
|
m_bRunning = true;
|
|
#endif
|
|
}
|
|
|
|
void CTimer::DestroyObject()
|
|
{
|
|
#ifdef _WIN32
|
|
// Terminate the timer
|
|
if (m_uiTimerID)
|
|
timeKillEvent(m_uiTimerID);
|
|
m_uiTimerID = 0ul;
|
|
#elif defined __unix__
|
|
// Terminate the timer
|
|
if (m_bRunning)
|
|
{
|
|
std::unique_lock<std::mutex> lock(m_mtxExecution);
|
|
m_bRunning = false;
|
|
timer_delete(m_timerid);
|
|
m_timerid = 0ul;
|
|
lock.unlock();
|
|
|
|
// Sleep 20ms to allow all outstanding tasks to end.
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(20));
|
|
}
|
|
#endif
|
|
|
|
// Delete the object
|
|
m_rtimersvc.RemoveTimer(this);
|
|
}
|
|
|
|
CTimer::operator bool() const
|
|
{
|
|
#ifdef _WIN32
|
|
return m_uiTimerID ? true : false;
|
|
#elif defined __unix__
|
|
return m_bRunning;
|
|
#endif
|
|
}
|
|
|
|
#ifdef _WIN32
|
|
void CTimer::ExecuteCallback()
|
|
{
|
|
if (m_rtimersvc.GetObjectState() != sdv::EObjectState::running) return;
|
|
if (!m_pExecute) return;
|
|
if (!m_bPrioritySet)
|
|
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);
|
|
m_bPrioritySet = true;
|
|
if (m_pExecute) m_pExecute->Execute();
|
|
}
|
|
#endif
|
|
|
|
CTaskTimerService::CTaskTimerService()
|
|
{
|
|
}
|
|
|
|
CTaskTimerService::~CTaskTimerService()
|
|
{
|
|
}
|
|
|
|
bool CTaskTimerService::OnInitialize()
|
|
{
|
|
#ifdef _WIN32
|
|
SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS);
|
|
|
|
// set up time resolution and maybe some other things
|
|
timeBeginPeriod(1);
|
|
#endif
|
|
|
|
return true;
|
|
}
|
|
|
|
void CTaskTimerService::OnShutdown()
|
|
{
|
|
#ifdef _WIN32
|
|
timeEndPeriod(1);
|
|
#endif
|
|
}
|
|
|
|
sdv::IInterfaceAccess* CTaskTimerService::CreateTimer(uint32_t uiPeriod, sdv::IInterfaceAccess* pTask)
|
|
{
|
|
if (GetObjectState() != sdv::EObjectState::configuring) return nullptr;
|
|
|
|
if (!uiPeriod) return nullptr;
|
|
if (!pTask) return nullptr;
|
|
sdv::core::ITaskExecute* pExecute = pTask->GetInterface<sdv::core::ITaskExecute>();
|
|
if (!pExecute) return nullptr;
|
|
|
|
std::unique_lock<std::mutex> lock(m_mtxTasks);
|
|
auto ptrTimer = std::make_unique<CTimer>(*this, uiPeriod, pExecute);
|
|
// Ignore cppcheck warning; normally the returned pointer should always have a value at this stage (otherwise an
|
|
// exception was triggered).
|
|
// cppcheck-suppress knownConditionTrueFalse
|
|
if (!ptrTimer)
|
|
return nullptr;
|
|
CTimer* pObject = ptrTimer.get();
|
|
if (pObject)
|
|
m_mapTasks.try_emplace(pObject, std::move(ptrTimer));
|
|
return pObject;
|
|
}
|
|
|
|
void CTaskTimerService::RemoveTimer(CTimer* pTimer)
|
|
{
|
|
std::unique_lock<std::mutex> lock(m_mtxTasks);
|
|
m_mapTasks.erase(pTimer);
|
|
}
|