Merge pull request 'watchdog-integration' (#2) from watchdog-integration into main

Reviewed-on: https://git.intern.spaceteamaachen.de/ALPAKA/Tasty/pulls/2
This commit is contained in:
dario 2024-04-17 22:12:14 +00:00
commit d44b87c987
13 changed files with 590 additions and 7 deletions

View File

@ -8,6 +8,8 @@
#ifndef STA_CONFIG_HPP
#define STA_CONFIG_HPP
#define STA_STM32_SWD_USART_IDX 2
#include <sta/devices/stm32/mcu/STM32F411xE.hpp>
// Doesn't really do too much right now. Has to be added for successful compilation.

View File

@ -2,6 +2,6 @@
#ifndef STA_TASTY_CONFIG_HPP
#define STA_TASTY_CONFIG_HPP
#define TASTY_CASE_5
#define TASTY_CASE_9
#endif // STA_TASTY_CONFIG_HPP

2
run.py
View File

@ -22,7 +22,7 @@ path = 'Tasty/src/cases/'
for case in os.listdir(path):
# Find the number of the test case.
match = re.match('case([0-9])+\.cpp', case)
match = re.match('case([0-9]+)\.cpp', case)
number = match.group(1)
with open('Tasty/include/sta/tasty/config.hpp', 'w') as f:

118
src/cases/case10.cpp Normal file
View File

@ -0,0 +1,118 @@
/*
* case10.cpp
*
* Created on: Feb 7, 2024
* Author: Dario
*/
/**
* A sleeping thread is not restarted by the watchdog. Additionally, using blocking() also prevents a restart.
*/
#include <sta/tasty/config.hpp>
#ifdef TASTY_CASE_10
#include <sta/tacos.hpp>
#include <sta/tasty/utils.hpp>
#include <sta/tacos/watchdog.hpp>
#include <sta/rtos/signal.hpp>
namespace sta
{
namespace tasty
{
RtosSignal * signal;
class ThreadA : public tacos::TacosThread
{
public:
ThreadA()
: tacos::TacosThread{"A", osPriorityNormal}
{
}
void pointless_expensive_computation()
{
uint32_t ticks = osKernelGetSysTimerCount();
uint32_t freq = osKernelGetSysTimerCount();
uint64_t dummyCounter = 0;
while (osKernelGetSysTimerCount() <= ticks + 2 * freq)
{
dummyCounter++;
}
}
void func() override
{
this->sleep(3000);
signal->notify();
// This should notify the watchdog that this thread should not be monitored.
blocking(
pointless_expensive_computation();
)
signal->notify();
pointless_expensive_computation();
}
};
std::shared_ptr<ThreadA> thread_A;
class Observer : public tacos::TacosThread
{
public:
Observer()
: tacos::TacosThread{"Observer", osPriorityNormal}
{
}
void func() override
{
uint16_t restarts = tacos::Watchdog::instance()->getNumRestarts();
// Wait for thread A to finish sleeping.
blocking(
signal->wait();
)
// Thread A shouldn't have been restarted because sleep() sets the thread's status to WAITING.
STA_TASTY_ASSERT(tacos::Watchdog::instance()->getNumRestarts() == restarts);
blocking(
signal->wait();
)
// Thread A shouldn't have been restarted because the computationally expensive computation was wrapped in blocking()
STA_TASTY_ASSERT(tacos::Watchdog::instance()->getNumRestarts() == restarts);
this->sleep(2500);
STA_TASTY_ASSERT(tacos::Watchdog::instance()->getNumRestarts() == restarts + 1);
STA_TASTY_TERMINATE();
}
};
void onTastyInit()
{
signal = new RtosSignal(2);
thread_A = tacos::addThread<ThreadA>({0});
tacos::addThread<Observer>({0, 1});
}
} // namespace tasty
}
#endif // TASTY_CASE_10

77
src/cases/case11.cpp Normal file
View File

@ -0,0 +1,77 @@
/*
* case11.cpp
*
* Created on: Feb 7, 2024
* Author: Dario
*/
/**
* Tests if a thread with IGNORED status is properly ignored.
*/
#include <sta/tasty/config.hpp>
#ifdef TASTY_CASE_11
#include <sta/tacos.hpp>
#include <sta/tasty/utils.hpp>
#include <sta/tacos/watchdog.hpp>
#include <sta/rtos/signal.hpp>
namespace sta
{
namespace tasty
{
class ThreadA : public tacos::TacosThread
{
public:
ThreadA()
: tacos::TacosThread{"A", osPriorityNormal}
{
}
void init() override
{
// Tells the watchdog to ignore this thread.
watchdogIgnore();
}
void func() override
{
// Thread is stuck in infinite loop
while (true)
{
osThreadYield();
}
}
};
class Observer : public tacos::TacosThread
{
public:
Observer()
: tacos::TacosThread{"Observer", osPriorityNormal}
{
}
void func() override
{
this->sleep(2000);
// The watchdog shouldn't have restarted thread A.
STA_TASTY_ASSERT(tacos::Watchdog::instance()->getNumRestarts() == 0);
STA_TASTY_TERMINATE();
}
};
void onTastyInit()
{
tacos::addThread<ThreadA>({0});
tacos::addThread<Observer>({0, 1});
}
} // namespace tasty
}
#endif // TASTY_CASE_11

View File

@ -1,5 +1,7 @@
/**
*
* Timed state transitions can be overwritten by timed state transitions with later deadline.
*
* TODO: Implement the correct requirement.
*/
#include <sta/tasty/config.hpp>
#ifdef TASTY_CASE_5
@ -13,8 +15,6 @@ namespace sta
{
class DummyThread : public tacos::TacosThread
{
private:
/* data */
public:
DummyThread()
: tacos::TacosThread{"Dummy", osPriorityNormal}

85
src/cases/case6.cpp Normal file
View File

@ -0,0 +1,85 @@
/**
* Tests if killing tasks works correctly and the memory is correctly freed.
*/
#include <sta/tasty/config.hpp>
#ifdef TASTY_CASE_6
#include <memory>
#include <sta/tacos.hpp>
#include <sta/tasty/utils.hpp>
#include <sta/rtos/debug/heap_stats.hpp>
#include <sta/rtos/signal.hpp>
namespace sta
{
namespace tasty
{
static uint8_t victim_alive_flag;
class Victim : public tacos::TacosThread
{
public:
Victim()
: tacos::TacosThread{"Dummy", osPriorityNormal}
{}
void func() override
{
victim_alive_flag = 1;
osThreadYield();
}
};
class ViciousMurderer : public tacos::TacosThread
{
public:
ViciousMurderer(std::shared_ptr<Victim> victim_ptr)
: tacos::TacosThread{"Dummy", osPriorityNormal},
victim_ptr_{victim_ptr}
{}
void func() override
{
// Kill the thread and set it's heartbeat flag to 0.
victim_ptr_->kill();
victim_alive_flag = 0;
uint8_t frees = sta::rtos::getNumFrees();
// Skip a tick
osThreadYield();
// The killed threat shouldn't have set the heartbeat flag.
STA_TASTY_ASSERT(victim_alive_flag == 0);
// Wait for 100 ticks.
this->sleep(100);
// After waiting 100 ticks, the flag should still be 0 because the thread doesn't run.
STA_TASTY_ASSERT(victim_alive_flag == 0);
// Also, there should be two more frees:
STA_TASTY_ASSERT(sta::rtos::getNumFrees() - frees == 2);
STA_TASTY_TERMINATE();
}
private:
std::shared_ptr<Victim> victim_ptr_;
};
void onTastyInit()
{
std::shared_ptr<Victim> victim_ptr = tacos::addThread<Victim>({0});
tacos::addThread<ViciousMurderer>({0}, victim_ptr);
}
} // namespace tasty
} // namespace sta
#endif // TASTY_CASE_6/*

86
src/cases/case7.cpp Normal file
View File

@ -0,0 +1,86 @@
/*
* case7.cpp
*
* Created on: Jan 24, 2024
* Author: Dario
*/
/**
* A thread gets stuck in an infinite loop and the watchdog restarts it.
*/
#include <sta/tasty/config.hpp>
#ifdef TASTY_CASE_7
#include <sta/tacos.hpp>
#include <sta/tasty/utils.hpp>
#include <sta/tacos/watchdog.hpp>
namespace sta
{
namespace tasty
{
static uint8_t alive_flag = 0;
class Dummy : public tacos::TacosThread
{
public:
Dummy()
: tacos::TacosThread{"Dummy", osPriorityNormal}
{
}
void init() override
{
alive_flag = 1;
}
void func() override
{
this->sleep(100);
// Thread is stuck in infinite loop
while (true)
{
osThreadYield();
}
}
};
class Observer : public tacos::TacosThread
{
public:
Observer()
: tacos::TacosThread{"Observer", osPriorityNormal}
{
}
void func() override
{
this->sleep(10);
STA_TASTY_ASSERT(alive_flag == 1);
this->sleep(110);
alive_flag = 0;
// After at most 1000 ticks, the watchdog should have revived the thread.
this->sleep(2000);
STA_TASTY_ASSERT(alive_flag == 1);
STA_TASTY_ASSERT(tacos::Watchdog::instance()->getNumRestarts() == 1);
STA_TASTY_TERMINATE();
}
};
void onTastyInit()
{
tacos::addThread<Dummy>({0});
tacos::addThread<Observer>({0});
}
} // namespace tasty
}
#endif // TASTY_CASE_7

97
src/cases/case8.cpp Normal file
View File

@ -0,0 +1,97 @@
/*
* case8.cpp
*
* Created on: Jan 24, 2024
* Author: Dario
*/
/**
* Two threads get stuck in a deadlock and are restarted.
*/
#include <sta/tasty/config.hpp>
#ifdef TASTY_CASE_8
#include <sta/tacos.hpp>
#include <sta/tasty/utils.hpp>
#include <sta/tacos/watchdog.hpp>
#include <sta/rtos/mutex.hpp>
namespace sta
{
namespace tasty
{
static RtosMutex * mutexA;
static RtosMutex * mutexB;
class ThreadA : public tacos::TacosThread
{
public:
ThreadA()
: tacos::TacosThread{"A", osPriorityNormal}
{
}
void func() override
{
mutexA->acquire();
this->sleep(10);
mutexB->acquire();
}
};
class ThreadB : public tacos::TacosThread
{
public:
ThreadB()
: tacos::TacosThread{"B", osPriorityNormal}
{
}
void func() override
{
mutexB->acquire();
this->sleep(10);
mutexA->acquire();
}
};
class Observer : public tacos::TacosThread
{
public:
Observer()
: tacos::TacosThread{"Observer", osPriorityNormal}
{
}
void func() override
{
this->sleep(2000);
STA_TASTY_ASSERT(tacos::Watchdog::instance()->getNumRestarts() == 2);
STA_TASTY_TERMINATE();
}
};
void onTastyInit()
{
mutexA = new RtosMutex("MutA");
mutexB = new RtosMutex("MutB");
tacos::addThread<ThreadA>({0});
tacos::addThread<ThreadB>({0});
tacos::addThread<Observer>({0});
}
} // namespace tasty
}
#endif // TASTY_CASE_8

114
src/cases/case9.cpp Normal file
View File

@ -0,0 +1,114 @@
/*
* case8.cpp
*
* Created on: Jan 24, 2024
* Author: Dario
*/
/**
* Tests if threads are properly terminated and started because of state transitions. The thread status is updated accordingly and no memory leaks occur.
*/
#include <sta/tasty/config.hpp>
#ifdef TASTY_CASE_9
#include <sta/tacos.hpp>
#include <sta/tasty/utils.hpp>
#include <sta/rtos/debug/heap_stats.hpp>
namespace sta
{
namespace tasty
{
class ThreadA : public tacos::TacosThread
{
public:
ThreadA()
: tacos::TacosThread{"A", osPriorityNormal}
{
}
void func() override
{
osThreadYield();
}
};
class ThreadB : public tacos::TacosThread
{
public:
ThreadB()
: tacos::TacosThread{"B", osPriorityNormal}
{
}
void func() override
{
osThreadYield();
}
};
std::shared_ptr<ThreadA> thread_A;
std::shared_ptr<ThreadB> thread_B;
class Observer : public tacos::TacosThread
{
public:
Observer()
: tacos::TacosThread{"Observer", osPriorityNormal}
{
}
void func() override
{
size_t mallocs = sta::rtos::getNumAllocs();
STA_TASTY_ASSERT(thread_A->isRunning());
STA_TASTY_ASSERT(!thread_B->isRunning());
STA_TASTY_ASSERT(thread_B->getStatus() == tacos::ThreadStatus::STOPPED);
this->sleep(20);
tacos::setState(0, 1);
this->sleep(5);
STA_TASTY_ASSERT(!thread_A->isRunning());
STA_TASTY_ASSERT(thread_B->isRunning());
STA_TASTY_ASSERT(thread_A->getStatus() == tacos::ThreadStatus::STOPPED);
STA_TASTY_ASSERT(sta::rtos::getNumAllocs() == mallocs + 2);
tacos::setState(1, 0);
this->sleep(5);
STA_TASTY_ASSERT(thread_A->isRunning());
STA_TASTY_ASSERT(!thread_B->isRunning());
STA_TASTY_ASSERT(thread_B->getStatus() == tacos::ThreadStatus::STOPPED);
STA_TASTY_ASSERT(sta::rtos::getNumAllocs() == mallocs + 2);
tacos::setState(0, 1);
STA_TASTY_ASSERT(!thread_A->isRunning());
STA_TASTY_ASSERT(thread_B->isRunning());
STA_TASTY_ASSERT(thread_A->getStatus() == tacos::ThreadStatus::STOPPED);
STA_TASTY_ASSERT(sta::rtos::getNumAllocs() == mallocs + 2);
STA_TASTY_TERMINATE();
}
};
void onTastyInit()
{
thread_A = tacos::addThread<ThreadA>({0});
thread_B = tacos::addThread<ThreadB>({1});
tacos::addThread<Observer>({0, 1});
}
} // namespace tasty
}
#endif // TASTY_CASE_9

View File

@ -18,9 +18,9 @@ namespace sta
{
for (TastyCheck check : checks_)
{
blocking(
/*blocking(
check();
);
);*/
}
}
} // namespace tasty

View File

@ -29,6 +29,7 @@ namespace sta
void ToggleThread::func()
{
/**
blocking(
this->sleep(ticks_);
)
@ -37,6 +38,7 @@ namespace sta
uint16_t next = 1 - state;
sta::tacos::setState(state, next, lockout_);
*/
}
} // namespace tasty
} // namespace sta

View File

@ -16,6 +16,8 @@ namespace sta
void test_terminate()
{
STA_DEBUG_PRINTLN("[T]");
while (true) {}
}
} // namespace tasty
} // namespace sta