Duetto (C++ for the Web) 0.9.4: Async RPCs, Promises, JavaScript interoperability


logo_duetto_quadrato_192

Around a month has passed since our talk at mloc.js. There we had a great oppor­tu­nity to talk about the tech­nol­ogy behind the duetto C++ com­piler for the Web. We also made our first pub­lic announce­ment of our promise based sup­port for asyn­chro­nous type safe RPC, which fits in our vision of C++ as the plat­form lan­guage for the Web, both on the client and on the server side.

Today, we announce the new, feature-packed release of Duetto — 0.9.4! This time, we really splurged, and added some qual­ity fea­tures that we’re sure you’ll find interesting:

  • Ini­tial sup­port for promise-based async RPC

  • Strong inter­op­er­abil­ity with JavaScript

    • __asm__ to exe­cute JavaScript from C++ code

    • [[jsex­port]] attribute to use C++ objects from JavaScript

  • Sup­port for C++ mutex/atomic/thread headers

As usual, you can find the full source code of the release here, plus binary pack­ages for Debian/Ubuntu on our PPA, a binary setup for win­dows and a binary archive for Mac OS X here.

Asyn­chro­nous RPC using promises

With duetto you can “tag” a method to let the com­piler know that the code should be com­piled for the server side. The com­piler also takes care of gen­er­at­ing RPC boil­er­plate includ­ing type safe seri­al­iza­tion of para­me­ters and dese­ri­al­iza­tion of the return value. While the basic model is syn­chro­nous, you can take advan­tage of promises to use fully type safe asyn­chro­nous RPC in duetto. See e.g. the fol­low­ing code:

// Pending requests for messages
vector<Promise<string>*> pendingRequests;
Promise<string>* getChatMessageRemote() [[server]]
{
     auto ret=new Promise<string>();
     // Store the new promise, you can use it later to complete
     // the request in asynchronous manner
     pendingRequests.push_back(ret);
     return ret;
}
void sendMessageRemote(const string& str) [[server]]
{
     for (auto p: pendingRequests)
          p->done(str);
     pendingRequests.clear();
}
void messageHandler(const string& newMessage) [[client]]
{
     client::console.log("Message received",newMessage.c_str());
}
void webMain() [[client]]
{
     // First wait for messages
     auto promise = getChatMessageRemote();
     // Add a callback to the promise
     promise->then(messageHandler);
     // Tell the promise that no more callbacks will be added
     promise->complete();
     // Now send a message, it will be echoed back
     sendMessageRemote("Test message");
}

The code is a sim­ple but work­ing exam­ple of a multi-user chat appli­ca­tion. The getChatMes­sageR­e­mote method returns a promise imme­di­ately after being called, with­out wait­ing for the server answer. On the server side we cre­ate a promise and return it. We also need to store it some­where to even­tu­ally ful­fill the promise by send­ing a value.

The client code needs to set a call­back to receive the value when the promise will be even­tu­ally ful­filled. Later on the client send a chat mes­sage using the sendMes­sageR­e­mote server method. This method is syn­chro­nous and will return only after the server side exe­cu­tion is com­pleted. The server echoes the mes­sage back to all con­nected clients by ful­fill­ing all pend­ing promises.

Strong JS interoperability

This release of duetto includes two fea­tures that pro­vide a supe­rior inte­gra­tion with man­u­ally writ­ten JavaScript code: the [[jsex­port]] class attribute and __asm__ sup­port for inline JavaScript.

[[jsex­port]] class attribute

We have intro­duced pre­lim­i­nary sup­port for a new attribute: [[jsex­port]]. This attribute is designed to be applied on C++ classes, doing so will cause make the class avail­able to man­u­ally writ­ten JavaScript code. The fol­low­ing exam­ple shows its typ­i­cal use:

class [[jsexport]] ExportExample
{
private:
    float a;
    int b;
public:
     ExportExample(float _a, int _b):a(_a),b(_b)
     {
     }
     void testMethod()
     {
          client::console.log(“Float value is”, a);
     }
};

Dur­ing com­pi­la­tion duetto will gen­er­ate bridge code to expose the Expor­tEx­am­ple C++ class to JavaScript code. You can then use the class when writ­ing JS code

// Using jsexport-ed classes from JavaScript code
var cppobj = new ExportExample(0.1, 42);
cppobj.testMethod();

The [[jex­port]] attribute places some lim­i­ta­tions on the class. In par­tic­u­lar, the object must have a triv­ial destruc­tor and no over­loaded meth­ods. More­over oper­a­tors can’t be exported to JavaScript. Hav­ing a triv­ial destruc­tor is nec­es­sary to make sure that the reg­u­lar Garbage Col­lec­tion mech­a­nism is suf­fi­cient to reclaim the object. We plan to expand this fea­ture in the future to reduce any lim­i­ta­tions as much as possible.

Inline JavaScript using __asm__

This release also adds sup­port for insert­ing arbi­trary JavaScript code inside C++ code using the __asm__ func­tion­al­ity. Our design choice has been to put no limit on what you can do inside __asm__, includ­ing break­ing the pro­gram, so be care­ful when tak­ing advan­tage of this capability.

void webMain()
{
      __asm__(“alert(‘JavaScript code inside __asm__’);”);
}

At the moment only this sim­ple form of JS code inser­tion is sup­ported. It’s not cur­rently pos­si­ble to pass vari­ables from sur­round­ing C++ code or return a value from __asm__, but we will extend this sup­port in the future.

Sup­port for C++ mutex/atomic/thread headers

Some C++ func­tion­al­i­ties, espe­cially the ones related to multi-threading, do not have a direct map­ping on the browser plat­form. That said, we want to pro­vide as much com­pat­i­bil­ity as pos­si­ble with exist­ing C++ code to reduce the effort when using duetto for porting.

As of duetto 0.9.4, we sup­port the fol­low­ing C++ thread­ing primitives:

  • mutex: Imple­mented as plain coun­ters, mutexes can­not block exe­cu­tion but report an error if a non-recursive mutex is acquired twice.

  • atomic: Imple­mented as plain inte­gers, since the browser is not actu­ally con­cur­rent there is no real sup­port for atomic operations.

  • thread: Mod­ern browsers sup­port Web­Work­ers, but the thread­ing model does not map to the one expected by C++ threads. We chose to make the user aware that C++ threads are not sup­ported by adding an explicit error mes­sage when the thread header is included in code com­piled with duetto.

 Fol­low us on @leaningtech, Face­book and at www.leaningtech.com for updates.

  • Clau­dio Russo

    maybe bet­ter to change the key­word to __js__ and reserve the __asm__ for llvm asm code, instead?

  • apig­notti

    I’m not really sure it will be coher­ent with the seman­tics of __asm__, which is sup­posed to work at the “machine code” level. On the browser plat­form the machine code is JS. Still, I will think about this, maybe it makes sense to add a new __llvm__ con­struct to work at the LLVM level.