Programming an Oracle Tuxedo ATMI Application Using C

     Previous  Next    Open TOC in new window    View as PDF - New Window  Get Adobe Reader - New Window
Content starts here

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 only supports:

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 “Oracle Tuxedo 11g Release 1 (11.1.1.3.0) Platform Data Sheets” in Installing the Oracle Tuxedo System.

Platform-specific Considerations for Multithreaded/Multicontexted Applications

Many platforms have idiosyncratic requirements for multithreaded and multicontexted applications. “Oracle Tuxedo 11g Release 1 (11.1.1.3.0) Platform Data Sheets” in 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().

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

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

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

Multicontexted Process in Two Domains

Auditing a Multithreaded or Multicontexted Application

Each context is counted as one user. 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 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:

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: There may also be threads that work independently of the Oracle Tuxedo system. We do not consider such threads in this documentation.
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() 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.) 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.

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() for a synchronous request or tpacall() 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 tpacall(), 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().

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() 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.

Unsolicited Messages

For each context in a multithreaded or multicontexted application, you may choose one of three methods for handling unsolicited messages.

A context may . . .
By setting . . .
Ignore unsolicited messages
TPU_IGN
Use dip-in notification
TPU_DIP
Use dedicated thread notification.
(available only for C applications)
TPU_THREAD

The following caveats apply:

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() 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.

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() 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(). 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 Server-Dispatched Threads an on 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 . . .
Then . . .
0
> 1
  1. The Oracle Tuxedo system creates a thread dispatcher.
  2. The dispatcher calls tpsvrinit() to join the application.
> 0
> 1
  1. The Oracle Tuxedo system creates a thread dispatcher.
  2. The dispatcher calls tpsvrinit() to join the application.
  3. The Oracle Tuxedo system creates additional threads for handling service requests, and a context for each new thread.
  4. Each new system-created thread calls tpsvrthrinit(3c) to join the application.

Work Phase

In this phase, the following activities occur:

How 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 call tpinit().

Each dispatched thread is associated with a separate context. This feature is useful in both 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).

UBBCONFIG Parameter
MIB Parameter
Default
MINDISPATCHTHREADS
TA_MINDISPATCHTHREADS
0
MAXDISPATCHTHREADS
TA_MAXDISPATCHTHREADS
1
THREADSTACKSIZE
TA_THREADSTACKSIZE
0 (representing the OS default)

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

How Multithreading and Multicontexting Work in Application-Created Threads of an ATMI Server

Using your operating system functions, you may create additional threads within a Tuxedo application server. Initially, application-created server threads are not associated with any Tuxedo context. Application-created threads may:

An application-created server thread may create a separate Tuxedo context and associate itself with this context using tpappthrinit(3c). The life cycle of a Tuxedo context created by tpappthrinit(3c) when an application-created server thread is active can be described in three phases:

Start-Up Phase

An application-created server thread creates a Tuxedo context and associates itself with this context by calling tpappthrinit(3c). The context created by tpappthrinit(3c) connects to the domain that the application server is in.

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.

Work Phase

Each application-created server thread performs the following tasks:

These tasks performed in an application-created server thread share the same characteristics as in a client program. For more information, see How Multithreading and Multicontexting Work in a Client.

Notes: An application-created server thread cannot call either tpreturn() or tpforward().

Application-created server thread may send, but cannot receive unsolicited messages.

Userlog Maintains Thread-specific Information

For each application-created thread in an ATMI server, userlog(3c) records the following identifying information: 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.

Completion Phase

When an application-created server thread has finished its work, the thread calls tpappthrterm(3c) to terminate the current context.

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.

A well-designed application normally waits for all work in a particular context to complete before it calls tpappthrterm(3c). Be sure that all application threads are synchronized before your application thread calls tpappthrterm(3c).

See Also

How Multithreading and Multicontexting Work in a Client

Design Considerations for a Multithreaded and Multicontexted ATMI Application

Writing Code to Enable Multicontexting in Application-Created Threads of an ATMI Server

Writing Code to Enable Server-Dispatched Multicontexting and Multithreading Threads in an ATMI Server

Writing a Multithreaded ATMI Client

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 consider 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:

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.

If the answer to this question . . .
Is YES, then you might consider using . . .
Does your client need to connect to more than one application without using the Domains feature?
Multicontexting.
Does your client perform the role of a multiplexer within your application? For example, have you designated one machine in your application the “surrogate” for 100 other machines?
Multicontexting.
Does your client use multicontexting?
Multithreading. By allocating one thread per context, you can simplify your code.
Does your client perform two or more tasks that can be executed independently for a long time such that the performance gains from concurrent execution outweigh the costs and complexities of threads synchronization?
Multithreading.
Do you want one server to process multiple concurrent requests?
Multithreading. Assign a value greater than 1 to MAXDISPATCHTHREADS. This value enables multiple clients, each in its own thread, for the server.
Do you want to perform ATMI calls in application-created server thread?
Multithreading.
Application-created server thread creates a separate Tuxedo context and associates itself with the context by calling tpappthrinit(3c) (This feature is available from Tuxedo 11g Release 1 (11.1.1.3.0))
If your client or server had multiple threads, would it be necessary to synchronize them after each thread had performed only a little work?
Not using multithreading.

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.

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.

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:

See Also

Writing Code to Enable Multicontexting in an ATMI Client

This section contains the following topics:

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.

Context Attributes

When writing your code, keep in mind the following considerations about contexts:

Setting Up Multicontexting at Initialization

When a client is ready to join an application, specify tpinit() with the TPMULTICONTEXTS flag set, as shown in Listing 10-1.

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() 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.

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(). Keep in mind, however, that in a multicontexted application tpterm() destroys the current context. All the threads operating on that context are affected. As the application programmer, you must carefully coordinate the use of multiple threads to make sure that tpterm() is not called unexpectedly.

It is important to avoid calling tpterm() on a context while other threads are still working on that context. If such a call to tpterm() is made, the Oracle Tuxedo system places the other threads that had been associated with that context in a special invalid context state. When in the invalid context state, most ATMI functions are disallowed. A thread may exit from the invalid context state by calling tpsetctxt(3c) or tpterm(). Most well designed applications never have to deal with the invalid context state.

Note: The Oracle Tuxedo system does not support multithreading in COBOL applications.

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() with the TPMULTICONTEXTS flag set.
  3. Obtain a handle to the current context by calling tpgetctxt(3c).
  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() with the TPMULTICONTEXTS flag set.
  6. Get a handle to the current context by calling tpgetctxt(3c).
  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().
  12. Switch the client to the secondapp context (by calling tpsetctxt(3c)) and call secondapp services.
  13. Terminate the secondapp context by calling tpterm().

The following sample code provides an example of these steps.

Note: In order to simplify the sample, error checking code is not included.
Listing 10-2 Sample Code for Switching Contexts in a Client
#include <stdio.h>
#include "atmi.h" /* Oracle Tuxedo header file */

#if defined(__STDC__) || defined(__cplusplus)
main(int argc, char *argv[])
#else
main(argc, argv)
int argc;
char *argv[];
#endif
{

          TPINIT * tpinitbuf;
       TPCONTEXT_T firstapp_contextID, secondapp_contextID;
   /* Assume that TUXCONFIG is initially set to /home/firstapp/TUXCONFIG*/
          /*
        * Attach to the Oracle Tuxedo system in multicontext mode.
        */
        tpinitbuf=tpalloc(“TPINIT”, NULL, TPINITNEED(0));
   tpinitbuf->flags = TPMULTICONTEXTS;

       if (tpinit((TPINIT *) tpinitbuf) == -1) {
      (void) fprintf(stderr, "Tpinit failed\n");
      exit(1);
       }

       /*
        * Obtain a handle to the current context.
        */
          tpgetctxt(&firstapp_contextID, 0);

          /*
        * Use tuxputenv to change the value of TUXCONFIG,
        * so we now tpinit to another application.
        */
        tuxputenv("TUXCONFIG=/home/second_app/TUXCONFIG");

        /*
        * tpinit to secondapp.
        */
        if (tpinit((TPINIT *) tpinitbuf) == -1) {
                 (void) fprintf(stderr, "Tpinit failed\n");
                 exit(1);
       }

    /*
        * Get a handle to the context of secondapp.
*/
       tpgetctxt(&secondapp_contextID, 0);

       /*
        * Now you can alternate between the two contexts
        * using tpsetctxt and the handles you obtained from
           * tpgetctxt. You begin with firstapp.
        */

       tpsetctxt(firstapp_contextID, 0);

       /*
           * You call services offered by firstapp and then switch
        * to secondapp.
        */

          tpsetctxt(secondapp_contextID, 0);

        /*
           * You call services offered by secondapp.
        * Then you switch back to firstapp.
        */

          tpsetctxt(firstapp_contextID, 0);

       /*
           * You call services offered by firstapp. When you have
           * finished, you terminate the context for firstapp.
        */

       tpterm();

       /*
           * Then you switch back to secondapp.
        */

       tpsetctxt(secondapp_contextID, 0);
       /*
           * You call services offered by secondapp. When you have
              finished, you terminate the context for secondapp and
          end your program.
        */

       tpterm();

       return(0);
}

Handling Unsolicited Messages

For each context in which you want to handle unsolicited messages, you must set up an unsolicited message handler or use the process handler default if you have set one up.

If tpsetunsol() is called from a thread that is not associated with a context, a per-process default unsolicited message handler for all new tpinit() contexts created is established. A specific context may change the unsolicited message handler for that context by calling tpsetunsol() again when the context is active. The per-process default unsolicited message handler may be changed by again calling tpsetunsol() in a thread not currently associated with a context.

Set up the handler in the same way you set one up for a single-threaded or single-contexted application. See tpsetunsol() for details.

You can use tpgetctxt(3c) in an unsolicited message handler if you want to identify the context in which you are currently working.

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:

See Also

Writing Code to Enable Server-Dispatched Multicontexting and Multithreading Threads in an ATMI Server

This topic includes the following sections:

Note: The instructions and sample code provided in this section refer to the C library functions provided by the Oracle Tuxedo system. (See the Oracle Tuxedo C Function Reference for details.) Equivalent COBOL routines are not available because multithreading (which is required to create a multicontexted server) is not supported for COBOL applications.

Context Attributes

When writing your code, keep in mind the following considerations about contexts:

Coding Rules for Server-Dispatched Threads in Multicontexted ATMI Server

Keep in mind the following rules for coding multicontexted servers:

Initializing and Terminating ATMI Servers and Server Threads

To initialize and terminate your servers and server threads, you can use the default functions provided by the Oracle Tuxedo system.

Table 10-1 Default Functions for Initialization and Termination
To . . .
Use the default function
Initialize a server
Initialize a server thread
Terminate a server
Terminate a server thread

See Also

How Multithreading and Multicontexting Work in Server-Dispatched Threads an on ATMI Server

Writing Code to Enable Multicontexting in Application-Created Threads of an ATMI Server

This topic includes the following sections:

Creating Threads

You can create additional threads within a Tuxedo application server by using OS threads functions. These new application threads may operate independently of the Tuxedo system, or they may operate in the same context as one of the server-dispatched threads. Application-created server threads may also operate in separate context created via tpappthrinit(3c).

Associating Application Threads with a Context

Initially, application-created server threads are not associated with any Tuxedo context. If called before being initialized, however, most ATMI functions perform an implicit tpinit(). Such calls introduce problems because servers are prohibited from calling tpinit(). (If a server process calls tpinit(), tpinit() returns -1 and sets tperrno(5) to TPEPROTO.)

Therefore, an application-created server thread must associate itself with a valid context before calling any ATMI functions. An application-created server thread may:

Associating Application Threads with an Existing Server-Dispatched Context

To associate an application-created server thread with an existing server-dispatched context, you must write code that implements the following procedures.

  1. Server-dispatched-thread_A gets a handle to the current context by calling tpgetctxt(3c).
  2. Server-dispatched-thread_A passes the handle returned by tpgetctxt(3c) to Application_thread_B.
  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.
Note: If this rule is not observed, then tpforward() or tpreturn() fails and indicates a service error to caller.

Sample Code for Associating Application Thread with an Existing Server-Dispatched Context in a Multicontexted Server

For those applications that need to create an application thread in a server, Listing 10-3 shows a multicontexted server example where a service creates another thread to help perform its work in the same server-dispatched context. Operating system (OS) threads functions differ from one OS to another. In this sample POSIX and ATMI functions are used.

Note: In order to simplify the example, error checking code is not included. Also, an example of a multicontexted server using only threads dispatched by the Oracle Tuxedo system is not included because such a server is coded in exactly the same way as a single-contexted server, as long as thread-safe programming practices are used.
Listing 10-3 Code Sample for Application-Created Server Thread Working in Server-Dispatched Context
#include <pthread.h>
#include <atmi.h>

void *withdrawalthread(void *);

struct sdata {
    TPCONTEXT_T ctxt;
    TPSVCINFO *svcinfoptr;
};

void
TRANSFER(TPSVCINFO *svcinfo)
{
    struct sdata transferdata;
    pthread_t withdrawalthreadid;

    tpgetctxt(&transferdata.ctxt, 0);
    transferdata.svcinfoptr = svcinfo;
pthread_create( &withdrawalthreadid, NULL,
withdrawalthread, &transferdata );
    tpcall("DEPOSIT", ...);
    pthread_join(withdrawalthreadid, NULL);
    tpreturn(TPSUCCESS, ...);
}

void *
withdrawalthread(void *arg)
{
    tpsetctxt(arg->ctxt, 0);
    tpopen();
    tpcall("WITHDRAWAL", ...);
    tpclose();
    tpsetctxt(TPNULLCONTEXT, 0);
    return(NULL);
}

Associating Application Threads with Application-Created Context

An application-created thread within an ATMI server can create a separate Tuxedo context and associate itself with this context by calling tpappthrinit(3c).

Context Attributes
Code Rules for Application-Created Thread of an ATMI Server in Application-Created Context

Sample Code for Associating Application Thread with Application-created server Context in a Multicontexted Server

For those applications with a need to create an application thread in a server, Listing 10-4 shows a multicontexted server example where a service creates another thread, and this application-created server thread operates in a separated context. Operating system (OS) threads functions differ from one OS to another. In this example, POSIX and ATMI functions are used.

Note: In order to simplify the example, error checking code is not included.
Listing 10-4 Code Sample for Application-Created Server Thread Working in Application-Created Context
#include <pthread.h>
#include <atmi.h>

void *withdrawalthread(void *);

void
TRANSFER(TPSVCINFO *svcinfo)
{
    pthread_t withdrawalthreadid;

    pthread_create( &withdrawalthreadid, NULL,
                    withdrawalthread, … );
    tpcall("DEPOSIT", ...);
    pthread_join(withdrawalthreadid, NULL);
    tpreturn(TPSUCCESS, ...);
}

void *
withdrawalthread(void *arg)
{
    tpappthrinit(NULL);
    tpopen();
    tpcall("WITHDRAWAL", ...);
    tpclose();
    tpappthrterm();
    return(NULL);
}

See Also

How Multithreading and Multicontexting Work in Application-Created Threads of an ATMI Server

How Multithreading and Multicontexting Work in Server-Dispatched Threads an on ATMI Server

Writing a Multithreaded ATMI Client

This topic includes the following sections:

Note: The Oracle Tuxedo system does not support multithreaded COBOL applications.

Coding Rules for a Multithreaded ATMI Client

Keep in mind the following rules for coding multithreaded clients:

Initializing an ATMI Client to Multiple Contexts

To have a client join more than one context, issue a call to the tpinit() function with the TPMULTICONTEXTS flag set in the flags element of the TPINIT data structure.

In any one process, either all calls to tpinit() must include the TPMULTICONTEXTS flag or no call to tpinit() may include this flag. The only exception to this rule is that if all of a client’s application associations are terminated by successful calls to tpterm(), then the process is restored to a state in which the inclusion of the TPMULTICONTEXTS flag in the next call to tpinit() is optional.

When tpinit() is invoked with the TPMULTICONTEXTS flag set, a new application association is created and is designated the current association. The Oracle Tuxedo domain to which the new association is made is determined by the value of the TUXCONFIG or WSENVFILE/WSNADDR environment variable.

When a client thread successfully executes tpinit() without the TPMULTICONTEXTS flag, all threads in the client are placed in the single-context state (TPSINGLECONTEXT).

On failure, tpinit() leaves the calling thread in its original context (that is, in the context state in which it was operating before the call to tpinit()).

Do not call tpterm() from a given context if any of the threads in that context are still working. See the table labeled Multicontext State Transitions for a description of the context states that result from calling tpterm() under these and other circumstances.

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(), tpsetctxt(3c), and tpterm(). (The tpgetctxt(3c) function does not produce any context state changes.)

Figure 10-4 Multicontext State Transitions

Multicontext State Transitions

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).

The following table lists all possible context state changes produced by calling tpinit(), tpsetctxt(3c), and tpterm().

Table 10-2 Context State Changes for a Client Thread
When this function is executed . . .
Then a thread in this context state results in . . .
Null Context
Single Context
Multicontext
Invalid Context
tpinit() without TPMULTICONTEXTS
Single context
Single context
Error
Error
tpinit() with TPMULTICONTEXTS
Multicontext
Error
Multicontext
Error
tpsetctxt(3c) to TPNULLCONTEXT
Null
Error
Null
Null
tpsetctxt(3c) to context 0
Error
Single context
Error
Error
tpsetctxt(3c) to context > 0
Multicontext
Error
Multicontext
Multicontext
Implicit tpinit()
Single context
N/A
N/A
Error
tpterm() in this thread
Null
Null
Null
Null
tpterm() in a different thread of this context
N/A
Null
Invalid
N/A

Getting Replies in a Multithreaded Environment

tpgetrply() receives responses only to requests made via tpacall(). Requests made with tpcall() are separate and cannot be retrieved with tpgetrply() regardless of the multithreading or multicontexting level.

tpgetrply() operates in only one context, which is the context in which it is called. Therefore, when you call tpgetrply() with the TPGETANY flag, only handles generated in the same context are considered. Similarly, a handle generated in one context may not be used in another context, but the handle may be used in any thread operating within the same context.

When tpgetrply() is called in a multithreaded environment, the following restrictions apply:

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:

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(), 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().
Note: 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.

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:

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

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.

Note: In order to simplify this example, error checking code has not been included.
Listing 10-5 Sample Code for a Multithreaded Client
#include <stdio.h>
#include <pthread.h>
#include <atmi.h>

TPINIT * tpinitbuf;
int timeout=60;
pthread_t withdrawalthreadid, stockthreadid;
TPCONTEXT_T ctxt;
void * stackthread(void *);
void * withdrawalthread(void *);

main()
{
       tpinitbuf = tpalloc(TPINIT, NULL, TPINITNEED(0));
       /*
        * This code will perform a transfer, using separate threads for the
        * withdrawal and deposit. It will also get the current
        * price of Oracle stock from a separate application, and calculate how
        * many shares the transferred amount can buy.
        */

       tpinitbuf->flags = TPMULTICONTEXTS;

       /* Fill in the rest of tpinitbuf. */
       tpinit(tpinitbuf);

       tpgetctxt(&ctxt, 0);
       tpbegin(timeout, 0);
       pthread_create(&withdrawalthreadid, NULL, withdrawalthread, NULL);
       tpcall("DEPOSIT", ...);

       /* Wait for the withdrawal thread to complete. */
       pthread_join(withdrawalthreadid, NULL);

       tpcommit(0);
       tpterm();

       /* Wait for the stock thread to complete. */
       pthread_join(stockthreadid, NULL);

       /* Print the results. */
       printf("$%9.2f has been transferred \
       from your savings account to your checking account.\n", ...);

       printf("At the current Oracle stock price of $%8.3f, \
       you could purchase %d shares.\n", ...);

       exit(0);
}



void *
stockthread(void *arg)
{

     /* The other threads have now called tpinit(), so resetting TUXCONFIG can
      * no longer adversely affect them.
     */

     tuxputenv("TUXCONFIG=/home/users/xyz/stockconf");
     tpinitbuf->flags = TPMULTICONTEXTS;
     /* Fill in the rest of tpinitbuf. */
     tpinit(tpinitbuf);
     tpcall("GETSTOCKPRICE", ...);
     /* Save the stock price in a variable that can also be accessed in main(). */
     tpterm();
     return(NULL);
}



void *
withdrawalthread(void *arg)
{
       /* Create a separate thread to get stock prices from a different
        * application.
       */


     pthread_create(&stockthreadid, NULL, stockthread, NULL);
     tpsetctxt(ctxt, 0);
     tpcall("WITHDRAWAL", ...);
     return(NULL);
}

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 Server-Dispatched Multicontexting and Multithreading Threads in an ATMI Server.

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() returns -1 and sets tperrno to TPEPROTO.

Calls to tpinit( ) Without TPMULTICONTEXTS

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().

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


  Back to Top       Previous  Next