Figure 10‑1 shows how a multithreaded client can issue calls to three servers simultaneously.
Figure 10‑2 shows how a server process can dispatch multiple threads to different clients simultaneously.
Figure 10‑3 shows how a multicontexted client process works within a domain. Each arrow represents an outstanding call to a server.
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.
If one thread of an application calls tpcommit() 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() and then start another transaction subject to the same restrictions that exist for single-threaded and single-context programs.
If tpinit() 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() 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.
process_ID.
thread_ID.
context_ID
The TM_MIB(5) supports this functionality in the
TA_THREADID and
TA_CONTEXTID fields in the
T_ULOG class.
|
•
|
MINDISPATCHTHREADS (or TA_MINDISPATCHTHREADS) must be less than or equal to MAXDISPATCHTHREADS (or TA_MAXDISPATCHTHREADS).
|
|
•
|
If MAXDISPATCHTHREADS (or TA_MAXDISPATCHTHREADS) is 1, then the dispatcher thread and the service function thread are the same thread.
|
|
•
|
If 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.
|
process_ID.
thread_ID.
context_ID
The TM_MIB(5) supports this functionality in the
TA_THREADID and
TA_CONTEXTID fields in the
T_ULOG class.
If necessary, after successfully calling tpappthrinit(3c), the application-created thread within an ATMI server may call
tpgetctxt(3c) and pass the handle that is returned (the value of which is the current context) to a second application thread within the same process.
The TM_MIB(5) supports this functionality in the
TA_THREADID and
TA_CONTEXTID fields in the
T_ULOG class.
tpappthrterm(3c) affects all application-created server threads that are currently working on the same context. Avoid calling
tpappthrterm(3c) while other application threads are still working on the terminated context.
|
•
|
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 each tpinit() you must accommodate whatever security mechanisms have been established.
|
ERROR_PROCESSING_CODE
}
.
.
.
|
Note:
|
In any one process, either all calls to tpinit() 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.
|
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.
|
1.
|
Set the TUXCONFIG environment variable to the value required by firstapp.
|
|
4.
|
Switch the value of the TUXCONFIG environment variable to the value required by the secondapp context, by calling tuxputenv().
|
#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);
}
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.
You can use tpgetctxt(3c) in an unsolicited message handler if you want to identify the context in which you are currently working.
|
•
|
A server cannot execute a tpreturn() or tpforward() if any application-created thread is still associated with any application context. Therefore, before a server-dispatched thread calls 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() 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.
|
3.
|
Application_thread_B associates itself with the current context by calling tpsetctxt(3c) specifying the handle received from Server-dispatched-thread_A.
|
|
Note:
|
Application-created server threads cannot call tpreturn() 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.
|
#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);
}
|
•
|
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 calling tpappthrterm(3c) after the application-created server thread has finished its work on that context.
|
|
•
|
It is not allowed to call tpappthrterm() in an application-created thread which is currently associated with a server-dispatched context.
|
|
•
|
It should be avoided calling tpappthrterm() on an application-created context while other application threads are still working on that context.
|
|
•
|
tpbegin() cannot be issued for a context that is already in transaction mode.
|
|
•
|
Two tpbegin() calls cannot be made simultaneously for the same context.
|
|
•
|
tpbegin() cannot be issued for a context that is already in transaction mode.
|
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.
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()).
|
Note:
|
When tpterm() 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).
|
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 in tpgetrply() for the same handle, tpgetrply() returns -1 and sets tperrno to TPEPROTO.
|
|
•
|
If a thread calls tpgetrply() for a specific handle while another thread in the same context is already waiting in tpgetrply() with the TPGETANY flag, the call returns -1 and sets tperrno(5) to TPEPROTO.
|
The same behavior occurs if a thread calls tpgetrply() 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.
|
•
|
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.
|
|
•
|
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).
|
|
Note:
|
For tpbroadcast(), 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 the TPSVCINFO data structure may differ from context to context within the same process.
|
|
•
|
tpconvert(3c)—the requested structure is converted, although it is probably relevant to only a subset of the process.
|
Listing 10‑5 shows a multithreaded client using ATMI calls. Threads functions differ from one operating system to another. In this example, POSIX functions are used.
#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.
*/
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.
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() returns
-1 and sets
tperrno to
TPEPROTO.
When tpinit() 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() 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().