デフォルトでは、rpcgen で生成されるコードはマルチスレッド対応になりません。グローバル変数は保護されず、戻り値も静的変数で返されます。マルチスレッド環境で実行できるマルチスレッド対応コードを生成するには、-M フラグを指定します。-M フラグは、-N か -C のどちらか (または両方) のフラグと共に指定します。
この機能を使用したマルチスレッド対応プログラムの例を示します。例 3-14に rpcgen のプロトコルファイル 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-16 に示すマルチスレッド対応のクライアント側スタブプログラム (-M で生成) とマルチスレッド対応でないクライアント側スタブプログラムを比較してください。マルチスレッド未対応のクライアント側スタブプログラムは、静的変数を使用して戻り値を格納し、一度に 1 つしかスレッドを使用することができません。
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); } |
例 3-17 に、サーバー側コードを示します。
マルチスレッド対応モードを使用するサーバープログラムをコンパイルする場合は、スレッドライブラリをリンクしなければなりません。そのためには、コンパイルコマンドに -lthread オプションを指定します。
#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 に示します。
program ADDPROG { version ADDVER { int add(int, int) = 1; } = 1; }= 199; |
このプログラムでは、2 つの数値を加えてその結果をクライアントに返します。次のコマンドで、このファイルに対して rpcgen を実行します。
% rpcgen -N -M -C 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); } |
サーバー側の手続きは例 3-20 のようになります。
マルチスレッド対応モードを使用するサーバー側プログラムをコンパイルする場合は、スレッドライブラリにリンクしなければなりません。そのためには、コンパイルコマンドに -lthread オプションを指定します
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); } |