2021-07-14 10:50:44 +02:00
|
|
|
#include "fsfw/osal/host/PeriodicTask.h"
|
2020-09-05 20:18:52 +02:00
|
|
|
|
2022-02-02 10:29:30 +01:00
|
|
|
#include <chrono>
|
|
|
|
#include <thread>
|
|
|
|
|
2021-07-14 10:50:44 +02:00
|
|
|
#include "fsfw/ipc/MutexFactory.h"
|
|
|
|
#include "fsfw/objectmanager/ObjectManager.h"
|
2022-02-02 10:29:30 +01:00
|
|
|
#include "fsfw/osal/host/Mutex.h"
|
|
|
|
#include "fsfw/osal/host/taskHelpers.h"
|
|
|
|
#include "fsfw/platform.h"
|
2021-07-14 10:50:44 +02:00
|
|
|
#include "fsfw/serviceinterface/ServiceInterface.h"
|
|
|
|
#include "fsfw/tasks/ExecutableObjectIF.h"
|
2020-09-05 20:18:52 +02:00
|
|
|
|
2021-05-12 16:47:53 +02:00
|
|
|
#if defined(PLATFORM_WIN)
|
2021-01-29 00:12:40 +01:00
|
|
|
#include <processthreadsapi.h>
|
2022-02-02 10:29:30 +01:00
|
|
|
|
2021-07-14 10:50:44 +02:00
|
|
|
#include "fsfw/osal/windows/winTaskHelpers.h"
|
2021-05-12 16:47:53 +02:00
|
|
|
#elif defined(PLATFORM_UNIX)
|
2020-09-05 20:18:52 +02:00
|
|
|
#include <pthread.h>
|
|
|
|
#endif
|
|
|
|
|
2022-02-02 10:29:30 +01:00
|
|
|
PeriodicTask::PeriodicTask(const char* name, TaskPriority setPriority, TaskStackSize setStack,
|
2022-05-18 15:42:18 +02:00
|
|
|
TaskPeriod setPeriod, TaskDeadlineMissedFunction dlmFunc_)
|
|
|
|
: PeriodicTaskBase(setPeriod, dlmFunc_), started(false), taskName(name) {
|
2022-02-02 10:29:30 +01:00
|
|
|
// It is probably possible to set task priorities by using the native
|
|
|
|
// task handles for Windows / Linux
|
|
|
|
mainThread = std::thread(&PeriodicTask::taskEntryPoint, this, this);
|
2021-05-12 16:47:53 +02:00
|
|
|
#if defined(PLATFORM_WIN)
|
2022-02-02 10:29:30 +01:00
|
|
|
tasks::setTaskPriority(reinterpret_cast<HANDLE>(mainThread.native_handle()), setPriority);
|
2021-05-12 16:47:53 +02:00
|
|
|
#elif defined(PLATFORM_UNIX)
|
2022-02-02 10:29:30 +01:00
|
|
|
// TODO: We could reuse existing code here.
|
2020-09-05 20:18:52 +02:00
|
|
|
#endif
|
2022-02-02 10:29:30 +01:00
|
|
|
tasks::insertTaskName(mainThread.get_id(), taskName);
|
2020-09-05 20:18:52 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
PeriodicTask::~PeriodicTask(void) {
|
2022-02-02 10:29:30 +01:00
|
|
|
// Do not delete objects, we were responsible for ptrs only.
|
|
|
|
terminateThread = true;
|
|
|
|
if (mainThread.joinable()) {
|
|
|
|
mainThread.join();
|
|
|
|
}
|
2020-09-05 20:18:52 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void PeriodicTask::taskEntryPoint(void* argument) {
|
2022-02-02 10:29:30 +01:00
|
|
|
PeriodicTask* originalTask(reinterpret_cast<PeriodicTask*>(argument));
|
2020-09-05 20:18:52 +02:00
|
|
|
|
2022-02-02 10:29:30 +01:00
|
|
|
if (not originalTask->started) {
|
|
|
|
// we have to suspend/block here until the task is started.
|
|
|
|
// if semaphores are implemented, use them here.
|
|
|
|
std::unique_lock<std::mutex> lock(initMutex);
|
|
|
|
initCondition.wait(lock);
|
|
|
|
}
|
2020-09-05 20:18:52 +02:00
|
|
|
|
2022-02-02 10:29:30 +01:00
|
|
|
this->taskFunctionality();
|
2021-01-03 14:16:52 +01:00
|
|
|
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
2022-02-02 10:29:30 +01:00
|
|
|
sif::debug << "PeriodicTask::taskEntryPoint: "
|
|
|
|
"Returned from taskFunctionality."
|
|
|
|
<< std::endl;
|
2021-01-03 13:58:18 +01:00
|
|
|
#endif
|
2020-09-05 20:18:52 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
ReturnValue_t PeriodicTask::startTask() {
|
2022-02-02 10:29:30 +01:00
|
|
|
started = true;
|
2020-09-05 20:18:52 +02:00
|
|
|
|
2022-02-02 10:29:30 +01:00
|
|
|
// Notify task to start.
|
|
|
|
std::lock_guard<std::mutex> lock(initMutex);
|
|
|
|
initCondition.notify_one();
|
2020-09-05 20:18:52 +02:00
|
|
|
|
2022-02-02 10:29:30 +01:00
|
|
|
return HasReturnvaluesIF::RETURN_OK;
|
2020-09-05 20:18:52 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
ReturnValue_t PeriodicTask::sleepFor(uint32_t ms) {
|
2022-02-02 10:29:30 +01:00
|
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(ms));
|
|
|
|
return HasReturnvaluesIF::RETURN_OK;
|
2020-09-05 20:18:52 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void PeriodicTask::taskFunctionality() {
|
2022-05-18 15:42:18 +02:00
|
|
|
initObjsAfterTaskCreation();
|
2022-02-02 10:29:30 +01:00
|
|
|
|
|
|
|
std::chrono::milliseconds periodChrono(static_cast<uint32_t>(period * 1000));
|
|
|
|
auto currentStartTime{std::chrono::duration_cast<std::chrono::milliseconds>(
|
|
|
|
std::chrono::system_clock::now().time_since_epoch())};
|
|
|
|
auto nextStartTime{currentStartTime};
|
|
|
|
|
|
|
|
/* Enter the loop that defines the task behavior. */
|
|
|
|
for (;;) {
|
|
|
|
if (terminateThread.load()) {
|
|
|
|
break;
|
2020-12-02 00:58:13 +01:00
|
|
|
}
|
2022-05-18 15:42:18 +02:00
|
|
|
for (const auto& objectPair : objectList) {
|
|
|
|
objectPair.first->performOperation(objectPair.second);
|
2022-02-02 10:29:30 +01:00
|
|
|
}
|
|
|
|
if (not delayForInterval(¤tStartTime, periodChrono)) {
|
2022-05-18 15:42:18 +02:00
|
|
|
if (dlmFunc != nullptr) {
|
|
|
|
this->dlmFunc();
|
2022-02-02 10:29:30 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-09-05 20:18:52 +02:00
|
|
|
}
|
|
|
|
|
2022-02-02 10:29:30 +01:00
|
|
|
bool PeriodicTask::delayForInterval(chron_ms* previousWakeTimeMs, const chron_ms interval) {
|
|
|
|
bool shouldDelay = false;
|
|
|
|
// Get current wakeup time
|
|
|
|
auto currentStartTime = std::chrono::duration_cast<std::chrono::milliseconds>(
|
|
|
|
std::chrono::system_clock::now().time_since_epoch());
|
|
|
|
/* Generate the tick time at which the task wants to wake. */
|
|
|
|
auto nextTimeToWake_ms = (*previousWakeTimeMs) + interval;
|
|
|
|
|
|
|
|
if (currentStartTime < *previousWakeTimeMs) {
|
|
|
|
/* The tick count has overflowed since this function was
|
|
|
|
lasted called. In this case the only time we should ever
|
|
|
|
actually delay is if the wake time has also overflowed,
|
|
|
|
and the wake time is greater than the tick time. When this
|
|
|
|
is the case it is as if neither time had overflowed. */
|
|
|
|
if ((nextTimeToWake_ms < *previousWakeTimeMs) && (nextTimeToWake_ms > currentStartTime)) {
|
|
|
|
shouldDelay = true;
|
2020-09-05 20:18:52 +02:00
|
|
|
}
|
2022-02-02 10:29:30 +01:00
|
|
|
} else {
|
|
|
|
/* The tick time has not overflowed. In this case we will
|
|
|
|
delay if either the wake time has overflowed, and/or the
|
|
|
|
tick time is less than the wake time. */
|
|
|
|
if ((nextTimeToWake_ms < *previousWakeTimeMs) || (nextTimeToWake_ms > currentStartTime)) {
|
|
|
|
shouldDelay = true;
|
|
|
|
}
|
|
|
|
}
|
2020-09-05 20:18:52 +02:00
|
|
|
|
2022-02-02 10:29:30 +01:00
|
|
|
/* Update the wake time ready for the next call. */
|
2020-09-05 20:18:52 +02:00
|
|
|
|
2022-02-02 10:29:30 +01:00
|
|
|
(*previousWakeTimeMs) = nextTimeToWake_ms;
|
2020-09-05 20:18:52 +02:00
|
|
|
|
2022-02-02 10:29:30 +01:00
|
|
|
if (shouldDelay) {
|
|
|
|
auto sleepTime =
|
|
|
|
std::chrono::duration_cast<std::chrono::milliseconds>(nextTimeToWake_ms - currentStartTime);
|
|
|
|
std::this_thread::sleep_for(sleepTime);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
// We are shifting the time in case the deadline was missed like rtems
|
|
|
|
(*previousWakeTimeMs) = currentStartTime;
|
|
|
|
return false;
|
2020-09-05 20:18:52 +02:00
|
|
|
}
|