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 ]
;
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;
Supervision Process . . . One per party
Supervisor:
PUBLIC PROC[ party: PartyData ] = {
event: ConvEvent;
toDo, failing, latest: BOOLLSE;
informationOnly: BOOLEAN←FALSE;
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: BOOL←FALSE, 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: ATOM ← NARROW[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;
};
}.