(* Ulm's Oberon Library Copyright (C) 1989-2005 by University of Ulm, SAI, D-89069 Ulm, Germany ---------------------------------------------------------------------------- Ulm's Oberon Library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Ulm's Oberon Library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ---------------------------------------------------------------------------- E-mail contact: oberon@mathematik.uni-ulm.de ---------------------------------------------------------------------------- $Id: Conditions.om,v 1.7 2005/02/09 09:53:25 borchert Exp $ ---------------------------------------------------------------------------- $Log: Conditions.om,v $ Revision 1.7 2005/02/09 09:53:25 borchert bug fix: we have to enter a busy loop even in case of interrupting events as there is a window between setup and Process.Pause Revision 1.6 2005/02/06 22:26:59 borchert bug fix: assure that the priority of asynchronous events exceeds those of interrupting events Revision 1.5 2004/09/03 08:59:34 borchert hash tab size for ConditionSet changed from 128 to 64 Revision 1.4 2004/09/01 13:32:18 borchert performance improvement: condition sets are now based on hashes Revision 1.3 2001/05/18 21:59:01 borchert SetupAsyncEvents checks now all conditions to add as much conditions as possible to setOfTrueConditions Revision 1.2 1996/01/04 16:59:56 borchert - conditions are now extensions of Disciplines.Object - some renamings: timecond -> timelimit, hint -> timecond - errors events have been replaced by assertions - WaitForAndSelect has been renamed to WaitFor (the old version of WaitFor vanished) - conditions are now tagged to allow some optimizations of the condition set operations - optimized support of async capability - redesign of blocking algorithm Revision 1.1 1994/02/22 20:06:25 borchert Initial revision ---------------------------------------------------------------------------- AFB 12/91 ---------------------------------------------------------------------------- *) MODULE ulmConditions; IMPORT Clocks := ulmClocks, Disciplines := ulmDisciplines, Events := ulmEvents, Objects := ulmObjects, Op := ulmOperations, Priorities := ulmPriorities, Process := ulmProcess, RelatedEvents := ulmRelatedEvents, Scales := ulmScales, Timers := ulmTimers, Times := ulmTimes, SYSTEM; CONST tags = 64; TYPE Tag = INTEGER; (* 0..tags-1 *) (* tags are used for the hashs *) VAR nextTag: Tag; (* 0..tags-1, 0..tags-1, ... *) TYPE Domain* = POINTER TO DomainRec; Condition* = POINTER TO ConditionRec; ConditionRec* = RECORD (Disciplines.ObjectRec) domain: Domain; tag: Tag; waitingForEvent: BOOLEAN; gotEvent: BOOLEAN; END; (* disjunctive list of conditions *) ConditionList = POINTER TO ConditionListRec; ConditionListRec = RECORD cond: Condition; next: ConditionList; END; BucketTable = ARRAY tags OF ConditionList; ConditionSet = POINTER TO ConditionSetRec; ConditionSetRec* = RECORD (Objects.ObjectRec) cardinality: INTEGER; bucket: BucketTable; (* for the iterator *) next: ConditionList; i: INTEGER; END; CONST select* = 0; timelimit* = 1; async* = 2; timecond* = 3; preconditions* = 4; TYPE CapabilitySet* = SET; (* OF [select..preconditions] *) TYPE SelectProc* = PROCEDURE (domain: Domain; conditionSet: ConditionSet; time: Times.Time; VAR setOfTrueConditions: ConditionSet; errors: RelatedEvents.Object; retry: BOOLEAN; VAR interrupted: BOOLEAN) : BOOLEAN; (* needs only to be provided if select is in caps; if timelimit isn't in caps then time is guaranteed to be equal to NIL *) TestProc* = PROCEDURE (domain: Domain; condition: Condition; errors: RelatedEvents.Object) : BOOLEAN; SendEventProc* = PROCEDURE (domain: Domain; condition: Condition; event: Events.Event; errors: RelatedEvents.Object) : BOOLEAN; (* sendevent needs only to be provided if async is in caps *) GetTimeProc* = PROCEDURE (domain: Domain; conditionSet: ConditionSet; VAR nextTime: Times.Time; VAR nextCond: Condition; errors: RelatedEvents.Object); (* needs only to be provided if timecond is in caps *) PreConditionsProc* = PROCEDURE (domain: Domain; condition: Condition; VAR preconds: ConditionSet; errors: RelatedEvents.Object) : BOOLEAN; (* needs only to be provided if preconditions is in caps *) Interface* = POINTER TO InterfaceRec; InterfaceRec* = RECORD (Objects.ObjectRec) test*: TestProc; select*: SelectProc; sendevent*: SendEventProc; gettime*: GetTimeProc; preconditions*: PreConditionsProc; END; Description* = POINTER TO DescriptionRec; DescriptionRec* = RECORD (Objects.ObjectRec) caps*: CapabilitySet; internal*: BOOLEAN; (* value does not change during Process.Pause? *) END; TYPE DomainRec* = RECORD (Disciplines.ObjectRec) if: Interface; desc: Description; END; TYPE GetTimeOfNextTryProc* = PROCEDURE (iteration: INTEGER; VAR time: Times.Time); (* return a relative time measure *) VAR getTimeOfNextTry: GetTimeOfNextTryProc; TYPE WakeupEvent = POINTER TO WakeupEventRec; WakeupEventRec = RECORD (Events.EventRec) condition: Condition; END; VAR nodelay: Times.Time; wakeupEventType: Events.EventType; (* used for busy loops only *) PROCEDURE WakeupHandler(event: Events.Event); BEGIN WITH event: WakeupEvent DO event.condition.gotEvent := TRUE; END; END WakeupHandler; PROCEDURE SetGetTimeOfNextTryProc*(p: GetTimeOfNextTryProc); BEGIN getTimeOfNextTry := p; END SetGetTimeOfNextTryProc; PROCEDURE GetTimeOfNextTry(iteration: INTEGER; VAR time: Times.Time); BEGIN Times.CreateAndSet(time, Times.relative, 0, 1, 0); iteration := iteration DIV 5; IF iteration > 8 THEN iteration := 8; END; WHILE iteration > 0 DO Op.Add2(SYSTEM.VAL(Op.Operand, time), time); DEC(iteration); END; END GetTimeOfNextTry; PROCEDURE CreateSet*(VAR conditionSet: ConditionSet); VAR i: INTEGER; cset: ConditionSet; BEGIN NEW(cset); cset.cardinality := 0; (* commented out for reasons of efficiency as NEW delivers 0-initialized areas anyway i := 0; WHILE i < tags DO conditionSet.bucket[i] := NIL; INC(i); END; *) cset.next := NIL; cset.i := 0; conditionSet := cset; END CreateSet; PROCEDURE Incl*(conditionSet: ConditionSet; condition: Condition); VAR listp: ConditionList; new: ConditionList; i: INTEGER; BEGIN (* check if condition is already present in conditionSet *) i := condition.tag; listp := conditionSet.bucket[i]; WHILE (listp # NIL) & (listp.cond # condition) DO listp := listp.next; END; IF listp # NIL THEN (* already in set *) RETURN END; NEW(new); new.cond := condition; new.next := conditionSet.bucket[i]; conditionSet.bucket[i] := new; INC(conditionSet.cardinality); END Incl; PROCEDURE Excl*(conditionSet: ConditionSet; condition: Condition); VAR prev, listp: ConditionList; i: INTEGER; BEGIN i := condition.tag; listp := conditionSet.bucket[i]; prev := NIL; WHILE (listp # NIL) & (listp.cond # condition) DO prev := listp; listp := listp.next; END; IF listp = NIL THEN (* condition not in set *) RETURN END; IF prev = NIL THEN conditionSet.bucket[i] := listp.next; ELSE prev.next := listp.next; END; DEC(conditionSet.cardinality); (* make the iterator more robust *) IF conditionSet.next = listp THEN conditionSet.next := listp.next; END; END Excl; PROCEDURE In*(conditionSet: ConditionSet; condition: Condition) : BOOLEAN; VAR listp: ConditionList; BEGIN listp := conditionSet.bucket[condition.tag]; WHILE (listp # NIL) & (listp.cond # condition) DO listp := listp.next; END; RETURN listp # NIL END In; PROCEDURE Union*(result: ConditionSet; set: ConditionSet); VAR listp: ConditionList; newelem, newelems: ConditionList; count: INTEGER; (* # of added elements in newelems *) i: INTEGER; BEGIN count := 0; i := 0; WHILE i < tags DO listp := set.bucket[i]; newelems := result.bucket[i]; IF newelems = NIL THEN WHILE listp # NIL DO NEW(newelem); newelem.cond := listp.cond; newelem.next := newelems; newelems := newelem; INC(count); listp := listp.next; END; ELSE WHILE listp # NIL DO IF ~In(result, listp.cond) THEN NEW(newelem); newelem.cond := listp.cond; newelem.next := newelems; newelems := newelem; INC(count); END; listp := listp.next; END; END; result.bucket[i] := newelems; INC(i); END; INC(result.cardinality, count); END Union; PROCEDURE Union3*(VAR result: ConditionSet; set1, set2: ConditionSet); BEGIN CreateSet(result); Union(result, set1); Union(result, set2); END Union3; PROCEDURE Card*(conditionSet: ConditionSet) : INTEGER; BEGIN RETURN conditionSet.cardinality END Card; PROCEDURE ExamineConditions*(conditionSet: ConditionSet); BEGIN conditionSet.next := NIL; conditionSet.i := 0; END ExamineConditions; PROCEDURE GetNextCondition*(conditionSet: ConditionSet; VAR condition: Condition) : BOOLEAN; VAR i: INTEGER; BEGIN IF conditionSet.next = NIL THEN i := conditionSet.i; WHILE (i < tags) & (conditionSet.bucket[i] = NIL) DO INC(i); END; conditionSet.i := i + 1; IF i >= tags THEN RETURN FALSE END; conditionSet.next := conditionSet.bucket[i]; END; condition := conditionSet.next.cond; conditionSet.next := conditionSet.next.next; RETURN TRUE END GetNextCondition; PROCEDURE InitDomain*(domain: Domain; if: Interface; desc: Description); BEGIN domain.if := if; domain.desc := desc; END InitDomain; PROCEDURE Init*(condition: Condition; domain: Domain); BEGIN condition.domain := domain; condition.tag := nextTag; nextTag := (nextTag + 1) MOD tags; condition.waitingForEvent := FALSE; condition.gotEvent := FALSE; END Init; PROCEDURE Test*(condition: Condition; errors: RelatedEvents.Object) : BOOLEAN; BEGIN IF condition.waitingForEvent & ~condition.gotEvent THEN RETURN FALSE ELSE RETURN condition.domain.if.test(condition.domain, condition, errors) END; END Test; PROCEDURE CommonDomain(cset: ConditionSet; VAR domain: Domain) : BOOLEAN; VAR dom: Domain; i: INTEGER; listp: ConditionList; BEGIN dom := NIL; i := 0; WHILE i < tags DO listp := cset.bucket[i]; WHILE listp # NIL DO IF dom = NIL THEN dom := listp.cond.domain; ELSIF dom # listp.cond.domain THEN RETURN FALSE END; listp := listp.next; END; INC(i); END; domain := dom; RETURN dom # NIL END CommonDomain; PROCEDURE SimpleWaitForAndSelect( conditionSet: ConditionSet; VAR setOfTrueConditions: ConditionSet; errors: RelatedEvents.Object); (* simple means that we don't need to take care of preconditions *) TYPE List = POINTER TO ListRec; Element = POINTER TO ElementRec; ListRec = RECORD head: Element; END; Ring = POINTER TO RingRec; RingRec = RECORD (ListRec) tail: Element; END; ElementRec = RECORD next: Element; domain: Domain; cset: ConditionSet; END; VAR domain: Domain; interrupted: BOOLEAN; ok: BOOLEAN; PROCEDURE SortConditions(VAR asyncList, timeList, others: List; VAR ring: Ring; VAR otherAreInternal: BOOLEAN); (* sort conditions into several lists: ayncList: list of conditions for which we can setup an event; after this setup we needn't to take care of them timeList: list of time conditions (based on system clock) ring: conditions which support select & timelimit otherAreInternal: is set to TRUE if all other conditions which are not put into one of the lists above remain unaffected while pausing *) VAR listp: ConditionList; i: INTEGER; PROCEDURE CreateList(VAR list: List); BEGIN NEW(list); list.head := NIL; END CreateList; PROCEDURE CreateRing(VAR ring: Ring); BEGIN NEW(ring); ring.head := NIL; ring.tail := NIL; END CreateRing; PROCEDURE Add(condition: Condition); VAR domain: Domain; PROCEDURE AddTo(list: List); VAR elp: Element; BEGIN elp := list.head; WHILE (elp # NIL) & (elp.domain # domain) DO elp := elp.next; END; IF elp = NIL THEN NEW(elp); elp.next := list.head; elp.domain := condition.domain; CreateSet(elp.cset); list.head := elp; IF list IS Ring THEN WITH list: Ring DO IF list.tail = NIL THEN list.tail := elp; END; list.tail.next := list.head; END; END; END; Incl(elp.cset, condition); END AddTo; BEGIN (* Add *) domain := condition.domain; IF timecond IN domain.desc.caps THEN IF timeList = NIL THEN CreateList(timeList); END; AddTo(timeList); ELSIF async IN domain.desc.caps THEN IF asyncList = NIL THEN CreateList(asyncList); END; AddTo(asyncList); ELSIF (select IN domain.desc.caps) & (timelimit IN domain.desc.caps) THEN IF ring = NIL THEN CreateRing(ring); END; AddTo(ring); ELSE IF others = NIL THEN CreateList(others); END; AddTo(others); IF ~domain.desc.internal THEN otherAreInternal := FALSE; END; END; END Add; BEGIN (* SortConditions *) asyncList := NIL; timeList := NIL; ring := NIL; otherAreInternal := TRUE; i := 0; WHILE i < tags DO listp := conditionSet.bucket[i]; WHILE listp # NIL DO Add(listp.cond); listp := listp.next; END; INC(i); END; END SortConditions; PROCEDURE SetupEventHandling(condition: Condition; VAR wakeupEvent: WakeupEvent); VAR wakeup: Events.EventType; priority: Priorities.Priority; BEGIN Events.Define(wakeup); priority := Events.GetPriority() + 1; IF priority < Priorities.interrupts + 1 THEN priority := Priorities.interrupts + 1; END; Events.SetPriority(wakeup, priority); Events.Handler(wakeup, WakeupHandler); NEW(wakeupEvent); wakeupEvent.type := wakeup; wakeupEvent.condition := condition; condition.waitingForEvent := TRUE; condition.gotEvent := FALSE; END SetupEventHandling; PROCEDURE SetupAsyncEvents(list: List) : BOOLEAN; VAR elp: Element; listp: ConditionList; i: INTEGER; wakeupEvent: WakeupEvent; sendevent: SendEventProc; anythingTrue: BOOLEAN; BEGIN anythingTrue := FALSE; elp := list.head; WHILE elp # NIL DO sendevent := elp.domain.if.sendevent; i := 0; WHILE i < tags DO listp := elp.cset.bucket[i]; WHILE listp # NIL DO IF ~listp.cond.waitingForEvent OR listp.cond.gotEvent THEN SetupEventHandling(listp.cond, wakeupEvent); IF ~sendevent(elp.domain, listp.cond, wakeupEvent, errors) THEN IF ~anythingTrue THEN CreateSet(setOfTrueConditions); END; Incl(setOfTrueConditions, listp.cond); listp.cond.waitingForEvent := FALSE; anythingTrue := TRUE; END; END; listp := listp.next; END; INC(i); END; elp := elp.next; END; RETURN ~anythingTrue END SetupAsyncEvents; PROCEDURE Block; (* block until one of the conditions becomes TRUE *) VAR asyncList: List; (* list of domains which supports async events *) timeList: List; (* list of domains which supports timecond *) ring: Ring; (* ring of domains which support select+timelimit *) largeRing: BOOLEAN; (* >=2 ring members *) ringMember: Element; (* current ring member *) others: List; (* those which are not member of the other lists *) otherAreInternal: BOOLEAN; waitErrors: RelatedEvents.Object; queue: RelatedEvents.Queue; (* queue of waitErrors *) busyLoop: BOOLEAN; (* TRUE if we have to resort to a busy loop *) wakeupEvent: Events.Event; (* iteration event for busy loops *) loopCnt: INTEGER; (* number of iterations *) nextTime: Times.Time; minTime: Times.Time; minTimeCond: Condition; interrupted: BOOLEAN; (* interrupted select? *) highPriority: BOOLEAN; (* priority >= Priorities.interrupt? *) PROCEDURE FixToRelTime(VAR time: Times.Time); VAR currentTime: Times.Time; relTime: Times.Time; BEGIN Clocks.GetTime(Clocks.system, currentTime); Op.Sub3(SYSTEM.VAL(Op.Operand, relTime), time, currentTime); time := relTime; END FixToRelTime; PROCEDURE GetMinTime(VAR nextTime: Times.Time; VAR minCond: Condition); VAR elp: Element; time: Times.Time; condition: Condition; BEGIN (* GetMinTime *) nextTime := NIL; minCond := NIL; IF timeList # NIL THEN elp := timeList.head; WHILE elp # NIL DO elp.domain.if.gettime(domain, elp.cset, time, condition, waitErrors); IF Scales.IsAbsolute(time) THEN FixToRelTime(time); END; IF (nextTime = NIL) OR (Op.Compare(time, nextTime) < 0) THEN nextTime := time; minCond := condition; END; elp := elp.next; END; END; END GetMinTime; PROCEDURE UpdateMinTime(VAR nextTime: Times.Time; VAR minCond: Condition); VAR set: ConditionSet; time: Times.Time; cond: Condition; BEGIN IF minCond = NIL THEN nextTime := NIL; ELSE CreateSet(set); Incl(set, minCond); minCond.domain.if.gettime(minCond.domain, set, time, cond, waitErrors); IF Scales.IsAbsolute(time) THEN FixToRelTime(time); END; nextTime := time; END; END UpdateMinTime; PROCEDURE TestNonRingMembers() : BOOLEAN; PROCEDURE TestList(list: List) : BOOLEAN; VAR domain: Domain; element: Element; selected: ConditionSet; interrupted: BOOLEAN; PROCEDURE TestAndSelect(conditionSet: ConditionSet; VAR setOfTrueConditions: ConditionSet; errors: RelatedEvents.Object) : BOOLEAN; VAR listp: ConditionList; i: INTEGER; condition: Condition; anythingTrue: BOOLEAN; BEGIN (* TestAndSelect *) anythingTrue := FALSE; CreateSet(setOfTrueConditions); i := 0; WHILE i < tags DO listp := conditionSet.bucket[i]; WHILE listp # NIL DO condition := listp.cond; IF domain.if.test(domain, condition, errors) THEN Incl(setOfTrueConditions, condition); anythingTrue := TRUE; END; listp := listp.next; END; INC(i); END; RETURN anythingTrue END TestAndSelect; BEGIN (* TestList *) IF list = NIL THEN RETURN FALSE END; element := list.head; WHILE element # NIL DO domain := element.domain; IF (select IN domain.desc.caps) & (timelimit IN domain.desc.caps) THEN IF domain.if.select(domain, element.cset, nodelay, selected, waitErrors, FALSE, interrupted) THEN ASSERT(Card(selected) > 0); Union(setOfTrueConditions, selected); RETURN TRUE END; ELSE IF TestAndSelect(element.cset, selected, waitErrors) THEN Union(setOfTrueConditions, selected); RETURN TRUE END; END; element := element.next; END; RETURN FALSE END TestList; PROCEDURE TestAsyncList(list: List) : BOOLEAN; VAR element: Element; listp: ConditionList; i: INTEGER; condition: Condition; anythingFound: BOOLEAN; BEGIN IF list = NIL THEN RETURN FALSE END; anythingFound := FALSE; element := list.head; WHILE element # NIL DO i := 0; WHILE i < tags DO listp := element.cset.bucket[i]; WHILE listp # NIL DO condition := listp.cond; IF condition.gotEvent THEN Incl(setOfTrueConditions, condition); anythingFound := TRUE; END; listp := listp.next; END; INC(i); END; element := element.next; END; RETURN anythingFound END TestAsyncList; BEGIN (* TestNonRingMembers *) CreateSet(setOfTrueConditions); RETURN TestAsyncList(asyncList) OR TestList(others) END TestNonRingMembers; BEGIN (* Block *) NEW(waitErrors); RelatedEvents.QueueEvents(waitErrors); SortConditions(asyncList, timeList, others, ring, otherAreInternal); IF asyncList # NIL THEN (* set up asynchronous events for these conditions -- this should be done before the first call of TestNonRingMembers() to avoid redundant test calls *) IF ~SetupAsyncEvents(asyncList) THEN (* one of them happened to be TRUE now *) RETURN END; END; IF TestNonRingMembers() THEN RETURN END; (* check for deadlock *) ASSERT((asyncList # NIL) OR (timeList # NIL) OR (ring # NIL) OR ~otherAreInternal); highPriority := Events.GetPriority() >= Priorities.interrupts; IF ring # NIL THEN ringMember := ring.head; largeRing := ring.head # ring.head.next; ELSE ringMember := NIL; largeRing := FALSE; END; GetMinTime(minTime, minTimeCond); busyLoop := largeRing OR ~otherAreInternal OR (asyncList # NIL); loopCnt := 0; LOOP (* until one of the conditions becomes TRUE *) (* determine timelimit parameter for select *) IF busyLoop THEN getTimeOfNextTry(loopCnt + 1, nextTime); ASSERT(Op.Compare(nextTime, nodelay) > 0); IF timeList # NIL THEN IF Op.Compare(minTime, nextTime) < 0 THEN nextTime := minTime; END; END; ELSIF timeList # NIL THEN nextTime := minTime; ELSE nextTime := NIL; minTime := NIL; minTimeCond := NIL; END; IF (minTime # NIL) & (Op.Compare(minTime, nodelay) <= 0) THEN CreateSet(setOfTrueConditions); Incl(setOfTrueConditions, minTimeCond); EXIT END; IF ringMember = NIL THEN ASSERT(~highPriority); IF nextTime # NIL THEN NEW(wakeupEvent); wakeupEvent.type := wakeupEventType; Events.SetPriority(wakeupEventType, Events.GetPriority() + 1); Timers.Schedule(Clocks.system, nextTime, wakeupEvent); END; Process.Pause; ELSE IF ringMember.domain.if.select (ringMember.domain, ringMember.cset, nextTime, setOfTrueConditions, waitErrors, (* retry = *) FALSE, interrupted) THEN ASSERT(Card(setOfTrueConditions) > 0); EXIT END; (* timelimit exceeded or interrupted *) ASSERT(interrupted OR (nextTime # NIL)); IF interrupted THEN (* remove error event *) RelatedEvents.GetQueue(waitErrors, queue); ELSIF (minTimeCond # NIL) & ~busyLoop THEN (* timelimit exceeded: minTimeCond is now TRUE *) CreateSet(setOfTrueConditions); Incl(setOfTrueConditions, minTimeCond); EXIT END; END; IF TestNonRingMembers() THEN EXIT END; IF timeList # NIL THEN UpdateMinTime(minTime, minTimeCond); END; INC(loopCnt); END; (* forward error events to error parameter of SimpleWaitForAndSelect *) RelatedEvents.GetQueue(waitErrors, queue); RelatedEvents.AppendQueue(errors, queue); END Block; BEGIN (* SimpleWaitForAndSelect *) IF CommonDomain(conditionSet, domain) & (select IN domain.desc.caps) THEN ok := domain.if.select (domain, conditionSet, NIL, setOfTrueConditions, errors, (* retry = *) TRUE, interrupted); (* a return value of FALSE is only to be expected if a time limit is given or if retry = FALSE *) ASSERT(ok); ELSE Block; END; END SimpleWaitForAndSelect; PROCEDURE WaitFor*(conditionSet: ConditionSet; VAR setOfTrueConditions: ConditionSet; errors: RelatedEvents.Object); VAR listp: ConditionList; i: INTEGER; testSet: ConditionSet; preconds: ConditionSet; domain: Domain; selected: ConditionSet; anyPreconditions: BOOLEAN; PROCEDURE PretestClosure(testSet, preconds: ConditionSet); VAR listp: ConditionList; i: INTEGER; domain: Domain; morePreconditions: ConditionSet; evenMorePreconditions: ConditionSet; BEGIN REPEAT CreateSet(morePreconditions); i := 0; WHILE i < tags DO listp := preconds.bucket[i]; WHILE listp # NIL DO domain := listp.cond.domain; IF (preconditions IN domain.desc.caps) & domain.if.preconditions(domain, listp.cond, evenMorePreconditions, errors) & (evenMorePreconditions # NIL) & (Card(evenMorePreconditions) > 0) THEN Union(morePreconditions, evenMorePreconditions); ELSE Incl(testSet, listp.cond); END; listp := listp.next; END; INC(i); END; preconds := morePreconditions; UNTIL Card(preconds) = 0 END PretestClosure; BEGIN (* WaitFor *) ASSERT(conditionSet.cardinality > 0); LOOP CreateSet(testSet); anyPreconditions := FALSE; i := 0; WHILE i < tags DO listp := conditionSet.bucket[i]; WHILE listp # NIL DO domain := listp.cond.domain; IF (preconditions IN domain.desc.caps) & domain.if.preconditions(domain, listp.cond, preconds, errors) & (preconds # NIL) & (Card(preconds) > 0) THEN PretestClosure(testSet, preconds); anyPreconditions := TRUE; ELSE Incl(testSet, listp.cond); END; listp := listp.next; END; INC(i); END; SimpleWaitForAndSelect(testSet, selected, errors); IF ~anyPreconditions THEN setOfTrueConditions := selected; EXIT END; i := 0; WHILE i < tags DO listp := selected.bucket[i]; WHILE listp # NIL DO IF ~In(conditionSet, listp.cond) THEN Excl(selected, listp.cond); END; listp := listp.next; END; INC(i); END; IF Card(selected) > 0 THEN setOfTrueConditions := selected; EXIT END; END; ASSERT(Card(setOfTrueConditions) > 0); END WaitFor; BEGIN SetGetTimeOfNextTryProc(GetTimeOfNextTry); Times.CreateAndSet(nodelay, Times.relative, 0, 0, 0); nextTag := 0; Events.Define(wakeupEventType); Events.Handler(wakeupEventType, Events.NilHandler); END ulmConditions.