c++libuvsingle-threadedlibsourcey

libuv - Limiting callback rate of idle event without blocking thread without multithreading


I'm using libsourcey which uses libuv as its underlying I/O networking layer. Everything is setup and seems to run (haven't testen anything yet at all since I'm only prototyping and experimenting). However, I require that next to the application loop (the one that comes with libsourcey which relies on libuv's loop), also calls an "Idle function". As it is now, it calls the Idle CB on every cycle which is very CPU consuming. I'd need a way to limit the call-rate of the uv_idle_cb without blocking the calling thread which is the same the application uses to process I/O data (not sure about this last statement, correct me if i'm mistaken).

The idle function will be managing several different aspects of the application and it needs to run only x times within 1 second. Also, everything needs to run one the same thread (planning to upgrade an older application's network infrastructure which runs entirely single-threaded).

This is the code I have so far which also includes the test I did with sleeping the thread within the callback but it blocks everything so even the 2nd idle cb I set up has the same call-rate as the 1st one.

struct TCPServers
{
    CTCPManager<scy::net::SSLSocket> ssl;
};

int counter = 0;
void idle_cb(uv_idle_t *handle)
{
    printf("Idle callback %d TID %d\n", counter, std::this_thread::get_id());

    counter++;

    std::this_thread::sleep_for(std::chrono::milliseconds(1000 / 25));
}

int counter2 = 0;
void idle_cb2(uv_idle_t *handle)
{
    printf("Idle callback2 %d TID %d\n", counter2, std::this_thread::get_id());

    counter2++;

    std::this_thread::sleep_for(std::chrono::milliseconds(1000 / 50));
}

class CApplication : public scy::Application
{
public:
    CApplication() : scy::Application(), m_uvIdleCallback(nullptr), m_bUseSSL(false)
    {}

    void start()
    {
        run();

        if (m_uvIdleCallback)
            uv_idle_start(&m_uvIdle, m_uvIdleCallback);

        if (m_uvIdleCallback2)
            uv_idle_start(&m_uvIdle2, m_uvIdleCallback2);
    }

    void stop()
    {
        scy::Application::stop();

        uv_idle_stop(&m_uvIdle);

        if (m_bUseSSL)
            scy::net::SSLManager::instance().shutdown();
    }

    void bindIdleEvent(uv_idle_cb cb)
    {
        m_uvIdleCallback = cb;
        uv_idle_init(loop, &m_uvIdle);
    }

    void bindIdleEvent2(uv_idle_cb cb)
    {
        m_uvIdleCallback2 = cb;
        uv_idle_init(loop, &m_uvIdle2);
    }

    void initSSL(const std::string& privateKeyFile = "", const std::string& certificateFile = "")
    {
        scy::net::SSLManager::instance().initNoVerifyServer(privateKeyFile, certificateFile);
        m_bUseSSL = true;
    }

private:
    uv_idle_t m_uvIdle;
    uv_idle_t m_uvIdle2;
    uv_idle_cb m_uvIdleCallback;
    uv_idle_cb m_uvIdleCallback2;
    bool m_bUseSSL;
};

int main()
{
    CApplication app;
    app.bindIdleEvent(idle_cb);
    app.bindIdleEvent2(idle_cb2);
    app.initSSL();
    app.start();

    TCPServers srvs;
    srvs.ssl.start("127.0.0.1", 9000);

    app.waitForShutdown([&](void*) {
        srvs.ssl.shutdown();
    });

    app.stop();

    system("PAUSE");
    return 0;
}

Thanks in advance if anyone can help out.


Solution

  • Solved the problem by using uv_timer_t and uv_timer_cb (Hadn't digged into libuv's doc yet). CPU usage went down drastically and nothing gets blocked.