マルチスレッド・ユーザー・モードでは、RPC ライブラリはスレッドを作成しません。このモードは、基本的には、シングルスレッド、またはデフォルトのモードのように作動します。唯一の違いは、データ構造のコピー (サービス・ディスパッチ・ルーチンへのトランスポートサービスなど) をマルチスレッド対応に引き渡す点です。
RPC サーバー開発者は、スレッドライブラリ全体のスレッドの作成と管理に対する責任を持ちます。ディスパッチルーチンでは、サービス開発者は、手続きの実行を新規作成のまたは既存のスレッドに割り当てることができます。thr_create() API は、さまざまな属性を持つスレッドを作成するために使用されます。すべてのスレッドのライブラリインタフェースは、thread.h で定義されます。詳細は、pthread_create(3THR) のマニュアルページを参照してください。
このモードは、サーバー開発者に幅広い柔軟性を提供しています。スレッドは、サービス要件に応じたスタックサイズを持ちます。スレッドは限定されます。異なる手続きは、異なる特長を持つスレッドによって実行されます。サービス開発者は、サービスの一部をシングルスレッドで実行できます。また、特定のスレッドに固有のシグナル処理を行うこともできます。
自動モードの場合と同じように、rpc_control() ライブラリは、ユーザーモードに切り換える場合に使用されます。表 4-10 に示した rpc_control() 演算 (RPC_SVC_MTMODE_GET() 以外) は、マルチスレッド自動モードにだけ適用されます。マルチスレッド・ユーザー・モードまたはシングルスレッドのデフォルトモードで使用すると、演算の結果が定義できません。
マルチスレッド・ユーザー・モードでは、サービス手続きは、戻しの前に svc_done() を呼び出さなければなりません。svc_done() は、クライアント要求が指定のサービス・トランスポート・ハンドルに向けたサービスに割り当てたリソースを解放しなければなりません。この機能は、クライアント要求がサービスされた後、あるいは応答の送信を妨げたエラーまたは異常な状態の後に呼び出されます。svc_done() が呼び出された後に、サービス・トランスポート・ハンドルは、サービス手続きによって参照されるべきではありません。例 4-41 は、マルチスレッド・ユーザー・モードでのサーバーを示します。
svc_done() は、マルチスレッド・ユーザー・モード内でだけ呼び出すことができます。詳細は、rpc_svc_calls(3NSL) のマニュアルページを参照してください。
#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 |
例 4-42 は、マルチスレッド・ユーザー・モードでのクライアントです。
#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); } |
例 4-43 は、マルチスレッド・ユーザー・モードのサーバー側を示します。マルチスレッドパフォーマンスは、関数 svc_getargs() が NULLPROC 以外の各手続きに呼び出される場合は、引き数 (この場合には xdr_void) がなくても改善されます。これは、マルチスレッド自動モードモードとマルチスレッドユーザーモードにおいてです。詳細は、rpc_svc_calls(3NSL) のマニュアルページを参照してください。
RPC マルチスレッド対応アプリケーションを作成する場合は常に、スレッドライブラリ内でリンクしなければなりません。コンパイルコマンドで -lthread を指定して、スレッドライブラリを最後にリンクするようにしなければなりません。
#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); } |