Table of Contents Previous Next PDF


Programming a Multithreaded and Multicontexted ATMI Application

Programming a Multithreaded and Multicontexted ATMI Application
This topic includes the following sections:
Support for Programming a Multithreaded/Multicontexted ATMI Application
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.
Platform-specific Considerations for Multithreaded/Multicontexted Applications
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.
See Also
Planning and Designing a Multithreaded/Multicontexted ATMI Application
This topic includes the following sections:
What Are Multithreading and Multicontexting?
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:
What Is Multithreading?
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.
Figure 10‑1 shows how a multithreaded client can issue calls to three servers simultaneously.
Figure 10‑1 Sample Multithreaded Process
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.
Figure 10‑2 shows how a server process can dispatch multiple threads to different clients simultaneously.
Figure 10‑2 Multiple Service Threads Dispatched in One Server Process
What Is Multicontexting?
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.
Figure 10‑3 shows how a multicontexted client process works within a domain. Each arrow represents an outstanding call to a server.
Figure 10‑3 Multicontexted Process in Two Domains
Licensing a Multithreaded or Multicontexted Application
For licensing purposes, each context is counted as one user. Additional licenses are not required to accommodate multiple threads within one context. For example:
See Also
Advantages and Disadvantages of a Multithreaded/Multicontexted ATMI Application
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.
Advantages of a Multithreaded/Multicontexted ATMI Application
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.
Disadvantages of a Multithreaded/Multicontexted ATMI Application
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:
Because the completed port must be tested and retested, the work required to port a multithreaded and/or multicontexted application is substantial.
See Also
How Multithreading and Multicontexting Work in a Client
When a multithreaded and multicontexted application is active, the life cycle of a client can be described in three phases:
Start-up Phase
In the start-up phase the following events occur:
Note:
Client Threads Join Multiple Contexts
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.
Client Threads Switch to an Existing Context
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” on page 10‑44.) 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” on page 10‑44.
Work Phase
In this phase each thread performs a task. The following is a list of sample tasks:
Service Requests
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.
Replies to Service Requests
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).
Transactions
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.
Unsolicited Messages
For each context in a multithreaded or multicontexted application, you may choose one of three methods for handling unsolicited messages.
 
Use dedicated thread notification.
(available only for C applications)
The following caveats apply:
If your application runs on a platform that supports multicontexting but not multithreading, then you cannot use the 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.
Userlog Maintains Thread-specific Information
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.
Completion Phase
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().
See Also
How Multithreading and Multicontexting Work in an ATMI Server
The events that occur in an ATMI server when a multithreaded and multicontexted application is active can be described in three phases:
Start-up Phase
What happens during the start-up phase depends on the value of the MINDISPATCHTHREADS and MAXDISPATCHTHREADS parameters in the configuration file.
 
If the value of MINDISPATCHTHREADS is . . .
And the value of MAXDISPATCHTHREADS
is . . .
2.
The dispatcher calls tpsvrinit(3c) to join the application.
2.
The dispatcher calls tpsvrinit(3c) to join the application.
Work Phase
In this phase, the following activities occur:
Server-dispatched Threads Are Used
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).
 
Each dispatched thread is created with the stack size specified by 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.
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.
Initially, the system boots MINDISPATCHTHREADS (or TA_MINDISPATCHTHREADS) server threads.
The system never boots more than MAXDISPATCHTHREADS (or TA_MAXDISPATCHTHREADS) server threads.
Application-created Threads Are Used
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.
Initially, application-created server threads are not associated with any server dispatch context. An application-created server thread may call tpsetctxt(3c) (and pass it a value returned by a previous call to tpgetctxt(3c) within a server-dispatched thread) to associate itself with that server-dispatched context.
An application-created server thread cannot call tpreturn(3c) or tpforward(3c). When an application-created server thread has finished its work, it must call tpsetctxt(3c) with the context set to TPNULLCONTEXT before the originally dispatched thread calls tpreturn().
Bulletin Board Liaison Verifies Sanity of System Processes
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.
System Keeps Statistics on Server Threads
For each server, the Oracle Tuxedo system maintains statistics for the following information:
Userlog Maintains Thread-specific Information
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.
Completion Phase
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.
See Also
Design Considerations for a Multithreaded and Multicontexted ATMI Application
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:
Environment Requirements
When considering the development of multithreaded and/or multicontexted applications, examine the following aspects of your development and run-time environments:
Do you need to set any parameters required by your RM to enable multithreaded access by your servers? For example, if you use an Oracle database with a multithreaded application, you must set the 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.
Design Requirements
When designing a multithreaded and/or multicontexted application, you should consider the following design questions:
Is the Task of Your Application Suitable for Multithreading and/or Multicontexting?
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.
 
Multithreading. Assign a value greater than 1 to MAXDISPATCHTHREADS. This value enables multiple clients, each in its own thread, for the server.
How Many Applications and Connections Do You Want?
Decide how many applications you want to access and the number of connections you want to make.
In both cases, multithreaded, multicontexted servers may be used.
What Synchronization Issues Need to Be Addressed?
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.
Will You Need to Port Your Application?
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.
Which Threads Model Is Best for You?
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 Restrictions for Workstation Clients
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.
See Also
Implementing a Multithreaded/ Multicontexted ATMI Application
Preliminary Guidelines for Programming a Multithreaded/Multicontexted ATMI Application
Before you start coding, make sure you have fulfilled or thought about the following:
Prerequisites for a Multithreaded ATMI Application
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.
General Multithreaded Programming Considerations
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.
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 compiled before tpcommit(3c) 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(3c) in one thread and tpgetrply(3c) in another, but the application must either:
Ensure that tpacall() is called before tpgetrply(), or
Manage the consequences if tpacall() is not called before tpgetrply()
Multiple threads may operate on the same conversation but application programmers must realize that if different threads issue tpsend(3c) 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
Writing Code to Enable Multicontexting in an ATMI Client
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” on page 10‑32.
Note:
Context Attributes
When writing your code, keep in mind the following considerations about contexts:
If an application-created server thread exits without changing context before the original dispatched thread exits, then tpreturn(3c) or tpforward(3c) fails. The execution of a thread exit does not automatically trigger a call to tpsetctxt(3c) to change the context to TPNULLCONTEXT.
If you call tpinit(3c) 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.
Setting Up Multicontexting at Initialization
When a client is ready to join an application, specify tpiit(3c) with the TPMULTICONTEXTS flag set, as shown in the following sample code.
Listing 10‑1 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 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.
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.
Synchronizing Threads Before an ATMI Client Termination
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:
Switching Contexts
The following is a summary of the coding steps that might be made by a client that calls services from two contexts.
1.
Set the TUXCONFIG environment variable to the value required by firstapp.
2.
Join the first application by calling tpinit(3c) with the TPMULTICONTEXTS flag set.
3.
4.
Switch the value of the TUXCONFIG environment variable to the value required by the secondapp context, by calling tuxputenv().
5.
Join the second application by calling tpinit(3c) with the TPMULTICONTEXTS flag set.
6.
7.
Beginning with the firstapp context, start toggling between contexts by calling tpsetctxt(3c).
8.
Call firstapp services.
9.
Switch the client to the secondapp context (by calling tpsetctxt(3c)) and call secondapp services.
10.
Switch the client to the firstapp context (by calling tpsetctxt(3c)) and call firstapp services.
11.
Terminate the firstapp context by calling tpterm(3c).
12.
Switch the client to the secondapp context (by calling tpsetctxt(3c)) and call secondapp services.
13.
Terminate the secondapp context by calling tpterm(3c).
The following sample code provides an example of these steps.
Note:
Listing 10‑2 Sample Code for Switching Contexts in a Client
#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);
}
 
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(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.
Coding Rules for Transactions in a Multithreaded/Multicontexted ATMI Application
The following consequences of using transactions should be kept in mind while you are writing your application:
You can call tpcommit(3c) from only one thread in any particular transaction.
See Also
Writing Code to Enable Multicontexting and Multithreading 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.
Context Attributes
When writing your code, keep in mind the following considerations about contexts:
If an application-created server thread exits without changing context before the original dispatched thread exits, then tpreturn(3c) or tpforward(3c) fails. The execution of a thread exit does not automatically trigger a call to tpsetctxt(3c) to change the context to TPNULLCONTEXT.
Coding Rules for a Multicontexted ATMI Server
Keep in mind the following rules for coding multicontexted servers:
A server is prohibited from calling tpinit(3c) or otherwise acting as a client. If a server process calls tpinit(), tpinit() returns -1 and sets tperrno(5) to TPEPROTO. An application-created server thread may not make ATMI calls before calling tpsetctxt(3c).
A server cannot execute a tpreturn(3c) or tpforward(3c) 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(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.
If there are outstanding ATMI calls, RPC calls, or conversations when tpreturn(3c) or tpforward(3c) is called, tpreturn() or tpforward() writes a message to the user log, indicates TPESVCERR to the caller, and returns control to the main server dispatch loop.
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 or you can use your own.
 
Programming an ATMI Server to Create Threads
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.
Creating Threads
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.
Associating Threads with a Context
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.
1.
2.
3.
Application_thread_B associates itself with the current context by calling tpsetctxt(3c), specifying the handle received from Server-dispatched-thread_A.
4.
Application-created server threads cannot call tpreturn(3c) or tpforward(3c). Before the originally 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(3c) or tpreturn(3c) fails and indicates a service error to the caller.
Sample Code for Creating an Application Thread in a Multicontexted ATMI Server
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.
Listing 10‑3 Code Sample for Creating a Thread in a Multicontexted Server
#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.
See Also
Writing a Multithreaded ATMI Client
This topic includes the following sections:
Note:
Coding Rules for a Multithreaded ATMI Client
Keep in mind the following rules for coding multithreaded clients:
Any thread operating in the same context within the same process can invoke tpgetrply(3c) to receive a response to an earlier call to tpacall(3c), regardless of whether or not that thread originally called tpacall().
All RPC calls and conversations must be completed before an attempt is made to commit the transaction. If an application calls tpcommit(3c) while RPC calls or conversations are outstanding, tpcommit() aborts the transaction, returns -1, and sets tperrno(5) to TPEABORT.
Functions such as tpcall(3c), tpacall(3c), tpgetrply(3c), tpconnect(3c), tpsend(3c), tprecv(3c), and tpdiscon(3c) should not be called in transaction mode unless you are sure that the transaction is not already committing or aborting.
Two tpbegin(3c) calls cannot be made simultaneously for the same context.
tpbegin(3c) 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 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:
To join an application, a multithreaded Workstation client must always call tpinit(3c) with the TPMULTICONTEXTS flag set, even if the client is running in single-context mode.
Initializing an ATMI Client to Multiple Contexts
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” on page 10‑40 for a description of the context states that result from calling tpterm() under these and other circumstances.
Context State Changes for an ATMI Client Thread
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. Figure 10‑4 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.)
 
Figure 10‑4 Multicontext State Transitions
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).
Table 10‑2 lists all possible context state changes produced by calling tpinit(3c), tpsetctxt(3c), and tpterm(3c).
 
tpinit(3c) without TPMULTICONTEXTS
tpinit(3c) with TPMULTICONTEXTS
tpsetctxt(3c) to TPNULLCONTEXT
tpsetctxt(3c) to context 0
tpsetctxt(3c) to context > 0
tpterm(3c) in this thread
tpterm(3c) in a different thread of this context
Getting Replies in a Multithreaded Environment
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:
If a thread calls tpgetrply(3c) 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(3c) 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(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.
At any given time, only one thread in a particular context can wait in tpgetrply(3c) with the 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.
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:
Note:
 
FIELDTBLS and FIELDTBLS32
FLDTBLDIR and FLDTBLDIR32
VIEWDIR and VIEWDIR32
VIEWFILES and VIEWFILES32
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, 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:
The call to tpinit(3c) 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).
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:
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).
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.
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.
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:
tpconvert(3c)—the requested structure is converted, although it is probably relevant to only a subset of the process.
tpinit(3c)—to the extent that the per-process TPMULTICONTEXTS mode or single-context mode is established. See also “Using Per-context Functions and Data Structures in a Multithreaded ATMI Client” on page 10‑44.
tuxgetenv(3c)—if the OS environment is per-process.
tuxputenv(3c)—if the OS environment is per-process.
tuxreadenv(3c)—if the OS environment is per-process.
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.
Using Per-thread Functions and Data Structures in a Multithreaded ATMI Client
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.
Sample Code for a Multithreaded ATMI Client
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:
Listing 10‑4 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 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);
}
 
See Also
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 Multicontexting and Multithreading in an ATMI Server” on page 10‑33.
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
Testing a Multithreaded/Multicontexted ATMI Application
This topic includes the following sections:
Testing Recommendations for a Multithreaded/Multicontexted ATMI Application
We recommend following these recommendations during testing of your multithreaded and/or multicontexted code:
Troubleshooting a Multithreaded/Multicontexted ATMI Application
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.
Improper Use of the TPMULTICONTEXTS Flag to tpinit( )
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.
Calls to tpinit( ) Without TPMULTICONTEXTS
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.
Insufficient Thread Stack Size
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.
Error Handling for a Multithreaded/Multicontexted ATMI Application
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
See Also

Copyright © 1994, 2017, Oracle and/or its affiliates. All rights reserved.