DOMWorkerThreads: Difference between revisions

no edit summary
No edit summary
No edit summary
 
(9 intermediate revisions by 2 users not shown)
Line 1: Line 1:
== DOM Worker Threads API Proposal ==
== Web Worker API Proposal ==


== Abstract ==
== API Proposal ==


This is an API '''proposal''' for DOM Worker Threads.
<blockquote>
<code>
<pre>


At this time this document is not complete and is not yet ready for general consideration or debate. It's just getting started!
[NoInterfaceObject] interface WorkerFactory {
  Worker createWorker(in DOMString scriptURL);
};


Look for a post to mozilla.dev.platform when this proposal is ready to be discussed.
interface Worker {
  void postMessage(in DOMString aMessage);


== Introduction ==
  // event handler attributes
          attribute EventListener onmessage;
          attribute EventListener onerror;
          attribute EventListener onload;
          attribute EventListener onunload;
};


=== Use Cases ===


=== Terminology ===
[NoInterfaceObject] interface WorkerGlobalScope
{
  readonly attribute WorkerGlobalScope self;
  readonly attribute WorkerLocation location;
  readonly attribute boolean closing;
  void close();


A '''worker''' is an execution context for JavaScript that runs off of the main UI thread. The global scope for a worker is almost empty - familiar objects such as <code>window</code> and <code>document</code> are not available, nor is the Mozilla-specific <code>Components</code> object.
  // event handler attributes
          attribute EventListener onunload;


A '''worker pool''' is a collection of related workers. Workers within a pool can communicate with each other freely, but workers cannot communicate with workers from another pool.
  // WorkerUtils
  void importScripts([Variadic] in DOMString urls);
  readonly attribute Storage localStorage;
  Database openDatabase(in DOMString name, in DOMString version, in DOMString displayName, in unsigned long estimatedSize);
  void showNotification(in DOMString title, in DOMString subtitle, in DOMString description);
  void showNotification(in DOMString title, in DOMString subtitle, in DOMString description, in VoidCallback onclick);
};
 
[NoInterfaceObject] interface WorkerLocation {
  readonly attribute DOMString href;
  readonly attribute DOMString protocol;
  readonly attribute DOMString host;
  readonly attribute DOMString hostname;
  readonly attribute DOMString port;
  readonly attribute DOMString pathname;
  readonly attribute DOMString search;
  readonly attribute DOMString hash;
};


== Conformance ==
</pre>
</code>
</blockquote>


== Dependencies ==
=== Sample usage ===


== Specification Proposal ==
This is a '''very''' suboptimal way of calculating a number in the Fibonacci sequence.


Main page:
<blockquote>
<blockquote>
<code>
<code>
<pre>
<pre>
#include "nsISupports.idl"
worker = createWorker("f.js");
worker.onmessage = function(e) {
  alert("The 100th Fibonacci number is " + e.data);
}
worker.postMessage(100);
</pre>
</code>
</blockquote>


interface nsIDOMWorkerThread;
f.js:
interface nsIScriptError;
<blockquote>
<code>
<pre>
parent.onmessage = function(e) {
  if (e.data <= 1)
    postMessage(e.data);


[scriptable, function, uuid(e50ca05d-1381-4abb-a021-02eb720cfc38)]
  w1 = createWorker("f.js");
interface nsIDOMWorkerMessageListener : nsISupports
  w1.onmessage = handler;
{
   w1.postMessage(e.data - 1);
   /**
 
  * An nsIDOMWorkerThread receives the onMessage callback when another
  w2 = createWorker("f.js");
  * worker posts a message to it.
  w2.onmessage = handler;
  *
   w2.postMessage(e.data - 2);
  * @param aMessage (in DOMString)
}
  *        The message sent from another worker.
  * @param aSource (in nsIDOMWorkerThread)
  *        The worker that sent the message. Useful for a quick response.
  */
   void onMessage(in DOMString aMessage,
                in nsIDOMWorkerThread aSource);
};


[scriptable, function, uuid(9df8422e-25dd-43f4-b9b9-709f9e074647)]
c = 0;
interface nsIDOMWorkerErrorListener : nsISupports
sum = 0;
{
  /**
  * An nsIDOMWorkerPool receives the onError callback when one of its child
  * workers has a parse error or an unhandled exception.
  *
  * @param aMessage (in nsIScriptError)
  *        Details about the error that occurred. See nsIScriptError.
  * @param aSource (in nsIDOMWorkerThread)
  *        The worker that sent the message. Depending on the specific error in
  *        question it may not be possible to use this object (in the case of a
  *        parse error, for instance, aSource will be unusable).
  */
  void onError(in nsIScriptError aError,
              in nsIDOMWorkerThread aSource);
};


[scriptable, uuid(6f19f3ff-2aaa-4504-9b71-dca3c191efed)]
function handler(e) {
interface nsIDOMWorkerThread : nsISupports
   sum += parseInt(e.data);
{
  if (++c == 2) {
   /**
    postMessage(sum);
  * Sends a message to the worker.
  }
  *
}
  * @param aMessage (in DOMString)
</pre>
  *        The message to send. This may be a string or an object that can be
</code>
  *        serialized via JSON (see http://developer.mozilla.org/en/docs/JSON).
</blockquote>
  */
  void postMessage(in DOMString aMessage);
};


[scriptable, uuid(45312e93-8a3e-4493-9bd9-272a6c23a16c)]
== References ==
interface nsIDOMWorkerPool : nsIDOMWorkerThread
{
  /**
  * The nsIDOMWorkerMessageListener which handles messages for this pool.
  *
  * Developers should set this attribute to a proper object before another
  * worker begins sending messages to ensure that all messages are received.
  */
  attribute nsIDOMWorkerMessageListener messageListener;


  /**
The only thing I've seen so far is the [http://code.google.com/apis/gears/api_workerpool.html Google Gears WorkerPool API]. We would certainly like to provide a API that would make migrating Gears code trivial.
  * The nsIDOMWorkerErrorListener which handles errors in child threads.
  *
  * Developers should set this attribute to a proper object before calling
  * createWorker in order to catch parse errors as well as runtime exceptions.
  */
  attribute nsIDOMWorkerErrorListener errorListener;


  /**
== API Proposal For Shared Workers ==
  * Create a new worker object by evaluating the given script.
  *
  * @param aSourceScript (in DOMString)
  *        The script to compile. See below for details on the scope in which
  *        the script will run.
  */
  nsIDOMWorkerThread createWorker(in DOMString aScriptText);


  /**
If we want to support shared workers in the initial release of this spec, here are two proposals that will work with the above initial API.
  * Create a new worker object by evaluating the given script file.
  *
  * @param aSourceScript (in nsIURI)
  *        The script file to compile. See below for details on the scope in
  *        which the script will run. See nsIURI.
  */
  nsIDOMWorkerThread createWorkerFromURI(in DOMString aScriptURI);
};


interface nsIDOMWorkerGlobalScope
=== Proposal 1 ===
{
  /**
  * The worker object that created this scope.
  */
  readonly attribute nsIDOMWorkerThread thisThread;


  /**
<blockquote>
  * Sends a message to the pool that created the worker.
<code>
  *
<pre>
  * @param aMessage (in DOMString)
  *        The message to send. This may be a string or an object that can be
  *        serialized via JSON (see http://developer.mozilla.org/en/docs/JSON).
  */
  void postMessageToPool(in DOMString aMessage);


   /**
[NoInterfaceObject] interface WorkerFactory {
  * The nsIDOMWorkerMessageListener which handles messages for this worker.
   ...
  *
  Worker createSharedWorker(in DOMString name, in DOMString scriptURL);
  * Developers should set this attribute to a proper object before another
};
  * worker begins sending messages to ensure that all messages are received.
  */
  attribute nsIDOMWorkerMessageListener messageListener;


  /**
[NoInterfaceObject] interface WorkerParent {
  * Loads the scripts indicated from the given URI or array.
   void postMessage(in DOMString aMessage);
  *
   attribute EventListener onmessage;
  * This function will block until the script files have been loaded and
  * executed. Load order is not guaranteed, but execution order will be
  * determined by the order in the given array.
  *
  * @param aURIString
  *        The URI to load or an array of URIs to load.
  *        XXX We could also make this function take a variable number of
  *            arguments and forget the array...
  *
  * @throws Will throw an nsIException if the URI cannot be loaded or
  *        executed.
  */
  void loadScripts(/* in JSObject aURIStringOrArray */);
 
  /**
  * Creates a new XMLHttpRequest object.
  *
  * Use: 'var req = new XMLHttpRequest();'
  */
  /* nsIJSXMLHttpRequest XMLHttpRequest(); */
 
  /**
  * See nsIDOMJSWindow.idl
  */
  void dump(in DOMString str);
 
  /**
  * See nsIDOMJSWindow.idl
  */
  long setTimeout(/* in JSObject aFunctionOrString, */
                  /* in JSObject aMilisecondDelay, */
                  /* [optional] in aArgsToFunctionOrString */);
 
  /**
  * See nsIDOMJSWindow.idl
  */
  long setInterval(/* in JSObject aFunctionOrString, */
                  /* in JSObject aMilisecondDelay, */
                  /* [optional] in aArgsToFunctionOrString */);
 
  /**
  * See nsIDOMJSWindow.idl
  */
   void clearTimeout(/* in JSObject aTimeoutId */);
 
  /**
  * See nsIDOMJSWindow.idl
  */
   void clearInterval(/* in JSObject aIntervalId */);
};
};


Line 200: Line 130:
</blockquote>
</blockquote>


=== The worker pool's scope ===
<code>createSharedWorker</code> creates a new <code>Worker</code> object that interacts with the same <code>WorkerGlobalScope</code> as any previously existing <code>Worker</code>s.


The scope for the worker pool is the standard DOM scope. All DOM objects such as <code>window</code> and <code>document</code> are available, as well as functions such as <code>alert()</code> and <code>dump()</code>.
When a shared <code>Worker</code> receives a message it can send data back to the sender using the <code>Event.source</code> property which is a <code>WorkerParent</code>.


=== The worker's scope ===
Using <code>WorkerGlobalScope.postMessage</code> and <code>WorkerGlobalScope.onmessage</code> results in a message being sent to the first context that opened the shared worker (or nothing if that context is dead).


The worker's scope is far more limited. See <code>nsIDOMWorkerGlobalScope</code> above for details.
Downsides with this proposal:
* There is no way to communicate back unless first communicated to.
* The first one to instantiate a shared worker get special treatment.


=== Sample usage ===
=== Proposal 2 ===


<blockquote>
<blockquote>
<code>
<code>
<pre>
<pre>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
  <title>Test threads</title>
  <body>
  <script language="javascript">
    var scriptToRun = 'function messageListener(message, source) { ' +
                      '  dump("Worker: " + message + "\\n"); ' +
                      '} ' +
                      'for (var i = 0; i < 10000000; i++) { ' +
                      ' /* do something */ ' +
                      '} '+
                      'postMessageToPool("Done!"); ';


    var wp = navigator.newWorkerPool();
[NoInterfaceObject] interface WorkerFactory {
    wp.messageListener = function(message, source) {
  ...
      dump("Pool: " + message + "\n");
  MessagePort connectToSharedWorker(in DOMString name, in DOMString scriptURL);
    };
};
    wp.errorListener = function(error, source) {
      dump("Pool: error = '" + error + "'\n");
    }


    var threads = []
[NoInterfaceObject] interface WorkerGlobalScope {
    for (var i = 0; i < 10; i++) {
  ...
      var thread = wp.createWorker(scriptToRun);
          attribute EventListener onconnect;
      thread.postMessage("hello");
};
      threads.push(thread);
    }
 
  </script>
  </body>
</html>


</pre>
</pre>
Line 249: Line 160:
</blockquote>
</blockquote>


== Not In This Specification ==
<code>connectToSharedWorker</code> creates a two new <code>MessagePort</code> which are entangled with each other. One of the two ports is returned, and the other is sent to the <code>Worker</code> with the given name. If such a <code>Worker</code> does not yet exist, one is created.
 
== References ==


The only thing I've seen so far is the [http://code.google.com/apis/gears/api_workerpool.html Google Gears WorkerPool API]. We would certainly like to provide a compatibility API that would make migrating Gears code trivial.
The <code>Worker</code> receives the other <code>MessagePort</code> through an <code>onconnect</code> <code>Event</code> fired on the <code>WorkerGlobalScope</code> object.


== Acknowledgements ==
* There is no way to get a reference to the shared <code>Worker</code> object itself.
* Communication for shared workers is different from communication for non-shared ones.
Confirmed users
716

edits