1 /***** spin: sched.c *****/
3 /* Copyright (c) 1989-2003 by Lucent Technologies, Bell Laboratories. */
4 /* All Rights Reserved. This software is for educational purposes only. */
5 /* No guarantee whatsoever is expressed or implied by the distribution of */
6 /* this code. Permission is given to distribute this code provided that */
7 /* this introductory message is not removed and no monies are exchanged. */
8 /* Software written by Gerard J. Holzmann. For tool documentation see: */
9 /* http://spinroot.com/ */
10 /* Send all bug-reports and/or questions to: bugs@spinroot.com */
16 extern int verbose, s_trail, analyze, no_wrapup;
17 extern char *claimproc, *eventmap, Buf[];
18 extern Ordered *all_names;
19 extern Symbol *Fname, *context;
20 extern int lineno, nr_errs, dumptab, xspin, jumpsteps, columns;
21 extern int u_sync, Elcnt, interactive, TstOnly, cutoff;
22 extern short has_enabled;
23 extern int limited_vis;
25 RunList *X = (RunList *) 0;
26 RunList *run = (RunList *) 0;
27 RunList *LastX = (RunList *) 0; /* previous executing proc */
28 ProcList *rdy = (ProcList *) 0;
29 Element *LastStep = ZE;
30 int nproc=0, nstop=0, Tval=0;
31 int Rvous=0, depth=0, nrRdy=0, MadeChoice;
32 short Have_claim=0, Skip_claim=0;
34 static int Priority_Sum = 0;
35 static void setlocals(RunList *);
36 static void setparams(RunList *, ProcList *, Lextok *);
37 static void talk(RunList *);
40 runnable(ProcList *p, int weight, int noparams)
41 { RunList *r = (RunList *) emalloc(sizeof(RunList));
45 r->pid = nproc++ - nstop + Skip_claim;
47 if ((verbose&4) || (verbose&32))
48 printf("Starting %s with pid %d\n", p->n->name, r->pid);
51 fatal("parsing error, no sequence %s", p->n?p->n->name:"--");
53 r->pc = huntele(p->s->frst, p->s->frst->status, -1);
57 p->s->last->status |= ENDSTATE; /* normal end state */
62 if (noparams) setlocals(r);
63 Priority_Sum += weight;
68 ready(Symbol *n, Lextok *p, Sequence *s, int det, Lextok *prov)
69 /* n=name, p=formals, s=body det=deterministic prov=provided */
70 { ProcList *r = (ProcList *) emalloc(sizeof(ProcList));
71 Lextok *fp, *fpt; int j; extern int Npars;
82 for (fp = p, j = 0; fp; fp = fp->rgt)
83 for (fpt = fp->lft; fpt; fpt = fpt->rgt)
84 j++; /* count # of parameters */
85 Npars = max(Npars, j);
94 for (p = rdy; p; p = p->nxt)
106 for (p = rdy; p; p = p->nxt)
107 { if (!p->p) continue;
109 for (f = p->p; f; f = f->rgt) /* types */
110 for (t = f->lft; t; t = t->rgt) /* formals */
111 { if (t->ntyp != ',')
112 t->sym->Nid = cnt--; /* overload Nid */
114 t->lft->sym->Nid = cnt--;
127 { sprintf(Buf, "%d:%s",
128 run->pid - Have_claim, run->n->name);
129 pstext(run->pid - Have_claim, Buf);
131 printf("proc %d = %s\n",
132 run->pid - Have_claim, run->n->name);
143 printf(" 0: proc - (%s) ", w);
146 printf("creates proc %2d (%s)",
147 run->pid - Have_claim,
149 if (run->priority > 1)
150 printf(" priority %d", run->priority);
155 #define MAXP 255 /* matches max nr of processes in verifier */
161 Symbol *s = m->sym; /* proctype name */
162 Lextok *n = m->lft; /* actual parameters */
164 if (m->val < 1) m->val = 1; /* minimum priority */
165 for (p = rdy; p; p = p->nxt)
166 if (strcmp(s->name, p->n->name) == 0)
167 { if (nproc-nstop >= MAXP)
168 { printf("spin: too many processes (%d max)\n", MAXP);
171 runnable(p, m->val, 0);
172 announce((char *) 0);
173 setparams(run, p, n);
174 setlocals(run); /* after setparams */
175 return run->pid - Have_claim + Skip_claim; /* effective simu pid */
177 return 0; /* process not found */
181 check_param_count(int i, Lextok *m)
183 Symbol *s = m->sym; /* proctype name */
184 Lextok *f, *t; /* formal pars */
187 for (p = rdy; p; p = p->nxt)
188 { if (strcmp(s->name, p->n->name) == 0)
189 { if (m->lft) /* actual param list */
190 { lineno = m->lft->ln;
193 for (f = p->p; f; f = f->rgt) /* one type at a time */
194 for (t = f->lft; t; t = t->rgt) /* count formal params */
198 { printf("spin: saw %d parameters, expected %d\n", i, cnt);
199 non_fatal("wrong number of parameters", "");
208 RunList *r, *q = (RunList *) 0;
210 for (p = rdy; p; p = p->nxt)
212 && strcmp(p->n->name, ":never:") == 0)
216 printf("spin: couldn't find claim (ignored)\n");
220 /* move claim to far end of runlist, and reassign it pid 0 */
223 pstext(0, "0::never:");
224 for (r = run; r; r = r->nxt)
225 { if (!strcmp(r->n->name, ":never:"))
227 sprintf(Buf, "%d:%s",
228 r->pid+1, r->n->name);
229 pstext(r->pid+1, Buf);
232 if (run->pid == 0) return; /* it is the first process started */
234 q = run; run = run->nxt;
235 q->pid = 0; q->nxt = (RunList *) 0; /* remove */
238 for (r = run; r; r = r->nxt)
239 { r->pid = r->pid+Have_claim; /* adjust */
251 for (r = run; r; r = r->nxt)
252 if (strcmp(n, r->n->name) == 0)
254 { printf("spin: remote ref to proctype %s, ", n);
255 printf("has more than one match: %d and %d\n",
268 { extern void putpostlude(void);
269 if (columns == 2) putpostlude();
271 printf("-------------\nfinal state:\n-------------\n");
277 printf("#processes: %d\n", nproc-nstop - Have_claim + Skip_claim);
281 verbose &= ~1; /* no more globals */
282 verbose |= 32; /* add process states */
283 for (X = run; X; X = X->nxt)
285 verbose = ov; /* restore */
287 printf("%d process%s created\n",
288 nproc - Have_claim + Skip_claim,
289 (xspin || nproc!=1)?"es":"");
291 if (xspin) alldone(0); /* avoid an abort from xspin */
292 if (fini) alldone(1);
295 static char is_blocked[256];
301 is_blocked[p%256] = 1;
302 for (i = j = 0; i < nproc - nstop; i++)
304 if (j >= nproc - nstop)
305 { memset(is_blocked, 0, 256);
312 silent_moves(Element *e)
316 switch (e->n->ntyp) {
319 f = get_lab(e->n, 1);
320 cross_dsteps(e->n, f->n);
321 return f; /* guard against goto cycles */
323 return silent_moves(e->sub->this->frst);
327 e->n->sl->this->last->nxt = e->nxt;
328 return silent_moves(e->n->sl->this->frst);
330 return silent_moves(e->nxt);
337 { SeqList *z; Element *has_else;
339 int j, k, nr_else = 0;
341 if (nproc <= nstop+1)
345 if (!interactive || depth < jumpsteps)
346 { /* was: j = (int) Rand()%(nproc-nstop); */
347 if (Priority_Sum < nproc-nstop)
348 fatal("cannot happen - weights", (char *)0);
349 j = (int) Rand()%Priority_Sum;
351 while (j - X->priority >= 0)
355 if (!X) { Y = NULL; X = run; }
358 { int only_choice = -1;
359 int no_choice = 0, proc_no_ch, proc_k;
361 Tval = 0; /* new 4.2.6 */
362 try_again: printf("Select a statement\n");
363 try_more: for (X = run, k = 1; X; X = X->nxt)
364 { if (X->pid > 255) break;
366 Choices[X->pid] = (short) k;
369 || (X->prov && !eval(X->prov)))
374 X->pc = silent_moves(X->pc);
375 if (!X->pc->sub && X->pc->n)
377 unex = !Enabled0(X->pc);
382 if (!xspin && unex && !(verbose&32))
386 printf("\tchoice %d: ", k++);
389 printf(" unexecutable,");
391 comment(stdout, X->pc->n, 0);
392 if (X->pc->esc) printf(" + Escape");
396 proc_no_ch = no_choice;
398 for (z = X->pc->sub, j=0; z; z = z->nxt)
399 { Element *y = silent_moves(z->this->frst);
403 if (y->n->ntyp == ELSE)
404 { has_else = (Rvous)?ZE:y;
414 if (!xspin && unex && !(verbose&32))
418 printf("\tchoice %d: ", k++);
421 printf(" unexecutable,");
423 comment(stdout, y->n, 0);
427 { if (no_choice-proc_no_ch >= (k-proc_k)-1)
428 { only_choice = nr_else;
429 printf("\tchoice %d: ", nr_else);
434 printf("\tchoice %d: ", nr_else);
436 printf(" unexecutable, [else]\n");
440 if (k - no_choice < 2 && Tval == 0)
442 no_choice = 0; only_choice = -1;
446 printf("Make Selection %d\n\n", k-1);
448 { if (k - no_choice < 2)
449 { printf("no executable choices\n");
452 printf("Select [1-%d]: ", k-1);
454 if (!xspin && k - no_choice == 2)
455 { printf("%d\n", only_choice);
469 { printf("\tchoice is outside range\n");
474 for (X = run; X; Y = X, X = X->nxt)
477 || j < Choices[X->nxt->pid])
479 MadeChoice = 1+j-Choices[X->pid];
489 RunList *Y = NULL; /* previous process in run queue */
491 int go, notbeyond = 0;
502 if (has_enabled && u_sync > 0)
503 { printf("spin: error, cannot use 'enabled()' in ");
504 printf("models with synchronous channels.\n");
515 printf("warning: never claim not used in random simulation\n");
517 printf("warning: trace assertion not used in random simulation\n");
524 if (X->pc && X->pc->n)
525 { lineno = X->pc->n->ln;
526 Fname = X->pc->n->fn;
528 if (cutoff > 0 && depth >= cutoff)
529 { printf("-------------\n");
530 printf("depth-limit (-u%d steps) reached\n", cutoff);
534 if (xspin && !interactive && --bufmax <= 0)
535 { int c; /* avoid buffer overflow on pc's */
536 printf("spin: type return to proceed\n");
539 if (c == 'q') wrapup(0);
543 depth++; LastStep = ZE;
544 oX = X; /* a rendezvous could change it */
546 if (X && X->prov && X->pc
547 && !(X->pc->status & D_ATOM)
549 { if (!xspin && ((verbose&32) || (verbose&4)))
551 printf("\t<<Not Enabled>>\n");
555 if (go && (e = eval_sub(X->pc)))
556 { if (depth >= jumpsteps
557 && ((verbose&32) || (verbose&4)))
559 if (!(e->status & D_ATOM) || (verbose&32)) /* no talking in d_steps */
562 if (!LastStep) LastStep = X->pc;
563 comment(stdout, LastStep->n, 0);
566 if (verbose&1) dumpglobals();
567 if (verbose&2) dumplocal(X);
569 if (!(e->status & D_ATOM))
574 { e = silent_moves(e);
577 oX->pc = e; LastX = X;
579 if (!interactive) Tval = 0;
580 memset(is_blocked, 0, 256);
582 if (X->pc && (X->pc->status & (ATOM|L_ATOM))
583 && (notbeyond == 0 || oX != X))
584 { if ((X->pc->status & L_ATOM))
586 continue; /* no process switch */
590 if (oX->pc->status & D_ATOM)
591 non_fatal("stmnt in d_step blocks", (char *)0);
593 if (X->pc->n->ntyp == '@'
594 && X->pid == (nproc-nstop-1))
595 { if (X != run && Y != NULL)
600 Priority_Sum -= X->priority;
603 dotag(stdout, "terminates\n");
606 if (!interactive) Tval = 0;
607 if (nproc == nstop) break;
608 memset(is_blocked, 0, 256);
609 /* proc X is no longer in runlist */
610 X = (X->nxt) ? X->nxt : run;
612 { if (p_blocked(X->pid))
615 if (depth >= jumpsteps)
617 X = (RunList *) 0; /* to suppress indent */
618 dotag(stdout, "timeout\n");
629 complete_rendez(void)
630 { RunList *orun = X, *tmp;
631 Element *s_was = LastStep;
633 int j, ointer = interactive;
637 if (orun->pc->status & D_ATOM)
638 fatal("rv-attempt in d_step sequence", (char *)0);
642 j = (int) Rand()%Priority_Sum; /* randomize start point */
644 while (j - X->priority >= 0)
649 for (j = nproc - nstop; j > 0; j--)
651 && (!X->prov || eval(X->prov))
652 && (e = eval_sub(X->pc)))
658 if ((verbose&32) || (verbose&4))
659 { tmp = orun; orun = X; X = tmp;
660 if (!s_was) s_was = X->pc;
663 comment(stdout, s_was->n, 0);
665 tmp = orun; orun = X; X = tmp;
666 if (!LastStep) LastStep = X->pc;
669 comment(stdout, LastStep->n, 0);
672 Rvous = 0; /* before silent_moves */
673 X->pc = silent_moves(e);
674 out: interactive = ointer;
683 interactive = ointer;
687 /***** Runtime - Local Variables *****/
690 addsymbol(RunList *r, Symbol *s)
694 for (t = r->symtab; t; t = t->next)
695 if (strcmp(t->name, s->name) == 0)
696 return; /* it's already there */
698 t = (Symbol *) emalloc(sizeof(Symbol));
701 t->hidden = s->hidden;
707 if (s->type != STRUCT)
708 { if (s->val) /* if already initialized, copy info */
709 { t->val = (int *) emalloc(s->nel*sizeof(int));
710 for (i = 0; i < s->nel; i++)
711 t->val[i] = s->val[i];
713 (void) checkvar(t, 0); /* initialize it */
716 fatal("saw preinitialized struct %s", s->name);
720 /* t->context = r->n; */
722 t->next = r->symtab; /* add it */
727 setlocals(RunList *r)
733 for (walk = all_names; walk; walk = walk->next)
737 && strcmp(sp->context->name, r->n->name) == 0
739 && (sp->type == UNSIGNED
746 || sp->type == STRUCT))
748 non_fatal("setlocals: cannot happen '%s'",
756 oneparam(RunList *r, Lextok *t, Lextok *a, ProcList *p)
761 fatal("missing actual parameters: '%s'", p->n->name);
762 if (t->sym->nel != 1)
763 fatal("array in parameter list, %s", t->sym->name);
766 at = Sym_typ(a->lft);
767 X = r; /* switch context */
770 if (at != ft && (at == CHAN || ft == CHAN))
771 { char buf[128], tag1[64], tag2[64];
772 (void) sputtype(tag1, ft);
773 (void) sputtype(tag2, at);
774 sprintf(buf, "type-clash in params of %s(..), (%s<-> %s)",
775 p->n->name, tag1, tag2);
776 non_fatal("%s", buf);
779 addsymbol(r, t->sym);
786 setparams(RunList *r, ProcList *p, Lextok *q)
787 { Lextok *f, *a; /* formal and actual pars */
788 Lextok *t; /* list of pars of 1 type */
794 for (f = p->p, a = q; f; f = f->rgt) /* one type at a time */
795 for (t = f->lft; t; t = t->rgt, a = (a)?a->rgt:a)
796 { if (t->ntyp != ',')
797 oneparam(r, t, a, p); /* plain var */
799 oneparam(r, t->lft, a, p); /* expanded struct */
808 { /* fatal("error, cannot eval '%s' (no proc)", s->name); */
811 for (r = X->symtab; r; r = r->next)
812 if (strcmp(r->name, s->name) == 0)
822 in_bound(Symbol *r, int n)
826 if (n >= r->nel || n < 0)
827 { printf("spin: indexing %s[%d] - size is %d\n",
829 non_fatal("indexing array \'%s\'", r->name);
837 { Symbol *r, *s = sn->sym;
838 int n = eval(sn->lft);
841 if (r && r->type == STRUCT)
842 return Rval_struct(sn, r, 1); /* 1 = check init */
844 return cast_val(r->type, r->val[n], r->nbits);
849 setlocal(Lextok *p, int m)
850 { Symbol *r = findloc(p->sym);
851 int n = eval(p->lft);
854 { if (r->type == STRUCT)
855 (void) Lval_struct(p, r, 1, m); /* 1 = check init */
860 m = (m & ((1<<r->nbits)-1));
863 r->val[n] = cast_val(r->type, m, r->nbits);
875 if (lnr) printf("%3d: ", depth);
877 if (Have_claim && X->pid == 0)
880 printf("%2d", X->pid - Have_claim);
881 printf(" (%s) ", X->n->name);
887 if ((verbose&32) || (verbose&4))
890 if (verbose&1) dumpglobals();
891 if (verbose&2) dumplocal(r);
896 p_talk(Element *e, int lnr)
897 { static int lastnever = -1;
903 if (Have_claim && X && X->pid == 0
904 && lastnever != newnever && e)
906 { printf("MSC: ~G line %d\n", newnever);
908 printf("%3d: proc - (NEVER) line %d \"never\" ",
910 printf("(state 0)\t[printf('MSC: never\\\\n')]\n");
912 { printf("%3d: proc - (NEVER) line %d \"never\"\n",
916 lastnever = newnever;
921 { printf("line %3d %s (state %d)",
923 e->n?e->n->fn->name:"-",
926 && ((e->status&ENDSTATE) || has_lab(e, 2))) /* 2=end */
927 { printf(" <valid end state>");
938 if (n->sym->type != 0 && n->sym->type != LABEL)
939 { printf("spin: error, type: %d\n", n->sym->type);
940 fatal("not a labelname: '%s'", n->sym->name);
943 { fatal("remote ref to label '%s' inside d_step",
946 if ((i = find_lab(n->sym, n->lft->sym, 1)) == 0)
947 fatal("unknown labelname: %s", n->sym->name);
953 { int prno, i, added=0;
962 prno = f_pid(n->lft->sym->name);
964 { prno = eval(n->lft->lft); /* pid - can cause recursive call */
966 if (n->lft->lft->ntyp == CONST) /* user-guessed pid */
968 { prno += Have_claim;
973 return 0; /* non-existing process */
976 for (Y = run; Y; Y = Y->nxt)
978 printf(" %s: i=%d, prno=%d, ->pid=%d\n", Y->n->name, i, prno, Y->pid);
982 for (Y = run; Y; Y = Y->nxt)
984 { if (strcmp(Y->n->name, n->lft->sym->name) != 0)
985 { printf("spin: remote reference error on '%s[%d]'\n",
986 n->lft->sym->name, prno-added);
987 non_fatal("refers to wrong proctype '%s'", Y->n->name);
989 if (strcmp(n->sym->name, "_p") == 0)
992 /* harmless, can only happen with -t */
996 /* new 4.0 allow remote variables */
1004 n->sym = findloc(n->sym);
1016 printf("remote ref: %s[%d] ", n->lft->sym->name, prno-added);
1017 non_fatal("%s not found", n->sym->name);
1018 printf("have only:\n");
1019 i = nproc - nstop - 1;
1020 for (Y = run; Y; Y = Y->nxt, i--)
1021 if (!strcmp(Y->n->name, n->lft->sym->name))
1022 printf("\t%d\t%s\n", i, Y->n->name);