2

I'm using uWebSockets in my C++ project, where I have my own custom event loop. It's a while loop, with a variable delay between each execution. It looks something like this:

while (true) {
    std::this_thread::sleep_for (variableTime);
    // Execute logic
}

I've previously been using another thread to execute logic, but I want to integrate the uWebSockets loop with my loop. Something like this:

#include <iostream>
#include <uWS/uWS.h>

using namespace std;

int main () {
    uWS::Hub h;

    h.onMessage([](uWS::WebSocket<uWS::SERVER> *ws, char *message, size_t length, uWS::OpCode opCode) {
        ws->send(message, length, opCode);
    });

    if (h.listen(3000)) {
        h.run();
    }

    while (true) {
        std::this_thread::sleep_for (variableTime);
        h.getMessages(); // <-- doesn't call `onMessage` until this is executed
        // Execute logic
    }
}

How would I go about doing this?

Luke
  • 2,038
  • 3
  • 22
  • 46

2 Answers2

3

Today I had the same question. After some digging around the source code I think I found the answer.

It seems what you're looking for is a recently added (https://github.com/uNetworking/uWebSockets/pull/762) Node::Poll (Hub inherits Node) function that is non blocking for use within the programs main loop. I think it should work exactly as the getMessages you had in mind.

Nicolás Abram
  • 333
  • 3
  • 12
  • 1
    This is exactly what I was looking for! I've worked around it with parallel threads, so I kind of wish they had of added it earlier :P but better late than never. Anyway thanks so much! – Luke Mar 04 '18 at 10:17
2

Up until v0.14.8, the answer above worked, but since then the functionality disappeared, there was a major refactoring of uWebSockets.

Anyway, now I'm running v20.14.0, and I wanted this functionality, and I resorted to a hack, but it's working well enough for me. This assumes that uWebSockets is using LIBUV:

// 1) create the app, but don't call app.run()
// auto app = uWS::SSLApp({ ... }).listen(3000, [](auto* listen_socket) {});

// 2) extract uv_loop from the Loop
// check us_loop_t from https://github.com/uNetworking/uSockets/blob/master/src/internal/eventing/libuv.h
struct dummy_us_loop_t
{
    alignas(LIBUS_EXT_ALIGNMENT) char data[104];

    uv_loop_t* uv_loop;
    int        is_default;
    void*      uv_pre;
    void*      uv_check;
};

auto uv_loop = ((dummy_us_loop_t*)uWS::Loop::get())->uv_loop;

// 3) optionally add uv_loop hooks, for ex and idle callback:
if (true)
{
    struct MyStruct
    {
        int x = 0;
        int y = 0;
    } myStruct = { .x = 5, .y = 9 };

    uv_idle_t idler = { .data = &myStruct };
    uv_idle_init(uv_loop, &idler);
    uv_idle_start(&idler, [](uv_idle_t* handle) {
        static int counter;
        const auto& obj = (MyStruct*)handle->data;
        std::cout << "idle: " << counter << " " << obj->x << " " << obj->y << std::endl;
        if (++counter > 100000) uv_idle_stop(handle);
    });
}

// 4) now you can just run the uv_loop step by step, instead of app.run()
while (true)
{
    // your custom code here
    std::cout << ".";

    uv_run(uv_loop, UV_RUN_NOWAIT);
}

Another way is to use pre and post handlers, but those will only be run when an event occurs, so it's not good for something in real time but good for frequent checks:

uWS::Loop::get()->addPostHandler((void*)200, [](uWS::Loop* loop) { std::cout << "post"; });
uWS::Loop::get()->addPreHandler((void*)200, [](uWS::Loop* loop) { std::cout << "pre"; });
Octo Poulos
  • 523
  • 6
  • 5