DIRECTORY Basics USING [charsPerWord, UnsafeBlock], IO USING [EndOfStream, GetChar, GetLength, STREAM, UnsafeGetBlock], RefText USING [ObtainScratch, ReleaseScratch], Rope USING [Concat, Compare, FromRefText, ROPE], SymTab USING [Create, Delete, EachPairAction, Fetch, GetSize, Insert, Pairs, Ref], YggDID USING [DID, StreamsForDID], YggDummyProcess USING [Detach, MsecToTicks, Pause, SetTimeout, Ticks], YggEnvironment USING [AccessRights, LockMode, LockOption, TransID], YggInternal USING [OpenDoc], YggLock USING [Failed, MakeLockID, Set], YggMonitoringLog USING [LockConflictInfo, notice], YggOpenDoc USING [Register], YggRep USING [AccurateGMT, AccurateGMTRep, AccurateGMTRepByteSize, AttributePreambleByte, AttributeValue, Bits, BitsRep, date, float, int, rope, shortRope, TypedPrimitiveElement, unknown, uninterpretedBytes, VDoc, VDocRep], YggTransContext USING [EstablishTransactionContext, TransactionWork], YggVolatileObjectCache; YggVolatilizeImpl: CEDAR MONITOR IMPORTS IO, RefText, Rope, SymTab, YggDID, YggDummyProcess, YggLock, YggOpenDoc, YggMonitoringLog, YggTransContext EXPORTS YggDID, YggRep, YggVolatileObjectCache ~ BEGIN ROPE: TYPE ~ Rope.ROPE; scratchBuffSize: INT = 128; didCache: SymTab.Ref _ NIL; desiredSizeOfDIDCache: INT _ 50; cacheEntry: TYPE ~ REF cacheEntryRep; cacheEntryRep: TYPE ~ RECORD [ users: INT _ 0, document: YggRep.VDoc _ NIL ]; myCondition: CONDITION; DID: PUBLIC TYPE ~ REF DIDRep; DIDRep: PUBLIC TYPE ~ ROPE; VolatizeFromDID: PUBLIC PROC [transID: YggEnvironment.TransID, did: YggDID.DID, access: YggEnvironment.AccessRights, lock: YggEnvironment.LockOption] RETURNS [document: YggRep.VDoc _ NIL] ~ { readInt: PROC RETURNS [int: INT] = { charsRead: INT _ 0; rint: INT; TRUSTED {charsRead _ IO.UnsafeGetBlock[attributes, [LOOPHOLE[@rint], 0, Basics.charsPerWord]];}; IF charsRead # Basics.charsPerWord THEN ERROR; RETURN[rint]; }; readString: PROC [extraChars: INT] RETURNS [string: ROPE _ NIL]= { nullFound: BOOL _ FALSE; lenInScratch: INT _ 0; numberToFetch: INT _ 3; fourChars: PACKED ARRAY [0..Basics.charsPerWord) OF CHAR; scratch: REF TEXT = RefText.ObtainScratch[scratchBuffSize]; UNTIL nullFound DO charsRead: INT _ 0; TRUSTED {charsRead _ IO.UnsafeGetBlock[attributes, [LOOPHOLE[@fourChars], 0 , numberToFetch]];}; IF charsRead # numberToFetch THEN ERROR; FOR charNo: INT IN [0..numberToFetch) DO IF fourChars[charNo] = 0C THEN {nullFound _ TRUE; EXIT}; scratch[lenInScratch] _ fourChars[charNo]; lenInScratch _ lenInScratch + 1; IF lenInScratch = scratchBuffSize THEN { scratch.length _ lenInScratch; string _ Rope.Concat[string, Rope.FromRefText[s: scratch, start: 0, len: lenInScratch]]; lenInScratch _ 0; }; ENDLOOP; numberToFetch _ 4; ENDLOOP; IF lenInScratch # 0 THEN { scratch.length _ lenInScratch; string _ Rope.Concat[string, Rope.FromRefText[s: scratch, start: 0, len: lenInScratch]]; }; RefText.ReleaseScratch[scratch]; }; contents, attributes, links: IO.STREAM _ NIL; contentsDocType: INT; innerVolatizeFromDID: YggTransContext.TransactionWork = { newLockMode: YggEnvironment.LockMode; openDoc: YggInternal.OpenDoc; openDoc _ YggOpenDoc.Register[trans: trans, did: did]; newLockMode _ YggLock.Set[ trans: trans, lock: YggLock.MakeLockID[did], mode: lock.mode, wait: lock.ifConflict=wait ! YggLock.Failed => { -- Log the error and let the error propagate. logProc: PROC [YggMonitoringLog.LockConflictInfo]; IF (logProc _ YggMonitoringLog.notice.lockConflict) # NIL THEN logProc[[ what: why, -- (conflict or timeout) where: "YggVolatilizeImpl.VolatizeFromDID", transID: transID, mode: lock.mode, specifics: entireFile[""], message: "" ]] } ]; IF (document _ LookupDIDInCache[did]) # NIL THEN RETURN; ReserveDIDInCache[did]; [contents, attributes, links] _ YggDID.StreamsForDID[did]; document _ NEW[YggRep.VDocRep]; document.did _ did; IF attributes # NIL THEN { doneWithAttributes: BOOL _ FALSE; contentsDocType _ readInt[]; DO -- for each attribute attributeName: ROPE _ NIL; -- attribute name attributeValue: LIST OF YggRep.AttributeValue _ NIL; -- attribute name numberOfAttributeValues: INT _ 1; preampleByte: CHAR; preample: YggRep.AttributePreambleByte; fieldDocType: INT; TRUSTED {preampleByte _ IO.GetChar[attributes ! IO.EndOfStream => { doneWithAttributes _ TRUE; CONTINUE; }; ];}; -- @preample points to preample[0] IF doneWithAttributes THEN EXIT; preample _ LOOPHOLE[preampleByte]; fieldDocType _ SELECT preample.typeCode FROM integer => YggRep.int, ropeLarge => YggRep.rope, ropeShort => YggRep.shortRope, float => YggRep.float, date => YggRep.date, uninterpretedBytes => YggRep.uninterpretedBytes, ENDCASE => YggRep.unknown; attributeName _ readString[1]; -- read attribute name IF ~preample.singletonAttribute THEN { numberOfAttributeValues _ readInt[]; }; FOR attVal: INT IN [0..numberOfAttributeValues) DO -- for each attribute value (a field) fieldName: ROPE _ NIL; valueSet: LIST OF YggRep.TypedPrimitiveElement; -- set of values for field IF ~preample.noFieldNames THEN { fieldName _ readString[1]; -- read field name }; DO -- for each typed primitive element in the field's value set fieldValue: YggRep.Bits; IF preample.typeCode = separate THEN { fieldDocType _ readInt[]; }; SELECT fieldDocType FROM YggRep.unknown => EXIT; YggRep.int => { i: INT32; ri: REF INT32; i _ readInt[]; ri _ NEW[INT32 _ i]; fieldValue _ ri; }; YggRep.shortRope => { rRope: ROPE; rRope _ readString[0]; fieldValue _ rRope; }; YggRep.rope => { len: INT; lenToRead: INT; charsRead: INT; rRope: ROPE; scratch: REF TEXT; len _ readInt[]; lenToRead _ Basics.charsPerWord * ((len - 1 + Basics.charsPerWord)/Basics.charsPerWord); scratch _ RefText.ObtainScratch[lenToRead]; TRUSTED {charsRead _ IO.UnsafeGetBlock[attributes, [LOOPHOLE[@scratch], 0 , lenToRead]];}; IF charsRead # lenToRead THEN ERROR; scratch.length _ len; rRope _ Rope.FromRefText[s: scratch, start: 0, len: len]; fieldValue _ rRope; }; YggRep.float => { rReal: REF REAL32; rReal _ NEW[REAL32]; TRUSTED { charsRead: INT; ptReal: POINTER TO REAL32; ptReal _ LOOPHOLE[rReal]; charsRead _ IO.UnsafeGetBlock[attributes, [LOOPHOLE[ptReal], 0, Basics.charsPerWord]]; IF charsRead # Basics.charsPerWord THEN ERROR; }; fieldValue _ rReal; }; YggRep.date => { rAccurateGMT: YggRep.AccurateGMT; rAccurateGMT _ NEW[YggRep.AccurateGMTRep]; TRUSTED { charsRead: INT; ptAGMT: POINTER TO YggRep.AccurateGMTRep; ptAGMT _ LOOPHOLE[rAccurateGMT]; charsRead _ IO.UnsafeGetBlock[attributes, [LOOPHOLE[ptAGMT], 0, YggRep.AccurateGMTRepByteSize]]; IF charsRead # YggRep.AccurateGMTRepByteSize THEN ERROR; }; fieldValue _ rAccurateGMT; }; ENDCASE => { charsRead: INT; junk: PACKED ARRAY [0..Basics.charsPerWord) OF CHAR; len: INT; lenToRead: INT; nullsToRead: INT; rBits: REF YggRep.BitsRep; len _ readInt[]; lenToRead _ Basics.charsPerWord * ((len - 1 + Basics.charsPerWord)/Basics.charsPerWord); rBits _ NEW[YggRep.BitsRep[len]]; rBits.validBytes _ len; TRUSTED { ptBits: POINTER TO YggRep.BitsRep; ptBits _ LOOPHOLE[rBits]; charsRead _ IO.UnsafeGetBlock[attributes, [LOOPHOLE[ptBits], 0, lenToRead]]; IF charsRead # lenToRead THEN ERROR; }; nullsToRead _ len - lenToRead; IF nullsToRead > 0 THEN TRUSTED { charsRead _ IO.UnsafeGetBlock[attributes, [LOOPHOLE[@junk], 0 , nullsToRead]]; IF charsRead # nullsToRead THEN ERROR; }; }; valueSet _ CONS[[fieldDocType, fieldValue], valueSet]; ENDLOOP; -- for each typed primitive element in the field's value set attributeValue _ CONS[[fieldName, valueSet], attributeValue]; ENDLOOP; -- for each attribute value IF Rope.Compare["$contents", attributeName, FALSE] = equal THEN { IF contents # NIL THEN ERROR; IF attributeValue = NIL THEN ERROR; IF attributeValue.first.valueSet = NIL THEN ERROR; IF attributeValue.rest # NIL THEN ERROR; IF attributeValue.first.valueSet = NIL THEN ERROR; IF attributeValue.first.valueSet.rest # NIL THEN ERROR; document.contents _ attributeValue.first.valueSet.first; } ELSE document.attributes _ CONS[[attributeName, preample.ordered, attributeValue], document.attributes]; ENDLOOP; -- for each attribute }; IF contents # NIL THEN { bits: REF YggRep.BitsRep; size: INT; bytesLeft: INT; nextByteToRead: INT _ 0; document.contents.docType _ YggRep.uninterpretedBytes; size _ bytesLeft _ IO.GetLength[contents]; document.contents.bits _ bits _ NEW[YggRep.BitsRep[size]]; bits.validBytes _ size; WHILE bytesLeft > 0 DO nBytesRead: INT; unsafeBlock: Basics.UnsafeBlock; TRUSTED { firstByte: POINTER; firstByte _ @document.contents.bits; unsafeBlock _ [LOOPHOLE[firstByte], nextByteToRead, bytesLeft]; nBytesRead _ IO.UnsafeGetBlock[self: contents, block: unsafeBlock]; }; bytesLeft _ bytesLeft - nBytesRead; nextByteToRead _ nextByteToRead + nBytesRead; ENDLOOP; }; CacheDID[did, document]; }; YggTransContext.EstablishTransactionContext[transID: transID, work: innerVolatizeFromDID]; }; LookupDIDInCache: PUBLIC ENTRY PROC [did: DID] RETURNS [document: YggRep.VDoc _ NIL] ~ { found: BOOL; val: REF; DO [found, val] _ SymTab.Fetch[x: didCache, key: did^]; IF found THEN { entry: cacheEntry; entry _ NARROW[val]; IF entry.users # 0 THEN {WAIT myCondition; LOOP;}; RETURN[entry.document]; }; ENDLOOP; }; ReserveDIDInCache: PUBLIC ENTRY PROC [did: DID] ~ { found: BOOL; val: REF; [found, val] _ SymTab.Fetch[x: didCache, key: did^]; IF found THEN { entry: cacheEntry; entry _ NARROW[val]; WHILE entry.users # 0 DO WAIT myCondition; ENDLOOP; entry.users _ 1; } ELSE ERROR; }; UnreserveDIDInCache: PUBLIC ENTRY PROC [did: DID] ~ { found: BOOL; val: REF; [found, val] _ SymTab.Fetch[x: didCache, key: did^]; IF found THEN { entry: cacheEntry; entry _ NARROW[val]; entry.users _ entry.users - 1; BROADCAST myCondition; } ELSE ERROR; }; CacheDID: PUBLIC ENTRY PROC [did: DID, document: YggRep.VDoc] ~ { found: BOOL; val: REF; [found, val] _ SymTab.Fetch[x: didCache, key: did^]; IF found THEN { entry: cacheEntry; entry _ NARROW[val]; entry.document _ document; } ELSE { IF ~SymTab.Insert[x: didCache, key: did^, val: NEW[cacheEntryRep _ [0, document]]] THEN ERROR; BROADCAST myCondition; }; }; InvalidateDID: PUBLIC ENTRY PROC [did: DID] ~ { found: BOOL; val: REF; [found, val] _ SymTab.Fetch[x: didCache, key: did^]; IF found THEN { entry: cacheEntry; entry _ NARROW[val]; WHILE entry.users # 0 DO WAIT myCondition; ENDLOOP; [] _ SymTab.Delete[x: didCache, key: did^]; }; }; SetSizeOfDIDCache: PUBLIC ENTRY PROC [size: INT] = { desiredSizeOfDIDCache _ size; }; DIDCacheTrimProcess: PROC = { ticksToWait: YggDummyProcess.Ticks; ticksToWait _ YggDummyProcess.MsecToTicks[293]; DO innerTrim: ENTRY PROC = { eachPairAction: SymTab.EachPairAction = { ce: cacheEntry; ce _ NARROW[val]; IF ce.users = 0 THEN [] _ SymTab.Delete[x: didCache, key: key]; size _ size - 1; IF size <= desiredSizeOfDIDCache THEN quit _ TRUE; }; size: INT; size _ SymTab.GetSize[didCache]; [] _ SymTab.Pairs[x: didCache, action: eachPairAction]; }; YggDummyProcess.Pause[ticksToWait]; IF SymTab.GetSize[didCache] > desiredSizeOfDIDCache THEN innerTrim[]; ENDLOOP; }; didCache _ SymTab.Create[mod: 129, case: TRUE]; TRUSTED { YggDummyProcess.Detach[FORK DIDCacheTrimProcess]; YggDummyProcess.SetTimeout[condition: @myCondition, ticks: YggDummyProcess.MsecToTicks[157]]; }; END. pYggVolatilizeImpl.mesa Copyright Σ 1988 by Xerox Corporation. All rights reserved. Bob Hagmann May 6, 1988 11:49:15 am PDT This module converts documents from stable to volatile forms. For Phase 0, the DIDRep is just a string that names a directory (without the trailing /). Exported conversion procedures Given a DID, return the volatile form of the document it refers to. read an int from the attributes stream read a null terminated string from the attributes stream PROCEDURE [trans: YggTransactionMap.TransHandle]; Check to be sure that the value is a singleton value of a singleton set IF links # NIL THEN { }; Cache management utilities Given a DID, return the volatile form of the document if it is cached. Block until object is cached, if it is reserved. Given a DID, reserve this DID as being volatized. Calling this procedure obligates the caller to call CacheDID to clear the reservation. Undo a reservation. Add this document to the cache for the did. Remove this did from the cache. Remove this did from the cache. Cache trim PROC [key: Key, val: Val] RETURNS [quit: BOOL _ FALSE]; Initialization Κz˜code•Mark outsideHeaderšœ™Kšœ<™šœ ˜ Kšœ ’˜%Jšœ+˜+J˜J˜J˜Jšœ ˜ J˜——J˜—Kšœ˜—Kšœ&œœœ˜8Kšœ˜Kšœ:˜:Kšœ œ˜Kšœ˜šœœœ˜Kšœœœ˜!Kšœ˜šœ’˜Kšœœœ’˜.Kšœœœœ’˜HKšœœ˜!Kšœœ˜Kšœ'˜'Kšœœ˜šœœœ˜CKšœœ˜Kšœ˜ Kšœ˜—Kšœ’"˜)Kšœœœ˜ Kšœ œ˜"šœœ˜,Kšœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ0˜0Kšœ˜—Kšœ ’˜7šœœ˜&Kšœ$˜$K˜—š œ œœœ’%˜YKšœ œœ˜Kšœ œœ’˜Jšœœ˜ Kšœ’˜/K˜—šœ’<˜?K˜šœœ˜&K˜K˜—šœ˜Kšœœ˜šœ˜Kšœœ˜ Kšœœœ˜Kšœ˜Kšœœœ˜K˜K˜—šœ˜Kšœœ˜ Kšœ˜Kšœ˜K˜—šœ˜Kšœœ˜ Kšœ œ˜Kšœ œ˜Kšœœ˜ Kšœ œœ˜Kšœ˜KšœX˜XKšœ+˜+Kšœœœ˜ZKšœœœ˜$Kšœ˜Kšœ9˜9Kšœ˜K˜—šœ˜Kšœœœ˜Kšœœœ˜šœ˜ Kšœ œ˜Kšœœœœ˜Kšœ œ˜Kšœ œœ#˜VKšœ!œœ˜.Kšœ˜—Kšœ˜Kšœ˜—šœ˜Kšœ!˜!Kšœœ˜*šœ˜ Kšœ œ˜Kšœœœ˜)Kšœ œ˜ Kšœ œœ-˜`Kšœ+œœ˜8Kšœ˜—Kšœ˜Kšœ˜—šœ˜ Kšœ œ˜Kš œœœœœ˜4Kšœœ˜ Kšœ œ˜Kšœ œ˜Kšœœ˜Kšœ˜KšœX˜XKšœœ˜!Kšœ˜šœ˜ Kšœœœ˜"Kšœ œ˜Kšœ œœ˜LKšœœœ˜$Kšœ˜—Kšœ˜šœœœ˜!Kšœ œœ˜NKšœœœ˜&Kšœ˜—K˜——Kšœ œ'˜6Kšœ’<˜F—Kšœœ(˜=Kšœ’˜%—šœ*œ œ˜Ašœ œœœ˜K™G—Kšœœœœ˜#Kšœ!œœœ˜2Kšœœœœ˜(Kšœ!œœœ˜2Kšœ&œœœ˜7Kšœ8˜8K˜—KšœœœI˜iKšœ’˜—K˜—šœ œœ˜Kšœ˜Kšœœ˜ Kšœ œ˜Kšœœ˜Kšœ6˜6Kšœœ˜*Kšœ œ˜:Kšœ˜šœ˜Kšœ œ˜Kšœ ˜ šœ˜ Kšœ œ˜Kšœ$˜$Kšœœ(˜?Kšœ œ4˜CKšœ˜—Kšœ#˜#Kšœ-˜-Kšœ˜—K˜—šœ œœ™K™—Kšœ˜K˜—KšœZ˜Z˜K™K™———™š œœœœœœœ˜XKšœœm™xKšœœ˜ Kšœœ˜ š˜Kšœ4˜4šœœ˜K˜Kšœœ˜Kšœœœœ˜2Kšœ˜K˜—Kšœ˜—K˜K™K™—š œœ œœ˜3Kšœœœl™‰Kšœœ˜ Kšœœ˜ Kšœ4˜4šœœ˜K˜Kšœœ˜Kšœœœœ˜3Kšœ˜K˜—Kšœœœ˜ K˜K™K™—š œœ œœ˜5Kšœ™Kšœœ˜ Kšœœ˜ Kšœ4˜4šœœ˜K˜Kšœœ˜Kšœ˜Kš œ ˜K˜—Kšœœœ˜ K˜K™K™—š œœ œœ˜AKšœ+™+Kšœœ˜ Kšœœ˜ Kšœ4˜4šœœ˜K˜Kšœœ˜Kšœ˜K˜—šœœ˜Kšœ-œ!œœ˜^Kš œ ˜K˜—K˜K™K™—š  œœ œœ˜/Kšœ™Kšœœ˜ Kšœœ˜ Kšœ4˜4šœœ˜K˜Kšœœ˜Kšœœœœ˜3Kšœ+˜+K˜—K˜K™—š œœœ˜4Kšœ™Kšœ˜K˜—K™—™ š œœ˜Lšœ#˜#Lšœ/˜/š˜š‘ œœœ˜šœ)˜)Lšœœœœ™7Lšœ˜Lšœœ˜Lšœœ+˜?Lšœ˜Lšœœœ˜2Lšœ˜—Lšœœ˜ Lšœ ˜ Lšœ7˜7Lšœ˜—Lšœ#˜#Lšœ2œ ˜ELšœ˜—Lšœ˜——™Kšœ)œ˜/šœ˜ Lšœœ˜1Lšœ]˜]Lšœ˜K™K™——K˜Kšœ˜—…—+?z