このマニュアルには、Solaris でのマルチスレッドプログラミングについては説明していません。次の項目については、『マルチスレッドのプログラミング』を参照してください。
スレッドの作成
スケジューリング
同期
シグナル
プロセスリソース
軽量プロセス (lwp)
並列性
データロックの技術
TI-RPC は、Solaris 2.4 以降のマルチスレッド RPC サーバをサポートします。マルチスレッドサーバと シングルスレッドのサーバの違いは、マルチスレッドサーバがスレッドの技術を使用して複数のクライアント要求を同時に処理することです。マルチスレッドサーバの方が、高度なパフォーマンスと可用性を備えています。
このリリースで新規に使用可能なインタフェースについては、まず、「マルチスレッドサーバの概要」から読んでください。
マルチスレッド対応のクライアントプログラムでは、RPC 要求が出されるたびにスレッドを 1 つ作成することができます。複数スレッドが同一のクライアントハンドルを共有する場合は、RPC要求発行できるのは一度に 1 つのスレッドだけです。その他のすべてのスレッドは、未処理の要求が終了するまで待たなければなりません。これに対して、複数スレッドがそれぞれ固有のクライアントハンドルを使用して RPC 要求を出す場合には、複数の要求が同時に処理されます。図 4-1は、異なるクライアントハンドルを使用するクライアント側の 2 つのスレッドから成るマルチスレッッド対応クライアント環境でのタイミングの例を示したものです。
例 4-33 は、クライアント側でマルチスレッド rstat プログラムを実行する場合を示します。クライアントプログラムは各ホストに対してスレッドを作成します。スレッドはそれぞれ、固有のクライアントハンドルを作成し、指定のホストにさまざまなRPC 呼び出しを行なっています。クライアント側の各スレッドは異なるハンドルを使用して RPC 呼び出しを行うため、RPC 呼び出しは同時に実行されます。
RPC マルチスレッド対応アプリケーションを作成する場合は常に、スレッドライブラリをリンクしなければなりません。コンパイルコマンドで -lthread を指定して、スレッドライブラリを最後にリンクするようにしなければなりません。
次のように入力して 例 4-33のプログラムを作成します。
$ cc rstat.c -lnsl -lthread
/* @(#)rstat.c 2.3 93/11/30 4.0 RPCSRC */ /* * w コマンドと同様の形式で、遠隔ホストのステータスを表示する簡単な * プログラム */ #include <thread.h> /* スレッドインターフェースの定義 */ #include <synch.h> /* 相互排他的ロックの定義 */ #include <stdio.h> #include <sys/param.h> #include <rpc/rpc.h> #include <rpcsvc/rstat.h> #include <errno.h> mutex_t tty; /* printf のための tty の制御 */ 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); /* rstat クライアントハンドル */ rstat_clnt = clnt_create(host, RSTATPROG, RSTATVERS_TIME, "udp"); if (rstat_clnt == NULL) { mutex_lock(&tty); /* ty の制御権を取得 */ clnt_pcreateerror(host); count++; cond_signal(&cv_finish); mutex_unlock(&tty); /* tty の制御権を解放 */ thr_exit(0); } rval = rstatproc_stats(NULL, &host_stat, rstat_clnt); if (!rval) { mutex_lock(&tty); /* tty の制御権を取得 */ clnt_perror(rstat_clnt, host); count++; cond_signal(&cv_finish); mutex_unlock(&tty); /* 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); /* 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); /* tty の制御権を解放 */ clnt_destroy(rstat_clnt); sleep(10); thr_exit(0); } /* クライアント側の MT rstat プログラムの実行 */ /* 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); }
Solaris 2.4 より前のバージョンでは、RPC サーバはシングルスレッドでした。つまり、クライアント側から要求が来るごとに処理していました。たとえば、2 つの要求を同時に受け取り、最初の処理に 30 秒、次の処理に 1 秒かかるとすると、2 つめの要求を出したクライアントは最初の処理が完了するまで待たなければなりません。これは、各 CPU が異なる要求を同時に処理するマルチプロセッササーバ環境を利用できず、他の要求がサーバによって処理することができるのに 1 つの要求の I/O の完了を待っている状態が生じ、望ましいものではありません。
Solaris 2.4 以降 の RPC ライブラリでは、サービス開発者がエンドユーザにより良いパフォーマンスを提供するマルチスレッドサーバを作成できる機能を追加しました。サーバのマルチスレッドの 2 つのモード、自動マルチスレッドモードとユーザ・マルチスレッド・モードは、TI-RPC でサポートされます。
自動モードでは、サーバは、クライアント要求を受信するごとに新規スレッドを自動的に作成します。このスレッドは要求を処理し、応答してから終了します。ユーザモードでは、サービス開発者が、入って来るクライアント要求を同時に処理するスレッドを作成、管理します。自動モードはユーザモードより使用はしやすいのですが、ユーザモードの方が特別な要件を必要とするサービス開発者に対して柔軟性があります。
RPC マルチスレッド対応アプリケーションを作成する場合は常に、スレッドライブラリをリンクしなければなりません。コンパイルコマンドで -lthread を指定して、スレッドライブラリを最後にリンクするようにしなければなりません。
サーバ側のマルチスレッドをサポートする呼び出しでは、rpc_control() と svc_done() がサポートされています。これらの呼び出しによってサーバ側でマルチスレッド処理が行なえるようになりました。rpc_control() 呼び出しがマルチスレッドモードを設定するために、自動モードとユーザモードの両方で使用されます。サーバが自動モードを使用する場合には、svc_done() を呼び出す必要はありません。ユーザモードの場合には、サーバが要求処理からのリソースを再要求できるようにするため、svc_done() は各クライアント要求が処理されてから呼び出されなければなりません。さらにマルチスレッド RPC サーバは、svc_run() をマルチスレッド対応で呼び出さなければなりません。svc_getreqpoll() と svc_getreqset() は、MT アプリケーションでは安全ではありません。
サーバプログラムが新規インタフェース呼び出しを行わない場合には、デフォルトのモードのシングルスレッドモードのままです。
サーバが使用しているモードに関係なく、RPC サーバ手続きはマルチスレッド対応にしなければなりません。通常これは、すべての静的変数とグロール変数が mutex ロックで保護される必要がある、ということです。相互排他と他の同期 API は、synch.h で定義されます。さまざまな同期インタフェースのリストは、condition(3T)、rwlock(3T)、mutex(3T) を参照してください。
図 4-2 は、マルチスレッドモードのどちらかで実行されるサーバの実行タイミングを示します。
サービス・トランスポート・ハンドル、SVCXPRT には、引き数を復号化するための領域と結果をコード化するための領域である 1 つのデータ領域があります。したがって、デフォルトでは、シングルスレッドモードであり、この構造は、これらの操作を行う関数を呼び出すスレッド間では自由に共有することはできません。ただし、サーバが、マルチスレッド自動モードまたはユーザモードにある場合には、この構造のコピーは、同時要求処理を可能にするために、サービスディスパッチ用のプログラムに引き渡されます。これらの状況では、ルーチンのマルチスレッド対応ではない一部のルーチンがマルチスレッド対応となります。特別に注意書きがない場合には、サーバインタフェースは通常、マルチスレッド対応です。サーバ側のインタフェースについての詳細は、rpc_svc_calls(3N) マニュアルページを参照してください。
マルチスレッド自動モードでは、RPC ライブラリはスレッドを作成し、管理することができます。サービス開発者が新規インタフェース呼び出し、rpc_control() を呼び出し、svc_run() を呼び出す前にサーバをマルチスレッド自動モードにします。このモードでは、プログラマはサービスプロシージャがマルチスレッド対応であることを確認するだけで十分です。
rpc_control() の使用によって、アプリケーションでグローバル RPC 属性を設定できます。現在はサービス側の操作しかサポートしていません。 表 4-8 は、自動モード用に定義された rpc_control() 操作を示します。追加の情報については、rpc control(3N) マニュアルページを参照してください。
表 4-8 rpc_control() ライブラリルーチン
マルチスレッドモードの設定 |
|
マルチスレッドの取得 |
|
最大スレッド数の設定 |
|
最大スレッド数の取得 |
|
現在アクティブなスレッドの合計数 |
|
RPC ライブラリ作成のスレッドの累積数 |
|
RPC ライブラリ内の thr_create エラー数 |
表 4-8 の get 演算は、RPC_SVC_MTMODE_GET() 以外はすべて、自動マルチスレッドモードにだけ適用されます。マルチスレッド・ユーザ・モードまたはデフォルトのシングル・スレッド・モードで使用する場合には、演算の結果は定義されません。
デフォルトでは、RPC ライブラリが一度に作成できるスレッドの最大数は 16 です。サーバが 16 以上のクライアント要求を同時に処理する必要がある場合には、スレッドの最大数を指定して設定する必要があります。このパラメータは、サーバによっていつでも設定することができ、これによってサーバ開発者はサーバによって使用されるスレッドリソースの上限を設定できます。例 4-34 は、マルチスレッド自動モードに作成された RPC プログラムの例です。この例では、スレッドの最大数は 20 に設定されています。
マルチスレッドのパフォーマンスは、関数 svc_getargs() が、NULLPROCS 以外のプロシージャによって呼び出されるごとに、引き数 (この場合には xdr_void())がない場合でも改善されていきます。これはマルチスレッド自動モードとマルチスレッドユーザモード両方の場合においでてす。詳細は、rpc_svc_calls(3N) マニュアルページを参照してください。
例 4-34 は、マルチスレッド自動モードでのサーバを示したものです。
RPC マルチスレッド対応アプリケーションを作成する場合は常に、スレッドライブラリ内でリンクしなければなりません。コンパイルコマンドで -lthread を指定して、スレッドライブラリを最後にリンクするようにしなければなりません。
次のように入力して例 4-34 のプログラムを作成します。
$ cc time_svc.c -lnsl -lthread
#include <stdio.h> #include <rpc/rpc.h> #include <synch.h> #include <thread.h> #include "time_prot.h" void time_prog(); main(argc, argv) int argc; char *argv[]; { int transpnum; char *nettype; int mode = RPC_SVC_MT_AUTO; int max = 20; /* スレッド最大数を 20 に設定 */ if (argc > 2) { fprintf(stderr, "usage: %s [nettype]¥n", argv[0]); exit(1); } if (argc == 2) nettype = argv[1]; else nettype = "netpath"; if (!rpc_control(RPC_SVC_MTMODE_SET, &mode)) { printf("RPC_SVC_MTMODE_SET: failed¥n"); exit(1); } if (!rpc_control(RPC_SVC_THRMAX_SET, &max)) { printf("RPC_SVC_THRMAX_SET: failed¥n"); exit(1); } transpnum = svc_create( time_prog, TIME_PROG, TIME_VERS, nettype); if (transpnum == 0) { fprintf(stderr, "%s: cannot create %s service.¥n", argv[0], nettype); exit(1); } svc_run(); } /* * サーバのディスパッチプログラムです。RPC サーバライブラリは、 * サーバのディスパッチャルーチン time_prog () を実行するスレッドを * 作成します。RPC ライブラリがスレッドを廃棄した後に行われます。 */ static void time_prog(rqstp, transp) struct svc_req *rqstp; SVCXPRT *transp; { switch (rqstp->rq_proc) { case NULLPROC: svc_sendreply(transp, xdr_void, NULL); return; case TIME_GET: dotime(transp); break; default: svcerr_noproc(transp); return; } } dotime(transp) SVCXPRT *transp; { struct timev rslt; time_t thetime; thetime = time((time_t *)0); rslt.second = thetime % 60; thetime /= 60; rslt.minute = thetime % 60; thetime /= 60; rslt.hour = thetime % 24; if (!svc_sendreply(transp, xdr_timev,(caddr_t) &rslt)) { svcerr_systemerr(transp); } }
例 4-35 は、サーバに対するtime_prot.h ヘッダファイルを示します。
include <rpc/types.h> struct timev { int second; int minute; int hour; }; typedef struct timev timev; bool_t xdr_timev(); #define TIME_PROG ((u_long)0x40000001) #define TIME_VERS ((u_long) 1) #define TIME_GET ((u_long) 1)
マルチスレッド・ユーザ・モードでは、RPC ライブラリはスレッドを作成しません。このモードは、基本的には、シングルスレッド、またはデフォルトのモードのように作動します。唯一の違いは、データ構造のコピー (サービス・ディスパッチ・ルーチンへのトランスポートサービスなど) をマルチスレッド対応に引き渡す点です。
RPC サーバ開発者は、スレッドライブラリ全体のスレッドの作成と管理に対する責任を持ちます。ディスパッチルーチンでは、サービス開発者は、プロシージャの実行を新規作成のまたは既存のスレッドに割り当てることができます。thr_create() API は、さまざまな属性を持つスレッドを作成するために使用されます。すべてのスレッドのライブラリインタフェースは、thread.h で定義されます。詳細は、pthread_create(3T) マニュアルページを参照してください。
このモードは、サーバ開発者に幅広い柔軟性を提供しています。スレッドは、サービス要件に応じたスタックサイズを持ちます。スレッドは限定されます。異なるプロシージャは、異なる特長を持つスレッドによって実行されます。サービス開発者は、サービスの一部をシングルスレッドで実行できます。また、特別なスレッド特定シングルプロセスを行うこともできます。
自動モードの場合と同じように、rpc_control() ライブラリは、ユーザモードに切り換える場合に使用されます。表 4-8 に示した rpc_control() 演算 (RPC_SVC_MTMODE_GET() 以外) は、マルチスレッド自動モードにだけ適用されます。マルチスレッド・ユーザ・モードまたはシングルスレッドのデフォルトモードで使用すると、演算の結果が定義できません。
マルチスレッド・ユーザ・モードでは、サービスプロシージャは、戻しの前に svc_done() を呼び出さなければなりません。svc_done() は、クライアント要求が指定のサービス・トランスポート・ハンドルに向けたサービスに割り当てたリソースを解放しなければなりません。この機能は、クライアント要求がサービスされた後、あるいは応答の送信を妨げたエラーまたは異常な状態の後に呼び出されます。svc_done() が呼び出された後に、サービス・トランスポート・ハンドルは、サービスプロシージャによって参照されるべきではありません。例 4-36は、マルチスレッド・ユーザ・モードでのサーバを示します。
svc_done() は、マルチスレッド・ユーザ・モード内でだけ呼び出すことができます。詳細は、rpc_svc_calls(3N) マニュアルページを参照してください。
#define SVC2_PROG 0x30000002 #define SVC2_VERS ((u_long) 1) #define SVC2_PROC_ADD ((u_long) 1) #define SVC2_PROC_MULT ((u_long) 2) struct intpair { u_short a; u_short b; }; typedef struct intpair intpair; struct svc2_add_args { long argument; SVCXPRT *transp; }; struct svc2_mult_args { intpair mult_argument; SVCXPRT *transp; }; extern bool_t xdr_intpair(); #define NTHREADS_CONST 500
例 4-37 は、マルチスレッド・ユーザ・モードに対するクライアントです。
#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; long result = 0; u_short mult_result = 0; long add_arg; long 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_long, (caddr_t) &add_arg, (xdrproc_t) xdr_long, (caddr_t) &result, tout); } else { pair.a = (u_short) thr_self(); pair.b = (u_short) 1; EXP_RSLT = (long) 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 = (long) mult_result; } if (test != RPC_SUCCESS) { printf("THREAD: %d clnt_call hav thr_exit((void *) -1); }; thr_exit((void *) 0); }
例 4-38 は、マルチスレッド・ユーザ・モードのサーバ側を示します。マルチスレッドパフォーマンスは、関数 svc_getargs() が NULLPROC 以外の各プロシージャに呼び出される場合は、引き数 (この場合には xdr_void) がなくても改善されます。これは、マルチスレッド自動モードモードとマルチスレッドユーサモードにおいてです。詳細は、rpc_svc_calls(3N) マニュアルページを参照してください。
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; { long 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 ((long) rqstp->rq_proc){ case NULLPROC: svc_sendreply(transp, xdr_void, (char *) 0); svc_done(transp); break; case SVC2_PROC_ADD: xdr_argument = xdr_long; (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(); long add_one(); void *svc2_add_worker(add_arg) struct svc2_add_args *add_arg; { long *result; bool_t (*xdr_result)(); xdr_result = xdr_long; result = (long *) malloc(sizeof (long)); *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);} long add_one(arg) long arg; { return (++arg); }