In the Mozilla platform, most activities such as layout, DOM operations, content JavaScript, and chrome JavaScript run on the main thread. However, it may be useful for C++ code in the Mozilla platform to run tasks on another thread. Typically, thread activities are triggered and managed using an XPCOM event-passing framework that uses the nsIRunnable
interface. Each runnable represents a task which can then be dispatched to another thread for execution.
In general, threading and message passing should be asynchronous. For example, let's say we have a function, CalculatePi(int digits)
, which will calculate π to an arbitrary number of digits:
void CalculatePi(int digits, nsCString& result); // This is synchronous
This can take a while, so we don't want to run this on the main thread. Instead, we want to run it on a new thread and be notified when the result is available. So we declare an asynchronous version of the same function:
typedef void (*PiCallback)(const nsCString& result); // Callback function void CalculatePiAsynchronously(int digits, PiCallback callback);
Creating a runnable
nsRunnable
is a helper class: it already implements threadsafe refcounting, so all you need to do is override the Run()
function. In our example, we actually need two runnables: one dispatched to the worker thread, and one to hand us back the result.
#include "nsThreadUtils.h" class PiResultTask : public nsRunnable { public: PiResultTask(PiCallback callback, const nsACString& result) : mCallback(callback) , mResult(result) , mWorkerThread(do_GetCurrentThread()) { MOZ_ASSERT(!NS_IsMainThread()); // This should be running on the worker thread } NS_IMETHOD Run() { MOZ_ASSERT(NS_IsMainThread()); // This method is supposed to run on the main thread! mCallback(mResult); // If we don't destroy the thread when we're done with it, it will hang around forever... bad! // But thread->Shutdown must be called from the main thread, not from the thread itself. mWorkerThread->Shutdown(); } private: PiCallback mCallback; nsCString mResult; nsCOMPtr<nsIThread> mWorkerThread; }; class PiCalculateTask : public nsRunnable { public: PiCalculateTask(PiCallback callback, int digits) : mCallback(callback) , mDigits(digits) { } NS_IMETHOD Run() { nsCString result; CalculatePi(mDigits, result); nsCOMPtr<nsIRunnable> resultrunnable = new PiResultTask(mCallback, result); NS_DispatchToMainThread(resultrunnable); } private: PiCallback mCallback; int mDigits; };
Putting it all together
To start a new thread, create it using the Thread Manager:
#include "nsXPCOMCIDInternal.h"
void CalculatePiAsynchronously(int digits, PiCallback callback) { // To create a new thread, get the thread managernsCOMPtr<nsIThreadManager>
tm = do_GetService(NS_THREADMANAGER_CONTRACTID); nsCOMPtr<nsIThread> mythread; nsresult rv = tm->NewThread(0, 0, getter_AddRefs(mythread)); if (NS_FAILED(rv)) { // In case of failure, call back immediately with an empty string which indicates failure callback(EmptyCString()); return; } nsCOMPtr<nsIRunnable> r = new PiCalculateTask(callback, digits); mythread->Dispatch(r, nsIEventTarget::DISPATCH_NORMAL); // The result callback will shut down the worker thread, we can let it go here... }