FireBreath Tips: Asynchronous Javascript Calls

December 1, 2010 8 Comments by Richard

Never block a Javascript call!

One cardinal rule of browser plugins is that you should never block the thread when processing a method or property call from Javascript.  In FireBreath, that means that any method or property on your JSAPI must never block, but should return in a timely manner.

The reason for this is that Javascript is effectively not a multi-threaded language. The way that Javascript handles setTimeout and setInterval varies, but from the perspective of your plugin, all JSAPI calls come in on the same thread — the UI thread. If you block on a JSAPI call the whole web browser will lock up until you finish.

In the programming circles that I hang out in, this is considered a Bad Thing.

However, there are often cases when you may need to perform some operation that is inherently blocking — such as searching the filesystem, processing a large file, or making a network request. There are doubtless several ways you could deal with this, but perhaps the easiest is to run the blocking operation on a new thread, and then call back into Javascript when you’re done.

If you think about it, this is why AJAX calls are all asynchronous.

FB::JSObjectPtr to the rescue!

One of the types supported by FireBreath is FB::JSObject. This represents any type of Javascript object that is passed in — so basically anything other than a primitive datatype. This includes arrays, raw objects (key : value stores), and functions as well as other things like DOM objects.

FB:JSObject has a method InvokeAsync that works the same way as Invoke would on a normal JSAPI object except that the call is asynchronous, so it doesn’t have a return value and thus doesn’t wait for the call to complete before returning. If we want, we can even pass back a reference to the current API object in the callback. To call a function (instead of a method on an object), we pass an empty string in for the method name when we call Invoke or InvokeAsync.

// Say we have a FB::JSObjectPtr callback
callback->InvokeAsync("", FB::variant_list_of(shared_from_this()));

Creating a thread

FireBreath relies on the boost thread library; this means that it is quite simple to create a new thread, particularly to run something minor. Keep in mind that my purpose isn’t to talk about proper techniques for multithreaded programming, so you’ll have to worry about things like making sure that your threads have all ended before shutting down and so forth on your own.

With boost thread, basically you just create a function that will be run on the other thread and use boost::bind to provide that method object (with a shared_from_this to the object instance to keep it from going away while the thread is running) to the thread constructor. Edit 1/19/2010: Someone rightly pointed out that using shared_from_this may not be (in fact usually is not) optimal. If you want to stop the thread when the JSAPI object goes away, you should pass “this” instead of shared_from_this. The code below has been updated to reflect that.

    boost::thread t(boost::bind(&MyPluginAPI::doSomethingTimeConsuming_thread,
         this, num, callback));

Putting it together

bool MyPluginAPI::doSomethingTimeConsuming( int num, FB::JSObjectPtr &callback )
{
    boost::thread t(boost::bind(&MyPluginAPI::doSomethingTimeConsuming_thread,
         this, num, callback));
    return true; // the thread is started
}

void MyPluginAPI::doSomethingTimeConsuming_thread( int num, FB::JSObjectPtr &callback )
{
    // Do something that takes a long time here
    int result = num * 10;
    callback->InvokeAsync("", FB::variant_list_of(shared_from_this())(result));
}

As you can see, when doSomethingTimeConsuming is called it accepts a callback. It creates a new thread and gives it the callback function.

Synchronous calls

It should also be noted that calls to Invoke on a FB::JSObject will also be threadsafe — they can be called from any thread. Keep in mind that a mutex and signal are being used behind to scenes to block until after the call is made to Javascript on the main thread, so there will be a performance hit for this.

8 Comments

  1. Zecapistolas
    6 years ago

    Hello, thanks for this tutorial….
    But I have some questions, because I cannot implement this on my project!

    – On your examples, where is the function ‘doSomethingTimeConsuming_thread’ ?
    – What is ‘shared_from_this()’ ? I think I have problems with this, because I have the follow error “T* boost::shared_ptr::operator->() const [with T = FB::JSObject]: Assertion `px != 0′ failed.”
    – What function I need to call, “bool MyPluginAPI::doSomethingTimeConsuming” or “void MyPluginAPI::doSomethingTimeConsuming” from for example JavaScript?

    Regards, ZeCarlos

  2. taxilian
    6 years ago

    Hehe. Good call! Sorry, I accidently labeled both functions the same.

    shared_from_this() is a method of FB::JSAPI that returns a FB::JSAPIPtr to the current object. It cannot be called during the constructor or destructor. From javascript you’ll registerMethod the doSomethingTimeConsuming function, as the other is the function called on the other thread.

    Hope this helps, and sorry for the confusion.

  3. zmichl
    6 years ago

    Hi, could you give an example how to get the result in JS?

    Thanks,
    Zbynek

  4. taxilian
    6 years ago

    You’re thinking about it too hard; whatever parameters you pass from FireBreath will be passed into the callback function. In the case of the doSomethingTimeConsuming_thread function above in javascript your function would get two parameters, a reference to the plugin object (not the element in this case, btw, just the API itself) and the result:

    function mycb(plugin, result) {
    alert(“plugin valid: ” + plugin.valid);
    alert(“Plugin time consuming process result: ” + result);
    }
    document.getElementById(“myplugin”).doSomethingTimeConsuming(7, mycb);

  5. Daniel Earwicker
    6 years ago

    Where I work we have some ActiveX controls which expose a few simple methods. No support for other browsers. So I was wondering how easy it would be to make a simple adaptor for other browsers, and stumbled on FireBreath, and I’m very glad I did! I haven’t used C++ for a couple of years and was half expecting the usual nightmare: “We have our own string class, and our own collection classes, and our own…” etc. But no, you use std:: and boost:: – also that cmake thing is beautiful, I wish that had fallen into my lap in about 2004 when I was doing cross platform C++. And on downloading and following the tutorial, in about two minutes I have an example plug-in built from source and working in my browser. Very few open source projects are as helpful as this. In other words, you’re doing everything right.

  6. Landbird
    6 years ago

    Hi, thanks for the thread example!
    My plugin works fine in Windows, but  I got an error in make_method when I compile it in Mac Xcode 4.2.1.
    The error message is “MethodConverter.h:115: error: invalid initialization of non-const reference of type ‘FB::JSObjectPtr&’ from a temporary of type ‘boost::shared_ptr'”
    Can’t I use it in Mac?

  7. taxilian
    6 years ago

    Of course it works on Mac.  Looks like you’re trying to pass a non-const reference (FB::JSObjectPtr& instead of constr FB::JSObjectPtr&). That won’t work.  In the future, stackoverflow.com is a much better place to ask these questions; I monitor the firebreath tag, so I’ll see it.

  8. Pal Sumita
    6 years ago

    Hi Daniel,
    I have written th eplugin using firebreath. but dont know how to call my activex control method from it. I am new ti firebreath. Can you share me some code how you have done it.

Post a Comment

Your email is never published or shared. Required fields are marked *