ONC+ Developer's Guide

MT User Mode

In MT User mode, the RPC library will not create any threads. This mode works, in principle, like the single-threaded, or default mode. The only difference is that it passes copies of data structures (such as the transport service handle to the service dispatch routine) to be MT safe.

The RPC server developer takes the responsibility for creating and managing threads through the thread library. In the dispatch routine, the service developer can assign the task of procedure execution to newly created or existing threads. The thr_create() API is used to create threads having various attributes. All thread library interfaces are defined in thread.h. See the pthread_create(3THR) man page for more details.

There is a lot of flexibility available to the service developer in this mode. Threads can now have different stack sizes based on service requirements. Threads may be bound. Different procedures may be executed by threads with different characteristics. The service developer may choose to run some services single threaded. The service developer may choose to do special thread-specific signal processing.

As in the Auto mode, the rpc_control() library call is used to turn on User mode. Note that the rpc_control() operations shown in Table 4-10 (except for RPC_SVC_MTMODE_GET()) apply only to MT Auto mode. If used in MT User mode or the single-threaded default mode, the results of the operations may be undefined.

Freeing Library Resources in User Mode

In the MT User mode, service procedures must invoke svc_done() before returning. svc_done() frees resources allocated to service a client request directed to the specified service transport handle. This function is invoked after a client request has been serviced, or after an error or abnormal condition that prevents a reply from being sent. After svc_done() is invoked, the service transport handle should not be referenced by the service procedure. Example 4-41 shows a server in MT User mode.


Note -

svc_done() must only be called within MT User mode. For more information on this call, see the rpc_svc_calls(3NSL) man page.



Example 4-41 MT User Mode: rpc_test.h

#define	SVC2_PROG 0x30000002
	#define	SVC2_VERS 1
	#define SVC2_PROC_ADD 1)
	#define SVC2_PROC_MULT 2

	struct intpair {
		u_short	a;
		u_short	b;
	};

	typedef struct intpair intpair;

	struct svc2_add_args {
		int argument;
		SVCXPRT *transp;
	};

	struct svc2_mult_args {
		intpair mult_argument;
		SVCXPRT *transp;
	};

	extern bool_t xdr_intpair();

	#define NTHREADS_CONST 500

Example 4-42 is the client for MT User mode.


Example 4-42 Client for MT User Mode

#define	 _REENTRANT
#include <stdio.h>
#include <rpc/rpc.h>
#include <sys/uio.h>
#include <netconfig.h>
#include <netdb.h>
#include <rpc/nettype.h>
#include <thread.h>
#include "rpc_test.h"
void *doclient();
int NTHREADS;
struct thread_info {
	thread_t client_id;
	int client_status;
};
struct thread_info save_thread[NTHREADS_CONST];
main(argc, argv)
	int argc;
	char *argv[];
{
	int index, ret;
	int thread_status;
	thread_t departedid, client_id;
	char *hosts;
	if (argc < 3) {
		printf("Usage: do_operation [n] host\n");
		printf("\twhere n is the number of threads\n");
		exit(1);
	} else
		if (argc == 3) {
			NTHREADS = NTHREADS_CONST;
			hosts = argv[1];  /* live_host */
		} else {
			NTHREADS = atoi(argv[1]);
			hosts = argv[2];
		}
	for (index = 0; index < NTHREADS; index++){
		if (ret = thr_create(NULL, NULL, doclient,
		(void *)  hosts, THR_BOUND, &client_id)){
			printf("thr_create failed: return value %d", ret);
			printf(" for %dth thread\n", index);
			exit(1);
		}
		save_thread[index].client_id = client_id;
	}
	for (index = 0; index < NTHREADS; index++){
		if (thr_join(save_thread[index].client_id, &departedid,	
		(void *)
		&thread_status)){
			printf("thr_join failed for thread %d\n",
			save_thread[index].client_id);
			exit(1);
		}
		save_thread[index].client_status = thread_status;
	}
}
	void *doclient(host)
	char *host;
{
	struct timeval tout;
	enum clnt_stat test;
	int result = 0;
	u_short mult_result = 0;
	int add_arg;
	int EXP_RSLT;
	intpair pair;
	CLIENT *clnt;
	if ((clnt = clnt_create(host, SVC2_PROG, SVC2_VERS, "udp"
==NULL) {
		clnt_pcreateerror("clnt_create error: ");
		thr_exit((void *) -1);
	}
	tout.tv_sec = 25;
	tout.tv_usec = 0;
	memset((char *) &result, 0, sizeof (result));
	memset((char *) &mult_result, 0, sizeof (mult_result));
	if (thr_self() % 2){
		EXP_RSLT = thr_self() + 1;
		add_arg = thr_self();
		test = clnt_call(clnt, SVC2_PROC_ADD, (xdrproc_t) xdr_int,
		(caddr_t) &add_arg, (xdrproc_t) xdr_int, (caddr_t) &result,
		tout);
	} else {
		pair.a = (u_short) thr_self();
		pair.b = (u_short) 1;
		EXP_RSLT = pair.a * pair.b;
		test = clnt_call(clnt, SVC2_PROC_MULT, (xdrproc_t)
		xdr_intpair,
		(caddr_t) &pair, (xdrproc_t) xdr_u_short,
		(caddr_t) &mult_result, tout);
		result = mult_result;
	}
	if (test != RPC_SUCCESS) {
		printf("THREAD: %d clnt_call hav
		thr_exit((void *) -1);
	};
	thr_exit((void *) 0);
}

Example 4-43 shows the server side in MT User mode. MT performance is enhanced if the function svc_getargs() is called by every procedure other than NULLPROC, even if there are no arguments (xdr_void may be used in this case). This is true for both the MT Auto and MT User modes. For more information on this call, see the rpc_svc_calls(3NSL) man page.


Note -

You must link in the thread library when writing RPC multithreaded-safe applications. The thread library must be the last named library on the link line. To do this, specify the -lthread option in the compile command.



Example 4-43 Server for MT User Mode

#define 	_REENTRANT
#include <stdio.h>
#include <rpc/rpc.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/uio.h>
#include <signal.h>
#include <thread.h>
#include "operations.h"

SVCXPRT *xprt;
void add_mult_prog();
void *svc2_add_worker();
void *svc2_mult_worker();
main(argc, argv)
	int argc;
	char **argv;
{
	int transpnum;
	char *nettype;
	int mode = RPC_SVC_MT_USER;
       if(rpc_control(RPC_SVC_MTMODE_SET,&mode) == FALSE){
                printf(" rpc_control is failed to set AUTO mode\n");
                exit(0);
        }
	if (argc > 2) {
		fprintf(stderr, "usage: %s [nettype]\n", argv[0]);
		exit(1);
	}
	if (argc == 2)
		nettype = argv[1];
	else
		nettype = "netpath";
	transpnum = svc_create(add_mult_prog, SVC2_PROG,
 	SVC2_VERS, nettype);
	if (transpnum == 0) {
		fprintf(stderr, "%s: cannot create %s service.\n", argv[0],
 		nettype);
		exit(1);
	}
	svc_run();
}
void add_mult_prog (rqstp, transp)
	struct svc_req *rqstp;
	SVCXPRT *transp;
{
	int argument;
	u_short mult_arg();
	intpair mult_argument;
	bool_t (*xdr_argument)();
	struct svc2_mult_args *sw_mult_data;
	struct svc2_add_args *sw_add_data;
	int ret;
	thread_t worker_id;
	switch (rqstp->rq_proc){
		case NULLPROC:
			svc_sendreply(transp, xdr_void, (char *) 0);
			svc_done(transp);
			break;
		case SVC2_PROC_ADD:
			xdr_argument = xdr_int;
			(void) memset((char *) &argument, 0, sizeof (argument));
			if (!svc_getargs(transp, xdr_argument,
			(char *) &argument)){
				printf("problem with getargs\n");
				svcerr_decode(transp);
				exit(1);
			}
			sw_add_data = (struct svc2_add_args *)
			malloc(sizeof (struct svc2_add_args));
			sw_add_data->transp = transp;
			sw_add_data->argument = argument;
			if (ret = thr_create(NULL, THR_MIN_STACK + 16 * 1024,
			svc2_add_worker, (void *) sw_add_data, THR_DETACHED,
				printf("SERVER: thr_create failed:");
				printf(" return value %d", ret);
				printf(" for add thread\n");
				exit(1);
			}
			break;
		case SVC2_PROC_MULT:
			xdr_argument = xdr_intpair;
			(void) memset((char *) &mult_argument, 0,
			sizeof (mult_argument));
			if (!svc_getargs(transp, xdr_argument,
			(char *) &mult_argument)){
				printf("problem with getargs\n");
				svcerr_decode(transp);
				exit(1);
			}
			sw_mult_data = (struct svc2_mult_args *)
			malloc(sizeof (struct svc2_mult_args));
			sw_mult_data->transp = transp;
			sw_mult_data->mult_argument.a = mult_argument.a;
			sw_mult_data->mult_argument.b = mult_argument.b;
			if (ret = thr_create(NULL, THR_MIN_STACK + 16 * 1024,
			svc2_mult_worker, (void *) sw_mult_data, THR_DETACHED,
			&worker_id)){
				printf("SERVER: thr_create failed:");
				printf("return value %d", ret);
				printf("for multiply thread\n");
				exit(1);
			break;
		default:
			svcerr_noproc(transp);
			svc_done(transp);
			break;
	}
}
u_short mult_arg();
int add_one();
void *svc2_add_worker(add_arg)
struct svc2_add_args *add_arg;
{	int *result;
	bool_t (*xdr_result)();
	xdr_result = xdr_int;
	result = *malloc(sizeof (int));
	*result = add_one(add_arg->argument);
	if (!svc_sendreply(add_arg->transp, xdr_result,
	(caddr_t) result)){
		printf("sendreply failed\n");
		svcerr_systemerr(add_arg->transp);
		svc_done(add_arg->transp);
		thr_exit((void *) -1);
	}
	svc_done(add_arg->transp);
	thr_exit((void *) 0);
}
void *svc2_mult_worker(m_arg)
struct svc2_mult_args *m_arg;
{
	u_short *result;
	bool_t (*xdr_result)();
	xdr_result = xdr_u_short;
	result = (u_short *) malloc(sizeof (u_short));
	*result = mult_arg(&m_arg->mult_argument);
	if (!svc_sendreply(m_arg->transp, xdr_result,
	(caddr_t) result)){
		printf("sendreply failed\n");
		svcerr_systemerr(m_arg->transp);
		svc_done(m_arg->transp);
		thr_exit((void *) -1);
	}
	svc_done(m_arg->transp);
	thr_exit((void *) 0);
}
u_short mult_arg(pair)
	intpair *pair;
{
	u_short result;
	result = pair->a * pair->b;
	return (result);}
int add_one(arg)
	int arg;
{
	return (++arg);
}