Threads and Workers for Add-ons in Firefox 4

Jorge Villalobos

17

The upcoming Firefox 4 includes a ton of significant changes, many of which have a direct effect on add-ons. The majority of these changes are just new and different ways of doing things. Unfortunately, there are a couple of changes that offer no alternative and add-on authors will just need to cope with them. The stability changes that were introduced in the threading model for Firefox 4 are an example of this.

The Thread Manager was introduced in Firefox 3, and it appeared to work OK all the way up to recent Firefox 4 betas. This object allowed add-ons to run JS code on separate threads, pass objects to these threads and even access XPCOM objects within them.

It turns out that this was detrimental to the stability of the browser, and passing JS objects between threads would cause Firefox to randomly crash. When the cause of these crashes was found, the bug was fixed, irreversibly rendering the Thread Manager object fairly useless for add-ons.

Since a number of add-ons have been relying on the Thread Manager for some time, this change was quickly picked up by some authors and a discussion about it began shortly thereafter. The conclusion of this discussion is that there was no way the Thread Manager could allow passing JS objects again, and that the appropriate alternative was to use ChromeWorkers. At the moment of this discussion, a ChromeWorker was just the same as a Worker, with the only difference of being able to use js-ctypes to connect to compiled libraries. This wasn’t enough for add-ons, though, so the introduction of XPCOM in ChromeWorkers began.

This leads us to the current state of affairs: if you want to run code on a separate thread, our best recommendation is to use ChromeWorkers. They can now access XPCOM objects, as explained in the documentation, limited to components that have been marked as thread-safe. Because of this only a few components are currently accessible, and trying to use a non-thread-safe component will throw an exception right away.

This is as good as things will get in the Firefox 4 timeline, unfortunately. I’m already talking with the platform team about ways to make more XPCOM components thread-safe and therefore useable from ChromeWorkers. I’ve been looking at Thread Manager usage in add-ons listed on AMO, so I have a good idea of how add-ons were using these interfaces. The most frequent uses, like file system I/O will take precedence, and hopefully they will be included in whatever Firefox release comes after 4.

If you have an add-on that is affected by this change, I’m interested in knowing how you are/were using threads in your add-on. This can have an effect in future Firefox development, so please leave a comment.

17 responses

  1. Justin Dolske wrote on ::

    The history you present here is a bit weak. Cross-thread JS/XPCOM has _never_ been a good idea, and it’s always been ticking time bomb for anyone using it. Crash data shows that this has been a major source of crashes, so it’s not just a theoretical issue or arbitrary change. It’s unfortunate that in the past we (Mozilla) haven’t been as crisp at communicating that as we could have been, but here we are.

    The thread manager isn’t really involved here, other than being the thing that provides access to APIs for working with threads. Rather, it’s that some parts of the platform were not intended for use from multiple threads (ie, the typical locking/race/concurrency danger areas), and so using the tools exposed by thread manager was risky unless you understood exactly what was going on (and sometimes not even then ;-) . With great power comes great responsibility.

  2. Mook wrote on :

    Yep, there is maybe one person I’d trust to correctly write threaded JS code (and that person isn’t me). Sadly, the JS engine was capable of being used across threads, but it’s the rest of the platform that wasn’t; blocking JS access isn’t really addressing the root problem. (Compartments itself has potential benefits in terms of GC, so it can still be a good thing.)

    It sounds like a ChromeWorker won’t be able to, for example, create a @mozilla.org/array;1 either, even though it was changed to be usable off the main thread (as long as you don’t pass it to a different thread)? This assume ChromeWorker GC (and therefore the last release) would have happened on the worker thread, I guess…

    In general: threads may or may not be the best tool for concurrency and generally making responsive UI, but it’s the tool most people have at least some passing familiarity with; combined with the fact that there’s a large number of platform pieces that look so useful, yet not actually usable, it’s not really surprising that people end up with crashes. It’s not like a debug build that would have warned about the errors is either a) easy to obtain (it’s multiple hours to build on a normal, non-beefy Windows machine), or b) actually usable (due to all the assertions one would trigger running a clean Firefox).

    Does this actually stop people poking at nsIThreadManager.mainThread.processNextEvent() though? That always scared the crap out of me…

    1. Arivald wrote on :

      nsIThreadManager.mainThread.processNextEvent() is still usefull.

      I use it to wait for some asynchrous APi, like new AddonManager.
      example:

      AddonManager.getAddonByID(ExtensionGUID, function(addon) {
      ExtensionData.version = addon.version;
      ExtensionData.location = addon.getResourceURI(”).QueryInterface(Components.interfaces.nsIFileURL).file;
      } );

      var thread = Components.classes['@mozilla.org/thread-manager;1'].getService(Components.interfaces.nsIThreadManager).currentThread;
      while (ExtensionData.location == false) thread.processNextEvent(true);

  3. Wladimir Palant wrote on ::

    @Mook: processNextEvent() stays accessible – I actually use it (though only in the unit test) :)
    It doesn’t look like there are any issues with this API, other than the fact that somebody else could spin his own event loop as well – and that will halt your loop for an amount of time that you cannot control. So yes, using this API to force synchronous code execution might not be the best idea.

  4. tito wrote on :

    shame-plug

    It would be very nice if someone can write a little example, on how to do a sqlite query and update the UI (dom) in-between without blocking the application..
    Regards,

    1. Jorge wrote on ::

      You can use asynchronous SQLite, which shouldn’t block you unless it’s a very heavy query. Updating the DOM always has to be done on the main thread, but if it is a very heavy operation, it can be done in segments separated by a timeout.

  5. Kohei Yoshino wrote on :

    A Japanese translation of this article is available at
    https://dev.mozilla.jp/2011/01/threads-n-workers-in-firefox-4/

  6. Skuallpa wrote on ::

    Thanks for this article

    >If you have an add-on that is affected by this change, I’m interested in knowing how you are/were using threads in your add-on.

    In order not to freeze UI while running a time consuming function of my Component, I launched this function in a separated thread, as below:

    var MyComponent = Components.classes["@foo/example_cpp;1"]
    .getService(Components.interfaces.nsIMyComponent);

    var backgroundTask = {
    callback: {
    result: 0,
    run: function() {
    processResult(this.result);
    }
    },
    run: function() {
    this.callback.result = MyComponent.myFunction();
    //dispatch event back to the main thread
    threadMngr.mainThread.dispatch(this.callback, thread.DISPATCH_NORMAL);
    }
    }

    var thread = threadMngr.newThread(0);
    thread.dispatch(backgroundTask, thread.DISPATCH_NORMAL);

    Since Gecko 2.0, this code is no longer working and I am trying to find an alternative with chromeWorkers. However, I did not find a clear example on how to do that. If anyone can give me an example, it would be great.

  7. joliclic wrote on ::

    >If you have an add-on that is affected by this change, I’m interested in knowing how you are/were using threads in your add-on.

    In the previous versions of one of my extensions, I had created a component to add asynchronously a list of items (url) to the History, in a separate thread.
    I now use the new mozIAsyncHistory, which does something similar. But devmo claims that “Warning: This interface is experimental and will change after Gecko 2.0″, so I’m not sure how my solution is perennial.

    1. Jorge Villalobos wrote on ::

      It looks like the feature was introduced in Gecko 2.0 (Firefox 4), so I wouldn’t think it would disappear any time soon. The warning is more along the lines that the feature is very new and is subject to modifications in the near future.

      So, it looks like this is the right solution, but you should monitor the documentation pages when new versions of Firefox are released.

      1. Vikas Agarwal wrote on :

        Hi Jorge,

        I have an XUL based application which does File I/O operation asynchronously using the way illustrated by “Skuallpa” above. Till i am not able to find any solution for this even I have tried Woker and ChromeWorker objects. They require only thread safe objects to be passed and created in their implementations. :(

        1. Jorge Villalobos wrote on ::

          Unfortunately there’s no way to run file I/O operations from Workers at the moment. It’s my #1 request for components that should be made thread-safe, but I don’t know when (or if) this will happen.
          You’ll either need to run them synchronously, or using nsIProcess or JS-cytpes to run them separately.

        2. James Newell wrote on ::

          Understanding Faith Desktop Version (based on XULRunner) is in the same boat.

          ChromeWorker looks much nicer compared to nsIRunnable so I’m hoping thread-safe file IO lands soon :)

  8. Max wrote on :

    What is about the FormData object?
    Will this become “thread save”?

  9. V wrote on ::

    Hi Jorge,

    good post, first info I can find about this at all.
    I am the developer of the TBTracer add-on and have been wanting to move the file reader and also the HTTP observer to a background thread for a while.
    So I’m guessing that won’t be happening for a while.
    In addition to nsILocalFile what about the Observer service?
    Are there plans to make that Thread safe any time soon?

    1. Jorge Villalobos wrote on ::

      Hey V,

      I wish I had an answer for you, but I don’t see this progressing in any way. I think that, in general, nothing is passed between threads unless it can be serialized as a string, and that would discard the Observer service because it also passes an object. You could use a combination of the observer service and message passing in order to move most of the observer code to a background thread, though.

      1. V wrote on ::

        Yeah,

        that was my plan.
        I mean the observer in my case observers HTTP reqs, builds an object with all the info (well an array of those) and then passes that on to the GUI code.
        So that would work.
        I just need to wait for the observer-service to be thread-safe.
        That’s the reason for my question.