WalnutRegistryImpl.mesa
Copyright Ó 1985, 1992 by Xerox Corporation. All rights reserved.
Donahue, July 9, 1985 1:23:55 pm PDT
Willie-Sue, December 29, 1986 5:57:23 pm PST
Walnut Registry provides notification to outside clients of changes in Walnut databases. Walnut RegistryImpl can be run without running Walnut -- WalnutKernel imports it, rather than the other way around.
Procedures registered with Walnut Registry are placed on the queue, using MBQueue.QueueClientAction, given in the registration when the database changes; clients are guaranteed that the procedures will be queued in the order in which the database changes that caused their invocation occurred.
DIRECTORY
MBQueue,
RefTab,
Rope USING [ROPE],
WalnutRegistry,
WalnutRegistryInternal,
WalnutRegistryPrivate;
WalnutRegistryImpl: CEDAR MONITOR
IMPORTS RefTab, WalnutRegistryInternal
EXPORTS WalnutRegistry, WalnutRegistryPrivate
= BEGIN
RegistrationEntry: TYPE = REF RegistrationRecord;
RegistrationRecord: TYPE = RECORD[queue: MBQueue.Queue, eventProc: EventProcEntry ¬ NIL, msgProc: MsgProcEntry ¬ NIL, msgGroupProc: MsgGroupEntry ¬ NIL, moveProc: MoveProcEntry ¬ NIL, msgSetProc: MsgSetProcEntry ¬ NIL];
EventProcEntry: TYPE = REF EventProcRecord;
EventProcRecord: TYPE = RECORD[clientData: REF ANY, proc: WalnutRegistry.EventProc, parent: RegistrationEntry, next, prev: EventProcEntry];
MsgProcEntry: TYPE = REF MsgProcRecord;
MsgProcRecord: TYPE = RECORD[clientData: REF ANY, proc: WalnutRegistry.MsgProc, parent: RegistrationEntry, next, prev: MsgProcEntry];
MoveProcEntry: TYPE = REF MoveProcRecord;
MoveProcRecord: TYPE = RECORD[clientData: REF ANY, proc: WalnutRegistry.MoveProc, parent: RegistrationEntry, next, prev: MoveProcEntry];
MsgGroupEntry: TYPE = REF MsgGroupRecord;
MsgGroupRecord: TYPE = RECORD[clientData: REF ANY, proc: WalnutRegistry.MsgGroupProc, parent: RegistrationEntry, next, prev: MsgGroupEntry];
MsgSetProcEntry: TYPE = REF MsgSetProcRecord;
MsgSetProcRecord: TYPE = RECORD[clientData: REF ANY, proc: WalnutRegistry.MsgSetProc, parent: RegistrationEntry, next, prev: MsgSetProcEntry];
nextRegistration: CARDINAL ¬ 0;
registrationTable: RefTab.Ref = RefTab.Create[];
the values in the table are the registration entries; the keys are the Registrations returned (they are simply REF CARDINALS)
the registrations are also linked into doubly-linked lists according to the type of procedures
eventProcHead: EventProcEntry;
msgProcHead: MsgProcEntry;
moveProcHead: MoveProcEntry;
msgSetProcHead: MsgSetProcEntry;
msgGroupHead: MsgGroupEntry;
we want to be able to notify new clients of the current state of Walnut
stateIs: WalnutRegistry.Event ¬ unknown;
CurrentWalnutState: PUBLIC ENTRY PROC
RETURNS[walnutState: WalnutRegistry.WalnutState] = {
SELECT stateIs FROM
unknown => RETURN[unknown];
initializing => RETURN[initializing];
stopped => RETURN[stopped];
ENDCASE => RETURN[active];
};
Register: PUBLIC ENTRY PROC [procSet: WalnutRegistry.ProcSet, queue: MBQueue.Queue] RETURNS[registration: WalnutRegistry.Registration] = {
newEntry: RegistrationEntry = NEW[RegistrationRecord ¬ [queue: queue]];
registration ¬ NEW[CARDINAL ¬ nextRegistration];
nextRegistration ¬ nextRegistration+1;
[] ¬ RefTab.Store[registrationTable, registration, newEntry];
stitch it into the appropriate lists
IF procSet.eventProc # NIL THEN {
newEntry.eventProc ¬ NEW[EventProcRecord ¬ [clientData: procSet.eventProcData, proc: procSet.eventProc, parent: newEntry]];
newEntry.eventProc.next ¬ eventProcHead;
newEntry.eventProc.prev ¬ eventProcHead.prev;
eventProcHead.prev.next ¬ newEntry.eventProc;
eventProcHead.prev ¬ newEntry.eventProc;
IF stateIs # unknown THEN
WalnutRegistryInternal.QueueEvent[
 stateIs, queue, procSet.eventProc, procSet.eventProcData];
};
IF procSet.msgProc # NIL THEN {
newEntry.msgProc ¬ NEW[MsgProcRecord ¬ [clientData: procSet.msgProcData, proc: procSet.msgProc, parent: newEntry]];
newEntry.msgProc.next ¬ msgProcHead;
newEntry.msgProc.prev ¬ msgProcHead.prev;
msgProcHead.prev.next ¬ newEntry.msgProc;
msgProcHead.prev ¬ newEntry.msgProc };
IF procSet.moveProc # NIL THEN {
newEntry.moveProc ¬ NEW[MoveProcRecord ¬ [clientData: procSet.moveProcData, proc: procSet.moveProc, parent: newEntry]];
newEntry.moveProc.next ¬ moveProcHead;
newEntry.moveProc.prev ¬ moveProcHead.prev;
moveProcHead.prev.next ¬ newEntry.moveProc;
moveProcHead.prev ¬ newEntry.moveProc };
IF procSet.msgGroupProc # NIL THEN {
newEntry.msgGroupProc ¬ NEW[MsgGroupRecord ¬ [clientData: procSet.msgGroupData, proc: procSet.msgGroupProc, parent: newEntry]];
newEntry.msgGroupProc.next ¬ msgGroupHead;
newEntry.msgGroupProc.prev ¬ msgGroupHead.prev;
msgGroupHead.prev.next ¬ newEntry.msgGroupProc;
msgGroupHead.prev ¬ newEntry.msgGroupProc };
IF procSet.msgSetProc # NIL THEN {
newEntry.msgSetProc ¬ NEW[MsgSetProcRecord ¬ [clientData: procSet.msgSetData, proc: procSet.msgSetProc, parent: newEntry]];
newEntry.msgSetProc.next ¬ msgSetProcHead;
newEntry.msgSetProc.prev ¬ msgSetProcHead.prev;
msgSetProcHead.prev.next ¬ newEntry.msgSetProc;
msgSetProcHead.prev ¬ newEntry.msgSetProc };
};
InvalidRegistration: PUBLIC ERROR = CODE;
UnRegister: PUBLIC ENTRY PROC [registration: WalnutRegistry.Registration] = {
entry: RegistrationEntry = NARROW[RefTab.Fetch[registrationTable, registration].val];
IF entry = NIL THEN ERROR InvalidRegistration;
IF entry.eventProc # NIL THEN {
entry.eventProc.prev.next ¬ entry.eventProc.next;
entry.eventProc.next.prev ¬ entry.eventProc.prev;
entry.eventProc.parent ¬ NIL };
IF entry.msgProc # NIL THEN {
entry.msgProc.prev.next ¬ entry.msgProc.next;
entry.msgProc.next.prev ¬ entry.msgProc.prev;
entry.msgProc.parent ¬ NIL };
IF entry.moveProc # NIL THEN {
entry.moveProc.prev.next ¬ entry.moveProc.next;
entry.moveProc.next.prev ¬ entry.moveProc.prev;
entry.moveProc.parent ¬ NIL };
IF entry.msgGroupProc # NIL THEN {
entry.msgGroupProc.prev.next ¬ entry.msgGroupProc.next;
entry.msgGroupProc.next.prev ¬ entry.msgGroupProc.prev;
entry.msgGroupProc.parent ¬ NIL };
IF entry.msgSetProc # NIL THEN {
entry.msgSetProc.prev.next ¬ entry.msgSetProc.next;
entry.msgSetProc.next.prev ¬ entry.msgSetProc.prev;
entry.msgSetProc.parent ¬ NIL };
[] ¬ RefTab.Delete[registrationTable, registration] };
GetProcs: PUBLIC ENTRY PROC[registration: WalnutRegistry.Registration] RETURNS[procSet: WalnutRegistry.ProcSet, queue: MBQueue.Queue] = {
entry: RegistrationEntry = NARROW[RefTab.Fetch[registrationTable, registration].val];
IF entry = NIL THEN ERROR InvalidRegistration;
queue ¬ entry.queue;
IF entry.eventProc # NIL THEN {
procSet.eventProc ¬ entry.eventProc.proc;
procSet.eventProcData ¬ entry.eventProc.clientData };
IF entry.msgProc # NIL THEN {
procSet.msgProc ¬ entry.msgProc.proc;
procSet.msgProcData ¬ entry.msgProc.clientData};
IF entry.moveProc # NIL THEN {
procSet.moveProc ¬ entry.moveProc.proc;
procSet.moveProcData ¬ entry.moveProc.clientData};
IF entry.msgGroupProc # NIL THEN {
procSet.msgGroupProc ¬ entry.msgGroupProc.proc;
procSet.msgGroupData ¬ entry.msgGroupProc.clientData};
IF entry.msgSetProc # NIL THEN {
procSet.msgSetProc ¬ entry.msgSetProc.proc;
procSet.msgSetData ¬ entry.msgSetProc.clientData}
};
CheckForMsgGroupRegistration: PUBLIC ENTRY PROC RETURNS[yes: BOOL] =
{ RETURN[msgGroupHead.next.proc # NIL] };
NotifyForEvent: PUBLIC ENTRY PROC[event: WalnutRegistry.Event] = TRUSTED {
stateIs ¬ IF event = mailRead THEN started ELSE event;
FOR e: EventProcEntry ¬ eventProcHead.next, e.next UNTIL e.proc = NIL DO
WalnutRegistryInternal.QueueEvent[event, e.parent.queue, e.proc, e.clientData];
ENDLOOP };
NotifyForMsgEvent: PUBLIC ENTRY PROC[msgEvent: WalnutRegistry.MsgEvent, msg: Rope.ROPE] = TRUSTED {
FOR e: MsgProcEntry ¬ msgProcHead.next, e.next UNTIL e.proc = NIL DO
WalnutRegistryInternal.QueueMsgEvent[msgEvent, msg, e.parent.queue, e.proc, e.clientData];
ENDLOOP};
NotifyForMove: PUBLIC ENTRY PROC[msg: Rope.ROPE, to, from: Rope.ROPE] = TRUSTED {
FOR e: MoveProcEntry ¬ moveProcHead.next, e.next UNTIL e.proc = NIL DO
WalnutRegistryInternal.QueueMoveEvent[msg, to, from, e.parent.queue, e.proc, e.clientData];
ENDLOOP};
NotifyForMsgGroup: PUBLIC ENTRY PROC[event: WalnutRegistry.MsgGroupEvent, group: WalnutRegistry.MsgGroup] = TRUSTED {
msgGroupCopy: REF WalnutRegistry.MsgGroup = NEW[WalnutRegistry.MsgGroup];
msgGroupCopy­ ¬ group;
FOR e: MsgGroupEntry ¬ msgGroupHead.next, e.next UNTIL e.proc = NIL DO
WalnutRegistryInternal.QueueMsgGroupEvent[event, msgGroupCopy, e.parent.queue, e.proc, e.clientData];
ENDLOOP};
NotifyForMsgSetEvent: PUBLIC ENTRY PROC[msgSetEvent: WalnutRegistry.MsgSetEvent, msgSet: Rope.ROPE] = TRUSTED {
FOR e: MsgSetProcEntry ¬ msgSetProcHead.next, e.next UNTIL e.proc = NIL DO
WalnutRegistryInternal.QueueMsgSetEvent[msgSetEvent, msgSet, e.parent.queue, e.proc, e.clientData];
ENDLOOP};
The heads to be non-NIL to make the list manipulation easier and faster
eventProcHead ¬ NEW[EventProcRecord ¬ [clientData: NIL, proc: NIL]];
eventProcHead.prev ¬ eventProcHead.next ¬ eventProcHead;
msgProcHead ¬ NEW[MsgProcRecord ¬ [clientData: NIL, proc: NIL]];
msgProcHead.prev ¬ msgProcHead.next ¬ msgProcHead;
moveProcHead ¬ NEW[MoveProcRecord ¬ [clientData: NIL, proc: NIL]];
moveProcHead.prev ¬ moveProcHead.next ¬ moveProcHead;
msgGroupHead ¬ NEW[MsgGroupRecord ¬ [clientData: NIL, proc: NIL]];
msgGroupHead.prev ¬ msgGroupHead.next ¬ msgGroupHead;
msgSetProcHead ¬ NEW[MsgSetProcRecord ¬ [clientData: NIL, proc: NIL]];
msgSetProcHead.prev ¬ msgSetProcHead.next ¬ msgSetProcHead;
END.