<> <> <> <<>> <> <<>> 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.