<> <> <> <<>> <<>> <> <<>> <<>> DIRECTORY Atom USING [GetPName, MakeAtom], Camelot USING [tidT], Process USING [-- Detach, -- MsecToTicks, SetTimeout], RedBlackTree USING [Compare, Create, Delete, GetKey, Insert, Lookup, Table], Rope USING [Concat, Equal, Match, ROPE, Substr], YggDID USING [DID, EqualDIDs], YggDIDMap USING [AddComponentFile, GetComponentFiles, OpenDocumentFromDID, RemoveComponentFile], YggDIDMapPrivate USING [Document, DocumentRep], YggDIDPrivate USING [DIDRep], YggEnvironment USING [nullTransID, TransID], YggFile USING [Create, Delete, FileFromComponentFiles, FileUseFromComponentFiles], YggFixedNames USING [ChildContainers, Children, IndexPrefix, IndexPrefixSize, ParentContainers, Parents], YggIndex USING [DeleteEntry, EntryProc, EnumerateEntries, Index, Open, WriteEntry], YggIndexMaint USING [], YggInternal USING [FileHandle], YggNav USING [GetAllProperties, GetContainersInContainer, GetObjectsInContainer, GetPatternsInContainer, GetSuperContainers], YggRep USING [Attribute, AttributeValue, CompareTimeStamps, did, MaxTimeStamp, metaAttributeMod, NextTimeStamp, nullTimeStamp, TimeStamp, TypedPrimitiveElement, VDoc, VolatizeFromDID], YggTransaction USING [CreateTrans, EqualTrans, Finish, Outcome]; YggIndexMaintImpl: CEDAR MONITOR IMPORTS Atom, Process, RedBlackTree, Rope, YggDID, YggDIDMap, YggFile, YggFixedNames, YggIndex, YggNav, YggRep, YggTransaction EXPORTS YggDID, YggInternal, YggIndexMaint ~ BEGIN <<>> <> <> <> <> <> <<>> <> <<1) Only index the "primary" containers>> <<2) Stabilize the work to do list. Recover it after crash recovery and start up. Write the list into the contents of the object with DID YggDIDPrivate.LowForIndexMaint. Only work items and their completion must be recoverable: the intermediate state of an index change can be redone (possibly with no change).>> <> <> <> <> <<0) size Used >> <<1) free? if so, which list>> <<2) sequence number (used when reconstructing lists)>> <<3.. n-1) all the bits>> <> <<3) Rationalize the use of transactions>> <<>> <> ROPE: TYPE = Rope.ROPE; Document: TYPE = REF DocumentRep; DocumentRep: PUBLIC TYPE = YggDIDMapPrivate.DocumentRep; DID: PUBLIC TYPE ~ REF DIDRep; DIDRep: PUBLIC TYPE ~ YggDIDPrivate.DIDRep; DIDTidNotFound: PUBLIC ERROR = CODE; DoingIndexMaint: BOOL _ FALSE; MyCondition: CONDITION; UpdateType: TYPE = {valueChange, addIndex, deleteIndex, metaChanged}; currentTimeStamp: YggRep.TimeStamp _ YggRep.nullTimeStamp; WorkItem: TYPE = RECORD[ did: DID, tid: Camelot.tidT, transactionCommitted: {unknown, abort, commit}, updateType: UpdateType, attributeOrPattern: ROPE, oldValues: LIST OF LIST OF YggRep.AttributeValue, newValues: LIST OF LIST OF YggRep.AttributeValue, metaAttributesChanged: LIST OF YggRep.metaAttributeMod, containersToApply: LIST OF ContainerItem, maxTimeStamp: YggRep.TimeStamp _ YggRep.nullTimeStamp, containersToIndex: LIST OF ContainerItem, attributePatternDIDs: LIST OF AttrPatDID _ NIL ]; ContainerItem: TYPE = RECORD[ did: DID, done: BOOL _ FALSE, attributeNamePattern: LIST OF ROPE _ NIL ]; WorkItemsForDID: TYPE = RECORD[ stuffToDo: LIST OF WorkItem ]; WorkToDoList: LIST OF WorkItemsForDID _ NIL; AttrPatDID: TYPE = RECORD[ attributeNamePattern: ROPE, attributes: LIST OF AttrDIDVal ]; AttrDIDVal: TYPE = RECORD[ attributeName: ROPE, didsAndValues: LIST OF DIDVal _ NIL ]; DIDVal: TYPE = RECORD[ did: DID, values: LIST OF LIST OF YggRep.AttributeValue _ NIL ]; containerLock: RedBlackTree.Table; numLockedContainers: INT _ 0; containerLockEntry: TYPE ~ REF containerLockRep; containerLockRep: TYPE ~ RECORD [ did: DID, users: INT _ 0 ]; <> NewValueForAttribute: PUBLIC PROC [did: YggDID.DID, tid: Camelot.tidT, attributeName: ROPE, oldValues: LIST OF LIST OF YggRep.AttributeValue, newValues: LIST OF LIST OF YggRep.AttributeValue] ~ { <> addValueToWorkToDoList[did, tid, attributeName, oldValues, newValues]; }; NewValueForMetaAttribute: PUBLIC PROC [did: YggDID.DID, tid: Camelot.tidT, metaAttributesChanged: LIST OF YggRep.metaAttributeMod] ~ { <> addNewValueForMetaAttributeToWorkToDoList[did, tid, metaAttributesChanged]; }; <<>> NewValueForAttributeCommitStatus: PUBLIC PROC [did: YggDID.DID, tid: Camelot.tidT, commited: BOOL] ~ { <> noteCommit[did, tid, commited]; }; <<>> AddOrRemoveIndexPattern: PUBLIC PROC [containerDID: YggDID.DID, pattern: ROPE, add: BOOL] RETURNS [ok: BOOL _ TRUE]~ { <> addIndexUpdateToWorkToDoList[containerDID, pattern, add]; }; <> addValueToWorkToDoList: ENTRY PROC[did: YggDID.DID, tid: Camelot.tidT, attributeName: ROPE, oldValues: LIST OF LIST OF YggRep.AttributeValue, newValues: LIST OF LIST OF YggRep.AttributeValue] ~ { lastwtdl: LIST OF WorkItemsForDID _ NIL; IF ~DoingIndexMaint THEN RETURN; FOR wtdl: LIST OF WorkItemsForDID _ WorkToDoList, wtdl.rest UNTIL wtdl = NIL DO IF YggDID.EqualDIDs[wtdl.first.stuffToDo.first.did, did] THEN { lastWI: LIST OF WorkItem _ NIL; FOR lowi: LIST OF WorkItem _ wtdl.first.stuffToDo, lowi.rest UNTIL lowi = NIL DO lastWI _ lowi; ENDLOOP; lastWI.rest _ CONS[[did, tid, unknown, valueChange, attributeName, oldValues, newValues, NIL, NIL], NIL]; EXIT; }; lastwtdl _ wtdl; REPEAT FINISHED => { vta: LIST OF WorkItemsForDID _ LIST[[LIST[[did, tid, unknown, valueChange, attributeName, oldValues, newValues, NIL]]]]; IF lastwtdl = NIL THEN WorkToDoList _ vta ELSE lastwtdl.rest _ vta; }; ENDLOOP; NOTIFY MyCondition; }; addIndexUpdateToWorkToDoList: ENTRY PROC[containerDID: YggDID.DID, pattern: ROPE, add: BOOL] ~ { lastwtdl: LIST OF WorkItemsForDID _ NIL; IF ~DoingIndexMaint THEN RETURN; FOR wtdl: LIST OF WorkItemsForDID _ WorkToDoList, wtdl.rest UNTIL wtdl = NIL DO IF YggDID.EqualDIDs[wtdl.first.stuffToDo.first.did, containerDID] THEN { lastWI: LIST OF WorkItem _ NIL; FOR lowi: LIST OF WorkItem _ wtdl.first.stuffToDo, lowi.rest UNTIL lowi = NIL DO lastWI _ lowi; ENDLOOP; lastWI.rest _ CONS[[containerDID, YggEnvironment.nullTransID, commit, IF add THEN addIndex ELSE deleteIndex, pattern, NIL, NIL, NIL, NIL], NIL]; EXIT; }; lastwtdl _ wtdl; REPEAT FINISHED => { vta: LIST OF WorkItemsForDID _ LIST[[LIST[[containerDID, YggEnvironment.nullTransID, commit, IF add THEN addIndex ELSE deleteIndex, pattern, NIL, NIL, NIL, NIL]]]]; IF lastwtdl = NIL THEN WorkToDoList _ vta ELSE lastwtdl.rest _ vta; }; ENDLOOP; bumpContainerUse[containerDID]; NOTIFY MyCondition; }; addNewValueForMetaAttributeToWorkToDoList: ENTRY PROC[did: YggDID.DID, tid: Camelot.tidT, metaAttributesChanged: LIST OF YggRep.metaAttributeMod] ~ { lastwtdl: LIST OF WorkItemsForDID _ NIL; IF ~DoingIndexMaint THEN RETURN; FOR wtdl: LIST OF WorkItemsForDID _ WorkToDoList, wtdl.rest UNTIL wtdl = NIL DO IF YggDID.EqualDIDs[wtdl.first.stuffToDo.first.did, did] THEN { lastWI: LIST OF WorkItem _ NIL; FOR lowi: LIST OF WorkItem _ wtdl.first.stuffToDo, lowi.rest UNTIL lowi = NIL DO lastWI _ lowi; ENDLOOP; lastWI.rest _ CONS[[did, tid, unknown, metaChanged, NIL, NIL, NIL, metaAttributesChanged, NIL], NIL]; EXIT; }; lastwtdl _ wtdl; REPEAT FINISHED => { vta: LIST OF WorkItemsForDID _ LIST[[LIST[[did, tid, unknown, metaChanged, NIL, NIL, NIL, metaAttributesChanged, NIL]]]]; IF lastwtdl = NIL THEN WorkToDoList _ vta ELSE lastwtdl.rest _ vta; }; ENDLOOP; bumpContainerUse[did]; NOTIFY MyCondition; }; noteCommit: ENTRY PROC [did: YggDID.DID, tid: Camelot.tidT, commited: BOOL] ~ { ENABLE UNWIND => {}; gotOne: BOOL _ FALSE; IF ~DoingIndexMaint THEN RETURN; FOR wtdl: LIST OF WorkItemsForDID _ WorkToDoList, wtdl.rest UNTIL wtdl = NIL DO IF YggDID.EqualDIDs[wtdl.first.first.did, did] THEN { FOR lowi: LIST OF WorkItem _ wtdl.first.stuffToDo, lowi.rest UNTIL lowi = NIL DO IF YggTransaction.EqualTrans[lowi.first.tid, tid] THEN { lowi.first.transactionCommitted _ IF commited THEN commit ELSE abort; gotOne _ TRUE; }; ENDLOOP; EXIT; }; REPEAT FINISHED => { ERROR DIDTidNotFound; }; ENDLOOP; IF ~gotOne THEN ERROR DIDTidNotFound; IF ~commited THEN bumpContainerUse[did]; NOTIFY MyCondition; }; <> IndexMaintProcess: PROC = { DO -- forever waitForABit: ENTRY PROC ~ { WAIT MyCondition; }; didSomething: BOOL _ FALSE; prevDID: LIST OF WorkItemsForDID _ NIL; FOR wtdl: LIST OF WorkItemsForDID _ WorkToDoList, wtdl.rest UNTIL wtdl = NIL DO <> doneWithDID: BOOL _ FALSE; entryRemoved: BOOL _ FALSE; prevWorkItems: LIST OF WorkItemsForDID _ NIL; removeEntry: ENTRY PROC ~ { IF prevWorkItems # NIL THEN prevWorkItems.first.rest _ wtdl.first.rest -- previous work item for DID still active ELSE { -- first work item for this DID IF prevDID = NIL THEN { -- first DID on the list IF wtdl.first.rest = NIL THEN { -- all updates for did are done and this is the first did on the list WorkToDoList _ wtdl.rest; } ELSE { -- more updates for did, this is the first did on the list, and this is the first update on the list wtdl.first.stuffToDo _ wtdl.first.stuffToDo.rest; }; } ELSE { IF wtdl.first.rest = NIL THEN { -- all updates for did are done and this is not the first did on the list prevDID.rest _ wtdl.rest; } ELSE { -- more updates for did and this is not the first did on the list wtdl.first.stuffToDo _ wtdl.first.stuffToDo.rest; }; }; }; }; vDoc: YggRep.VDoc _ NIL; vDoc _ YggRep.VolatizeFromDID[YggEnvironment.nullTransID, wtdl.first.first.did, readOnly, [read, fail], TRUE]; IF vDoc = NIL THEN {prevWorkItems _ wtdl; LOOP;}; -- but what if the did has been deleted? entryRemoved _ FALSE; FOR lowi: LIST OF WorkItem _ wtdl.first, lowi.rest UNTIL lowi = NIL DO <> doRemoveEntry: BOOL _ FALSE; somethingChanged: BOOL _ FALSE; IF lowi.first.transactionCommitted = unknown THEN LOOP; -- waiting for the commit state to be known IF lowi.first.transactionCommitted = abort THEN {removeEntry[]; LOOP}; -- transaction didn't make it; dump item SELECT lowi.first.updateType FROM valueChange => { [doRemoveEntry, somethingChanged] _ doValueChange[lowi, vDoc]; }; addIndex => { [doRemoveEntry, somethingChanged] _ doCreateIndexChange[lowi, vDoc]; }; deleteIndex => { [doRemoveEntry, somethingChanged] _ doDeleteIndexChange[lowi, vDoc]; }; metaChanged => { [doRemoveEntry, somethingChanged] _ doMetaChange[lowi, vDoc]; }; ENDCASE => ERROR; IF doRemoveEntry THEN { entryRemoved _ TRUE; removeEntry[]; didSomething _ TRUE; }; IF ~entryRemoved THEN prevWorkItems _ wtdl; prevDID _ wtdl; ENDLOOP; -- for a DID ENDLOOP; -- Flip through the work to do list IF ~didSomething THEN waitForABit[]; ENDLOOP; -- forever }; <> doValueChange: PROC [lowi: LIST OF WorkItem, vDoc: YggRep.VDoc] RETURNS [doRemoveEntry: BOOL _ FALSE, somethingChanged: BOOL _ FALSE] ~ { keepTrying: BOOL _ TRUE; IF lowi.first.containersToApply = NIL THEN getContainersToApply[vDoc, lowi, YggFixedNames.Parents]; -- just starting, so find parents WHILE keepTrying DO FOR parentContainers: LIST OF ContainerItem _ lowi.first.containersToApply, parentContainers.rest UNTIL parentContainers = NIL DO doc: Document; atomForIndex: ATOM; transID: YggEnvironment.TransID; componentFiles: LIST OF YggInternal.FileHandle; outcome: YggTransaction.Outcome; superDIDs: LIST OF DID _ NIL; success: BOOL _ FALSE; indexFile: YggInternal.FileHandle; IF parentContainers.first.done THEN LOOP; -- already done transID _ YggTransaction.CreateTrans[YggEnvironment.nullTransID]; doc _ YggDIDMap.OpenDocumentFromDID[parentContainers.first.did, transID]; lowi.first.maxTimeStamp _ YggRep.MaxTimeStamp[lowi.first.maxTimeStamp, doc.timeStampForIndexMaint]; componentFiles _ YggDIDMap.GetComponentFiles[doc]; atomForIndex _ Atom.MakeAtom[Rope.Concat[YggFixedNames.IndexPrefix, lowi.first.attributeOrPattern]]; indexFile _ YggFile.FileFromComponentFiles[componentFiles: componentFiles, fileUse: atomForIndex]; [superDIDs, success] _ YggNav.GetSuperContainers[transID, parentContainers.first.did, TRUE]; IF ~success THEN { outcome _ YggTransaction.Finish[transID: transID, requestedOutcome: abort]; keepTrying _ FALSE; EXIT; }; [keepTrying: keepTrying, outcome: outcome] _ doOneChange[transID: transID, doc: doc, indexFile: indexFile, attributeName: lowi.first.attributeOrPattern, atomForIndex: atomForIndex, containerDID: parentContainers.first.did, indexedDID: lowi.first.did, oldValues: lowi.first.oldValues, newValues: lowi.first.newValues]; IF ~keepTrying THEN EXIT; outcome _ YggTransaction.Finish[transID: transID, requestedOutcome: commit]; IF outcome = commit THEN { somethingChanged _ TRUE; parentContainers.first.done _ TRUE; IF superDIDs # NIL THEN addSuperDIDs[superDIDs, lowi]; -- append supers to list of stuff to do, provided that they have not yet been seen } ELSE { keepTrying _ FALSE; EXIT; }; IF ~keepTrying THEN EXIT; REPEAT FINISHED => { -- all parents processed OK !!! ok: BOOL _ TRUE; ok _ checkTimeStamps[lowi, lowi.first.maxTimeStamp]; IF ok THEN { doRemoveEntry _ TRUE; somethingChanged _ TRUE; keepTrying _ FALSE; } ELSE { -- something changed; force re-evaluation of my super contatiners and redo loop FOR parentContainers: LIST OF ContainerItem _ lowi.first.containersToApply, parentContainers.rest UNTIL parentContainers = NIL DO parentContainers.first.done _ FALSE; ENDLOOP; }; }; ENDLOOP; ENDLOOP; }; doMetaChange: PROC [lowi: LIST OF WorkItem, vDoc: YggRep.VDoc] RETURNS [doRemoveEntry: BOOL _ FALSE, somethingChanged: BOOL _ FALSE] ~ { FOR mam: LIST OF YggRep.metaAttributeMod _ lowi.first.metaAttributesChanged, mam.rest UNTIL mam = NIL DO SELECT TRUE FROM Rope.Equal[YggFixedNames.Parents, mam.first.attributeName] => { <> doRemoveEntry _ TRUE; somethingChanged _ TRUE; }; Rope.Equal[YggFixedNames.Children, mam.first.attributeName] => { -- add/remove DID as child to this parent (in WorkItem) keepTrying: BOOL _ TRUE; didSomething: BOOL _ TRUE; outcome: YggTransaction.Outcome; containerDID: DID; transID: YggEnvironment.TransID; containerDoc: Document; containerVDoc: YggRep.VDoc; containerDID _ mam.first.didValue; containerVDoc _ YggRep.VolatizeFromDID[YggEnvironment.nullTransID, containerDID, readOnly, [read, fail], TRUE]; IF containerVDoc = NIL THEN RETURN [FALSE, FALSE]; -- try again later IF lowi.first.containersToApply = NIL THEN lowi.first.containersToApply _ LIST[[containerDID, FALSE]]; WHILE keepTrying DO FOR parentContainers: LIST OF ContainerItem _ lowi.first.containersToApply, parentContainers.rest UNTIL parentContainers = NIL DO superDIDs: LIST OF DID _ NIL; success: BOOL _ FALSE; IF parentContainers.first.done THEN LOOP; -- already done transID _ YggTransaction.CreateTrans[YggEnvironment.nullTransID]; containerDoc _ YggDIDMap.OpenDocumentFromDID[parentContainers.first.did, transID]; lowi.first.maxTimeStamp _ YggRep.MaxTimeStamp[lowi.first.maxTimeStamp, containerDoc.timeStampForIndexMaint]; [superDIDs, success] _ YggNav.GetSuperContainers[transID, parentContainers.first.did, TRUE]; IF ~success THEN { outcome _ YggTransaction.Finish[transID: transID, requestedOutcome: abort]; keepTrying _ FALSE; EXIT; }; FOR aL: LIST OF YggRep.Attribute _ vDoc.attributes, aL.rest UNTIL aL = NIL DO attributeName: ROPE _ aL.first.attributeName; [keepTrying: keepTrying, outcome: outcome, didSomething: didSomething] _ doOneChange [ transID: transID, doc: containerDoc, indexFile: NIL, attributeName: attributeName, atomForIndex: Atom.MakeAtom[Rope.Concat[ YggFixedNames.IndexPrefix, attributeName]], containerDID: containerDID, indexedDID: vDoc.did, oldValues: IF mam.first.add THEN NIL ELSE LIST[aL.first.value], newValues: IF mam.first.add THEN LIST[aL.first.value] ELSE NIL]; IF ~keepTrying THEN EXIT; IF didSomething THEN { outcome _ YggTransaction.Finish[transID: transID, requestedOutcome: commit]; IF outcome = commit THEN { somethingChanged _ TRUE; IF aL.rest # NIL THEN transID _ YggTransaction.CreateTrans[YggEnvironment.nullTransID]; } ELSE { keepTrying _ FALSE; EXIT; }; }; REPEAT FINISHED => { -- container processed OK !!! parentContainers.first.done _ TRUE; IF superDIDs # NIL THEN addSuperDIDs[superDIDs, lowi]; -- append supers to list of stuff to do, provided that they have not yet been seen }; ENDLOOP; outcome _ YggTransaction.Finish[transID: transID, requestedOutcome: abort]; IF ~keepTrying THEN EXIT; REPEAT FINISHED => { -- all parents processed OK !!! ok: BOOL _ TRUE; ok _ checkTimeStamps[lowi, lowi.first.maxTimeStamp]; IF ok THEN { doRemoveEntry _ TRUE; somethingChanged _ TRUE; keepTrying _ FALSE; } ELSE { -- something changed; force re-evaluation of my super contatiners and redo loop FOR parentContainers: LIST OF ContainerItem _ lowi.first.containersToApply, parentContainers.rest UNTIL parentContainers = NIL DO parentContainers.first.done _ FALSE; ENDLOOP; }; }; ENDLOOP; ENDLOOP; }; Rope.Equal[YggFixedNames.ParentContainers, mam.first.attributeName] => { <> doRemoveEntry _ TRUE; somethingChanged _ TRUE; }; Rope.Equal[YggFixedNames.ChildContainers, mam.first.attributeName] => { <> <<1) look up all super containers that have to be indexed>> <<2) build a list of attribute patterns to index>> <<3) scan container and all its children containers; for each attribute that is indexed remember the did of the object>> <<4) for each super container, index matched attributes>> <<5) check to be sure nothing changed in the super container DG; on change GO TO 1>> keepTrying: BOOL _ TRUE; didSomething: BOOL _ TRUE; outcome: YggTransaction.Outcome; containerDID: DID; transID: YggEnvironment.TransID; containerDoc: Document; <<1) look up all super containers that have to be indexed>> <<2) build a list of attribute patterns to index>> containerDID _ mam.first.didValue; WHILE keepTrying DO IF lowi.first.containersToApply = NIL THEN { lastContainersToApply: LIST OF ContainerItem; lastContainersToApply _ lowi.first.containersToApply _ LIST[[containerDID, FALSE]]; transID _ YggTransaction.CreateTrans[YggEnvironment.nullTransID]; FOR cta: LIST OF ContainerItem _ lowi.first.containersToApply, cta.rest UNTIL cta = NIL DO superDIDs: LIST OF DID _ NIL; success: BOOL _ FALSE; patterns: LIST OF ROPE; containerDoc _ YggDIDMap.OpenDocumentFromDID[cta.first.did, transID]; lowi.first.maxTimeStamp _ YggRep.MaxTimeStamp[lowi.first.maxTimeStamp, containerDoc.timeStampForIndexMaint]; [superDIDs, success] _ YggNav.GetSuperContainers[transID, cta.first.did, TRUE]; IF ~success THEN { outcome _ YggTransaction.Finish[transID: transID, requestedOutcome: abort]; keepTrying _ FALSE; EXIT; }; FOR dids: LIST OF DID _ superDIDs, dids.rest UNTIL dids = NIL DO FOR knownSupers: LIST OF ContainerItem _ lowi.first.containersToApply, knownSupers.rest UNTIL knownSupers = NIL DO IF YggDID.EqualDIDs[knownSupers.first.did, dids.first] THEN EXIT; REPEAT FINISHED => { lastContainersToApply _ CONS[[dids.first, FALSE], lastContainersToApply]; }; ENDLOOP; ENDLOOP; [patterns, success] _ YggNav.GetPatternsInContainer[cta.first.did, TRUE]; IF ~success THEN { keepTrying _ FALSE; EXIT; }; cta.first.attributeNamePattern _ patterns; FOR lop: LIST OF ROPE _ patterns, lop.rest UNTIL lop = NIL DO FOR loapd: LIST OF AttrPatDID _ lowi.first.attributePatternDIDs, loapd.rest UNTIL loapd = NIL DO IF Rope.Equal[loapd.first.attributeNamePattern, lop.first] THEN EXIT; REPEAT FINISHED => { lowi.first.attributePatternDIDs _ CONS[[loapd.first.attributeNamePattern, NIL], lowi.first.attributePatternDIDs]; }; ENDLOOP; ENDLOOP; ENDLOOP; IF ~ keepTrying THEN { lowi.first.containersToApply _ NIL; RETURN [FALSE, FALSE]; -- try again later }; outcome _ YggTransaction.Finish[transID: transID, requestedOutcome: commit]; }; <<3) scan container and all its children containers; for each attribute that is indexed remember the did of the object>> IF ~keepTrying THEN EXIT; -- should be redundant IF lowi.first.containersToIndex = NIL THEN lowi.first.containersToIndex _ LIST[[lowi.first.did, FALSE]]; { FOR cta: LIST OF ContainerItem _ lowi.first.containersToIndex, cta.rest UNTIL cta = NIL OR ~keepTrying DO subDIDs: LIST OF DID _ NIL; didsInContainer: LIST OF DID _ NIL; success: BOOL _ FALSE; IF cta.first.done THEN LOOP; transID _ YggTransaction.CreateTrans[YggEnvironment.nullTransID]; containerDoc _ YggDIDMap.OpenDocumentFromDID[cta.first.did, transID]; [subDIDs, success] _ YggNav.GetContainersInContainer[transID, cta.first.did, TRUE]; IF ~success THEN { outcome _ YggTransaction.Finish[transID: transID, requestedOutcome: abort]; keepTrying _ FALSE; EXIT; }; FOR dids: LIST OF DID _ subDIDs, dids.rest UNTIL dids = NIL DO lastKnownSubs: LIST OF ContainerItem _ NIL; FOR knownSubs: LIST OF ContainerItem _ lowi.first.containersToIndex, knownSubs.rest UNTIL knownSubs = NIL DO lastKnownSubs _ knownSubs; IF YggDID.EqualDIDs[knownSubs.first.did, dids.first] THEN EXIT; REPEAT FINISHED => { lastKnownSubs _ CONS[[dids.first, FALSE], lastKnownSubs]; }; ENDLOOP; ENDLOOP; [dids: didsInContainer, success: success] _ YggNav.GetObjectsInContainer[trans: transID, containerDID: cta.first.did, dontWait: TRUE]; IF ~success THEN { outcome _ YggTransaction.Finish[transID: transID, requestedOutcome: abort]; keepTrying _ FALSE; RETURN; }; outcome _ YggTransaction.Finish[transID: transID, requestedOutcome: commit]; FOR nowDID: LIST OF DID _ didsInContainer, nowDID.rest UNTIL nowDID = NIL DO skipDID: BOOL _ FALSE; addedValueForDID: BOOL _ FALSE; properties: LIST OF YggRep.Attribute _ NIL; transID _ YggTransaction.CreateTrans[YggEnvironment.nullTransID]; [properties: properties] _ YggNav.GetAllProperties[trans: transID, did: nowDID.first]; outcome _ YggTransaction.Finish[transID: transID, requestedOutcome: commit]; FOR loa: LIST OF YggRep.Attribute _ properties, loa.rest UNTIL loa = NIL OR skipDID DO -- for each attribute FOR apd: LIST OF AttrPatDID _ lowi.first.attributePatternDIDs, apd.rest UNTIL apd = NIL OR skipDID DO IF Rope.Match[apd.first.attributeNamePattern, loa.first.attributeName] THEN { -- if the attribute should be indexed FOR apdItem: LIST OF AttrDIDVal _ apd.first.attributes, apdItem.rest UNTIL apdItem = NIL OR skipDID DO -- look for the attribute name under the pattern IF Rope.Equal[apdItem.first.attributeName, loa.first.attributeName] THEN { FOR didOnList: LIST OF DIDVal _ apdItem.first.didsAndValues, didOnList.rest UNTIL didOnList = NIL DO IF YggDID.EqualDIDs[didOnList.first.did, nowDID.first] THEN { IF addedValueForDID THEN { didOnList.first.values _ CONS[loa.first.value, didOnList.first.values]; } ELSE { skipDID _ TRUE; EXIT; }; }; REPEAT FINISHED => { addedValueForDID _ TRUE; apdItem.first.didsAndValues _ CONS[[nowDID.first, LIST[loa.first.value]], apdItem.first.didsAndValues]; }; ENDLOOP; EXIT; }; REPEAT FINISHED => IF ~skipDID THEN { -- attribute name not listed under pattern addedValueForDID _ TRUE; apd.first.attributes _ CONS[[loa.first.attributeName, LIST[[nowDID.first, LIST[loa.first.value]]]], apd.first.attributes]; }; ENDLOOP; }; ENDLOOP; ENDLOOP; ENDLOOP; cta.first.done _ TRUE; ENDLOOP; }; <<4) for each super container, index matched attributes>> IF ~ keepTrying THEN EXIT; { FOR cta: LIST OF ContainerItem _ lowi.first.containersToApply, cta.rest UNTIL cta = NIL DO IF cta.first.done THEN LOOP; FOR anp: LIST OF ROPE _ cta.first.attributeNamePattern, anp.rest UNTIL anp = NIL DO FOR apd: LIST OF AttrPatDID _ lowi.first.attributePatternDIDs, apd.rest UNTIL apd = NIL DO IF Rope.Equal[apd.first.attributeNamePattern, anp.first] THEN { FOR apdItem: LIST OF AttrDIDVal _ apd.first.attributes, apdItem.rest UNTIL apdItem = NIL DO indexFile: YggInternal.FileHandle; containerDoc: Document; atomForIndex: ATOM; componentFiles: LIST OF YggInternal.FileHandle; transID _ YggTransaction.CreateTrans[YggEnvironment.nullTransID]; containerDoc _ YggDIDMap.OpenDocumentFromDID[cta.first.did, transID]; componentFiles _ YggDIDMap.GetComponentFiles[containerDoc]; atomForIndex _ Atom.MakeAtom[Rope.Concat[YggFixedNames.IndexPrefix, apdItem.first.attributeName]]; indexFile _ YggFile.FileFromComponentFiles[componentFiles: componentFiles, fileUse: atomForIndex]; FOR didsToIndex: LIST OF DIDVal _ apdItem.first.didsAndValues, didsToIndex.rest UNTIL didsToIndex = NIL DO [keepTrying: keepTrying, outcome: outcome, didSomething: didSomething] _ doOneChange [ transID: transID, doc: containerDoc, indexFile: indexFile, attributeName: apdItem.first.attributeName, atomForIndex: atomForIndex, containerDID: cta.first.did, indexedDID: didsToIndex.first.did, oldValues: IF mam.first.add THEN NIL ELSE didsToIndex.first.values, newValues: IF mam.first.add THEN didsToIndex.first.values ELSE NIL]; IF ~keepTrying THEN EXIT; IF didSomething AND indexFile = NIL THEN { indexFile _ YggFile.FileFromComponentFiles[componentFiles: componentFiles, fileUse: atomForIndex]; }; ENDLOOP; REPEAT FINISHED => { -- container processed OK !!! cta.first.done _ TRUE; }; ENDLOOP; EXIT; }; REPEAT FINISHED => ERROR; ENDLOOP; ENDLOOP; ENDLOOP; }; <<5) check to be sure nothing changed in the super container DG; on change GO TO 1>> IF ~ keepTrying THEN EXIT; { ok: BOOL _ TRUE; ok _ checkTimeStamps[lowi, lowi.first.maxTimeStamp]; IF ok THEN FOR indexContainers: LIST OF ContainerItem _ lowi.first.containersToIndex, indexContainers.rest UNTIL indexContainers = NIL DO doc: Document; doc _ YggDIDMap.OpenDocumentFromDID[indexContainers.first.did, YggEnvironment.nullTransID]; IF YggRep.CompareTimeStamps[doc.timeStampForIndexMaint, lowi.first.maxTimeStamp] = greater THEN { ok _ FALSE; EXIT; }; ENDLOOP; IF ok THEN { doRemoveEntry _ TRUE; somethingChanged _ TRUE; keepTrying _ FALSE; } ELSE { -- something changed; force re-evaluation of my super contatiners and index containter and redo loop (shucks) lowi.first.containersToApply _ NIL; lowi.first.containersToIndex _ NIL; }; }; ENDLOOP; }; ENDCASE => ERROR; ENDLOOP; }; doCreateIndexChange: PROC [lowi: LIST OF WorkItem, vDoc: YggRep.VDoc] RETURNS [doRemoveEntry: BOOL _ FALSE, somethingChanged: BOOL _ FALSE] ~ { <> <<1) build up list of containers to index>> <<2) build up list of attributes/dids>> <<3) apply to index>> <> <<4) check container time stamps>> <<5) if a problem, go to 1>> keepTrying: BOOL _ TRUE; didSomething: BOOL _ TRUE; outcome: YggTransaction.Outcome; containerDID: DID _ lowi.first.did; transID: YggEnvironment.TransID; containerDoc: Document; newPatternToIndex: ROPE _ lowi.first.attributeOrPattern; WHILE keepTrying DO success: BOOL _ FALSE; fileUses: LIST OF ATOM _ NIL; IF lowi.first.containersToIndex = NIL THEN lowi.first.containersToIndex _ LIST[[lowi.first.did, FALSE]]; IF lowi.first.attributePatternDIDs = NIL THEN lowi.first.attributePatternDIDs _ LIST[[newPatternToIndex, NIL]]; FOR cti: LIST OF ContainerItem _ lowi.first.containersToIndex, cti.rest UNTIL cti = NIL OR ~keepTrying DO subDIDs: LIST OF DID _ NIL; didsInContainer: LIST OF DID _ NIL; success: BOOL _ FALSE; IF cti.first.done THEN LOOP; transID _ YggTransaction.CreateTrans[YggEnvironment.nullTransID]; containerDoc _ YggDIDMap.OpenDocumentFromDID[cti.first.did, transID]; <<1) build up list of containers to index>> [subDIDs, success] _ YggNav.GetContainersInContainer[transID, cti.first.did, TRUE]; IF ~success THEN { outcome _ YggTransaction.Finish[transID: transID, requestedOutcome: abort]; keepTrying _ FALSE; EXIT; }; FOR dids: LIST OF DID _ subDIDs, dids.rest UNTIL dids = NIL DO lastKnownSubs: LIST OF ContainerItem _ NIL; FOR knownSubs: LIST OF ContainerItem _ lowi.first.containersToIndex, knownSubs.rest UNTIL knownSubs = NIL DO lastKnownSubs _ knownSubs; IF YggDID.EqualDIDs[knownSubs.first.did, dids.first] THEN EXIT; REPEAT FINISHED => { lastKnownSubs _ CONS[[dids.first, FALSE], lastKnownSubs]; }; ENDLOOP; ENDLOOP; <<2) build up list of attributes/dids>> [dids: didsInContainer, success: success] _ YggNav.GetObjectsInContainer[trans: transID, containerDID: cti.first.did, dontWait: TRUE]; IF ~success THEN { outcome _ YggTransaction.Finish[transID: transID, requestedOutcome: abort]; keepTrying _ FALSE; RETURN; }; outcome _ YggTransaction.Finish[transID: transID, requestedOutcome: commit]; FOR nowDID: LIST OF DID _ didsInContainer, nowDID.rest UNTIL nowDID = NIL DO skipDID: BOOL _ FALSE; addedValueForDID: BOOL _ FALSE; properties: LIST OF YggRep.Attribute _ NIL; transID _ YggTransaction.CreateTrans[YggEnvironment.nullTransID]; [properties: properties] _ YggNav.GetAllProperties[trans: transID, did: nowDID.first]; outcome _ YggTransaction.Finish[transID: transID, requestedOutcome: commit]; FOR loa: LIST OF YggRep.Attribute _ properties, loa.rest UNTIL loa = NIL OR skipDID DO -- for each attribute IF Rope.Match[loa.first.attributeName, newPatternToIndex] THEN { FOR apdItem: LIST OF AttrDIDVal _ lowi.first.attributePatternDIDs.first.attributes, apdItem.rest UNTIL apdItem = NIL OR skipDID DO -- look for the attribute name under the pattern IF Rope.Equal[apdItem.first.attributeName, loa.first.attributeName] THEN { FOR didOnList: LIST OF DIDVal _ apdItem.first.didsAndValues, didOnList.rest UNTIL didOnList = NIL DO IF YggDID.EqualDIDs[didOnList.first.did, nowDID.first] THEN { IF addedValueForDID THEN { didOnList.first.values _ CONS[loa.first.value, didOnList.first.values]; } ELSE { skipDID _ TRUE; EXIT; }; }; REPEAT FINISHED => { addedValueForDID _ TRUE; apdItem.first.didsAndValues _ CONS[[nowDID.first, LIST[loa.first.value]], apdItem.first.didsAndValues]; }; ENDLOOP; EXIT; }; REPEAT FINISHED => IF ~skipDID THEN { -- attribute name not listed under pattern addedValueForDID _ TRUE; lowi.first.attributePatternDIDs.first.attributes _ CONS[[loa.first.attributeName, LIST[[nowDID.first, LIST[loa.first.value]]]], lowi.first.attributePatternDIDs.first.attributes]; }; ENDLOOP; }; ENDLOOP; ENDLOOP; cti.first.done _ TRUE; ENDLOOP; <<3) apply to index>> IF ~ keepTrying THEN EXIT; { FOR apdItem: LIST OF AttrDIDVal _ lowi.first.attributePatternDIDs.first.attributes, apdItem.rest UNTIL apdItem = NIL DO indexFile: YggInternal.FileHandle; containerDoc: Document; atomForIndex: ATOM; componentFiles: LIST OF YggInternal.FileHandle; transID _ YggTransaction.CreateTrans[YggEnvironment.nullTransID]; containerDoc _ YggDIDMap.OpenDocumentFromDID[lowi.first.did, transID]; componentFiles _ YggDIDMap.GetComponentFiles[containerDoc]; atomForIndex _ Atom.MakeAtom[Rope.Concat[YggFixedNames.IndexPrefix, apdItem.first.attributeName]]; indexFile _ YggFile.FileFromComponentFiles[componentFiles: componentFiles, fileUse: atomForIndex]; FOR didsToIndex: LIST OF DIDVal _ apdItem.first.didsAndValues, didsToIndex.rest UNTIL didsToIndex = NIL DO [keepTrying: keepTrying, outcome: outcome, didSomething: didSomething] _ doOneChange [ transID: transID, doc: containerDoc, indexFile: indexFile, attributeName: apdItem.first.attributeName, atomForIndex: atomForIndex, containerDID: lowi.first.did, indexedDID: didsToIndex.first.did, oldValues: NIL, newValues: didsToIndex.first.values]; IF ~keepTrying THEN EXIT; IF didSomething AND indexFile = NIL THEN { indexFile _ YggFile.FileFromComponentFiles[componentFiles: componentFiles, fileUse: atomForIndex]; }; ENDLOOP; REPEAT FINISHED => { -- container processed OK !!! }; ENDLOOP; }; IF ~ keepTrying THEN EXIT; ENDLOOP; }; doDeleteIndexChange: PROC [lowi: LIST OF WorkItem, vDoc: YggRep.VDoc] RETURNS [doRemoveEntry: BOOL _ FALSE, somethingChanged: BOOL _ FALSE] ~ { keepTrying: BOOL _ TRUE; didSomething: BOOL _ TRUE; outcome: YggTransaction.Outcome; containerDID: DID _ lowi.first.did; componentFiles: LIST OF YggInternal.FileHandle; transID: YggEnvironment.TransID; containerDoc: Document; WHILE keepTrying DO success: BOOL _ FALSE; patterns: LIST OF ROPE; fileUses: LIST OF ATOM _ NIL; transID _ YggTransaction.CreateTrans[YggEnvironment.nullTransID]; [patterns, success] _ YggNav.GetPatternsInContainer[containerDID, TRUE]; IF ~success THEN { outcome _ YggTransaction.Finish[transID: transID, requestedOutcome: abort]; keepTrying _ FALSE; RETURN; }; containerDoc _ YggDIDMap.OpenDocumentFromDID[containerDID, transID]; componentFiles _ YggDIDMap.GetComponentFiles[containerDoc]; fileUses _ YggFile.FileUseFromComponentFiles[componentFiles]; FOR locf: LIST OF ATOM _ fileUses, locf.rest UNTIL locf = NIL DO useName: ROPE _ Atom.GetPName[locf.first]; matchName: ROPE _ NIL; IF Rope.Equal[YggFixedNames.IndexPrefix, Rope.Substr[useName, 0, YggFixedNames.IndexPrefixSize]] THEN matchName _ Rope.Substr[useName, YggFixedNames.IndexPrefixSize] ELSE LOOP; FOR lop: LIST OF ROPE _ patterns, lop.rest UNTIL lop = NIL DO IF Rope.Match[pattern: lop.first, object: matchName] THEN EXIT; REPEAT FINISHED => { indexFile: YggInternal.FileHandle _ NIL; indexFile _ YggFile.FileFromComponentFiles[componentFiles: componentFiles, fileUse: locf.first]; YggDIDMap.RemoveComponentFile[containerDoc, indexFile, transID]; YggFile.Delete[file: indexFile, tid: transID]; }; ENDLOOP; ENDLOOP; outcome _ YggTransaction.Finish[transID: transID, requestedOutcome: abort]; IF outcome = commit THEN { somethingChanged _ TRUE; doRemoveEntry_TRUE; keepTrying _ FALSE; }; ENDLOOP; }; doOneChange: PROC [transID: YggEnvironment.TransID, doc: Document, indexFile: YggInternal.FileHandle, attributeName: ROPE, atomForIndex: ATOM, containerDID: DID, indexedDID: DID, oldValues: LIST OF LIST OF YggRep.AttributeValue, newValues: LIST OF LIST OF YggRep.AttributeValue] RETURNS [keepTrying: BOOL _ TRUE, outcome: YggTransaction.Outcome _ active, didSomething: BOOL _ FALSE] ~ { <> IF indexFile = NIL THEN { -- no index yet, but should there be? success: BOOL _ FALSE; patterns: LIST OF ROPE; shouldBeIndexed: BOOL _ FALSE; [patterns, success] _ YggNav.GetPatternsInContainer[containerDID, TRUE]; IF ~success THEN { outcome _ YggTransaction.Finish[transID: transID, requestedOutcome: abort]; keepTrying _ FALSE; RETURN; }; FOR lor: LIST OF ROPE _ patterns, lor.rest UNTIL lor= NIL DO IF Rope.Match[pattern: lor.first, object: attributeName] THEN { shouldBeIndexed _ TRUE; RETURN; }; ENDLOOP; IF shouldBeIndexed THEN { componentFiles: LIST OF YggInternal.FileHandle; componentFiles _ YggDIDMap.GetComponentFiles[doc]; indexFile _ YggFile.FileFromComponentFiles[componentFiles: componentFiles, fileUse: atomForIndex]; IF indexFile = NIL THEN { indexFile _ YggFile.Create[size: 32, did: containerDID, fileUse: atomForIndex, nearToDid: containerDID, tid: transID]; YggDIDMap.AddComponentFile[doc, indexFile, transID]; }; }; }; IF indexFile # NIL THEN { index: YggIndex.Index; index _ YggIndex.Open[did: containerDID, fileUse: atomForIndex, cacheSize: 8, initialize: FALSE]; <> FOR loloav: LIST OF LIST OF YggRep.AttributeValue _ oldValues, loloav.rest UNTIL loloav = NIL DO FOR listoav: LIST OF YggRep.AttributeValue _ loloav.first, listoav.rest UNTIL listoav = NIL DO FOR valueSet: LIST OF YggRep.TypedPrimitiveElement _ listoav.first.valueSet, valueSet.rest UNTIL valueSet = NIL DO enumProc: YggIndex.EntryProc ~ { <> tpesToDelete _ CONS[value, tpesToDelete] }; tpesToDelete: LIST OF YggRep.TypedPrimitiveElement _ NIL; YggIndex.EnumerateEntries[index: index, start: valueSet.first, end: valueSet.first, proc: enumProc]; FOR tpes: LIST OF YggRep.TypedPrimitiveElement _ tpesToDelete, tpes.rest UNTIL tpes = NIL DO didSomething _ TRUE; [] _ YggIndex.DeleteEntry[index: index, value: tpes.first, did: indexedDID, trans: transID]; ENDLOOP; ENDLOOP; ENDLOOP; ENDLOOP; <> FOR loloav: LIST OF LIST OF YggRep.AttributeValue _ newValues, loloav.rest UNTIL loloav = NIL DO FOR listoav: LIST OF YggRep.AttributeValue _ loloav.first, listoav.rest UNTIL listoav = NIL DO FOR valueSet: LIST OF YggRep.TypedPrimitiveElement _ listoav.first.valueSet, valueSet.rest UNTIL valueSet = NIL DO didSomething _ TRUE; [] _ YggIndex.WriteEntry[index: index, value: valueSet.first, did: indexedDID, trans: transID]; ENDLOOP; ENDLOOP; ENDLOOP; }; }; <> <> <> <> <<[patterns, success] _ YggNav.GetPatternsInContainer[parentContainers.first.did, TRUE];>> <> <> <> <> <<};>> <> <> <> <> <<};>> <> <> <> <> <<};>> <<};>> <> <> <> <> <> <> <> <> <> <> <<};>> <> <> <> <<[] _ YggIndex.DeleteEntry[index: index, value: tpes.first, did: lowi.first.did];>> <> <> <> <> <> <> <> <> <> <> <> <> <<};>> getContainersToApply: PROC [seedVDoc: YggRep.VDoc, lowi: LIST OF WorkItem, attrToFollow: ROPE _ YggFixedNames.Parents] ~ { parents: LIST OF YggRep.AttributeValue _ NIL; FOR loa: LIST OF YggRep.Attribute _ seedVDoc.metaAttributes, loa.rest UNTIL loa = NIL DO IF Rope.Equal[loa.first.attributeName, attrToFollow] THEN {parents _ loa.first.value; EXIT}; ENDLOOP; IF parents # NIL THEN { FOR parentListAsAttr: LIST OF YggRep.AttributeValue _ parents, parentListAsAttr.rest UNTIL parentListAsAttr = NIL DO FOR parentVS: LIST OF YggRep.TypedPrimitiveElement _ parentListAsAttr.first.valueSet, parentVS.rest UNTIL parentVS = NIL DO IF parentVS.first.docType # YggRep.did THEN LOOP; -- junk lowi.first.containersToApply _ CONS[[NARROW[parentVS.first.bits, DID], FALSE], lowi.first.containersToApply]; ENDLOOP; ENDLOOP; }; }; checkTimeStamps: PROC [lowi: LIST OF WorkItem, timeStamp: YggRep.TimeStamp] RETURNS [ok: BOOL _ TRUE] ~ { doc: Document; doc _ YggDIDMap.OpenDocumentFromDID[lowi.first.did, YggEnvironment.nullTransID]; IF YggRep.CompareTimeStamps[doc.timeStampForIndexMaint, timeStamp] = greater THEN { ok _ FALSE; }; IF ok THEN FOR parentContainers: LIST OF ContainerItem _ lowi.first.containersToApply, parentContainers.rest UNTIL parentContainers = NIL DO doc: Document; doc _ YggDIDMap.OpenDocumentFromDID[parentContainers.first.did, YggEnvironment.nullTransID]; IF YggRep.CompareTimeStamps[doc.timeStampForIndexMaint, timeStamp] = greater THEN { ok _ FALSE; EXIT; }; ENDLOOP; }; addSuperDIDs: PROC [superDIDs: LIST OF DID _ NIL, lowi: LIST OF WorkItem] ~ { <> lastPC: LIST OF ContainerItem _ NIL; FOR pC: LIST OF ContainerItem _ lowi.first.containersToApply, pC.rest UNTIL pC = NIL DO lastPC _ pC; ENDLOOP; FOR lod: LIST OF DID _ superDIDs, lod.rest UNTIL lod = NIL DO FOR pC: LIST OF ContainerItem _ lowi.first.containersToApply, pC.rest UNTIL pC = NIL DO IF YggDID.EqualDIDs[pC.first.did, lod.first] THEN EXIT; REPEAT FINISHED => { lastPC _ CONS[[lod.first, FALSE], lastPC]; }; ENDLOOP; ENDLOOP; }; <> debumpContainerUse: PROC [containerDID: YggDID.DID] ~ { val: REF; val _ RedBlackTree.Lookup[self: containerLock, lookupKey: containerDID]; IF val # NIL THEN { entry: containerLockEntry; entry _ NARROW[val]; entry.users _ entry.users - 1; IF entry.users = 0 THEN { [] _ RedBlackTree.Delete[self: containerLock, deleteKey: containerDID]; numLockedContainers _ numLockedContainers - 1; IF numLockedContainers < 0 THEN ERROR; }; } ELSE { ERROR; }; }; bumpContainerUse: INTERNAL PROC [containerDID: YggDID.DID] ~ { val: REF; val _ RedBlackTree.Lookup[self: containerLock, lookupKey: containerDID]; IF val # NIL THEN { entry: containerLockEntry; entry _ NARROW[val]; entry.users _ entry.users + 1; } ELSE { numLockedContainers _ numLockedContainers + 1; RedBlackTree.Insert[self: containerLock, insertKey: containerDID, dataToInsert: NEW[containerLockRep _ [containerDID, 1]]]; }; }; GetKeyProc: RedBlackTree.GetKey = { <> entry: containerLockEntry _ NARROW[data]; RETURN[ entry.did ]; }; CompareProc: RedBlackTree.Compare = { <> entryData: containerLockEntry _ NARROW[data]; keyData: DID _ NARROW[k]; SELECT keyData.didHigh FROM > entryData.did.didHigh => RETURN [greater]; < entryData.did.didHigh => RETURN [less]; ENDCASE => { SELECT keyData.didLow FROM > entryData.did.didLow => RETURN [greater]; < entryData.did.didLow => RETURN [less]; ENDCASE => RETURN [equal]; }; }; <> NextTimeStamp: ENTRY PROC RETURNS [cts: YggRep.TimeStamp] _ { currentTimeStamp _ YggRep.NextTimeStamp[currentTimeStamp]; }; <> containerLock _ RedBlackTree.Create[getKey: GetKeyProc, compare: CompareProc]; TRUSTED { <> <> <> Process.SetTimeout[condition: @MyCondition, ticks: Process.MsecToTicks[50]]; }; [] _ NextTimeStamp[]; END.