![]() ![]() ![]() ![]() ![]() ![]() |
This topic includes the following sections:
The Oracle Tuxedo system supports only:
If your operating system supports POSIX threads functions as well as other types of threads functions, we recommend using the POSIX threads functions, which make your code easier to port to other platforms later.
To find out whether your platform supports a kernel-level threads package, C functions, or POSIX functions, see the data sheet for your operating system in Installing the Oracle Tuxedo System.
Many platforms have idiosyncratic requirements for multithreaded and multicontexted applications. Installing the Oracle Tuxedo System lists these platform-specific requirements. To find out what is needed on your platform, check the appropriate data sheet.
This topic includes the following sections:
The Oracle Tuxedo system allows you to use a single process to perform multiple tasks simultaneously. The programming techniques for implementing this sort of process usage are multithreading and multicontexting. This topic provides basic information about these techniques:
Multithreading is the inclusion of more than one unit of execution in a single process. In a multithreaded application, multiple simultaneous calls can be made from the same process. For example, an individual process is not limited to one outstanding tpcall(3c).
In a server, multithreading requires multicontexting except when application-created threads are used in a singled-context server. The only way to create a multithreaded, single-context application is to use application-created threads.
The Oracle Tuxedo system supports multithreaded applications written in C. It does not support multithreaded COBOL applications.
The following diagram shows how a multithreaded client can issue calls to three servers simultaneously.
In a multithreaded application, multiple service-dispatched threads are available in the same server, which means that fewer servers need to be started for that application.
The following diagram shows how a server process can dispatch multiple threads to different clients simultaneously.
A context is an association to a domain. Multicontexting is the ability of a single process to have one of the following:
Multicontexting can be used in both clients and servers. When used in servers, multicontexting implies the use of multithreading, as well.
For a more complete list of the characteristics of a context, see “Context Attributes” in one of the following sections:
The Oracle Tuxedo system supports multicontexted applications written in either C or COBOL. Multithreaded applications, however, are supported only in C.
The following diagram shows how a multicontexted client process works within a domain. Each arrow represents an outstanding call to a server.
For licensing purposes, each context is counted as one user. Additional licenses are not required to accommodate multiple threads within one context. For example:
Multithreading and multicontexting are powerful tools for enhancing the performance of Oracle Tuxedo applications—given the appropriate circumstances. Before embarking on a plan to use these techniques, however, it is important to understand potential benefits and pitfalls.
Multithreaded and multicontexted ATMI applications offer the following advantages:
For certain applications, performance and concurrency can be improved by using multithreading and multicontexting together. In other applications, performance can be unaffected or even degraded by using multithreading and multicontexting together. How performance is affected depends on your application.
In some applications it is easier to code different remote procedure calls and conversations in separate threads than to manage them from the same thread.
Your Oracle Tuxedo clients can be connected to more than one application at a time.
Because one server can dispatch multiple service threads, the number of servers to start for your application is reduced. This capability for multiple dispatched threads is especially useful for conversational servers, which otherwise must be dedicated to one client for the entire duration of a conversation.
For applications in which client threads are created by the Microsoft Internet Information Server API or the Netscape Enterprise Server interface (that is, the NSAPI), the use of multiple threads is essential if you want to obtain the full benefits afforded by these tools. This may be true of other tools, as well.
Multithreaded and multicontexted ATMI applications present the following disadvantages:
Multithreaded and multicontexted applications are not easy to write. Only experienced programmers should undertake coding for these types of applications.
It is much harder to replicate an error in a multithreaded or multicontexted application than it is to do so in a single-threaded, single-contexted application. As a result, it is more difficult, in the former case, to identify and verify root causes when errors occur.
The task of managing concurrency among threads is difficult and has the potential to introduce new problems into an application.
Testing a multithreaded application is more difficult than testing a single-threaded application because defects are often timing-related and more difficult to reproduce.
Existing code often requires significant re-architecting to take advantage of multithreading and multicontexting. Programmers need to:
When a multithreaded and multicontexted application is active, the life cycle of a client can be described in three phases:
In the start-up phase the following events occur:
Note: | There may also be threads that work independently of the Oracle Tuxedo system. We do not consider such threads in this documentation. |
A client in an Oracle Tuxedo multicontexted application can have more than one application association as long as the following rules are observed:
To join multiple contexts, clients call the tpinit(3c) function with the TPMULTICONTEXTS
flag set in the flags
element of the TPINFO
data type.
When tpinit()
is called with the TPMULTICONTEXTS
flag set, a new application association is created and is designated the current association for the thread. The Oracle Tuxedo domain to which the new association is made is determined by the value of the TUXCONFIG
or WSENVFILE/WSNADDR
environment variable.
Many ATMI functions operate on a per-context basis. (For a complete list, see Using Per-context Functions and Data Structures in a Multithreaded ATMI Client.) In such cases, the target context must be the current context. Although clients can join more than one context, at any time, in any thread, only one context can be the current context.
As task priorities shift within an application, requiring interactions with one Oracle Tuxedo domain rather than another, it is sometimes advantageous to reassign a thread from one context to another.
In such situations, one client threads calls tpgetctxt(3c) and passes the handle that is returned (the value of which is the current context) to a second client thread. The second thread then associates itself with the current context by calling tpsetctxt(3c) and specifying the handle it received from tpgetctxt(3c) via the first thread.
Once the second thread is associated with the desired context, it is available to perform tasks executed by ATMI functions that operate on a per-context basis. For details, see Using Per-context Functions and Data Structures in a Multithreaded ATMI Client.
In this phase each thread performs a task. The following is a list of sample tasks:
A thread sends a request to a server by calling either tpcall(3c) for a synchronous request or tpacall(3c)for an asynchronous request. If the request is sent with tpcall()
, then the reply is received without further action by any thread.
If an asynchronous request for a service has been sent with tpcall(3c), a thread in the same context (which may or may not be the same thread that sent the request) gets the reply by calling tpgetrply(3c).
If one thread starts a transaction, then all threads that share the context of that thread also share the transaction.
Many threads in a context may work on a transaction, but only one thread may commit or abort it. The thread that commits or aborts the transaction can be any thread working on the transaction; it is not necessarily the same thread that started the transaction. Threaded applications are responsible for providing appropriate synchronization so that the normal rules of transactions are followed. (For example, there can be no outstanding RPC calls or conversations when a transaction is committed, and no stray calls are allowed after a transaction has been committed or aborted.) A process may be part of at most one transaction for each of its application associations.
If one thread of an application calls tpcommit(3c) concurrently with an RPC or conversational call in another thread of the application, the system acts as if the calls were issued in some serial order. An application context may temporarily suspend work on a transaction by calling tpsuspend(3c) and then start another transaction subject to the same restrictions that exist for single-threaded and single-context programs.
For each context in a multithreaded or multicontexted application, you may choose one of three methods for handling unsolicited messages.
TPU_THREAD
unsolicited notification method. As a result, you cannot receive immediate notification of events.If receiving immediate notification of events is important to your application, then you should carefully consider whether to use a multicontexted approach on this platform.
When dedicated thread notification is chosen, the system dedicates a separate thread to receive unsolicited messages and dispatch the unsolicited message handler. Only one copy of the unsolicited message handler can run at any one time in a given context.
If tpinit(3c) is called on a platform for which the Oracle Tuxedo system does not support threads, with parameters indicating that TPU_THREAD
notification is being requested on a platform that does not support threads, tpinit()
returns -1
and sets tperrno
to TPEINVAL
. If the UBBCONFIG(5) default NOTIFY
option is set to THREAD
but threads are not available on a particular machine, the default behavior for that machine is downgraded to DIPIN
. The difference between these two behaviors allows an administrator to specify a default for all machines in a mixed configuration—a configuration that includes some machines that support threads and some that do not—but it does not allow a client to explicitly request a behavior that is not available on its machine.
If tpsetunsol(3c)is called from a thread that is not associated with a context, a per-process default unsolicited message handler for all new tpinit(3c) 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.
If a process has multiple associations with the same application, then each association is assigned a different CLIENTID
so that it is possible to send an unsolicited message to a specific application association. If a process has multiple associations with the same application, then any tpbroadcast(3c) is sent separately to each of the application associations that meet the broadcast criteria. When performing a dip-in check for receiving unsolicited messages, an application checks for only those messages sent to the current application association.
In addition to the ATMI functions permitted in unsolicited message handlers, it is permissible to call tpgetctxt(3c) within an unsolicited message handler. This functionality allows an unsolicited message handler to create another thread to perform any more substantial ATMI work required within the same context.
For each thread in each application, userlog(3c) records the following identifying information:
process_ID.thread_ID.context_ID
Placeholders are printed in the thread_ID
and context_ID
fields of entries for non-threaded platforms and single-contexted applications.
The TM_MIB(5) supports this functionality in the TA_THREADID
and TA_CONTEXTID
fields in the T_ULOG
class.
In this phase, when the client process is about to exit, on behalf of the current context and all associated threads, a thread ends its application association by calling tpterm(3c). Like other ATMI functions, tpterm()
operates on the current context. It affects all threads for which the context is set to the terminated context, and terminates any commonality of context among these threads.
A well-designed application normally waits for all work in a particular context to complete before it calls tpterm()
. Be sure that all threads are synchronized before your application calls tpterm()
.
The events that occur in an ATMI server when a multithreaded and multicontexted application is active can be described in three phases:
What happens during the start-up phase depends on the value of the MINDISPATCHTHREADS
and MAXDISPATCHTHREADS
parameters in the configuration file.
|
||
|
In this phase, the following activities occur:
In response to clients’ requests for a service, the server dispatcher creates multiple threads (up to a configurable maximum) in one server that can be assigned to various client requests concurrently. A server cannot become a client by calling tpinit(3c).
Each dispatched thread is associated with a separate context. This feature is useful in conversational and RPC servers. It is especially useful for conversational servers which otherwise sit idle, waiting for the client side of a conversation while other conversational connections are waiting for service.
This functionality is controlled by the following parameters in the SERVERS
section of the UBBCONFIG(5) file and the TM_MIB(5).
THREADSTACKSIZE
(or TA_THREADSTACKSIZE
). If this parameter is not specified or has a value of 0, the operating system default is used. On a few operating systems on which the default is too small to be used by the Oracle Tuxedo system, a larger default is used.THREADSTACKSIZE
, then the operating system default is used.MINDISPATCHTHREADS
(or TA_MINDISPATCHTHREADS
) must be less than or equal to MAXDISPATCHTHREADS
(or TA_MAXDISPATCHTHREADS
).MAXDISPATCHTHREADS
(or TA_MAXDISPATCHTHREADS
) is 1, then the dispatcher thread and the service function thread are the same thread.MAXDISPATCHTHREADS
(or TA_MAXDISPATCHTHREADS
) is greater than 1, any separate thread used for dispatching other threads does not count toward the limit of dispatched threads.MINDISPATCHTHREADS
(or TA_MINDISPATCHTHREADS
) server threads. MAXDISPATCHTHREADS
(or TA_MAXDISPATCHTHREADS
) server threads.Using your operating system functions, you may create additional threads within an application server. Application-created threads may:
Some restrictions govern what you can do if you create threads in your application.
TPNULLCONTEXT
before the originally dispatched thread calls tpreturn()
.The Bulletin Board Liaison (BBL) periodically checks servers. If a server is taking too long to execute a particular service request, the BBL kills that server. (If specified, the BBL then restarts the server.) If the BBL kills a multicontexted server, the other service calls that are currently being executed are also terminated as a result of the process being killed.
The BBL also sends a message to any process or thread that has been waiting longer than its timeout value to receive a message. The blocking message receive call then returns an error indicating a timeout.
For each server, the Oracle Tuxedo system maintains statistics for the following information:
TA_CURDISPATCHTHREADS
)TA_HWDISPATCHTHREADS
)TA_NUMDISPATCHTHREADS
)For each thread in each application, userlog(3c) records the following identifying information:
process_ID.thread_ID.context_ID
Placeholders are printed in the thread_ID
and context_ID
fields of entries for non-threaded platforms and single-contexted applications.
The TM_MIB(5) supports this functionality in the TA_THREADID
and TA_CONTEXTID
fields in the T_ULOG
class.
When the application is shut down, tpsvrthrdone(3c) and tpsvrdone(3c) are called to perform any termination processing that is necessary, such as closing a resource manager.
Multithreaded and multicontexted ATMI applications are appropriate for some Oracle Tuxedo domains, but not all. To decide whether to create such applications, you should answer several basic questions about the following:
When considering the development of multithreaded and/or multicontexted applications, examine the following aspects of your development and run-time environments:
THREADS=true
parameter as part of the OPENINFO
string passed to Oracle. By doing so, you make it possible for individual threads to operate as separate Oracle associations.Not all RMs support mixed-mode operation. Some require all threads in a given process to map to the same RM association. If you are designing an application that will make use of transactional RM access within application-created threads, make sure your RM supports mixed-mode operation.
When designing a multithreaded and/or multicontexted application, you should consider the following design questions:
The following table provides a list of questions to help you decide whether your application would be improved if it were multithreaded and/or multicontexted. This list is not comprehensive; your individual requirements will determine other factors that should be considered.
For additional suggestions, we recommend that you consult a multithreaded and/or multicontexted programming publication.
Decide how many applications you want to access and the number of connections you want to make.
This issue is an important one during the design phase. It is, however, beyond the scope of this documentation. Please refer to a publication about multithreaded and/or multicontexted programming.
If you may need to port your application in the future, you should keep in mind that different operating systems have different sets of functions. If you think you may want to port your application after completing the initial version of it on one platform, remember to consider the amount of staff time that will be needed to revise the code with a different set of functions.
Various models for multithreaded programs are now being used, including the following:
We do not discuss threads models in this documentation. We recommend that you research all available models and consider your design requirements carefully when choosing a programming model for your application.
Interoperability between release 7.1 Workstation clients and applications based on pre-7.1 releases of the Oracle Tuxedo system is supported in any of the following situations:
An Oracle Tuxedo Release 7.1 Workstation client with multiple threads in a single context cannot interoperate with a pre-7.1 release of the Oracle Tuxedo system.
Before you start coding, make sure you have fulfilled or thought about the following:
Make sure your environment meets the following prerequisites before starting your development project.
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 Installing the Oracle Tuxedo System.
Only experienced programmers should write multithreaded programs. In particular, programmers should already be familiar with basic design issues specific to this task, such as:
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.
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:
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. 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.
To enable multicontexting in a client, you must write code that:
If your application uses transactions, you should 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. |
When writing your code, keep in mind the following considerations about contexts:
TPNULLCONTEXT
.tpinit()
you must accommodate whatever security mechanisms have been established.
When a client is ready to join an application, specify tpiit(3c) with the TPMULTICONTEXTS
flag set, as shown in the following sample code.
#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 to tpinit(3c) 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(3c), then the process is restored to a state in which the inclusion of the TPMULTICONTEXTS flag in the next call to tpinit() is optional. |
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.
When you are ready to disconnect a client from an application, invoke tpterm(3c). 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(3c) 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. |
The following is a summary of the coding steps that might be made by a client that calls services from two contexts.
TUXCONFIG
environment variable to the value required by firstapp
.TPMULTICONTEXTS
flag set.TUXCONFIG
environment variable to the value required by the secondapp
context, by calling tuxputenv()
.TPMULTICONTEXTS
flag set.firstapp
context, start toggling between contexts by calling tpsetctxt(3c).firstapp
services.secondapp
context (by calling tpsetctxt(3c)) and call secondapp
services.firstapp
context (by calling tpsetctxt(3c)) and call firstapp
services.firstapp
context by calling tpterm(3c).secondapp
context (by calling tpsetctxt(3c)) and call secondapp
services.secondapp
context by calling tpterm(3c).The following sample code provides an example of these steps.
Note: | In order to simplify the sample, error checking code is not included. |
#include <stdio.h>
#include "atmi.h" /* BEA 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 BEA 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);
}
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(3c) is called from a thread that is not associated with a context, a per-process default unsolicited message handler for all new tpinit(3c) 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(3c) 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.
The following consequences of using transactions should be kept in mind while you are writing your application:
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. |
When writing your code, keep in mind the following considerations about contexts:
TPNULLCONTEXT
.Keep in mind the following rules for coding multicontexted servers:
tpinit()
, tpinit()
returns -1
and sets tperrno(5) to TPEPROTO
. An application-created server thread may not make ATMI calls before calling tpsetctxt(3c).tpreturn()
, each application-created thread associated with that context must call tpsetctxt(3c) with the context set to either TPNULLCONTEXT
or another valid context.
If this rule is violated, then tpreturn(3c) or tpforward(3c) 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.
tpreturn()
or tpforward()
writes a message to the user log, indicates TPESVCERR
to the caller, and returns control to the main server dispatch loop.To initialize and terminate your servers and server threads, you can use the default functions provided by the Oracle Tuxedo system or you can use your own.
You may create additional threads within an application server, although most applications using multicontexted servers use only the dispatched server threads created by the system. This section provides instructions for doing so.
You may create additional threads within an application server by using OS threads functions. These new threads may operate independently of the Oracle Tuxedo system, or they may operate in the same context as one of the server-dispatched threads.
Initially, application-created server threads are not associated with any server-dispatched context. If called before being initialized, however, most ATMI functions perform an implicit tpinit(3c). 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 an existing context before calling any ATMI functions. To associate an application-created server thread with an existing context, you must write code that implements the following procedure.
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(3c) or tpreturn(3c) fails and indicates a service error to the caller.
For those applications with a need to create an application thread in a server, the following code sample shows a multicontexted server in which a service creates another thread to help perform its work. Operating system (OS) threads functions differ from one OS to another. In this sample POSIX and ATMI functions are used.
Notes: | In order to simplify the sample, 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. |
#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();
return(NULL);
}
The previous example accomplishes a funds transfer by invoking the DEPOSIT
service in the originally dispatched thread, and WITHDRAWAL
in an application-created thread. This example is based on the assumption that the resource manager being used allows a mixed model such that multiple threads of a server can be associated with a particular database connection without all threads of the server being associated with that instance. Most resource managers, however, do not support such a model.
A simpler way to code this example is to avoid the use of an application-created thread. To obtain the same concurrency provided by the two calls to tpcall(3c) in the example, substitute two calls to tpacall(3c) and two calls to tpgetrply(3c) in the server-dispatched thread.
This topic includes the following sections:
Note: | The Oracle Tuxedo system does not support multithreaded COBOL applications. |
Keep in mind the following rules for coding multithreaded clients:
tpacall()
.tpcommit()
aborts the transaction, returns -1
, and sets tperrno(5) to TPEABORT
.TUXCONFIG
or WSNADDR
before calling tpinit(3c). You must synchronize the setting of the environment variable and the tpinit()
call if multiple threads may be performing such an action. All application associations in a client must obey the following rules:TPMULTICONTEXTS
flag set, even if the client is running in single-context mode.
To have a client join more than one context, issue a call to the tpinit(3c) function with the TPMULTICONTEXTS
flag set in the flags
element of the TPINIT
data structure.
In any one process, either all calls to tpinit(3c) 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(3c), 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(3c) 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(3c) without the TPMULTICONTEXTS
flag, all threads in the client are placed in the single-context state (TPSINGLECONTEXT
).
On failure, tpinit(3c) 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(3c) 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.
In a multicontext 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 diagram illustrates the context state changes that result from calls to tpinit(3c), tpsetctxt(3c), and tpterm(3c). (The tpgetctxt(3c) function does not produce any context state changes.)
Note: | When tpterm(3c) 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(3c), tpsetctxt(3c), and tpterm(3c).
tpsetctxt(3c) to context 0
|
||||
tpsetctxt(3c) to context > 0
|
||||
Implicit tpinit(3c)
|
||||
tpterm(3c) in this thread
|
||||
tpterm(3c) in a different thread of this context
|
tpgetrply(3c) receives responses only to requests made via tpacall(3c). Requests made with tpcall(3c) are separate and cannot be retrieved with tpgetrply()
regardless of the multithreading or multicontexting level.
tpgetrply(3c) 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(3c) is called in a multithreaded environment, the following restrictions apply:
tpgetrply()
for the same handle, tpgetrply()
returns -1
and sets tperrno
to TPEPROTO
. tpgetrply()
with the TPGETANY
flag, the call returns -1
and sets tperrno(5) to TPEPROTO
.
The same behavior occurs if a thread calls tpgetrply(3c) with the TPGETANY
flag while another thread in the same context is already waiting in tpgetrply()
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.
TPGETANY
flag set. If a second thread in the same context invokes tpgetrply()
with the TPGETANY
flag while a similar call is outstanding, this second call returns -1
and sets tperrno(5) to TPEPROTO
.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:
Note: | The environment is initially empty for those operating systems that do not recognize an operating system environment. |
TUXCONFIG
, WSNADDR
, or WSENVFILE
environment variable to the proper value before each call to tpinit(3c). If such an application is multithreaded, a mutex or other application-defined concurrency control will probably be needed to ensure that: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). The following ATMI functions affect only the application contexts in which they are called:
Note: | For tpbroadcast(3c), 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(3c). |
Note: | If tpsetunsol(3c) is called from a thread that is not associated with a context, a per-process default unsolicited message handler for all new tpinit(3c) 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. |
CLIENTID
, client name, username, transaction ID, and the contents of the TPSVCINFO
data structure may differ from context to context within the same process.The following Oracle Tuxedo functions affect the entire process in which they are called:
TPMULTICONTEXTS
mode or single-context mode is established. See also Using Per-context Functions and Data Structures in a Multithreaded ATMI Client.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.
Only the calling thread is affected by the following:
The Ferror, Ferror32(5), tperrno(5), tpurcode(5), and Uunix_err
variables are specific to each thread.
The identity of the current context is specific to each thread.
The following example shows a multithreaded client using ATMI calls. Threads functions differ from one operating system to another. In this example, POSIX functions are used.
Note: | In order to simplify this example, error checking code has not been included. |
#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 BEA 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 BEA 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);
}
Multithreaded servers are almost always multicontexted, as well. For information about writing a multithreaded server, see Writing Code to Enable Multicontexting and Multithreading in an ATMI Server.
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.
This topic includes the following sections:
We recommend following these recommendations during testing of your multithreaded and/or multicontexted code:
When you need to investigate possible causes of errors, we recommend that you start by checking whether and how the TPMULTICONTEXTS
flag has been set. Errors are frequently introduced by failures to set this flag or to set it properly.
If a process includes the TPMULTICONTEXTS
flag in a state for which this flag is not allowed (or omits TPMULTICONTEXTS
in a state that requires it), then tpinit(3c) returns -1
and sets tperrno
to TPEPROTO
.
When tpinit(3c) is invoked without TPMULTICONTEXTS
, it behaves as it does when called in a single-contexted application. When tpinit()
has been invoked once, subsequent tpinit()
calls without the TPMULTICONTEXTS
flag succeed without further action. This is true even if the value of the TUXCONFIG
or WSNADDR
environment variable in the application has been changed. Calling tpinit()
without the TPMULTICONTEXTS
flag set is not allowed in multicontext mode.
If a client has not joined an application and tpinit(3c) is called implicitly (as a result of a call to another function that calls tpinit()
), then the Oracle Tuxedo system interprets the action as a call to tpinit()
without the TPMULTICONTEXTS
flag for purposes of determining which flags may be used in subsequent calls to tpinit()
.
For most ATMI functions, if a function is invoked by a thread that is not associated with a context in a process already operating in multicontext mode, the ATMI function fails with tperrno(5)=TPEPROTO
.
On certain operating systems, the operating system default thread stack size is insufficient for use with the Oracle Tuxedo system. Compaq Tru64 UNIX and UnixWare are two operating systems for which this is known to be the case. If the default thread stack size parameter is used, applications on these platforms dump core when a function with substantial stack usage requirements is called by any thread other than the main thread. Often the core file that is created does not give any obvious clues to the fact that an insufficient stack size is the cause of the problem.
When the Oracle Tuxedo system is creating threads on its own, such as server-dispatched threads or a client unsolicited message thread, it can adjust the default stack size parameter on these platforms to a sufficient value. However, when an application is creating threads on its own, the application must specify a sufficient stack size. At a minimum, a value of 128K should be used for any thread that will access the Oracle Tuxedo system.
On Compaq Tru64 UNIX and other systems on which POSIX threads are used, a thread stack size is specified by invoking pthread_attr_setstacksize()
before calling pthread_create()
. On UnixWare, the thread stack size is specified as an argument to thr_create()
. Consult your operating system documentation for further information on this subject.
Errors are reported in the user log. For each error, whether in single-context mode or multicontext mode, the following information is recorded:
process_ID.thread_ID.context_ID
![]() ![]() ![]() |