Creating Thread-Safe ESQL/C Applications
ESQL/C provides shared and static thread-safe as well as shared and static default versions of the Informix general libraries on a UNIX operating system, and only thread-safe shared libraries in a Windows environment.
A thread-safe ESQL/C application can have one active connection per thread and many threads per application. The thread-safe libraries contain thread-safe (or reentrant) functions. A thread-safe function is one that behaves correctly when several threads call it simultaneously.
In the case of ESQL/C on a UNIX operating system, the thread-safe Informix general libraries use functions from the Distributed Computing Environment (DCE) thread package. The DCE thread library, which the Open Software Foundation (OSF) developed, creates a standard interface for thread-safe applications.
In Windows environments, the Informix general libraries use the Windows API to ensure that they are thread safe. 
With the thread-safe Informix general libraries, you can develop thread-safe ESQL/C applications. A thread-safe application can have many threads of control. It separates a process into multiple execution threads, each of which runs independently. While a non-threaded ESQL/C application can establish many connections to one or more databases, it can have only one active connection at a time. An active connection is one that is ready to process SQL requests. A thread-safe ESQL/C application can have one active connection per thread and many threads per application.
When you specify the -thread command-line option, the esql command passes this option to the ESQL/C preprocessor, esqlc. With the -thread option, the ESQL/C preprocessor generates thread-safe code that different threads can execute concurrently.
Characteristics of Thread-Safe ESQL/C Code
The thread-safe ESQL/C code has the following characteristics that are different from non-thread-safe code:
For example, ESQL/C allocates sqlda structures dynamically and binds host variables to these sqlda structures at runtime. For more information on sqlda structures to perform dynamic SQL, see Chapter 12, "Working with the Database Server."
Because of the preceding differences, the thread-safe C source file (.c) that the ESQL/C preprocessor generates is different from the non-threaded C source file. Therefore, you cannot link ESQL/C applications that have been compiled with the -thread option with applications that have not already been compiled with -thread. To link such applications, you must compile both applications with the -thread option.
Programming a Thread-Safe ESQL/C Application
This section provides useful hints for how to create thread-safe ESQL/C applications. It discusses the following programming techniques for a thread-safe environment:
Concurrent Active Connections
In a thread-safe ESQL/C application, a database server connection can be in one of the following states:
The major advantage of a thread-safe ESQL/C application is that each thread can have one active connection to a database server. Use the CONNECT statement to establish a connection and make it active. Use the SET CONNECTION statement (with no DORMANT clause) to make a dormant connection active.
When a thread makes an active connection dormant, that connection becomes available to other threads. Conversely, when a thread makes a dormant connection active, that connection becomes unavailable to other threads. Use the SET CONNECTION...DORMANT statement to explicitly put a connection in a dormant state.
The current connection is the active database server connection that is currently sending SQL requests to, and possibly receiving data from, the database server. A single-threaded application has only one current (or active) connection at a time. In a multithreaded application, each thread can have a current connection. Thus a multithreaded application could have multiple active connections simultaneously.
When you switch connections with the SET CONNECTION statement (with no DORMANT clause), SET CONNECTION implicitly puts the current connection in the dormant state. Once in a dormant state, a connection is available to other threads. Any thread can access any dormant connection. However, a thread can only have one active connection at a time.
Figure 13-4 shows a thread-safe ESQL/C application that establishes three concurrent connections, each of which is active.
In Figure 13-4, the ESQL/C application consists of the following threads:
All connections in Figure 13-4 are concurrently active and can execute SQL statements. The following code fragment establishes the connections that Figure 13-4 illustrates. It does not show DCE-related calls and code for the start_threads() function.
You can use the ifx_getcur_conn_name() function to obtain the name of the current connection. For more information, see "Identifying an Explicit Connection".
Connections Across Threads
If your application contains threads that need to use the same connection, one thread might be using the connection when another thread needs to access it. To avoid this type of contention, your ESQL/C application must manage access to the connections.
The simplest way to manage a connection that several threads must use is to put the SET CONNECTION statement in a loop. (For more information about the SET CONNECTION statement, see the Informix Guide to SQL: Syntax.) The code fragment in Figure 13-5 shows a simple SET CONNECTION loop.
The preceding algorithm waits for the connection that the host variable :con_name names to become available. However, the disadvantage of this method is that it consumes CPU cycles.
The following code fragment uses the CONNECT statement to establish connections and SET CONNECTION statements to make dormant connections active within threads. It also uses SET CONNECTION...DORMANT to make active connections dormant. This code fragment establishes the connections that Figure 13-4 illustrates. It does not show DCE-related calls and code for the start_threads() function.
In this code fragment, thread_1() uses a SET CONNECTION statement loop (see Figure 13-7) to wait for con2 to become available. Once thread_2() makes con2 dormant, other threads can use this connection. At this time, the SET CONNECTION statement in thread_1() is successful and thread_1() can use the con2 connection to update table t2.
The DISCONNECT ALL Statement
The DISCONNECT ALL statement serially disconnects all connections in an application. In a thread-safe ESQL/C application, only the thread that issues the DISCONNECT ALL statement can be processing an SQL statement (in this case, the DISCONNECT ALL statement). If any other thread is executing an SQL statement, the DISCONNECT ALL statement fails when it tries to disconnect that connection. This failure might leave the application in an inconsistent state.
For example, suppose a DISCONNECT ALL statement successfully disconnects connection A and connection B but is unable to disconnect connection C because this connection is processing an SQL statement. The DISCONNECT ALL statement fails, with connections A and B disconnected but connection C open. Informix recommends that you issue the DISCONNECT ALL statement in the main function of your application after all threads complete their work.
While the DISCONNECT ALL statement is serially disconnecting application connections, ESQL/C blocks other connection requests. If another thread requests a connect while the DISCONNECT ALL statement executes, this thread must wait until the DISCONNECT ALL statement completes before ESQL/C can send this new connection request to the database server.
Prepared Statements Across Threads
PREPARE statements are scoped at the connection level. That is, they are associated with a connection. When a thread makes a connection active, it can access any of the prepared statements that are associated with this connection. If your thread-safe ESQL/C application uses prepared statements, you might want to isolate compilation of PREPARE statements so that they are compiled only once in a program.
One possible way to structure your application is to execute the statements that initialize the connection context as a group. The connection context includes the name of the current user and the information that the database environment associates with this name (including prepared statements).
For each connection, the application would perform the following steps:
1. Use the CONNECT statement to establish the connection that the thread requires.
2. Use the PREPARE statement to compile any SQL statements that are associated with the connection.
3. Use the SET CONNECTION...DORMANT statement to put the connection in the dormant state.
Once the connection is dormant, any thread can access the dormant connection through the SET CONNECTION statement. When the thread makes this connection active, it can send the corresponding prepared statement(s) to the database server for execution.
In Figure 13-6, the code fragment prepares SQL statements during the connection initialization and executes them later in the program.
The code fragment in Figure 13-6 performs the following actions:
1. The main thread calls start_con_threads(), which calls start_con_thread() to start two threads:
2. The main thread calls start_execute_threads(), which calls start_execute_thread() to execute the prepared statements for each of the two threads:
3. The main thread disconnects all connections.
Cursors Across Threads
Like prepared statements, cursors are scoped at the connection level. That is, they are associated with a connection. When a thread makes a connection active, it can access any of the database cursors that are declared for this connection. If your thread-safe ESQL/C application uses database cursors, you might want to isolate the declaration of cursors in much the same way that you can isolate prepared statements (see "Prepared Statements Across Threads"). The following code fragment shows a modified version of the start_con_thread() function (in Figure 13-6). This version prepares an SQL statement and declares a cursor for that statement:
For a sample thread-safe program that uses cursors across threads, see "A Sample Thread-Safe Program".
Environment Variables Across Threads
Environment variables are not thread-scoped in a thread-safe ESQL/C application. That is, if a thread changes the value of a particular environment variable, this change is visible in all other threads as well.
Decimal Functions
The dececvt() and decfcvt() functions of the ESQL/C library return a character string that can be overwritten if two threads simultaneously call these functions. For this reason, use the thread-safe versions of these two functions, ifx_dececvt() and ifx_decfcvt(), respectively. For more information, see "Using ESQL/C Thread-Safe Decimal Functions".
DCE Restrictions
A thread-safe ESQL/C code is also subject to all restrictions that the DCE thread package imposes. DCE requires that all applications that use the DCE thread library be ANSI compliant. This section lists some of the restrictions to keep in mind when you create a thread-safe ESQL/C application. For more information, refer to your DCE documentation.
Operating-System Calls
You must substitute DCE thread-jacket routines for all operating-system calls within the thread-safe ESQL/C application. Thread-jacket routines take the name of a system call, but they call the DCE pthread_lock_global_np() function to lock the global mutual exclusion lock (mutex) before they call the actual system service. (Mutexes serialize thread execution by ensuring that only one thread at a time executes a critical section of code.) The DCE include file, pthread.h, defines the jacketed versions of system calls.
The fork() Operating-System Call
In the DCE environment, restrict use of the fork() operating-system call. In general, terminate all threads but one before you call fork(). An exception to this rule is when a call to the exec() system call immediately follows the fork() call. If your application uses fork(), Informix recommends that the child process call sqldetach() before it executes any ESQL/C statements. For more information on sqldetach(), see page 12-57.
Resource Allocation
Informix recommends that you include the DCE pthread_yield() call in tight loops to remind the scheduler to allocate resources as necessary. The call to pthread_yield() instructs the DCE scheduler to try to uniformly allocate resources in the event that a thread is caught in a tight loop, waiting for a connection (thus preventing other threads from proceeding). The following code fragment shows a call to the pthread_yield() routine:

Linking Thread-Safe Libraries
The esql command links the thread-safe versions of the static (available on UNIX operating systems only) or shared Informix general libraries when you specify the -thread command-line option.
Linking Thread-Safe Informix General Libraries to an ESQL/C Module on a UNIX Operating System
Perform the following steps to link thread-safe Informix general libraries to an ESQL/C module on a UNIX operating system:
1. Install the DCE thread package on the same client computer as the ESQL/C product. For more information, refer to your DCE installation instructions.
2. Set the THREADLIB environment variable to indicate which thread package to use when you compile the application.
The following C-shell command sets THREADLIB to the DCE thread package:
setenv THREADLIB DCE
3. Compile your program with the esql command, and specify the -thread command-line option.
The -thread command-line option tells esql to generate thread-safe code and to link in thread-safe libraries. The following command links thread-safe shared libraries with the file.exe executable file:
esql -thread file.ec -o file.exe
The -thread command-line option instructs the esql command to perform the following steps:
1. Pass the -thread option to the ESQL/C preprocessor to generate thread-safe code.
2. Call the C compiler with the -DIFX_THREAD command-line option.
3. Link the appropriate thread libraries (shared or static) to the executable file.
If you specify the -thread option but do not set THREADLIB, or if you set THREADLIB to some unsupported thread package, the esql command issues the following message:
Defining Thread-Safe Variables
When you specify the -thread command-line option to esql, the ESQL/C preprocessor passes the IFX_THREAD definition to the C compiler. The IFX_THREAD definition tells the C compiler to create thread-scoped variables for variables that are global in non-thread-safe ESQL/C code.
For example, when the C compiler includes the sqlca.h file with IFX_THREAD set, it defines thread-scoped variables for the ESQL/C status variables: SQLCODE, SQLSTATE, and the sqlca structure. The thread-scoped versions of status variables are macros that map the global status variables to thread-safe function calls that obtain thread-specific status information.
Figure 13-7 shows an excerpt from the sqlca.h file with the thread-scoped definitions for ESQL/C status variables.
Linking Shared or Static Versions
To tell the esql command to link the thread-safe versions of the Informix libraries into your application, use the -thread command-line option of esql, as follows:
Linking Thread-Safe Informix General Libraries to an ESQL/C Module in a Windows Environment
To create a thread-safe ESQL/C application, you must perform the following steps:
1. In your ESQL/C source file, include the appropriate thread functions and variables of the Windows API. For more information about threads, consult your Microsoft or Borland programmer documentation.
2. When you compile the ESQL/C source file, specify the -thread command-line option of the esql command.
The -thread option tells the ESQL/C preprocessor to generate thread-safe C code when it translates SQL and ESQL/C statements. This thread-safe code includes calls to thread-safe functions in the Informix DLLs.
If you are not creating an ESQL/C application with threads, omit the -thread option. Although the Informix DLLs are thread safe, your non-thread-safe application does not use the thread-safe feature when you omit -thread. 
|