ONC+ 開発ガイド

マルチスレッド対応コードのコンパイル

デフォルトでは、rpcgen で生成されるコードはマルチスレッド対応になりません。グローバル変数は保護されず、戻り値も静的変数で返されます。マルチスレッド環境で実行できるマルチスレッド対応コードを生成するには、-M フラグを指定します。-M フラグは、-N-C のどちらか (または両方) のフラグと共に指定します。

この機能を使用したマルチスレッド対応プログラムの例を示します。次に、 rpcgen のプロトコルファイル msg.x を示します。


例 3–14 マルチスレッド対応プログラム : msg.x

program MESSAGEPROG {
version PRINTMESSAGE {
        int PRINTMESSAGE(string) = 1;
        } = 1;
} = 0x4001;

文字列はリモートプロシージャに引き渡されます。リモートプロシージャは、文字列を表示してクライアントの文字列長を返します。マルチスレッド対応のスタブを作成するには、rpcgen -M msg.x を実行します。

次に、このプロトコルファイルを使用したクライアント側プログラムの例を示します。


例 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);
}

プログラムを再入可能にするために、rpcgen が生成したコードには、引数も戻り値もポインタで渡す必要があります。スタブ関数の戻り値は、リモートプロシージャの呼び出しが正常終了したかエラーが起こったかを示します。正常終了した場合は、RPC_SUCCESS が返されます。例 3–15 に示すマルチスレッド対応のクライアント側スタブプログラム (-M で生成) と例 3–16 に示すマルチスレッド対応でないクライアント側スタブプログラムを比較してください。マルチスレッド未対応のクライアント側スタブプログラムは、静的変数を使用して戻り値を格納し、一度に 1 つしかスレッドを使用することができません。


例 3–16 クライアント側スタブプログラム (マルチスレッドに対応していない場合)

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);
}
 

次に、サーバー側コードを示します。


注 –

マルチスレッド対応モードを使用するサーバー側プログラムをコンパイルする場合は、スレッドライブラリにリンクしなければなりません。そのためには、コンパイルコマンドに -lthread オプションを指定します。



例 3–17 マルチスレッド対応サーバー側スタブプログラム

#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;
{
	/*
	 * 必要に応じてメモリー解放のためのコードを挿入
	 */
	(void) xdr_free(xdr_result, result);
}

サーバー側のコードでは、静的変数を使用して戻り値を格納しないでください。呼び出し側のルーチンから戻り値へのポインタが渡されますので、戻り値はそこに返します。正常終了の場合は 1 を返し、エラーが起こった場合は 0 を返します。

rpcgen が生成するコードには、手続きの呼び出しで割り当てたメモリーを解放するルーチンの呼び出しも含まれています。メモリーの不正使用を避けるため、サービスルーチンで割り当てたメモリーはすべてそのルーチンで解放する必要があります。上の例では、messageprog_1_freeresult() でメモリーの解放を行います。

通常は、xdr_free() を使用して割り当てたメモリーを解放します。上の例では、メモリー割り当てを行なっていないので、メモリーの解放は実行されません。

次に、- M フラグを-N と-C のフラグと共に指定する例として、add.x を示します。


例 3–18 マルチスレッド対応のプログラム : add.x

program ADDPROG {
 version ADDVER {	
 int add(int, int) = 1;
	 } = 1;
}= 199;

このプログラムでは、2 つの数値を加えてその結果をクライアントに返します。このファイルに対して rpcgen を実行するには、 rpcgen -N -M -C add.x と入力します。 次に、マルチスレッド対応クライアントプログラムの例を示します。


例 3–19 マルチスレッド対応クライアント側プログラム : add.x

/*
 * このクライアント側メインルーチンでは複数のスレッドを起動します。
 * 各スレッドから同時にサーバールーチンを呼び出します。
 */
 
#include "add.h"
 
CLIENT *clnt;
#define NUMCLIENTS 5
struct argrec {
	int arg1;
	int arg2;
};
 
/* 
 * 現在実行中のスレッド数をカウント
 */
int numrunning;
mutex_t numrun_lock;
cond_t condnum;
 
void
addprog(struct argrec *args)
{
	enum clnt_stat retval;
	int result;
	/* サーバールーチンの呼び出し */
	retval = add_1(args->arg1, args->arg2,
											&result, clnt);
	if (retval != RPC_SUCCESS) {
		clnt_perror(clnt, "call failed");
	} else
		printf("thread #%x call succeeded,
					result = %d\n", thr_getself(),
					result);
/*
 * 実行中のスレッド数をデクリメント
 */
	mutex_lock(&numrun_lock);
	numrunning--;
	cond_signal(&condnum);
	mutex_unlock(&numrun_lock);
	thr_exit(NULL);
}
 
main(int argc, char *argv[])
{
	char *host;
	struct argrec args[NUMCLIENTS];
	int i;
	thread_t mt;
	int ret;
 
	if (argc < 2) {
		printf("usage:  %s server_host\n",
					argv[0]);
		exit(1);
	}
	host = argv[1];
	clnt = clnt_create(host, ADDPROG, ADDVER,
									"netpath");
	if (clnt == (CLIENT *) NULL) {
		clnt_pcreateerror(host);
		exit(1);
	};
	mutex_init(&numrun_lock, USYNC_THREAD, NULL);
	cond_init(&condnum, USYNC_THREAD, NULL);
	numrunning = 0;
 
	/* 個々のスレッドの起動 */
	for (i = 0; i < NUMCLIENTS; i++) {
		args[i].arg1 = i;
		args[i].arg2 = i + 1;
		ret = thr_create(NULL, NULL, addprog,
									(char *) &args[i],
				 					THR_NEW_LWP, &mt);
		if (ret == 0)
			numrunning++;
	}
 
	mutex_lock(&numrun_lock);
	/* 全スレッドの終了を待つ */
	while (numrunning != 0)
		cond_wait(&condnum, &numrun_lock);
	mutex_unlock(&numrun_lock);
	clnt_destroy(clnt);
}
 

次に、サーバー側の手続きを示します。


注 –

マルチスレッド対応モードを使用するサーバー側プログラムをコンパイルする場合は、スレッドライブラリにリンクしなければなりません。そのためには、コンパイルコマンドに -lthread オプションを指定します。



例 3–20 マルチスレッド対応サーバー側プログラム : add.x

add_1_svc(int arg1, int arg2, 
						int *result, struct svc_req *rqstp)
{
	bool_t retval;
	/* 結果の計算 */
	*result = arg1 + arg2;
	retval = 1;
	return (retval);
}
 
/*
 * サーバー手続きで割り当てたメモリーを解放するルーチン
 */
int
addprog_1_freeresult(SVCXPRT *transp,
									xdrproc_t xdr_result,
									caddr_t result)
 
{
	(void) xdr_free(xdr_result, result);
}