1
\$\begingroup\$

I would like to make the timer queue: 1. Each timer has id, time expired and callback to call 2. Queue stores timer and execute the callback of each timer whenever expired.

Here is my code and demo:

    #include <iostream>
    #include <thread>
    #include <chrono>
    #include <mutex>
    #include <functional>
    #include <list>
    #include <condition_variable>
    #include <ctime>

    // Helper function to print time
    void print_time()
    {
        std::chrono::time_point<std::chrono::system_clock> now = std::chrono::system_clock::now();
        std::chrono::system_clock::duration tp = now.time_since_epoch();
        tp -= std::chrono::duration_cast<std::chrono::seconds>(tp);
        std::time_t ttp = std::chrono::system_clock::to_time_t(now);
        tm t = *gmtime(&ttp);

        std::printf("[%04u-%02u-%02u %02u:%02u:%02u.%03u]: ", t.tm_year + 1900,
                t.tm_mon + 1, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec,
                static_cast<unsigned>(tp/std::chrono::milliseconds(1)));
    }

        typedef std::function<void(int)> TimerCallback;

    struct TimerItem {
        int id;
        TimerCallback cb;
        std::chrono::time_point<std::chrono::system_clock> until;
    };

    bool compareTimerItem(std::shared_ptr<TimerItem> lhs, std::shared_ptr<TimerItem> rhs)
    {
        return std::chrono::duration_cast<std::chrono::milliseconds>(lhs->until - rhs->until).count() < 0 ? true: false;
    }

 class TimerQueue {

public:

    virtual ~TimerQueue() {        m_thread.join(); }
    TimerQueue(const TimerQueue&) = delete;
    TimerQueue& operator=(TimerQueue&) = delete;

    static std::shared_ptr<TimerQueue> getInstance() {
        static std::shared_ptr<TimerQueue> instance(new TimerQueue);
        return instance;
    }

    void setTimer(int id, long milliseconds, TimerCallback cb)
    {
        print_time();
        std::cout << __FUNCTION__ << "id: " << id << "\ttimer: " << milliseconds << std::endl;

        std::unique_lock<std::mutex> lk_cd(m_timerListMtx);
        std::chrono::time_point<std::chrono::system_clock> now = std::chrono::system_clock::now();
        std::chrono::time_point<std::chrono::system_clock> until = now + std::chrono::milliseconds(milliseconds);
        std::shared_ptr<TimerItem> item = std::make_shared<TimerItem>();
        item->id = id;
        item->cb = cb;
        item->until = until;
        {
            if (isTimerExist(id) == false)
            {
                std::lock_guard<std::mutex> lk(m_queueMtx);
                m_timerList.push_back(item);
                m_timerList.sort(compareTimerItem);
                TimerQueue::m_timer_ready = true;
            }
        }
        lk_cd.unlock();
        m_cvTimerList.notify_one();
    }

    void removeTimer(int id)
    {
        std::lock_guard<std::mutex> lk(m_queueMtx);

        std::shared_ptr<TimerItem> item;
        std::list<std::shared_ptr<TimerItem>>::iterator it = m_timerList.begin();
        while (it != m_timerList.end())
        {
            item = *it;
            if (item->id == id)
            {
                it = m_timerList.erase(it);
                break;
            }
            else
            {
                ++it;
            }
        }
    }

    bool isTimerExist(int id)
    {
        std::lock_guard<std::mutex> lk(m_queueMtx);
        bool result = false;
        std::shared_ptr<TimerItem> item;
        std::list<std::shared_ptr<TimerItem>>::iterator it = m_timerList.begin();
        while (it != m_timerList.end())
        {
            item = *it;
            if (item->id == id)
            {
                result = true;
                break;
            }
            else
            {
                ++it;
            }
        }
        return result;
    }

    static bool m_timer_ready;

private:
    TimerQueue() {
        std::cout << "Timer Queue constructor\n";
        m_thread = std::thread(&TimerQueue::start, this);
    }

    void start()
    {
        std::cout << "Start thread\n";
        std::shared_ptr<TimerItem> item;
        while (true)
        {
            if (m_timerList.empty() == true)
            {
//                std::cout << "timer list is empty\n";
                std::unique_lock<std::mutex> lk(m_timerListMtx);
                m_cvTimerList.wait(lk, []{return TimerQueue::m_timer_ready;});
            }
            else
            {
                {
                    std::lock_guard<std::mutex> lk(m_queueMtx);
                    item = m_timerList.front();
                }
                {
                    std::chrono::time_point<std::chrono::system_clock> now = std::chrono::system_clock::now();
                    auto count_down = std::chrono::duration_cast<std::chrono::milliseconds>(item->until - now);
//                    std::cout << "Count down: " << count_down.count() << std::endl;
                    if (count_down.count() <= 0)
                    {
                        item->cb(item->id);
                        {
                            std::lock_guard<std::mutex> lk(m_queueMtx);
                            m_timerList.pop_front();
                        }
                    }
                }
            }
        }
    }

    std::list<std::shared_ptr<TimerItem>> m_timerList;
    std::mutex m_timerListMtx;
    std::mutex m_queueMtx;
    std::condition_variable m_cvTimerList;
    std::thread m_thread;
};

void foo(int num)
{
    print_time();
    std::cout << " Foo: " << num << std::endl;
}

bool TimerQueue::m_timer_ready = false;

int main(int argc, char *argv[])
{
    long ms = 1000;
    TimerCallback cb = foo;

    for (int i = 0; i < 10; ++i)
    {
        TimerQueue::getInstance()->setTimer(i, ms * i, cb);
        std::this_thread::sleep_for(std::chrono::milliseconds(1000));
    }

    return 0;
}
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.