/* 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;
}
syntax highlighted by Code2HTML, v. 0.9.1