Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C++

Simple Timer

5.00/5 (1 vote)
15 Mar 2019MIT 3.6K  
A simple timer

Jonathan Boccara over at Fluent{C++} made a post a while ago titled A Simple Timer in C++. I felt things could be done… differently 😉 so I decided to write my own version of the timer code.

First, I felt there’s no need to actually instantiate timer objects; a simple function call to set_timeout or set_interval from namespace timer should be sufficient.
Second, I didn’t like the way cancellation was done. Single stop call interrupted all intervals and timeouts. How about a cancellation event per set_timeout or set_interval call?
Finally, I wanted the set_timeout and set_interval functions to accept any callable with any number of arguments.
That’s exactly how I designed my interface.

Usage Example

C++
#include <iostream>
#include <mutex>
#include "timer.h"
using namespace std;

mutex cout_lock;
#define trace(x) { scoped_lock<mutex> lock(cout_lock); cout << x << endl; }

int main(int argc, char** argv)
{
    auto e1 = timer::set_timeout(1s, []() { trace("timeout"); });
    auto e2 = timer::set_timeout(6s, []() { trace("canceled timeout"); });

    auto e3 = timer::set_interval(1s, []() { trace("interval"); });
    auto e4 = timer::set_interval(6s, []() { trace("canceled interval"); });

    trace("waiting 5s...");
    this_thread::sleep_for(5s);

    e2->signal();
    e4->signal();

    trace("waiting 5s...");
    this_thread::sleep_for(5s);

    return 1;
}

Program output:

waiting 5s…
timeout
interval
interval
interval
interval
waiting 5s…
interval
interval
interval
interval
interval
Program ended with exit code: 1

timer.h

C++
#pragma once

#include <thread>
#include <memory>
#include "event.h"

namespace timer
{
    template<typename D, typename F, typename... Args>
    std::shared_ptr<manual_event> set_timeout(D d, F f, Args&&... args)
    {
        auto event = std::make_shared<manual_event>();
        std::thread([=]()
        {
            if(event->wait_for(d)) return;
            f(args...);
        }).detach();
        return event;
    }

    template<typename D, typename F, typename... Args>
    std::shared_ptr<manual_event> set_interval(D d, F f, Args&&... args)
    {
        auto event = std::make_shared<manual_event>();
        std::thread([=]()
        {
            while(true)
            {
                if(event->wait_for(d)) return;
                f(args...);
            }
        }).detach();
        return event;
    }
}

Updated event.h

C++
#pragma once

#include <mutex>
#include <condition_variable>

class manual_event
{
public:
    explicit manual_event(bool signaled = false) noexcept
    : m_signaled(signaled) {}

    void signal() noexcept
    {
        {
            std::unique_lock<std::mutex> lock(m_mutex);
            m_signaled = true;
        }
        m_cv.notify_all();
    }

    void wait() noexcept
    {
        std::unique_lock<std::mutex> lock(m_mutex);
        m_cv.wait(lock, [&](){ return m_signaled != false; });
    }

    template<typename T>
    bool wait_for(T t) noexcept
    {
        std::unique_lock<std::mutex> lock(m_mutex);
        return m_cv.wait_for(lock, t, [&](){ return m_signaled != false; });
    }

    template<typename T>
    bool wait_until(T t) noexcept
    {
        std::unique_lock<std::mutex> lock(m_mutex);
        return m_cv.wait_until(lock, t, [&](){ return m_signaled != false; });
    }

    void reset() noexcept
    {
        std::unique_lock<std::mutex> lock(m_mutex);
        m_signaled = false;
    }

private:
    bool m_signaled = false;
    std::mutex m_mutex;
    std::condition_variable m_cv;
};

License

This article, along with any associated source code and files, is licensed under The MIT License