28e9566dc5
from Ori_B: There were a small number of changes needed from the tarball on spinroot.org: - The mkfile needed to be updated - Memory.h needed to not be included - It needed to invoke /bin/cpp instead of gcc -E - It depended on `yychar`, which our yacc doesn't provide. I'm still figuring out how to use spin, but it seems to do the right thing when testing a few of the examples: % cd $home/src/Spin/Examples/ % spin -a peterson.pml % pcc pan.c -D_POSIX_SOURCE % ./6.out (Spin Version 6.4.7 -- 19 August 2017) + Partial Order Reduction Full statespace search for: never claim - (none specified) assertion violations + acceptance cycles - (not selected) invalid end states + State-vector 32 byte, depth reached 24, errors: 0 40 states, stored 27 states, matched 67 transitions (= stored+matched) 0 atomic steps hash conflicts: 0 (resolved) Stats on memory usage (in Megabytes): 0.002 equivalent memory usage for states (stored*(State-vector + overhead)) 0.292 actual memory usage for states 128.000 memory used for hash table (-w24) 0.534 memory used for DFS stack (-m10000) 128.730 total actual memory usage unreached in proctype user /tmp/Spin/Examples/peterson.pml:20, state 10, "-end-" (1 of 10 states) pan: elapsed time 1.25 seconds pan: rate 32 states/second
1166 lines
24 KiB
C
1166 lines
24 KiB
C
/***** spin: sched.c *****/
|
|
|
|
/*
|
|
* This file is part of the public release of Spin. It is subject to the
|
|
* terms in the LICENSE file that is included in this source directory.
|
|
* Tool documentation is available at http://spinroot.com
|
|
*/
|
|
|
|
#include <stdlib.h>
|
|
#include "spin.h"
|
|
#include "y.tab.h"
|
|
|
|
extern int verbose, s_trail, analyze, no_wrapup;
|
|
extern char *claimproc, *eventmap, Buf[];
|
|
extern Ordered *all_names;
|
|
extern Symbol *Fname, *context;
|
|
extern int lineno, nr_errs, dumptab, xspin, jumpsteps, columns;
|
|
extern int u_sync, Elcnt, interactive, TstOnly, cutoff;
|
|
extern short has_enabled, has_priority, has_code, replay;
|
|
extern int limited_vis, product, nclaims, old_priority_rules;
|
|
extern int old_scope_rules, scope_seq[128], scope_level, has_stdin;
|
|
|
|
extern int pc_highest(Lextok *n);
|
|
|
|
RunList *X = (RunList *) 0;
|
|
RunList *run = (RunList *) 0;
|
|
RunList *LastX = (RunList *) 0; /* previous executing proc */
|
|
ProcList *rdy = (ProcList *) 0;
|
|
Element *LastStep = ZE;
|
|
int nproc=0, nstop=0, Tval=0, Priority_Sum = 0;
|
|
int Rvous=0, depth=0, nrRdy=0, MadeChoice;
|
|
short Have_claim=0, Skip_claim=0;
|
|
|
|
static void setlocals(RunList *);
|
|
static void setparams(RunList *, ProcList *, Lextok *);
|
|
static void talk(RunList *);
|
|
|
|
void
|
|
runnable(ProcList *p, int weight, int noparams)
|
|
{ RunList *r = (RunList *) emalloc(sizeof(RunList));
|
|
|
|
r->n = p->n;
|
|
r->tn = p->tn;
|
|
r->b = p->b;
|
|
r->pid = nproc++ - nstop + Skip_claim;
|
|
r->priority = weight;
|
|
p->priority = (unsigned char) weight; /* not quite the best place of course */
|
|
|
|
if (!noparams && ((verbose&4) || (verbose&32)))
|
|
{ printf("Starting %s with pid %d",
|
|
p->n?p->n->name:"--", r->pid);
|
|
if (has_priority) printf(" priority %d", r->priority);
|
|
printf("\n");
|
|
}
|
|
if (!p->s)
|
|
fatal("parsing error, no sequence %s",
|
|
p->n?p->n->name:"--");
|
|
|
|
r->pc = huntele(p->s->frst, p->s->frst->status, -1);
|
|
r->ps = p->s;
|
|
|
|
if (p->s->last)
|
|
p->s->last->status |= ENDSTATE; /* normal end state */
|
|
|
|
r->nxt = run;
|
|
r->prov = p->prov;
|
|
if (weight < 1 || weight > 255)
|
|
{ fatal("bad process priority, valid range: 1..255", (char *) 0);
|
|
}
|
|
|
|
if (noparams) setlocals(r);
|
|
Priority_Sum += weight;
|
|
|
|
run = r;
|
|
}
|
|
|
|
ProcList *
|
|
ready(Symbol *n, Lextok *p, Sequence *s, int det, Lextok *prov, enum btypes b)
|
|
/* n=name, p=formals, s=body det=deterministic prov=provided */
|
|
{ ProcList *r = (ProcList *) emalloc(sizeof(ProcList));
|
|
Lextok *fp, *fpt; int j; extern int Npars;
|
|
|
|
r->n = n;
|
|
r->p = p;
|
|
r->s = s;
|
|
r->b = b;
|
|
r->prov = prov;
|
|
r->tn = (short) nrRdy++;
|
|
n->sc = scope_seq[scope_level]; /* scope_level should be 0 */
|
|
|
|
if (det != 0 && det != 1)
|
|
{ fprintf(stderr, "spin: bad value for det (cannot happen)\n");
|
|
}
|
|
r->det = (unsigned char) det;
|
|
r->nxt = rdy;
|
|
rdy = r;
|
|
|
|
for (fp = p, j = 0; fp; fp = fp->rgt)
|
|
for (fpt = fp->lft; fpt; fpt = fpt->rgt)
|
|
j++; /* count # of parameters */
|
|
Npars = max(Npars, j);
|
|
|
|
return rdy;
|
|
}
|
|
|
|
int
|
|
find_maxel(Symbol *s)
|
|
{ ProcList *p;
|
|
|
|
for (p = rdy; p; p = p->nxt)
|
|
if (p->n == s)
|
|
return p->s->maxel++;
|
|
return Elcnt++;
|
|
}
|
|
|
|
static void
|
|
formdump(void)
|
|
{ ProcList *p;
|
|
Lextok *f, *t;
|
|
int cnt;
|
|
|
|
for (p = rdy; p; p = p->nxt)
|
|
{ if (!p->p) continue;
|
|
cnt = -1;
|
|
for (f = p->p; f; f = f->rgt) /* types */
|
|
for (t = f->lft; t; t = t->rgt) /* formals */
|
|
{ if (t->ntyp != ',')
|
|
t->sym->Nid = cnt--; /* overload Nid */
|
|
else
|
|
t->lft->sym->Nid = cnt--;
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
announce(char *w)
|
|
{
|
|
if (columns)
|
|
{ extern char Buf[];
|
|
extern int firstrow;
|
|
firstrow = 1;
|
|
if (columns == 2)
|
|
{ sprintf(Buf, "%d:%s",
|
|
run->pid - Have_claim, run->n->name);
|
|
pstext(run->pid - Have_claim, Buf);
|
|
} else
|
|
{ printf("proc %d = %s\n",
|
|
run->pid - Have_claim, run->n->name);
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (dumptab
|
|
|| analyze
|
|
|| product
|
|
|| s_trail
|
|
|| !(verbose&4))
|
|
return;
|
|
|
|
if (w)
|
|
printf(" 0: proc - (%s) ", w);
|
|
else
|
|
whoruns(1);
|
|
printf("creates proc %2d (%s)",
|
|
run->pid - Have_claim,
|
|
run->n->name);
|
|
if (run->priority > 1)
|
|
printf(" priority %d", run->priority);
|
|
printf("\n");
|
|
}
|
|
|
|
#ifndef MAXP
|
|
#define MAXP 255 /* matches max nr of processes in verifier */
|
|
#endif
|
|
|
|
int
|
|
enable(Lextok *m)
|
|
{ ProcList *p;
|
|
Symbol *s = m->sym; /* proctype name */
|
|
Lextok *n = m->lft; /* actual parameters */
|
|
|
|
if (m->val < 1)
|
|
{ m->val = 1; /* minimum priority */
|
|
}
|
|
for (p = rdy; p; p = p->nxt)
|
|
{ if (strcmp(s->name, p->n->name) == 0)
|
|
{ if (nproc-nstop >= MAXP)
|
|
{ printf("spin: too many processes (%d max)\n", MAXP);
|
|
break;
|
|
}
|
|
runnable(p, m->val, 0);
|
|
announce((char *) 0);
|
|
setparams(run, p, n);
|
|
setlocals(run); /* after setparams */
|
|
return run->pid - Have_claim + Skip_claim; /* effective simu pid */
|
|
} }
|
|
return 0; /* process not found */
|
|
}
|
|
|
|
void
|
|
check_param_count(int i, Lextok *m)
|
|
{ ProcList *p;
|
|
Symbol *s = m->sym; /* proctype name */
|
|
Lextok *f, *t; /* formal pars */
|
|
int cnt = 0;
|
|
|
|
for (p = rdy; p; p = p->nxt)
|
|
{ if (strcmp(s->name, p->n->name) == 0)
|
|
{ if (m->lft) /* actual param list */
|
|
{ lineno = m->lft->ln;
|
|
Fname = m->lft->fn;
|
|
}
|
|
for (f = p->p; f; f = f->rgt) /* one type at a time */
|
|
for (t = f->lft; t; t = t->rgt) /* count formal params */
|
|
{ cnt++;
|
|
}
|
|
if (i != cnt)
|
|
{ printf("spin: saw %d parameters, expected %d\n", i, cnt);
|
|
non_fatal("wrong number of parameters", "");
|
|
}
|
|
break;
|
|
} }
|
|
}
|
|
|
|
void
|
|
start_claim(int n)
|
|
{ ProcList *p;
|
|
RunList *r, *q = (RunList *) 0;
|
|
|
|
for (p = rdy; p; p = p->nxt)
|
|
if (p->tn == n && p->b == N_CLAIM)
|
|
{ runnable(p, 1, 1);
|
|
goto found;
|
|
}
|
|
printf("spin: couldn't find claim %d (ignored)\n", n);
|
|
if (verbose&32)
|
|
for (p = rdy; p; p = p->nxt)
|
|
printf("\t%d = %s\n", p->tn, p->n->name);
|
|
|
|
Skip_claim = 1;
|
|
goto done;
|
|
found:
|
|
/* move claim to far end of runlist, and reassign it pid 0 */
|
|
if (columns == 2)
|
|
{ extern char Buf[];
|
|
depth = 0;
|
|
sprintf(Buf, "%d:%s", 0, p->n->name);
|
|
pstext(0, Buf);
|
|
for (r = run; r; r = r->nxt)
|
|
{ if (r->b != N_CLAIM)
|
|
{ sprintf(Buf, "%d:%s", r->pid+1, r->n->name);
|
|
pstext(r->pid+1, Buf);
|
|
} } }
|
|
|
|
if (run->pid == 0) return; /* it is the first process started */
|
|
|
|
q = run; run = run->nxt;
|
|
q->pid = 0; q->nxt = (RunList *) 0; /* remove */
|
|
done:
|
|
Have_claim = 1;
|
|
for (r = run; r; r = r->nxt)
|
|
{ r->pid = r->pid+Have_claim; /* adjust */
|
|
if (!r->nxt)
|
|
{ r->nxt = q;
|
|
break;
|
|
} }
|
|
}
|
|
|
|
int
|
|
f_pid(char *n)
|
|
{ RunList *r;
|
|
int rval = -1;
|
|
|
|
for (r = run; r; r = r->nxt)
|
|
if (strcmp(n, r->n->name) == 0)
|
|
{ if (rval >= 0)
|
|
{ printf("spin: remote ref to proctype %s, ", n);
|
|
printf("has more than one match: %d and %d\n",
|
|
rval, r->pid);
|
|
} else
|
|
rval = r->pid;
|
|
}
|
|
return rval;
|
|
}
|
|
|
|
void
|
|
wrapup(int fini)
|
|
{
|
|
limited_vis = 0;
|
|
if (columns)
|
|
{ extern void putpostlude(void);
|
|
if (columns == 2) putpostlude();
|
|
if (!no_wrapup)
|
|
printf("-------------\nfinal state:\n-------------\n");
|
|
}
|
|
if (no_wrapup)
|
|
goto short_cut;
|
|
if (nproc != nstop)
|
|
{ int ov = verbose;
|
|
printf("#processes: %d\n", nproc-nstop - Have_claim + Skip_claim);
|
|
verbose &= ~4;
|
|
dumpglobals();
|
|
verbose = ov;
|
|
verbose &= ~1; /* no more globals */
|
|
verbose |= 32; /* add process states */
|
|
for (X = run; X; X = X->nxt)
|
|
talk(X);
|
|
verbose = ov; /* restore */
|
|
}
|
|
printf("%d process%s created\n",
|
|
nproc - Have_claim + Skip_claim,
|
|
(xspin || nproc!=1)?"es":"");
|
|
short_cut:
|
|
if (s_trail || xspin) alldone(0); /* avoid an abort from xspin */
|
|
if (fini) alldone(1);
|
|
}
|
|
|
|
static char is_blocked[256];
|
|
|
|
static int
|
|
p_blocked(int p)
|
|
{ int i, j;
|
|
|
|
is_blocked[p%256] = 1;
|
|
for (i = j = 0; i < nproc - nstop; i++)
|
|
j += is_blocked[i];
|
|
if (j >= nproc - nstop)
|
|
{ memset(is_blocked, 0, 256);
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static Element *
|
|
silent_moves(Element *e)
|
|
{ Element *f;
|
|
|
|
if (e->n)
|
|
switch (e->n->ntyp) {
|
|
case GOTO:
|
|
if (Rvous) break;
|
|
f = get_lab(e->n, 1);
|
|
cross_dsteps(e->n, f->n);
|
|
return f; /* guard against goto cycles */
|
|
case UNLESS:
|
|
return silent_moves(e->sub->this->frst);
|
|
case NON_ATOMIC:
|
|
case ATOMIC:
|
|
case D_STEP:
|
|
e->n->sl->this->last->nxt = e->nxt;
|
|
return silent_moves(e->n->sl->this->frst);
|
|
case '.':
|
|
return silent_moves(e->nxt);
|
|
}
|
|
return e;
|
|
}
|
|
|
|
static int
|
|
x_can_run(void) /* the currently selected process in X can run */
|
|
{
|
|
if (X->prov && !eval(X->prov))
|
|
{
|
|
if (0) printf("pid %d cannot run: not provided\n", X->pid);
|
|
return 0;
|
|
}
|
|
if (has_priority && !old_priority_rules)
|
|
{ Lextok *n = nn(ZN, CONST, ZN, ZN);
|
|
n->val = X->pid;
|
|
if (0) printf("pid %d %s run (priority)\n", X->pid, pc_highest(n)?"can":"cannot");
|
|
return pc_highest(n);
|
|
}
|
|
if (0) printf("pid %d can run\n", X->pid);
|
|
return 1;
|
|
}
|
|
|
|
static RunList *
|
|
pickproc(RunList *Y)
|
|
{ SeqList *z; Element *has_else;
|
|
short Choices[256];
|
|
int j, k, nr_else = 0;
|
|
|
|
if (nproc <= nstop+1)
|
|
{ X = run;
|
|
return NULL;
|
|
}
|
|
if (!interactive || depth < jumpsteps)
|
|
{ if (has_priority && !old_priority_rules) /* new 6.3.2 */
|
|
{ j = Rand()%(nproc-nstop);
|
|
for (X = run; X; X = X->nxt)
|
|
{ if (j-- <= 0)
|
|
break;
|
|
}
|
|
if (X == NULL)
|
|
{ fatal("unexpected, pickproc", (char *)0);
|
|
}
|
|
j = nproc - nstop;
|
|
while (j-- > 0)
|
|
{ if (x_can_run())
|
|
{ Y = X;
|
|
break;
|
|
}
|
|
X = (X->nxt)?X->nxt:run;
|
|
}
|
|
return Y;
|
|
}
|
|
if (Priority_Sum < nproc-nstop)
|
|
fatal("cannot happen - weights", (char *)0);
|
|
j = (int) Rand()%Priority_Sum;
|
|
|
|
while (j - X->priority >= 0)
|
|
{ j -= X->priority;
|
|
Y = X;
|
|
X = X->nxt;
|
|
if (!X) { Y = NULL; X = run; }
|
|
}
|
|
|
|
} else
|
|
{ int only_choice = -1;
|
|
int no_choice = 0, proc_no_ch, proc_k;
|
|
|
|
Tval = 0; /* new 4.2.6 */
|
|
try_again: printf("Select a statement\n");
|
|
try_more: for (X = run, k = 1; X; X = X->nxt)
|
|
{ if (X->pid > 255) break;
|
|
|
|
Choices[X->pid] = (short) k;
|
|
|
|
if (!X->pc || !x_can_run())
|
|
{ if (X == run)
|
|
Choices[X->pid] = 0;
|
|
continue;
|
|
}
|
|
X->pc = silent_moves(X->pc);
|
|
if (!X->pc->sub && X->pc->n)
|
|
{ int unex;
|
|
unex = !Enabled0(X->pc);
|
|
if (unex)
|
|
no_choice++;
|
|
else
|
|
only_choice = k;
|
|
if (!xspin && unex && !(verbose&32))
|
|
{ k++;
|
|
continue;
|
|
}
|
|
printf("\tchoice %d: ", k++);
|
|
p_talk(X->pc, 0);
|
|
if (unex)
|
|
printf(" unexecutable,");
|
|
printf(" [");
|
|
comment(stdout, X->pc->n, 0);
|
|
if (X->pc->esc) printf(" + Escape");
|
|
printf("]\n");
|
|
} else {
|
|
has_else = ZE;
|
|
proc_no_ch = no_choice;
|
|
proc_k = k;
|
|
for (z = X->pc->sub, j=0; z; z = z->nxt)
|
|
{ Element *y = silent_moves(z->this->frst);
|
|
int unex;
|
|
if (!y) continue;
|
|
|
|
if (y->n->ntyp == ELSE)
|
|
{ has_else = (Rvous)?ZE:y;
|
|
nr_else = k++;
|
|
continue;
|
|
}
|
|
|
|
unex = !Enabled0(y);
|
|
if (unex)
|
|
no_choice++;
|
|
else
|
|
only_choice = k;
|
|
if (!xspin && unex && !(verbose&32))
|
|
{ k++;
|
|
continue;
|
|
}
|
|
printf("\tchoice %d: ", k++);
|
|
p_talk(X->pc, 0);
|
|
if (unex)
|
|
printf(" unexecutable,");
|
|
printf(" [");
|
|
comment(stdout, y->n, 0);
|
|
printf("]\n");
|
|
}
|
|
if (has_else)
|
|
{ if (no_choice-proc_no_ch >= (k-proc_k)-1)
|
|
{ only_choice = nr_else;
|
|
printf("\tchoice %d: ", nr_else);
|
|
p_talk(X->pc, 0);
|
|
printf(" [else]\n");
|
|
} else
|
|
{ no_choice++;
|
|
printf("\tchoice %d: ", nr_else);
|
|
p_talk(X->pc, 0);
|
|
printf(" unexecutable, [else]\n");
|
|
} }
|
|
} }
|
|
X = run;
|
|
if (k - no_choice < 2 && Tval == 0)
|
|
{ Tval = 1;
|
|
no_choice = 0; only_choice = -1;
|
|
goto try_more;
|
|
}
|
|
if (xspin)
|
|
printf("Make Selection %d\n\n", k-1);
|
|
else
|
|
{ if (k - no_choice < 2)
|
|
{ printf("no executable choices\n");
|
|
alldone(0);
|
|
}
|
|
printf("Select [1-%d]: ", k-1);
|
|
}
|
|
if (!xspin && k - no_choice == 2)
|
|
{ printf("%d\n", only_choice);
|
|
j = only_choice;
|
|
} else
|
|
{ char buf[256];
|
|
fflush(stdout);
|
|
if (scanf("%64s", buf) == 0)
|
|
{ printf("\tno input\n");
|
|
goto try_again;
|
|
}
|
|
j = -1;
|
|
if (isdigit((int) buf[0]))
|
|
j = atoi(buf);
|
|
else
|
|
{ if (buf[0] == 'q')
|
|
alldone(0);
|
|
}
|
|
if (j < 1 || j >= k)
|
|
{ printf("\tchoice is outside range\n");
|
|
goto try_again;
|
|
} }
|
|
MadeChoice = 0;
|
|
Y = NULL;
|
|
for (X = run; X; Y = X, X = X->nxt)
|
|
{ if (!X->nxt
|
|
|| X->nxt->pid > 255
|
|
|| j < Choices[X->nxt->pid])
|
|
{
|
|
MadeChoice = 1+j-Choices[X->pid];
|
|
break;
|
|
} }
|
|
}
|
|
return Y;
|
|
}
|
|
|
|
void
|
|
multi_claims(void)
|
|
{ ProcList *p, *q = NULL;
|
|
|
|
if (nclaims > 1)
|
|
{ printf(" the model contains %d never claims:", nclaims);
|
|
for (p = rdy; p; p = p->nxt)
|
|
{ if (p->b == N_CLAIM)
|
|
{ printf("%s%s", q?", ":" ", p->n->name);
|
|
q = p;
|
|
} }
|
|
printf("\n");
|
|
printf(" only one claim is used in a verification run\n");
|
|
printf(" choose which one with ./pan -a -N name (defaults to -N %s)\n",
|
|
q?q->n->name:"--");
|
|
printf(" or use e.g.: spin -search -ltl %s %s\n",
|
|
q?q->n->name:"--", Fname?Fname->name:"filename");
|
|
}
|
|
}
|
|
|
|
void
|
|
sched(void)
|
|
{ Element *e;
|
|
RunList *Y = NULL; /* previous process in run queue */
|
|
RunList *oX;
|
|
int go, notbeyond = 0;
|
|
#ifdef PC
|
|
int bufmax = 100;
|
|
#endif
|
|
if (dumptab)
|
|
{ formdump();
|
|
symdump();
|
|
dumplabels();
|
|
return;
|
|
}
|
|
if (has_code && !analyze)
|
|
{ printf("spin: warning: c_code fragments remain uninterpreted\n");
|
|
printf(" in random simulations with spin; use ./pan -r instead\n");
|
|
}
|
|
|
|
if (has_enabled && u_sync > 0)
|
|
{ printf("spin: error, cannot use 'enabled()' in ");
|
|
printf("models with synchronous channels.\n");
|
|
nr_errs++;
|
|
}
|
|
if (product)
|
|
{ sync_product();
|
|
alldone(0);
|
|
}
|
|
if (analyze && (!replay || has_code))
|
|
{ gensrc();
|
|
multi_claims();
|
|
return;
|
|
}
|
|
if (replay && !has_code)
|
|
{ return;
|
|
}
|
|
if (s_trail)
|
|
{ match_trail();
|
|
return;
|
|
}
|
|
|
|
if (claimproc)
|
|
printf("warning: never claim not used in random simulation\n");
|
|
if (eventmap)
|
|
printf("warning: trace assertion not used in random simulation\n");
|
|
|
|
X = run;
|
|
Y = pickproc(Y);
|
|
|
|
while (X)
|
|
{ context = X->n;
|
|
if (X->pc && X->pc->n)
|
|
{ lineno = X->pc->n->ln;
|
|
Fname = X->pc->n->fn;
|
|
}
|
|
if (cutoff > 0 && depth >= cutoff)
|
|
{ printf("-------------\n");
|
|
printf("depth-limit (-u%d steps) reached\n", cutoff);
|
|
break;
|
|
}
|
|
#ifdef PC
|
|
if (xspin && !interactive && --bufmax <= 0)
|
|
{ int c; /* avoid buffer overflow on pc's */
|
|
printf("spin: type return to proceed\n");
|
|
fflush(stdout);
|
|
c = getc(stdin);
|
|
if (c == 'q') wrapup(0);
|
|
bufmax = 100;
|
|
}
|
|
#endif
|
|
depth++; LastStep = ZE;
|
|
oX = X; /* a rendezvous could change it */
|
|
go = 1;
|
|
if (X->pc
|
|
&& !(X->pc->status & D_ATOM)
|
|
&& !x_can_run())
|
|
{ if (!xspin && ((verbose&32) || (verbose&4)))
|
|
{ p_talk(X->pc, 1);
|
|
printf("\t<<Not Enabled>>\n");
|
|
}
|
|
go = 0;
|
|
}
|
|
if (go && (e = eval_sub(X->pc)))
|
|
{ if (depth >= jumpsteps
|
|
&& ((verbose&32) || (verbose&4)))
|
|
{ if (X == oX)
|
|
if (!(e->status & D_ATOM) || (verbose&32)) /* no talking in d_steps */
|
|
{ if (!LastStep) LastStep = X->pc;
|
|
/* A. Tanaka, changed order */
|
|
p_talk(LastStep, 1);
|
|
printf(" [");
|
|
comment(stdout, LastStep->n, 0);
|
|
printf("]\n");
|
|
}
|
|
if (verbose&1) dumpglobals();
|
|
if (verbose&2) dumplocal(X);
|
|
|
|
if (!(e->status & D_ATOM))
|
|
if (xspin)
|
|
printf("\n");
|
|
}
|
|
if (oX != X
|
|
|| (X->pc->status & (ATOM|D_ATOM))) /* new 5.0 */
|
|
{ e = silent_moves(e);
|
|
notbeyond = 0;
|
|
}
|
|
oX->pc = e; LastX = X;
|
|
|
|
if (!interactive) Tval = 0;
|
|
memset(is_blocked, 0, 256);
|
|
|
|
if (X->pc && (X->pc->status & (ATOM|L_ATOM))
|
|
&& (notbeyond == 0 || oX != X))
|
|
{ if ((X->pc->status & L_ATOM))
|
|
notbeyond = 1;
|
|
continue; /* no process switch */
|
|
}
|
|
} else
|
|
{ depth--;
|
|
if (oX->pc && (oX->pc->status & D_ATOM))
|
|
{ non_fatal("stmnt in d_step blocks", (char *)0);
|
|
}
|
|
if (X->pc
|
|
&& X->pc->n
|
|
&& X->pc->n->ntyp == '@'
|
|
&& X->pid == (nproc-nstop-1))
|
|
{ if (X != run && Y != NULL)
|
|
Y->nxt = X->nxt;
|
|
else
|
|
run = X->nxt;
|
|
nstop++;
|
|
Priority_Sum -= X->priority;
|
|
if (verbose&4)
|
|
{ whoruns(1);
|
|
dotag(stdout, "terminates\n");
|
|
}
|
|
LastX = X;
|
|
if (!interactive) Tval = 0;
|
|
if (nproc == nstop) break;
|
|
memset(is_blocked, 0, 256);
|
|
/* proc X is no longer in runlist */
|
|
X = (X->nxt) ? X->nxt : run;
|
|
} else
|
|
{ if (p_blocked(X->pid))
|
|
{ if (Tval && !has_stdin)
|
|
{ break;
|
|
}
|
|
if (!Tval && depth >= jumpsteps)
|
|
{ oX = X;
|
|
X = (RunList *) 0; /* to suppress indent */
|
|
dotag(stdout, "timeout\n");
|
|
X = oX;
|
|
Tval = 1;
|
|
} } } }
|
|
|
|
if (!run || !X) break; /* new 5.0 */
|
|
|
|
Y = pickproc(X);
|
|
notbeyond = 0;
|
|
}
|
|
context = ZS;
|
|
wrapup(0);
|
|
}
|
|
|
|
int
|
|
complete_rendez(void)
|
|
{ RunList *orun = X, *tmp;
|
|
Element *s_was = LastStep;
|
|
Element *e;
|
|
int j, ointer = interactive;
|
|
|
|
if (s_trail)
|
|
return 1;
|
|
if (orun->pc->status & D_ATOM)
|
|
fatal("rv-attempt in d_step sequence", (char *)0);
|
|
Rvous = 1;
|
|
interactive = 0;
|
|
|
|
j = (int) Rand()%Priority_Sum; /* randomize start point */
|
|
X = run;
|
|
while (j - X->priority >= 0)
|
|
{ j -= X->priority;
|
|
X = X->nxt;
|
|
if (!X) X = run;
|
|
}
|
|
for (j = nproc - nstop; j > 0; j--)
|
|
{ if (X != orun
|
|
&& (!X->prov || eval(X->prov))
|
|
&& (e = eval_sub(X->pc)))
|
|
{ if (TstOnly)
|
|
{ X = orun;
|
|
Rvous = 0;
|
|
goto out;
|
|
}
|
|
if ((verbose&32) || (verbose&4))
|
|
{ tmp = orun; orun = X; X = tmp;
|
|
if (!s_was) s_was = X->pc;
|
|
p_talk(s_was, 1);
|
|
printf(" [");
|
|
comment(stdout, s_was->n, 0);
|
|
printf("]\n");
|
|
tmp = orun; /* orun = X; */ X = tmp;
|
|
if (!LastStep) LastStep = X->pc;
|
|
p_talk(LastStep, 1);
|
|
printf(" [");
|
|
comment(stdout, LastStep->n, 0);
|
|
printf("]\n");
|
|
}
|
|
Rvous = 0; /* before silent_moves */
|
|
X->pc = silent_moves(e);
|
|
out: interactive = ointer;
|
|
return 1;
|
|
}
|
|
|
|
X = X->nxt;
|
|
if (!X) X = run;
|
|
}
|
|
Rvous = 0;
|
|
X = orun;
|
|
interactive = ointer;
|
|
return 0;
|
|
}
|
|
|
|
/***** Runtime - Local Variables *****/
|
|
|
|
static void
|
|
addsymbol(RunList *r, Symbol *s)
|
|
{ Symbol *t;
|
|
int i;
|
|
|
|
for (t = r->symtab; t; t = t->next)
|
|
if (strcmp(t->name, s->name) == 0
|
|
&& (old_scope_rules
|
|
|| strcmp((const char *)t->bscp, (const char *)s->bscp) == 0))
|
|
return; /* it's already there */
|
|
|
|
t = (Symbol *) emalloc(sizeof(Symbol));
|
|
t->name = s->name;
|
|
t->type = s->type;
|
|
t->hidden = s->hidden;
|
|
t->isarray = s->isarray;
|
|
t->nbits = s->nbits;
|
|
t->nel = s->nel;
|
|
t->ini = s->ini;
|
|
t->setat = depth;
|
|
t->context = r->n;
|
|
|
|
t->bscp = (unsigned char *) emalloc(strlen((const char *)s->bscp)+1);
|
|
strcpy((char *)t->bscp, (const char *)s->bscp);
|
|
|
|
if (s->type != STRUCT)
|
|
{ if (s->val) /* if already initialized, copy info */
|
|
{ t->val = (int *) emalloc(s->nel*sizeof(int));
|
|
for (i = 0; i < s->nel; i++)
|
|
t->val[i] = s->val[i];
|
|
} else
|
|
{ (void) checkvar(t, 0); /* initialize it */
|
|
}
|
|
} else
|
|
{ if (s->Sval)
|
|
fatal("saw preinitialized struct %s", s->name);
|
|
t->Slst = s->Slst;
|
|
t->Snm = s->Snm;
|
|
t->owner = s->owner;
|
|
/* t->context = r->n; */
|
|
}
|
|
t->next = r->symtab; /* add it */
|
|
r->symtab = t;
|
|
}
|
|
|
|
static void
|
|
setlocals(RunList *r)
|
|
{ Ordered *walk;
|
|
Symbol *sp;
|
|
RunList *oX = X;
|
|
|
|
X = r;
|
|
for (walk = all_names; walk; walk = walk->next)
|
|
{ sp = walk->entry;
|
|
if (sp
|
|
&& sp->context
|
|
&& strcmp(sp->context->name, r->n->name) == 0
|
|
&& sp->Nid >= 0
|
|
&& (sp->type == UNSIGNED
|
|
|| sp->type == BIT
|
|
|| sp->type == MTYPE
|
|
|| sp->type == BYTE
|
|
|| sp->type == CHAN
|
|
|| sp->type == SHORT
|
|
|| sp->type == INT
|
|
|| sp->type == STRUCT))
|
|
{ if (!findloc(sp))
|
|
non_fatal("setlocals: cannot happen '%s'",
|
|
sp->name);
|
|
}
|
|
}
|
|
X = oX;
|
|
}
|
|
|
|
static void
|
|
oneparam(RunList *r, Lextok *t, Lextok *a, ProcList *p)
|
|
{ int k; int at, ft;
|
|
RunList *oX = X;
|
|
|
|
if (!a)
|
|
fatal("missing actual parameters: '%s'", p->n->name);
|
|
if (t->sym->nel > 1 || t->sym->isarray)
|
|
fatal("array in parameter list, %s", t->sym->name);
|
|
k = eval(a->lft);
|
|
|
|
at = Sym_typ(a->lft);
|
|
X = r; /* switch context */
|
|
ft = Sym_typ(t);
|
|
|
|
if (at != ft && (at == CHAN || ft == CHAN))
|
|
{ char buf[256], tag1[64], tag2[64];
|
|
(void) sputtype(tag1, ft);
|
|
(void) sputtype(tag2, at);
|
|
sprintf(buf, "type-clash in params of %s(..), (%s<-> %s)",
|
|
p->n->name, tag1, tag2);
|
|
non_fatal("%s", buf);
|
|
}
|
|
t->ntyp = NAME;
|
|
addsymbol(r, t->sym);
|
|
(void) setval(t, k);
|
|
|
|
X = oX;
|
|
}
|
|
|
|
static void
|
|
setparams(RunList *r, ProcList *p, Lextok *q)
|
|
{ Lextok *f, *a; /* formal and actual pars */
|
|
Lextok *t; /* list of pars of 1 type */
|
|
|
|
if (q)
|
|
{ lineno = q->ln;
|
|
Fname = q->fn;
|
|
}
|
|
for (f = p->p, a = q; f; f = f->rgt) /* one type at a time */
|
|
for (t = f->lft; t; t = t->rgt, a = (a)?a->rgt:a)
|
|
{ if (t->ntyp != ',')
|
|
oneparam(r, t, a, p); /* plain var */
|
|
else
|
|
oneparam(r, t->lft, a, p); /* expanded struct */
|
|
}
|
|
}
|
|
|
|
Symbol *
|
|
findloc(Symbol *s)
|
|
{ Symbol *r;
|
|
|
|
if (!X)
|
|
{ /* fatal("error, cannot eval '%s' (no proc)", s->name); */
|
|
return ZS;
|
|
}
|
|
for (r = X->symtab; r; r = r->next)
|
|
{ if (strcmp(r->name, s->name) == 0
|
|
&& (old_scope_rules
|
|
|| strcmp((const char *)r->bscp, (const char *)s->bscp) == 0))
|
|
{ break;
|
|
} }
|
|
if (!r)
|
|
{ addsymbol(X, s);
|
|
r = X->symtab;
|
|
}
|
|
return r;
|
|
}
|
|
|
|
int
|
|
in_bound(Symbol *r, int n)
|
|
{
|
|
if (!r) return 0;
|
|
|
|
if (n >= r->nel || n < 0)
|
|
{ printf("spin: indexing %s[%d] - size is %d\n",
|
|
r->name, n, r->nel);
|
|
non_fatal("indexing array \'%s\'", r->name);
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
int
|
|
getlocal(Lextok *sn)
|
|
{ Symbol *r, *s = sn->sym;
|
|
int n = eval(sn->lft);
|
|
|
|
r = findloc(s);
|
|
if (r && r->type == STRUCT)
|
|
return Rval_struct(sn, r, 1); /* 1 = check init */
|
|
if (in_bound(r, n))
|
|
return cast_val(r->type, r->val[n], r->nbits);
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
setlocal(Lextok *p, int m)
|
|
{ Symbol *r = findloc(p->sym);
|
|
int n = eval(p->lft);
|
|
|
|
if (in_bound(r, n))
|
|
{ if (r->type == STRUCT)
|
|
(void) Lval_struct(p, r, 1, m); /* 1 = check init */
|
|
else
|
|
{
|
|
#if 0
|
|
if (r->nbits > 0)
|
|
m = (m & ((1<<r->nbits)-1));
|
|
r->val[n] = m;
|
|
#else
|
|
r->val[n] = cast_val(r->type, m, r->nbits);
|
|
#endif
|
|
r->setat = depth;
|
|
} }
|
|
|
|
return 1;
|
|
}
|
|
|
|
void
|
|
whoruns(int lnr)
|
|
{ if (!X) return;
|
|
|
|
if (lnr) printf("%3d: ", depth);
|
|
printf("proc ");
|
|
if (Have_claim && X->pid == 0)
|
|
printf(" -");
|
|
else
|
|
printf("%2d", X->pid - Have_claim);
|
|
if (old_priority_rules)
|
|
{ printf(" (%s) ", X->n->name);
|
|
} else
|
|
{ printf(" (%s:%d) ", X->n->name, X->priority);
|
|
}
|
|
}
|
|
|
|
static void
|
|
talk(RunList *r)
|
|
{
|
|
if ((verbose&32) || (verbose&4))
|
|
{ p_talk(r->pc, 1);
|
|
printf("\n");
|
|
if (verbose&1) dumpglobals();
|
|
if (verbose&2) dumplocal(r);
|
|
}
|
|
}
|
|
|
|
void
|
|
p_talk(Element *e, int lnr)
|
|
{ static int lastnever = -1;
|
|
static char nbuf[128];
|
|
int newnever = -1;
|
|
|
|
if (e && e->n)
|
|
newnever = e->n->ln;
|
|
|
|
if (Have_claim && X && X->pid == 0
|
|
&& lastnever != newnever && e)
|
|
{ if (xspin)
|
|
{ printf("MSC: ~G line %d\n", newnever);
|
|
#if 0
|
|
printf("%3d: proc - (NEVER) line %d \"never\" ",
|
|
depth, newnever);
|
|
printf("(state 0)\t[printf('MSC: never\\\\n')]\n");
|
|
} else
|
|
{ printf("%3d: proc - (NEVER) line %d \"never\"\n",
|
|
depth, newnever);
|
|
#endif
|
|
}
|
|
lastnever = newnever;
|
|
}
|
|
|
|
whoruns(lnr);
|
|
if (e)
|
|
{ if (e->n)
|
|
{ char *ptr = e->n->fn->name;
|
|
char *qtr = nbuf;
|
|
while (*ptr != '\0')
|
|
{ if (*ptr != '"')
|
|
{ *qtr++ = *ptr;
|
|
}
|
|
ptr++;
|
|
}
|
|
*qtr = '\0';
|
|
} else
|
|
{ strcpy(nbuf, "-");
|
|
}
|
|
printf("%s:%d (state %d)",
|
|
nbuf,
|
|
e->n?e->n->ln:-1,
|
|
e->seqno);
|
|
if (!xspin
|
|
&& ((e->status&ENDSTATE) || has_lab(e, 2))) /* 2=end */
|
|
{ printf(" <valid end state>");
|
|
}
|
|
}
|
|
}
|
|
|
|
int
|
|
remotelab(Lextok *n)
|
|
{ int i;
|
|
|
|
lineno = n->ln;
|
|
Fname = n->fn;
|
|
if (n->sym->type != 0 && n->sym->type != LABEL)
|
|
{ printf("spin: error, type: %d\n", n->sym->type);
|
|
fatal("not a labelname: '%s'", n->sym->name);
|
|
}
|
|
if (n->indstep >= 0)
|
|
{ fatal("remote ref to label '%s' inside d_step",
|
|
n->sym->name);
|
|
}
|
|
if ((i = find_lab(n->sym, n->lft->sym, 1)) == 0) /* remotelab */
|
|
{ fatal("unknown labelname: %s", n->sym->name);
|
|
}
|
|
return i;
|
|
}
|
|
|
|
int
|
|
remotevar(Lextok *n)
|
|
{ int prno, i, added=0;
|
|
RunList *Y, *oX;
|
|
Lextok *onl;
|
|
Symbol *os;
|
|
|
|
lineno = n->ln;
|
|
Fname = n->fn;
|
|
|
|
if (!n->lft->lft)
|
|
prno = f_pid(n->lft->sym->name);
|
|
else
|
|
{ prno = eval(n->lft->lft); /* pid - can cause recursive call */
|
|
#if 0
|
|
if (n->lft->lft->ntyp == CONST) /* user-guessed pid */
|
|
#endif
|
|
{ prno += Have_claim;
|
|
added = Have_claim;
|
|
} }
|
|
|
|
if (prno < 0)
|
|
{ return 0; /* non-existing process */
|
|
}
|
|
#if 0
|
|
i = nproc - nstop;
|
|
for (Y = run; Y; Y = Y->nxt)
|
|
{ --i;
|
|
printf(" %s: i=%d, prno=%d, ->pid=%d\n", Y->n->name, i, prno, Y->pid);
|
|
}
|
|
#endif
|
|
i = nproc - nstop + Skip_claim; /* 6.0: added Skip_claim */
|
|
for (Y = run; Y; Y = Y->nxt)
|
|
if (--i == prno)
|
|
{ if (strcmp(Y->n->name, n->lft->sym->name) != 0)
|
|
{ printf("spin: remote reference error on '%s[%d]'\n",
|
|
n->lft->sym->name, prno-added);
|
|
non_fatal("refers to wrong proctype '%s'", Y->n->name);
|
|
}
|
|
if (strcmp(n->sym->name, "_p") == 0)
|
|
{ if (Y->pc)
|
|
{ return Y->pc->seqno;
|
|
}
|
|
/* harmless, can only happen with -t */
|
|
return 0;
|
|
}
|
|
|
|
/* check remote variables */
|
|
oX = X;
|
|
X = Y;
|
|
|
|
onl = n->lft;
|
|
n->lft = n->rgt;
|
|
|
|
os = n->sym;
|
|
if (!n->sym->context)
|
|
{ n->sym->context = Y->n;
|
|
}
|
|
{ int rs = old_scope_rules;
|
|
old_scope_rules = 1; /* 6.4.0 */
|
|
n->sym = findloc(n->sym);
|
|
old_scope_rules = rs;
|
|
}
|
|
i = getval(n);
|
|
|
|
n->sym = os;
|
|
n->lft = onl;
|
|
X = oX;
|
|
return i;
|
|
}
|
|
printf("remote ref: %s[%d] ", n->lft->sym->name, prno-added);
|
|
non_fatal("%s not found", n->sym->name);
|
|
printf("have only:\n");
|
|
i = nproc - nstop - 1;
|
|
for (Y = run; Y; Y = Y->nxt, i--)
|
|
if (!strcmp(Y->n->name, n->lft->sym->name))
|
|
printf("\t%d\t%s\n", i, Y->n->name);
|
|
|
|
return 0;
|
|
}
|