Sybase Technical Library - Product Manuals Home
[Search Forms] [Previous Section with Hits] [Next Section with Hits] [Clear Search] Expand Search

List of topics [Table of Contents] Browse mode

Open Client Client-Library/C Reference Manual

[-] Chapter 2 Topics
[-] Asynchronous programming

Asynchronous programming

Asynchronous applications are designed to make constructive use of time that would otherwise be spent waiting for certain operations to complete. Typically, reading from and writing to a network or external device is much slower than straightforward program execution. Also, it takes time for a server to process commands and send results back to the client application.

Some applications execute several tasks that involve idle time. For example, an interactive application might:

  1. Wait for user input

  2. Execute commands on a connection to Server X

  3. Execute commands on a connection to Server Y

Client-Library's asynchronous modes help such an application to execute these tasks concurrently. When executing server commands, routines that send commands or read results return immediately. This means the application calls another routine to start command operations on another connection, and the application responds more quickly to user input.

Client-Library's asynchronous mode is one method for achieving concurrent task execution. The other is to use multiple threads. For information on using Client-Library in a multithreaded environment, see "Multithreaded programming".

Asynchronous applications

By default, Client-Library applications are synchronous. Routines that read from or write to the network do not return control to the caller until all the necessary I/O requests are complete.

When writing an asynchronous application, the application programmer must enable asynchronous Client-Library behavior at the context or connection level by setting the Client-Library property CS_NETIO. The possible network I/O modes are:

When fully asynchronous or deferred-asynchronous mode is enabled, all Client-Library routines that read from or write to the network either:

By returning CS_PENDING, a routine indicates that the requested operation has begun and will complete asynchronously. The application receives the completion status from the call either by polling (that is, calling ct_poll periodically) or when Client-Library invokes the application's completion callback. Both methods are described under "Completions".

Asynchronous routines

The following Client-Library routines behave asynchronously:

The CS_BUSY return code

Any Client-Library routine that takes a command or connection structure as a parameter returns CS_BUSY. CS_BUSY indicates that a routine cannot perform because the relevant connection is currently busy, waiting for an asynchronous operation to complete.

An application calls the following routines while an asynchronous operation is pending:

Completions

Every asynchronous mode Client-Library call that returns CS_PENDING produces a . This value corresponds to the return code that would have been returned by a synchronous-mode call to the routine (for example, CS_SUCCEED or CS_FAIL).

An application determines when an asynchronous routine completes either by polling or via completion callbacks. For polling, the application periodically calls ct_poll to determine if the asynchronous call has completed. With completion callbacks, the application is automatically notified when asynchronous calls complete.

To properly exit Client-Library, wait until all asynchronous operations are complete, then call ct_exit.

If an asynchronous operation is in progress when ct_exit is called, the routine returns CS_FAIL and does not exit Client-Library properly, even when CS_FORCE_EXIT is used.

Deferred asynchronous completions

The application polls for the completion status by calling ct_poll periodically until ct_poll indicates that the operation is complete. This mode of operation is called and corresponds to setting the CS_NETIO property to CS_DEFER_IO.

If the application installs a completion callback, the callback routine is called by ct_poll when ct_poll detects the completion. The application itself must call ct_poll.

The application learns the completion status of the asynchronous call from ct_poll. If a completion callback is installed, it also receives the completion status as an input parameter.

The Client-Library chapter in the Open Client/Server Programmer's Supplement describes the asynchronous modes, if any, that are supported on your platform.

Fully asynchronous completions

On platforms where Client-Library uses signal-driven or thread-driven I/O, Client-Library automatically calls the application's completion callback routine when an asynchronous routine completes. This mode of operation is called and corresponds to setting the CS_NETIO property to CS_ASYNC_IO.

When a connection is fully asynchronous, the application does not have to poll for the completion status. Client-Library automatically invokes the application's completion callback, which receives the completion status as an input parameter. Completion callbacks are described under "Defining a completion callback".

When Client-Library is used within an Open Server, the CS_NETIO property cannot be set to CS_ASYNC_IO. The Open Server thread scheduler allows multitasking in an Open Server application.

On asynchronous connections, it is possible for Client-Library to complete an asynchronous operation and call the callback routine before the initiating routine returns. When this happens, the initiating routine still returns CS_PENDING, and the application's completion callback receives the completion status.

Client-Library's fully asynchronous operation is either thread-driven or signal-driven. On platforms that do not support either multiple threads or signal-driven I/O, Client-Library cannot be fully asynchronous.

Signal-driven completion handling

On some platforms such as UNIX, Client-Library uses operating system signals (also called interrupts) to read results and send commands over the network. Internally, Client-Library interacts with the network using non-blocking system calls and installs its own internal signal handler to receive the completion status for these system calls.

Note that on signal-driven I/O platforms, Client-Library may be signal-driven even when the CS_NETIO property is not CS_ASYNC_IO. On signal-driven I/O platforms, Client-Library uses signal-driven I/O if any of the following is true:

Warning!

When Client-Library uses signal-driven I/O, a signal can interrupt the processing of system calls made by the application. If an error code indicates that a system call was interrupted, reissue the call.

On platforms where signal-driven I/O is used to implement Client-Library's fully asynchronous mode, fully asynchronous applications have the following restrictions:

Thread-driven completion handling

On some platforms, such as Windows NT, Client-Library uses thread-driven I/O to operate in fully asynchronous mode.

When this I/O strategy is used, Client-Library spawns internal worker threads to interact with the network. When the application calls a routine that requires network I/O, the I/O request is passed to the worker thread. The asynchronous routine then returns CS_PENDING and the worker thread waits for the completion. When the I/O request completes, the worker thread calls the application's completion callback.

On platforms where thread-driven I/O is used, fully asynchronous applications have the following restrictions:

On thread-driven I/O platforms such as Windows NT, a fully asynchronous program is multithreaded in its callback execution even if the mainline code is single-threaded. For thread-driven I/O, a Client-Library worker thread interacts with the network for each fully asynchronous connection. The worker thread invokes the connection's callbacks for any callback events that it discovers. See "Fully asynchronous completions".

When fully asynchronous I/O is in effect on platforms where Client-Library uses thread-driven I/O, the application's callbacks are invoked by a Client-Library worker thread. On these platforms, a fully asynchronous application's callbacks are multithreaded even if the application itself uses a single-threaded design.

Issues affecting multithreaded application design are discussed in "Multithreaded programming".

Client-Library's interrupt-level memory requirements

On operating systems where Client-Library uses signal-driven I/O, such as UNIX-based systems and Digital OpenVMS VAX, fully asynchronous applications must provide a way for Client-Library to satisfy its interrupt-level memory requirements.

Ordinarily, Client-Library routines satisfy their memory requirements by calling malloc. However, not all implementations of malloc are safely called at the interrupt level. For this reason, fully asynchronous applications are required to provide an alternate way for Client-Library to satisfy its memory requirements.

Client-Library provides two mechanisms by which an asynchronous application satisfies Client-Library's memory requirements:

On platforms that use signal-driven I/O, Client-Library's behavior is undefined if a fully asynchronous application fails to provide a safe way for Client-Library to satisfy memory requirements.

Client-Library attempts to satisfy memory requirements from the following sources in the following order:

  1. Memory pool

  2. User-supplied allocation and free routines

  3. System routines

Layered applications

Asynchronous applications are often layered. In these types of applications, the lower layer protects the higher layer from low-level asynchronous detail.

The higher-level layer typically consists of:

The lower-level layer typically consists of:

Using ct_wakeup and CS_DISABLE_POLL

ct_wakeup and the CS_DISABLE_POLL property are used in layered asynchronous applications as follows:

A layered application that is using a routine to perform a large operation typically uses ct_wakeup and CS_DISABLE_POLL as follows:

  1. The application performs any necessary initialization, installs callback routines, opens connections, and so on.

  2. The application calls the routine that is performing the large operation.

  3. If the application uses ct_poll to check for asynchronous completions, then the routine must disable polling. This prevents ct_poll from reporting lower-level asynchronous completions to the higher-level layer. To disable polling, the routine sets CS_DISABLE_POLL to CS_TRUE.

    If the application does not call ct_poll, the routine does not need to disable polling.

  4. The routine calls ct_callback to replace the higher-level layer's completion callback with its own completion callback.

  5. The routine performs its work.

  6. The routine reinstalls the higher-level layer's completion callback.

  7. If polling has been disabled, the routine enables it again by setting the CS_DISABLE_POLL property to CS_FALSE.

  8. The routine calls ct_wakeup to trigger the higher-level layer's completion callback routine.

An example

An application that performs asynchronous database updates might include the routine do_update, where do_update calls all of the Client-Library routines that are necessary to perform a database update.

The main application calls do_update asynchronously and go on with its other work.

When called, do_update replaces the main application's completion callback routine with its own callback (so that the main application's callback routine is not triggered by low-level asynchronous completions). It then proceeds with the work of the update. To perform the update, do_update calls several Client-Library routines, including ct_send and ct_results, which behave asynchronously. When each asynchronous routine completes, it triggers do_update's completion callback.

When do_update has finished the update operation, it reinstalls the main application's completion callback and calls ct_wakeup with function as its own function ID. This triggers the main application's completion callback, letting the main application know that do_update has completed.


List of topics [Table of Contents] Browse mode