Search code examples
c++libuv

In libuv framework, when using two call back functions, why they affect each other


In some practices, I found when I use two handlers, for example, a uv_timer with an uv_idle of libuv, they may act not as I expected. I wrote a similar toy example here:

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <uv.h>

uv_idle_t idle;
uv_loop_t *loop;
uv_timer_t timer;

void cb2(uv_timer_t *handler)
{
    printf("bbb\n");
    sleep(1);
}

void cb(uv_idle_t *handler)
{
    printf("aaa\n");
    sleep(5);
    // while(1);
}

int main(int argc, char **argv)
{
    loop = new uv_loop_t;
    uv_loop_init(loop);
    uv_idle_init(loop, &idle);
    uv_timer_init(loop, &timer);
    uv_timer_start(&timer, cb2, 5, 1000);
    uv_idle_start(&idle, cb);

    // uv_idle_stop(&idle);
    printf("Now quitting.\n");
    uv_run(loop, UV_RUN_DEFAULT);
    while (1)
    {
        ;
    }
    uv_loop_close(loop);
    free(loop);
    return 0;
}

I ran it under linux. I want the timer run cb2 repeatly every 1000ms, at the same time, another idle function cb is also runing. They should be two irrelevant threads that do their own thing separatly. So I expect that they print the "aaa" for every about 2 seconds and print "bbb" for every 5 seconds.

However the fact is when I start them simultaneously, the "aaa" will choke the "bbb" output. a "bbb" only printed after an "aaa" printed. That is confused, because I though the framework is asychronous.

Why do these happen? and how should I use it properly? Thank you!


Solution

  • print the "aaa" for every about 2 seconds and print "bbb" for every 5 seconds

    You set the timer to fire repeatedly every 1000 ms:

    uv_timer_start(&timer, cb2, 5, 1000);
    

    You can't expect it to fire every 2 seconds when setting the timer to fire repeatedly every second.

    Regarding the uv_idle_t handle:

    Despite the name, idle handles will get their callbacks called on every loop iteration, not when the loop is actually “idle”.

    Then there's this:

    loop = new uv_loop_t;
    // ...
    free(loop);
    

    Never do this. If you create something with new you should delete it when done time it. You don't have to create it dyncamically here though, so just create an automatic variable.

    If you want two timed events, make them both so:

    #include <uv.h>
    
    #include <cstdio>
    
    void cb2(uv_timer_t* handler) {
        std::puts("bbb");
    }
    
    void cb(uv_timer_t* handler) {
        std::puts("aaa");
    }
    
    int main() {
        uv_loop_t loop;
        uv_loop_init(&loop);
    
        uv_timer_t timer_1;
        uv_timer_init(&loop, &timer_1);
        uv_timer_start(&timer_1, cb2, 0, 1000);
    
        uv_timer_t timer_5;
        uv_timer_init(&loop, &timer_5);
        uv_timer_start(&timer_5, cb, 0, 5000);
    
        uv_run(&loop, UV_RUN_DEFAULT);
    
        uv_loop_close(&loop);
    }
    

    Note that uv_loop_close will never be reached since there's nothing shutting down the event loop.