Abhinav Gauniyal Abhinav Gauniyal - 1 month ago 7
C++ Question

add another timer on already running loop

Given the following program -

#include <iostream>
#include <uv.h>

int main()
{
uv_loop_t loop;
uv_loop_init(&loop);

std::cout << "Libuv version: " << UV_VERSION_MAJOR << "."
<< UV_VERSION_MINOR << std::endl;

int r = 0;

uv_timer_t t1_handle;
r = uv_timer_init(&loop, &t1_handle);
uv_timer_start(&t1_handle,
[](uv_timer_t *t) { std::cout << "Timer1 called\n"; }, 0, 2000);

uv_run(&loop, UV_RUN_DEFAULT);

// second timer
uv_timer_t t2_handle;
r = uv_timer_init(&loop, &t2_handle);
uv_timer_start(&t2_handle,
[](uv_timer_t *t) { std::cout << "Timer2 called\n"; }, 0, 1000);

uv_loop_close(&loop);
}


The second timer handle is never run on the loop, since the loop is already running, and "Timer2 called" is never printed. So I tried stopping the loop temporarily after running it and then adding the second timer -

....
uv_run(&loop, UV_RUN_DEFAULT);

// some work

uv_stop(&loop);
// now add second timer
uv_run(&loop, UV_RUN_DEFAULT); // run again
....


But this again didn't work, probably because the later lines won't be executed after 1st loop starts running with an repeating timer. So how should I add a new timer handle to already running uvloop?

Answer

You are right that loop needs to be stopped before it can register a new handle. It cannot be achieved by calling uv_stop function right after uv_run, because uv_run needs to return first. It can be achieved for example by stopping it using a handle callback. Here is quite silly example of how it can be done using the existing Timer1 handle. It stops the loop exactly one time on the first run.

#include <iostream>
#include <uv.h>

int main() {
  uv_loop_t loop;
  uv_loop_init(&loop);

  std::cout << "Libuv version: " << UV_VERSION_MAJOR << "." << UV_VERSION_MINOR
            << std::endl;

  int r = 0;

  uv_timer_t t1_handle;
  r = uv_timer_init(&loop, &t1_handle);
  *(bool *)t1_handle.data = true; // need to stop the loop
  uv_timer_start(&t1_handle,
                 [](uv_timer_t *t) {
                   std::cout << "Timer1 called\n";
                   bool to_stop = *(bool *)t->data;
                   if (to_stop) {
                     std::cout << "Stopping loop and resetting the flag\n";
                     uv_stop(t->loop);
                     *(bool *)t->data = false; // do not stop the loop again
                   }
                 },
                 0, 2000);
  uv_run(&loop, UV_RUN_DEFAULT);
  std::cout << "After uv_run\n";

  // second timer
  uv_timer_t t2_handle;
  r = uv_timer_init(&loop, &t2_handle);
  uv_timer_start(&t2_handle,
                 [](uv_timer_t *t) { std::cout << "Timer2 called\n"; }, 0,
                 1000);
  std::cout << "Start loop again\n";
  uv_run(&loop, UV_RUN_DEFAULT);

  uv_loop_close(&loop);
}

So the output is

Libuv version: 1.9
Timer1 called
Stopping loop and resetting the flag
After uv_run
Start loop again
Timer2 called
Timer2 called
Timer1 called
Timer2 called
Timer2 called
Timer1 called