I have a class that is creating an asynchronous timer that should run on the background. I have created a timer class that creates an object with its individual thread and should run in the background. On top of that I have two functions CreateTimer and DeleteTimer which will be used to access the timer class objects. My intention is to call the CreateTimer and DeleteTimer for multiple threads as well. I have the following code:
#include <condition_variable>
#include <chrono>
#include <vector>
#include <algorithm>
#include <iostream>
#include <thread>
#include <atomic>
typedef struct
{
uint32_t msgId;
uint32_t param1;
uint32_t param2;
}THRD_EVENT_T;
enum THRD_EVENTID_T
{
THRD_MSGID_TIMER_FIRST,
THRD_STATEA,
THRD_STATEB,
THRD_STATEC,
THRD_MSGID_TIMER_LAST
};
std::mutex m;
void func_(THRD_EVENTID_T timerId_, int32_t pipeId_ )
{
std::cout<<"done\n";
}
class Timer
{
public:
Timer(THRD_EVENTID_T timerId, int32_t pipeId, uint32_t msPeriod)
: timerId_(timerId)
, pipeId_(pipeId)
, msPeriod_(msPeriod)
{
}
Timer(const Timer &obj) // copy constructor
{
timerId_ = obj.timerId_;
pipeId_ = obj.pipeId_;
msPeriod_ = obj.msPeriod_;
}
// Timer()
// {
// }
Timer() = delete;
Timer(Timer&&) = default;
~Timer()
{
if (thread_.joinable())
thread_.join();
}
void start()
{
std::cout<<"starting the timer\n";
thread_ = std::thread
{
[this]()
{
std::unique_lock<std::mutex> lck(mutex_);
cv_.wait_for(lck, msPeriod_, [this]() { return stop_waiting_.load(); });
if (not stop_waiting_)
func_(timerId_, pipeId_);
}
};
}
void stop()
{
stop_waiting_.store(true);
std::cout<<"Stopping the timer\n";
cv_.notify_one();
}
THRD_EVENTID_T GetTimerID()
{
return timerId_;
}
private:
std::chrono::milliseconds msPeriod_;
THRD_EVENTID_T timerId_;
int32_t pipeId_;
std::thread thread_;
std::condition_variable cv_;
std::mutex mutex_;
std::atomic<bool> stop_waiting_{false};
};
std::vector<Timer> tmrclass;
int32_t CreateTimer( const THRD_EVENTID_T timerId, const int32_t pipeId, const uint32_t msPeriod )
{
std::cout<<"Creating the timer\n";
std::unique_lock<std::mutex> lock(m);
Timer c (timerId, pipeId, msPeriod);
tmrclass.emplace_back(c);
c.start();
std::cout<<"exiting the createtimer function\n";
std::cout<<"returning....\n";
return 0;
}
int32_t Delete_Timer( const THRD_EVENTID_T timerId)
{
int32_t ret = -1;
std::cout<<"Deleting the timer function\n";
std::unique_lock<std::mutex> lock(m);
for (auto &f : tmrclass)
{
if(f.GetTimerID() == timerId)
{
std::cout<<"found the timer to delete\n";
f.stop();
break;
}
else
{
ret = -1;
}
}
return ret;
}
int main(int argc, const char *argv[])
{
THRD_EVENTID_T timerId;
timerId = THRD_STATEA;
int32_t pipeId = 0;
uint32_t msPeriod = 5000;
CreateTimer(timerId, pipeId, msPeriod);
std::cout<<"Have I exited the resource\n";
THRD_EVENTID_T timerId2;
timerId2 = THRD_STATEB;
int32_t pipeId2 = 1;
uint32_t msPeriod2 = 1000;
CreateTimer(timerId2, pipeId2, msPeriod2);
Delete_Timer(timerId);
return 0;
}
When I run it the code isn't returning from the CreateTimer
function it ends up being stuck after printing out returning....
I cant figure out why it is not returning from the CreateTimer
function
It's because of the destruction of the c
object, which attempts to join
the thread. It will block until the thread is finished. This causes a deadlock because there's no-one to tell the thread to exit.
For a truly independent timer you need the thread to be detached. Or you need to use some other design (single-threaded event-loops are commonly used). Or use OS-native timers instead.