Frontier Tutorials / Working With Threads / An Example

An Example

As an example of a threaded application, let's look at a web search CGI that searches multiple commercial search engines and combines the results. (I'll not provide an entire CGI; that's left as an exercise for the reader. <evil laugh>)

First, we need a main thread, or executive. It takes the search terms from the paramTable and starts a separate thread for each search engine to be called.

local
space picturequeryThreadInfo = ThreadSafeGetUnique( tableType, "MultiQuery", @system.temp )
space picturetempThreadID
« Create Yahoo subtable
new( tableType, @queryThreadInfo^.yahoo )
« Copy params table for Yahoo query
queryThreadInfo^.yahoo.params = searchParams^
« Start the Yahoo query thread
queryThreadInfo^.yahoo.threadID = thread.easyCall( "queryYahoo", @queryThreadInfo^.yahoo )
« Create AltaVista subtable
new( tableType, @queryThreadInfo^.altaVista )
« Copy params table for AltaVista query
queryThreadInfo^.altaVista.params = searchParams^
« Start the AltaVista query thread
queryThreadInfo^.altaVista.threadID = thread.easyCall( "queryAltaVista", @queryThreadInfo^.altaVista )

(The ThreadSafeGetUnique script is documented on the Thread Utilities page.)

Then, for each search engine known to the CGI, we need a routine that constructs the query from parameters provided by the executive, calls tcp.httpClient to query the search engine, extracts the search results from the HTML page it received from the search engine into a table for use by the executive, and exits. These search-engine-specific routines are called queryYahoo and queryAltaVista in the code fragment above.

While the separate query threads are running, the executive has to bide its time, waiting for them to complete. The simple way to do this is for it to monitor a completion flag in the query threads' parameter tables.

local
space pictureyahooQueryCompleted = false
space picturealtaVistaQueryCompleted = false
space picturequeriesCompleted = false
space picture 
while ( ! queriesCompleted )
space picturethread.sleepFor( 1 )
space pictureif ( ! yahooQueryCompleted )
space pictureyahooQueryCompleted = queryThreadInfo^.yahoo.queryCompleted
space pictureif ( ! altaVistaQueryCompleted )
space picturealtaVistaQueryCompleted = queryThreadInfo^.altaVista.queryCompleted
space picturequeriesCompleted = yahooQueryCompleted && altaVistaQueryCompleted

The call to thread.sleepFor results in this loop executing once every second. We could simply call thread.exists to determine whether each thread has completed, but the queryCompleted flags allow the query threads to indicate when they have completed successfully. We should actually check both, so we will know if the thread terminates for some other reason, so we don't wait forever for the flag to be set. Also, since Web interactions can take a long time, we should have time-out logic that will kill a query thread if it takes longer than a user-specified time.

The final stage, then, will be to combine the results received and extracted from the various search engines into a single report page, and return it to the user.

The processing timeline then looks something like this:

Executive -- Startup Phase
  1. Parse search parameters.
  2. Start separate threads to query Yahoo and AltaVista.
  3. Give each query thread its own copy of the search parameters table.
Monitoring Phase
  1. Monitor completion flag in each query thread's parameter table.
  2. When completion flag is set in one query thread's parameter table, copy search results from query thread's parameter table to combined results table and delete query thread's parameter table.
  3. When all query threads have completed, advance to reporting phase.
Yahoo Query Thread
  1. Generate Yahoo query from parameter table.
  2. Call tcp.httpClient to get query results page from Yahoo.
  3. Extract query results from query results page and insert them into results subtable of parameter table. Convert relevancy rankings to internal scale.
  4. Set completion flag in parameter table.
AltaVista Query Thread
  1. Generate AltaVista query from parameter table.
  2. Call tcp.httpClient to get query results page from Yahoo.
  3. Extract query results from query results page and insert them into results subtable of parameter table. Convert relevancy rankings to internal scale.
  4. Set completion flag in parameter table.
Reporting Phase

  1. Merge results from separate threads into combined results table.
  2. Render combined results table to HTML and return the page to the user.

This is, of course, a greatly simplified example. A complete multiple-search CGI would need at least the following additions:

So much for how threads work. Let's take a closer look at Frontier's thread verbs.

Tutorial Contents
Working With Threads
What Are Threads?
Semaphores--Traffic Control for Threads
How to Be Thread-Friendly
Rules of Thread Safety
An Example
Frontier's Thread Verbs
Thread Utilities
Glossary of Terms
About the Author