Explore multithreaded programming in XUL

Retrieve search results simultaneously from Google, Yahoo, and Microsoft's Bing

As you create cross-platform desktop applications using XUL, you also can enhance your skills with JavaScript, CSS, and even HTML. XUL's cross-platform capabilities are not a collection of least common denominator features. Instead, XUL gives you the kind of power that you might expect from a desktop application toolkit, including access to native threads. You can even access native threads directly from JavaScript, writing code that executes in parallel. In this article, you will examine the multithreading capabilities of XUL, and create an application that uses multiple threads to retrieve data. You will take a classic IO-bound application, one that accesses multiple remote data sources over the Internet, and speed it up through multiple threads in XUL. The application will allow users to view and compare anonymous results of three popular search engines: Google, Yahoo, and Bing from Microsoft®.

Michael Galpin, Software architect, eBay

Michael Galpin's photoMichael Galpin is an architect at eBay. He is a frequent contributor to developerWorks and has also written for TheServerSide.com and the Java Developer's Journal, as well as his own blog. He has been programming professionally since 1998 and holds a degree in mathematics from the California Institute of Technology.



01 September 2009

Also available in Chinese Japanese

Prerequisites

In this article, you will develop a XUL application (XML User Interface Language). Familiarity with XUL development is certainly beneficial, but not strictly required. XUL is mostly an XML widget language, similar to HTML in some ways, with support for JavaScript and CSS. You can use other languages with XUL, but you will only use JavaScript in this article. Thus, experience with JavaScript is required, but knowledge of other languages is not needed. The concurrency APIs used in this article were introduced in Mozilla's JavaScript 1.9.1 that is part of Firefox 3.5. You can use either Firefox 3.5 or Mozilla's XULRunner 1.9.1 to run the code shown in this article. See Resources for links to these tools.


Problem statement

Frequently used acronyms

  • API: Application program interface
  • CPU: Central Processing Unit
  • CSS: Cascading Stylesheets
  • HTML: Hypertext Markup Language
  • IO: Input-Output
  • JSON: JavaScript Object Notation
  • UI: User Interface
  • URL: Uniform Resource Locator
  • XML: Extensible Markup Language
  • XPCOM: Cross Platform Component Object Model
  • XUL: XML User Interface Language

Back in 2005, Herb Sutter declared that the free lunch was over. His analysis and conclusion were both pretty simple. Moore's Law had run its course. Computers were no longer advancing through faster processors. Software developers had put this increasing processor speed to good use by always increasing the amount of computations needed! This was straightforward. Faster computers meant you could do more stuff. Now computers are getting faster by adding more cores. You can still do more stuff, but you have to do it using concurrent programming. You have to use those cores.

You have probably heard all of this before. Many programming languages and platforms have added new concurrency features to help developers take advantage of multi-core computers. For some languages that already had multi-threaded programming APIs, this meant creating better APIs and abstractions to make multi-threaded programming easier to understand and less error-prone. Some languages did not have any support for multithreaded programming and have added it. This is the case for JavaScript, the main programming language for XUL programmers. Starting with JavaScript 1.9.1, multithreaded programming has come to JavaScript on the XUL platform.


Threads in ye olde XUL

Now you might say to yourself: "Wait a minute! XUL is the main technology behind Firefox and its extensions. Don't try to tell me that these XUL applications have been single-threaded all of this time!" Of course you are correct. However, it is key to remember that XUL supports more than just JavaScript. Through the power of XPCOM, you can implement components in other languages, in particular in C++. Of course C++ has support for native threads, so that was always a way to use multithreading from within a XUL application. However, the native programming language for XUL has always been JavaScript, or more precisely, Mozilla's flavor of JavaScript. Until now, JavaScript lacked a way to explicitly spawn and manage arbitrary threads, but JavaScript 1.9.1 changes that. Despite being a minor release, JavaScript 1.9.1 adds a major new feature: multithreading through the Workers API.


The Workers API

So what exactly is this Workers API in JavaScript? With Workers, you can spawn as many threads as you want, and you can use those threads to do whatever tasks you want them to. In many scenarios, you might want to spawn one or more threads within your application. One common use case is to perform some periodic operation. This is common in many applications, and many developers use JavaScript's setInterval function for this. The only problem with this function is that when it periodically executes, it executes on the main thread and thus blocks any interactions from the user during that time. If its execution takes any noticeable time at all, then the application freezes up during this execution. Hence, the benefits of executing on a separate thread.

This really brings me to the more general use case for multiple threads: long running operations. Any long running operation that operates on the main thread will cause an application to freeze up. You always want such operations to execute on their own thread. What are some examples of such long running operations? You might see examples of prime factorizations of large integers, or sorting huge lists of complex data structures, and those are certainly long running operations. They are not the most common tasks to do in XUL applications. What is a little more common is some kind of IO operation, like accessing a Web service, or reading from the local file system. This is exactly the kind of operation you will examine in this article. You will see that the Workers API allows you to not just spawn threads, but to execute arbitrarily complex tasks on the spawned thread. Finally, you usually want to perform some kind of update to your UI as a result of such operations, and doing this from another thread can be very hazardous. Luckily, the Workers API was designed with this in mind, and greatly simplifies what might be a difficult scenario. Look at how you can use the Workers API and examine it in the context of a simple application.


The taste test application

As mentioned earlier, one common use of multithreading is some type of IO operation, such as accessing a Web service over the Internet. Now, JavaScript/XUL has always provided multithreading for network operations: XMLHttpRequest. The secret sauce of Ajax defaults to performing the network IO on a background thread with the caller providing a callback function that gets invoked on the main thread after the IO completes. However, you do not have explicit control of this thread; the runtime does it for you. Further, what if you need to do multiple network operations? In that case your requests will get serialized by the runtime. In this application you will perform this kind of task—accessing multiple remote Web services simultaneously using the multiple threads through the Workers API. The application will let users perform a "blind taste test" as they view and compare anonymous results of these search engines: Google, Yahoo, and Microsoft's Bing.


Three search engines, three threads

The application that you will explore is pretty simple. It will allow the user to enter one or more search terms. It will then search the three search engines, and anonymously display the results so the user doesn't know which list of results came from which search engine. Start with the XUL code for the user interface (see Listing 1).

Listing 1. XUL UI code
<hbox>
    <label value="Enter" id="lbl"/>
    <textbox value="" id="term"/>
    <button label="Go!" onclick="search()" id="btn"/>
</hbox>

This is simple XUL code to create a search form. It creates a horizontal layout with three widgets. The first widget is a label, the second is a text box where the user enters a search term, and the last widget is a button. When a user clicks the button, the search function is invoked. This is where the worker threads are spawned, as in Listing 2.

Listing 2. Spawning Workers
function search(){
    var keyword = document.getElementById("term").value;
    var workerScripts = ["google.js", "yahoo.js", "bing.js"];
    var worker = {};
    for (var i=0;i<workerScripts.length;i++){
        worker = new Worker(workerScripts[i]);
        worker.onmessage = function(event) {
            displayResults(event.data);
        };
        worker.postMessage(keyword);
    }
}

If you used XUL based upon JavaScript before 1.9.1, then you have to queue up all three requests, or use XPCOM with C++ to retrieve the data efficiently. However, with JavaScript Workers, it becomes straightforward to spawn multiple threads to retrieve this data. Each Worker object takes a single string for its constructor. The string is the location of another JavaScript file that will become the body of the worker. The script in the worker's file will be executed on its own thread.

The three scripts in this question are titled google.js, yahoo.js, and bing.js. You put those into an array and then iterate over the array. For each script in the array, you create the Worker by simply passing in the string that specifies the location of the worker's script. Next, for each worker being created, you set its onmessage function. This is the function that is called whenever the worker sends data back to the main thread. In this case you simply delegate the work to another function called displayResults. Finally, to send the name of the search terms that you want to use with each search engine, we call the postMessages function on each Worker object. This will allow the worker thread to access the Web service, thus all three Web services are called concurrently. Not only are the calls simultaneous, but the processing of the results is also done in parallel.

So what exactly goes on with this onmessage/postMessage paradigm? This is the fundamental threading model behind the Workers API. The usual threading model that most people are familiar with is the model used by C++, Java™ technology, and many other programming languages. To communicate, threads generally modify shared memory. This is efficient, but leads to a lot of complications (semaphores, mutexes, and so on). These complexities are needed for UI programming as well, since you can quickly cause your UI to lock up or crash if multiple threads alter the UI. Instead of bringing all of these complexities to XUL, the JavaScript Workers API uses a simpler model. This model is based on message passing, and is somewhat similar to the actor model used in Erlang and Scala.

Going back to Listing 2, the Worker instance acts as a proxy to the spawned thread. To send the thread a message, the postMessage method on the Worker instance is invoked. Any object can be passed to this method. Of course the spawned thread can pass a message back to its parent thread. When it does this, the onmessage method of the Worker instance is invoked. This has a no-op default implementation, so you must override this (if you care about results coming back from the thread). In Listing 2, to override this, set it equal to a function that accepts a single parameter called event. This is the object that gets sent from the spawned thread. For the function in Listing 2, extract the data property of the event, as this is the actual data sent by the thread, and pass it to another function for updating the UI.

You have seen the parent thread's side of things. What about the spawned, child threads? As seen in Listing 2 above, a separate JavaScript file is used for each spawned thread. In Listing 3, look at the first of these for accessing the Google search API.

Listing 3. Google search worker
onmessage = function(event){
    var keyword = event.data;
    var results = searchGoogle(keyword);
    postMessage(results);
}

function searchGoogle(keyword){
    var url = "http://ajax.googleapis.com/ajax/services/"+
        "search/web?v=1.0&rsz=large&q="+keyword;
    var xhr = new XMLHttpRequest();
    xhr.open("GET", this.url, false);
    xhr.send();
    var response = JSON.parse(xhr.responseText);
    var results = [];
    var result = {};
    var data = response.results;
    for (var i=0;i<data.length;i++){
        result.url = data[i].url;
        result.title = data[i].title;
        result.description = data[i].content;
        results.push(result);       
    }
    return results; 
}

The code in Listing 3 has some similarities to what you saw in Listing 2. The thread has an onmessage method. This is the function that is invoked whenever a message is sent to the thread. If you go back to Listing 2, the postMessage method on the Worker instance is invoked. This causes the onmessage function in Listing 3 to be invoked. The object passed in to postMessage becomes the data property on the object passed to the onmessage method. This is the keyword being used for the search in this example.

Going back to Listing 3, once the keyword is extracted from the message that was passed in, the searchGoogle function is invoked. This function uses Google's search API to request search results for the keyword. To invoke the Web service, notice that you use the standard JavaScript API XMLHttpRequest. You might notice that when you send the request, using the open method, you specify three parameters. The last parameter specifies if the request is asynchronous or not. It defaults to true, so the request is asynchronous. In such cases, you need to override the onreadystatechange function of the XMLHttpRequest instance so that you can set a callback function for the asynchronous request. However, when you make such a request from a Worker thread, you don't need to do this. Instead, set the asynchronous flag to false, and make the call synchronously. Thus the send method blocks until the response has been returned from the Web service. This allows you to easily handle the response.

In this case the response from Google is a JSON structure. You can eval the response directly, but it is safer to use a parser. The standard JSON parser from json.org is included as part of JavaScript 1.9.1 as well. So you can simply use its parse method to safely parse the JSON data into a JavaScript object. The rest of the code simply extracts data from the structure returned by Google and puts it into a common structure. This data is then sent back to the parent thread by using the postMessage function. When the postMessage function on the worker thread is invoked, this will cause the onmessage method on the Worker instance (in the parent thread) to be called with the data sent from the worker thread. That is why you set this onmessage function back in Listing 2. Each of the other Workers does very similar work. For example, Listing 4 shows the worker thread for the Bing search engine.

Listing 4. Bing search worker
onmessage = function(event){
    var keyword = event.data;
    var results = searchBing(keyword);
    postMessage(results);
}

function searchBing(keyword){
    var bingAppId = "YOUR APP ID GOES HERE";
    var url = "http://api.search.live.net/json.aspx?Appid="+ 
        bingAppId+"&query="+keyword+"&sources=web";
    var xhr = new XMLHttpRequest();
    xhr.open("GET", this.url, false);
    xhr.send();
    var response = JSON.parse(xhr.responseText);
    var results = [];
    var results = {};
    var data = response.SearchResponse.Web.Results;
    for (var i=0;i<data.length;i++){
        result.url = data[i].Url;
        result.title = data[i].Title;
        result.description = data[i].Description;
        results.push(result);       
    }
    return results; 
}

As you can see, this is very similar to the code in Listing 3. It uses the onmessage/postMessage paradigm to receive and send data from its parent thread. The URL of the Web service is a little different, of course. The return structure is also a little different, but you simply map it back to the common structure seen in Listing 3. As you can imagine, the code for the Yahoo Worker is very similar to the Google and Bing Workers. The URL is a little different, and the mapping back to the normalized data structure is a little different (see Download for all of the source code). Once the data is in a normalized structure, it can be passed to a common function for updating the user interface.


Summary

Concurrent programming promises to be the key to successful applications both today and especially in the future. Applications that use multiple threads to get the most out of their environment can provide a superior user experience. In this article, you took a very simple example, querying three popular search engines. However, you can imagine the improvement in user experience it makes to retrieve the results from each engine simultaneously, instead of doing it one search engine at a time. Even on a single CPU machine this will make a big difference, as you do not waste as much time waiting on a response from a Web service. The Worker APIs added to JavaScript 1.9.1 make this type of programming easy for any XUL-based application. You can employ the techniques shown here in Firefox extensions or desktop applications that use XULRunner. The APIs are simple to use, and you can spawn multiple threads without worry about locking. The Workers API just removed any excuse to not use multithreading to improve your XUL applications .


Download

DescriptionNameSize
Search engine test sourcexul-workers.zip3KB

Resources

Learn

Get products and technologies

Discuss

Comments

developerWorks: Sign in

Required fields are indicated with an asterisk (*).


Need an IBM ID?
Forgot your IBM ID?


Forgot your password?
Change your password

By clicking Submit, you agree to the developerWorks terms of use.

 


The first time you sign into developerWorks, a profile is created for you. Information in your profile (your name, country/region, and company name) is displayed to the public and will accompany any content you post, unless you opt to hide your company name. You may update your IBM account at any time.

All information submitted is secure.

Choose your display name



The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerWorks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

Required fields are indicated with an asterisk (*).

(Must be between 3 – 31 characters.)

By clicking Submit, you agree to the developerWorks terms of use.

 


All information submitted is secure.

Dig deeper into XML on developerWorks


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=XML, Web development
ArticleID=423045
ArticleTitle=Explore multithreaded programming in XUL
publish-date=09012009