Table of Contents
This chapter describes the configuration for using Multithread and Multicontext.
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.
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.
This section describes the client program flow and how to implement the program. Also included are examples of multithreading and multicontexting.
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.
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.
The following routines must be applied in the program implementation to use multithreading and multicontexting.
Starting section
Implementation section
Ending section
For more information about the functions used to implement a client program, refer to "Tmax Reference Guide".
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.
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".
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.
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.
The following are examples of a client program, a server program, and a Makefile.
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 */ /**********************************************************/
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); }
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)
This section describes the server program flow and contains examples of multithread and multicontext.
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.
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".
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 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.
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.
The server process calls the tpsvrinit() function as it is booted.
A thread pool is configured and service threads are created according to the environment configuration.
Each thread created initially calls the tpsvrthrinit() function one time.
Service threads wait in the thread pool. When a service request is received, an idle thread executes the service routine.
A service routine is executed by using the jobs described in Section 7.2.2, “Implementation Section”.
After calling the tpreturn() and tpforward() functions, the thread returns the process results to a client and waits in the thread pool.
If the server process is terminated, all created service threads execute the tpsvrthrdone() function and are terminated.
The server process calls the tpsvrdone() function and is terminated.
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.
A service request is received, and a service thread performs a service routine.
The service routine calls the tpgetctxt() function to retrieve its Context-ID.
An existing user-created thread is used or a new thread is created within the service routine.
The user-created thread receives the Context-ID from a service thread and calls tpsetctxt() to share the context.
Each thread perform its defined routine. Two threads can call ATMI APIs such as tpcall() and tpacall().
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.
The user-created thread checks its termination time.
The service thread executes tpreturn() and is returned to the thread pool to wait for the next service request.
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.
The following APIs can be utilized when writing multithread and multicontext server programs.
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".
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".
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.
The following are examples of a client program, a server program, and a Makefile.
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; }
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)
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
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().
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(¶m.ctxtid, 0); param.svcinfo = svcinfo; pthread_create(&tid, NULL, THREAD, ¶m); 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; }
Refer to Section 7.3.4, “Makefile”.
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