VolumeGroupImpl.mesa
Last edited by:
MBrown on January 31, 1984 3:41:56 pm PST
Taft on April 10, 1983 4:35 pm
Kolling on February 22, 1983 1:49 pm
Loose ends:
1. Someday this should be coordinated with whatever AccessControl is doing to maintain its own VolumeGroup-related data structures.
DIRECTORY
AlpineEnvironment,
File,
FileLock,
FilePrivate,
TransactionMap,
VolumeGroup;
VolumeGroupImpl: MONITOR
The module monitor protects the VolumeGroupImpl internal data structures and serves no other purpose visible to clients.
IMPORTS File, FileLock, FilePrivate
EXPORTS VolumeGroup =
BEGIN OPEN VolumeGroup;
VolumeGroup.
Open: PUBLIC PROCEDURE [volumeGroupID: VolumeGroupID, volumes: LIST OF VolumeID, trans: TransactionMap.Handle] =
BEGIN
OpenEntry: ENTRY PROCEDURE =
BEGIN
IF Find[volumeGroupID]#NIL THEN RETURN WITH ERROR Failed[duplicateVolGroup];
FOR vol: LIST OF VolumeID ← volumes, vol.rest WHILE vol#NIL DO
IF FindVolume[vol.first]#nullVolumeGroupID THEN
RETURN WITH ERROR Failed[duplicateVol];
ENDLOOP;
volumeGroups ← FilePrivate.stdPZone.CONS[[volumeGroupID: volumeGroupID, volumes: volumes], volumeGroups];
END; -- OpenEntry
IF volumeGroupID=nullVolumeGroupID THEN ERROR Failed[nullVolGroupID];
IF volumes=NIL THEN ERROR Failed[empty];
IF trans#NIL THEN
FileLock.AcquireVolumeGroupLock[volumeGroupID: volumeGroupID, trans: trans, requested: [write, fail], minimum: write];
OpenEntry[];
END;
Close: PUBLIC PROCEDURE [volumeGroupID: VolumeGroupID, trans: TransactionMap.Handle] =
BEGIN
CloseEntry: ENTRY PROCEDURE =
BEGIN
IF ~Delete[volumeGroupID] THEN
RETURN WITH ERROR Failed[unknownVolumeGroupID];
END; -- CloseEntry
FileLock.AcquireVolumeGroupLock[volumeGroupID: volumeGroupID, trans: trans, requested: [write, fail], minimum: write];
CloseEntry[];
END;
Identify: PUBLIC PROCEDURE [volID: VolOrVolGroupID, trans: TransactionMap.Handle ← TransactionMap.nullHandle, lock: LockOption ← [none, wait]] RETURNS [volumeID: VolumeID, volumeGroupID: VolumeGroupID] =
BEGIN
IdentifyEntry: ENTRY PROCEDURE =
BEGIN
volumes: LIST OF VolumeID = Find[[volID]];
IF volumes#NIL THEN
{volumeID ← nullVolumeID; volumeGroupID ← [volID]; RETURN};
volumeGroupID ← FindVolume[[volID]];
IF volumeGroupID#nullVolumeGroupID THEN volumeID ← [volID]
ELSE RETURN WITH ERROR Failed[unknownVolOrVolGroupID];
END; -- IdentifyEntry
VerifyStillThere: ENTRY PROCEDURE =
BEGIN
IF Find[volumeGroupID]=NIL THEN
RETURN WITH ERROR Failed[unknownVolOrVolGroupID];
END; -- VerifyStillThere
IdentifyEntry[];
IF lock.mode#none THEN
BEGIN
FileLock.AcquireVolumeGroupLock[volumeGroupID: volumeGroupID, trans: trans, requested: lock, minimum: read];
VerifyStillThere[];
END;
END;
GetVolumes: PUBLIC PROCEDURE [volumeGroupID: VolumeGroupID, trans: TransactionMap.Handle ← TransactionMap.nullHandle, lock: LockOption ← [none, wait]] RETURNS [volumes: LIST OF VolumeID] =
BEGIN
GetVolumesEntry: ENTRY PROCEDURE = {volumes ← Find[volumeGroupID]};
IF lock.mode#none THEN
FileLock.AcquireVolumeGroupLock[volumeGroupID: volumeGroupID, trans: trans, requested: lock, minimum: read];
GetVolumesEntry[];
IF volumes=NIL THEN ERROR Failed[unknownVolumeGroupID];
END;
GetNext: PUBLIC PROCEDURE [volumeGroupID: VolumeGroupID, trans: TransactionMap.Handle ← TransactionMap.nullHandle, lock: LockOption ← [none, wait]] RETURNS [nextVolumeGroup: VolumeGroupID] =
BEGIN
GetNextEntry: ENTRY PROCEDURE RETURNS [VolumeGroupID] =
BEGIN
IF volumeGroupID=nullVolumeGroupID AND volumeGroups#NIL THEN
RETURN [volumeGroups.first.volumeGroupID]
ELSE
FOR group: LIST OF VolumeGroupRecord ← volumeGroups, group.rest WHILE group#NIL DO
IF volumeGroupID=group.first.volumeGroupID AND group.rest#NIL THEN
RETURN [group.rest.first.volumeGroupID];
ENDLOOP;
RETURN [nullVolumeGroupID];
END; -- GetNextEntry
StillThere: ENTRY PROCEDURE RETURNS [BOOLEAN] =
{RETURN[Find[volumeGroupID]#NIL]};
DO
nextVolumeGroup ← GetNextEntry[];
IF lock.mode=none OR nextVolumeGroup=nullVolumeGroupID THEN EXIT;
FileLock.AcquireVolumeGroupLock[volumeGroupID: nextVolumeGroup, trans: trans, requested: lock, minimum: read];
IF StillThere[] THEN EXIT;
ENDLOOP;
END;
SelectVolumeForCreate: PUBLIC ENTRY PROCEDURE [volumeGroupID: VolumeGroupID, count: PageCount] RETURNS [volumeID: VolumeID] =
This version just selects the emptiest unit. A cleverer algorithm might be desirable, perhaps with some caching of recent results as well.
BEGIN
volumes: LIST OF VolumeID = Find[volumeGroupID];
maxFreePages: PageCount ← 0;
IF volumes=NIL THEN RETURN WITH ERROR Failed[unknownVolumeGroupID];
FOR vol: LIST OF VolumeID ← volumes, vol.rest WHILE vol#NIL DO
freePageCount: PageCount = File.LogicalInfo[File.FindVolumeFromID[vol.first]].free;
IF freePageCount>maxFreePages THEN
{maxFreePages ← freePageCount; volumeID ← vol.first};
ENDLOOP;
IF count>maxFreePages THEN RETURN WITH ERROR Failed[insufficientSpace];
END;
Lock: PUBLIC PROCEDURE [volumeGroupID: VolumeGroupID, trans: TransactionMap.Handle, lock: LockOption ← [read, wait]] =
BEGIN
VerifyStillThere: ENTRY PROCEDURE =
BEGIN
IF Find[volumeGroupID]=NIL THEN
RETURN WITH ERROR Failed[unknownVolumeGroupID];
END;
FileLock.AcquireVolumeGroupLock[volumeGroupID: volumeGroupID, trans: trans, requested: lock, minimum: read];
VerifyStillThere[];
END;
Failed: PUBLIC ERROR [why: Failure] = CODE;
Internal stuff
VolumeGroupRecord: TYPE = RECORD [
volumeGroupID: VolumeGroupID,
volumes: LIST OF VolumeID];
volumeGroups: LIST OF VolumeGroupRecord ← NIL;
Find: INTERNAL PROCEDURE [volumeGroupID: VolumeGroupID] RETURNS [volumes: LIST OF VolumeID] =
Returns NIL if not found, otherwise the list of volumes belonging to the group.
BEGIN
FOR group: LIST OF VolumeGroupRecord ← volumeGroups, group.rest WHILE group#NIL DO
IF volumeGroupID=group.first.volumeGroupID THEN RETURN [group.first.volumes];
ENDLOOP;
RETURN [NIL];
END;
FindVolume: INTERNAL PROCEDURE [volumeID: VolumeID] RETURNS [volumeGroupID: VolumeGroupID] =
Returns nullVolumeGroupID if not found, otherwise the VolumeGroupID of the containing volume group.
BEGIN
FOR group: LIST OF VolumeGroupRecord ← volumeGroups, group.rest WHILE group#NIL DO
FOR vol: LIST OF VolumeID ← group.first.volumes, vol.rest WHILE vol#NIL DO
IF volumeID=vol.first THEN RETURN [group.first.volumeGroupID];
ENDLOOP;
ENDLOOP;
RETURN [nullVolumeGroupID];
END;
Delete: INTERNAL PROCEDURE [volumeGroupID: VolumeGroupID] RETURNS [found: BOOLEAN] =
BEGIN
IF volumeGroups#NIL AND volumeGroupID=volumeGroups.first.volumeGroupID
THEN {volumeGroups ← volumeGroups.rest; RETURN [TRUE]}
ELSE FOR group: LIST OF VolumeGroupRecord ← volumeGroups, group.rest WHILE group#NIL DO
IF group.rest#NIL AND volumeGroupID=volumeGroups.rest.first.volumeGroupID THEN
{group.rest ← group.rest.rest; RETURN [TRUE]};
ENDLOOP;
RETURN [FALSE];
END;
END.