ONC+ Developer's Guide

MT Client Overview

In a multithread client program, a thread can be created to issue each RPC request. When multiple threads share the same client handle, only one thread at a time will be able to make an RPC request. All other threads will wait until the outstanding request is complete. On the other hand, when multiple threads make RPC requests using different client handles, the requests are carried out concurrently. Figure 4-2 illustrates a possible timing of a multithreaded client implementation consisting of two client threads using different client handles.

Example 4-38 shows the client side implementation of a multithreaded rstat program. The client program creates a thread for each host. Each thread creates its own client handle and makes various RPC calls to the given host. Because the client threads are using different handles to make the RPC calls, they can carry out the RPC calls concurrently.

Figure 4-2 Two Client Threads Using Different Client Handles (Real time)

Graphic


Note -

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


Compile the program in Example 4-38 by typing:

$ cc rstat.c -lnsl -lthread


Example 4-38 Client for MT rstat

/* @(#)rstat.c 2.3 93/11/30 4.0 RPCSRC */
/*
 * Simple program that prints the status of a remote host,
 *  in a format similar to that used by the 'w' command.
 */

#include <thread.h>		/* thread interfaces defined */
#include <synch.h>	/* mutual exclusion locks defined */
#include <stdio.h>
#include <sys/param.h>
#include <rpc/rpc.h>
#include <rpcsvc/rstat.h>
#include <errno.h>

mutex_t tty;					/* control of tty for printf's */
cond_t cv_finish;
int count = 0;

main(argc, argv)
int argc;
char **argv;
{
	int i;
	thread_t tid;
	void *do_rstat();

	if (argc < 2) {
		fprintf(stderr, "usage: %s \"host\" [...]\n", argv[0]);
		exit(1);
	}

	mutex_lock(&tty);

	for (i = 1; i < argc; i++) {
		if (thr_create(NULL, 0, do_rstat, argv[i], 0, &tid) < 0) {
			fprintf(stderr, "thr_create failed: %d\n", i);
			exit(1);
		} else
			fprintf(stderr, "tid: %d\n", tid);
	}

	while (count < argc-1) {
		printf("argc = %d, count = %d\n", argc-1, count);
		cond_wait(&cv_finish, &tty);

	}

	exit(0);
}

bool_t rstatproc_stats();

void *
do_rstat(host)
char *host;
{
	CLIENT *rstat_clnt;
	statstime host_stat;
	bool_t rval;
	struct tm *tmp_time;
	struct tm host_time;
	struct tm host_uptime;
	char days_buf[16];
	char hours_buf[16];

	mutex_lock(&tty);
	printf("%s: starting\n", host);
	mutex_unlock(&tty);

	/* client handle to rstat */
	rstat_clnt = clnt_create(host, RSTATPROG, RSTATVERS_TIME,
							"udp");
	if (rstat_clnt == NULL) {
		mutex_lock(&tty);				/* get control of tty */
		clnt_pcreateerror(host);
		count++;
		cond_signal(&cv_finish);
		mutex_unlock(&tty);	/* release control of tty */

		thr_exit(0);

	}

	rval = rstatproc_stats(NULL, &host_stat, rstat_clnt);
	if (!rval) {
		mutex_lock(&tty);	/* get control of tty */
		clnt_perror(rstat_clnt, host);
		count++;
		cond_signal(&cv_finish);
		mutex_unlock(&tty);	/* release control of tty */

		thr_exit(0);

	}

	tmp_time = localtime_r(&host_stat.curtime.tv_sec,
                       &host_time);

	host_stat.curtime.tv_sec = host_stat.boottime.tv_sec;

	tmp_time = gmtime_r(&host_stat.curtime.tv_sec,
                    &host_uptime);

	if (host_uptime.tm_yday != 0)
		sprintf(days_buf, "%d day%s, ", host_uptime.tm_yday,
		         (host_uptime.tm_yday > 1) ? "s" : "");
	else
		days_buf[0] = '\0';

	if (host_uptime.tm_hour != 0)
		sprintf(hours_buf, "%2d:%02d,",
			host_uptime.tm_hour, host_uptime.tm_min);

	else if (host_uptime.tm_min != 0)
		sprintf(hours_buf, "%2d mins,", host_uptime.tm_min);
	else

		hours_buf[0] = '\0';

	mutex_lock(&tty);	/* get control of tty */
	printf("%s: ", host);
	printf(" %2d:%02d%cm up %s%s load average: %.2f %.2f %.2f\n",
		(host_time.tm_hour > 12)  ? host_time.tm_hour - 12

		: host_time.tm_hour,
		host_time.tm_min,
		(host_time.tm_hour >= 12) ? 'p'
		: 'a',
		days_buf,
		hours_buf,
		(double)host_stat.avenrun[0]/FSCALE,
		(double)host_stat.avenrun[1]/FSCALE,
		(double)host_stat.avenrun[2]/FSCALE);
	count++;
	cond_signal(&cv_finish);
	mutex_unlock(&tty);	/* release control of tty */
	clnt_destroy(rstat_clnt);

	sleep(10);
	thr_exit(0);
}

/* Client side implementation of MT rstat program */
/* Default timeout can be changed using clnt_control() */
static struct timeval TIMEOUT = { 25, 0 };

bool_t
rstatproc_stats(argp, clnt_resp, clnt)
	void *argp;
	statstime *clnt_resp;
	CLIENT *clnt;
{

	memset((char *)clnt_resp, 0, sizeof (statstime));
	if (clnt_call(clnt, RSTATPROC_STATS,
		(xdrproc_t) xdr_void, (caddr_t) argp,
		(xdrproc_t) xdr_statstime, (caddr_t) clnt_resp,
		TIMEOUT) != RPC_SUCCESS) {
		return (FALSE);

	}
	return (TRUE);
}