-- File: DispatcherImpl.mesa - last edit:
-- AOF 27-Nov-87 12:01:19
-- SMA 21-May-86 11:05:15
-- Copyright (C) 1984, 1985, 1986, 1987 by Xerox Corporation. All rights reserved.
DIRECTORY
Buffer USING [
Buffer, Dequeue, Enqueue, QueueCleanup, QueueInitialize, QueueObject,
Type],
--ByteBlt USING [ByteBlt],
CommPriorities USING [receiver],
--Environment USING [Block],
Protocol1 USING [
Action, ProtocolFamily, FamilyUnit, DecapsulatorProc, Matrix, Matrices,
MatrixRecord, FamilyIndex, UniqueName, Family, EncapsulatorProc],
CommFlags USING [doDebug],
CommHeap USING [zone],
CommunicationInternal USING [],
Driver USING [
GetDeviceChain, Glitch, Device, ReturnFreeBuffer],
Inline USING [LongCOPY],
Process USING [Abort, DisableTimeout, EnableAborts, Pause, SetPriority],
SpecialCommunication USING [SpyProc, SpyType];
DispatcherImpl: MONITOR
IMPORTS Buffer, <<ByteBlt,>> CommHeap, Driver, Inline, Process
EXPORTS
Buffer, Protocol1, CommunicationInternal, Driver, SpecialCommunication =
BEGIN
-- EXPORTed TYPEs
Device: PUBLIC TYPE = Driver.Device;
--ERRORS
NoVacancy: PUBLIC ERROR = CODE;
NotInterested: PUBLIC ERROR = CODE;
mainFork: PROCESS;
dispatcherReady: CONDITION;
globalInputQueue: Buffer.QueueObject;
globalOutputQueue: Buffer.QueueObject;
orphanSpy: SpecialCommunication.SpyProc ← NIL;
condo: Condo ← [0, ALL[NIL]]; --family housing project
Condo: TYPE = RECORD[
occupied: Protocol1.FamilyIndex ← 0, --current occupation
unit: ARRAY Protocol1.FamilyIndex OF Protocol1.Family];
MemberSupport: TYPE = RECORD[
SEQUENCE COMPUTED Protocol1.FamilyIndex OF Protocol1.MatrixRecord];
uniqueAvail: PACKED ARRAY Protocol1.ProtocolFamily OF BOOLEAN ← [
ns: FALSE, pup: FALSE, arpa: FALSE, osi: FALSE, upt1: TRUE, upt2: TRUE];
BadSource: ERROR = CODE;
NameNotAssigned: ERROR = CODE;
TenentRegistered: ERROR = CODE;
InvalidPacketType: ERROR = CODE;
TenentNotRegistered: PUBLIC << Protocol1 >> ERROR = CODE;
--CONSTANT
unitSize: CARDINAL = SIZE[Protocol1.MatrixRecord];
AcquireUniqueName: PUBLIC ENTRY PROC RETURNS[name: Protocol1.UniqueName] =
BEGIN
--There are only two unattached family names. How tough can this be?
SELECT TRUE FROM
uniqueAvail[upt1] => {uniqueAvail[upt1] ← FALSE; RETURN[upt1]};
uniqueAvail[upt2] => {uniqueAvail[upt2] ← FALSE; RETURN[upt2]};
ENDCASE => RETURN WITH ERROR NoVacancy;
END; --AcquireUniqueName
AddFamilyMember: PUBLIC PROC[driver: Device, matrix: Protocol1.Matrix] =
--Add new support for this driver
BEGIN
EnterMonitor: ENTRY PROC =
BEGIN
rich: LONG POINTER TO MemberSupport;
poor: LONG DESCRIPTOR FOR Protocol1.Matrices ← driver.matrix;
base: Protocol1.Matrix ← @poor[0]; --this variable is overloaded
length: Protocol1.FamilyIndex ← LENGTH[poor];
THROUGH[0..length) DO
IF base.family = matrix.family THEN --overlaying old family
BEGIN
rich ← LOOPHOLE[@poor[0]]; --the rich share space with the poor
action ← modify; --that's what we are doing
EXIT; --our work is done
END;
base ← base + unitSize; --try next family in unit
REPEAT FINISHED =>
BEGIN
rich ← CommHeap.zone.NEW[MemberSupport[length + 1]]; --new living space
IF BASE[poor] # NIL THEN
BEGIN
base ← @poor[0]; --this is were the poor live(d)
Inline.LongCOPY[to: rich, from: base, nwords: unitSize * length];
CommHeap.zone.FREE[@base]; --toss the poor
END;
base ← @rich[length]; --that's where the new family lives
length ← SUCC[length]; --in a new, modern and larger unit
action ← add; --this is a new family member
END;
ENDLOOP;
base↑ ← [
family: matrix.family,
context: matrix.context,
encapsulator: matrix.encapsulator,
decapsulator: matrix.decapsulator];
driver.matrix ← DESCRIPTOR[rich, length]; --move in the rich
driver.receiveBufferLen ← MAX[--keep track of largest used
driver.receiveBufferLen, matrix.family.maxBufferSize];
END; --EnterMonitor
action: Protocol1.Action;
EnterMonitor[]; --get the data base changes out of the way
matrix.family.stateChanged[driver, matrix.context, action]; --tell family
END; --AddFamilyMember
GetFamilyUnit: PUBLIC ENTRY PROC[name: Protocol1.ProtocolFamily]
RETURNS[family: Protocol1.Family] =
BEGIN
FOR i: Protocol1.FamilyIndex IN[0..condo.occupied) DO
IF condo.unit[i].name = name THEN RETURN[condo.unit[i]];
REPEAT FINISHED => ERROR TenentNotRegistered;
ENDLOOP;
END; --GetFamilyUnit
EvictFamily: PUBLIC ENTRY PROC[name: Protocol1.ProtocolFamily] =
BEGIN
driver: Device;
un: Protocol1.FamilyIndex ← FIRST[Protocol1.FamilyIndex]; --compression
census: CARDINAL = condo.occupied; --make copy so we can modify original
--This will be a noop of the family 'name' doesn't exist
FOR fi: Protocol1.FamilyIndex IN [0..census) DO
family: Protocol1.Family ← condo.unit[fi];
IF family.name = name THEN
BEGIN --collapse this unit out of condo (only one match)
family.status ← dead; --turn on the gas, thus the prediction
Process.Pause[1]; --wait for gas to take affect
FOR driver ← Driver.GetDeviceChain[], driver.next UNTIL driver = NIL DO
RemoveFamilyMemberInternal[driver, family]; --remove members first
ENDLOOP;
condo.occupied ← condo.occupied - 1; --moved to Miami
condo.unit[fi] ← NIL; --sorta like repainting the rooms
END
ELSE {condo.unit[un] ← family; un ← SUCC[un]}; --compress
ENDLOOP;
END; --EvictFamily
RegisterFamily: PUBLIC PROC[family: Protocol1.Family] =
BEGIN
EnterMonitor: ENTRY PROC = --INLINE
BEGIN
FOR fi IN[0..condo.occupied) DO
IF condo.unit[fi].name = family.name THEN EXIT; --overlay old
REPEAT FINISHED =>
{fi ← condo.occupied; condo.occupied ← condo.occupied + 1};
ENDLOOP;
condo.unit[fi] ← family; --the family is now living in the condo
END; --EnterMonitor
fi: Protocol1.FamilyIndex;
SELECT TRUE FROM
(~CommFlags.doDebug) => NULL;
(family.name ~IN Protocol1.ProtocolFamily), (uniqueAvail[family.name]) =>
Driver.Glitch[NameNotAssigned];
ENDCASE;
EnterMonitor[]; --set up the condo
END; --RegisterFamily
ReleaseUniqueName: PUBLIC ENTRY PROC[name: Protocol1.UniqueName] =
BEGIN
IF CommFlags.doDebug THEN
BEGIN
IF ~uniqueAvail[name] THEN Driver.Glitch[NameNotAssigned];
FOR i: Protocol1.FamilyIndex IN[0..condo.occupied) DO
IF condo.unit[i].name = name THEN Driver.Glitch[TenentRegistered];
ENDLOOP;
END;
uniqueAvail[name] ← TRUE; --now it's available again
END; --ReleaseUniqueName
RemoveFamilyMember: PUBLIC ENTRY PROC[
driver: Device, family: Protocol1.Family] =
{RemoveFamilyMemberInternal[driver, family]};
RemoveFamilyMemberInternal: PRIVATE INTERNAL PROC[
driver: Device, family: Protocol1.Family] =
BEGIN
length: Protocol1.FamilyIndex;
bPoor, bRich: Protocol1.Matrix;
poor: LONG POINTER TO MemberSupport;
rich: LONG DESCRIPTOR FOR Protocol1.Matrices ← driver.matrix;
bRich ← @rich[0];
THROUGH[0..LENGTH[rich]) DO
--just checking for family existance
IF bRich.family = family THEN EXIT; --and found it
bRich ← bRich + unitSize;
REPEAT FINISHED => RETURN; --there's nothing to do here
ENDLOOP;
IF (length ← LENGTH[rich] - 1) = 0 THEN poor ← NIL
ELSE
BEGIN
poor ← CommHeap.zone.NEW[MemberSupport[length]];
bPoor ← @poor[0]; bRich ← @rich[0]; --need starting points
THROUGH[0..LENGTH[rich]) DO
IF bRich.family # family THEN
{bPoor↑ ← bRich↑; bPoor ← bPoor + unitSize};
bRich ← bRich + unitSize;
ENDLOOP;
END;
family.stateChanged[driver, bRich.context, remove]; --toss the bum
bRich ← @rich[0]; CommHeap.zone.FREE[@bRich]; --raze the rich
driver.matrix ← DESCRIPTOR[poor, length]; --replace with poor
IF (poor # NIL) AND (family.maxBufferSize > driver.receiveBufferLen) THEN
BEGIN
maxSeen: CARDINAL ← 0; --nice number to compare against
bPoor ← @poor[0]; --get a starting value
THROUGH[0..length) DO
maxSeen ← MAX[maxSeen, bPoor.family.maxBufferSize];
bPoor ← bPoor + unitSize;
ENDLOOP;
driver.receiveBufferLen ← maxSeen; --smash it back
END;
END; --RemoveFamilyMemberInternal
Ripple: PROC [b: Buffer.Buffer, alignment: {forClient, forDriver}] = {};
<<
BEGIN
<<
Like the fine wine, it's now time.
If we're rippling for the client, then we want b.highLayer.startIndex to be
zero. That's assuming that most (all) protocols we implement are either
word aligned or simply don't care. If we're rippling for the client, the
buffer was last touched by a driver. So, we should ripple it up (increasing
values of addresses). We assert that this buffer has aleady been decapsulated
and that b.linkLayer = [@b.bufferBody, 0, SIZE[{encapsulation}]] and that
b.highLayer = [@{start of data}, n, n + {data length}];
If we're rippling for the driver, we want b.linkLayer.startIndex to be
zero. Our drivers (faces) really don't like byte aligned things, so we
move it around here in just one spot to make life simple(r). And, since the
client was the last one to touch this buffer, we will ripple the bits down
(decreasing values of indexes).
>>
LoopHole: TYPE = LONG POINTER TO PhoneeBlock;
PhoneeBlock: TYPE = RECORD[b: LONG INTEGER, start, stop: INTEGER];
AJust: PROC[p: LONG POINTER TO Environment.Block, ap, ai: INTEGER] = INLINE
{OPEN r: LOOPHOLE[p, LoopHole]; r ← [r.b + ap, r.start + ai, r.stop + ai]};
to, from: Environment.Block;
SELECT alignment FROM
(forClient) =>
BEGIN
si: INTEGER = b.highLayer.startIndex;
IF (si MOD 2) = 0 THEN AJust[@b.highLayer, (si / 2), -si]
ELSE
BEGIN
to ← from ← [
b.linkLayer.blockPointer, b.linkLayer.startIndex,
b.linkLayer.startIndex + b.fo.driver.length];
to ← [
from.blockPointer, from.startIndex + 3, from.stopIndexPlusOne + 3];
AJust[@b.linkLayer, 1, 1]; --result link layer will be odd
AJust[@b.highLayer, (si + 1) / 2, -si]; --high layer will be zero
[] ← ByteBlt.ByteBlt[to: to, from: from, overLap: move];
END;
END;
(forDriver) =>
BEGIN
si: INTEGER = b.linkLayer.startIndex;
IF (si MOD 2) = 0 THEN AJust[@b.linkLayer, -((si + 1) / 2), -si]
ELSE
BEGIN
to ← from ← [
b.linkLayer.blockPointer, b.linkLayer.startIndex,
b.linkLayer.startIndex + b.fo.driver.length];
AJust[LONG[@to], -((si + 1) / 2), -si];
AJust[@b.linkLayer, -((si + 1) / 2), -si]; --link layer will be zero
AJust[@b.highLayer, -((si + 1) / 2), si]; --high layer will be odd
[] ← ByteBlt.ByteBlt[to: to, from: from, overLap: move];
END;
END;
ENDCASE;
END; --Ripple
>>
EncapsulateAndTransmit: PUBLIC Protocol1.EncapsulatorProc =
BEGIN
--ASSUMPTION: b.fo.network and b.fo.type are set properly
type: Buffer.Type ← b.fo.type;
driver: Device ← b.fo.network;
unit: LONG POINTER TO Protocol1.MatrixRecord ← @driver.matrix[0];
IF ~driver.alive THEN
{b.fo.status ← noRouteToNetwork; PutOnGlobalDoneQueue[b]; RETURN};
THROUGH[0..LENGTH[driver.matrix]) DO
SELECT TRUE FROM
(unit.family.name # type) => NULL; --go to next one
(unit.family.status = alive) => --and it's healthy
BEGIN
b.fo.context ← unit.context; --set context for encapsulation
unit.encapsulator[b, immediate]; --call the encapsulation code
IF unit.family.spy # NIL THEN b ← unit.family.spy[b, send];
SELECT TRUE FROM
(b = NIL) => RETURN; --spy proc consumed our buffer!
<<(b.linkLayer.startIndex # 0) => Ripple[b, forDriver];>>
ENDCASE;
driver.sendRawBuffer[b];
RETURN; --then get out of the loop
END;
ENDCASE => EXIT; --found family, but it was dead
unit ← unit + unitSize; --next apartment
ENDLOOP;
OrphanEncapsulate[b];
END; --EncapsulateAndTransmit
SetMaximumBufferSize: PUBLIC <<Protocol1>> ENTRY PROC[
driver: Device, family: Protocol1.Family, size: CARDINAL] =
BEGIN
<<
Lots of semantics here.
1) Both 'driver' and 'family' are specified
o family.maxBufferSize ← size
o driver.receiveBufferLen ← MAX family.maxBufferSize for all families
supported by 'driver'
2) ('driver' = NIL) => driver = all drivers that support 'family'
3) ('family' = NIL) => family = all families in the condo
>>
EachHouse: INTERNAL PROC[family: Protocol1.Family] =
BEGIN
IF driver = NIL THEN
FOR driver ← Driver.GetDeviceChain[], driver.next UNTIL driver = NIL DO
driver.receiveBufferLen ← TakeCensus[driver]; ENDLOOP
ELSE driver.receiveBufferLen ← TakeCensus[driver];
END; --EachHouse
TakeCensus: INTERNAL PROC[driver: Device] RETURNS[census: NATURAL ← size] =
BEGIN
length: NATURAL = LENGTH[driver.matrix]; --number of houses
house: LONG POINTER TO Protocol1.MatrixRecord ← @driver.matrix[0];
THROUGH[0..length) DO
census ← MAX[census, house.family.maxBufferSize]; --compare sizes
house ← house + SIZE[Protocol1.MatrixRecord]; --go to next house
ENDLOOP;
END; --TakeCensus
IF family = NIL THEN
FOR i: Protocol1.FamilyIndex IN[0..condo.occupied) DO
EachHouse[condo.unit[i]]; ENDLOOP
ELSE {family.maxBufferSize ← size; EachHouse[family]};
END; --SetMaximumBufferSize
SetSpyProc: PUBLIC ENTRY PROC [
spy: SpecialCommunication.SpyProc, name: SpecialCommunication.SpyType]
RETURNS [oldSpy: SpecialCommunication.SpyProc] =
BEGIN
IF name = orphan THEN {oldSpy ← orphanSpy; orphanSpy ← spy}
ELSE
FOR fi: Protocol1.FamilyIndex IN[0..condo.occupied) DO
IF condo.unit[fi].name = name THEN
{oldSpy ← condo.unit[fi].spy; condo.unit[fi].spy ← spy};
ENDLOOP;
END; --SetSpyProc
OrphanDecapsulate: PROC[b: Buffer.Buffer] =
BEGIN
IF orphanSpy # NIL THEN b ← orphanSpy[b, receive];
IF b # NIL THEN b.requeueProcedure[b];
END; --OrphanDecapsulate
OrphanEncapsulate: PROC[b: Buffer.Buffer] =
BEGIN
b.fo.status ← aborted;
IF orphanSpy # NIL THEN b ← orphanSpy[b, send];
IF b # NIL THEN b.requeueProcedure[b];
END; --OrphanEncapsulate
DispatcherOn: PUBLIC PROC =
BEGIN
orphanSpy ← NIL;
Buffer.QueueInitialize[@globalInputQueue];
Buffer.QueueInitialize[@globalOutputQueue];
mainFork ← FORK MainDispatcher[];
END;
DispatcherOff: PUBLIC PROC =
BEGIN
IF condo.occupied # 0 THEN Driver.Glitch[TenentRegistered];
Process.Abort[mainFork]; JOIN mainFork;
Buffer.QueueCleanup[@globalInputQueue];
Buffer.QueueCleanup[@globalOutputQueue];
END;
PutOnGlobalInputQueue: PUBLIC ENTRY PROC[b: Buffer.Buffer] =
{Buffer.Enqueue[@globalInputQueue, b]; NOTIFY dispatcherReady};
PutOnGlobalDoneQueue: PUBLIC PROC[b: Buffer.Buffer] =
BEGIN
LockedDone: ENTRY PROC = INLINE
{Buffer.Enqueue[@globalOutputQueue, b]; NOTIFY dispatcherReady};
SELECT TRUE FROM
(b.requeueProcedure # Driver.ReturnFreeBuffer), (b.fo.allNets) =>
BEGIN
<<IF b.highLayer.startIndex # 0 THEN Ripple[b, forClient];>>
LockedDone[];
END;
ENDCASE => Driver.ReturnFreeBuffer[b];
END;
MainDispatcher: PUBLIC PROC =
BEGIN
GrabOutputBuffer: ENTRY PROC = INLINE
{b ← Buffer.Dequeue[@globalOutputQueue]};
GrabInputBuffer: ENTRY PROC = INLINE
{b ← Buffer.Dequeue[@globalInputQueue]};
CheckQueuesAndWait: ENTRY PROC = INLINE
BEGIN
ENABLE UNWIND => NULL;
SELECT TRUE FROM
(globalInputQueue.length # 0) => NULL;
(globalOutputQueue.length # 0) => NULL;
ENDCASE => WAIT dispatcherReady;
END; --CheckQueuesAndWait
b: Buffer.Buffer;
network: Device;
unit: Protocol1.Matrix;
Process.SetPriority[CommPriorities.receiver];
--UNTIL ABORTED-- DO
ENABLE ABORTED => EXIT;
WHILE globalOutputQueue.length # 0 DO
--process send buffers first, they should be fast and easy
GrabOutputBuffer[];
WHILE b.fo.allNets --OR pickUpNextBuffer OR done-- DO
network ← NARROW[b.fo.network, Device]; --test last net dead case
IF network = NIL THEN GOTO done; --shouldn't even be here
b.fo.network ← network ← NARROW[network.next, Device]; --advance
IF network = NIL THEN GOTO done; --done now, huh
IF network.alive THEN
BEGIN
unit ← @network.matrix[0];
THROUGH[0..LENGTH[network.matrix]) DO
family: Protocol1.Family ← unit.family;
SELECT TRUE FROM
(b.fo.type # family.name) => {unit ← unit + unitSize; LOOP};
(family.status # alive) => EXIT; --family in no position to use
ENDCASE =>
BEGIN
b.fo.context ← unit.context; family.broadcast[b];
GOTO pickUpNextBuffer; --as soon as we find a family
END;
--this driver doesn't support family - does the next?
ENDLOOP;
END;
REPEAT
pickUpNextBuffer => NULL; --b was rebroadcasted
done =>
{b.fo.context ← NIL; b.fo.allNets ← FALSE; b.requeueProcedure[b]};
FINISHED =>
{b.fo.context ← NIL; b.fo.allNets ← FALSE; b.requeueProcedure[b]};
ENDLOOP; --WHILE b.fo.allNets
ENDLOOP; --WHILE globalOutputQueue.length # 0
IF globalInputQueue.length # 0 THEN
BEGIN
GrabInputBuffer[]; --get buffer from driver
unit ← @NARROW[b.fo.network, Device].matrix[0];
-- give it to the right router, and it requeues the buffer
THROUGH[0..LENGTH[NARROW[b.fo.network, Device].matrix]) DO
IF unit.family.status = dead THEN LOOP; --can't use this one
b.fo.context ← unit.context; --copy in context
SELECT (b.fo.type ← unit.decapsulator[b]) FROM
(vagrant) => NULL; --failed - try next family on block
(orphan) => {OrphanDecapsulate[b]; EXIT}; --bad at level-0
ENDCASE =>
BEGIN --that's the one
<<IF b.highLayer.startIndex # 0 THEN Ripple[b, forClient];>>
IF unit.family.spy # NIL THEN b ← unit.family.spy[b, receive];
IF b # NIL THEN unit.family.receive[b];
EXIT;
END;
unit ← unit + unitSize; --next unit
REPEAT FINISHED => {b.fo.type ← orphan; OrphanDecapsulate[b]};
ENDLOOP;
END;
CheckQueuesAndWait[]; --see if we should loop again right away
ENDLOOP;
END;
-- initialization
Process.DisableTimeout[@dispatcherReady];
Process.EnableAborts[@dispatcherReady];
END. -- DispatcherImpl module
LOG
14-May-84 16:11:41 AOF Post Klamath
4-Apr-85 15:54:56 AOF Tidying up the condo when tenant moves out
26-Jun-85 14:58:32 AOF call stateChanged proc when removing family members
26-Jul-85 13:11:08 AOF UNWIND => NULL in CheckQueuesAndWait
21-Oct-85 13:19:37 AOF UNWIND => Rework of dispatcher's outQ processing
9-Apr-86 16:27:39 AOF Processing of buffers with .allNets =TRUE
7-May-86 12:22:15 AOF ....and check for b.allNets first
9-May-86 15:59:53 SMA Support for different sizes of encapsulation.
17-Jun-86 18:36:11 AOF Families recording their buffer size requirements.
28-Aug-86 10:46:17 AOF Use priorityPilotRealtimeSwappable priority
12-Nov-86 18:30:55 AOF Ripple wine and odd aligment buffers
11-Feb-87 18:08:12 AOF AR 10254: Dangling pointer in DispatcherImpl (James)
23-Sep-87 9:42:01 AOF Remove the Ripple code
19-Oct-87 12:44:11 AOF Remove the Ripple code some more better
27-Nov-87 12:00:30 AOF AR #12346 - Scanning the matrix to set length down.