1
0
Files
irix-657m-src/irix/cmd/tk/test2.c
2022-09-29 17:59:04 +03:00

1066 lines
25 KiB
C

#include "assert.h"
#include "tkm.h"
#include "unistd.h"
#include "memory.h"
#include "stdlib.h"
#include "stdio.h"
#include "strings.h"
#include "getopt.h"
#include "ulocks.h"
#include "mutex.h"
#include "time.h"
#include "signal.h"
#include "sys/types.h"
#include "sys/prctl.h"
#include "mbox.h"
static void sig(int);
static void node(int, int);
static int srpc(struct mbox *, tks_ch_t, void *, int, void *, int);
#define MAXTOKENS 10
#define MAXSERVICES 10
#define SERVICE1_ID 0
#define SERVICE2_ID 1
/* are we the service1 server? */
#define S1S() (services[SERVICE1_ID].server_node)
#define AM_S1S(n) (n == services[SERVICE1_ID].server_node)
#define S2S() (services[SERVICE2_ID].server_node)
#define AM_S2S(n) (n == services[SERVICE2_ID].server_node)
/*
* protocol
*/
struct scheme {
tks_ch_t handle; /* client handle */
struct mbox *return_addr; /* return address for RPC */
};
/*
* htonode - convert a handle to a node number
*/
#define htonode(h) (h)
#define nodetoh(n) (n)
#define TEST2_OBTAIN 1 /* client -> server RPC */
#define TEST2_REVOKE 2 /* server -> client 1-way */
#define TEST2_RETURN 3 /* client -> server 1-way */
#define TEST2_RECALL 4 /* server -> client 1-way */
#define TEST2_RETURN_RECALL 5 /* client -> server 1-way */
#define TEST2_GEN 6 /* client -> server RPC */
struct test2_req {
struct scheme addr; /* MUST be first!! */
int op;
int service_id;
union {
/* obtain request message */
struct obtain_req {
tk_set_t obtain;
tk_set_t toreturn;
int data[MAXTOKENS];
} obtain_req;
struct revoke_req {
tk_set_t torevoke;
} revoke_req;
struct recall_req {
tk_set_t torecall;
int flag;
} recall_req;
struct return_req {
tk_set_t toreturn;
int data[MAXTOKENS];
tk_set_t refuse;/* for TEST2_RETURN_RECALL only */
tk_set_t eh;/* for TEST2_RETURN_RECALL only */
} return_req;
struct gen_req {
int gen;
} gen_req;
} req_un;
};
struct test2_res {
struct scheme addr; /* MUST be first!! */
int op;
union {
/* obtain response message */
struct obtain_res {
tk_set_t granted;
tk_set_t already;
int data[MAXTOKENS];
} obtain_res;
struct gen_res {
tk_set_t existance;
} gen_res;
} res_un;
};
/*
* XXX currently all services are really pretty much the same - they
* share the same basic algorithms..
*/
struct service_info {
int server_node; /* node that will act as server */
int service_id;
int cmpl; /* client multi-programming level */
int nclasses; /* # classes in token
* we use nclasses+1 as the existance token
*/
void (*wait)(struct test2_req *, int);
void (*start)(void *);
void **nsdata; /* private data */
tk_set_t alltk; /* set of all tokens */
} services[MAXSERVICES];
#define S1_P(n) (services[SERVICE1_ID].nsdata[n])/* get private data for node */
#define S1_NCLASS() (services[SERVICE1_ID].nclasses) /* get nclasses */
#define S1_ALLTK() (services[SERVICE1_ID].alltk)
int nloops = 10;
pid_t ppid;
unsigned long ndoneclients;
int verbose;
/*
* the following arena is used for all locks and semaphores for all nodes
* and all services ...
*/
usptr_t *usptr;
char *Cmd;
int nodes;
int dumplog = 0;
static void service1_wait(struct test2_req *, int);
static void service1_start(void *);
int
main(int argc, char **argv)
{
int c, i;
int cmpl, nservices;
int nclasses;
unsigned long totclients;
int nwaiters;
setlinebuf(stdout);
setlinebuf(stderr);
Cmd = strdup(argv[0]);
ppid = getpid();
prctl(PR_COREPID, 0, 1);
cmpl = 1;
nodes = 0;
nclasses = 1;
while ((c = getopt(argc, argv, "TN:vLm:n:t:")) != EOF)
switch (c) {
case 'T':
__tk_tracenow = 1;
break;
case 'v':
verbose = 1;
break;
case 't':
nclasses = atoi(optarg);
break;
case 'L':
dumplog++;
break;
case 'm':
cmpl = atoi(optarg);
break;
case 'N':
nodes = atoi(optarg);
break;
case 'n':
nloops = atoi(optarg);
break;
default:
fprintf(stderr, "illegal option %c\n", c);
exit(1);
}
/*
* setup services
*/
for (i = 0; i < MAXSERVICES; i++)
services[i].service_id = -1;
nservices = 1;
services[0].server_node = 0;
services[0].service_id = SERVICE1_ID;
services[0].cmpl = cmpl;
services[0].nclasses = nclasses;
services[0].wait = service1_wait;
services[0].start = service1_start;
services[0].nsdata = (void **)malloc(nodes * sizeof(void *));
services[0].alltk = TK_NULLSET;
/* alltk includes existance token */
for (i = 0; i < S1_NCLASS()+1; i++)
TK_COMBINE_SET(services[0].alltk, TK_MAKE(i, TK_READ|TK_WRITE|TK_SWRITE));
#ifdef LATER
nservices++;
services[1].server_node = 1;
services[1].service_id = SERVICE2_ID;
services[1].cmpl = cmpl;
services[1].nclasses = nclasses;
#endif
nodes = nodes < nservices ? nservices : nodes;
/*
* XXX we really need more why??
* 1) each service has 'cmpl' client threads per node
* 2) Each service server needs a thread per potential client thread
* that is not on its node
* 3) each client not on the server needs a 'wait' thread.
* 4) Each server needs a thread to handle the GEN message from each
* node.
*/
totclients = nodes * nservices * cmpl;
nwaiters = nservices * ((nodes - 1) * cmpl);
nwaiters *= 2;
nwaiters += (nodes - 1);
usconfig(CONF_INITUSERS, nwaiters + totclients + 1);
sigset(SIGTERM, sig);
sigset(SIGABRT, sig);
sigset(SIGUSR1, sig);
prctl(PR_SETEXITSIG, SIGTERM);
/* initialize RPC */
initmbox();
/* call tkc_init to get arenas started up in correct sequence */
tkc_init();
tks_init();
/*
* alloc an area for locks - note that all nodes 'share' this
* but that should be OK
*/
usconfig(CONF_ARENATYPE, US_SHAREDONLY);
if ((usptr = usinit("/usr/tmp/tktest2")) == NULL)
abort();
/* create 'nodes' */
for (i = 0; i < nodes; i++)
node(i, nodes);
while (ndoneclients != totclients)
sginap(100);
if (dumplog)
tk_printlog(stdout, 1000, dumplog > 2 ? TK_LOG_ALL :
(dumplog > 1 ? TK_LOG_TS : 0));
return 0;
}
static void
sig(int s)
{
if (s == SIGABRT) {
tk_printlog(stdout, 1000, dumplog > 2 ? TK_LOG_ALL :
(dumplog > 1 ? TK_LOG_TS : 0));
sigset(SIGABRT, SIG_DFL);
abort();
} else if (s == SIGUSR1) {
tk_printlog(stdout, -1, dumplog > 2 ? TK_LOG_ALL :
(dumplog > 1 ? TK_LOG_TS : 0));
return;
}
exit(0);
}
/*
* Node
*/
/*
* do all waits for ootb messages - both client and server
*/
static void
node_wait(void *a)
{
int node = (int)(ptrdiff_t)a;
struct mbox *mbox;
auto struct test2_req *req;
auto int dyn;
mbox = ntombox(node);
for (;;) {
readmbox(mbox, (void *)&req, &dyn);
(services[req->service_id].wait)(req, node);
if (dyn)
freemboxmsg(req);
}
}
static void
node(int mynode, int nodes)
{
int i, j;
struct mbox *mb;
pid_t spid;
mb = allocmbox();
setmbox(mynode, mb);
for (i = 0; i < MAXSERVICES; i++) {
if (services[i].service_id < 0)
continue;
/* start up wait threads -
* if we are the server then we need 'cmpl' server threads
* per client node but no client wait threads.
* if we are a client we need 'cmpl' client wait threads
* but no server wait threads ..
*/
if (services[i].server_node == mynode) {
int ns;
ns = (nodes - 1) * services[SERVICE1_ID].cmpl;
/* one per node for GEN message */
ns += (nodes - 1);
for (j = 0; j < ns; j++) {
if ((spid = sproc(node_wait, PR_SALL,
(void *)(ptrdiff_t)mynode)) < 0) {
perror("sproc");
exit(1);
}
if (verbose)
printf("%s:started up server wait thread on node %d pid %d\n",
Cmd, mynode, spid);
}
} else {
int ns;
ns = services[SERVICE1_ID].cmpl;
for (j = 0; j < ns; j++) {
if ((spid = sproc(node_wait, PR_SALL,
(void *)(ptrdiff_t)mynode)) < 0) {
perror("sproc");
exit(1);
}
if (verbose)
printf("%s:started up client wait thread on node %d pid %d\n",
Cmd, mynode, spid);
}
}
if ((spid = sproc(services[i].start, PR_SALL, (void *)(ptrdiff_t)mynode)) < 0) {
perror("sproc");
exit(1);
}
if (verbose)
printf("%s:started up service %d on node %d pid %d\n",
Cmd, i, mynode, spid);
}
}
/* remote data for clients */
struct cdata {
TKC_DECL(cs, MAXTOKENS);
int target[MAXTOKENS];
ulock_t lock; /* protect mp client threads */
tks_ch_t h; /* handle (contains node we're on) */
int initted; /* are tokens created? */
int node; /* node we're running on */
usema_t *sync; /* sync up threads */
usema_t *sync2; /* sync up threads */
int ref; /* ref count */
};
/* local data for server */
struct sdata {
TKS_DECL(ss, MAXTOKENS);
ulock_t lock; /* protect remote and local threads */
struct cdata *cp; /* server's client data */
usema_t *sync; /* for recall operation */
int gen; /* which generation we're accepting requests
* for
*/
usema_t *gensync; /* wait for generation */
int nwaitgen;
int ncl; /* track # clients that have initialized */
};
#define INOT 1 /* not initialized */
#define IIS 2 /* in progress of initializing */
#define IAM 3 /* initialized */
int shadowtarget[MAXTOKENS]; /* to track 'target' */
char *levels[] = {
"UNK0",
"READ",
"WRITE",
"UNK3",
"SWRITE",
"UNK5",
"UNK6",
"UNK7"
};
static void client_obtain(void *, tk_set_t, tk_set_t, tk_set_t *, tk_set_t *);
static void client_revoke(void *, tk_set_t);
static void client_recall(void *, tk_set_t, tk_set_t, tk_set_t);
static void server_revoke(void *o, tks_ch_t h, tk_set_t r);
static void server_recall(void *o, tks_ch_t h, tk_set_t r, int);
static void service1_recalled(void *o, tk_set_t r, tk_set_t);
static void server_return(struct sdata *, struct test2_req *);
static void
service1_play(struct cdata *cp)
{
int i, lev, class;
struct timespec ts;
int loops = 10;
for (i = 0; i < loops; i++) {
if (verbose)
printf("client %d node %d loop %d\n",
get_pid(), cp->node, i);
/* get which token */
lev = 1 << (rand() % 3);
class = rand() % S1_NCLASS();
tkc_acquire(cp->cs, TK_MAKE(class, lev));
ussetlock(cp->lock);
if (lev == TK_WRITE) {
/*
* note that 2 threads in a client 'node'
* can both have the TK_WRITE level - the token
* module makes no claims within a node
*/
cp->target[class] = rand();
shadowtarget[class] = cp->target[class];
} else if (cp->target[class] != shadowtarget[class]) {
fprintf(stderr, "ERROR:client:%d:0x%x class:%d cd.target %d shadowtarget %d\n",
get_pid(),
cp->h,
class,
cp->target[class], shadowtarget[class]);
abort();
}
usunsetlock(cp->lock);
ts.tv_sec = 0;
ts.tv_nsec = (rand() % 4) * 1000 * 1000;
nanosleep(&ts, NULL);
tkc_release(cp->cs, TK_MAKE(class, lev));
}
}
/*
* service1_test - the real test code
* This runs on each node and is multi-threaded
*/
static void
service1_test(int node)
{
int i, j;
tkc_ifstate_t iface;
tks_ifstate_t svriface;
struct sdata *sp;
struct cdata *cp;
tk_set_t existance;
struct test2_req req;
struct test2_res res;
char cname[TK_NAME_SZ], sname[TK_NAME_SZ];
iface.tkc_obtain = client_obtain;
iface.tkc_revoke = client_revoke;
iface.tkc_recall = client_recall;
svriface.tks_revoke = server_revoke;
svriface.tks_recall = server_recall;
svriface.tks_recalled = service1_recalled;
existance = TK_MAKE(S1_NCLASS(), TK_READ);
for (i = 0; i < nloops; i++) {
/*
* for server create both server and client token sets
*/
sprintf(cname, "Ctest2%d", i);
sprintf(sname, "Stest2%d", i);
if (AM_S1S(node)) {
sp = S1_P(node);
cp = sp->cp;
ussetlock(cp->lock);
if (cp->initted == INOT) {
cp->initted = IIS;
usunsetlock(cp->lock);
/* create token set */
tks_create(sname, sp->ss, (void *)sp, &svriface,
S1_NCLASS() + 1);
tkc_create(cname, cp->cs, (void *)cp, &iface,
S1_NCLASS() + 1,
TK_NULLSET);
cp->initted = IAM;
/*
* immediately give ourselves an existance token
*/
tkc_acquire(cp->cs, existance);
/*
* we are now ready to handle generation 'i'
*/
ussetlock(cp->lock);
sp->gen = i;
sp->ncl = 0;
for (j = 0; j < sp->nwaitgen; j++)
usvsema(sp->gensync);
sp->nwaitgen = 0;
usunsetlock(cp->lock);
/*
* now wait for all clients to 'register'
*/
while (sp->ncl < (nodes - 1)) {
sginap(10);
}
/* wake up other server/client threads on our
* node
*/
for (j = 1; j < services[SERVICE1_ID].cmpl; j++)
usvsema(cp->sync2);
} else {
usunsetlock(cp->lock);
uspsema(cp->sync2);
}
} else {
cp = S1_P(node);
ussetlock(cp->lock);
if (cp->initted == INOT) {
cp->initted = IAM;
usunsetlock(cp->lock);
/*
* send message to server requesting permission
* to play in generation 'gen'
*/
req.req_un.gen_req.gen = i;
req.op = TEST2_GEN;
req.service_id = SERVICE1_ID;
srpc(ntombox(S1S()), cp->h, &req, sizeof(req),
&res, sizeof(res));
/*
* since we now have existance token
* the previous generation must be done so
* we can initialize a new one
*/
tkc_create(cname, cp->cs, (void *)cp, &iface,
S1_NCLASS() + 1,
res.res_un.gen_res.existance);
/* wake all other threads (on our node) up */
for (j = 1; j < services[SERVICE1_ID].cmpl; j++)
usvsema(cp->sync2);
} else {
usunsetlock(cp->lock);
uspsema(cp->sync2);
}
}
/*
* At this point all clients have an existance token
* so that no matter who finishes first, the server won't
* be able to complete the recall until everyone is
* at least out of 'play'
*/
service1_play(cp);
/*
* all threads but the last one will wait
*/
ussetlock(cp->lock);
assert(cp->ref > 0);
if (--cp->ref != 0) {
usunsetlock(cp->lock);
uspsema(cp->sync);
continue;
}
usunsetlock(cp->lock);
/* now that we are done - release our 'existance' token */
tkc_release(cp->cs, existance);
/*
* we are last thread in our group
* The server thread starts a recall
*/
if (AM_S1S(node)) {
/* recall and destroy token */
tks_recall(sp->ss, existance);
/* service1_recalled will be called when all done */
uspsema(sp->sync);
/* test that everything got sent back */
for (j = 0; j < S1_NCLASS(); j++) {
if (cp->target[j] != shadowtarget[j]) {
fprintf(stderr, "ERROR:server:%d:class:%d cd.target %d shadowtarget %d\n",
get_pid(),
j,
cp->target[j], shadowtarget[j]);
abort();
}
}
tks_destroy(sp->ss);
}
/* reset the ref count and go again */
cp->ref = services[SERVICE1_ID].cmpl;
cp->initted = INOT;
/* wake all other threads (on our node) up */
for (j = 1; j < services[SERVICE1_ID].cmpl; j++)
usvsema(cp->sync);
}
test_then_add(&ndoneclients, 1);
pause();
}
static void
service1_launch(void *a)
{
service1_test((int)(ptrdiff_t)a);
/* NOTREACHED */
}
void
service1_start(void *a)
{
int mynode = (int)(ptrdiff_t)a;
int i;
struct sdata sd;
struct cdata cd; /* this is what we're manipulating */
/* init a client data object */
cd.h = nodetoh(mynode); /* handle is just node number */
cd.initted = INOT;
cd.lock = usnewlock(usptr);
cd.sync = usnewsema(usptr, 0);
cd.sync2 = usnewsema(usptr, 0);
cd.node = mynode;
cd.ref = services[SERVICE1_ID].cmpl;
if (AM_S1S(mynode)) {
/* init server side */
sd.lock = usnewlock(usptr);
sd.sync = usnewsema(usptr, 0);
sd.cp = &cd;
/*
* to provide syncronization between loops, clients
* send a request to the server requesting to 'play' in a
* particular generation - that request will sleep
* until the server is ready
*/
sd.gensync = usnewsema(usptr, 0);
sd.gen = 0; /* "now serving" */
sd.nwaitgen = 0;
/* place local data in 'fake' private memory */
S1_P(mynode) = &sd;
} else {
/* place local data in 'fake' private memory */
S1_P(mynode) = &cd;
}
/*
* each client / server is multi-threaded
*/
for (i = 1; i < services[SERVICE1_ID].cmpl; i++) {
if (sproc(service1_launch, PR_SALL,
(void *)(ptrdiff_t)mynode) < 0) {
perror("sproc");
exit(1);
}
}
service1_test(mynode);
/* NOTREACHED */
}
/* ARGSUSED */
static void
client_obtain(void *o,
tk_set_t obtain,
tk_set_t toreturn,
tk_set_t *already,
tk_set_t *refused)
{
struct test2_req req;
struct test2_res res;
struct cdata *cp = (struct cdata *)o;
int i;
if (AM_S1S(cp->node)) {
/* we are server */
struct sdata *sp = S1_P(cp->node);
auto tk_set_t granted;
if (toreturn != TK_NULLSET)
tks_return(sp->ss, cp->h, toreturn);
tks_obtain(sp->ss, cp->h, obtain, &granted, already);
assert(obtain == granted);
return;
}
/* remote client */
/* send out obtain request message */
req.req_un.obtain_req.obtain = obtain;
req.req_un.obtain_req.toreturn = toreturn;
req.op = TEST2_OBTAIN;
req.service_id = SERVICE1_ID;
for (i = 0; i < S1_NCLASS(); i++) {
if (TK_IS_IN_SET(toreturn, TK_MAKE(i, TK_WRITE))) {
/* returning write token - return data also */
req.req_un.obtain_req.data[i] = cp->target[i];
}
}
srpc(ntombox(S1S()), cp->h, &req, sizeof(req), &res, sizeof(res));
if (req.op != res.op) {
fprintf(stderr, "client_obtain: srpc return message op wrong!\n");
exit(1);
}
for (i = 0; i < S1_NCLASS(); i++) {
if (TK_GET_CLASS(res.res_un.obtain_res.granted, i) != TK_NULLSET) {
cp->target[i] = res.res_un.obtain_res.data[i];
}
}
*already = res.res_un.obtain_res.already;
}
/*
* client_revoke - callout from token client module
*/
static void
client_revoke(void *o, tk_set_t revoke)
{
struct test2_req req;
struct cdata *cp = (struct cdata *)o;
int i;
auto tk_set_t tret;
struct sdata *sp;
if (AM_S1S(cp->node))
sp = S1_P(cp->node);
/*
* if our existance token was recalled, then shut down entire object
* This includes sending back all tokens we might have cached
*/
if (TK_GET_CLASS(revoke, S1_NCLASS()) != TK_NULLSET) {
tkc_returning(cp->cs, S1_ALLTK(), &tret, 0);
revoke |= tret;
}
if (AM_S1S(cp->node)) {
/* we are server */
tks_return(sp->ss, cp->h, revoke);
} else {
/* send return message and data */
req.req_un.return_req.toreturn = revoke;
req.op = TEST2_RETURN;
req.service_id = SERVICE1_ID;
for (i = 0; i < S1_NCLASS(); i++) {
if (TK_GET_CLASS(revoke, i) != TK_NULLSET) {
req.req_un.return_req.data[i] = cp->target[i];
}
}
req.addr.handle = cp->h;
writembox_copy(ntombox(S1S()), &req, sizeof(req));
}
/* inform client token module that all is done */
tkc_return(cp->cs, revoke, TK_NULLSET);
if (TK_GET_CLASS(revoke, S1_NCLASS()) != TK_NULLSET)
tkc_destroy(cp->cs);
}
/*
* callout when client receives a recall request
* For service1 a recall means to shut down usage of the object
*/
static void
client_recall(void *o, tk_set_t toreturn, tk_set_t refuse, tk_set_t eh)
{
struct test2_req req;
struct cdata *cp = (struct cdata *)o;
int i;
auto tk_set_t tret;
struct sdata *sp;
if (AM_S1S(cp->node))
sp = S1_P(cp->node);
/*
* if our existance token was recalled, then shut down entire object
* This includes sending back all tokens we might have cached
*/
if (TK_GET_CLASS(toreturn, S1_NCLASS()) != TK_NULLSET) {
tkc_returning(cp->cs, S1_ALLTK(), &tret, 0);
toreturn |= tret;
}
if (AM_S1S(cp->node)) {
/* we are server */
tks_return_recall(sp->ss, cp->h, toreturn, refuse, eh);
} else {
/* send return message and data */
req.req_un.return_req.toreturn = toreturn;
req.req_un.return_req.refuse = refuse;
req.req_un.return_req.eh = eh;
req.op = TEST2_RETURN_RECALL;
req.service_id = SERVICE1_ID;
for (i = 0; i < S1_NCLASS(); i++) {
if (TK_GET_CLASS(toreturn, i) != TK_NULLSET) {
req.req_un.return_req.data[i] = cp->target[i];
}
}
req.addr.handle = cp->h;
writembox_copy(ntombox(S1S()), &req, sizeof(req));
}
/* inform client token module that all is done */
tkc_return(cp->cs, toreturn, TK_NULLSET);
if (TK_GET_CLASS(toreturn, S1_NCLASS()) != TK_NULLSET)
tkc_destroy(cp->cs);
}
/*
* get client handle from within token module - used for TRACING only
*/
tks_ch_t
getch_t(void *o)
{
struct cdata *cp = (struct cdata *)o;
assert(cp);
return(cp->h);
}
/*
* callout function that is called from the token server module when a
* token (set) needs to be revoked. It sends a message to the appropriate
* client. This sends a 1 way message
*/
/* ARGSUSED */
static void
server_revoke(void *o, tks_ch_t h, tk_set_t r)
{
struct test2_req req;
struct sdata *sp = (struct sdata *)o;
if (AM_S1S(htonode(h))) {
tkc_revoke(sp->cp->cs, r);
return;
}
req.req_un.revoke_req.torevoke = r;
req.op = TEST2_REVOKE;
req.service_id = SERVICE1_ID;
writembox_copy(ntombox(htonode(h)), &req, sizeof(req));
}
/*
* callout function to start recall process - called as the result of
* a tks_recall operation
*/
static void
server_recall(void *o, tks_ch_t h, tk_set_t r, int flag)
{
struct test2_req req;
struct sdata *sp = (struct sdata *)o;
if (AM_S1S(htonode(h))) {
tkc_recall(sp->cp->cs, r, flag);
return;
}
req.req_un.recall_req.torecall = r;
req.req_un.recall_req.flag = flag;
req.op = TEST2_RECALL;
req.service_id = SERVICE1_ID;
writembox_copy(ntombox(htonode(h)), &req, sizeof(req));
}
/*
* callout function - called when a token has been recalled and can now be
* reclaimed/destroyed
*/
/* ARGSUSED */
static void
service1_recalled(void *o, tk_set_t r, tk_set_t refused)
{
struct sdata *sp = (struct sdata *)o;
assert(AM_S1S(sp->cp->node));
assert(refused == TK_NULLSET);
usvsema(sp->sync);
}
/*
* server_return - called by the server message handler when a TEST2_RETURN or
* TEST2_RETURN_RECALL
* message is received. The data is updated and the token(s) returned.
*/
static void
server_return(struct sdata *sp, struct test2_req *req)
{
tk_set_t r;
int i;
r = req->req_un.return_req.toreturn;
/*
* update any modified data
* We shouldn't need any locks since the caller still has the
* token for WR or RD, noone should be able to change it
*/
for (i = 0; i < S1_NCLASS(); i++) {
if (TK_IS_IN_SET(r, TK_MAKE(i, TK_WRITE))) {
sp->cp->target[i] = req->req_un.return_req.data[i];
} else if (TK_IS_IN_SET(r, TK_MAKE(i, TK_READ))) {
if (sp->cp->target[i] != req->req_un.return_req.data[i]) {
printf("server revoked a read token and got back different data.\n");
printf("\tserver data:%d client data %d\n",
sp->cp->target[i],
req->req_un.return_req.data[i]);
abort();
}
}
}
/* return set of tokens */
if (req->op == TEST2_RETURN)
tks_return(sp->ss, req->addr.handle, r);
else
tks_return_recall(sp->ss, req->addr.handle, r,
req->req_un.return_req.refuse,
req->req_un.return_req.eh);
}
/*
* this thread waits for any out-of-the-blue messages
* Both client and server.
* 'node' is which node we are.
*/
static void
service1_wait(struct test2_req *req, int node)
{
struct cdata *cp; /* client side data */
struct sdata *sp; /* server side data */
struct test2_res res;
tk_set_t toreturn;
int i;
switch (req->op) {
/* client messages */
case TEST2_REVOKE:
cp = (struct cdata *)S1_P(node);
assert(!AM_S1S(node));
tkc_revoke(cp->cs, req->req_un.revoke_req.torevoke);
break;
case TEST2_RECALL:
cp = (struct cdata *)S1_P(node);
assert(!AM_S1S(node));
tkc_recall(cp->cs, req->req_un.recall_req.torecall,
req->req_un.recall_req.flag);
break;
/* server messages */
case TEST2_OBTAIN:
sp = (struct sdata *)S1_P(node);
assert(AM_S1S(node));
toreturn = req->req_un.obtain_req.toreturn;
if (toreturn != TK_NULLSET) {
/*
* if returning write token - update data
*/
for (i = 0; i < S1_NCLASS(); i++) {
if (TK_IS_IN_SET(toreturn, TK_MAKE(i, TK_WRITE)))
sp->cp->target[i] = req->req_un.obtain_req.data[i];
}
tks_return(sp->ss, req->addr.handle, toreturn);
}
tks_obtain(sp->ss, req->addr.handle,
req->req_un.obtain_req.obtain,
&res.res_un.obtain_res.granted,
&res.res_un.obtain_res.already);
for (i = 0; i < S1_NCLASS(); i++) {
if (TK_GET_CLASS(res.res_un.obtain_res.granted, i) != TK_NULLSET)
res.res_un.obtain_res.data[i] =
sp->cp->target[i];
}
res.op = TEST2_OBTAIN;
writembox_copy((struct mbox *)req->addr.return_addr,
&res, sizeof(res));
break;
case TEST2_RETURN_RECALL:
case TEST2_RETURN:
sp = (struct sdata *)S1_P(node);
assert(AM_S1S(node));
/* 1-way message in response to a revoke/recall */
server_return(sp, req);
break;
case TEST2_GEN:
{
auto tk_set_t granted, already;
/* client wants to play at 'gen' - wait till server is ready */
sp = (struct sdata *)S1_P(node);
assert(AM_S1S(node));
ussetlock(sp->cp->lock);
if (sp->gen > req->req_un.gen_req.gen) {
abort();
} else if (sp->gen < req->req_un.gen_req.gen) {
/* wait */
assert(sp->nwaitgen >= 0);
sp->nwaitgen++;
usunsetlock(sp->cp->lock);
uspsema(sp->gensync);
} else
usunsetlock(sp->cp->lock);
/* hack - increment # clients */
ussetlock(sp->cp->lock);
sp->ncl++;
usunsetlock(sp->cp->lock);
/* all set - respond with existance token */
tks_obtain(sp->ss, req->addr.handle,
TK_MAKE(S1_NCLASS(), TK_READ),
&granted,
&already);
assert(already == TK_NULLSET);
assert(granted == TK_MAKE(S1_NCLASS(), TK_READ));
res.op = TEST2_GEN;
res.res_un.gen_res.existance = granted;
writembox_copy((struct mbox *)req->addr.return_addr,
&res, sizeof(res));
break;
}
default:
fprintf(stderr, "service1-wait:Unknown msg op:%d\n", req->op);
abort();
break;
}
}
/*
* rpc -
*/
static int
srpc(struct mbox *to, tks_ch_t h, void *req, int reqlen, void *res, int reslen)
{
struct mbox *mb;
mb = allocmbox();
((struct scheme *)req)->handle = h; /* our handle */
((struct scheme *)req)->return_addr = mb;/* where to return message to */
writembox(to, req, reqlen);
/* wait for response */
readmbox_copy(mb, res, reslen);
freembox(mb);
return 0;
}