Search code examples
c++multithreadingdeadlock

function is not returning after creating a new class of an asynchronous timer


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


Solution

  • 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.