1

I am using emscripten to port a c++ project into the web, and web application that is going to interact with my C++ code is on NodeJs.

So, I am using Socket.io on Node.js, and I want to use it with my c++ code too, so I went with using a javascript library that uses socket.io code, however it does not seem to work.

I wrote this little example demonstrating this case:

#include <iostream>
#include <stdlib.h>
#include <stdio.h>
#include <emscripten.h>

int val = 0;

extern "C" 
{
    extern void send_board(char* flat_board);
    extern bool receive_board(char** _string_dest);
}

void one_iter()
{
    #ifdef SEND
    char* c = "test";

    std::cout << val << std::endl;

    if(val%1000 == 0){
        send_board(c);
    }
    val++;
    #else
    char* c;
    if(receive_board(&c)) std::cout << "Received:" << c << std::endl;
    #endif
}


int main()
{
    emscripten_set_main_loop(one_iter, 0, 1);
    return 0;
}

and

mergeInto(LibraryManager.library, {
      send_board: function(message) {
        socket.on('connect', function(){
            socket.emit('game_request_sender_pipeline', {
                message: "Hi",
            });
        }); 
      },

      receive_board: function(_string_dest_in_c){

        socket.on('connect', function(){
            socket.on('game_request_receiver_pipeline' , function (message)
            {
                var msg = "Other side : " + message.message;
                var buffer = Module._malloc(message.message.length + 1);
                Module.writeStringToMemory(msg, buffer);
                setValue(_string_dest_in_c, buffer, '*');
                return true;
            });
        });

        return false;
      },
    });

and I compiled with the following:

// for the sending test
em++ main.cpp --js-library path_to_js_library.js -o socket.html -DSEND=1
// for the receiving test
em++ main.cpp --js-library path_to_js_library.js -o socket.html

and in Node.Js server code, I have:

io.on('connection', function (socket) {

        socket.on('game_request_sender_pipeline' ,  function (message) 
        {      
            console.log("game_request_sender_pipeline on.");
            socket.broadcast.emit('game_request_receiver_pipeline', {
                message: message.message,
            });
            console.log("game_request_receiver_pipeline emitted.");
        });
});

The result is pretty weird, til I thought that it was not working, I cancel the nodejs server and relaunched it, and then the results popped up in the browsers' console.

4
  • one_iter() is synchronous, but receive_board seems asynchronous. Commented Apr 18, 2016 at 5:04
  • Should I implement callbacks to workaround the asynchronous nature of it? Commented Apr 18, 2016 at 10:24
  • I think so.emscripten_set_main_loop's 3rd parameter is true, one_iter is called many times. Commented Apr 18, 2016 at 12:19
  • Yes, because the real problem is happening in a graphical game, and I need the last arg to be true to simulate an infinite loop, correct me if I am wrong Commented Apr 18, 2016 at 12:25

1 Answer 1

2

It seems that the suggestions in the comment have sense to them.

emscripten_set_main_loop would simulate a synchronous behavior, however the calls from the javascript api are asynchronous due to socket.io

So to solve the issue, instead of using return statements and conditionally execute the code I want- whether that be on true or false- I thought of using callbacks.

The idea goes like this:

  1. In the main loop, there going to be a call to receive_board.
  2. receive_board is going to receive a success callback and a failure callback as a parameter. (The callbacks are C function)
  3. We are going to call the callbacks from c using Module.ccall.
  4. The callbacks actually contain the code I wanted to execute conditionally upon the receipt

In order to use ccall, we will have to use the keyword EMSCRIPTEN_KEEPALIVE in the function definition, and in order to avoid writing that keyword for each callback that is going to be defined, I decided to use it only for a one function that will call the callbacks.

extern "C"
{
 EMSCRIPTEN_KEEPALIVE void callback_invoker(void* fn_ptr, void* fn_arg)
 {
    // some kind of cast to the original function signature, and to the original fn_arg type
    (*fn_ptr)(fn_arg);
 }
}

And in the javascript side

mergeInto(LibraryManager.library, {
      receive_board: function(_string_dest_in_c, succcess_callback, failure_callback, s_cb_arg, f_cb_arg){

        socket.on('connect', function(){
            socket.on('game_request_receiver_pipeline' , function (message)
            {
                var msg = "Other side : " + message.message;
                var buffer = Module._malloc(message.message.length + 1);
                Module.writeStringToMemory(msg, buffer);
                setValue(_string_dest_in_c, buffer, '*');
                Module.ccal('callback_invoker', 'void', ['number', 'number'], [success_callback, s_cb_arg]);
                return;
            });
        });

        Module.ccal('callback_invoker', 'void', ['number', 'number'], [failure_callback, f_cb_arg]);
      },
    });

This way, I solved the aforementioned problem.


Inspired by this answer

Sign up to request clarification or add additional context in comments.

1 Comment

Very smart solution; didn't think about dynamically allocating memory from the JS side!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.