The asptest program is listed as a separate protocol in the graph.comp file (next page). The program performs a "ping-pong" test, bouncing ASP messages back and forth between client and server.
Since there is no separate include file, we start with a whole bunch of defines.
/* * $RCSfile: asptest.c,v $ * * x-kernel v3.3 * * Copyright (c) 1996,1993,1991,1990 Arizona Board of Regents */ #ifndef SUNOS #includeThe argPrefix macro returns TRUE if "str" is a proper prefix of "arg". The argEq macro returns TRUE if "str" is identical to "arg".#endif #include #include #include "xkernel.h" #include "asp.h" #include "ip.h" #include "x_stdio.h" #define argPrefix(str) ((!strncmp(arg, str, strlen(str))) && strlen(arg)>strlen(str)) #define argEq(str) (!strcmp(arg, str))
#define HOST_TYPE IPhost #ifndef STR2HOST #define STR2HOST str2ipHost #endif #define TRACE_VAR traceasptestpThis controls the built-in xkernel tracing mechanism.
The existing code does not set the value of TRACE_VAR, presumably because they expect us to do it. [It is set in graph.comp by adding, for example, trace=TR_MAJOR_EVENTS after a tab just before the semicolon on the line for the protocol that one wants to trace; see section 12.2 in the x-kernel Programmer's manual for more information.] I suggest setting to either TR_MAJOR_EVENTS (report every open, close, etc.) or TR_FULL_TRACE (every subroutine entry and exit).
#define PROT_STRING "asp" #define TIMES 5 #define DELAY 3 #define SPIN_DELAY 1 #define FAILURE_LIMIT 3The above four parameters control the "ping-pong" test below.
typedef struct { Sessn lls; Sessn controlSessn; Msg savedMsg; XkHandle clientPushResult; XkHandle serverPushResult; int done, idle; int clientReceived; XTime starttime, endtime; } PState;The "Pstate" structure represents the state of the asptest "protocol." We'll be alert to the usage of these state values later.
static HOST_TYPE ServerAddr = { 0, 0, 0, 0 }; static HOST_TYPE ClientAddr = { 0, 0, 0, 0 };ServerAddr and ClientAddr will be overwritten in asptest_init() below.
static HOST_TYPE myHost; static ASPport serverPort = 1789; static ASPport clientPort = 1865; static int serverParam, clientParam; static char *serverString; /* Sizes of messages sent, in bytes */ #define NUM_TESTS 11 static int size[] = { 1, 200, 400, 600, 800, 1024, 2048, 4096, 8192, 16384, 32768 }; static int trips = 100;I find it helpful when debugging to reduce NUM_TESTS to a smaller value such as 1 or 2.
The idea of the "ping-pong" test is to measure the time taken for 100 round trips using each of the message sizes in size[].
int TRACE_VAR; static void client(Event, void *); static void server(Event, void *); static XkReturn clientDemux(Protl, Sessn, Msg *); static int runTest(Protl, int, int); static XkReturn serverDemux(Protl, Sessn, Msg *); static void processOptions(void); static XkReturn closeDone(Sessn); static XkReturn customOpenDone(Protl, Protl, Sessn, Protl); int asptest_init(Protl); int asptest_init(Protl self)This is automatically built into the xkernel and called to initialize the asptest "protocol" object. (Note that there is no main function in this file or anywhere in the asp source code.)
This function starts another process to run the client or server version of asptest, depending on whether the "-c" or "-s" flag was entered on the command line.
{ Protl llp; PState *ps; processOptions();processOptions() reads the command line and gets
printf("%s timing test\n", PROT_STRING); llp = xGetProtlDown(self, 0); if (llp == ERR_PROTL) Kabort("Test protocol has no lower protocol"); xControlProtl(xGetProtlDown(self, 0), GETMYHOST, (char *)&myHost, sizeof(HOST_TYPE)); ps = X_NEW(PState); bzero((char *)ps, sizeof(PState)); self->state = (VOID *)ps; if (serverParam) evDetach(evSchedule(server, self, 0));If we are a server, we use the event mechanism to start a server process.
The function "server" will be called with argument "self" at time 0 microseconds in the future.
The evDetach() routine throws away the event handle generated by evSchedule, so there is no way to cancel the event.
else if (clientParam) { STR2HOST(&ServerAddr, serverString); ClientAddr = myHost; evDetach(evSchedule(client, self, 0)); }If we are a client, we use the event mechanism to start a client process.
return 0; } static void processOptions()This is called by asptest_init() to read the command line, which has been stuck into globalArgv[] by the xkernel.
{ int i; char *arg; for (i = 1; i < globalArgc; i++) { arg = globalArgv[i]; if (argEq("-s")) serverParam = 1; else if (argPrefix("-c")) { clientParam = 1; serverString = arg + 2; } else if (argEq("-c")) { clientParam = 1; serverString = globalArgv[i+1]; i++; } else if (argPrefix("-trips=")) sscanf(arg + strlen("-trips="), "%d", &trips); else if (argEq ("-trips")) { sscanf(globalArgv[i+1], "%d", &trips); i++; } } } /* * Server startup routine */ static void server(Event ev, VOID *foo)This is called by asptest_init() in case the -s flag appears on the command line. As called, "foo" is the asptest protocol object.
Notice that the first argument is the event handle which was generated by evSchedule() in asptest_init(). This argument is unused -- it is required by the event scheduling conventions.
The roles of the asptest client and server are as follows:
{ Part p; Protl myProtl = (Protl)foo; char buf[256]; time_t t = time(0); printf("%s", ctime(&t)); #ifdef SUNOS gethostname(buf, 255); #else sysinfo(SI_HOSTNAME, buf, 255); #endif printf("I am the server, running on %s\n\n", buf); /* Since we are a test protocol, need to fill out own method table. */ myProtl->demux = serverDemux; myProtl->opendone = customOpenDone; myProtl->closedone = closeDone; partInit(&p, 1); partPush(p, ANY_HOST, 0); partPush(p, &serverPort, sizeof(ASPport));Create a participant list p with one entry, which will be the address of the server ASP port.
The single entry p is itself a stack of addresses. Onto this stack we push:
if (xOpenEnable(myProtl, myProtl, xGetProtlDown(myProtl, 0), &p) == XK_FAILURE) { printf("%s test server can't openenable lower protocol\n", PROT_STRING); } else printf("%s test server done with xopenenable\n", PROT_STRING); }Now we call aspopenenable() with the participant list p. This completes the server startup routine -- the next thing that will happen is an incoming packet from the client, which will trigger the Demux routine.
/* * Client initialization routine */ static void client(Event ev, VOID *foo)This routine is called from asptest_init() if the "-c" flag was entered on the command line. The "ev" and "foo" parameters have the same meaning as in server() just above.
{ Protl myProtl = (Protl)foo; PState *ps; char buf[256]; time_t t = time(0); int i; Part p[2]; printf("%s", ctime(&t)); #ifdef SUNOS gethostname(buf, 255); #else sysinfo(SI_HOSTNAME, buf, 255); #endif printf("I am the client, running on %s\n", buf); printf("\nRunning ASP ping-pong test\n"); printf("Each message makes %d round trips\n\n", trips); ps = (PState *)myProtl->state; /* Since we are a test protocol, need to fill out own method table. */ myProtl->demux = clientDemux; partInit(p, 2); partPush(p[0], &ServerAddr, sizeof(IPhost)); partPush(p[0], &serverPort, sizeof(ASPport)); partPush(p[1], ANY_HOST, 0); partPush(p[1], &clientPort, sizeof(ASPport));The client participant list needs two addresses: its own and the server's. (Recall that the server participant list needs only the server adddress itself. The server doesn't care where the client is, because it is the job of the client to open the communication.)
Again, each participant address consists of an IP address (the "host address") together with an ASP port address which is pushed on top of it.
ps->lls = xOpen(myProtl, myProtl, xGetProtlDown(myProtl, 0), p); if (ps->lls == ERR_SESSN) { printf("%s test: open failed!\n", PROT_STRING); Kabort("End of test"); } customOpenDone(myProtl, xGetProtlDown(myProtl, 0), ps->lls, myProtl);The openDone() routine just prints out a trace message.
Finally, the client initiates the ping-pong tests below.
for (i = 0; i < NUM_TESTS; i++) runTest(myProtl, size[i], i); xClose(ps->lls); fprintf(stderr, "End of test\n"); } static #if defined(__GNUC__) inline #endif void checkHandle(h, str) XkHandle h; char *str;The checkHandle routine is used in runTest(), clientDemux(), and serverDemux() to abort the test in case of an error return. The parameter "h" is returned from a Push operation, attempting to send a message to the remote ASP.
{ switch (h) { case XMSG_ERR_HANDLE: case XMSG_ERR_WOULDBLOCK: sprintf(errBuf, "%s returns error handle %d", str, h); Kabort(errBuf); default: break; } } int runTest(self, len, testNumber) Protl self; int len; int testNumber;The runTest routine is called by client() to run one set of TIMES ping-pong tests. Each ping-pong test consists of firing one message of length "len" at the server. The clientDemux() and serverDemux() routines will bounce message back and forth until the trips limit "trips" is reached.
{ Msg msg; PState *ps = (PState *)self->state; XTime total; int test, failures; xAssert(ps);The xAssert() routine will abort the program if ps is NULL.
msgConstructAllocate(&ps->savedMsg, len);Create an empty test message of length "len" bytes. We do not bother to fill in the contents.
for (test = 0; test < TIMES; test++) { printf("Test %d, run %d ...\n", testNumber, test); printf("%d trips using msg length: %d\n", trips, len); ps->clientReceived = 0; ps->done = 0; failures = 0; xGetTime(&(ps->starttime)); msgConstructCopy(&msg, &ps->savedMsg); ps->clientPushResult = xPush(ps->lls, &msg); checkHandle(ps->clientPushResult, "client push"); msgDestroy(&msg); /* Wait until test is completed. */ do { ps->idle = 1; Delay(SPIN_DELAY * 1000); if (ps->idle) { failures++; if (failures >= FAILURE_LIMIT) break; } } while (!ps->done);The protocol state members ps->idle and ps->done are set by the clientDemux() routine below:
if (!ps->done) { printf("\nTest failed after %d trips... terminating\n\n", ps->clientReceived); continue; } xSubTime(&total, ps->endtime, ps->starttime); printf("\nTime for test %d, run %d: %6d.%06d\n\n", testNumber, test, total.sec, total.usec); Delay(DELAY * 1000); } msgDestroy(&ps->savedMsg); return 0; } XkReturn serverDemux(self, lls, dg) Protl self; Sessn lls; Msg *dg;The serverDemux function is called from xkernel when a message comes in. It simply echoes characters back to the client, which is responsible for counting them.
{ PState *ps = (PState *)self->state; static int c = 1; xIfTrace(asptestp, TR_MAJOR_EVENTS) { putchar('.'); if (!(c++ % 50)) putchar('\n'); } /* Echo packet back to client */ ps->serverPushResult = xPush(lls, dg); checkHandle(ps->serverPushResult, "server push"); return XK_SUCCESS; } static XkReturn clientDemux(self, lls, dg) Protl self; Sessn lls; Msg *dg;The clientDemux function is similar to serverDemux(), except
{ PState *ps = (PState *)self->state; Msg tmpMsg; static int c = 1; ps->clientReceived++; ps->idle = 0; xIfTrace(asptestp, TR_MAJOR_EVENTS) { putchar('.'); if (!(c++ % 50)) putchar('\n'); } if (ps->clientReceived < trips) { /* * We need to construct a copy of the original message rather than * just loop back the incoming message to avoid an increasingly * fragmented message structure in the case of loopback. */ msgConstructCopy(&tmpMsg, &ps->savedMsg); ps->clientPushResult = xPush(lls, &tmpMsg); checkHandle(ps->clientPushResult, "client push"); msgDestroy(&tmpMsg); } else { xGetTime(&ps->endtime); ps->done = 1; } return XK_SUCCESS; } static XkReturn closeDone(lls) Sessn lls;closeDone is automatically called when an ASP close occurs. Note that the server ASP won't be closed, as it has no idea when the last test message is sent.
{ xTrace2(asptestp, TR_MAJOR_EVENTS, "\n%s test -- closedone (%x) called", PROT_STRING, lls); if (serverParam) xClose(lls); return XK_SUCCESS; } static XkReturn customOpenDone(self, llp, s, hlpType) Protl self, llp, hlpType; Sessn s;The openDone() routine is automatically called from xkernel when a port has been passively opened, and then later an active open succeeds. Accordingly, this will only be called on the server side.
{ xTrace0(asptestp, TR_MAJOR_EVENTS, "asp test program openDone"); if (serverParam) xDuplicate(s); return XK_SUCCESS; }
This ends the commented walk-through of the ASP protocol.