10.3 Implementing a Multithreaded/ Multicontexted ATMI Application
- Preliminary Guidelines for Programming a Multithreaded/Multicontexted ATMI Application
- Writing Code to Enable Multicontexting in an ATMI Client
- Writing Code to Enable Server-Dispatched Multicontexting and Multithreading Threads in an ATMI Server
- Writing Code to Enable Multicontexting in Application-Created Threads of an ATMI Server
- Writing a Multithreaded ATMI Client
- Using Per-context Functions and Data Structures in a Multithreaded ATMI Client
- Using Per-process Functions and Data Structures in a Multithreaded ATMI Client
- Using Per-thread Functions and Data Structures in a Multithreaded ATMI Client
- Sample Code for a Multithreaded ATMI Client
- Writing a Multithreaded ATMI Server
- Compiling Code for a Multithreaded/Multicontexted ATMI Application
10.3.1 Preliminary Guidelines for Programming a Multithreaded/Multicontexted ATMI Application
Before you start coding, ensure you have fulfilled or thought about the following:
10.3.1.1 Prerequisites for a Multithreaded ATMI Application
Ensure your environment meets the following prerequisites before starting your development project.
- Your operating system must provide a suitable threads package supported by the Oracle Tuxedo system.
The Oracle Tuxedo system does not supply tools for creating threads, but it supports various threads packages provided by different operating systems. To create and synchronize threads, you must use the functions native to your operating system. To find out which, if any, threads packages are supported by your operating system, see Oracle Tuxedo Platform Data Sheets in Installing the Oracle Tuxedo System.
- If you are using multithreaded servers, the resource managers used by those servers must support threads.
10.3.1.2 General Multithreaded Programming Considerations
Only experienced programmers must write multithreaded programs. In particular, programmers must already be familiar with basic design issues specific to this task, such as:
- The need for concurrency control among multiple threads
- The need to avoid the use of static variables in most instances
- Potential problems that may arise from the use of signals in multithreaded programs
These are just a few of the issues, too numerous to list here, with which we assume any programmer undertaking the writing of a multithreaded program is already familiar. These issues are discussed in many commercially available books on the subject of multithreaded programming.
10.3.1.3 Concurrency Considerations
Multithreading enables different threads of an application to perform concurrent operations on the same conversation. We do not recommend this approach, but the Oracle Tuxedo system does not forbid it. If different threads perform concurrent operations on the same conversation, the system acts as if the concurrent calls were issued in some arbitrary order.
When programming with multiple threads, you must manage the concurrency among them by using mutexes or other concurrency-control functions. Here are three examples of the need for concurrency control:
- When multithreaded threads are operating on the same context, the programmer must ensure that functions are being executed in the required serial order. For example, all RPC calls and conversations must be invoked before tpcommit() can be called. If
tpcommit()
is called from a thread other than the thread from which all these RPC or conversational calls are made, some concurrency control is probably required in the application. - Similarly, it is permissible to call tpacall() in one thread and tpgetrply() in another, but the application must either:
- Ensure that
tpacall()
is called beforetpgetrply()
, or - Manage the consequences if
tpacall()
is not called beforetpgetrply()
- Ensure that
- Multiple threads may operate on the same conversation but application programmers must realize that if different threads issue tpsend() at approximately the same time, the system acts as though these
tpsend()
calls have been issued in an arbitrary order.
For most applications, the best strategy is to code all the operations for one conversation in one thread. The second best strategy is to serialize these operations using concurrency control.
See Also:
- Design Considerations for a Multithreaded and Multicontexted ATMI Application
- Writing Code to Enable Multicontexting in an ATMI Client
- Writing Code to Enable Server-Dispatched Multicontexting and Multithreading Threads in an ATMI Server
- Writing Code to Enable Multicontexting in Application-Created Threads of an ATMI Server
- Writing a Multithreaded ATMI Client
- Writing a Multithreaded ATMI Server
10.3.2 Writing Code to Enable Multicontexting in an ATMI Client
This section contains the following topics:
If your application uses transactions, you must also keep in mind the consequences of multicontexting for transactions. For more information, see Coding Rules for Transactions in a Multithreaded/Multicontexted ATMI Application.
Note:
The instructions and sample code provided in this section refer to the C library functions provided by the Oracle Tuxedo system. Equivalent COBOL library functions are also available; for details, see the Oracle Tuxedo COBOL Function Reference.- Context Attributes
- Setting Up Multicontexting at Initialization
- Implementing Security for a Multicontexted ATMI Client
- Synchronizing Threads Before an ATMI Client Termination
- Switching Contexts
- Handling Unsolicited Messages
- Coding Rules for Transactions in a Multithreaded/Multicontexted ATMI Application
10.3.2.1 Context Attributes
When writing your code, keep in mind the following considerations about contexts:
- If an application-created server thread which is currently associated with a server-dispatched context exits without changing context before the original dispatched thread exits, then
tpreturn()
ortpforward()
fails. The execution of a thread exit does not automatically trigger a call totpsetctxt(3c)
to change the context toTPNULLCONTEXT
. - For all contexts in a process, the same buffer type switch must be used.
- As with any other type of data structure, a multithreaded application must properly make use of Oracle Tuxedo buffers, that is, buffers must not be used concurrently in two calls when one of the following may be true:
- Both calls may use the buffer
- Both calls may free the buffer
- One call may use the buffer and one call may free the buffer
- If you call
tpinit()
more than once, either to join multiple applications or to make multiple connections to a single application, keep in mind that on eachtpinit()
you must accommodate whatever security mechanisms have been established.
Parent topic: Writing Code to Enable Multicontexting in an ATMI Client
10.3.2.2 Setting Up Multicontexting at Initialization
When a client is ready to join an application, specify joining-application.html#GUID-442A4ACC-9A40-4DF4-BD74-EF30D79940CA__GUID-3FBBF8D2-DC4A-436F-B2C2-4477EF68D5F1tpinit()
with the TPMULTICONTEXTS
flag set, as shown in the following listing.
Listing Sample Code for a Client Joining a Multicontexted Application
#include <stdio.h>
#include <atmi.h>
TPINIT * tpinitbuf;
main()
{
tpinitbuf = tpalloc(“TPINIT”, NULL, TPINITNEED(0));
tpinitbuf->flags = TPMULTICONTEXTS;
.
.
.
if (tpinit (tpinitbuf) == -1) {
ERROR_PROCESSING_CODE
}
.
.
.
}
A new application association is created and assigned to the Oracle Tuxedo domain specified in the TUXCONFIG
or WSENVFILE/WSNADDR
environment variable.
Note:
In any one process, either all calls totpinit()
must include the TPMULTICONTEXTS
flag or else no call to tpinit()
may include this flag. The only exception to this rule is that if all of a client’s application associations are terminated by successful calls to tpterm()
, then the process is restored to a state in which the inclusion of the TPMULTICONTEXTS
flag in the next call to tpinit()
is optional.
Parent topic: Writing Code to Enable Multicontexting in an ATMI Client
10.3.2.3 Implementing Security for a Multicontexted ATMI Client
Each application association in the same process requires a separate security validation. The nature of that validation depends on the type of security mechanisms used in your application. In an Oracle Tuxedo application you might, for example, use a system-level password or an application password.
As the programmer of a multicontexted application, you are responsible for identifying the type of security used in your application and implementing it for each application association in a process.
Parent topic: Writing Code to Enable Multicontexting in an ATMI Client
10.3.2.4 Synchronizing Threads Before an ATMI Client Termination
When you are ready to disconnect a client from an application, invoke tpterm()
. Keep in mind, however, that in a multicontexted application tpterm()
destroys the current context. All the threads operating on that context are affected. As the application programmer, you must carefully coordinate the use of multiple threads to make sure that tpterm()
is not called unexpectedly.
It is important to avoid calling tpterm()
on a context while other threads are still working on that context. If such a call to tpterm()
is made, the Oracle Tuxedo system places the other threads that had been associated with that context in a special invalid context state. When in the invalid context state, most ATMI functions are disallowed. A thread may exit from the invalid context state by calling tpsetctxt(3c)
or tpterm()
. Most well designed applications never have to deal with the invalid context state.
Note:
The Oracle Tuxedo system does not support multithreading in COBOL applications.Parent topic: Writing Code to Enable Multicontexting in an ATMI Client
10.3.2.5 Switching Contexts
The following is a summary of the coding steps that might be made by a client that calls services from two contexts.
- Set the
TUXCONFIG
environment variable to the value required byfirstapp
. - Join the first application by calling
tpinit()
with theTPMULTICONTEXTS
flag set. - Obtain a handle to the current context by calling
tpgetctxt(3c)
. - Switch the value of the
TUXCONFIG
environment variable to the value required by thesecondapp
context, by callingtuxputenv()
. - Join the second application by calling
tpinit()
with theTPMULTICONTEXTS
flag set. - Get a handle to the current context by calling
tpgetctxt(3c)
. - Beginning with the
firstapp
context, start toggling between contexts by callingtpsetctxt(3c)
. - Call
firstapp
services. - Switch the client to the
secondapp
context (by callingtpsetctxt(3c)
) and callsecondapp
services. - Switch the client to the
firstapp
context (by callingtpsetctxt(3c)
) and callfirstapp
services. - Terminate the
firstapp
context by callingtpterm()
. - Switch the client to the
secondapp
context (by callingtpsetctxt(3c)
) and callsecondapp
services. - Terminate the
secondapp
context by callingtpterm()
.
The following sample code provides an example of these steps.
Note:
To simplify the sample, error checking code is not included.Listing Sample Code for Switching Contexts in a Client
#include <stdio.h>
#include "atmi.h"/* Oracle Tuxedo header file */
#if defined(__STDC__) || defined(__cplusplus)
main(int argc, char *argv[])
#else
main(argc, argv)
int argc;
char *argv[];
#endif
{
TPINIT * tpinitbuf;
TPCONTEXT_T firstapp_contextID, secondapp_contextID;
/* Assume that TUXCONFIG is initially set to /home/firstapp/TUXCONFIG*/
/*
* Attach to the Oracle Tuxedo system in multicontext mode.
*/
tpinitbuf=tpalloc(“TPINIT”, NULL, TPINITNEED(0));
tpinitbuf->flags = TPMULTICONTEXTS;
if (tpinit((TPINIT *) tpinitbuf) == -1) {
(void) fprintf(stderr, "Tpinit failed\n");
exit(1);
}
/*
* Obtain a handle to the current context.
*/
tpgetctxt(&firstapp_contextID, 0);
/*
* Use tuxputenv to change the value of TUXCONFIG,
* so we now tpinit to another application.
*/
tuxputenv("TUXCONFIG=/home/second_app/TUXCONFIG");
/*
* tpinit to secondapp.
*/
if (tpinit((TPINIT *) tpinitbuf) == -1) {
(void) fprintf(stderr, "Tpinit failed\n");
exit(1);
}
/*
* Get a handle to the context of secondapp.
*/
tpgetctxt(&secondapp_contextID, 0);
/*
* Now you can alternate between the two contexts
* using tpsetctxt and the handles you obtained from
* tpgetctxt. You begin with firstapp.
*/
tpsetctxt(firstapp_contextID, 0);
/*
* You call services offered by firstapp and then switch
* to secondapp.
*/
tpsetctxt(secondapp_contextID, 0);
/*
* You call services offered by secondapp.
* Then you switch back to firstapp.
*/
tpsetctxt(firstapp_contextID, 0);
/*
* You call services offered by firstapp. When you have
* finished, you terminate the context for firstapp.
*/
tpterm();
/*
* Then you switch back to secondapp.
*/
tpsetctxt(secondapp_contextID, 0);
/*
* You call services offered by secondapp. When you have
finished, you terminate the context for secondapp and
end your program.
*/
tpterm();
return(0);
}
Parent topic: Writing Code to Enable Multicontexting in an ATMI Client
10.3.2.6 Handling Unsolicited Messages
For each context in which you want to handle unsolicited messages, you must set up an unsolicited message handler or use the process handler default if you have set one up.
If tpsetunsol()
is called from a thread that is not associated with a context, a per-process default unsolicited message handler for all new tpinit()
contexts created is established. A specific context may change the unsolicited message handler for that context by calling tpsetunsol()
again when the context is active. The per-process default unsolicited message handler may be changed by again calling tpsetunsol()
in a thread not currently associated with a context.
Set up the handler in the same way you set one up for a single-threaded or single-contexted application. See tpsetunsol()
for details.
You can use tpgetctxt(3c)
in an unsolicited message handler if you want to identify the context in which you are currently working.
Parent topic: Writing Code to Enable Multicontexting in an ATMI Client
10.3.2.7 Coding Rules for Transactions in a Multithreaded/Multicontexted ATMI Application
The following consequences of using transactions must be kept in mind while you are writing your application:
- You can have only one transaction in any one context.
- You can have a different transaction for each context.
- All the threads associated with a given context at a given time share the same transaction state (if any) of that context.
- You must synchronize your threads so all conversations and RPC calls are complete before you call tpcommit().
- You can call tpcommit() from only one thread in any particular transaction.
See Also:
Parent topic: Writing Code to Enable Multicontexting in an ATMI Client
10.3.3 Writing Code to Enable Server-Dispatched Multicontexting and Multithreading Threads in an ATMI Server
This topic includes the following sections:
Note: The instructions and sample code provided in this section refer to the C library functions provided by the Oracle Tuxedo system. (See the Oracle Tuxedo C Function Reference for details.) Equivalent COBOL routines are not available because multithreading (which is required to create a multicontexted server) is not supported for COBOL applications.
10.3.3.1 Context Attributes
When writing your code, keep in mind the following considerations about contexts:
- If an application-created server thread which is currently associated with a server-dispatched context exits without changing context before the original dispatched thread exits, then
tpreturn()
ortpforward()
fails. The execution of a thread exit does not automatically trigger a call totpsetctxt(3c)
to change the context to TPNULLCONTEXT. - For all contexts in a process, the same buffer type switch must be used.
- As with any other type of data structure, a multithreaded application must properly make use of Oracle Tuxedo buffers, that is, buffers must not be used concurrently in two calls when one of the following may be true:
- Both calls may use the buffer.
- Both calls may free the buffer.
- One call may use the buffer and one call may free the buffer.
10.3.3.2 Coding Rules for Server-Dispatched Threads in Multicontexted ATMI Server
Keep in mind the following rules for coding multicontexted servers:
- The Oracle Tuxedo dispatcher on the server may dispatch the same service and/or different services multiple times, creating a different dispatch context for each service dispatched.
- A server is prohibited from calling
tpinit()
. If a server process callstpinit()
,tpinit()
returns-1
and setstperrno(5)
toTPEPROTO
. - Only a server-dispatched thread may call
tpreturn()
ortpforward()
. - A server cannot execute a
tpreturn()
ortpforward()
if any application-created thread is still associated with any application context. Therefore, before a server-dispatched thread callstpreturn()
, each application-created thread associated with that context must calltpsetctxt(3c)
with the context set to eitherTPNULLCONTEXT
or another valid context.
If this rule is violated, then tpreturn()
or tpforward()
writes a message to the user log, indicates TPESVCERR
to the caller, and returns control to the main server dispatch loop. The threads that had been in the context where the invalid tpreturn()
was done are placed in an invalid context.
- If there are outstanding ATMI calls, RPC calls, or conversations when
tpreturn()
ortpforward()
is called,tpreturn()
ortpforward()
writes a message to the user log, indicatesTPESVCERR
to the caller, and returns control to the main server dispatch loop. - A server-dispatched thread may not call
tpsetctxt(3c)
. - Unlike single-contexted servers, it is permissible for a multicontexted server thread to call a service that is offered only by that same server process.
10.3.3.3 Initializing and Terminating ATMI Servers and Server Threads
To initialize and terminate your servers and server threads, you can use the default functions provided by the Oracle Tuxedo system.
Table 10-1 Default Functions for Initialization and Termination
To ... | Use the default function … |
---|---|
Initialize a server |
tpsvrinit (3c)
|
Initialize a server thread |
tpsvrthrinit (3c)
|
Terminate a server |
tpsvrdone (3c)
|
Terminate a server thread |
tpsvrthrdone (3c)
|
10.3.4 Writing Code to Enable Multicontexting in Application-Created Threads of an ATMI Server
This topic includes the following sections:
- Creating Threads
- Associating Application Threads with a Context
- Associating Application Threads with an Existing Server-Dispatched Context
- Sample Code for Associating Application Thread with an Existing Server-Dispatched Context in a Multicontexted Server
- Associating Application Threads with Application-Created Context
- Sample Code for Associating Application Thread with Application-created server Context in a Multicontexted Server
10.3.4.1 Creating Threads
You can create additional threads within a Tuxedo application
server by using OS threads functions. These new application threads
may operate independently of the Tuxedo system, or they may operate
in the same context as one of the server-dispatched threads.
Application-created server threads may also operate in separate
context created via tpappthrinit(3c)
.
10.3.4.2 Associating Application Threads with a Context
Initially, application-created server threads are not associated
with any Tuxedo context. If called before being initialized,
however, most ATMI functions perform an implicit
tpinit()
. Such calls introduce problems because
servers are prohibited from calling tpinit()
. (If a
server process calls tpinit()
, tpinit()
returns -1
and sets tperrno(5)
to
TPEPROTO
.)
Therefore, an application-created server thread must associate itself with a valid context before calling any ATMI functions. An application-created server thread may:
- Associate itself with an existing server-dispatched context
- Associate itself with a separated context created via
tpappthrinit(3c)
(This feature is available from Tuxedo 11g Release 1 (11.1.1.3.0))
10.3.4.3 Associating Application Threads with an Existing Server-Dispatched Context
To associate an application-created server thread with an existing server-dispatched context, you must write code that implements the following procedures.
-
Server-dispatched-thread_A
gets a handle to the current context by callingtpgetctxt(3c)
. -
Server-dispatched-thread_A
passes the handle returned bytpgetctxt(3c)
toApplication_thread_B
. -
Application_thread_B
associates itself with the current context by callingtpsetctxt(3c)
specifying the handle received fromServer-dispatched-thread_A
.
Note:
Application-created server threads cannot calltpreturn()
and/or tpforward()
. Before the original server-dispatched thread calls tpreturn()
or tpforward()
, all application-created server threads that have been in that context must switch to TPNULLCONTEXT
or another valid context.
If this rule is not observed, then tpforward()
or
tpreturn()
fails and indicates a service error to
caller.
10.3.4.4 Sample Code for Associating Application Thread with an Existing Server-Dispatched Context in a Multicontexted Server
For those applications that need to create an application thread in a server, the following listing shows a multicontexted server example where a service creates another thread to help perform its work in the same server-dispatched context. Operating system (OS) threads functions differ from one OS to another. In this sample POSIX and ATMI functions are used.
Note:
To simplify the example, error checking code is not included. Also, an example of a multicontexted server using only threads dispatched by the Oracle Tuxedo system is not included because such a server is coded in exactly the same way as a single-contexted server, as long as thread-safe programming practices are used.Listing Code Sample for Application-Created Server Thread Working in Server-Dispatched Context
#include <pthread.h>
#include <atmi.h>
void *withdrawalthread(void *);
struct sdata {
TPCONTEXT_T ctxt;
TPSVCINFO *svcinfoptr;
};
void
TRANSFER(TPSVCINFO *svcinfo)
{
struct sdata transferdata;
pthread_t withdrawalthreadid;
tpgetctxt(&transferdata.ctxt, 0);
transferdata.svcinfoptr = svcinfo;
pthread_create( &withdrawalthreadid, NULL,
withdrawalthread, &transferdata );
tpcall("DEPOSIT", ...);
pthread_join(withdrawalthreadid, NULL);
tpreturn(TPSUCCESS, ...);
}
void *
withdrawalthread(void *arg)
{
tpsetctxt(arg->ctxt, 0);
tpopen();
tpcall("WITHDRAWAL", ...);
tpclose();
tpsetctxt(TPNULLCONTEXT, 0);
return(NULL)
}
10.3.4.5 Associating Application Threads with Application-Created Context
An application-created thread within an ATMI server can create a
separate Tuxedo context and associate itself with this context by
calling tpappthrinit(3c)
.
10.3.4.5.1 Context Attributes
- The context created via
tpappthrinit(3c)
is independent from any server-dispatched context. It connects to the domain that the application server is in. - The context created via
tpappthrinit(3c)
must be terminated by callingtpappthrterm(3c)
after the application-created server thread has finished its work on that context. - Depending on the type of security mechanism used in your
application, user authentication information can be associated with
the context when invoking
tpappthrinit(3c)
. Each application association of an application-created context requires a separate security validation.
10.3.4.5.2 Code Rules for Application-Created Thread of an ATMI Server in Application-Created Context
- Only Tuxedo server process can call
tpappthrinit()
/tpappthrterm()
. - The server must be built with
"-t"
option when executingbuildserver
command. - It is not allowed to call
tpappthrinit()
/tpappthrterm()
in a client program. - It is not allowed to call
tpappthrinit()
/tpappthrterm()
in a server-dispatched thread. - It is not allowed to call
tpappthrterm()
in an application-created thread which is currently associated with a server-dispatched context. - It must be avoided calling
tpappthrterm()
on an application-created context while other application threads are still working on that context. - Handles and call descriptors are portable within the same context in the same process, but not between contexts or processes. Handles and call descriptors can be used only in the application context in which they are originally assigned.
- Once a conversation has been started, any thread in the same context within the same process can work on that conversation.
- Any application thread operating in the same context within the
same server process can invoke
tpgetrply()
to receive a response to an earlier call totpacall()
, regardless of whether or not that application thread originally calledtpacall()
. - A transaction can be committed or aborted by only one application thread in the same context within the same process, which may or may not be the same application thread that started it.
-
tpbegin()
cannot be issued for a context that is already in transaction mode. - If the application server is in a group that is configured with
XA transaction,
tpopen()
must be invoked before performing any transaction activities, such astpbegin()
and SQL operations. When transaction operations are finished,tpclose()
must be called. - Application-created server thread may send, but cannot receive unsolicited messages.
10.3.4.6 Sample Code for Associating Application Thread with Application-created server Context in a Multicontexted Server
For those applications with a need to create an application thread in a server, the following listing shows a multicontexted server example where a service creates another thread, and this application-created server thread operates in a separated context. Operating system (OS) threads functions differ from one OS to another. In this example, POSIX and ATMI functions are used.
Note:
To simplify the example, error checking code is not included.Listing Code Sample for Application-Created Server Thread Working in Application-Created Context
#include <pthread.h>
#include <atmi.h>
void *withdrawalthread(void *);
void
TRANSFER(TPSVCINFO *svcinfo)
{
pthread_t withdrawalthreadid;
pthread_create( &withdrawalthreadid, NULL,
withdrawalthread, … );
tpcall("DEPOSIT", ...);
pthread_join(withdrawalthreadid, NULL);
tpreturn(TPSUCCESS, ...);
}
void *
withdrawalthread(void *arg)
{
tpappthrinit(NULL);
tpopen();
tpcall("WITHDRAWAL", ...);
tpclose();
tpappthrterm();
return(NULL);
}
10.3.5 Writing a Multithreaded ATMI Client
This topic includes the following sections:
Note:
The Oracle Tuxedo system does not support multithreaded COBOL applications.10.3.5.1 Coding Rules for a Multithreaded ATMI Client
Keep in mind the following rules for coding multithreaded clients:
- Once a conversation has been started, any thread in the same process can work on that conversation. Handles and call descriptors are portable within the same context in the same process, but not between contexts or processes. Handles and call descriptors can be used only in the application context in which they are originally assigned.
- Any thread operating in the same context within the same process can invoke
tpgetrply()
to receive a response to an earlier call totpacall()
, regardless of whether or not that thread originally calledtpacall()
. - A transaction can be committed or aborted by only one thread, which may or may not be the same thread that started it.
- All RPC calls and all conversations must be completed before an attempt is made to commit the transaction. If an application calls
tpcommit()
while RPC calls or conversations are outstanding,tpcommit()
aborts the transaction, returns-1
, and setstperrno(5)
toTPEABORT
. - Functions such as
tpcall()
,tpacall()
,tpgetrply()
,tpconnect()
,tpsend()
,tprecv()
, andtpdiscon()
must not be called in transaction mode unless you are sure that the transaction is not already committing or aborting. - Two
tpbegin()
calls cannot be made simultaneously for the same context. -
tpbegin()
cannot be issued for a context that is already in transaction mode. - If you are using a client and you want to connect to more than one domain, you must manually change the value of
TUXCONFIG
orWSNADDR
before callingtpinit()
. You must synchronize the setting of the environment variable and thetpinit()
call if multiple threads may be performing such an action. All application associations in a client must obey the following rules:- All associations must be made to the same release of the Oracle Tuxedo system.
- Either every application association in a particular client must be made as a native client, or every application association must be made as a Workstation client.
- To join an application, a multithreaded Workstation client must always call
tpinit()
with theTPMULTICONTEXTS
flag set, even if the client is running in single-context mode.
Parent topic: Writing a Multithreaded ATMI Client
10.3.5.2 Initializing an ATMI Client to Multiple Contexts
To have a client join more than one context, issue a call to the tpinit()
function with the TPMULTICONTEXTS
flag set in the flags
element of the TPINIT
data structure.
In any one process, either all calls to tpinit()
must include the TPMULTICONTEXTS
flag or no call to tpinit()
may include this flag. The only exception to this rule is that if all of a client’s application associations are terminated by successful calls to tpterm()
, then the process is restored to a state in which the inclusion of the TPMULTICONTEXTS
flag in the next call to tpinit()
is optional.
When tpinit()
is invoked with the TPMULTICONTEXTS
flag set, a new application association is created and is designated the current association. The Oracle Tuxedo domain to which the new association is made is determined by the value of the TUXCONFIG
or WSENVFILE/WSNADDR
environment variable.
When a client thread successfully executes tpinit()
without the TPMULTICONTEXTS
flag, all threads in the client are placed in the single-context state (TPSINGLECONTEXT
).
On failure, tpinit()
leaves the calling thread in its original context (that is, in the context state in which it was operating before the call to tpinit()
).
Do not call tpterm()
from a given context if any of the threads in that context are still working. See the table labeled Multicontext State Transitions for a description of the context states that result from calling tpterm()
under these and other circumstances.
Parent topic: Writing a Multithreaded ATMI Client
10.3.5.3 Context State Changes for an ATMI Client Thread
In a multi-context application, calls to various functions result in context state changes for the calling thread and any other threads that are active in the same context as the calling process. The following figure illustrates the context state changes that result from calls to tpinit()
, tpsetctxt(3c)
, and tpterm()
. (The tpgetrply()
function does not produce any context state changes.)
Figure 10-4 Multi-context State Transitions

Note:
Whentpterm()
is called by a thread running in the multicontext state (TPMULTICONTEXTS
), the calling thread is placed in the null context state (TPNULLCONTEXT
). All other threads associated with the terminated context are switched to the invalid context state (TPINVALIDCONTEXT
).
The following table lists all possible context state changes produced by calling tpinit()
, tpsetctxt(3c)
, and tpterm()
.
Table 10-2 Context State Changes for a Client Thread
When this function is executed . . . | Then a thread in this context state results in . . . | |||
---|---|---|---|---|
Null Context | Single Context | Multi Context | Invalid Context | |
tpinit() without TPMULTICONTEXTS
|
Single context | Single context | Error | Error |
tpinit() with TPMULTICONTEXTS
|
Multicontext | Error | Multicontext | Error |
tpsetctxt(3c) to TPNULLCONTEXT
|
Null | Error | Null | Null |
tpsetctxt(3c) to context 0
|
Error | Single context | Error | Error |
tpsetctxt(3c) to context > 0
|
Multicontext | Error | Multicontext | Multicontext |
Implicit tpinit()
|
Single context | N/A | N/A | Error |
tpterm() in this thread
|
Null | Null | Null | Null |
tpterm() in a different thread of this context
|
N/A | Null | Invalid | N/A |
Parent topic: Writing a Multithreaded ATMI Client
10.3.5.4 Getting Replies in a Multithreaded Environment
tpgetrply()
receives responses only to requests made via tpacall()
. Requests made with tpcall()
are separate and cannot be retrieved with tpgetrply()
regardless of the multithreading or multicontexting level.
tpgetrply() operates in only one context, which
is the context in which it is called. Therefore, when you call
tpgetrply()
with the TPGETANY
flag, only
handles generated in the same context are considered. Similarly, a
handle generated in one context may not be used in another context,
but the handle may be used in any thread operating within the same
context.
When tpgetrply()
is called in a multithreaded environment, the following restrictions apply:
- If a thread calls
tpgetrply()
for a specific handle while another thread in the same context is already waiting intpgetrply()
for the same handle,tpgetrply()
returns-1
and setstperrno
toTPEPROTO
. - If a thread calls
tpgetrply()
for a specific handle while another thread in the same context is already waiting intpgetrply()
with theTPGETANY
flag, the call returns-1
and setstperrno(5)
toTPEPROTO
.The same behavior occurs if a thread calls
tpgetrply()
with theTPGETANY
flag while another thread in the same context is already waiting intpgetrply()
for a specific handle. These restrictions protect a thread that is waiting on a specific handle from having its reply taken by a thread waiting on any handle. - At any given time, only one thread in a particular context can wait in
tpgetrply()
with theTPGETANY
flag set. If a second thread in the same context invokestpgetrply()
with theTPGETANY
flag while a similar call is outstanding, this second call returns-1
and setstperrno(5)
toTPEPROTO
.
Parent topic: Writing a Multithreaded ATMI Client
10.3.5.5 Using Environment Variables in a Multithreaded and/or Multicontexted Environment
When an Oracle Tuxedo application is run in an environment that is multicontexted and/or multithreaded, the following considerations apply to the use of environment variables:
- A process initially inherits its environment from the operating system environment. On platforms that support environment variables, such variables make up a per-process entity. Therefore, applications that depend on per-context environment settings must use the
tuxgetenv(3c)
function instead of an OS function.Note:
The environment is initially empty for those operating systems that do not recognize an operating system environment. - Many environment variables are read by the Oracle Tuxedo system only once per process or once per context and then cached within the Oracle Tuxedo system. Changes to such variables once cached in the process have no effect.
Caching is done on a . . . | For environment variables such as . . . |
---|---|
Per-context basis | TUXCONFIG
|
FIELDTBLS and FIELDTBLS32
|
|
FLDTBLDIR and FLDTBLDIR32
|
|
ULOGPFX
|
|
VIEWDIR and VIEWDIR32
|
|
WSNADDR
|
|
WSDEVICE
|
|
WSENV
|
|
Per-process basis | TMTRACE
|
TUXDIR
|
|
ULOGDEBUG
|
- The
tuxputenv(3c)
function affects the environment for the entire process. - When you call the
tuxreadenv(3c)
function, it reads a file containing environment variables and adds them to the environment for the entire process. - The
tuxgetenv(3c)
function returns the current value of the requested environment variable in the current context. Initially, all contexts have the same environment, but the use of environment files specific to a particular context can cause different contexts to have different environment settings. - If a client intends to initialize to more than one domain, the client must change the value of the
TUXCONFIG
,WSNADDR
, orWSENVFILE
environment variable to the proper value before each call totpinit()
. If such an application is multithreaded, a mutex or other application-defined concurrency control will probably be needed to ensure that:- The appropriate environment variable is reset.
- The call to
tpinit()
is made without the environment variable being reset by any other thread
- When a client initializes to the system, the
WSENVFILE
and/or machine environment file is read and affects the environment in that context only. The previous environment for the process as a whole remains for that context to the extent that it is not overridden within the environment file(s).
Parent topic: Writing a Multithreaded ATMI Client
10.3.6 Using Per-context Functions and Data Structures in a Multithreaded ATMI Client
The following ATMI functions affect only the application contexts in which they are called:
-
tpabort()
-
tpacall()
-
tpadmcall(3c)
-
tpbegin()
-
tpbroadcast()
-
tpcall()
-
tpcancel()
-
tpchkauth()
-
tpchkunsol()
-
tpclose(3c)
-
tpcommit()
-
tpconnect()
-
tpdequeue(3c)
-
tpdiscon()
-
tpenqueue(3c)
-
tpforward()
-
tpgetlev()
-
tpgetrply()
-
tpinit()
-
tpnotify()
tpopen(3c)
-
tppost()
-
tprecv()
-
tpresume()
-
tpreturn()
-
tpscmt(3c)
-
tpsend()
-
tpservice(3c)
-
tpsetunsol()
-
tpsubscribe()
-
tpsuspend()
-
tpterm()
-
tpunsubscribe()
-
tx_begin(3c)
-
tx_close(3c)
-
tx_commit(3c)
-
tx_info(3c)
-
tx_open(3c)
-
tx_rollback(3c)
-
tx_set_commit_return(3c)
-
tx_set_transaction_control(3c)
-
tx_set_transaction_timeout(3c)
-
userlog(3c)
Note:
Fortpbroadcast()
, the broadcast message is identified as having come from a particular application association. For tpnotify(3c)
, the notification is identified as having come from a particular application association. See “Using Per-process Functions and Data Structures in a Multithreaded Client” for notes about tpinit()
.
If tpsetunsol()
is called from a thread that is not associated with a context, a per-process default unsolicited message handler for all new tpinit()
contexts created is established. A specific context may change the unsolicited message handler for that context by calling tpsetunsol()
again when the context is active. The per-process default unsolicited message handler may be changed by again calling tpsetunsol()
in a thread not currently associated with a context.
- The
CLIENTID
, client name, username, transaction ID, and the contents of theTPSVCINFO
data structure may differ from context to context within the same process. - Asynchronous call handles and connection descriptors are valid in the contexts in which they are created. The unsolicited notification type is specific per-context. Although signal-based notification may not be used with multiple contexts, each context may choose one of three options:
- Ignoring unsolicited messages
- Using dip-in notification
- Using dedicated thread notification
10.3.7 Using Per-process Functions and Data Structures in a Multithreaded ATMI Client
The following Oracle Tuxedo functions affect the entire process in which they are called:
-
tpadvertise()
-
tpalloc()
-
tpconvert(3c)
—the requested structure is converted, although it is probably relevant to only a subset of the process. -
tpfree()
-
tpinit()
—to the extent that the per-processTPMULTICONTEXTS
mode or single-context mode is established. See also Using Per-context Functions and Data Structures in a Multithreaded ATMI Client. -
tprealloc()
-
tpsvrdone()
-
tpsvrinit()
-
tptypes()
-
tpunsubscribe()
-
tuxgetenv(3c)
—if the OS environment is per-process. -
tuxreadenv(3c)
—if the OS environment is per-process. -
Usignal(3c)
The determination of single-context mode, multicontext mode, or uninitialized mode affects an entire process. The buffer type switch, the view cache, and environment variable values are also per-process functions.
10.3.8 Using Per-thread Functions and Data Structures in a Multithreaded ATMI Client
Only the calling thread is affected by the following:
-
CATCH
-
tperrordetail(3c)
-
tpgetctxt(3c)
-
tpgprio()
-
tpsetctxt(3c)
-
tpsprio()
-
tpstrerror(3c)
-
tpstrerrordetail(3c)
-
TRY(3c)
-
Uunix_err(3c)
The Ferror
, Ferror32(5)
, tperrno(5)
, tpurcode(5)
, and Uunix_err
variables are specific to each thread, for more information, refer Section 5 - File Formats, Data Descriptions, MIBs, and System Processes Reference.
The identity of the current context is specific to each thread.
10.3.9 Sample Code for a Multithreaded ATMI Client
The following listing shows a multithreaded client using ATMI calls. Threads functions differ from one operating system to another. In this example, POSIX functions are used.
In order to simplify this example, error checking code has not been included.
Listing Sample Code for a Multithreaded Client
#include <stdio.h>
#include <pthread.h>
#include <atmi.h>
TPINIT * tpinitbuf;
int timeout=60;
pthread_t withdrawalthreadid, stockthreadid;
TPCONTEXT_T ctxt;
void * stackthread(void *);
void * withdrawalthread(void *);
main()
{
tpinitbuf = tpalloc(TPINIT, NULL, TPINITNEED(0));
/*
* This code will perform a transfer, using separate threads for the
* withdrawal and deposit. It will also get the current
* price of Oracle stock from a separate application, and calculate how
* many shares the transferred amount can buy.
*/
tpinitbuf->flags = TPMULTICONTEXTS;
/* Fill in the rest of tpinitbuf. */
tpinit(tpinitbuf);
tpgetctxt(&ctxt, 0);
tpbegin(timeout, 0);
pthread_create(&withdrawalthreadid, NULL, withdrawalthread, NULL);
tpcall("DEPOSIT", ...);
/* Wait for the withdrawal thread to complete. */
pthread_join(withdrawalthreadid, NULL);
tpcommit(0);
tpterm();
/* Wait for the stock thread to complete. */
pthread_join(stockthreadid, NULL);
/* Print the results. */
printf("$%9.2f has been transferred \
from your savings account to your checking account.\n", ...);
printf("At the current Oracle stock price of $%8.3f, \
you could purchase %d shares.\n", ...);
exit(0);
}
void *
stockthread(void *arg)
{
/* The other threads have now called tpinit(), so resetting TUXCONFIG can
* no longer adversely affect them.
*/
tuxputenv("TUXCONFIG=/home/users/xyz/stockconf");
tpinitbuf->flags = TPMULTICONTEXTS;
/* Fill in the rest of tpinitbuf. */
tpinit(tpinitbuf);
tpcall("GETSTOCKPRICE", ...);
/* Save the stock price in a variable that can also be accessed in main(). */
tpterm();
return(NULL);
}
void *
withdrawalthread(void *arg)
{
/* Create a separate thread to get stock prices from a different
* application.
*/
pthread_create(&stockthreadid, NULL, stockthread, NULL);
tpsetctxt(ctxt, 0);
tpcall("WITHDRAWAL", ...);
return(NULL);
}
10.3.10 Writing a Multithreaded ATMI Server
Multithreaded servers are almost always multicontexted, as well. For information about writing a multithreaded server, see Writing Code to Enable Server-Dispatched Multicontexting and Multithreading Threads in an ATMI Server.
10.3.11 Compiling Code for a Multithreaded/Multicontexted ATMI Application
The programs provided by the Oracle Tuxedo system for compiling or building executables, such as buildserver(1) and buildclient(1), automatically include any required compiler flags. If you use these tools, then you do not need to set any flags at compile time.
If, however, you compile your .c
files into
.o
files before doing a final compilation, you may
need to set platform-specific compiler flags. Such flags must be
set consistently for all code linked into a single process.
If you are creating a multithreaded server, you must run the buildserver(1)
command with the -t
option. This option is mandatory for multithreaded servers; if you do not specify it at build time and later try to boot the new server with a configuration file in which the value of MAXDISPATCHTHREADS
is greater than 1, a warning message is recorded in the user log and the server reverts to single-threaded operation.
To identify any operating system-specific compiler parameters that are required when you compile .c
files into .o
files in a multithreaded environment, run buildclient(1)
or buildserver(1)
with the -v
option set on a test file.
See Also: