ThPartySupervisorImpl.mesa
Copyright © 1985, 1986 by Xerox Corporation. All rights reserved.
Last modified by D. Swinehart, July 5, 1986 2:13:59 pm PDT
Polle Zellweger (PTZ) August 22, 1985 1:59:56 pm PDT
DIRECTORY
BasicTime USING [ Now, Period ],
LarkPlay USING [ MergeToneSpecs, ToneSpec, ToneSpecRec ],
Process USING [ Detach, EnableAborts, SecondsToTicks, SetTimeout ],
RefID USING [ nullID, Release ],
RPC USING [ CallFailed ],
SafeStorage USING [ GetCanonicalType, Type ],
ThNet USING [ pd ],
ThParty USING [ Deregister, SetRingEnable ],
ThPartyMonitorImpl,
ThPartyPrivate USING [
CFRef, ConversationBody, ConversationData, DehandleConv, DehandleParty, DestroyConversation, Distribute, DistributionProc, FindOtherParty, GetEvent, MakeServiceRname, PartyBody, PartyData, RingMethod, outsideRingTune, Verify ],
Thrush USING [ ConversationHandle, ConvEvent, Credentials, Disposition, EventSequence, EventSequenceBody, H, HandleFault, KillHandle, NB, nullHandle, PartyHandle, Priorities, SHHH, StateID, ThHandle ],
Triples USING [ Any, Erase, Foreach, ForeachProc, Select ],
ThSmartsRpcControl,
TU USING [ MakeUnique],
VoiceUtils USING [ Problem ]
;
ThPartySupervisorImpl: CEDAR MONITOR LOCKS root
IMPORTS
BasicTime,
LarkPlay,
Process,
root: ThPartyMonitorImpl,
RefID,
RPC,
SafeStorage,
--SpyLog,--
ThNet,
Thrush,
ThParty,
ThPartyPrivate,
ThSmartsRpcControl,
Triples,
TU,
VoiceUtils
EXPORTS ThPartyPrivate
SHARES ThPartyMonitorImpl = {
Copies, Obligatory Concrete
CFRef: TYPE = ThPartyPrivate.CFRef;
ConversationData: TYPE = ThPartyPrivate.ConversationData;
RTConvType: SafeStorage.Type = SafeStorage.GetCanonicalType[CODE[ThPartyPrivate.ConversationBody]];
ConvEvent: TYPE = Thrush.ConvEvent;
Credentials: TYPE = Thrush.Credentials;
H: PROC[r: REF] RETURNS [Thrush.ThHandle] = INLINE { RETURN[LOOPHOLE[Thrush.H[r]]]; };
NB: TYPE = Thrush.NB;
PartyData: TYPE = ThPartyPrivate.PartyData;
RTPartyType: SafeStorage.Type = SafeStorage.GetCanonicalType[CODE[ThPartyPrivate.PartyBody]];
StateID: TYPE = Thrush.StateID;
Types
maxSpvrInterval: NAT ← 10;
ringTuneDelay: NAT ← 2400; -- ms. delay between tune starts.
Supervision Process . . . One per party
Supervisor: PUBLIC PROC[ party: PartyData ] = {
event: ConvEvent;
toDo, failing, latest: BOOL�LSE;
informationOnly: BOOLEANFALSE;
Inform: ThPartyPrivate.DistributionProc = {
PROC[smarts: SmartsData ] RETURNS [ d: Thrush.Disposition ];
ENABLE RPC.CallFailed => {
smarts.canProgress←FALSE;
ThParty.Deregister[smartsID: H[smarts]];
RETRY; };
yourParty: BOOL = event.credentials.partyID=H[party];
d←pass;
IF ~smarts.canProgress THEN RETURN;
IF event.keyTable=NIL AND event.intervalSpecs=NIL AND (event.proseSpecs=NIL OR (event.proseSpecs.first.type=request AND party.type=individual)) AND
(NOT yourParty) AND (event.state#initiating) THEN RETURN;
Don't report uninteresting events. Allow request proseSpecs to get to service (for speech) and/or trunk (for feeping) smarts. (Note: a proseSpec list will be either wholly requests or some combination of started and finished at this level.) Need to change if proseSpecs used to communicate with own Lark.
I think this is a hack. July 23, 1984 3:06 pm PDT
IF yourParty AND smarts.properties.role=voiceTerminal THEN
SetupRingTunes[event];
Tell only voiceTerminals about the ring or ringback tune
SELECT (d ← smarts.interface.Progress[shh: smarts.shh, smartsID: H[smarts],
informationOnly: informationOnly, yourParty: yourParty,
event: event, latestEvent: latest ]) FROM
actedAndPass => informationOnly←TRUE;
willAlwaysPassThisRequest => smarts.canProgress←FALSE;
ENDCASE;
More hack. July 23, 1984 3:06 pm PDT
event.ringTune ← NIL;
};
TRUSTED {
Process.EnableAborts[@party.actionNeeded];
Process.SetTimeout[@party.actionNeeded, Process.SecondsToTicks[maxSpvrInterval]];
};
DO
ENABLE ABORTED => CONTINUE;
<<Party can't fail until all its smarts do. Likely this abort was issued by operator from debugger due to an RPC failure that will be worse next time. If that doesn't work, must fail party by failing all of its smarts. Things aren't very clean in here.>>
{ party.partyFailed ← TRUE; CONTINUE; };
[ event, toDo, latest ] ← ScanPostOrWait[party];
Have discovered a change in state that our Smarts should know about. The Smarts will now learn about it, even if the state changes again before they can act on the change.
informationOnly ← FALSE;
IF toDo THEN []←ThPartyPrivate.Distribute[party: party, proc: Inform]
ELSE IF failing THEN EXIT
ELSE IF party.partyFailed THEN failing←TRUE -- one more pass to clean up!
ELSE IF party.supervisor=NIL THEN RETURN; -- vanish to keep local frames to minimum.
ENDLOOP;
EndParty[party];
};
Supervise: PUBLIC INTERNAL PROC[party: PartyData] = TRUSTED {
IF party.supervisor=NIL THEN
Process.Detach[party.supervisor ← FORK Supervisor[party]];
NOTIFY party.actionNeeded; -- Spurious if process just spawned?
};
ScanPostOrWait: ENTRY PROC[ party: PartyData ]
RETURNS [ event: ConvEvent, toDo: BOOLFALSE, latest: BOOL ] = {
ENABLE UNWIND => NULL;
Scanner: INTERNAL Triples.ForeachProc = TRUSTED {
PROC[trip: TripleRec] RETURNS [continue: BOOLEAN←TRUE]
conv: ConversationData;
WITH trip.att SELECT FROM
r: CFRef => {
cfRef: CFRef←NIL;
conv ← NARROW[trip.obj];
IF r.lastNotedID<conv.currentStateID THEN cfRef ← r
ELSE IF r.event.state=idle THEN Dissolve[r, conv, party];
IF cfRef#NIL THEN {
cfRef.lastNotedID ← cfRef.lastNotedID+1;
toDo←TRUE;
latest ←
cfRef.lastNotedID = conv.currentStateID OR cfRef.lastNotedID = cfRef.lastPostedID;
event ← ThPartyPrivate.GetEvent[conv, cfRef.lastNotedID];
RETURN[FALSE];
};
};
ENDCASE;
};
Triples.Foreach[Triples.Any, Triples.Any, party, Scanner];
IF ~toDo AND ~party.partyFailed THEN
IF party.numConvs=0 THEN party.supervisor←NIL ELSE WAIT party.actionNeeded;
};
Distribution utility. Basic Party distribution function << At last!! >>
Distribute: PUBLIC PROC[ party: PartyData, proc: ThPartyPrivate.DistributionProc ]
RETURNS [ d: Thrush.Disposition←pass ] = {
priorities: Thrush.Priorities;
IF party = NIL THEN RETURN[pass];
priorities ← NARROW[Triples.Select[$Priorities, party, -- priorities -- ]];
Perform distribution in order of this Party's priorities
FOR priorities: Thrush.Priorities ← NARROW[Triples.Select[$Priorities, party, --priorities--]], priorities.rest
WHILE priorities#NIL DO
<<Flatten next priority level, for now>>
FOR subpriorities: Thrush.Priorities ← NARROW[priorities.first], subpriorities.rest
WHILE subpriorities#NIL DO
priority: ATOMNARROW[subpriorities.first];
DistOne: Triples.ForeachProc = {
d ← proc[smarts: NARROW[trip.val]];
SELECT d FROM
actedAndStop => RETURN[FALSE];
actedAndPass, pass, willAlwaysPassThisRequest => RETURN[TRUE];
ENDCASE=> {
VoiceUtils.Problem[remark: "Invalid distribute return code", where: $System];
RETURN[TRUE];
};
};
Distribute to all Smarts at this priority level.
Triples.Foreach[priority, party, Triples.Any, DistOne];
IF d=actedAndStop THEN RETURN; ENDLOOP; ENDLOOP; };
Dissolve: INTERNAL PROC[cfRef: CFRef, conv: ConversationData, party: PartyData] = {
If Post generates new work to do, stop and do it, then cycle again.
Post is probably the wrong word for this activity, now. But the work needs doing.
IF cfRef.event.state#idle THEN RETURN;
IF cfRef.socket#RefID.nullID THEN [] ← RefID.Release[cfRef.socket];
cfRef.socket ← RefID.nullID;
Triples.Erase[cfRef, conv, party];
conv.numParties ← conv.numParties-1;
party.numConvs ← party.numConvs-1;
IF conv.numParties=0 THEN ThPartyPrivate.DestroyConversation[conv];
When a trunk party leaves a conversation, it loses its identity (<< something wrong here? >>)
When a recording party leaves, it reverts to available.
TRUSTED { SELECT party.type FROM
trunk => {
party.outgoing ← NIL;
party.reservedBy ← Thrush.nullHandle;
Triples.Erase[$RnameForTrunk, party, Triples.Any];
};
service => {
party.reservedBy ← Thrush.nullHandle;
TU.MakeUnique[$RnameForParty, party,
ThPartyPrivate.MakeServiceRname[party.serviceName].serviceRnameAtom];
};
ENDCASE;
};
};
EndParty: ENTRY PROC[party: PartyData] = {
Thrush.KillHandle[H[party], RTPartyType!Thrush.HandleFault=>CONTINUE];
Triples.Erase[Triples.Any, party, Triples.Any];
party.supervisor ← NIL;
};
GetHistory: PUBLIC ENTRY PROC[
shhh: Thrush.SHHH,
credentials: Credentials,
firstState: StateID,
lastState: StateID -- default: get latest
] RETURNS [ nb: Thrush.NB, events: Thrush.EventSequence←NIL ] = {
ENABLE UNWIND => NULL;
conv: ConversationData;
numStates: NAT;
[conv, , , nb] ← ThPartyPrivate.Verify[credentials];
IF nb#success OR conv=NIL OR conv.currentStateID=0 THEN RETURN;
IF lastState=0 OR lastState>conv.currentStateID THEN lastState𡤌onv.currentStateID;
SELECT firstState FROM
<1 => firstState𡤁
>conv.currentStateID => RETURN;
ENDCASE;
IF lastState<firstState THEN RETURN;
numStates ← lastState-firstState+1;
events←NEW[Thrush.EventSequenceBody[numStates]];
FOR i: NAT IN [0..numStates) DO events[i] ← conv.log[i+firstState]; ENDLOOP; };
SetupRingTunes: PROC[event: Thrush.ConvEvent] = TRUSTED {
partyID: Thrush.PartyHandle = event.credentials.partyID;
party: PartyData = ThPartyPrivate.DehandleParty[partyID];
otherParty: PartyData;
otherTune: LarkPlay.ToneSpec;
otherMethod: ThPartyPrivate.RingMethod ← standard;
divisor: NAT ← 1;
If timed, see if time has expired. If so, switch back to default ring tune
SELECT event.state FROM ringing, maybe => NULL; ENDCASE => RETURN;
SELECT party.type FROM
individual => {
SELECT party.ringEnable FROM
offTimed, subduedTimed =>
IF BasicTime.Period[from: party.ringTime, to: BasicTime.Now[]] > 0 THEN
ThParty.SetRingEnable[partyID: partyID, ringEnable: party.defaultRingEnable];
ENDCASE;
SELECT party.ringEnable FROM
offTimed, off => RETURN;
ENDCASE;
otherParty ← ThPartyPrivate.DehandleParty[
ThPartyPrivate.FindOtherParty[partyID,
ThPartyPrivate.DehandleConv[event.credentials.convID]].partyID];
IF otherParty#NIL THEN SELECT otherParty.type FROM
individual => {
divisor ← 2; otherMethod ← otherParty.ringDo;
IF otherMethod # standard THEN otherTune ← otherParty.ringTune;
};
trunk => IF event.state=ringing THEN {
otherTune ← ThPartyPrivate.outsideRingTune; otherMethod ← bothTunes;
};
ENDCASE;
SELECT event.state FROM
maybe => {
IF otherTune=NIL THEN RETURN;
event.ringTune ← NEW[LarkPlay.ToneSpecRec ← otherTune^];
event.ringTune.volume ← ThNet.pd.tonesVolume+2;
event.ringTune.repeatIndefinitely ← TRUE;
RETURN;
};
ringing => NULL;
ENDCASE => RETURN;
event.ringTune ← party.ringTune;
IF party.ringDo # bothTunes OR otherMethod # bothTunes OR otherTune=NIL THEN RETURN;
event.ringTune ←
LarkPlay.MergeToneSpecs[event.ringTune, otherTune, divisor, ringTuneDelay];
};
ENDCASE;
};
}.
Swinehart, May 15, 1985 10:35:09 am PDT
Cedar 6.0
changes to: Dissolve (new way to idle service (recording) party), proseSpecs test in Inform
Swinehart, May 20, 1985 1:16:28 pm PDT
PartyBody is not a variant record any more
changes to: Dissolve, SetupRingTunes
Swinehart, May 22, 1985 12:02:13 pm PDT
ServiceName changes
changes to: Dissolve
Swinehart, May 22, 1985 12:08:44 pm PDT
recording => service
changes to: Dissolve
Polle Zellweger (PTZ) August 14, 1985 4:22:41 pm PDT
Don't report proseSpec transitions to the individual's party -- would have to be changed to use proseSpecs to send tunes to your own Lark. Idea is to keep traffic of large ropes down. Since Dan is using proseSpecs to make the trunk feep to his bank, have to report them to trunk parties.
changes to: Inform (local of Supervisor)
Polle Zellweger (PTZ) August 22, 1985 1:59:57 pm PDT
changes to: Inform (local of Supervisor)
Swinehart, May 17, 1986 3:19:05 pm PDT
Cedar 6.1
changes to: DIRECTORY, ThPartySupervisorImpl, DistOne