The Thread Manager

  • Revision slug: The_Thread_Manager
  • Revision title: The Thread Manager
  • Revision id: 58797
  • Created:
  • Creator: Sheppy
  • Is current revision? No
  • Comment 15 words added, 1 words removed

Revision Content

{{ Fx_minversion_header("3") }}

The Thread Manager, introduced in Firefox 3, offers an easy to use mechanism for creating threads and dispatching events to them for processing.

{{ Warning("While these interfaces still exist in Firefox 4, it is no longer possible to transfer JavaScript objects or functions from one thread to another in Gecko 2.0, making event dispatch to any thread but the current impossible from JavaScript. Consider using a " .. domxref("ChromeWorker") .. " instead; it's not a perfect replacement but may work for you.") }}

Interfaces

There are several interfaces that provide threading support:

{{ Interface("nsIThreadManager") }}
The Thread Manager itself lets you create threads.
{{ Interface("nsIThread") }}
The nsIThread interface encapsulates an operating system thread, providing easy cross-platform access to multithreading in your code.
{{ Interface("nsIThreadPool") }}
A thread pool provides a limited set of worker threads. When you dispatch an event to the pool, the pool selects an available worker thread to process the event.
{{ Interface("nsIThreadInternal") }}
A subclass of {{ Interface("nsIThread") }} that is implemented by the XPCOM thread object to add support for observing dispatch activity on a thread.
{{ Interface("nsIThreadObserver") }}
Provides the ability to monitor a thread, to receive notifications when events are dispatched to it and when they're finished being processed.
{{ Interface("nsIThreadEventFilter") }}
This interface is used by the {{ interfacemethod("nsIThreadInternal","pushEventQueue") }} method in {{ Interface("nsIThreadInternal") }} to allow event filtering.

Using the Thread Manager

To use the Thread Manager, you need to encapsulate each thread's working code in an {{ Interface("nsIRunnable") }} XPCOM object. You can write the entire object in JavaScript, though, and it's not hard to do.

{{ Note("The DOM is not thread safe. You must not access the DOM or user interface from background threads. Doing so will likely crash. This also includes not being on a different thread where the DOM is reachable in your scope, such as scripts included in documents.") }}

{{ Note("Currently nsIChannel APIs can only be used from the main thread.") }}

In this section, we'll look at a simple example.

The background thread

First, we need an XPCOM object to handle the work to be done in the background thread:

var workingThread = function(threadID, number) {
  this.threadID = threadID;
  this.number = number;
  this.result = 0;
};

workingThread.prototype = {
  run: function() {
    try {
      // This is where the working thread does its processing work.
      
      for (var i = 0; i<= this.number; i++) {
        this.result += i;
      }
      
      // When it's done, call back to the main thread to let it know
      // we're finished.
      
      main.dispatch(new mainThread(this.threadID, this.result),
        Components.interfaces.nsIThread.DISPATCH_NORMAL);
    } catch(err) {
      Components.utils.reportError(err);
    }
  },
  
  QueryInterface: function(iid) {
    if (iid.equals(Components.interfaces.nsIRunnable) ||
        iid.equals(Components.interfaces.nsISupports)) {
            return this;
    }
    throw Components.results.NS_ERROR_NO_INTERFACE;
  }
};

The constructor for this thread saves a thread ID and a number into local variables, and sets a result variable to 0. These values will be used when the thread is executed.

There are two methods on the object aside from the constructor:

run()
The run() method is called when the {{ Interface("nsIThread") }} interface's dispatch() method is called. This is the routine that does the actual work in the background thread. In this case, we're computing the sum of all numbers from 0 through this.number. When the computation is complete, we get access to the main thread using the mainThread object and dispatch a callback to it to share the results.
QueryInterface()
Since threads' XPCOM objects need to handle the {{ Interface("nsIRunnable") }} interface, we need to respond correctly when this method is called to ask if our object handles that interface.

The main thread

The XPCOM object that handles the main thread is used as a callback from the background task. Its run() method is called when the background thread wishes to let the user know the result of its computations. This is necessary because background threads can't touch the user interface, so they need to ask the main thread to do it.

var mainThread = function(threadID, result) {
  this.threadID = threadID;
  this.result = result;
};

mainThread.prototype = {
  run: function() {
    try {
      // This is where we react to the completion of the working thread.
      alert('Thread ' + this.threadID + ' finished with result: ' + this.result);
    } catch(err) {
      Components.utils.reportError(err);
    }
  },
  
  QueryInterface: function(iid) {
    if (iid.equals(Components.interfaces.nsIRunnable) ||
        iid.equals(Components.interfaces.nsISupports)) {
            return this;
    }
    throw Components.results.NS_ERROR_NO_INTERFACE;
  }
};

The run() method here simply displays the output to the user using an alert box.

Putting it all together

To actually use the Thread Manager to do these computations in the background, we first need to create the {{ Interface("nsIThread") }} object to run the workingThread task on:

var background = Components.classes["@mozilla.org/thread-manager;1"].getService().newThread(0);

We also need to get a reference to the main thread's {{ Interface("nsIThread") }}:

var main = Components.classes["@mozilla.org/thread-manager;1"].getService().mainThread;

Once we have that information, we can dispatch a task to the background thread:

background.dispatch(new workingThread(1, 5000000), Components.interfaces.nsIThread.DISPATCH_NORMAL);

This starts the background thread running, computing the sum of all the numbers between 0 and 5,000,000. When the work is complete, the main thread's run() method is called to share the result with the user. In the meantime, the main thread can continue about its business, doing whatever it needs to do (such as respond to user interaction).

{{ languages( { "es": "es/El_administrador_de_hilos", "ja": "ja/The_Thread_Manager" } ) }}

Revision Source

<p>{{ Fx_minversion_header("3") }}</p>
<p>The Thread Manager, introduced in Firefox 3, offers an easy to use mechanism for creating threads and dispatching events to them for processing.</p>
<p>{{ Warning("While these interfaces still exist in Firefox 4, it is no longer possible to transfer JavaScript objects or functions from one thread to another in Gecko 2.0, making event dispatch to any thread but the current impossible from JavaScript. Consider using a " .. domxref("ChromeWorker") .. " instead; it's not a perfect replacement but may work for you.") }}</p>
<h3 name="Interfaces">Interfaces</h3>
<p>There are several interfaces that provide threading support:</p>
<dl> <dt>{{ Interface("nsIThreadManager") }}</dt> <dd>The Thread Manager itself lets you create threads.</dd> <dt>{{ Interface("nsIThread") }}</dt> <dd>The <code>nsIThread</code> interface encapsulates an operating system thread, providing easy cross-platform access to multithreading in your code.</dd> <dt>{{ Interface("nsIThreadPool") }}</dt> <dd>A thread pool provides a limited set of worker threads. When you dispatch an event to the pool, the pool selects an available worker thread to process the event.</dd> <dt>{{ Interface("nsIThreadInternal") }}</dt> <dd>A subclass of {{ Interface("nsIThread") }} that is implemented by the XPCOM thread object to add support for observing dispatch activity on a thread.</dd> <dt>{{ Interface("nsIThreadObserver") }}</dt> <dd>Provides the ability to monitor a thread, to receive notifications when events are dispatched to it and when they're finished being processed.</dd> <dt>{{ Interface("nsIThreadEventFilter") }}</dt> <dd>This interface is used by the {{ interfacemethod("nsIThreadInternal","pushEventQueue") }} method in {{ Interface("nsIThreadInternal") }} to allow event filtering.</dd>
</dl>
<h3 name="Using_the_Thread_Manager">Using the Thread Manager</h3>
<p>To use the Thread Manager, you need to encapsulate each thread's working code in an {{ Interface("nsIRunnable") }} XPCOM object. You can write the entire object in JavaScript, though, and it's not hard to do.</p>
<p>{{ Note("The DOM is not thread safe. You must not access the DOM or user interface from background threads. Doing so will likely crash. This also includes not being on a different thread where the DOM is reachable in your scope, such as scripts included in documents.") }}</p>
<p>{{ Note("Currently nsIChannel APIs can only be used from the main thread.") }}</p>
<p>In this section, we'll look at a simple example.</p>
<h4 name="The_background_thread">The background thread</h4>
<p>First, we need an XPCOM object to handle the work to be done in the background thread:</p>
<pre>var workingThread = function(threadID, number) {
  this.threadID = threadID;
  this.number = number;
  this.result = 0;
};

workingThread.prototype = {
  run: function() {
    try {
      // This is where the working thread does its processing work.
      
      for (var i = 0; i&lt;= this.number; i++) {
        this.result += i;
      }
      
      // When it's done, call back to the main thread to let it know
      // we're finished.
      
      main.dispatch(new mainThread(this.threadID, this.result),
        Components.interfaces.nsIThread.DISPATCH_NORMAL);
    } catch(err) {
      Components.utils.reportError(err);
    }
  },
  
  QueryInterface: function(iid) {
    if (iid.equals(Components.interfaces.nsIRunnable) ||
        iid.equals(Components.interfaces.nsISupports)) {
            return this;
    }
    throw Components.results.NS_ERROR_NO_INTERFACE;
  }
};
</pre>
<p>The constructor for this thread saves a thread ID and a number into local variables, and sets a result variable to 0. These values will be used when the thread is executed.</p>
<p>There are two methods on the object aside from the constructor:</p>
<dl> <dt><code>run()</code></dt> <dd>The <code>run()</code> method is called when the {{ Interface("nsIThread") }} interface's <code>dispatch()</code> method is called. This is the routine that does the actual work in the background thread. In this case, we're computing the sum of all numbers from 0 through <code>this.number</code>. When the computation is complete, we get access to the main thread using the <code>mainThread</code> object and dispatch a callback to it to share the results.</dd> <dt><code>QueryInterface()</code></dt> <dd>Since threads' XPCOM objects need to handle the {{ Interface("nsIRunnable") }} interface, we need to respond correctly when this method is called to ask if our object handles that interface.</dd>
</dl>
<h4 name="The_main_thread">The main thread</h4>
<p>The XPCOM object that handles the main thread is used as a callback from the background task. Its <code>run()</code> method is called when the background thread wishes to let the user know the result of its computations. This is necessary because background threads can't touch the user interface, so they need to ask the main thread to do it.</p>
<pre>var mainThread = function(threadID, result) {
  this.threadID = threadID;
  this.result = result;
};

mainThread.prototype = {
  run: function() {
    try {
      // This is where we react to the completion of the working thread.
      alert('Thread ' + this.threadID + ' finished with result: ' + this.result);
    } catch(err) {
      Components.utils.reportError(err);
    }
  },
  
  QueryInterface: function(iid) {
    if (iid.equals(Components.interfaces.nsIRunnable) ||
        iid.equals(Components.interfaces.nsISupports)) {
            return this;
    }
    throw Components.results.NS_ERROR_NO_INTERFACE;
  }
};
</pre>
<p>The <code>run()</code> method here simply displays the output to the user using an alert box.</p>
<h4 name="Putting_it_all_together">Putting it all together</h4>
<p>To actually use the Thread Manager to do these computations in the background, we first need to create the {{ Interface("nsIThread") }} object to run the <code>workingThread</code> task on:</p>
<pre class="eval">var background = Components.classes["@mozilla.org/thread-manager;1"].getService().newThread(0);
</pre>
<p>We also need to get a reference to the main thread's {{ Interface("nsIThread") }}:</p>
<pre class="eval">var main = Components.classes["@mozilla.org/thread-manager;1"].getService().mainThread;
</pre>
<p>Once we have that information, we can dispatch a task to the background thread:</p>
<pre class="eval">background.dispatch(new workingThread(1, 5000000), Components.interfaces.nsIThread.DISPATCH_NORMAL);
</pre>
<p>This starts the background thread running, computing the sum of all the numbers between 0 and 5,000,000. When the work is complete, the main thread's <code>run()</code> method is called to share the result with the user. In the meantime, the main thread can continue about its business, doing whatever it needs to do (such as respond to user interaction).</p>
<p>{{ languages( { "es": "es/El_administrador_de_hilos", "ja": "ja/The_Thread_Manager" } ) }}</p>
Revert to this revision