/***** spin: mesg.c *****/
/* Copyright (c) 1989-2003 by Lucent Technologies, Bell Laboratories. */
/* All Rights Reserved. This software is for educational purposes only. */
/* No guarantee whatsoever is expressed or implied by the distribution of */
/* this code. Permission is given to distribute this code provided that */
/* this introductory message is not removed and no monies are exchanged. */
/* Software written by Gerard J. Holzmann. For tool documentation see: */
/* http://spinroot.com/ */
/* Send all bug-reports and/or questions to: [email protected] */
#include <stdlib.h>
#include "spin.h"
#include "y.tab.h"
#ifndef MAXQ
#define MAXQ 2500 /* default max # queues */
#endif
extern RunList *X;
extern Symbol *Fname;
extern Lextok *Mtype;
extern int verbose, TstOnly, s_trail, analyze, columns;
extern int lineno, depth, xspin, m_loss, jumpsteps;
extern int nproc, nstop;
extern short Have_claim;
Queue *qtab = (Queue *) 0; /* linked list of queues */
Queue *ltab[MAXQ]; /* linear list of queues */
int nqs = 0, firstrow = 1, has_stdin = 0;
char Buf[4096];
static Lextok *n_rem = (Lextok *) 0;
static Queue *q_rem = (Queue *) 0;
static int a_rcv(Queue *, Lextok *, int);
static int a_snd(Queue *, Lextok *);
static int sa_snd(Queue *, Lextok *);
static int s_snd(Queue *, Lextok *);
extern void sr_buf(int, int);
extern void sr_mesg(FILE *, int, int);
extern void putarrow(int, int);
static void sr_talk(Lextok *, int, char *, char *, int, Queue *);
int
cnt_mpars(Lextok *n)
{ Lextok *m;
int i=0;
for (m = n; m; m = m->rgt)
i += Cnt_flds(m);
return i;
}
int
qmake(Symbol *s)
{ Lextok *m;
Queue *q;
int i;
if (!s->ini)
return 0;
if (nqs >= MAXQ)
{ lineno = s->ini->ln;
Fname = s->ini->fn;
fatal("too many queues (%s)", s->name);
}
if (analyze && nqs >= 255)
{ fatal("too many channel types", (char *)0);
}
if (s->ini->ntyp != CHAN)
return eval(s->ini);
q = (Queue *) emalloc(sizeof(Queue));
q->qid = ++nqs;
q->nslots = s->ini->val;
q->nflds = cnt_mpars(s->ini->rgt);
q->setat = depth;
i = max(1, q->nslots); /* 0-slot qs get 1 slot minimum */
q->contents = (int *) emalloc(q->nflds*i*sizeof(int));
q->fld_width = (int *) emalloc(q->nflds*sizeof(int));
q->stepnr = (int *) emalloc(i*sizeof(int));
for (m = s->ini->rgt, i = 0; m; m = m->rgt)
{ if (m->sym && m->ntyp == STRUCT)
i = Width_set(q->fld_width, i, getuname(m->sym));
else
q->fld_width[i++] = m->ntyp;
}
q->nxt = qtab;
qtab = q;
ltab[q->qid-1] = q;
return q->qid;
}
int
qfull(Lextok *n)
{ int whichq = eval(n->lft)-1;
if (whichq < MAXQ && whichq >= 0 && ltab[whichq])
return (ltab[whichq]->qlen >= ltab[whichq]->nslots);
return 0;
}
int
qlen(Lextok *n)
{ int whichq = eval(n->lft)-1;
if (whichq < MAXQ && whichq >= 0 && ltab[whichq])
return ltab[whichq]->qlen;
return 0;
}
int
q_is_sync(Lextok *n)
{ int whichq = eval(n->lft)-1;
if (whichq < MAXQ && whichq >= 0 && ltab[whichq])
return (ltab[whichq]->nslots == 0);
return 0;
}
int
qsend(Lextok *n)
{ int whichq = eval(n->lft)-1;
if (whichq == -1)
{ printf("Error: sending to an uninitialized chan\n");
/* whichq = 0; */
return 0;
}
if (whichq < MAXQ && whichq >= 0 && ltab[whichq])
{ ltab[whichq]->setat = depth;
if (ltab[whichq]->nslots > 0)
return a_snd(ltab[whichq], n);
else
return s_snd(ltab[whichq], n);
}
return 0;
}
#ifndef PC
#include <termios.h>
static struct termios initial_settings, new_settings;
void
peek_ch_init(void)
{
tcgetattr(0,&initial_settings);
new_settings = initial_settings;
new_settings.c_lflag &= ~ICANON;
new_settings.c_lflag &= ~ECHO;
new_settings.c_lflag &= ~ISIG;
new_settings.c_cc[VMIN] = 0;
new_settings.c_cc[VTIME] = 0;
}
int
peek_ch(void)
{ int n;
has_stdin = 1;
tcsetattr(0, TCSANOW, &new_settings);
n = getchar();
tcsetattr(0, TCSANOW, &initial_settings);
return n;
}
#endif
int
qrecv(Lextok *n, int full)
{ int whichq = eval(n->lft)-1;
if (whichq == -1)
{ if (n->sym && !strcmp(n->sym->name, "STDIN"))
{ Lextok *m;
#ifndef PC
static int did_once = 0;
if (!did_once) /* 6.2.4 */
{ peek_ch_init();
did_once = 1;
}
#endif
if (TstOnly) return 1;
for (m = n->rgt; m; m = m->rgt)
if (m->lft->ntyp != CONST && m->lft->ntyp != EVAL)
{
#ifdef PC
int c = getchar();
#else
int c = peek_ch(); /* 6.2.4, was getchar(); */
#endif
if (c == 27 || c == 3) /* escape or control-c */
{ printf("quit\n");
exit(0);
} /* else: non-blocking */
if (c == EOF) return 0; /* no char available */
(void) setval(m->lft, c);
} else
{ fatal("invalid use of STDIN", (char *)0);
}
return 1;
}
printf("Error: receiving from an uninitialized chan %s\n",
n->sym?n->sym->name:"");
/* whichq = 0; */
return 0;
}
if (whichq < MAXQ && whichq >= 0 && ltab[whichq])
{ ltab[whichq]->setat = depth;
return a_rcv(ltab[whichq], n, full);
}
return 0;
}
static int
sa_snd(Queue *q, Lextok *n) /* sorted asynchronous */
{ Lextok *m;
int i, j, k;
int New, Old;
for (i = 0; i < q->qlen; i++)
for (j = 0, m = n->rgt; m && j < q->nflds; m = m->rgt, j++)
{ New = cast_val(q->fld_width[j], eval(m->lft), 0);
Old = q->contents[i*q->nflds+j];
if (New == Old)
continue;
if (New > Old)
break; /* inner loop */
goto found; /* New < Old */
}
found:
for (j = q->qlen-1; j >= i; j--)
for (k = 0; k < q->nflds; k++)
{ q->contents[(j+1)*q->nflds+k] =
q->contents[j*q->nflds+k]; /* shift up */
if (k == 0)
q->stepnr[j+1] = q->stepnr[j];
}
return i*q->nflds; /* new q offset */
}
void
typ_ck(int ft, int at, char *s)
{
if ((verbose&32) && ft != at
&& (ft == CHAN || at == CHAN))
{ char buf[128], tag1[64], tag2[64];
(void) sputtype(tag1, ft);
(void) sputtype(tag2, at);
sprintf(buf, "type-clash in %s, (%s<-> %s)", s, tag1, tag2);
non_fatal("%s", buf);
}
}
static int
a_snd(Queue *q, Lextok *n)
{ Lextok *m;
int i = q->qlen*q->nflds; /* q offset */
int j = 0; /* q field# */
if (q->nslots > 0 && q->qlen >= q->nslots)
return m_loss; /* q is full */
if (TstOnly) return 1;
if (n->val) i = sa_snd(q, n); /* sorted insert */
q->stepnr[i/q->nflds] = depth;
for (m = n->rgt; m && j < q->nflds; m = m->rgt, j++)
{ int New = eval(m->lft);
q->contents[i+j] = cast_val(q->fld_width[j], New, 0);
if ((verbose&16) && depth >= jumpsteps)
sr_talk(n, New, "Send ", "->", j, q);
typ_ck(q->fld_width[j], Sym_typ(m->lft), "send");
}
if ((verbose&16) && depth >= jumpsteps)
{ for (i = j; i < q->nflds; i++)
sr_talk(n, 0, "Send ", "->", i, q);
if (j < q->nflds)
printf("%3d: warning: missing params in send\n",
depth);
if (m)
printf("%3d: warning: too many params in send\n",
depth);
}
q->qlen++;
return 1;
}
static int
a_rcv(Queue *q, Lextok *n, int full)
{ Lextok *m;
int i=0, oi, j, k;
extern int Rvous;
if (q->qlen == 0)
return 0; /* q is empty */
try_slot:
/* test executability */
for (m = n->rgt, j=0; m && j < q->nflds; m = m->rgt, j++)
if ((m->lft->ntyp == CONST
&& q->contents[i*q->nflds+j] != m->lft->val)
|| (m->lft->ntyp == EVAL
&& q->contents[i*q->nflds+j] != eval(m->lft->lft)))
{ if (n->val == 0 /* fifo recv */
|| n->val == 2 /* fifo poll */
|| ++i >= q->qlen) /* last slot */
return 0; /* no match */
goto try_slot;
}
if (TstOnly) return 1;
if (verbose&8)
{ if (j < q->nflds)
printf("%3d: warning: missing params in next recv\n",
depth);
else if (m)
printf("%3d: warning: too many params in next recv\n",
depth);
}
/* set the fields */
if (Rvous)
{ n_rem = n;
q_rem = q;
}
oi = q->stepnr[i];
for (m = n->rgt, j = 0; m && j < q->nflds; m = m->rgt, j++)
{ if (columns && !full) /* was columns == 1 */
continue;
if ((verbose&8) && !Rvous && depth >= jumpsteps)
{ sr_talk(n, q->contents[i*q->nflds+j],
(full && n->val < 2)?"Recv ":"[Recv] ", "<-", j, q);
}
if (!full)
continue; /* test */
if (m && m->lft->ntyp != CONST && m->lft->ntyp != EVAL)
{ (void) setval(m->lft, q->contents[i*q->nflds+j]);
typ_ck(q->fld_width[j], Sym_typ(m->lft), "recv");
}
if (n->val < 2) /* not a poll */
for (k = i; k < q->qlen-1; k++)
{ q->contents[k*q->nflds+j] =
q->contents[(k+1)*q->nflds+j];
if (j == 0)
q->stepnr[k] = q->stepnr[k+1];
}
}
if ((!columns || full)
&& (verbose&8) && !Rvous && depth >= jumpsteps)
for (i = j; i < q->nflds; i++)
{ sr_talk(n, 0,
(full && n->val < 2)?"Recv ":"[Recv] ", "<-", i, q);
}
if (columns == 2 && full && !Rvous && depth >= jumpsteps)
putarrow(oi, depth);
if (full && n->val < 2)
q->qlen--;
return 1;
}
static int
s_snd(Queue *q, Lextok *n)
{ Lextok *m;
RunList *rX, *sX = X; /* rX=recvr, sX=sendr */
int i, j = 0; /* q field# */
for (m = n->rgt; m && j < q->nflds; m = m->rgt, j++)
{ q->contents[j] = cast_val(q->fld_width[j], eval(m->lft), 0);
typ_ck(q->fld_width[j], Sym_typ(m->lft), "rv-send");
}
q->qlen = 1;
if (!complete_rendez())
{ q->qlen = 0;
return 0;
}
if (TstOnly)
{ q->qlen = 0;
return 1;
}
q->stepnr[0] = depth;
if ((verbose&16) && depth >= jumpsteps)
{ m = n->rgt;
rX = X; X = sX;
for (j = 0; m && j < q->nflds; m = m->rgt, j++)
sr_talk(n, eval(m->lft), "Sent ", "->", j, q);
for (i = j; i < q->nflds; i++)
sr_talk(n, 0, "Sent ", "->", i, q);
if (j < q->nflds)
printf("%3d: warning: missing params in rv-send\n",
depth);
else if (m)
printf("%3d: warning: too many params in rv-send\n",
depth);
X = rX; /* restore receiver's context */
if (!s_trail)
{ if (!n_rem || !q_rem)
fatal("cannot happen, s_snd", (char *) 0);
m = n_rem->rgt;
for (j = 0; m && j < q->nflds; m = m->rgt, j++)
{ if (m->lft->ntyp != NAME
|| strcmp(m->lft->sym->name, "_") != 0)
i = eval(m->lft);
else i = 0;
if (verbose&8)
sr_talk(n_rem,i,"Recv ","<-",j,q_rem);
}
if (verbose&8)
for (i = j; i < q->nflds; i++)
sr_talk(n_rem, 0, "Recv ", "<-", j, q_rem);
if (columns == 2)
putarrow(depth, depth);
}
n_rem = (Lextok *) 0;
q_rem = (Queue *) 0;
}
return 1;
}
static void
channm(Lextok *n)
{ char lbuf[512];
if (n->sym->type == CHAN)
strcat(Buf, n->sym->name);
else if (n->sym->type == NAME)
strcat(Buf, lookup(n->sym->name)->name);
else if (n->sym->type == STRUCT)
{ Symbol *r = n->sym;
if (r->context)
{ r = findloc(r);
if (!r)
{ strcat(Buf, "*?*");
return;
} }
ini_struct(r);
printf("%s", r->name);
strcpy(lbuf, "");
struct_name(n->lft, r, 1, lbuf);
strcat(Buf, lbuf);
} else
strcat(Buf, "-");
if (n->lft->lft)
{ sprintf(lbuf, "[%d]", eval(n->lft->lft));
strcat(Buf, lbuf);
}
}
static void
difcolumns(Lextok *n, char *tr, int v, int j, Queue *q)
{ extern int pno;
if (j == 0)
{ Buf[0] = '\0';
channm(n);
strcat(Buf, (strncmp(tr, "Sen", 3))?"?":"!");
} else
strcat(Buf, ",");
if (tr[0] == '[') strcat(Buf, "[");
sr_buf(v, q->fld_width[j] == MTYPE);
if (j == q->nflds - 1)
{ int cnr;
if (s_trail) cnr = pno; else cnr = X?X->pid - Have_claim:0;
if (tr[0] == '[') strcat(Buf, "]");
pstext(cnr, Buf);
}
}
static void
docolumns(Lextok *n, char *tr, int v, int j, Queue *q)
{ int i;
if (firstrow)
{ printf("q\\p");
for (i = 0; i < nproc-nstop - Have_claim; i++)
printf(" %3d", i);
printf("\n");
firstrow = 0;
}
if (j == 0)
{ printf("%3d", q->qid);
if (X)
for (i = 0; i < X->pid - Have_claim; i++)
printf(" .");
printf(" ");
Buf[0] = '\0';
channm(n);
printf("%s%c", Buf, (strncmp(tr, "Sen", 3))?'?':'!');
} else
printf(",");
if (tr[0] == '[') printf("[");
sr_mesg(stdout, v, q->fld_width[j] == MTYPE);
if (j == q->nflds - 1)
{ if (tr[0] == '[') printf("]");
printf("\n");
}
}
typedef struct QH {
int n;
struct QH *nxt;
} QH;
static QH *qh;
void
qhide(int q)
{ QH *p = (QH *) emalloc(sizeof(QH));
p->n = q;
p->nxt = qh;
qh = p;
}
int
qishidden(int q)
{ QH *p;
for (p = qh; p; p = p->nxt)
if (p->n == q)
return 1;
return 0;
}
static void
sr_talk(Lextok *n, int v, char *tr, char *a, int j, Queue *q)
{ char s[128];
if (qishidden(eval(n->lft)))
return;
if (columns)
{ if (columns == 2)
difcolumns(n, tr, v, j, q);
else
docolumns(n, tr, v, j, q);
return;
}
if (xspin)
{ if ((verbose&4) && tr[0] != '[')
sprintf(s, "(state -)\t[values: %d",
eval(n->lft));
else
sprintf(s, "(state -)\t[%d", eval(n->lft));
if (strncmp(tr, "Sen", 3) == 0)
strcat(s, "!");
else
strcat(s, "?");
} else
{ strcpy(s, tr);
}
if (j == 0)
{ char snm[128];
whoruns(1);
{ char *ptr = n->fn->name;
char *qtr = snm;
while (*ptr != '\0')
{ if (*ptr != '\"')
{ *qtr++ = *ptr;
}
ptr++;
}
*qtr = '\0';
printf("%s:%d %s",
snm, n->ln, s);
}
} else
printf(",");
sr_mesg(stdout, v, q->fld_width[j] == MTYPE);
if (j == q->nflds - 1)
{ if (xspin)
{ printf("]\n");
if (!(verbose&4)) printf("\n");
return;
}
printf("\t%s queue %d (", a, eval(n->lft));
Buf[0] = '\0';
channm(n);
printf("%s)\n", Buf);
}
fflush(stdout);
}
void
sr_buf(int v, int j)
{ int cnt = 1; Lextok *n;
char lbuf[512];
for (n = Mtype; n && j; n = n->rgt, cnt++)
if (cnt == v)
{ if(strlen(n->lft->sym->name) >= sizeof(lbuf))
{ non_fatal("mtype name %s too long", n->lft->sym->name);
break;
}
sprintf(lbuf, "%s", n->lft->sym->name);
strcat(Buf, lbuf);
return;
}
sprintf(lbuf, "%d", v);
strcat(Buf, lbuf);
}
void
sr_mesg(FILE *fd, int v, int j)
{ Buf[0] ='\0';
sr_buf(v, j);
#if 1
fprintf(fd, Buf, (char *) 0); /* prevent compiler warning */
#else
fprintf(fd, Buf);
#endif
}
void
doq(Symbol *s, int n, RunList *r)
{ Queue *q;
int j, k;
if (!s->val) /* uninitialized queue */
return;
for (q = qtab; q; q = q->nxt)
if (q->qid == s->val[n])
{ if (xspin > 0
&& (verbose&4)
&& q->setat < depth)
continue;
if (q->nslots == 0)
continue; /* rv q always empty */
#if 0
if (q->qlen == 0) /* new 7/10 -- dont show if queue is empty */
{ continue;
}
#endif
printf("\t\tqueue %d (", q->qid);
if (r)
printf("%s(%d):", r->n->name, r->pid - Have_claim);
if (s->nel > 1 || s->isarray)
printf("%s[%d]): ", s->name, n);
else
printf("%s): ", s->name);
for (k = 0; k < q->qlen; k++)
{ printf("[");
for (j = 0; j < q->nflds; j++)
{ if (j > 0) printf(",");
sr_mesg(stdout, q->contents[k*q->nflds+j],
q->fld_width[j] == MTYPE);
}
printf("]");
}
printf("\n");
break;
}
}
void
nochan_manip(Lextok *p, Lextok *n, int d)
{ int e = 1;
if (d == 0 && p->sym && p->sym->type == CHAN)
{ setaccess(p->sym, ZS, 0, 'L');
if (n && n->ntyp == CONST)
fatal("invalid asgn to chan", (char *) 0);
if (n && n->sym && n->sym->type == CHAN)
{ setaccess(n->sym, ZS, 0, 'V');
return;
}
}
/* ok on the rhs of an assignment: */
if (!n || n->ntyp == LEN || n->ntyp == RUN
|| n->ntyp == FULL || n->ntyp == NFULL
|| n->ntyp == EMPTY || n->ntyp == NEMPTY)
return;
if (n->sym && n->sym->type == CHAN)
{ if (d == 1)
fatal("invalid use of chan name", (char *) 0);
else
setaccess(n->sym, ZS, 0, 'V');
}
if (n->ntyp == NAME
|| n->ntyp == '.')
e = 0; /* array index or struct element */
nochan_manip(p, n->lft, e);
nochan_manip(p, n->rgt, 1);
}
typedef struct BaseName {
char *str;
int cnt;
struct BaseName *nxt;
} BaseName;
static BaseName *bsn;
void
newbasename(char *s)
{ BaseName *b;
/* printf("+++++++++%s\n", s); */
for (b = bsn; b; b = b->nxt)
if (strcmp(b->str, s) == 0)
{ b->cnt++;
return;
}
b = (BaseName *) emalloc(sizeof(BaseName));
b->str = emalloc(strlen(s)+1);
b->cnt = 1;
strcpy(b->str, s);
b->nxt = bsn;
bsn = b;
}
void
delbasename(char *s)
{ BaseName *b, *prv = (BaseName *) 0;
for (b = bsn; b; prv = b, b = b->nxt)
{ if (strcmp(b->str, s) == 0)
{ b->cnt--;
if (b->cnt == 0)
{ if (prv)
{ prv->nxt = b->nxt;
} else
{ bsn = b->nxt;
} }
/* printf("---------%s\n", s); */
break;
} }
}
void
checkindex(char *s, char *t)
{ BaseName *b;
/* printf("xxx Check %s (%s)\n", s, t); */
for (b = bsn; b; b = b->nxt)
{
/* printf(" %s\n", b->str); */
if (strcmp(b->str, s) == 0)
{ non_fatal("do not index an array with itself (%s)", t);
break;
} }
}
void
scan_tree(Lextok *t, char *mn, char *mx)
{ char sv[512];
char tmp[32];
int oln = lineno;
if (!t) return;
lineno = t->ln;
if (t->ntyp == NAME)
{ strcat(mn, t->sym->name);
strcat(mx, t->sym->name);
if (t->lft) /* array index */
{ strcat(mn, "[]");
newbasename(mn);
strcpy(sv, mn); /* save */
strcpy(mn, ""); /* clear */
strcat(mx, "[");
scan_tree(t->lft, mn, mx); /* index */
strcat(mx, "]");
checkindex(mn, mx); /* match against basenames */
strcpy(mn, sv); /* restore */
delbasename(mn);
}
if (t->rgt) /* structure element */
{ scan_tree(t->rgt, mn, mx);
}
} else if (t->ntyp == CONST)
{ strcat(mn, "1"); /* really: t->val */
sprintf(tmp, "%d", t->val);
strcat(mx, tmp);
} else if (t->ntyp == '.')
{ strcat(mn, ".");
strcat(mx, ".");
scan_tree(t->lft, mn, mx);
} else
{ strcat(mn, "??");
strcat(mx, "??");
}
lineno = oln;
}
void
no_nested_array_refs(Lextok *n) /* a [ a[1] ] with a[1] = 1, causes trouble in pan.b */
{ char mn[512];
char mx[512];
/* printf("==================================ZAP\n"); */
bsn = (BaseName *) 0; /* start new list */
strcpy(mn, "");
strcpy(mx, "");
scan_tree(n, mn, mx);
/* printf("==> %s\n", mn); */
}
void
no_internals(Lextok *n)
{ char *sp;
if (!n->sym
|| !n->sym->name)
return;
sp = n->sym->name;
if ((strlen(sp) == strlen("_nr_pr") && strcmp(sp, "_nr_pr") == 0)
|| (strlen(sp) == strlen("_pid") && strcmp(sp, "_pid") == 0)
|| (strlen(sp) == strlen("_p") && strcmp(sp, "_p") == 0))
{ fatal("invalid assignment to %s", sp);
}
no_nested_array_refs(n);
}
|