Rogue Wave banner
Previous fileTop of DocumentContentsIndexNext file

5.5 The IOU Classes

The IOU template classes implement a thread synchronization mechanism called a future, as explained in Section 5.2.1.

The concrete IOU classes are templates that are parameterized on the type of the result that is stored. This type has the following characteristics:

An IOU object possesses two distinct interfaces:

These two interfaces are realized by the IOU handle classes RWTIOUEscrow and RWTIOUResult.

You can convert from one IOU handle type to the other by using copy-construction:

5.5.1 Testing for Empty IOU Handles

An IOU handle instance can be empty. Any attempt to use an empty handle as if it were a valid IOU object produces an RWTHRInvalidPointer exception. You can determine whether an IOU handle is empty by using the isValid() member of the handle instance.

5.5.2 Constructing an IOU

The IOU mechanism is implemented using a handle-body architecture. The construction of an IOU handle does not result in the construction of a complete IOU object. To build a viable IOU object you must first construct an escrow object and bind that object to one or more IOU handles.

As is the case with many other Threads.h++ classes, three methods can be used to construct an IOU escrow object:

Only the third method is explained in this section. Complete descriptions of all IOU make functions and macros can be found in the Threads.h++ Reference Guide.

The RWTThreadEscrowImp template class in the Threading package has a static member function, make(), that dynamically allocates an escrow object and returns an RWTEscrowHandle instance bound to that escrow object. The RWTEscrowHandle instance can be assigned to, or used to initialize, an RWTIOUResult or RWTIOUEscrow handle instance, as in this example:

If you use the RWTRunnableIOUFunction and RWTThreadIOUFunction classes, found in the Threading package, you can avoid escrow construction entirely by letting these classes construct the escrow object for you. You can retrieve a handle to the internal escrow instance by calling the result() member function in these runnable classes. This function returns an RWTIOUResult handle instance.

5.5.3 Closing an IOU

In the Interthread Communication package, the act of storing or writing the result of an operation into an IOU is called closing the IOU. To close an IOU, you must possess an RWTIOUEscrow handle that references the IOU escrow object.

The RWTIOUEscrow class has two functions related to closing an IOU:

5.5.3.1 The close() Function

The close() function stores the final result of an operation into the IOU object. It releases any threads that have been waiting for the result.

RWTIOUEscrow also has a function operator called operator()() and an assignment operator called operator=() that have the same capability as the close() function. Given that "iou" is an instance of the RWTIOUEscrow class, the following statements are functionally identical:

5.5.3.2 The setException() Function

RWTIOUEscrow has two versions of the setException() function. Both indicate whether or not the operation failed to produce a result due to an error condition. One version of the function accepts a reference to an instance of the base exception class, RWTHRxmsg. The other version accepts an RWCString message that is used internally to initialize an RWTHRxmsg instance. In either case, these functions:

5.5.3.3 Example

Typical uses of the close() and setException() functions are shown in the code fragment in Example 47.

Example 47 -- Closing an IOU

The IOU object is intended as a "one-shot" communication mechanism. An IOU instance can only be closed once during its life-span-it is not possible to reset or reuse an IOU. Any attempt to do so produces an RWTHREscrowAlreadyClosed exception.

The RWTIOUEscrow class also has several functions that applications can call to query the status of an IOU object:

5.5.4 Redeeming an IOU

The act of reading a result from an IOU is called redeeming the IOU. To redeem an IOU, you must possess an RWTIOUResult handle that references the IOU escrow object.

The RWTIOUResult class has three functions for redeeming the IOU:

These three members perform an identical function. Given that iou is an instance of the RWTIOUResult class, the following statements are functionally equivalent:

5.5.4.1 Rules about Threads and IOUs

Threads use IOUs in the following ways:

5.5.4.2 Aborting a Request

The RWTIOUResult::abort() member can be used to signal that the IOU client no longer needs the result represented by the IOU. This function simply sets a flag within the IOU, so that it can be polled by another thread.

5.5.4.3 Querying the Status of an IOU Object

The RWTIOUResult class also has several functions that can be used to query the status of an IOU object:

5.5.5 Using IOUs

Obtaining results from asynchronous operations often requires complex synchronization that is difficult to code and difficult to understand. The IOU object has an easy-to-use mechanism that simplifies this retrieval process.

5.5.5.1 Example

The following example demonstrates the use of an IOU object for retrieving the results of an operation executed in another thread. Example 48 uses a form of threaded runnable that invokes a function and stores any return value into an IOU.

Example 48 -- Using an IOU object to retrieve results from another thread

//1Create a threaded runnable to execute the service.
//2Start the thread.
//3Return the IOU result for the service.
//4Invoke the asynchronous service.
//5Redeem the IOU.

5.5.5.2 Closing and Redeeming an IOU

In this example, the RWThreadIOUFunction class closes the IOU when the sync_service() function returns a result back to the runnable.

Threads attempting to redeem an IOU that has not been closed block until another thread writes a result or exception to that IOU object. If an IOU is closed with an exception, any attempt to redeem the IOU produces that exception.

For more information, see Section 5.5.3, "Closing an IOU," and Section 5.5.4, "Redeeming an IOU."

5.5.5.3 Using Active Objects

The example in Section 5.5.5.1 shows how an asynchronous operation can be created. Although the implementation seems straightforward, it has a potential problem-it does not have a way to join with the thread that has been created, so it has no way to insure that the thread has exited before any process eventually exits.

Using an active object solves the join problem. For more information, see Section 3.8, "Using Threads To Build Active Objects."

5.5.6 Waiting for IOUs

In the examples in Section 3.8.1, "Using Runnables," Section 3.8.2, "Using Runnable Servers,"and Section 5.5.5, "Using IOUs," the IOU was immediately redeemed to get a result, effectively producing synchronous behavior. A more interesting solution includes launching several asynchronous operations at the same time.

5.5.6.1 Asynchronous Example

In Example 49, four operations are started before attempting to redeem the results. Assume that:

In this hypothetical situation, the IOU results are likely to become redeemable in an order different from the order in which the corresponding operations were launched.

Example 49 -- Responding to results from asynchronous operations in order of launch

Redeeming the IOUs in the way shown above forces the results to be acted upon in the same order in which the operations were launched. To take maximum advantage of the concurrency of threads, the application needs to be designed to process the results of these operations in the order in which they become available.

Consider a hypothetical Web browser written using the Threading and Interthread Communication packages. A typical Web page can consist of text and images. Each of the images is identified by a Web link and can be distributed across various sites on a network. The browser, after loading a Web page containing these image links, can choose to launch separate threads to handle the retrieval of each image. In this situation, IOUs can represent the individual images being retrieved by each of the threads. If the display code in the browser were forced to redeem the IOUs in the same sequence as the retrieval operations were launched, then a lengthy transfer could unnecessarily delay rendering of the remaining portions of the page. If, however, our hypothetical browser could draw each image as it arrives, the user would see each image on the page appear as soon as it became available.

5.5.7 Trapping IOUs with RWTIOUTrap

The RWTIOUTrap template class can retrieve the next redeemable IOU in a group of IOUs. This class uses an internal producer-consumer queue to capture RWTIOUResult instances as they become redeemable.

5.5.7.1 Waiting for Trapped IOUs

A thread waits for trapped IOUs to become redeemable by calling the trap's getNext() function. This function returns the next IOU in its internal queue, and waits for an IOU to arrive if the queue is found to be empty.

5.5.7.2 Using Timed Waits

An RWTIOUTrap can also be used to implement a timed wait on an IOU result. This capability is significant because the RWTIOUResult class does not allow a time-limit to be placed on a redemption operation. The following code demonstrates this capability:

Example 50 -- Implementing a timed wait on an IOU result

To see another example of RWTIOUTrap usage, refer to Section 5.5.6, "Waiting for IOUs."

5.5.7.3 Improving the Asynchronous Example

To wait for the next redeemable IOU, use the RWTIOUTrap class. It uses an internal producer-consumer queue to capture RWTIOUResult instances as they become redeemable. To trap an IOU result, the code must pass the IOU handle to the RWTIOUTrap instance in a call to the trap's setTrap() function. This approach is an improvement on the way asynchronous operations were handled in Example 49.

Once an IOU has been registered with a trap, a handle to the IOU is automatically enqueued inside the trap when the IOU is closed. An IOU can be registered with any number of traps-a separate handle is enqueued in each trap instance at IOU closure.

If the IOU has already been closed and is redeemable when the IOU is added to a trap, a redeemable IOU is enqueued immediately during the setTrap() call.

A thread waits for trapped IOUs to become redeemable by calling the trap's getNext() function. This function returns the next IOU in its internal queue and waits for an IOU to arrive if the queue is empty.

See Section 5.5.7, "Trapping IOUs with RWTIOUTrap," for additional information regarding this class.

5.5.7.4 Another Asynchronous Example

Example 51 provides another alternative for processing the results of asynchronous operations. In this example, the RWRunnableServer is replaced by an RWServerPool, an identification value is added for each operation invocation, and the RWTIOUTrap processes the results of the operations.

Example 51 -- Using an IOU trap to handle results from asynchronous operations

//1Define the actual service to be performed.
//2Define a constructor that creates and starts the internal server pool. The constructor argument specifies the number of runnable server threads to create within the pool.
//3Define a destructor that stops the internal server and waits for it to exit.
//4Define a member function to initiate an asynchronous operation by constructing a synchronous runnable and passing it to the internal server. The IOU representing the result of the operation is returned to the caller.
//5Construct an instance of the service provider.
//6Create an IOU trap instance.
//7Start one of several asynchronous service requests using the loop index to identify each request. Add the IOU result of each request to the trap.
//8Redeem the next available IOU result provided by the trap, blocking (if necessary) until the trap captures another result.

Previous fileTop of DocumentContentsIndexNext file

©Copyright 2000, Rogue Wave Software, Inc.
Contact Rogue Wave about documentation or support issues.