Daan Pape
Daan Pape

Reputation: 1140

Simple timer event loop

The usecase

I'm writing a C program which needs multiple timers. I had first written a fast prototype which started a pthread for every timer. Just a simple while-loop with a sleep command because 1 second resolution is enough.

But with more than 10 timers it's not a very efficient nor production grade code quality. Therefore I wanted to use an eventloop. I've read several times about libuv and thought to give it a try.

So my idea was simple. Have 1 timer thread which runs the timer eventloop and add and remove timers on the fly. The timers are very simple countdown timers which execute a function pointer when they reach 0 and which can be canceled. So no repetition is needed.

The problem

I think the real problem here is documentation, the libuv docs simply aren't very clear on how to achieve this. So I think the code I currently have is rubbish. Let me walk you through it.

At the beginning of my program I'm starting a phtread with the following entrypoint:

static void* _uv_loop_thread_entry_point(void *args)
{
    /* Initialize the timer event loop */
    timer_event_loop = malloc(sizeof(uv_loop_t));
    uv_loop_init(timer_event_loop);

    /* Keep running the eventloop */
    while(uv_run(timer_event_loop, UV_RUN_DEFAULT) == 0) {
        /* Wait 1 second for new handles */
        sleep(1);
    };

    /* The timer event loop has stopped, free all resources */
    uv_loop_close(timer_event_loop);
    free(timer_event_loop);

    return NULL;
}

Than whenever I want to use a timer I would do the following:

uv_timer_init(timer_event_loop, uv_timer);
uv_timer_start(uv_timer, timeout_ms, ??);

Now I have several questions:

  1. How to use the timer without repetition
  2. Where do I set the callback function for this timer
  3. How can I know how much time is left in this timer

I can really use some help on this subject.

Update 1

Ok, I'm making some progress and it basically works. The libuv event loop runs in a separate thread. And to already answer some of my questions:

  1. When the last argument in uv_timer_start is 0 the timer will not repeat itself.

  2. The callback for the timer timeout event is the second argument of uv_timer_start

I don't yet know the best answer to my third question. But for now I keep track of the time when my timer has started and the current time and take the difference. I than substract the difference from the total time of the timer to know how long it will take before the timer will end.

I would still like to know if my uv_run implementation is correct.

Kind regards, Daan

Upvotes: 1

Views: 2256

Answers (1)

saghul
saghul

Reputation: 2010

You are playing in undefined behavior territory. libuv is not thread-safe see the docs here So while running the loop in a thread is ok, creating a timer in another thread while the loop is running is not.

You can still do it by using a uv_async_t handle and a semaphore: uv_async_send is thread-safe, so you would call it from the outside, stop the loop and signal a semaphore. The calling thread would wait until the semaphore is signaled. At this point the loop is stopped, so it's ok to create a new timer and add it.

There is no API to know how much time a timer has left.

If all you need is a loop to control some timers libuv might be overkill. You could use timerfd if you're on Linux, or a hand-built event loop which only does timers on top of select for example.

Upvotes: 2

Related Questions