FILE: JukeboxImpl.mesa
Last Edited by: Stewart, December 30, 1983 11:38 am, Cedar5
Last Edited by: Swinehart, September 23, 1984 8:54:07 pm PDT
Doug Terry, July 28, 1986 4:35:44 pm PDT
Ades, February 21, 1986 2:52:13 pm PST
DIRECTORY
Basics USING [BITAND, BITNOT, BITOR, LowHalf],
BasicTime USING [Now],
FileNames USING [ConvertToSlashFormat, ResolveRelativePath],
FS USING [BytesForPages, Close, Create, Delete, Error, ExpandName, nullOpenFile, Open, OpenFile, Read, SetByteCountAndCreatedTime, Write],
IO USING [int, PutF, PutRope, STREAM],
Jukebox,
Rope USING [Equal, ROPE],
VM USING [AddressForPageNumber, Allocate, Free, Interval, wordsPerPage];
JukeboxImpl: MONITOR LOCKS self.LOCK USING self: Jukebox.Handle
IMPORTS Basics, BasicTime, FileNames, FS, IO, Rope, VM
EXPORTS Jukebox
SHARES Jukebox =
BEGIN OPEN Jukebox;
Global variables
instances: PUBLIC LIST OF Jukebox.Handle ← NIL;
Standard error messages.
FormatErrorMsg: Rope.ROPE = "Internal format error in jukebox.";
OpenErrorMsg: Rope.ROPE = "No jukebox open.";
BadTuneErrorMsg: Rope.ROPE = "Bad tune id.";
BadTunePointerMsg: Rope.ROPE = "Bad tune pointer.";
Standard page numbers:
HeaderPageNumber: Jukebox.PageNumber = 1;
FirstMapPageNumber: Jukebox.PageNumber = 2;
Bit patterns used to manipulate the tune header bit map:
Bits:
ARRAY[0..15]
OF
CARDINAL =
[1,2,4,10B,20B,40B,100B,200B,400B,1000B,2000B,
4000B,10000B,20000B,40000B,100000B];
BitsInUse: CARDINAL = 177777B;
Signal definitions.
Error: PUBLIC ERROR[reason: ErrorCode, rope: Rope.ROPE] = CODE;
MissingChirp: PUBLIC ERROR = CODE;
EOF: PUBLIC ERROR = CODE;
Utility procedures
SHORT:
PROC [l:
LONG
UNSPECIFIED]
RETURNS [
UNSPECIFIED] = {
RETURN[Basics.LowHalf[l]];
};
Exported procedures
CreateJukebox:
PUBLIC
PROC [name: Rope.
ROPE, nPages:
INT, nTunes:
INT] = {
space: VM.Interval; -- Holds jukebox disk block.
locWindow: Jukebox.WindowOrigin; -- Used to initialize jukebox.
h: LONG POINTER TO JukeboxHeader;
m: LONG POINTER TO TuneBitMap;
d: LONG POINTER TO TuneDescriptor;
f: LONG POINTER TO FreeList;
nChirps, nMaps: INT;
firstChirp: INT;
t1, t2: INTEGER;
fullFName: Rope.ROPE;
errorMsg: Rope.ROPE ← NIL;
self: Jukebox.Handle ← NEW[Jukebox.JukeboxObject];
nMaps ← (nTunes + 16*bitMapSize - 1)/(16*bitMapSize);
firstChirp ← 2 + nMaps + 2*nTunes;
nChirps ← (nPages - firstChirp)/pagesPerChirp;
IF nChirps < 1 THEN ERROR Error[TooManyTunes, "Too many tunes requested for jukebox size."];
name ← FileNames.ResolveRelativePath[name];
fullFName ←
FS.ExpandName[name !
FS.Error =>
IF error.group = $user
THEN {
errorMsg ← error.explanation;
CONTINUE;
}].fullFName;
IF errorMsg # NIL THEN ERROR Error[reason: NoJukebox, rope: errorMsg];
locWindow.file ← FS.nullOpenFile;
locWindow.file ←
FS.Open[name: fullFName, lock: $write, wDir:
NIL !
FS.Error => {
SELECT error.group
FROM
$lock => ERROR Error[reason: JukeboxOpen, rope: error.explanation];
ENDCASE => CONTINUE;
}];
IF locWindow.file #
FS.nullOpenFile
THEN {
FS.Close[locWindow.file];
ERROR Error[reason: JukeboxOpen, rope: "Cannot create jukebox: already exists"];
};
locWindow.file ← FS.Create[name: fullFName, pages: nPages, setKeep: TRUE, keep: 1, wDir: NIL];
FS.SetByteCountAndCreatedTime[file: locWindow.file, bytes: FS.BytesForPages[nPages], created: BasicTime.Now[]];
Allocate a space and set pointers so we can initialize the
various blocks of the jukebox.
space ← VM.Allocate[count: 1];
h ← LOOPHOLE[VM.AddressForPageNumber[page: space.page]];
m ← LOOPHOLE[h];
d ← LOOPHOLE[h];
f ← LOOPHOLE[h];
Initialize the header page.
h.magic ← magicJukeboxHeader;
h.nTunes ← nTunes;
h.nFreeChirps ← h.nChirps ← nChirps;
h.freeListHead ← h.firstChirp ← firstChirp;
h.tuneHWM ← -1;
locWindow.base ← HeaderPageNumber;
CopyOut[space, locWindow];
Initialize the tune header bit map.
FOR i: INTEGER IN [0..bitMapSize) DO m.bits[i] ← 0 ENDLOOP;
m.magic ← magicTuneBitMap;
FOR i:
INT
IN [0..nMaps)
DO
locWindow.base ← FirstMapPageNumber + i;
CopyOut[space, locWindow];
ENDLOOP;
In the last map page, mark tune headers after the last valid one
as in use.
t1 ← SHORT[nMaps*bitMapSize*16 - nTunes];
t2 ← bitMapSize - t1/16;
t1 ← t1 MOD 16;
FOR i: INTEGER IN [t2..bitMapSize) DO m.bits[i] ← BitsInUse ENDLOOP;
FOR i:
INTEGER
IN [16-t1..15]
DO
m.bits[t2-1] ← Basics.BITOR[m.bits[t2-1], Bits[i]];
ENDLOOP;
CopyOut[space, locWindow];
Initialize the tune header pages.
d.magic ← magicTuneHeader;
d.state ← available;
d.size ← 0;
FOR i: INTEGER IN [0..nHdrChirps) DO d.chirps[i] ← 0 ENDLOOP;
d.indirectChirp ← 0;
d.deepChirp ← 0;
d.openCount ← 0;
FOR i:
INT
IN [0..nTunes)
DO
d.tuneId ← locWindow.base ← 2*i + 2 + nMaps;
CopyOut[space, locWindow];
ENDLOOP;
Initialize the free list.
f.magic ← magicFreeList;
f.nChirps ← freeListSize;
FOR i:
INTEGER
IN [0..freeListSize)
DO
f.chirps[i] ← firstChirp + pagesPerChirp*i;
ENDLOOP;
f.next ← f.chirps[freeListSize-1] + pagesPerChirp;
locWindow.base ← firstChirp;
CopyOut[space, locWindow];
FOR i:
INT
IN [1..(nChirps + freeListSize - 1)/freeListSize)
DO
FOR j:
INTEGER
IN [0..freeListSize)
DO
f.chirps[j] ← f.chirps[j] + freeListSize*pagesPerChirp;
ENDLOOP;
f.next ← f.next + freeListSize*pagesPerChirp;
locWindow.base ← firstChirp + i*freeListSize*pagesPerChirp;
CopyOut[space, locWindow];
ENDLOOP;
Handle the last block of the free list specially, since it may not contain
a full list of chirps.
f.nChirps ← SHORT[nChirps MOD freeListSize];
f.next ← 0;
IF f.nChirps = 0 THEN f.nChirps ← freeListSize;
CopyOut[space, locWindow];
VM.Free[space];
FS.Close[locWindow.file];
locWindow.file ← FS.nullOpenFile;
};
DeleteJukebox:
PUBLIC
PROC [name: Rope.
ROPE] = {
fullFName: Rope.ROPE;
errorMsg: Rope.ROPE ← NIL;
name ← FileNames.ResolveRelativePath[name];
fullFName ←
FS.ExpandName[name !
FS.Error =>
IF error.group = $user
THEN {
errorMsg ← error.explanation;
CONTINUE;
}].fullFName;
IF errorMsg # NIL THEN ERROR Error[reason: NoJukebox, rope: errorMsg];
IF FindJukebox[fullFName] # NIL THEN ERROR Error[reason: JukeboxOpen, rope: "Cannot delete an open jukebox"];
FS.Delete[name: fullFName, wDir: NIL];
};
FindJukebox:
PUBLIC
PROC [name: Rope.
ROPE]
RETURNS [Jukebox.Handle] = {
fullFName: Rope.ROPE;
errorMsg: Rope.ROPE ← NIL;
name ← FileNames.ResolveRelativePath[name];
fullFName ←
FS.ExpandName[name !
FS.Error =>
IF error.group = $user
THEN {
errorMsg ← error.explanation;
CONTINUE;
}].fullFName;
IF errorMsg # NIL THEN ERROR Error[reason: NoJukebox, rope: errorMsg];
fullFName ← FileNames.ConvertToSlashFormat[fullFName];
FOR l:
LIST
OF Jukebox.Handle ← instances, l.rest
WHILE l #
NIL
DO
IF Rope.Equal[l.first.jukeboxName, fullFName, FALSE] THEN RETURN[l.first];
ENDLOOP;
RETURN[NIL];
};
OpenJukebox:
PUBLIC
PROC [name: Rope.
ROPE]
RETURNS [Jukebox.Handle] = {
fullFName: Rope.ROPE;
errorMsg: Rope.ROPE ← NIL;
self: Jukebox.Handle ← NEW[Jukebox.JukeboxObject];
name ← FileNames.ResolveRelativePath[name];
fullFName ←
FS.ExpandName[name !
FS.Error =>
IF error.group = $user
THEN {
errorMsg ← error.explanation;
CONTINUE;
}].fullFName;
IF errorMsg # NIL THEN ERROR Error[reason: NoJukebox, rope: errorMsg];
fullFName ← FileNames.ConvertToSlashFormat[fullFName];
IF FindJukebox[fullFName] # NIL THEN ERROR Error[reason: JukeboxOpen, rope: "Jukebox already open"];
self.window.file ←
FS.Open[name: fullFName, lock: $write, wDir:
NIL !
FS.Error => {
SELECT error.group
FROM
$user => ERROR Error[reason: NoJukebox, rope: error.explanation];
$lock => ERROR Error[reason: JukeboxOpen, rope: error.explanation];
ENDCASE => REJECT;
}];
self.jukeboxName ← fullFName;
OpenLocked[self];
RETURN[self];
};
OpenLocked:
ENTRY
PROC [self: Jukebox.Handle] = {
ourhdr: LONG POINTER TO JukeboxHeader ← NIL;
ourfree: LONG POINTER TO FreeList ← NIL;
{
ENABLE
UNWIND => {
IF ourhdr # NIL THEN VM.Free[self.hdrSpace];
IF ourfree # NIL THEN VM.Free[self.freeSpace];
};
Allocate spaces to cache the header block, and a free list block.
self.hdrSpace ← VM.Allocate[count: 1];
self.freeSpace ← VM.Allocate[count: 1];
ourhdr ← LOOPHOLE[VM.AddressForPageNumber[self.hdrSpace.page]];
ourfree ← LOOPHOLE[VM.AddressForPageNumber[self.freeSpace.page]];
Read in the header block, first bit map block, and first free list block.
self.window.base ← HeaderPageNumber;
CopyIn[self.hdrSpace, self.window];
IF ourhdr.magic # magicJukeboxHeader THEN ERROR Error[JukeboxFormat, FormatErrorMsg];
self.currentMapPageNumber ← FirstMapPageNumber;
self.window.base ← self.currentFreeChirpNumber ← ourhdr.freeListHead;
IF self.currentFreeChirpNumber # 0
THEN {
CopyIn[self.freeSpace, self.window];
IF ourfree.magic # magicFreeList THEN ERROR Error[JukeboxFormat, FormatErrorMsg];
}
ELSE {
ourfree.magic ← magicFreeList;
ourfree.nChirps ← 0;
ourfree.next ← 0;
};
self.maxMapPageNumber ← FirstMapPageNumber + (ourhdr.nTunes/(16*bitMapSize));
self.firstDesPageNumber ← self.maxMapPageNumber + 1;
self.openTuneHeaderList ← NIL;
Advance the header to reference the NEXT free list block, and write out the header. This way, if the system crashes we may lose track of a few free blocks but will never think a block is free when it isn't.
ourhdr.freeListHead ← ourfree.next;
ourhdr.nFreeChirps ← ourhdr.nFreeChirps - ourfree.nChirps;
self.window.base ← HeaderPageNumber;
CopyOut[self.hdrSpace, self.window];
self.hdr ← ourhdr;
self.free ← ourfree;
instances ← CONS[self, instances];
}};
Info:
PUBLIC
ENTRY
PROC [self: Jukebox.Handle]
RETURNS [name: Rope.
ROPE, nPages, nTunes:
INT, tuneHWM:
INT] = {
ENABLE UNWIND => NULL;
loc: Rope.ROPE;
loc ← self.jukeboxName;
IF self.hdr = NIL THEN RETURN[name: NIL, nPages: 0, nTunes: 0, tuneHWM: -1];
RETURN [name: loc, nPages: self.hdr.nChirps*pagesPerChirp, nTunes: self.hdr.nTunes, tuneHWM: self.hdr.tuneHWM];
};
CloseJukebox:
PUBLIC
ENTRY
PROC [self: Jukebox.Handle]
RETURNS [Jukebox.Handle] = {
ENABLE UNWIND => NULL;
IF self.hdr = NIL THEN ERROR Error[NoJukebox, OpenErrorMsg];
Close all of the open tunes.
WHILE self.openTuneHeaderList #
NIL
DO
self.openTuneHeaderList.openCount ← 1;
InternalCloseTune[self, self.openTuneHeaderList, TRUE]
ENDLOOP;
Update the free list and the header block. Order of write-out is important here, if the jukebox is to survive after inopportune crashes.
self.hdr.nFreeChirps ← self.hdr.nFreeChirps + self.free.nChirps;
IF self.free.nChirps # 0
THEN {
self.free.next ← self.hdr.freeListHead;
self.hdr.freeListHead ← self.window.base ← self.currentFreeChirpNumber;
CopyOut[self.freeSpace, self.window];
};
self.window.base ← HeaderPageNumber;
CopyOut[self.hdrSpace, self.window];
Clear out the data structures.
VM.Free[self.freeSpace];
self.free ← NIL;
VM.Free[self.hdrSpace];
self.hdr ← NIL;
FS.Close[self.window.file];
self.window.file ← FS.nullOpenFile;
instances ← DRemove[ref: self, list: instances];
RETURN[NIL];
};
DRemove:
INTERNAL
PROC [ref: Jukebox.Handle, list:
LIST
OF Jukebox.Handle]
RETURNS[
LIST
OF Jukebox.Handle] = {
l, l1: LIST OF Jukebox.Handle ← NIL;
l ← list;
UNTIL l =
NIL
DO
IF l.first = ref
THEN {
IF l1 = NIL THEN RETURN[l.rest]; -- ref was first object on list
l1.rest ← l.rest;
RETURN[list];
};
l1 ← l;
l ← l.rest;
ENDLOOP;
RETURN [list];
}; -- of Dremove;
CreateTune:
PUBLIC
ENTRY
PROC [self: Jukebox.Handle, tuneId: Jukebox
.TuneID ← Jukebox.newTuneID]
RETURNS [Tune] = {
This procedure creates a new tune. If tuneId is negative, then this routine selects a new tuneId. If tuneId is positive, then the indicated tune is used: if it had previously been allocated, then its old contents are discarded.
ENABLE UNWIND => NULL;
tune: Tune ← NIL;
word, bit: INTEGER;
mapPage: INT;
mapSpace: VM.Interval;
map: LONG POINTER TO TuneBitMap ← NIL;
tuneSpace: VM.Interval;
{
ENABLE
UNWIND => {
IF map # NIL THEN VM.Free[mapSpace];
IF tune # NIL THEN VM.Free[tuneSpace];
};
IF self.hdr = NIL THEN ERROR Error[NoJukebox, OpenErrorMsg];
Allocate a space for a map page and for the new tune.
mapSpace ← VM.Allocate[count: 1];
map ← LOOPHOLE[VM.AddressForPageNumber[mapSpace.page]];
tuneSpace ← VM.Allocate[count: 1];
tune ← LOOPHOLE[VM.AddressForPageNumber[tuneSpace.page]];
If an explicit tune has been specified, then validate it, read it in, and delete its contents. Note: if the tune is currently in the cache, then it is locked against us.
IF tuneId >= 0
THEN {
IF tuneId >= self.hdr.nTunes THEN ERROR Error[BadTune, BadTuneErrorMsg];
IF searchTuneCache[self, tuneId] # NIL THEN ERROR Error[TuneLocked, "Tune is locked."];
self.window.base ← desPage[self, tuneId];
CopyIn[space: tuneSpace, window: self.window];
IF tune.magic # magicTuneHeader
THEN ERROR Error[JukeboxFormat, FormatErrorMsg];
tune.tuneId ← tuneId;
tune.space ← tuneSpace;
IF tune.state = inUse THEN killTune[self: self, tune: tune, updateMap: FALSE]
ELSE {
The tune wasn't previously in use. Mark the bit map to show it being used.
self.window.base ← FirstMapPageNumber + tuneId/(bitMapSize*16);
CopyIn[space: mapSpace, window: self.window];
IF map.magic # magicTuneBitMap THEN ERROR Error[JukeboxFormat, FormatErrorMsg];
word ← SHORT[tuneId MOD (bitMapSize*16)];
bit ← word MOD 16;
word ← word/16;
map.bits[word] ← Basics.BITOR[map.bits[word], Bits[bit]];
CopyOut[mapSpace, self.window];
};
}
ELSE {
Scan through the tune bit map, looking for a free tune. Remember that the bit map is just a HINT: the final authority is the state field in the tune header.
mapPage ← self.window.base ← self.currentMapPageNumber;
CopyIn[mapSpace, self.window];
IF map.magic # magicTuneBitMap THEN ERROR Error[JukeboxFormat, FormatErrorMsg];
WHILE
TRUE
DO
FOR i:
INTEGER
IN [0..bitMapSize)
DO
IF map.bits[i] # 177777B
THEN {
FOR j:
INTEGER
IN [0..16)
DO
IF Basics.BITAND[map.bits[i], Bits[j]] = 0 THEN {
We seem to have a tune header that is free. Rewrite the map page to mark the tune as not available, then read in the tune descriptor to make sure it really is available. If not, just keep on trying.
map.bits[i] ← Basics.BITOR[map.bits[i], Bits[j]];
self.window.base ← mapPage;
CopyOut[mapSpace, self.window];
tuneId ← (mapPage-FirstMapPageNumber)*16*bitMapSize +
i*16 + j;
self.window.base ← desPage[self, tuneId];
CopyIn[tuneSpace, self.window];
IF tune.magic # magicTuneHeader THEN ERROR Error[JukeboxFormat, FormatErrorMsg];
IF tune.state = available THEN GOTO gotTune;
};
ENDLOOP;
};
ENDLOOP;
mapPage ← mapPage+1;
IF mapPage > self.maxMapPageNumber THEN mapPage ← FirstMapPageNumber;
IF mapPage = self.currentMapPageNumber THEN ERROR Error[NoTunesAvailable, "All tune headers in use."];
self.window.base ← mapPage;
CopyIn[mapSpace, self.window];
IF map.magic # magicTuneBitMap THEN ERROR Error[JukeboxFormat, FormatErrorMsg];
REPEAT
gotTune => self.currentMapPageNumber ← mapPage;
ENDLOOP;
};
Now we REALLY have a tune. Initialize the header, write it out again, and link it into the main-memory list. Before we do that, check if the tuneHWM in the jukebox header should be updated: if so, do it first so that in the event of a crash although the HWM may be too large it will never be too small.
IF tuneId > self.hdr.tuneHWM THEN
{ self.hdr.tuneHWM ← tuneId;
self.window.base ← HeaderPageNumber;
CopyOut[self.hdrSpace, self.window]
};
tune.state ← inUse;
tune.runData ← NIL;
tune.createDate ← tune.appendDate ← tune.playDate ← BasicTime.Now[];
tune.size ← 0;
FOR i:
INTEGER
IN [0..nHdrChirps)
DO
tune.chirps[i] ← 0;
ENDLOOP;
tune.indirectChirp ← tune.deepChirp ← 0;
tune.tuneId ← tuneId;
tune.openCount ← 1;
tune.next ← self.openTuneHeaderList;
tune.space ← tuneSpace;
tune.writable ← TRUE;
tune.kill ← FALSE;
tune.deepCache ← tune.indirectCache ← NIL;
self.window.base ← desPage[self, tuneId];
CopyOut[tuneSpace, self.window];
self.openTuneHeaderList ← tune;
RETURN [tune];
}};
DeleteTune:
PUBLIC
ENTRY
PROC [self: Jukebox.Handle, tuneId: Jukebox
.TuneID] = {
ENABLE UNWIND => NULL;
tuneSpace: VM.Interval;
tune: Tune ← NIL;
{
ENABLE
UNWIND => {
IF tune # NIL THEN VM.Free[tuneSpace];
};
IF self.hdr = NIL THEN ERROR Error[NoJukebox, OpenErrorMsg];
IF (tuneId >= self.hdr.nTunes) OR (tuneId < 0) THEN ERROR Error[BadTune, BadTuneErrorMsg];
tuneSpace ← VM.Allocate[count: 1];
Find and validate the tune. Check on the open list before going to disk.
tune ← searchTuneCache[self, tuneId];
IF tune = NIL THEN {
The tune isn't in memory. Get it from disk.
tune ← LOOPHOLE[VM.AddressForPageNumber[tuneSpace.page]];
self.window.base ← desPage[self, tuneId];
CopyIn[tuneSpace, self.window];
tune.tuneId ← tuneId;
tune.openCount ← 0;
tune.next ← NIL;
tune.space ← tuneSpace;
tune.writable ← tune.kill ← FALSE;
};
We've got the tune. Make sure its create date matches and that its magic number hasn't gotten trashed.
IF tune.magic # magicTuneHeader THEN ERROR Error[JukeboxFormat, FormatErrorMsg];
IF tune.createDate # createDateTHEN ERROR Error[BadTune, BadTuneErrorMsg];
IF tune.state # inUse THEN ERROR Error[BadTune, BadTuneErrorMsg];
Mark the tune to be killed. If no-one is using the tune, then deallocate it.
tune.kill ← TRUE;
IF tune.openCount = 0 THEN killTune[self, tune];
VM.Free[tuneSpace];
}};
killTune:
INTERNAL
PROC [self: Jukebox.Handle, tune: Tune, updateMap:
BOOL ←
TRUE] = {
This procedure will release all the chirp space in a tune, mark the disk copy of the header as available, and update the bit map. (The bit map is only updated if updateMap is TRUE)
mapSpace: VM.Interval;
map: LONG POINTER TO TuneBitMap ← NIL;
deepSpace: VM.Interval;
deepList: LONG POINTER TO ChirpList ← NIL;
word, bit: INTEGER;
{ENABLE UNWIND => IF map # NIL THEN VM.Free[mapSpace];
Mark the header as available and write it out.
tune.state ← available;
tune.openCount ← 0;
tune.kill ← FALSE;
self.window.base ← desPage[self, tune.tuneId];
CopyOut[tune.space, self.window];
Release all of the blocks in the tune.
FOR i:
INTEGER
IN [0..nHdrChirps)
DO
IF tune.chirps[i] # 0 THEN FreeChirp[self, tune.chirps[i]];
ENDLOOP;
IF tune.indirectChirp # 0 THEN FreeIndirect[self, tune.indirectChirp];
IF tune.deepChirp # 0
THEN {
ENABLE
UNWIND =>
IF deepList # NIL THEN VM.Free[deepSpace];
deepSpace ← VM.Allocate[count: pagesPerChirp];
deepList ← LOOPHOLE[VM.AddressForPageNumber[deepSpace.page]];
self.window.base ← tune.deepChirp;
CopyIn[space: deepSpace, window: self.window];
IF deepList.magic # magicDeepList
THEN ERROR Error[JukeboxFormat, FormatErrorMsg];
FOR i:
INTEGER
IN [0..chirpListSize)
DO
IF deepList.chirps[i] # 0 THEN FreeIndirect[self, deepList.chirps[i]];
ENDLOOP;
FreeChirp[self, tune.deepChirp];
VM.Free[deepSpace];
};
IF NOT updateMap THEN RETURN;
Now update the tune bit map to show this tune as available.
mapSpace ← VM.Allocate[count: 1];
map ← LOOPHOLE[VM.AddressForPageNumber[mapSpace.page]];
self.window.base ← FirstMapPageNumber + tune.tuneId/(bitMapSize*16);
CopyIn[mapSpace, self.window];
word ← SHORT[tune.tuneId MOD (bitMapSize*16)];
bit ← word MOD 16;
word ← word/16;
map.bits[word] ← Basics.BITAND[map.bits[word], Basics.BITNOT[Bits[bit]]];
CopyOut[mapSpace, self.window];
VM.Free[mapSpace];
}};
OpenTune:
PUBLIC
ENTRY
PROC [self: Jukebox.Handle, tuneId: TuneID, write:
BOOL]
RETURNS [Tune] = {
ENABLE UNWIND => NULL;
tune: Tune ← NIL;
tuneSpace: VM.Interval;
{
ENABLE
UNWIND => {
IF tune # NIL THEN VM.Free[tuneSpace];
};
IF self.hdr = NIL THEN ERROR Error[NoJukebox, OpenErrorMsg];
Convert the user tune id into a page number that we can use internally, and make sure that it's valid.
IF (tuneId >= self.hdr.nTunes) OR (tuneId < 0) THEN ERROR Error[BadTune, BadTuneErrorMsg];
tuneSpace ← VM.Allocate[count: 1];
First, go through the list of open tunes to see if this one is already open. If so, just use the open tune. Note that a tune is permitted to be writable by at most one client at a time.
tune ← searchTuneCache[self, tuneId];
IF tune #
NIL
THEN {
IF tune.createDate # createDate THEN ERROR Error[BadTune, BadTuneErrorMsg];
IF tune.kill THEN ERROR Error[BadTune, BadTuneErrorMsg];
IF tune.writable OR write THEN ERROR Error[TuneLocked, "Tune is locked."];
tune.openCount ← tune.openCount + 1;
VM.Free[tuneSpace];
tune.runData ← NIL;
RETURN [tune];
};
The tune isn't in memory. Check its number, and then get it from disk.
tune ← LOOPHOLE[VM.AddressForPageNumber[tuneSpace.page]];
self.window.base ← desPage[self, tuneId];
CopyIn[tuneSpace, self.window];
IF tune.magic # magicTuneHeader THEN ERROR Error[JukeboxFormat, FormatErrorMsg];
IF tune.createDate # createDate THEN ERROR Error[BadTune. BadTuneErrorMsg];
IF tune.state # inUse THEN ERROR Error[BadTune, BadTuneErrorMsg];
tune.tuneId ← tuneId;
tune.openCount ← 1;
tune.next ← self.openTuneHeaderList;
tune.space ← tuneSpace;
tune.writable ← write;
tune.kill ← FALSE;
tune.deepCache ← tune.indirectCache ← NIL;
self.openTuneHeaderList ← tune;
tune.runData ← NIL;
RETURN [tune];
CloseTune:
PUBLIC
ENTRY
PROC [self: Jukebox.Handle, tune: Tune] = {
ENABLE UNWIND => NULL;
InternalCloseTune[self, tune, TRUE];
};
ArchiveCloseTune:
PUBLIC
ENTRY
PROC [self: Jukebox.Handle, tune: Tune] = {
only for use of the TuneArchive RestoreTune routine: it exits without modifying the datestamps in the tune
ENABLE UNWIND => NULL;
InternalCloseTune[self, tune, FALSE];
};
InternalCloseTune:
INTERNAL
PROC [self: Jukebox.Handle, tune: Tune, updateDates:
BOOLEAN] = {
tuneptr: LONG POINTER TO Tune;
tuneSpace: VM.Interval;
First, make sure that the tune and jukebox are valid.
IF self.hdr = NIL THEN ERROR Error[NoJukebox, OpenErrorMsg];
IF tune.magic # magicTuneHeader THEN ERROR Error[BadTune, BadTuneErrorMsg];
Decrement the open count. If no-one else is using the tune then flush it from the open list and deallocate it if necessary.
tune.openCount ← tune.openCount - 1;
IF tune.openCount = 0
THEN {
IF updateDates THEN {IF tune.writable THEN tune.appendDate ← BasicTime.Now[]
ELSE tune.playDate ← BasicTime.Now[]};
IF tune.deepCache #
NIL
THEN {
IF tune.deepCache.dirty
THEN {
self.window.base ← tune.deepChirp;
CopyOut[space: tune.deepSpace, window: self.window];
};
VM.Free[tune.deepSpace];
tune.deepCache ← NIL;
};
IF tune.indirectCache #
NIL
THEN {
IF tune.indirectCache.dirty
THEN {
self.window.base ← tune.indirectCache.page;
CopyOut[space: tune.indirectSpace, window: self.window];
};
VM.Free[tune.indirectSpace];
tune.indirectCache ← NIL;
};
tuneSpace ← tune.space;
tuneptr ← @self.openTuneHeaderList;
WHILE tuneptr^ # tune
DO
IF tuneptr^ = NIL THEN ERROR Error[Bug, "Unexpected tune list end."];
tuneptr ← @(tuneptr^.next);
ENDLOOP;
tuneptr^ ← tune.next;
Kill the tune as the very last thing, if it is necessary at all.
IF tune.kill THEN killTune[self, tune]
ELSE {
self.window.base ← desPage[self, tune.tuneId];
CopyOut[tuneSpace, self.window];
};
tune.magic ← 0;
IF tune.runData#NIL THEN { VM.Free[tune.runSpace]; tune.runData ← NIL; };
VM.Free[tuneSpace];
};
};
AllocChirp:
ENTRY
PROC [self: Jukebox.Handle]
RETURNS [Jukebox.PageNumber] = {
Allocates a chirp from the free list.
ENABLE UNWIND => NULL;
chirp: Jukebox.PageNumber;
WHILE self.free.nChirps = 0
DO
IF self.free.next = 0 THEN ERROR Error[NoFreeChirps, "No free chirps left."];
self.window.base ← self.currentFreeChirpNumber ← self.free.next;
CopyIn[space: self.freeSpace, window: self.window];
IF self.free.magic # magicFreeList THEN ERROR Error[JukeboxFormat, FormatErrorMsg];
self.hdr.freeListHead ← self.free.next;
self.hdr.nFreeChirps ← self.hdr.nFreeChirps - self.free.nChirps;
self.window.base ← HeaderPageNumber;
CopyOut[space: self.hdrSpace, window: self.window];
ENDLOOP;
self.free.nChirps ← self.free.nChirps-1;
chirp ← self.free.chirps[self.free.nChirps];
RETURN [chirp];
};
AllocateIndirect:
PROC [self: Jukebox.Handle, magic:
INTEGER]
RETURNS [Jukebox.PageNumber] = {
Allocates a chirp from the free list, initializes it as an empty indirect chirp, and saves it on disk again.
page: Jukebox.PageNumber;
space: VM.Interval;
chirpList: LONG POINTER TO ChirpList;
{ENABLE UNWIND => IF chirpList # NIL THEN VM.Free[space];
page ← AllocChirp[self];
space ← VM.Allocate[count: pagesPerChirp];
chirpList ← LOOPHOLE[VM.AddressForPageNumber[space.page]];
chirpList.magic ← magic;
FOR j: INTEGER IN [0..chirpListSize) DO chirpList.chirps[j] ← 0 ENDLOOP;
chirpList.dirty ← FALSE;
self.window.base ← page;
CopyOut[space: space, window: self.window];
VM.Free[space];
RETURN [page];
}};
FreeChirp:
PROC [self: Jukebox.Handle, chirp: Jukebox.PageNumber] = {
Returns a chirp to the free list. Note: the caller must make
sure that the monitor lock is held.
ENABLE UNWIND => NULL;
Write out the current free list block if it is full.
IF self.free.nChirps = freeListSize
THEN {
self.window.base ← self.currentFreeChirpNumber;
CopyOut[space: self.freeSpace, window: self.window];
self.free.next ← self.currentFreeChirpNumber;
self.free.nChirps ← 0;
Update the jukebox header.
self.hdr.freeListHead ← self.free.next;
self.hdr.nFreeChirps ← self.hdr.nFreeChirps + freeListSize;
self.window.base ← HeaderPageNumber;
CopyOut[space: self.hdrSpace, window: self.window];
};
IF self.free.nChirps = 0 THEN self.currentFreeChirpNumber ← chirp;
self.free.chirps[self.free.nChirps] ← chirp;
self.free.nChirps ← self.free.nChirps+1;
};
FreeIndirect:
PROC [self: Jukebox.Handle, page: Jukebox.PageNumber] = {
Returns all of the chirps in an indirect chirp to the free list.
indirectSpace: VM.Interval;
chirpList: LONG POINTER TO ChirpList ← NIL;
{ENABLE UNWIND => IF chirpList # NIL THEN VM.Free[indirectSpace];
indirectSpace ← VM.Allocate[count: pagesPerChirp];
chirpList ← LOOPHOLE[VM.AddressForPageNumber[indirectSpace.page]];
self.window.base ← page;
CopyIn[space: indirectSpace, window: self.window];
IF chirpList.magic # magicChirpList THEN ERROR Error[JukeboxFormat, FormatErrorMsg];
FOR i:
INTEGER
IN [0..chirpListSize)
DO
IF chirpList.chirps[i] # 0 THEN FreeChirp[self, chirpList.chirps[i]];
ENDLOOP;
FreeChirp[self, page];
VM.Free[indirectSpace];
}};
FindChirp:
PUBLIC
PROC [self: Jukebox.Handle, tune: Tune, chirp:
INT, signalMissingChirp:
BOOL ←
FALSE, signalEOF:
BOOL ←
FALSE]
RETURNS [Jukebox.WindowOrigin] = {
Sets a window to correspond to the location of a chirp in a tune. If the chirp falls past the end of the tune and signalEOF is TRUE, then EOF is signalled. If the chirp hasn't been allocated in the file, then a new chirp is allocated (if signalMissingChirp is FALSE) or a MissingChirp is raised (if signalMissingChirp is TRUE).
index, indirect: INTEGER;
chirpWindow: Jukebox.WindowOrigin;
IF self.hdr = NIL THEN ERROR Error[NoJukebox, OpenErrorMsg];
IF tune.magic # magicTuneHeader THEN ERROR Error[BadTunePointer, BadTunePointerMsg];
Check for EOF and update the tune's size if necessary.
IF (tune.size <= chirp)
THEN {
IF signalEOF THEN ERROR EOF[];
tune.size ← chirp+1;
};
chirpWindow.file ← self.window.file;
IF chirp < nHdrChirps
THEN {
index ← SHORT[chirp];
IF tune.chirps[index] = 0
THEN {
IF signalMissingChirp THEN ERROR MissingChirp[];
tune.chirps[index] ← AllocChirp[self];
};
chirpWindow.base ← tune.chirps[index];
RETURN [chirpWindow];
};
chirp ← chirp - nHdrChirps;
index ← SHORT[chirp MOD chirpListSize];
chirp ← chirp/chirpListSize;
IF chirp > chirpListSize THEN ERROR Error[TuneTooLarge, "Can't have tune that large."];
indirect ← SHORT[chirp];
IF (indirect # tune.indirect) OR (tune.indirectCache = NIL) THEN {
Alas, there isn't a cached indirect block to make everything simple. Read in an indirect block (this may also require a deep block to be read as well).
IF tune.indirectCache =
NIL
THEN {
tune.indirectSpace ← VM.Allocate[count: pagesPerChirp];
tune.indirectCache ← LOOPHOLE[VM.AddressForPageNumber[tune.indirectSpace.page]];
tune.indirectCache.dirty ← FALSE;
tune.indirect ← -1;
};
IF tune.indirectCache.dirty
THEN {
tune.indirectCache.dirty ← FALSE;
self.window.base ← tune.indirectCache.page;
CopyOut[space: tune.indirectSpace, window: self.window];
};
IF indirect = 0
THEN {
IF tune.indirectChirp = 0
THEN {
IF signalMissingChirp THEN ERROR MissingChirp[];
tune.indirectChirp ← AllocateIndirect[self, magicChirpList];
};
self.window.base ← tune.indirectChirp;
}
ELSE {
IF (tune.deepCache = NIL) THEN {
Things couldn't be worse: we'll first have to read the deep indirect block, then an indirect block.
IF tune.deepChirp = 0
THEN {
IF signalMissingChirp THEN ERROR MissingChirp[];
tune.deepChirp ← AllocateIndirect[self, magicDeepList];
};
self.window.base ← tune.deepChirp;
tune.deepSpace ← VM.Allocate[count: pagesPerChirp];
tune.deepCache ← LOOPHOLE[VM.AddressForPageNumber[tune.deepSpace.page]];
CopyIn[space: tune.deepSpace, window: self.window];
};
IF tune.deepCache.magic # magicDeepList THEN ERROR Error[JukeboxFormat, FormatErrorMsg];
IF tune.deepCache.chirps[indirect-1] = 0
THEN {
IF signalMissingChirp THEN ERROR MissingChirp[];
tune.deepCache.chirps[indirect-1] ← AllocateIndirect[self, magicChirpList];
tune.deepCache.dirty ← TRUE;
};
self.window.base ← tune.deepCache.chirps[indirect-1];
};
CopyIn[space: tune.indirectSpace, window: self.window];
tune.indirectCache.page ← self.window.base;
tune.indirect ← indirect;
};
IF tune.indirectCache.magic # magicChirpList THEN ERROR Error[JukeboxFormat, FormatErrorMsg];
IF tune.indirectCache.chirps[index] = 0
THEN {
IF signalMissingChirp THEN ERROR MissingChirp[];
tune.indirectCache.chirps[index] ← AllocChirp[self];
tune.indirectCache.dirty ← TRUE;
};
chirpWindow.base ← tune.indirectCache.chirps[index];
RETURN [chirpWindow];
};
ought the following be an entry procedure? If so then write a similar FindHeaderSpace and make TuneAccess.NextTuneNumber call it!
FindClientSpace:
PUBLIC
PROC [self: Jukebox.Handle, tuneId:
INT]
RETURNS [clientWindow: Jukebox.WindowOrigin] = {
IF self.hdr = NIL THEN ERROR Error[NoJukebox, OpenErrorMsg];
IF (tuneId >= self.hdr.nTunes) OR (tuneId < 0) THEN ERROR Error[BadTune, BadTuneErrorMsg];
clientWindow.file ← self.window.file;
clientWindow.base ← desPage[self, tuneId] + 1;
};
desPage:
PROC [self: Jukebox.Handle, tuneId:
INT]
RETURNS [
INT] = {
This local procedure just computes the descriptor page for a tune based on its id.
RETURN[self.firstDesPageNumber + 2*tuneId];
};
searchTuneCache:
INTERNAL
PROC [self: Jukebox.Handle, tuneId:
INT]
RETURNS [Tune] = {
This local procedure checks to see if a given tune is in our memory cache. If so, then it returns a pointer to the cached copy. If not, then it returns NIL.
tune: Tune;
tune ← self.openTuneHeaderList;
WHILE tune #
NIL
DO
IF tune.tuneId = tuneId THEN RETURN [tune];
tune ← tune.next;
ENDLOOP;
RETURN [NIL];
};
Scavenge:
PUBLIC
ENTRY
PROC [self: Jukebox.Handle, stream:
IO.
STREAM, makeFixes:
BOOL]
RETURNS [nFree:
INT, recovered:
INT] = {
This routine scans the jukebox to make sure that each chirp is present in at most one place. When a chirp is found both on the free list and in a tune, or if a chirp is found in two separate tunes, a message is output to stream, and one of the instances of the chirp is deleted (if makeFixes is TRUE). If some kind of format error is discovered, a message is output and the relevant tune is deleted (if makeFixes is TRUE). If makeFixes is FALSE then no corrections are made that would cause information to be lost from a tune. In any event, when the scan is finished, the free list is regenerated and reordered. This will recover chirps that got lost. The return values give information about the state of the free list.
The routine also sets the tuneHWM in the jukebox header to the highest currently allocated tune
ENABLE UNWIND => NULL;
varArray: TYPE = ARRAY [0..0) OF INT;
chirpMap: LONG POINTER TO varArray ← NIL;
chirpSpace: VM.Interval;
tuneId: INT;
tuneSpace, indirectSpace, deepSpace: VM.Interval;
tune: Tune ← NIL;
indirect: LONG POINTER TO ChirpList ← NIL;
deep: LONG POINTER TO ChirpList ← NIL;
index: INT;
oldNFree, newNFree: INT;
badTunes: BOOL ← FALSE;
unused: INT ← -1;
tuneHWM: INT ← -1;
gcIndex: PROC [chirp: INT] RETURNS [INT] = {
This local procedure merely computes a chirp index from a chirp number. If the chirp is out of range for the jukebox, generate an error.
result: INT;
chirp ← chirp-self.hdr.firstChirp;
result ← chirp/pagesPerChirp;
IF (result >= self.hdr.nChirps) OR (result < 0) THEN ERROR Error[BadChirpPointer, "Bad chirp pointer."];
IF (result*pagesPerChirp) # chirp THEN ERROR Error[BadChirpPointer, "Bad chirp pointer."];
RETURN[result];
};
gcIndirect: PROC [tuneId: INT, chirp: INT, magic: INTEGER] = {
This local procedure checks all the chirps referenced in an indirect block.
index: INT;
IF chirp = 0 THEN RETURN;
self.window.base ← chirp;
CopyIn[space: indirectSpace, window: self.window];
IF indirect.magic # magic THEN ERROR Error[JukeboxFormat, FormatErrorMsg];
FOR i:
INTEGER
IN [0..chirpListSize)
DO
IF indirect.chirps[i] # 0
THEN {
index ← gcIndex[indirect.chirps[i]];
IF chirpMap[index] = unused THEN chirpMap[index] ← tuneId
ELSE {
IO.PutRope[stream, "Indirect/deep pointer is duplicate:\n"];
IO.PutF[stream, " Chirp: %d, 1st tune: %d, 2nd tune: %d.\n",
IO.int[indirect.chirps[i]], IO.int[chirpMap[index]],
IO.int[tuneId]];
IF makeFixes
THEN {
IO.PutRope[stream, " Chirp deleted from 2nd tune.\n"];
indirect.chirps[i] ← 0;
CopyOut[space: indirectSpace, window: self.window];
};
};
};
ENDLOOP;
};
{
ENABLE
UNWIND => {
IF indirect # NIL THEN VM.Free[indirectSpace];
IF deep # NIL THEN VM.Free[deepSpace];
IF tune # NIL THEN VM.Free[tuneSpace];
IF chirpMap # NIL THEN VM.Free[chirpSpace];
};
Main drag of scavenger:
IF self.hdr = NIL THEN ERROR Error[NoJukebox, OpenErrorMsg];
IF self.openTuneHeaderList # NIL THEN ERROR Error[TunesOpen, "Can't scavenge: tune(s) open"];
For starters, allocate space for the chirp map, based on the size of the jukebox.
chirpSpace ← VM.Allocate[count: ((self.hdr.nChirps * SIZE[INT]) / VM.wordsPerPage) + 1];
chirpMap ← LOOPHOLE[VM.AddressForPageNumber[chirpSpace.page]];
FOR i: INT IN [0..self.hdr.nChirps) DO chirpMap[i] ← unused ENDLOOP;
Scan all of the tunes in the jukebox. For each tune, scan all the descriptor information, and label each chirp with the tune id.
tuneSpace ← VM.Allocate[count: 1];
tune ← LOOPHOLE[VM.AddressForPageNumber[tuneSpace.page]];
indirectSpace ← VM.Allocate[count: pagesPerChirp];
indirect ← LOOPHOLE[VM.AddressForPageNumber[indirectSpace.page]];
deepSpace ← VM.Allocate[count: pagesPerChirp];
deep ← LOOPHOLE[VM.AddressForPageNumber[deepSpace.page]];
FOR tuneId IN [0..self.hdr.nTunes) DO
If some error occurs underneath us here, output an error message, and discard the tune if that is allowed. If we can't discard the tune, then leave a note not to reconstruct the free list, since there may be valid chirps unseen in the tune.
ENABLE {
Error => {
IO.PutRope[stream, rope];
IO.PutF[stream, " (in tune %d)\n", IO.int[tuneId]];
IF makeFixes
THEN {
IO.PutRope[stream, " Tune is being deleted"];
IO.PutRope[stream, " (some free chirps may not be recovered).\n"];
tune.magic ← magicTuneHeader;
tune.state ← available;
self.window.base ← desPage[self, tuneId];
CopyOut[space: tuneSpace, window: self.window];
}
ELSE badTunes ← TRUE;
CONTINUE;
};
ANY => {
IO.PutF[stream, "Unknown error occurred in scavenging tune %d.\n", IO.int[tuneId]];
IF makeFixes
THEN {
IO.PutRope[stream, " Tune is being deleted"];
IO.PutRope[stream, " (some free chirps may not be recovered).\n"];
tune.magic ← magicTuneHeader;
tune.state ← available;
self.window.base ← desPage[self, tuneId];
CopyOut[space: tuneSpace, window: self.window];
}
ELSE badTunes ← TRUE;
CONTINUE;
};
};
self.window.base ← desPage[self, tuneId];
CopyIn[space: tuneSpace, window: self.window];
IF tune.magic # magicTuneHeader THEN ERROR Error[JukeboxFormat, FormatErrorMsg];
IF tune.state = available THEN LOOP ELSE tuneHWM ← tuneId;
Scan top-level chirp pointers.
FOR i:
INTEGER
IN [0..nHdrChirps)
DO
IF tune.chirps[i] # 0
THEN {
index ← gcIndex[tune.chirps[i]];
IF chirpMap[index] = unused THEN chirpMap[index] ← tuneId
ELSE {
IO.PutRope[stream, "Top level pointer is duplicate:\n"];
IO.PutF[stream, " Chirp: %d, 1st tune: %d, 2nd tune: %d.\n",
IO.int[tune.chirps[i]], IO.int[chirpMap[index]],
IO.int[tuneId]];
IF makeFixes
THEN {
IO.PutRope[stream, " Chirp deleted from 2nd tune.\n"];
tune.chirps[i] ← 0;
CopyOut[space: tuneSpace, window: self.window];
};
};
};
ENDLOOP;
Scan first-level indirect pointers.
IF tune.indirectChirp # 0
THEN {
index ← gcIndex[tune.indirectChirp];
IF chirpMap[index] = unused THEN chirpMap[index] ← tuneId
ELSE {
IO.PutRope[stream, "Tune.indirectChirp is duplicate:\n"];
IO.PutF[stream, " Chirp: %d, 1st tune: %d, 2nd tune: %d.\n",
IO.int[tune.indirectChirp], IO.int[chirpMap[index]],
IO.int[tuneId]];
IF makeFixes
THEN {
IO.PutRope[stream, " Chirp deleted from 2nd tune.\n"];
tune.indirectChirp ← 0;
self.window.base ← desPage[self, tuneId];
CopyOut[space: tuneSpace, window: self.window];
};
};
gcIndirect[tuneId, tune.indirectChirp, magicChirpList];
};
Scan deep pointers.
IF tune.deepChirp # 0
THEN {
index ← gcIndex[tune.deepChirp];
IF chirpMap[index] = unused THEN chirpMap[index] ← tuneId
ELSE {
IO.PutRope[stream, "Tune.deepChirp is duplicate:\n"];
IO.PutF[stream, " Chirp: %d, 1st tune: %d, 2nd tune: %d.\n",
IO.int[tune.deepChirp], IO.int[chirpMap[index]],
IO.int[tuneId]];
IF makeFixes
THEN {
IO.PutRope[stream, " Chirp deleted from 2nd tune.\n"];
tune.deepChirp ← 0;
self.window.base ← desPage[self, tuneId];
CopyOut[space: tuneSpace, window: self.window];
};
};
gcIndirect[tuneId, tune.deepChirp, magicDeepList];
self.window.base ← tune.deepChirp;
CopyIn[space: deepSpace, window: self.window];
FOR i:
INTEGER
IN [0..chirpListSize)
DO gcIndirect[tuneId, deep.chirps[i], magicChirpList] ENDLOOP;
};
ENDLOOP;
If there are bad tunes right away, quit right now without looking at the free list.
IF badTunes
THEN {
IO.PutRope[stream, "Bad tunes weren't deleted, so can't scavenge free list.\n"];
RETURN [nFree: self.hdr.nFreeChirps+self.free.nChirps, recovered: 0];
};
Now, scan the free list to make sure that all the blocks on it are currently free.
oldNFree ← 0;
newNFree ← 0;
DO
FOR i:
INTEGER
IN [0..self.free.nChirps)
DO
oldNFree ← oldNFree + 1;
index ← gcIndex[self.free.chirps[i]];
IF chirpMap[index] # unused
THEN {
IO.PutRope[stream, "Free list chirp is duplicate:\n"];
IO.PutF[stream, " Chirp: %d, Tune: %d.\n",
IO.int[self.free.chirps[i]],
IO.int[chirpMap[index]]];
};
ENDLOOP;
IF self.free.next = 0 THEN EXIT;
self.currentFreeChirpNumber ← self.window.base ← self.free.next;
CopyIn[space: self.freeSpace, window: self.window];
ENDLOOP;
Now we have the whole chirp map set up. Just rewrite the free list in order.
self.free.next ← 0;
self.free.nChirps ← 0;
self.currentFreeChirpNumber ← 0;
self.hdr.nFreeChirps ← 0;
FOR i:
INT
IN [0..self.hdr.nChirps)
DO
IF chirpMap[i] = unused
THEN {
newNFree ← newNFree + 1;
FreeChirp[self, self.hdr.firstChirp + i*pagesPerChirp];
};
ENDLOOP;
If the HWM is different from that computed here then write out the header with the new value
IF tuneHWM # self.hdr.tuneHWM THEN
{ self.hdr.tuneHWM ← tuneHWM;
self.window.base ← HeaderPageNumber;
CopyOut[self.hdrSpace, self.window]
Release storage and quit.
VM.Free[tuneSpace];
VM.Free[indirectSpace];
VM.Free[deepSpace];
chirpMap ← NIL;
VM.Free[chirpSpace];
RETURN[nFree: newNFree, recovered: newNFree - oldNFree];
}};
CopyIn:
PROC [space:
VM.Interval, window: Jukebox.WindowOrigin] = {
FS.Read[file: window.file, from: window.base, nPages: space.count, to: VM.AddressForPageNumber[space.page]];
};
CopyOut:
PROC [space:
VM.Interval, window: Jukebox.WindowOrigin] = {
FS.Write[file: window.file, to: window.base, nPages: space.count, from: VM.AddressForPageNumber[space.page]];
};
END.
Last Edited by: Swinehart, March 1, 1983 2:51 pm
Last Edited by: Stewart, April 19, 1983 3:03 pm, volume stuff
Last Edited by: Stewart, April 20, 1983 12:02 pm, remove use of Heap.systemZone
Last Edited by: Stewart, May 22, 1983 6:32 pm, add FindClientSpace
December 27, 1983 12:42 pm, Stewart, Cedar 5
Doug Terry, July 28, 1986 4:35:44 pm PDT
changes to: CreateTune, DeleteTune, OpenTune