By default, the code generated by rpcgen is not MT safe. It uses unprotected global variables and returns results in the form of static variables. The -M flag generates MT-safe code which can be used in a multithreaded environment. This can be used with the C-style flag, the ANSI C flag, or both.
An example of an MT-safe program with this interface follows. The rpcgen protocol file is msg.x, shown in Example 3-14.
program MESSAGEPROG { version PRINTMESSAGE { int PRINTMESSAGE(string) = 1; } = 1; } = 0x4001;
A string is passed to the remote procedure, which prints it and returns the length of the string to the client. The MT-Safe stubs are generated with:
% rpcgen -M msg.x
A possible client-side code that could be used with this is shown in Example 3-15.
#include "msg.h" void messageprog_1(host) char *host; { CLIENT *clnt; enum clnt_stat retval_1; int result_1; char * printmessage_1_arg; clnt = clnt_create(host, MESSAGEPROG, PRINTMESSAGE, "netpath"); if (clnt == (CLIENT *) NULL) { clnt_pcreateerror(host); exit(1); } printmessage_1_arg = (char *) malloc(256); strcpy(printmessage_1_arg, "Hello World"); retval_1 = printmessage_1(&printmessage_1_arg, &result_1,clnt); if (retval_1 != RPC_SUCCESS) { clnt_perror(clnt, "call failed"); } printf("result = %d\n", result_1); clnt_destroy(clnt); } main(argc, argv) int argc; char *argv[]; { char *host; if (argc < 2) { printf("usage: %s server_host\n", argv[0]); exit(1); } host = argv[1]; messageprog_1(host); } |
Note that a pointer to both the arguments and the results needs to be passed in to the rpcgen-generated code. This is to preserve reentrancy. The value returned by the stub function indicates whether this call is a success or a failure. The stub returns RPC_SUCCESS if the call is successful. Compare the MT-safe client stub (generated with the -M option) and the not MT-safe client stub shown in Example 3-16. The client stub that is not MT safe uses a static to store returned results and can use only one thread at a time.
int * printmessage_1(argp, clnt) char **argp; CLIENT *clnt; { static int clnt_res; memset((char *)&clnt_res, 0, sizeof (clnt_res)); if (clnt_call(clnt, PRINTMESSAGE, (xdrproc_t) xdr_wrapstring, (caddr_t) argp, (xdrproc_t) xdr_int, (caddr_t) &clnt_res, TIMEOUT) != RPC_SUCCESS) { return (NULL); } return (&clnt_res); }
The server side code is shown in Example 3-17.
When compiling a server that uses MT-safe mode, you must link in the threads library. To do this, specify the -lthread option in the compile command.
#include "msg.h" #include <syslog.h> bool_t printmessage_1_svc(argp, result, rqstp) char **argp; int *result; struct svc_req *rqstp; { int retval; if (*argp == NULL) { syslog(LOG_INFO, "argp is NULL\n"); *result = 0; } else { syslog("argp is %s\n", *argp); *result = strlen (*argp); } retval = 1; return (retval); } int messageprog_1_freeresult(transp, xdr_result, result) SVCXPRT *transp; xdrproc_t xdr_result; caddr_t result; { /* * Insert additional freeing code here, * if needed */ (void) xdr_free(xdr_result, result); } |
The server side code should not use statics to store returned results. A pointer to the result is passed in and this should be used to pass the result back to the calling routine. A return value of 1 indicates success to the calling routine, while 0 indicates a failure.
In addition, the code generated by rpcgen also generates a call to a routine to free any memory that may have been allocated when the procedure was called. To prevent memory leaks, any memory allocated in the service routine needs to be freed in this routine. messageprog_1_freeresult() frees the memory.
Normally, xdr_free() frees any allocated memory for you (in this case, no memory was allocated, so no freeing needs to take place).
As an example of the use of the -M flag with the C-style and ANSI C flag, consider the following file, add.x, shown in Example 3-18.
program ADDPROG { version ADDVER { int add(int, int) = 1; } = 1; }= 199;
This program adds two numbers and returns its result to the client. rpcgen is invoked on it, with the following command: % rpcgen -N -M -C add.x The multithreaded client code to call this is shown in Example 3-19.
The server-side procedure is shown in Example 3-20.
When compiling a server that uses MT-safe mode, you must link in the threads library. To do this, specify the -lthread option in the compile command.
add_1_svc(int arg1, int arg2, int *result, struct svc_req *rqstp) { bool_t retval; /* Compute result */ *result = arg1 + arg2; retval = 1; return (retval); } /* Routine for freeing memory that may * be allocated in the server procedure */ int addprog_1_freeresult(SVCXPRT *transp, xdrproc_t xdr_result, caddr_t result) { (void) xdr_free(xdr_result, result); }