Chapter 7. Multithread and Multicontext

Table of Contents

7.1. Overview
7.2. Client Program
7.2.1. Program Flow
7.2.2. Program Implementation
7.2.3. Program Example
7.3. Server Program
7.3.1. Overview
7.3.2. Program Flow
7.3.3. Program Implementation
7.3.4. Service Processing Program Example
7.3.5. Context Sharing Program Example

This chapter describes the configuration for using Multithread and Multicontext.

7.1. Overview

The Tmax system supports a kernel-level threads package to enable multithreading and multicontexting. A developer must consider logic when writing a program that creates and removes a thread. The Tmax system supports multithreading/multicontexting for C applications but not for COBOL applications.

Note

The multithread and multicontext functions are available in the client library in Tmax version 3.8.15 and later and for the server library in Tmax v5.0 SP2 and later.

7.2. Client Program

This section describes the client program flow and how to implement the program. Also included are examples of multithreading and multicontexting.

7.2.1. Program Flow

7.2.1.1. Multithread

Multithread is when one or more execution units are included within a single process. A Tmax Multithreaded application can make multiple service requests at the same time within the same process.

The following is the flow of a multithreaded client application program. A single client process can call two services simultaneously.

[Figure 7.1] Tmax Client Multithread Application

Tmax Client Multithread Application

7.2.1.2. Multicontext

Multicontext is a programming methodology where a single client can make multiple connections to the Tmax system.

The following is the flow of a multicontext client application program. A single client contains multiple contexts and each context establishes a single connection to the Tmax system. When using multicontext, the Tmax system is considered a single user.

[Figure 7.2] Tmax Client Multicontext Application

Tmax Client Multicontext Application


7.2.2. Program Implementation

The following routines must be applied in the program implementation to use multithreading and multicontexting.

  1. Starting section

  2. Implementation section

  3. Ending section

Note

For more information about the functions used to implement a client program, refer to "Tmax Reference Guide".

Starting Section

The following functions can be used to start multithread/multicontext.

  • tpstart()

    int tpstart (TPSTART_T *tpinfo )

    tpstart() creates a connection to the Tmax system. The flags of the first argument, TPSTART_T, can be set to TPMULTICONTEXTS (Multicontext) or TPSINGLECONTEXT (Singlecontext). If the flag is unspecified, TPSTART_T is set to TPSINGLECONTEXT by default.

    Once a client is specified to run in Singlecontext or Multicontext, it continues to run in that mode.

    Note

    For more information about tpstart(), refer to Tmax. "3.3.10. tpstart".

  • tpsetctxt()

    int tpsetctxt(int ctxtid, long flags)

    tpsetctxt() sets the current context. Client and server programs create the tpsetctxt() function differently. For more information about the function, refer to "9.14.2. tpsetctxt".

Implementation Section

ATMI functions are used to implement multithread and multicontext. To use ATMI functions, a user must know whether the context used by the client is the current context.

  • Synchronous Communication

    When the Tmax system communicates with a client in synchronous mode, communication operates normally if the current context is not TPINVALIDCONTEXT. TPINVALIDCONTEXT is the state in which the current context is freed by another thread.

    E.g., thread1 issues tpstart() and gets context1. Simultaneously, thread2 uses context1 via tpsetctxt(). After completing the process in thread1, tpend() is called, which deletes context1 from the memory, and thread2 gets TPINVALIDCONTEXT.

    If the client is not in multicontext mode, it can automatically start a connection to Tmax system by calling an API, such as tpcall(), without using tpstart(). However, in multicontext mode, the client must issue an API, such as tpcall(), after using tpstart().

  • Asynchronous Communication

    In multicontext mode, after a thread calls tpacall(), another thread can access the results by calling tpgetrply() if the context is shared by the two threads. tpacall() must be called before tpgetrply(). The priority of the two threads must be explicitly defined in order to get a valid result.

    Similar to synchronous communication, the process is successfully completed as long as the current context is not TPINVALIDCONTEXT.

  • Transaction

    If a thread starts a transaction, the transactions of the threads that use the same context become a single transaction. Similar to asynchronous communication, priority must be explicitly defined. In addition, the process is successfully completed as long as the current context is not TPINVALIDCONTEXT.

Ending Section

Multithreading and multicontext are terminated by tpend().

int tpend()

If tpend() is not used, the information of the context and associated threads is not deleted from memory. Use of tpend() is recommended to prevent memory-related problems.

7.2.3. Program Example

The following are examples of a client program, a server program, and a Makefile.

Client Program

The following is a client program.

/*******************************************************************/
/*               Multi-thread/Multi-context Sample Program         */
/*                                                                 */
/* TmaxSoft Co. / QA                                               */
/* remarks: TOUPPER of Tmax must be started already.               */
/*******************************************************************/

#include       <pthread.h>
#include        <stdlib.h>
#include        <stdio.h>
#include        <errno.h>
#include        <string.h>
#include        <unistd.h>
#include        <netdb.h>
#include        <sys/types.h>
#include       <usrinc/atmi.h>

#define MAX_CTID_CNT       400       /* The number of maximum contexts*/
#define MAX_CALL           1       
#define NUM_THREADS        2       /* The number of threads that must be created at once*/
#define NUM_CONTEXTS       40       /* The number of contexts to be created in a thread*/

void *mythread(void *arg);
int svcCall(char* svc, char* arg);

int newContext();
int altContext(int id);
int delContext();

#define CTID_EMPTY       0
#define       CTID_OCCUPIED       1
#define THRERR   (void *)-1
#define THRSUC   (void *)1

extern int errno;
extern int _init_wthr_flag;
int           thr_id;
TPSTART_T* tpinfo;

int main()
{
        void      *retVal;
        char      argData[100];
        int       tcnt = 0;
        int       scnt = 0;
               
        pthread_t   p_thread[NUM_THREADS];        

        memset(argData, 0x00, sizeof(argData));
        strcpy(argData,"...mtmc test...");
       
       if (tmaxreadenv("tmax.env","TMAX") == -1)
       {
              printf( "tmax read env failed\n" );
              return FALSE;
       }       
    
        tpinfo = (TPSTART_T *)tpalloc("TPSTART", NULL, 0);
        if (tpinfo == NULL) 
        {
               printf("[THR:%d]tpinfo tpalloc fail[%d][%s]\n",pthread_self(), 
                      tperrno,tpstrerror(tperrno));
        }
        
#ifdef _MULTI_THREAD_TEST_  
        
       while(scnt<MAX_CALL) 
       {
          for ( tcnt=0 ; tcnt<NUM_THREADS ; tcnt++)
          {       
              if (pthread_create(&p_thread[tcnt], NULL, mythread,argData))
              {
                  fprintf(stderr, "mythread start fail...[%d]\n", errno);
                  return FALSE;
               }
          }
          for(tcnt=0 ; tcnt<NUM_THREADS ; tcnt++)
          {
              pthread_join(p_thread[tcnt], &retVal);
          }
                
           scnt++;
          sleep(1);
       }
        
#else       
      if (pthread_create(&p_thread[tcnt], NULL, mythread, argData))
       {  
          fprintf(stderr, "mythread start fail...[%d]\n", errno);
          return FALSE;
       }
          pthread_join(p_thread[tcnt], &retVal);          
#endif            
       
        tpfree((char *)tpinfo);
        return TRUE;
}


/**********************************************************/
/* Sub Process : myhread                                  */
/**********************************************************/
void *mythread(void *arg)
{
       int i,j,k;

printf("[THR:%d] thread start\n",pthread_self());

#ifdef _MULTI_CONTXT_TEST_
        tpinfo->flags = TPmulticontextS;
        for(i=0;i<NUM_CONTEXTS/2;i++)
       {
              j=newContext();
              k=newContext();

              svcCall("TOUPPER",arg);
              delContext();
               
               altContext(j);
               svcCall("TOUPPER",arg);
               delContext();
               
       }
        
#else
       tpinfo->flags = TPSINGLECONTEXT;
       newContext();
       svcCall("TOUPPER",arg);
       delContext();
       
#endif
                
printf("[THR:%d] thread finish\n",pthread_self());

        return THRSUC;
}


/**********************************************************/
/* Sub Process : delContext                               */
/**********************************************************/
int delContext()
{
      int i;
      int id;
       
      i = tpgetctxt(&id,TPNOFLAGS);
        
      if (i < 0) 
      {
          printf("\t[delContext]tpgetctxt fail[%d][%s]\n",
                  tperrno,tpstrerror(tperrno));
          return -1;
      }
        
      tpend();
      printf("\t[THR:%d][CTXT:%d]tpend.\n",pthread_self(),id); 
      return 1;
}

/**********************************************************/
/* Sub Process : newContext                               */
/**********************************************************/
int newContext()
{
       int i;
       int id;
       
       i = tpstart(tpinfo);
       
        if (i < 0)
        {
                printf("\t[newContext]tpstart fail[%d][%s]\n", tperrno, 
                       tpstrerror(tperrno));
                tpfree((char *)tpinfo);
                return -1;
        }
        
        i = tpgetctxt(&id,TPNOFLAGS);
        
        if (i < 0) 
        {
               printf("\t[newContext]tpgetctxt fail[%d][%s]\n", tperrno, 
                       tpstrerror(tperrno));
               return -1;
        } 
        return id;
}

/**********************************************************/
/* Sub Process : altContext                               */
/**********************************************************/
int altContext(int id)
{
       int i;
       int ret;

       ret = tpsetctxt(id, TPNOFLAGS);
        
       if (ret < 0)
       {
           printf("\t[altContext]tpsetctxt fail[%d][%s]\n",  tperrno,
                  tpstrerror(tperrno));
           tpfree((char *)tpinfo);
           return -1;
       }
       
       return 1;
}

/**********************************************************/
/* Sub Process : svcCall                                  */
/**********************************************************/
int svcCall(char* svc, char* arg)
{
       int ret;
       long rlen;
       char *sbuf, *rbuf;
       int  id;
       
       ret=tpgetctxt(&id,TPNOFLAGS);
       
       sbuf = (char *)tpalloc("STRING", NULL, 0);       
        if (sbuf == NULL)
        {
               printf("\t[svrCall]tpalloc error[%d][%s]\n",tperrno, 
                      tpstrerror(tperrno));
               return -1;
        }
        
        rbuf = (char *)tpalloc("STRING", NULL, 0);       
        if (rbuf == NULL)
        {
               printf("\t[svrCall]tpalloc error[%d][%s]\n",tperrno, 
                      tpstrerror(tperrno));
               return -1;
        }
        
        strcpy(sbuf, (char *)arg);
        
        ret = tpcall(svc, (char *)sbuf, strlen(arg), (char **)&rbuf, (long *)&rlen,
                     TPNOFLAGS);
        
        if (ret < 0)
        {
               printf("\t[svcCall]tpcall fail.[%d][%s]\n",tperrno, 
                      tpstrerror(tperrno));
        }
        else
        {
              printf("\t[THR:%d][CTXT:%d]tpcall success.\n",pthread_self(),id); 
        }
        
        tpfree((char *)sbuf);
        tpfree((char *)rbuf);        
}
/**********************************************************/
/*                             END                        */
/**********************************************************/

Server Program

The following is a server program.

#include <stdio.h>
#include <usrinc/atmi.h>

TOUPPER(TPSVCINFO *msg)
{
        int  i;

        printf("\tTOUPPER service is started!\n");
        printf("\tINPUT : data=%s\n", msg->data);

        for (i = 0; i < msg->len; i++)
             msg->data[i] = toupper(msg->data[i]);

        printf("\tOUTPUT: data=%s\n", msg->data);

        tpreturn(TPSUCCESS,0,(char *)msg->data, 0,0);
}

Makefile

The following is a makefile of a client program.

TARGET = $(COMP_TARGET)
APOBJS = $(TARGET).o

TMAXLIBD = $(TMAXDIR)/lib64

TMAXLIBS = -lcli
#TMAXLIBS =/home/ancestor/tmax/lib/libclid.a

#In case of multi_thread / multi_context
CFLAGS = -q64 -O -I$(TMAXDIR) -D_ MULTI_THREAD_TEST_ -D _MULTI_CONTXT_TEST_

#In case of single_thread / multi_context
CFLAGS = -q64 -O -I$(TMAXDIR) -D _MULTI_CONTXT_TEST_

LDFLAGS = -brtl

#
.SUFFIXES : .c

.c.o: 
       $(CC) $(CFLAGS) $(LDFLAGS) -c $<

#
# client compile
#
$(TARGET): $(APOBJS)
       $(CC) $(CFLAGS) $(LDFLAGS) -L$(TMAXLIBD) -o $(TARGET) $(APOBJS) $(TMAXLIBS) 

#
clean:
       -rm -f *.o core $(TARGET)

7.3. Server Program

This section describes the server program flow and contains examples of multithread and multicontext.

7.3.1. Overview

The Tmax multithread and multicontext server library supports the TCS type but not the USC and RDP types.

The multithread and multicontext server library supports multithreads and provides multicontext functions. Therefore, a single server process can handle service requests from multiple threads, and multiple threads can share a single context to run a service.

The standard Tmax server library does not provide multicontext functions, so a thread created by a user cannot use the APIs provided by the Tmax server library.

The following are the items that must be configured to use the multithread and multicontext server library.

  • A server application must be written by using related API.

  • When building a server program, the libsvrmt.so and tmaxsvrmt.dll multithread and multicontext server libraries must be linked.

  • In the SERVER section of the Tmax configuration file, the multithread and multicontext servers fields must be set. Set the information that the server uses multithread and multicontext.

    • Related Fields: SVRTYPE, MINTHR, MAXTHR, STACKSIZE, CPC, etc.

Using multithreads to write a program has various benefits. However, If concurrency and performance are not taken into consideration when writing a program, unexpected operations may occur. In order to write a multithread program, the user must have a deep understanding of potential problems of multithread programming.

The following are the advantages and disadvantages of multithread and multicontext.

  • Advantages

    • Code is simplified, which enables intuitive programming.

    • Number of server processes may be reduced.

  • Disadvantages

    • Difficult to keep synchronicity and to write the code.

    • Difficult to debug when an error occurs.

    • When porting to a multithread program, existing code must be reviewed to ensure it is thread-safe.

    • When integrating with RM, multithread support must be checked.

Note

The necessity of using multithreads must be sufficiently investigated by considering the advantages and disadvantages. If a problem occurs while processing a server program, check the log message. For more information about log messages, refer to "Tmax Error Message Reference Guide".

7.3.2. Program Flow

7.3.2.1. Multithread

Multiple threads can exist in a single process to handle multiple service requests. In the multithread and multicontext server library, a thread is classified as a Service thread or a User-created thread.

Service Thread

Service threads are managed by the multithread and multicontext server library. The server library creates a specific number of threads according to the configuration file to concurrently process multiple service requests. The library also manages the threads in a thread pool.

A thread pool works according to the MINTHR and MAXTHR fields of the configuration file. When a server process is booted, the minimum number of threads is created by default. If the number of threads dips below the minimum number, additional threads are created to maintain the minimum number. If the number of service requests increases and no idle threads exist in the thread pool, additional threads (up to the maximum number) are created to process the services.

[Figure 7.3] Tmax Server Multithread Application

Tmax Server Multithread Application

If a server process receives service requests, the server process handles each service request in a separate thread independently and returns the results to the client.

The basic operations of service routines and regular server programs are the same. Therefore, a service routine can be written like in a regular server library. Note that multiple threads can execute the same service routine, so all routines must be written thread-safe.

The following are the steps for writing a server program flow. Only step 5 is not executed automatically by the server library.

  1. The server process calls the tpsvrinit() function as it is booted.

  2. A thread pool is configured and service threads are created according to the environment configuration.

  3. Each thread created initially calls the tpsvrthrinit() function one time.

  4. Service threads wait in the thread pool. When a service request is received, an idle thread executes the service routine.

  5. A service routine is executed by using the jobs described in Section 7.2.2, “Implementation Section”.

  6. After calling the tpreturn() and tpforward() functions, the thread returns the process results to a client and waits in the thread pool.

  7. If the server process is terminated, all created service threads execute the tpsvrthrdone() function and are terminated.

  8. The server process calls the tpsvrdone() function and is terminated.

User-Created Thread

User-created threads can be created in a service or initialization routine such as tpsvrinit() or tpsvrthrinit(). User-created threads have their own starting routine and are not related to the thread pool managed by the server library. Therefore, when a service request is received, a user-created thread does not handle the request. The user must specify the time a thread is created or terminated.

Threads created by a user in tpsvrinit() or tpsvrthrinit() work independently from the multithread and multicontext server libraries. User-created threads do not have context. A user-created thread can be an alternative to a service routine, work with a service routine, or work independently from a service routine according to its purpose. Users must have Section 7.3.3, “Matters to be Considered for Development” when creating a thread.

The following are the steps for writing a flow when a user-created thread shares the context of a server thread. Steps 1 and 8 are executed automatically by the server library.

  1. A service request is received, and a service thread performs a service routine.

  2. The service routine calls the tpgetctxt() function to retrieve its Context-ID.

  3. An existing user-created thread is used or a new thread is created within the service routine.

  4. The user-created thread receives the Context-ID from a service thread and calls tpsetctxt() to share the context.

  5. Each thread perform its defined routine. Two threads can call ATMI APIs such as tpcall() and tpacall().

  6. Before a service thread calls tpreturn() or tpforward(), the following must be completed.

    • Terminate all synchronous/asynchronous/interactive communication.

    • If a transaction is in processing, commit or roll back the transaction.

    • The user-created thread calls tpsetctxt() to stop sharing the context.

  7. The user-created thread checks its termination time.

  8. The service thread executes tpreturn() and is returned to the thread pool to wait for the next service request.

7.3.2.2. multicontext

Context in the server library is information required to process a service request. In the multithread environment, each thread requires multicontext methodology to process each service independently.

All service threads managed by a thread pool have their own context by default. The multicontext methodology allows multiple threads to share a single context. Unlike service threads, user-created threads do not have their own context. To allow a user-created thread to use Tmax APIs, such tpcall(), tpgetctxt(), and tpsetctxt(), the API must be called to share the context of a service thread. If a user-created thread calls a Tmax API without sharing a context, the call fails and the TPEPROTO error code is displayed.

In the multithread and multicontext server library, context manages transaction information, synchronous/asynchronous communication information, and interactive communication information; if a user-created thread shares the context of a thread, the information is also shared.

[Figure 7.4] Tmax Server Multicontext Application

Tmax Server Multicontext Application

7.3.3. Program Implementation

The following APIs can be utilized when writing multithread and multicontext server programs.

Related API

  • tpsvrthrinit

    int tpsvrthrinit(int argc, char *argv[])

    Available only in the multithread and multicontext servers, tpsvrthrinit initializes a service thread when it is created. Service threads are managed by the thread pool after the tpsvrinit function is called from the multithread and multicontext servers. For more information about tpsvrthrinit(), refer to "10.1.5. tpsvrthrinit".

  • tpsvrthrdone

    int tpsvrthrdone()

    Available only by the multithread and multicontext servers. The multithread and multicontext servers terminate service threads before calling tpsvrdone to terminate the server process. For more information about tpsvrthrdone(), refer to "10.1.6. tpsvrthrdone".

  • tpsetctxt

    int tpsetctxt(int ctxtid, long flags)

    tpsetctxt sets the current context. Server programs and client programs differ in their programming method. For more information about tpsetctxt(), refer to "9.14.2. tpsetctxt".

  • tpgetctxt

    int tpgetctxt(int *ctxtid, long flags)

    tpgetctxt returns context ID, which is currently set in the thread that called this function, as the first parameter. For more information about tpgetctxt(), refer to "9.14.1. tpgetctxt".

Note

The server library provides other APIs that can be used. For more information about APIs used to implement a program, refer to "Tmax Reference Guide".

Matters to be Considered for Development

The following must be considered when writing a server program.

When APIs, such as tpsetctxt() and tpgetctxt(), are used between a service thread and a user-created thread, the following must be taken into consideration.

  • The time that user-created threads are created and terminated must be valid. If service routine only creates a user-created thread but does not delete it, calling the service routine can results in a large number of threads, which negatively affects system performance.

  • User-created threads do not have context by default, so they must share context with tpsetctxt() to communicate with Tmax.

  • The tpreturn() or tpforward() function must be called by a service thread. User-created threads cannot call tpreturn().

  • If any synchronous, asynchronous, or interactive communication is incomplete when the tpreturn() or tpforward() function is called, the TPESVCERR error code is returned to the client and the service fails.

  • A user-created thread cannot continue to share context when the tpreturn() or tpforward() function is called. Developers must use the tpsetctxt() function to set the context to TPNULLCONTEXT so a user-created thread releases the context before calling tpreturn(). Alternatively, developers can specify a different context and stop sharing the context with the user-created thread. Otherwise, tpreturn() returns the TPESVCERR error code to the client and the service fails.

  • The tpsetctxt() function cannot be called from a service thread. If called, the TPEPROTO error code is returned and the service fails.

  • Regardless of which thread among those that share the same content starts a transaction, the transaction can be committed or rolled back from only a single thread.

In addition to the above points, the following must also be taken into consideration.

  • When a service timeout occurs in the multithread or multicontext server, the server process is immediately terminated. Other service requests currently being processed are also canceled because, it is difficult to determine when the thread was suspended if it was suspended due to a timeout while running a service. For example, if a thread is suspended after occupying synchronization resources or receiving dynamically allocated memory, complex code is required to resolve this case and unexpected operations may occur.

  • The tpstart() and tpend() functions used by clients cannot be used.

  • Only services provided by its own server process can be called in synchronous/asynchronous communication. In this situation, a deadlock resolution process (e.g., setting a service timeout for related services) must be prepared in case a deadlock occurs.

7.3.4. Service Processing Program Example

The following are examples of a client program, a server program, and a Makefile.

Server Program

The following is a server program.

#include <stdio.h>
#include <usrinc/tmaxapi.h>

int tpsvrinit(int argc, char **argv)
{
        printf("tpsvrinit()");
        return 1;
}

int tpsvrthrinit(int argc, char **argv)
{
        printf("tpsvrthrinit()");
        return 1;
}

MTOUPPER(TPSVCINFO *msg)
{
        int i;
        printf("MTOUPPER service is started!");
        for (i = 0; i < msg->len; i++)
        msg->data[i] = toupper(msg->data[i]);
        tpreturn(TPSUCCESS,0,msg->data, 0, 0);
}

MTOLOWER(TPSVCINFO *msg)
{
        int i;
        printf("MTOLOWER service is started!");
        for (i = 0; i < msg->len; i++)
        msg->data[i] = tolower(msg->data[i]);
        tpreturn(TPSUCCESS,0,msg->data, 0, 0);
}

int tpsvrthrdone()
{
        printf("tpsvrthrdone()");
        return 1;
}

int tpsvrdone()
{
        printf("tpsvrdone()");
        return 1;
}

Makefile

The following is a server Makefile.

TARGET = $(COMP_TARGET)
APOBJS = $(TARGET).o
NSDLOBJ = $(TMAXDIR)/lib/sdl.o

LIBS = -lsvrmt -lnodb
OBJS = $(APOBJS) $(SVCTOBJ)
SVCTOBJ = $(TARGET)_svctab.o

CFLAGS = -I$(TMAXDIR) -D_MCONTEXT

APPDIR = $(TMAXDIR)/appbin
SVCTDIR = $(TMAXDIR)/svct
LIBDIR = $(TMAXDIR)/lib

#
.SUFFIXES : .c

.c.o:
        $(CC) $(CFLAGS) -c $<

#
# server compile
#

$(TARGET): $(OBJS)
        $(CC) $(CFLAGS) -L$(LIBDIR) -o $(TARGET) $(OBJS) $(LIBS) $(NSDLOBJ)
        mv $(TARGET) $(APPDIR)/.
        cp $(TARGET).c $(APPDIR)/.
        rm -f $(OBJS)

$(APOBJS): $(TARGET).c
        $(CC) $(CFLAGS) -c $(TARGET).c


$(SVCTOBJ):
        cp -f $(SVCTDIR)/$(TARGET)_svctab.c .
        touch ./$(TARGET)_svctab.c
        $(CC) $(CFLAGS) -c ./$(TARGET)_svctab.c

clean:
        -rm -f *.o core $(APPDIR)/$(TARGET)

Environment Configuration

The following is an environment configuration file.

*DOMAIN
tmax1   SHMKEY = 77214, MINCLH = 1, MAXCLH = 1,
        TPORTNO = 8888, BLOCKTIME = 30, MAXCACALL = 1024

*NODE
tmax    TMAXDIR = "/home/test/tmax",
        APPDIR = "/home/test/tmax/appbin",
        PATHDIR = "/home/test/tmax/path",
        TLOGDIR = "/home/test/tmax/log/tlog",
        ULOGDIR = "/home/test/tmax/log/ulog",
        SLOGDIR = "/home/test/tmax/log/slog",
        MAXCPC = 200

*SVRGROUP
svg1    NODENAME = tmax

*SERVER
svrmt1  SVGNAME = svg1, SVRTYPE = "STD_MT",
        MIN = 1, MAX = 1,
        CPC = 10, MINTHR = 5, MAXTHR = 10

*SERVICE
MTOUPPER SVRNAME = svrmt1, SVCTIME = 20
MTOLOWER SVRNAME = svrmt1, SVCTIME = 20

7.3.5. Context Sharing Program Example

A client calls the "MSERVICE" service. A service routine creates a user-created thread, and the user-created thread shares the context of the service thread and simultaneously requests a service through tpcall().

Server Program

The following is a server program.

#include <stdio.h>
#include <string.h>
#include <pthread.h>
#include <usrinc/tmaxapi.h>

void * THREAD(void *arg);

typedef struct {
        int ctxtid;
        TPSVCINFO *svcinfo;
} param_t;

int testcall(char *service, char *msg, long flags)
{
        char *sndbuf, *rcvbuf;
        long sndlen, rcvlen;

        sndlen = strlen(msg);
        if((sndbuf = (char *) tpalloc("STRING", NULL, sndlen)) == NULL) {
                printf("Error allocating send buffer, [tperrno:%d]", tperrno);
                return -1;
        }
        if((rcvbuf = (char *) tpalloc("STRING", NULL, 0)) == NULL) {
                printf("Error allocating recv buffer, [tperrno:%d]", tperrno);
                tpfree(sndbuf);
                return -1;
        }

        strcpy(sndbuf, msg);
        if(tpcall(service, sndbuf, sndlen, (char **)&rcvbuf, &rcvlen, flags) == -1)
                printf("tpcall(%s) failed, [tperrno:%d, tpurcode:%d]", service, tperrno, tpurcode);
        else
                printf("tpcall(%s) success, [rcvbuf:%s]", service, rcvbuf);

        tpfree(sndbuf);
        tpfree(rcvbuf);
        return 0;
}

MTOUPPER(TPSVCINFO *svcinfo)
{
        int i;

        printf("MTOUPPER service is started! [len:%d, data:%s]\n", svcinfo->len, svcinfo->data);

        for (i = 0; i < svcinfo->len; i++)
                svcinfo->data[i] = toupper(svcinfo->data[i]);

        sleep(1);
        printf("MTOUPPER service is finished!\n");
        tpreturn(TPSUCCESS, 0, svcinfo->data, 0, 0);
}

MTOLOWER(TPSVCINFO *svcinfo)
{
        int i;.

        printf("MTOLOWER service is started! [len:%d, data:%s]\n", svcinfo->len, svcinfo->data);
        for (i = 0; i < svcinfo->len; i++)
                svcinfo->data[i] = tolower(svcinfo->data[i]);

        sleep(1);
        printf("MTOLOWER service is finished!\n");
        tpreturn(TPSUCCESS, 0, (char *)svcinfo->data, 0, 0);
}

MSERVICE(TPSVCINFO *svcinfo)
{
        pthread_t tid;
        param_t param;

        printf("MSERVICE service is started!");

        tpgetctxt(&param.ctxtid, 0);
        param.svcinfo = svcinfo;
        pthread_create(&tid, NULL, THREAD, &param);

        testcall("MTOLOWER", svcinfo->data, 0);

        pthread_join(tid, NULL);
        printf("MSERVICE service is finished!");
        tpreturn(TPSUCCESS, 0, svcinfo->data, 0L, 0);
}

void *THREAD(void *arg)
{
        param_t *param;
        TPSVCINFO *svcinfo;

        param = (param_t *)arg;
        svcinfo = param->svcinfo;

        if (tpsetctxt(param->ctxtid, 0) == -1) {
                printf("tpsetctxt(%d) failed, [tperrno:%d]", param->ctxtid, tperrno);
                return NULL;
        }

        testcall("MTOUPPER", svcinfo->data, 0);

        if (tpsetctxt(TPNULLCONTEXT, 0) == -1) {
                printf("tpsetctxt(TPNULLCONTEXT) failed, [tperrno:%d]", tperrno);
                return NULL;
        }
        return NULL;
}

int tpsvrinit(int argc, char *argv[])
{
        printf("do tpsvrinit()");
        return 1;
}

int tpsvrthrinit(int argc, char *argv[])
{
        printf("do tpsvrthrinit()");
        return 1;
}

int tpsvrthrdone(void)
{
        printf("do tpsvrthrdone()");
        return 1;
}

int tpsvrdone(void)
{
        printf("do tpsvrdone()");
        return 1;
}

Makefile

Refer to Section 7.3.4, “Makefile”.

Environment Configuration

The following is an environment configuration file.

*DOMAIN
tmax1   SHMKEY = 77214, MINCLH = 1, MAXCLH = 1,
        TPORTNO = 8888, BLOCKTIME = 30, MAXCACALL = 1024

*NODE
tmax    TMAXDIR = "/home/test/tmax",
        APPDIR = "/home/test/tmax/appbin",
        PATHDIR = "/home/test/tmax/path",
        TLOGDIR = "/home/test/tmax/log/tlog",
        ULOGDIR = "/home/test/tmax/log/ulog",
        SLOGDIR = "/home/test/tmax/log/slog",
        MAXCPC = 200

*SVRGROUP
svg1    NODENAME = tmax

*SERVER
svrmt2  SVGNAME = svg1, SVRTYPE = "STD_MT",
        MIN = 1, MAX = 1,
        CPC = 10, MINTHR = 5, MAXTHR = 10

*SERVICE
MSERVICE SVRNAME = svrmt2, SVCTIME = 10
MTOUPPER SVRNAME = svrmt2
MTOLOWER SVRNAME = svrmt2