Search code examples
javascriptfirefoxfirefox-addonfirefox-addon-restartless

Working asynchronously with jsctypes in restartless extension


I have a small bootstrapped extension that loads a dll file at startup() and holds it in a global variable. I don't know exactly how to use this properly maybe you will correct me on this, but I'm more interested in knowing if the functions I use from the dll file can be called asynchronously.

Right now what I'm doing is :

Components.utils.import("resource://gre/modules/ctypes.jsm");
log = function(str) { Cc["@mozilla.org/consoleservice;1"].getService(Ci.nsIConsoleService).logStringMessage(str+'\n'); }

var lib;
var someFunc;

...

function startup(aData, aReason) {
   lib = ctypes.open(dllPath);
   if(!lib)
   {
      log("dll load error");
      lib = null;
      return;
   }
   else
   {
      var initDll = lib.declare("Init", ctypes.default_abi, ctypes.long);
      var status = initDll();
      log("Dll returned " + status);

      if(status == ctypes.long(0))
      {
         someFunc = lib.declare("SomeFunc", ctypes.default_abi, ctypes.long, ctypes.long);
      }
   }
}

Once I have someFunc I use it in the code quite often, but the call takes a long time. Is there a way to call it asynchronously? Or to call it in such a way that Firefox doesn't freeze for the duration of the call?


Solution

  • If the external (bound via ctypes) API isn't itself asyncrhonous, by definition you're not going to be able to call it asynchronously in JavaScript.

    The easiest workaround is to use some kind of WebWorker to call the API in a background thread. Seeing as you need to do some privileged stuff, the ChromeWorker APIs fit the bill.

    The mechanics of it are:

    1. Launch the ChromeWorker (a.k.a the 'worker')
    2. Send the 'worker' a message via postMessage to kick off the 'job'
    3. The message event handler in the 'worker' should load ctypes and make the synchronous call to the external code, and then...
    4. Have the 'worker' send a message via postMessage to the caller (your main code) to say (effectively, "I'm done").

    If the external API requires call-specific arguments, you could organise to post those via the first postMessage. If the external API returns anything significant, you could organise to receive those via the second postMessage.

    All of that will look a bit fiddly, so you'll want to package it into a JavaScript code module; that will allow you to separate the ctypes stuff from the rest of your code, and you can use createEvent and dispatchEvent in the calling thread (inside the module) to make the API seem asynchronous but hide the 'worker' part.