-- CoordinatorTransIDImpl.mesa
-- Transaction ID generator.
-- Last edited by
-- MBrown on November 3, 1982 3:29 pm
-- NOTES:
-- (1) random bits in TransID should be more unpredictable; someone who sees enough
--consecutive TransIDs should not be able to guess the next one, even knowing
--the TransID generation algorithm.
-- (2) need to implement controlled shutdown (and restart) of TransID generator?
--Note that the value of maxSeqNumsIssuedWithoutForce may only be decreased after
--a controlled shutdown (not after a crash); increasing it is always OK.
DIRECTORY
AlpineEnvironment,
AlpineInternal,
ConcreteTransID,
Coordinator,
CoordinatorInternal USING [],
Log;
CoordinatorTransIDImpl: MONITOR
IMPORTS
Log
EXPORTS
AlpineEnvironment,
AlpineInternal,
CoordinatorInternal =
BEGIN
VolumeID: TYPE = AlpineEnvironment.VolumeID;
TransID: PUBLIC TYPE = ConcreteTransID.TransID;
-- AlpineEnvironment.TransID.
CoordinatorObject: PUBLIC TYPE = Coordinator.Object;
-- AlpineInternal.CoordinatorObject.
myFileStoreID: VolumeID;
randomSeed: INT ← 0;
lastSeqNum: INT ← INT.FIRST;
-- The largest transaction sequence number ever issued.
nSeqNumsIssuedSinceForce: INT [0 .. maxSeqNumsIssuedWithoutForce];
maxSeqNumsIssuedWithoutForce: INT = 8;
nextForceLogRecordID: Log.RecordID;
NoticeCoordinatorBegin: PUBLIC PROC [trans: TransID] = {
lastSeqNum ← MAX [trans.idOnFileStore, lastSeqNum];
randomSeed ← trans.randomBits;
};
InitTransIDGenerator: PUBLIC ENTRY PROC [fileStore: VolumeID] = {
myFileStoreID ← fileStore;
lastSeqNum ← lastSeqNum + (2*maxSeqNumsIssuedWithoutForce - 1);
nSeqNumsIssuedSinceForce ← 0;
-- nextForceLogRecordID will be initialized on first call to NextTransID, since
--nSeqNumsIssuedSinceForce = 0.
InitRandom[randomSeed];
};
NextTransID: PUBLIC ENTRY PROC [c: Coordinator.Handle] = {
IF nSeqNumsIssuedSinceForce = maxSeqNumsIssuedWithoutForce THEN {
Log.Force[followingRecord: nextForceLogRecordID];
nSeqNumsIssuedSinceForce ← 0 };
c.transID ← TransID[
fileStore: myFileStoreID,
idOnFileStore: (lastSeqNum ← lastSeqNum + 1),
randomBits: Random[]];
[thisRecord: c.beginRecord, followingRecord: c.forceRecord] ← Log.CoordinatorWrite[
c, coordinatorBegin, Log.nullBlock];
IF nSeqNumsIssuedSinceForce = 0 THEN nextForceLogRecordID ← c.forceRecord;
nSeqNumsIssuedSinceForce ← nSeqNumsIssuedSinceForce + 1;
};
-- Additive random number generator using the recurrence y(n) = y(n-55) - y(n-24).
--See Knuth Algorithm 3.2.2 A (Volume 2, second edition, p.27.)
y: ARRAY [0..55] OF INT; -- y[0] included to improve the compiled code.
j, k: INTEGER [0..55];
Random: INTERNAL PROC [] RETURNS [result: INT] = INLINE {
result ← y[k] - y[j];
y[k] ← result;
IF (j ← j-1) = 0 THEN j ← 55;
IF (k ← k-1) = 0 THEN k ← 55;
RETURN [result]
};
InitRandom: INTERNAL PROC [seed: INT] = {
numCalls: INTEGER = 289;
minSeed: INT = INT.LAST / 10;
maxSeed: INT = minSeed * 9;
defaultSeed: INT = 1354117939;
g, gPrev, gSave: INT;
IF seed = 0 OR seed = INT.FIRST THEN seed ← defaultSeed;
seed ← seed.ABS;
WHILE seed<minSeed DO seed ← seed*3 ENDLOOP;
WHILE seed>maxSeed DO seed ← seed/3 ENDLOOP;
-- The array y is initialized by placing seed in y[55], and scattering the values
-- (-1)**(i-1) * (F(i) - seed*F(i-1)), 0<i<55,
-- (where F(i) denotes the i-th Fibonacci number) throughout the rest of y. Then
--the generating procedure Random is called, numCalls times, to make things
--sufficiently random.
y[55] ← gPrev ← seed;
g ← 1;
FOR i: CARDINAL IN [1..54] DO
y[(21*i) MOD 55] ← gSave ← g; g ← gPrev-g; gPrev ← gSave;
ENDLOOP;
j ← 24; k ← 55;
THROUGH [1..numCalls] DO [] ← Random[] ENDLOOP;
};
END.