/* iop.c -- runtime support of the input statement */ #include "rts.h" static void move_in (); /* * Append an invocation block to the * end of the specified invocation list. * Since the only place append, appendlist, and delete_inv are used * is where exclusive access to a class has been obtained, we don't * need to grab the queue's lock. */ #define append(ibp, ilist) { \ if (((ilist).head) == NULL) { \ (ilist).head = (ibp); \ (ilist).tail = (ibp); \ (ibp)->last = NULL; \ } else { \ (ilist).tail->next = (ibp); \ (ibp)->last = (ilist).tail; \ (ilist).tail = (ibp); \ } \ } /* * Append an invocation list to the * end of the specified invocation list. */ #define appendlist(ibl, ilist) { \ if (((ilist).head) == NULL) \ (ilist) = (ibl); \ else { \ (ilist).tail->next = (ibl).head; \ (ibl).head->last = (ilist).tail; \ (ilist).tail = (ibl).tail; \ } \ } /* Remove a node from a single-headed doubly-linked list. */ #define delete_inv(node, queue, next, last) { \ if (node->last == NULL) \ if (((queue).head = node->next) != NULL) \ (queue).head->last = NULL; \ else \ (queue).tail = NULL; \ else if ((node->last->next = node->next) != NULL) \ node->next->last = node->last; \ else \ (queue).tail = node->last; \ } /* * Invoke an input operation. Place the invocation block on the appropriate * list. Wake up process if new invocation could possibly satisfy guard in * its input statement. */ void mpd_invk_iop (ibp, clap) Invb ibp; Class clap; { Proc pr; Oper op; ibp->next = NULL; LOCK (clap->clmutex, "mpd_invk_iop"); if (clap->inuse) append (ibp, clap->new_in) else { op = (Oper) ibp->opc.oper_entry; LOCK (op->omutex, "mpd_invk_iop"); op->pending++; UNLOCK (op->omutex, "mpd_invk_iop"); append (ibp, clap->old_in) /* keep blocked_on accurate! */ LOCK_QUEUE ("mpd_invk_iop"); for (pr = clap->old_pr.head; pr; pr = pr->next) pr->blocked_on = &clap->new_pr; UNLOCK_QUEUE ("mpd_invk_iop"); if (clap->new_pr.head == NULL) clap->new_pr.head = clap->old_pr.head; else clap->new_pr.tail->next = clap->old_pr.head; clap->new_pr.tail = clap->old_pr.tail; clap->old_pr.head = NULL; clap->old_pr.tail = NULL; if (clap->new_pr.head != NULL) { clap->inuse = TRUE; clap->new_pr.head->next_inv = clap->old_in.head; if (clap->new_pr.head->else_leg) { clap->else_pr = clap->else_pr->next_else; if (clap->else_pr == NULL) clap->else_tailpr = NULL; } awaken (clap->new_pr); } } UNLOCK (clap->clmutex, "mpd_invk_iop"); } /* * Gain initial access to an input operation class. * Allows GC to start searching for a valid invocation. */ void mpd_iaccess (clap, else_present) Class clap; Bool else_present; { mpd_check_stk (CUR_STACK); LOCK (clap->clmutex, "mpd_iaccess"); CUR_PROC->else_leg = else_present; if (clap->inuse) { LOCK_QUEUE ("mpd_iaccess"); /* protect block-scheduler */ block (&clap->new_pr); if (CUR_PROC->else_leg) { if (clap->else_pr == NULL) { clap->else_pr = CUR_PROC; clap->else_tailpr = CUR_PROC; } else { clap->else_tailpr->next_else = CUR_PROC; clap->else_tailpr = CUR_PROC; } clap->else_tailpr->next_else = NULL; } UNLOCK (clap->clmutex, "mpd_iaccess"); mpd_scheduler (); } else { clap->inuse = TRUE; CUR_PROC->next_inv = clap->old_in.head; UNLOCK (clap->clmutex, "mpd_iaccess"); } } /* * Regain subsequent access to an input operation class. */ void mpd_reaccess (clap) Class clap; { LOCK (clap->clmutex, "mpd_reaccess"); if (clap->new_in.head == NULL) { /* * No new invocations for me, so block. Give access * to class if any other process wants to see it. */ LOCK_QUEUE ("mpd_reaccess #1");/* protect block-awaken-scheduler */ block (&clap->old_pr); if (clap->new_pr.head == NULL) clap->inuse = FALSE; else { clap->new_pr.head->next_inv = clap->old_in.head; if (clap->new_pr.head->else_leg) { clap->else_pr = clap->else_pr->next_else; if (clap->else_pr == NULL) clap->else_tailpr = NULL; } awaken (clap->new_pr); } UNLOCK (clap->clmutex, "mpd_reaccess"); mpd_scheduler (); } else { /* * New invocations have arrived since the last time * this process searched the invocation queue. * Oldest process doing a reaccess () gets to look at * next invocation, proceeding in FCFS order until it's * my turn again. If no older processes, just go around. */ move_in (clap); if ((clap->old_pr.head) != NULL) { Proc pr; /* protect block-awaken-scheduler, plus blocked_on */ LOCK_QUEUE ("mpd_reaccess #2"); block (&clap->old_pr); for (pr = clap->old_pr.head; pr; pr = pr->next) pr->blocked_on = &clap->new_pr; clap->old_pr.tail->next = clap->new_pr.head; clap->new_pr.head = clap->old_pr.head; if (clap->new_pr.tail == NULL) clap->new_pr.tail = clap->old_pr.tail; clap->old_pr.head = NULL; clap->old_pr.tail = NULL; clap->new_pr.head->next_inv = clap->old_in.head; if (clap->new_pr.head->else_leg) { clap->else_pr = clap->else_pr->next_else; if (clap->else_pr == NULL) clap->else_tailpr = NULL; } awaken (clap->new_pr); UNLOCK (clap->clmutex, "mpd_reaccess"); mpd_scheduler (); } else { CUR_PROC->next_inv = clap->old_in.head; UNLOCK (clap->clmutex, "mpd_reaccess"); } } } /* * Check that o is a legal, local op cap for use in an input statement. * Return TRUE if it matches the op entry oentry. */ Bool mpd_cap_ck (locn, oentry, o) char *locn; Ptr oentry; Ocap o; { mpd_check_stk (CUR_STACK); if (o.oper_entry == 0) { if (o.seqn == NOOP_SEQN) return FALSE; /* noop is legal but never matches */ else mpd_loc_abort (locn, "input from null capability"); } if (o.vm != mpd_my_vm) { /* if remote */ if (o.vm <= 0 || o.vm > MAX_VM || !mpd_exec_up) mpd_loc_abort (locn, "invalid operation capability"); mpd_loc_abort (locn, "illegal use of remote capability"); } return oentry == o.oper_entry; } /* * Remove an invocation block from the specified input * operation queue. The GC can service the invocation now. */ void mpd_rm_iop (locn, cp, ibp) char *locn; Ptr cp; Invb ibp; { Class clap; Oper op; mpd_check_stk (CUR_STACK); DEBUG (D_INVOKE, "%-12.12s in %08lX c%08lX", CUR_PROC->pname, ibp, cp); clap = (Class) cp; LOCK (clap->clmutex, "mpd_rm_iop"); if (!ibp) TRACE ("ARM", locn, CUR_PROC); /* leg */ else if (ibp->forwarded) TRACE ("ARM", locn, ibp->forwarder); /* forward request */ else TRACE ("ARM", locn, ibp->invoker); /* normal request */ /* * remove the invocation, unless this is the "else" leg and there isn't one */ if (ibp != NULL) { op = (Oper) ibp->opc.oper_entry; LOCK (op->omutex, "mpd_rm_iop"); op->pending--; delete_inv (ibp, clap->old_in, next, last); UNLOCK (op->omutex, "mpd_rm_iop"); } /* * Handle any other processes waiting for this class. */ if (clap->new_in.head == NULL) { /* * No new invocations. Give access to class if any other process * wants to see it. */ if (clap->old_in.head == NULL) { if (clap->else_pr == NULL) { if (clap->new_pr.head) { Proc pr; /* keep blocked_on accurate! */ LOCK_QUEUE ("mpd_rm_iop"); for (pr = clap->new_pr.head; pr; pr = pr->next) pr->blocked_on = &clap->old_pr; UNLOCK_QUEUE ("mpd_rm_iop"); if (clap->old_pr.head) clap->old_pr.tail->next = clap->new_pr.head; else clap->old_pr.head = clap->new_pr.head; clap->old_pr.tail = clap->new_pr.tail; clap->new_pr.head = NULL; clap->new_pr.tail = NULL; } clap->inuse = FALSE; } else { /* * NEVER AN ELSE ON THE OLD_PR * move processes from new process list to old process * list until reach a blocked process with an else * be sure to keep blocked_on accurate! */ Proc pr; pr = clap->new_pr.head; if (!pr->else_leg) { LOCK_QUEUE ("mpd_rm_iop"); while ((pr->next) && pr->next->else_leg == FALSE) { pr->blocked_on = &clap->old_pr; pr = pr->next; } pr->blocked_on = &clap->old_pr; UNLOCK_QUEUE ("mpd_rm_iop"); if (clap->old_pr.head) { clap->old_pr.tail->next = clap->new_pr.head; clap->old_pr.tail = pr; } else { clap->old_pr.head = clap->new_pr.head; clap->old_pr.tail = pr; } clap->new_pr.head = pr->next; pr->next = NULL; } /* first proc with an else is now in clap->new_pr.head */ clap->else_pr = clap->else_pr->next_else; if (clap->else_pr == NULL) clap->else_tailpr = NULL; clap->new_pr.head->next_inv = NULL; awaken (clap->new_pr); } } else { if (clap->new_pr.head == NULL) clap->inuse = FALSE; else { clap->new_pr.head->next_inv = clap->old_in.head; if (clap->new_pr.head->else_leg) { clap->else_pr = clap->else_pr->next_else; if (clap->else_pr == NULL) clap->else_tailpr = NULL; } awaken (clap->new_pr); } } } else { /* * New invocations have arrived. Does any process need to look at them? */ move_in (clap); if (clap->old_pr.head) { Proc pr; /* keep blocked_on accurate! */ LOCK_QUEUE ("mpd_rm_iop"); for (pr = clap->old_pr.head; pr; pr = pr->next) pr->blocked_on = &clap->new_pr; UNLOCK_QUEUE ("mpd_rm_iop"); if (clap->new_pr.head) clap->old_pr.tail->next = clap->new_pr.head; else clap->new_pr.tail = clap->old_pr.tail; clap->new_pr.head = clap->old_pr.head; clap->old_pr.head = NULL; clap->old_pr.tail = NULL; } if (clap->new_pr.head) { clap->new_pr.head->next_inv = clap->old_in.head; if (clap->new_pr.head->else_leg) { clap->else_pr = clap->else_pr->next_else; if (clap->else_pr == NULL) clap->else_tailpr = NULL; } awaken (clap->new_pr); } else clap->inuse = FALSE; } UNLOCK (clap->clmutex, "mpd_rm_iop"); } /* * Move all invocations of the specified class from class's new invocation * list to its old invocation list. * For each invocation on the new list, update its operation's pending. */ static void move_in (clap) Class clap; { Invb ibp; Oper op; for (ibp = clap->new_in.head; ibp; ibp = ibp->next) { op = (Oper) ibp->opc.oper_entry; LOCK (op->omutex, "move_in"); op->pending++; UNLOCK (op->omutex, "move_in"); } appendlist (clap->new_in, clap->old_in); clap->new_in.head = NULL; clap->new_in.tail = NULL; }