Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

Using Asynchronous Calls in COM


April 1999/Using Asynchronous Calls in COM

Using Asynchronous Calls in COM

Greg Heidel

Borrowing against the future is a great way to get more done right now, both in real life and in asynchronous programming.


Introduction

Anyone who has ever used an application with a GUI (graphical user interface) knows the frustration when the dreaded hourglass rears its ugly head. While computers are getting faster and faster, developers must still realize that time-intensive operations, such as database queries, HTTP requests, and sorts, still require more time than the average user is willing to wait. This can be a significant drawback when building interfaces based on COM. COM method calls are synchronous by default, which means that the client thread is blocked until the response is received. In a typical, single-threaded application, this is typically how method invocation works and therefore makes sense as the default behavior. However, synchronous operation is not desirable in all cases. In many cases, such as a lengthy request to retrieve some data, control needs to be returned to the application end-user while the application continues processing the task in the background.

To avoid the undesirable effects of blocking, developers sometimes resort to writing code to spawn additional threads, or delegating the task to another process. The burden of writing, testing, and debugging this additional code falls on the developer. What's more, spawning threads or processes is not an easy task in some environments, particularly Visual Basic. A better solution is to have the COM object implement this functionality and allow the developers to determine how they want to retrieve the results when they are available. IOUs, also known as futures, provide a means for implementing COM objects that support asynchronous methods. In this article I explain the IOU mechanism and show how you can implement it in your own COM objects. This article assumes knowledge of C++, COM, and ATL.

Synchronous and Asynchronous Methods

First, a quick review of synchronous and asynchronous behavior. Normal function invocations are synchronous. A function performs some operation that produces a result or return value and then returns that result to the caller of the function. The caller's thread of control blocks until the result is finished. A thread may have only one blocking call outstanding at any one time.

Asynchronous functions return to their caller before their associated operation has finished. Using this technique, your application can execute other operations instead of blocking until the operation completes.

Enter the IOU

An IOU is a promise for a value that is forthcoming. It is a placeholder for that value. Usually the writer of an IOU (perhaps another process or a remote server) and the reader (client) of an IOU are in different threads of control. In this sense IOUs are a mechanism for inter-thread communication. An IOU is a write-once/read-many structure. It may be written to only once, but may be read any number of times.

A function that is called to launch an asynchronous operation can immediately return an IOU instance that is bound to the future result of that operation. At any time after that point, any thread can attempt to redeem the IOU. Redeeming an IOU is the process of getting the result from the IOU when it is available. If a thread attempts to redeem the IOU before the operation is completed, that thread is blocked until the result is redeemable (available) or the operation is aborted. A thread can obtain the result of an IOU by two methods: it can test if the value has been set, and then redeem the IOU; or it can register to be notified when the IOU is redeemable.

In the following discussion I present some of the details involved in implementing an IOU. The examples used in this article are supplemented using the ATL class library from Microsoft.

The class diagram in Figure 1 illustrates the class relationships used in the sample code in this article. The IOU class used in this article is the parameterized class ResultIOU_ (Figure 2). The template parameter T represents the type of the value stored in the result IOU. This type must have a public copy constructor and allow dynamic allocation using the new operator. This is necessary because when the ResultIOU_ class is instantiated it will create a new copy of the stored value.

ResultIOU_ incorporates the handle-body and reference counting idioms. These techniques are used to simplify memory management, provide for efficient by-value sharing, and provide for automated object lifetime management. The handle is a very lightweight class that can be passed by value. The handle contains a reference to a body. As it is manipulated within an application, the handle increments or decrements a reference count maintained within the body. This count is incremented each time a handle is bound to the body instance and it is decremented each time a handle is detached from the body instance. When the last handle to a given body instance is destroyed, the body instance is deleted.

ResultIOU_ uses an event object and a mutex object to coordinate the execution of multiple threads. The event object is used to delay the calling thread until the result is redeemable. An event is simply a mechanism upon which threads can wait and signal. It is the programmer's responsibility to implement the code to wait at the appropriate point of execution when the event is false, and to signal when the event becomes true. The event class is a simple class wrapper for the Windows event object. To delay on an event variable, a thread calls the wait method. The wait method checks the current state. If the state is nonsignaled, the calling thread enters an efficient wait state. Another thread can unblock waiting threads by calling the signal method. ResultIOU_ uses an event object to coordinate thread access to the IOU result.

A mutex object is a synchronization object whose state is set to signaled when it is not owned by any thread, and nonsignaled when it is owned. Only one thread at a time can own a mutex object, whose name comes from the fact that it is useful in coordinating mutually exclusive access to a shared resource. ResultIOU_ uses a mutex object to synchronize access to its data members.

Here's a synopsis of the methods in the IOU class:

void Close(T result)

Set the result. The thread performing the requested asynchronous operation will call this method to set the result in the IOU upon completion of the operation.

bool Redeemable()

Test the IOU to see if it is redeemable. The IOU is redeemable if the result has been set. Redeemable is used in a polling implementation to test the IOU. Redeemable will return immediately.

T Redeem()

Retrieve the result contained in the IOU. This call will block until the IOU is redeemable or until the requested asynchronous operation is aborted.

void Abort()

Cancel the operation. This operation simply releases any threads that are blocking on the IOU. The IOU is redeemable after completion of the abort operation.

bool Aborted()

Test the IOU to see if it is has been aborted This is a non-blocking method. The implementation of ResultIOU_ is shown in Figure 3.

Notification Mechanisms

The holder of the IOU decides how and when to redeem the result held by the IOU. There are three common ways to redeem an IOU.

1. Calling the Redeem method. This call will block the caller until the result is ready.

2. Polling. Poll the IOU via the Redeemable method. Redeemable returns immediately regardless of whether the IOU is redeemable. An IOU is redeemable if a result has been set or the IOU operation has been aborted.

3. Notification. The IOU can notify the calling thread via a defined notification mechanism. Possible options include posting a windows message, invoking a callback function, or using a COM event.

In this article I use the COM event, also known as COM connectable object, architecture to provide the IOU notification mechanism.

COM and Connectable Objects

The COM standard for providing event notification is connectable objects. Connectable objects provide a standard way to establish two-way communication between an object and a client. Connectable objects stipulate the specifics for creating and destroying this two-way communication between objects.

In order for the IOU to notify the client using connectable objects, the IOU must support one or more outgoing COM interfaces. An outgoing interface is an interface that the IOU defines but does not implement. Since a connectable object defines the outgoing interface, it knows how to invoke that interface's methods. The holder of the IOU (the client) actually implements this interface. The client then passes the IOU a pointer to this interface when it registers for the notification event. The IOU holds onto this interface pointer and then uses it to notify the client when the result is redeemable. This process is analogous to a C/C++ callback.

In the sample application (described below) the outgoing interface is IResultNotifier. The IResultNotifier interface defines a single method, RequestComplete. This is the method that the IOU will call when the server has finished processing the request.

Sample Application

I now present a sample application that uses the IOU. The example simulates sending a request to a server. The sample does not actually send a request but merely sleeps for three seconds and then returns a simulated value. The purpose of the sample is to convey the basic IOU framework. The request asks for an address, based on a business name. In a more realistic example the request would go to a remote server, or the request might be for a list of addresses. Either of these cases might require a number of seconds to satisfy the request and are therefore likely candidates for an IOU.

The sample code includes two class wrappers. The two wrappers primarily hide the COM implementation details from the developer. These details include object creation, error handling, and lifetime management i.e. interface reference counting. The Request class wrapper (Figure 4) additionally implements the event notification mechanism for asynchronous requests. Request accomplishes this using the class IOUSink. IOUSink (Figure 5) provides the implementation of the outgoing interface IResultNotifier, and more specifically, the event notification method RequestComplete.

If asynchronous notification is requested, an IOUSink is created and passed a pointer to the IOU. When the request is completed the RequestComplete method will be called. RequestComplete then calls the virtual method ResultIOUComplete of the IOU class ResultIOU.

The ResultIOU class wrapper (Figure 6) provides an empty default implementation of ResultIOUComplete. The sample application illustrates a simple derived class MyIOU. The code is shown in Figure 7. The code simply creates a MyIOU object and a Request object. Request's select method is called to simulate the selection criteria for the request and then the send method is invoked. The bool variable notify is set based on a command-line argument; it controls whether the request is sent asynchronously or synchronously. MyIOU overrides the ResultIOUComplete method from ResultIOU. It redeems the IOU, and outputs the value to the standard output.

The following shows the output that results from running the sample program with and without the /asynch command-line argument:

C:\>ioutest
701 Brazos St.
Press Enter to end...

C:\>ioutest /asynch
Press Enter to end...
701 Brazos St.

Conclusion

That's a quick overview of using an IOU to implement asynchronous methods in COM. There are a number of other areas related to the implementation of the IOU and COM. These areas are left as homework for the reader. The IOU code and the sample code is available for download from the CUJ ftp site (see p. 3 for downloading instructions). The sample code includes the complete implementation of the COM objects and the underlying C++ code to support the IOU. The sample application is also available.

IOUs provide a flexible method for implementing asynchronous COM methods. They allow the developer to choose between blocking, polling, or notification to redeem the value from a method invocation. Additionally, with a small amount of additional work, the developer can provide the user with alternative methods of notification.

Greg Heidel is a senior principal at TSC (Technology Solutions Company). TSC delivers business and technology consulting services that help clients transform customer relationships and improve operations.


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.