-- File: DynamicZoneCold.mesa
-- Last edited by Levin:   6-Oct-80  8:33:18

DIRECTORY
  AltoDefs USING [PageSize],
  DynamicZone USING [PageCount],
  DynamicZonePrivate USING [
    AllocateNode, AllocProcs, DeallocateNode, GetPages, MDSZoneHandle, MDSZoneObject,
    SegTypeIndex],
  FSPDefs USING [DestroyZone, MakeNewZone, PruneZone, ZonePointer],
  SegmentDefs USING [DataSegmentType, HeapDS],
  Storage USING [FreePages],
  StringDefs USING [AppendString],
  SystemDefs USING [HeapZone];

DynamicZoneCold: MONITOR
  IMPORTS DynamicZonePrivate, FSPDefs, Storage, StringDefs, SystemDefs
  EXPORTS DynamicZone =

  BEGIN OPEN DynamicZone, DynamicZonePrivate, FSPDefs, SegmentDefs;


  -- Global Variables --

  sysZone: MDSZone;

  allocProcs: AllocProcs;

  availableTypes: PACKED ARRAY SegTypeIndex OF BOOLEAN;

  rover: SegTypeIndex;


  -- Miscellaneous Declarations --

  ZoneStillInUse: ERROR = CODE;


  -- Externally Visible Procedures --

  InitializeZones: PUBLIC ENTRY PROCEDURE =
    -- must be called before any other procedures in this interface.
    BEGIN
    availableTypes ← ALL[TRUE];
    rover ← LAST[SegTypeIndex];
    allocProcs ← [AllocateNode, DeallocateNode];
    sysZone ← InitZone[SystemDefs.HeapZone[], HeapDS, NIL];
    END;

  FinalizeZones: PUBLIC ENTRY PROCEDURE =
    -- caled to clean up zone allocator.  Note:  it is the client's responsibility
    -- to destroy any zones previously created with CreateZone.
    BEGIN
    tempZone: MDSZone = sysZone;
    FOR segIndex: SegTypeIndex IN SegTypeIndex DO
      IF ~availableTypes[segIndex] THEN RETURN WITH ERROR ZoneStillInUse;
      ENDLOOP;
    tempZone.FREE[@LOOPHOLE[sysZone, MDSZoneHandle]];
    END;

  CreateZone: PUBLIC PROCEDURE [initialPages: PageCount ← 1, id: STRING ← NIL]
    RETURNS [MDSZone] =
    -- creates a new zone whose allocation behavior is determined by 'initialPages'.
    -- 'id' is for assistance in debugging.
    BEGIN
    segType: DataSegmentType = AllocateSegmentType[];
    fspZone: ZonePointer ← MakeNewZone[
      base: GetPages[initialPages, segType], length: initialPages*AltoDefs.PageSize,
      deallocate: Storage.FreePages];
    RETURN[InitZone[fspZone, segType + 1, id]];
    END;

  TooManyZones: PUBLIC ERROR = CODE;

  SystemZone: PUBLIC PROCEDURE RETURNS [MDSZone] =
    -- returns the shared system zone
    BEGIN
    RETURN[sysZone]
    END;

  DestroyZone: PUBLIC PROCEDURE [zone: MDSZone] =
    -- unconditionally destroys the zone.  It is the client's responsibility to ensure
    -- that no dangling pointers remain.
    BEGIN
    z: MDSZoneHandle = LOOPHOLE[zone];
    FreeSegmentType[z.segType];
    FSPDefs.DestroyZone[z.fspZone];
    END;

  PruneZone: PUBLIC PROCEDURE [zone: MDSZone] =
    -- attempts to eliminate excessive space allocated to the zone.
    BEGIN
    [] ← FSPDefs.PruneZone[LOOPHOLE[zone, MDSZoneHandle].fspZone];
    END;


  -- Internal Procedures --

  InitZone: PROCEDURE [fspZone: ZonePointer, segType: DataSegmentType, id: STRING]
    RETURNS [MDSZone] =
    BEGIN
    tempZoneObject: MDSZoneObject ← MDSZoneObject[
      procs: @allocProcs, fspZone: fspZone, segType: segType, id: NIL];
    tempZone: MDSZone = LOOPHOLE[@tempZoneObject];
    zone: MDSZoneHandle = tempZone.NEW[MDSZoneObject];
    IF id ~= NIL AND id.length ~= 0 THEN
      BEGIN
      tempZoneObject.id ← tempZone.NEW[StringBody[id.length]];
      StringDefs.AppendString[tempZoneObject.id, id];
      END;
    zone↑ ← tempZoneObject;
    RETURN[LOOPHOLE[zone]]
    END;

  AllocateSegmentType: ENTRY PROCEDURE RETURNS [DataSegmentType] =
    BEGIN
    last: SegTypeIndex = rover;
    DO
      rover ← IF rover = LAST [SegTypeIndex] THEN FIRST[SegTypeIndex] ELSE SUCC[rover];
      IF rover = last THEN EXIT;
      IF availableTypes[rover] THEN {availableTypes[rover] ← FALSE; RETURN[rover*2]};
      ENDLOOP;
    RETURN WITH ERROR TooManyZones;
    END;

  FreeSegmentType: ENTRY PROCEDURE [segType: DataSegmentType] =
    BEGIN
    availableTypes[segType/2] ← TRUE;
    END;


  END.