-- BBActionImpl.mesa
-- Russ Atkinson, November 1, 1982 3:42 pm
DIRECTORY
AMEvents USING [Event],
BBAction USING
[Action, ActionId, ActionKind, ActionRep, ActionStatus],
BBZones USING [GetQuantizedZone];
BBActionImpl: CEDAR MONITOR
IMPORTS BBZones
EXPORTS BBAction
= BEGIN OPEN BBAction, AMEvents;
z: ZONE ← BBZones.GetQuantizedZone[];
LastId: ActionId ← 0;
actionList: Action ← NIL;
pendingCount: INT ← 0;
globalChangeCount: INT ← 1; -- must stay > 0
change: CONDITION;
Change: INTERNAL PROC = INLINE {
globalChangeCount ← globalChangeCount + 1;
IF globalChangeCount <= 0 THEN globalChangeCount ← 1;
BROADCAST change
};
ActionError: PUBLIC ERROR = CODE;
-- error given when pre-condition not met
NewAction: PUBLIC ENTRY PROC [event: Event, kind: ActionKind ← other] RETURNS [Action] = {
-- creates a new action with new id
ENABLE UNWIND => NULL;
id: ActionId ← LastId ← LastId + 1;
action: Action ← NIL;
IF id <= 0 THEN ERROR; -- how did we get 2**31 actions?
action
← z.NEW[ActionRep ←
[id: id, rest: actionList,
kind: kind, status: new,
event: event,
in: NIL, out: NIL]];
actionList ← action;
RETURN [action];
};
WaitForDataGiven: PUBLIC ENTRY PROC [action: Action] RETURNS [REF] = {
-- (PRE: status = busy OR status = other)
-- suspends caller (as pendingIn) until data is given
-- data is returned from the call when proceding
-- (POST: status = busy)
ENABLE UNWIND => NULL;
IF action = NIL THEN RETURN WITH ERROR ActionError;
SELECT action.status FROM
busy, new => {};
ENDCASE => RETURN WITH ERROR ActionError;
action.status ← pendingIn;
pendingCount ← pendingCount + 1;
Change[];
WHILE action.status # busy DO
IF action.status = dead THEN RETURN WITH ERROR ABORTED;
WAIT change;
ENDLOOP;
RETURN [action.in];
};
WaitForDataTaken: PUBLIC ENTRY PROC [action: Action, out: REF] = {
-- (PRE: status = busy OR status = other)
-- suspends caller (as pendingOut) until data (out) is taken
-- when the data is taken, the process proceeds (busy)
-- (POST: status = busy)
ENABLE UNWIND => NULL;
IF action = NIL THEN RETURN WITH ERROR ActionError;
SELECT action.status FROM
busy, new => {};
ENDCASE => RETURN WITH ERROR ActionError;
action.out ← out;
action.status ← pendingOut;
pendingCount ← pendingCount + 1;
Change[];
WHILE action.status # busy DO
IF action.status = dead THEN RETURN WITH ERROR ABORTED;
WAIT change;
ENDLOOP;
};
WaitForChange: PUBLIC ENTRY PROC
[changeCount: INT] RETURNS [INT] = {
-- suspends caller until the change count is different than
-- the given changeCount, then returns the new change count
-- the changeCount will change for every time a process becomes pending,
-- busy, or dead; the change count may wrap around, but will always
-- be > 0
ENABLE UNWIND => NULL;
WHILE changeCount = globalChangeCount DO
WAIT change;
ENDLOOP;
RETURN [globalChangeCount];
};
ForceChange: PUBLIC ENTRY PROC = {
-- forces a change to the change count
globalChangeCount ← globalChangeCount + 1;
Change[];
};
GetChangeCount: PUBLIC PROC RETURNS [INT] = {
-- get the current change count
RETURN [globalChangeCount];
};
NextPendingAction: PUBLIC ENTRY PROC [action: Action] RETURNS [Action] = {
-- (PRE: status # dead)
-- returns the next eldest pending action
-- returns NIL if no pending actions in list
-- start with NIL to get most recent
ENABLE UNWIND => NULL;
IF action = NIL OR action.status = dead
THEN
{action ← actionList;
IF action = NIL THEN RETURN [NIL]}
ELSE
action ← action.rest;
FOR al: Action ← action, al.rest WHILE al # NIL DO
SELECT al.status FROM
pendingIn, pendingOut => RETURN [al];
dead => ERROR; -- HELP!!!
ENDCASE;
ENDLOOP;
RETURN [NIL];
};
NextAction: PUBLIC ENTRY PROC [action: Action] RETURNS [Action] = {
-- (PRE: status # dead)
-- returns the next eldest action
-- returns NIL if no actions in list
-- start with NIL to get most recent
ENABLE UNWIND => NULL;
IF action = NIL OR action.status = dead THEN RETURN [actionList];
RETURN [action.rest];
};
GiveData: PUBLIC ENTRY PROC [action: Action, in: REF] = {
-- (PRE: status = pendingIn)
-- supplies the input data, allows the action to proceed
-- (POST: status = busy)
ENABLE UNWIND => NULL;
IF action = NIL OR action.status # pendingIn
THEN RETURN WITH ERROR ActionError;
action.in ← in;
action.status ← busy;
pendingCount ← pendingCount - 1;
Change[];
};
TakeData: PUBLIC ENTRY PROC [action: Action] RETURNS [REF] = {
-- (PRE: status = pendingOut)
-- obtains the output data, allows the action to proceed
-- (POST: status = busy)
ENABLE UNWIND => NULL;
IF action = NIL OR action.status # pendingOut
THEN RETURN WITH ERROR ActionError;
action.status ← busy;
pendingCount ← pendingCount - 1;
Change[];
RETURN [action.out];
};
PeekData: PUBLIC ENTRY PROC [action: Action] RETURNS [REF] = {
-- (PRE: status = pendingOut)
-- obtains the output data, does not allow process to proceed
-- (POST: status = pendingOut)
ENABLE UNWIND => NULL;
IF action = NIL OR action.status # pendingOut
THEN RETURN WITH ERROR ActionError;
RETURN [action.out];
};
Abort: PUBLIC ENTRY PROC [action: Action] = {
-- (PRE: status # dead)
-- aborts the pending action
-- (aborts the process if status # other)
-- (POST: status = dead, action not in list)
ENABLE UNWIND => NULL;
IF action = NIL OR action.status = dead
THEN RETURN WITH ERROR ActionError;
InternalRemove[action];
Change[];
};
InternalRemove: PROC [action: Action] = {
al: Action ← actionList; -- trailing list node
SELECT action.status FROM
pendingIn, pendingOut => pendingCount ← pendingCount - 1;
ENDCASE;
action.status ← dead;
IF al = action THEN {
actionList ← al.rest;
al.rest ← NIL;
RETURN};
DO
rest: Action ← al.rest;
IF rest = NIL THEN ERROR; -- HELP!!!
IF rest = action THEN {
al.rest ← rest.rest;
action.rest ← NIL;
EXIT};
al ← rest;
ENDLOOP;
};
END.