DBStorageTupleImpl:
CEDAR
MONITOR
IMPORTS
DBCommon,
DBSegment,
Process,
RefTab,
SafeStorage,
IO
EXPORTS
DBStorage,
DBStorageTuple
= BEGIN
TID: TYPE = DBCommon.TID;
NullTID: TID = DBCommon.NullTID;
Segment: TYPE = DBCommon.Segment;
TupleObject: PUBLIC TYPE = DBCommon.TupleObject;
TupleHandle: TYPE = REF TupleObject;
nullTupleHandle: TupleHandle;
After initialization, points to a TupleObject with tid = NullTID (on storedTupleList.)
This is returned by NullTupleHandle[] so that others may share. Nobody should modify!
storedFinalizationQLen: INT = 100;
establishFinalizationFailed: BOOL ← FALSE;
tupleFQ: SafeStorage.FinalizationQueue ← SafeStorage.NewFQ[length: storedFinalizationQLen];
systemTuples: TupleHandle ← NIL;
freeTuples: TupleHandle ← NIL;
freeCount: NAT ← 0;
For each segment in use there is an associated storedTupleList (head of doubly-linked list (through pred, succ) of all in-use stored tuple objects).
SegmentRecordHandle: TYPE = REF SegmentRecord;
SegmentRecord: TYPE = RECORD [ storedTupleList: TupleHandle ← NIL ];
A RefTab is used to map segments to their tuple and free lists. The segment table and the free list are monitored
sizeOfSegmentTable: CARDINAL = 17;
segmentTable: RefTab.Ref = RefTab.Create[mod: sizeOfSegmentTable];
maxFree: CARDINAL = 100;
InitSegmentTuples:
PUBLIC
ENTRY PROC [segment: Segment] = {
sHandle: SegmentRecordHandle ← NARROW[RefTab.Fetch[segmentTable, segment].val];
IF sHandle # NIL THEN RETURN;
sHandle ← NEW[SegmentRecord];
sHandle.storedTupleList ← NEW[TupleObject ← [tid: NullTID, pred: NIL, succ: NIL, cacheHint: NIL]];
sHandle.storedTupleList.pred ← sHandle.storedTupleList.succ ← sHandle.storedTupleList;
[] ← RefTab.Store[segmentTable, segment, sHandle] };
ConsTupleObject:
PUBLIC
ENTRY
PROC [tid:
TID, cacheHint: DBCommon.CacheHandle]
RETURNS [result: TupleHandle] = {
ENABLE UNWIND => InitSegmentTuples[DBSegment.SegmentFromTID[tid]];
segment: Segment = DBSegment.SegmentFromTID[tid];
sHandle: SegmentRecordHandle;
found: BOOLEAN; item: RefTab.Val;
[found, item] ← RefTab.Fetch[segmentTable, segment];
IF NOT found THEN ERROR;
sHandle ← NARROW[item];
IF freeCount > 0
THEN {
result ← GetFreeTuple[sHandle.storedTupleList];
result.tid ← tid; result.cacheHint ← cacheHint
}
ELSE {
result ← NEW[TupleObject ← [tid: tid, cacheHint: cacheHint]];
result.pred ← sHandle.storedTupleList.pred;
result.succ ← sHandle.storedTupleList;
sHandle.storedTupleList.pred.succ ← result;
sHandle.storedTupleList.pred ← result;
SafeStorage.EnableFinalization[result] };
RETURN[result];
};
GetFreeTuple:
INTERNAL
PROC[head: TupleHandle]
RETURNS[result: TupleHandle] = {
result ← freeTuples.succ;
Remove the handle from the free list
result.succ.pred ← result.pred;
result.pred.succ ← result.succ;
And add it to the list of stored handles that begins at head
result.succ ← head.succ;
result.pred ← head;
head.succ.pred ← result;
head.succ ← result;
freeCount ← freeCount - 1;
SafeStorage.EnableFinalization[result] };
ConsSystemTupleObject:
PUBLIC
ENTRY
PROC []
RETURNS [result: TupleHandle] = {
IF freeCount > 0 THEN { result ← GetFreeTuple[systemTuples] }
ELSE {
result ← NEW[TupleObject ← []];
result.pred ← systemTuples.pred;
result.succ ← systemTuples;
systemTuples.pred.succ ← result;
systemTuples.pred ← result;
SafeStorage.EnableFinalization[result] };
RETURN[result] };
TupleObjectFinalizerProcess:
PROC [] = {
FinalizeStoredTupleObject:
ENTRY
PROC [t: TupleHandle] = {
ENABLE UNWIND => NULL;
IF freeCount < maxFree
THEN {
Cut it out of the list to which it belongs
t.succ.pred ← t.pred;
t.pred.succ ← t.succ;
Stitch it back into the free list
t.succ ← freeTuples.succ;
t.pred ← freeTuples;
freeTuples.succ.pred ← t;
freeTuples.succ ← t;
freeCount ← freeCount + 1 }
ELSE {
Next line is a noop for single-element list (t.succ = t), but that is ok ...
t.succ.pred ← t.pred; t.pred.succ ← t.succ;
t.succ ← t.pred ← NIL;
}
};
DO
th: TupleHandle ← NARROW[SafeStorage.FQNext[tupleFQ]];
FinalizeStoredTupleObject[th];
ENDLOOP;
};
TIDOfTuple:
PUBLIC
PROC [x: TupleHandle]
RETURNS [
TID] = {
RETURN[x.tid] };
EqualTuple:
PUBLIC
PROC [x, y: TupleHandle]
RETURNS [
BOOLEAN] = {
IF x=NIL THEN x ← nullTupleHandle;
IF y=NIL THEN y ← nullTupleHandle;
RETURN[x.tid = y.tid];
};
NullTupleHandle: PUBLIC PROC [] RETURNS [TupleHandle] = { RETURN[nullTupleHandle] };
InvalidateMatchingTuples:
PUBLIC
ENTRY PROC [x: TupleHandle] = {
xTID: TID = x.tid;
s: Segment = DBSegment.SegmentFromTID[x.tid];
sHandle: SegmentRecordHandle;
found: BOOLEAN; item: RefTab.Val;
[found, item] ← RefTab.Fetch[segmentTable, s];
IF NOT found THEN ERROR;
sHandle ← NARROW[item];
FOR p: TupleHandle ← sHandle.storedTupleList.succ, p.succ
UNTIL p=sHandle.storedTupleList
DO
IF p.tid=xTID THEN p.tid ← NullTID;
ENDLOOP;
};
IsValidTuple:
PUBLIC
PROC [x: TupleHandle]
RETURNS [valid:
BOOL] = {
IF DBSegment.IsValidTID[x.tid] THEN RETURN [valid: TRUE]
ELSE { x.tid ← NullTID; RETURN [valid: FALSE] };
};
NullTuple:
PUBLIC
PROC [x: TupleHandle]
RETURNS [yes:
BOOL] = {
IF x = NIL THEN RETURN [TRUE];
RETURN[ x.tid = NullTID];
};
InvalidateTuple:
PUBLIC
PROC [x: TupleHandle] = {
IF x#NIL THEN x.tid ← NullTID;
};
CallAfterFinishTransaction:
PUBLIC
ENTRY PROC [s: Segment] = {
sHandle: SegmentRecordHandle;
found: BOOLEAN; item: RefTab.Val;
[found, item] ← RefTab.Fetch[segmentTable, s];
IF NOT found THEN ERROR;
sHandle ← NARROW[item];
FOR p: TupleHandle ← sHandle.storedTupleList.succ, p.succ UNTIL p=sHandle.storedTupleList DO p.tid ← NullTID ENDLOOP };
PrintTupleObjectToStream:
PROC [t: TupleHandle, debugStream:
IO.
STREAM] = {
IOref:
PROC[p:
REF
ANY]
RETURNS[
IO.Value] =
INLINE {
RETURN[IO.card[LOOPHOLE[p, LONG CARDINAL]]]};
debugStream.PutF["tupleHdl: %11bB, tupleID: %12bB, cacheHdl: %11bB",
IOref[t], IO.card[t.tid], IOref[t.cacheHint]] };
PrintTupleObject:
PUBLIC
PROC [t: TupleHandle] = {
PrintTupleObjectToStream[t, DBCommon.GetDebugStream[]]
};
PrintTupleList:
PROC [t: TupleHandle] =
BEGIN start: TupleHandle← t;
out: IO.STREAM = DBCommon.GetDebugStream[];
out.PutF["tid %g\n", IO.int[t.tid]];
FOR t← start.succ, t.succ
UNTIL t=start
DO
out.PutF["tid %g\n", IO.int[t.tid]] ENDLOOP;
END;
SafeStorage.EstablishFinalization[ type:
CODE[TupleObject], npr: 2, fq: tupleFQ !
SafeStorage.CantEstablishFinalization => {establishFinalizationFailed← TRUE; CONTINUE} ];
IF establishFinalizationFailed
THEN
SafeStorage.ReEstablishFinalization[ type: CODE[TupleObject], npr: 2, fq: tupleFQ ];
systemTuples ← NEW[TupleObject ← [tid: NullTID, pred: NIL, succ: NIL, cacheHint: NIL]];
systemTuples.pred ← systemTuples.succ ← systemTuples;
freeTuples ← NEW[TupleObject ← [tid: NullTID, pred: NIL, succ: NIL, cacheHint: NIL]];
freeTuples.pred ← freeTuples.succ ← freeTuples;
nullTupleHandle ← NEW[TupleObject ← [tid: NullTID, pred: NIL, succ: NIL, cacheHint: NIL]];
TRUSTED{ Process.Detach[ FORK TupleObjectFinalizerProcess[] ] }