Dec 232017
 
Virtual memory manager for TP 5.5. Source and docs included. Excellent set of routines, requires Turbo Power Objects to compile.
File VMMNGR.ZIP from The Programmer’s Corner in
Category Pascal Source Code
Virtual memory manager for TP 5.5. Source and docs included. Excellent set of routines, requires Turbo Power Objects to compile.
File Name File Size Zip Size Zip Type
EXDYNARR.PAS 5000 1431 deflated
EXVMQUEU.PAS 952 373 deflated
VMM.DOC 39805 11062 deflated
VMM.IN1 1319 376 deflated
VMM.IN2 23013 5238 deflated
VMM.IN3 12228 3394 deflated
VMM.IN4 9404 2502 deflated
VMMNGR.PAS 17102 4310 deflated
VMMTEST1.PAS 3773 1223 deflated
VMMTEST2.PAS 1619 701 deflated

Download File VMMNGR.ZIP Here

Contents of the VMM.DOC file






VMM - Virtual Memory Manager


A complementary unit for Object Professional
a package from TurboPower Software

Author : Patrick Philippot - 7/90



This package is released to the public domain. It
can be distributed to anyone FOR FREE. I DO NOT
authorize anyone to charge ANY AMOUNT of money
for this package wathever the media used to
distribute it.


First of all let me apologize for my rather poor
english. I hope anyone will understand the
following documentation.

WARNING : You need Turbo Professional version 1.0x
to compile this package.

*****************



A. Overview

The Virtual Memory Manager object (VMM) allows you to allocate memory for
dynamic variables without being limited by the size of the Turbo Pascal
heap. That is, VMM is not a replacement system for the Turbo Pascal memory
manager but it provides you with an easy-to-use complementary package able
to temporarily store allocated blocks in EMS or on disk and to automatically
move them into RAM when they are needed.

VMM was written with the following specifications in mind:

- VMM methods do not replace the System.GetMem and System.FreeMem routines.
They can peacefully coexist with VMM without any trouble. VMM memory blocks
are allocated in a separate RAM area, allocated on the heap. This area does
not interfere with the memory areas managed by Turbo Pascal itself.

- The VMM object is intended to be a complementary memory management
system which will be used to allocate medium sized blocks of memory (or
numerous smaller ones) that don't need to be present in RAM at any time.

- VMM does not require the Turbo Pascal compiler to be patched to generate
a particular interrupt each time a pointer is dereferenced. Instead, it
provides a dereferencing routine which is to be applied to each pointer
returned by VMM. This will guarantee compatibility with future versions of
Turbo Pascal.

- VMM is an object. This provides you with the general advantages related
to objects but also with this one : you can generate as many objects of type
VMM as you want, each of them dealing with different kind of data. This is
very much like having several heaps enabled to use disk and EMS to swap out
data, each of them being dedicated to a particular role in your application.

Using VMM

All the internal mechanisms of VMM are fully transparent to you and adding
the capabilities of VMM to your applications is very straightforward.

First of all, like for many objects, you have to initialize the VMM
object. This is done using one of two methods. The Init method initializes
the VMM with default options which are suitable for most cases. You only
have to provide the name of the swap file. The InitCustom method allows you
to gain more control over the VMM object and to specify the initialization
parameters.

Then you will allocate memory blocks using the GetMemV method and free them
using the FreeMemV method. Dereferencing is made thru the VmmDrf inline
macro. Dereferencing a pointer means that you make reference to the data it
points to by adding a caret (^) after the name of the pointer.

When your program terminates or if you want to recover the space used in RAM,
EMS and on disk by your VMM, you'll call the Done method.

That's all.

VMMTEST1 and VMMTEST2 are short programs showing you the whole process of
initializing a VMM, allocating and freeing memory with it. Since they allocate
a lot of virtual memory blocks they execute rather lengthy. They were used to
debug the VMMNGR unit.


B. Dynamic arrays

Although Dynamic Arrays are not specific to the VMM, we use them heavily
in the VMM design. This allows VMM to use the smallest possible amount
of RAM memory to store the structures needed to manage the allocated blocks.
Dynamic Arrays can be used for many other purposes so we decided to include
the related documentation in this section.

The DynArray object allows you to create arrays holding elements of any size,
growing dynamically when a size increase is needed. DynArrays are very
useful when the number of elements in the array is not known at compile time
and when other dynamic structures like linked lists or queues are not
relevant.

A DynArray is not as easy to use as a "normal" array because elements cannot
be accessed directly. However this drawback is compensated by the fact that
DynArrays are more generic and can hold elements of any type.

The total size of a dynamic array as well as the size of a single element
cannot exceed 64k. More accurately, 65521 bytes.

Although this capability is not used by VMM objects, DynArrays can be
stored to and loaded from streams.

EXDYNARR.PAS shows you how DynArrays work.

--------------------------------------------------------------------------------
Init

Declaration
constructor Init(MaxElements, ElementSize, Incr : Word);

Purpose
Initializes an empty DynArray.

Description
Sets data area pointer to nil, item count to zero and initializes other data
from the passed parameters. MaxElements is the maximum number of items the
dynamic array may contain. ElementSize is the size of a single item. Incr is
the basis used to calculate the minimum size increment when inserting new
items. Since inserting a new item may cause the data area to be reallocated,
choosing a very small value for Incr will naturally save memory space but
will cause heap fragmentation. For example, giving Incr the value of 128
will cause space for 128 items to be allocated even if only the first item
has been inserted. Inserting the 129th item will cause space for 128
additional items to be allocated.

No space is allocated for the array until an element is inserted.

Example
MyDynArray.Init(100, SizeOf(LongInt), 5);
Initializes a dynamic array containing at most 100 elements of type LongInt.
The size of the array will be increased by increments of 5*4 bytes.

--------------------------------------------------------------------------------
Done

Declaration
destructor Done; virtual;

Purpose
Destroy a DynArray.

Description
Frees data area and resets data area pointer to nil.

Example
MyDynArray.Done;

See Also
Clear

--------------------------------------------------------------------------------
SetElem

Declaration
procedure SetElem(Index : Word; var Elem);

Purpose
Move the contents of Elem in the indexth element of the DynArray.

Description

Valid indexes range from 0 to MaxElements minus 1. DynArrays are always 0
based. Allocates enough space to the data area to hold at least Index
elements. Elements which have not been set are filled with nulls. If Index
is not a valid index, GetStatus returns epFatal+ecBadParam. If there is not
enough memory available to allocate to the data area, GetStatus returns
epFatal+ecOutOfMemory.

Index may range from 0 to GetMaxIndex.

Example
var
L : LongInt;

L := 100000;
MyDynArray.SetElem(50, L);

Puts the value 100000 in the 51th element of MyDynArray.

See Also
GetElem GetValidElems GetMaxIndex

--------------------------------------------------------------------------------
GetElem

Declaration
procedure GetElem(Index : Word; var Elem);

Purpose
Puts the value of the Indexth element of array in Elem.

Description

Provided Index is a valid index, Elem will be loaded with the value
contained in the Indexth element of the array. It is assumed that Elem is big
enough to hold one element of the array. Otherwise memory will be overwritten.

Example
var
L : LongInt;

MyDynArray.GetELem(20, L);
if L = TestValue then
...
Puts value of 21th element of the array in L.

See Also
GetValidElems SetElem GetMaxIndex

--------------------------------------------------------------------------------
GetElemSize

Declaration
function GetElemSize : Word;

Purpose
Return size of a single element.

Description
Returns size passed to the ElemSize parameter in the Init method.

Example
if MyDynArray.GetElemSize > 4 then
...
Tests if size of one element is greater than 4 bytes.

See Also
Init

--------------------------------------------------------------------------------
GetArraySize

Declaration
function GetArraySize : Word;

Purpose
Return the actual size in bytes allocated to the data area.

Description

As the elements are inserted the size of the dynamically allocated data area
increases. GetArraySize return the size occupied by the data area on the
heap independently from the number of elements actually used.

Example
var
L : LongInt;
S : Word;
MyDynArray.Init(100, SizeOf(LongInt), 20);
L := 100000
SetElem(0, L);
S := MyDynArray.GetArraySize;
{S now yields 20*4 bytes}
L := 200000
SetElem(1, L);
S := MyDynArray.GetArraySize;
{S still yields 80 bytes}

See Also
GetValidElems

--------------------------------------------------------------------------------
GetMaxIndex

Declaration
function GetMaxIndex : Word;

Purpose
Return maximum admissible value for an index regardless of the actual size
of the data area.

Description
This is the greatest value acceptable for the Index parameter passed to the
Init method. It is equal to MaxElements-1.

Example
MyDynArray.Init(100, SizeOf(LongInt), 20);
I := MyDynArray.GetMaxIndex;
{I now yields 99}

--------------------------------------------------------------------------------
GetValidElems

Declaration
function GetValidElems : Word;

Purpose
Return the maximum index that can be passed as a parameter to GetElem.

Description
Although the Init method specifies the greatest number of elements a dynamic
array may contain, no memory is allocated to hold all these elements unless
a call to SetElem is made for the latest element in the array. The actual
size of the data area is not a good information because we allocate memory
for it with minimum increments. So the GetElem method should not use indexes
greater than GetValidElems-1.

Example
var
L : LongInt;
I : Word;
MyDynArray.Init(100, SizeOf(LongInt), 20);
I := MyDynArray.GetMaxIndex;
{I = 99}
L := 100000
SetElem(0, L);
I := MyDynArray.GetValidElems;
{I = 1}
L := 200000
SetElem(9, L);
S := MyDynArray.GetValidElems;
{I = 10}

See Also
GetMaxIndex

--------------------------------------------------------------------------------
Shrink

Declaration
procedure Shrink(ElemNb : Word);

Purpose
Decrease data area size and discard exceeding elements.

Description
Reallocate the data area and discard elements above index ElemNb-1. If
ElemNb is greater than GetValidElems nothing happens. This doesn't prevent
from setting any element between the first (index 0) and the last elements
(GetMaxIndex).

Example
MyDynArray.Shrink(50);

--------------------------------------------------------------------------------
Clear

Declaration
procedure Clear;

Purpose
Reset the dynamic array and discard any data.

Description
Disposes of the data area and reset all internal variables. The dynamic array
is left in the same state as after the Init method has been run.

Example
MyDynArray.Clear;

--------------------------------------------------------------------------------
Load

Declaration
constructor Load(var S : IdStream);

Purpose
Load the dynamic array from a stream.

Description
S is a properly intialized stream object.

Load reads the next sequence of bytes from the stream S. These bytes must
have been written by a previous call to DynARray.Store. Load allocates heap
space for the data area, copy the data to that area and initializes all
internal variables. The dynamic array is left in the same state as it was
when the call to the Store method was made.

if Load fails, the constructor will return False and the error code will
be stored in the global variable InitStatus.

The stream registration procedure for a DynArray is DynArrayStream.

Example
MyDynArray.Load(S);

See Also
Store

--------------------------------------------------------------------------------
Store

Declaration
constructor Store(var S : IdStream);

Purpose
Store the dynamic array to a stream.

Description
S is a properly intialized stream object.

Store writes an image of the DynArray to the stream S. The image includes
the array data, the current number of valid elements, the maximum number of
elements, the size of the data area, the minimum grow increment, the maximum
number of elements and the size of a single element.

To check for errors that have occured during the Store, call the stream's
GetStatus function.

The stream registration procedure for a DynArray is DynArrayStream.

Example
MyDynArray.Store(S);

See Also
Load
--------------------------------------------------------------------------------
GetStatus

Declaration
function GetStatus : Word;

Purpose
Return status code and reset internal result to zero.

Description
Each DynArray maintains an internal variable that stores the current status of
the dynamic array. GetStatus provides direct access to this variable. Like
IOResult, GetStatus clears the internal status variable after it returns the
current value.

When no error has occured GetStatus returns 0. Otherwise it returns an error
code as specified in the Error handling section. Expected error values
include:

epFatal+ecBadParam
epFatal+ecOutOfMemory

Example
MyDynArray.SetElem(10, V);
if GetStatus <> 0 then begin
{process error}
...
end;

--------------------------------------------------------------------------------
PeekStatus

Declaration
function PeekStatus : Word;

Purpose
Return status and leave internal variable unchanged.

Description
Works like GetStatus but doesn't clear the internal status variable.

--------------------------------------------------------------------------------
Error

Declaration
procedure Error(Code : Word);

Purpose
Report an error.

Description
Applications do not call this routine directly. When a DynArray method
detects an error, it calls the error method to report it. The default
implementation of this method simply assigns the error code to an internal
status variable. An object derived from VMM may override the Error method
to provide different error handling behavior.

--------------------------------------------------------------------------------

C. The Virtual Memory Manager

Although VMM is very simple to use, it's important to have a look at the
way it works. You will use VMMs more efficiently if you have some ideas
about the GPVMM design.

VMM components are:

- the VMM pointers which are actually handles returned by the GetMemV
procedure. These pointers have to be considered like normal pointers by the
user except that they cannot be dereferenced directly. The VmmDrf inline
macro has to be used to dereference such pointers.

- the RAM area which is used as a transfer area where memory blocks are
first allocated and to which or from which these blocks are paged in and out
if needed. When you allocate a block with the GetMemV procedure you can be
sure that the block is present in the RAM area just after calling GetMemV.
(in general you cannot assume that a particular block is present in memory).
Later, if we need room in the RAM area to allocate a new block or to page in
an older one, blocks present in the RAM area will be paged out using the LRU
algorithm until enough memory space has been made free. The RAM area is
allocated on the Turbo Pascal heap. It may be greater than 64k, provided you
replace the standard GetMem procedure by a routine of your own. A hook is
provided for that.

- the Freelists, which are used to keep track of free blocks in RAM, EMS and
on disk. Freelists are all derived from the DynArray object type which has
been described in the previous section. There are 3 freelists :
vmRamFreeList, vmEmsFreeList and vmDskFreeList.

- the Descriptor Table, which is able to translate a VMM pointer returned
by GetMemV into a record holding all the information about the memory block.
A VMM Descriptor knows where a block is located, whether it is locked in
RAM and where it is located on the actual media containing it (RAM, EMS or
swap file). The Descriptor is also derived from the DynArray type.

- The LRU queue which keeps track of the Least Recently Used VMM pointers
(handles). When paging out is needed, VMM first swaps out the blocks that
were used (which pointers were dereferenced) least recently. This is
achieved by pushing a VMM pointer (actually, only the handle part of it)
into the LRU queue each time it created or dereferenced. If the VMM
pointer already exists in the queue it is removed before adding it to the
queue. The LRU queue is derived from the OPROOT StaticQueue object. It has
two additional methods : IsEmpty which returns true if the queue is empty
and Remove which is able to remove the fist occurence of an element in a
StaticQueue updating data and head and tail pointers. VMM always try to
remove a handle from the LRU queue before adding to the tail. This makes sure
that the handle will be unique in the queue. Trying to page out the same
handle twice would cause a system error.

- The dereference handler. This procedure (actually an interrupt handler) is
not part of the object definition. Its name is DerefHandler and it is called
by the VmmDrf inline macro. The interrupt vector used is 66h.

Interrupt 66h is one of the user-definable interrupts described by IBM. If
this interrupt conflicts with your environment, you may change the global
typed constant VmIntUsed to use a different interrupt. User-definable
interrupts range from 60h to 66h. (67h is used for EMS, so it should be
avoided here). If you change the interrupt, be sure to modify the VmmDrf
inline function as well.

The interrupt vector is restored when your program exits or when a runtime
error occurs.

Since the dereference handler does not know which VMM has generated the
pointer which is to be dereferenced, it refers to a global pointer called
VmmActiveMgr. This pointer is set by a special method, LinkToDerefHandler,
which stores the value of the SELF pointer into VmmActiveMgr. This way, the
dereference handler always knows which data to use. Failing to call
LinkToDerefHandler before dereferencing a VMM pointer will likely cause
your program to hang.

If you used GetMemV to allocate space for a dynamic record, you'll need to do
some typecasting to access the different fields of the record. Example:

type
MyRec = record
I : Integer;
W : Word;
S : String;
end;
MyRecPtr = ^MyRec;

var
MyPtr = MyRecPtr;

...
GetMemV(MyPtr, SizeOf(MyRec));
...
VmmDrf(MyPtr)^.S {Will generate a compiler error}
MyRecPtr(VmmDrf(MyPtr))^.S {Will compile}
MyRec(VmmDrf(MyPtr)^).S {Will compile}


When you request a memory allocation, the following process occurs.

First, the VMM object examines the RAM area to check if enough memory is
available. If not, memory blocks will be paged out to EMS or disk until enough
memory is available in the RAM area. Blocks are paged out to EMS first and
then to disk. The VMM will not use all EMS or disk space available. You can
control this by setting the EmsToKeep and DskToKeep parameters when calling
InitCustom. The defaults used by the Init method are 1 megabyte disk space
and 10% of the available Ems memory left free.

When memory blocks are dereferenced, the same mechanism occurs until the
block can be loaded into the RAM area. Under some circumstances it may be
possible that all virtual memory ressources (EMS and disk) are exhausted and
that not enough free space is available in the RAM area. In order to prevent
such a "dead lock", VMM refuses to allocate space if there is not enough
space in EMS or on disk to page out 3 times the size of the RAM area. Keeping
free only as many bytes as the RAM area can hold is not sufficient because of
fragmentation and because we may have for example to swap out to disk blocks
that have been primarily paged in from EMS.

VMM allows the RAM area to be greater than 64k. Though, since this area is
allocated on the Turbo Pascal heap, it is not possible to use System.GetMem to
achieve this. If you have replacement routines for System.GetMem and
System.FreeMem, you can tell VMM to use them by setting the value of two
global procedure variables : UserGetMem and UserFreeMem. These routines must
be of type GetMemFUnc and FreeMemProc:

GetMemFunc = function(var P; Size : LongInt) : Boolean;
FreeMemProc = procedure(var P; Size : LongInt);

UserGetMem must return nil if no allocation was made.
UserFreeMem must set the pointer to nil.

UserGetMem defaults to VmmGetMem and UserFreeMem defaults to VmmFreeMem which
are revised versions of GetMemCheck and FreeMemCheck found in OpRoot.

VMM has very few options. You can only specify if you VMM to use only
EMS virtual memory or only disk virtual memory. Disabling both ressources
wouldn't make much sense. So it's impossible. Another option let you specify
if the swap file has to be deleted when the VMM object is destroyed. This
is the default option. Keeping the swap file on the disk will be sometimes
useful for debugging purposes.

Both vmUseEms and vmUseDsk options are set to true unless the corresponding
parameter of InitCustom explicitly excludes one of these ressources (empty
name for the swap file or NoEms -$FFFF- constant for EmsToKeep). Lack of
both ressources when a VMM is initialized causes the initialization
process to fail. If either EMS or disk space are not present in sufficient
amounts, the corresponding option is set to false. If your program later
releases some EMS memory or delete some files you may try to activate the
relevant option.

--------------------------------------------------------------------------------
Init

Declaration
constructor Init(SwapFName : PathStr);

Purpose
Initialize a Virtual Memory Manager.

Description
Init initializes the VMM using default options. You only have to provide
the name of the swap file. Init calls InitCustom with the following
parameters:

MaxHeapAlloc = 65521 bytes
DefIncr = 128 bytes
DefFreeEntries div 2 = 1024 entries / 4096 bytes
DefFreeEntries = 2048 entries / 8192 bytes
DefQueueSize = 1024-1 entries
DefEmsToKeep = 10% of Ems pages available when Init is called
DefDskToKeep = 1 megabyte is left free on swap disk

Please refer to the description of InitCustom to have an explanation of the
meaning of these parameters. Using Init will be sufficient in most cases.

Don't worry about the way you pass the swap filename. This name will be
expanded (via FExpand) to produce a full qualified unique filename.

Example
VM.Init('VMM.SWP');
VM.LinkToDerefHandler;

See Also
InitCustom LinkToDerefHandler

--------------------------------------------------------------------------------
InitCustom

Declaration
constructor InitCustom(RamSize : LongInt;
Incr, MaxVmmEntries,
MaxFreeEntries, VmmQueueEntries,
EmsPagesToKeep : Word;
DskToKeep : LongInt;
SwapFName : PathStr);

Purpose
Initialize a Virtual Memory Manager with custom options.

Description
InitCustom provides a more sophisticated way of initializing a VMM.

RamSize specifies how much memory has to be allocated on the Turbo Pascal
heap for the RAM area. This size may be greater than 64k, provided
UserGetMem (see above) points to a routine allowing such a huge allocation.
Init uses a default of 65521 bytes.

Incr is the increment used to initialize the dynamic arrays holding the
freelists. Please refer to the DynArray.Init method to understand the role
of this increment value.

MaxVmmEntries specifies the maximum number of VMM pointers that can be
allocated by the memory manager. This doesn't mean that space is reserved
for all of these entries. Space is allocated on demand when entries are
created by GetMemV.

MaxFreeEntries defines the maximum number of entries in the freelists.
Freelists do not need to be very big unless you generate a big number of
VMM pointers or you use FreeMemV very heavily. The default values of Init
will do in almost all circumstances.

VmmQueueEntries specifies how many handles can be held in the LRU queue.
Because the RAM area is much smaller than the memory space available in EMS
or on disk, you don't need to set up a very big LRU queue. In theory, to be
sure that the paging process will never fail we should have as many entries
in the LRU queue as we can hold memory blocks in the RAM area. A very
extreme case would be allocating blocks of one byte. Assuming we would have
a RAM area of 65521 bytes, we would need 65521 entries in LRU queue! This is
impossible because each entry need two bytes. Most of the time a VMM will
deal with middle sized memory blocks. So using a default of 512 entries will
be sufficient.

EmsPagesToKeep specifies how many EMS pages the VMM should leave free at
any time for the application program (or for another VMM). If you want to
use, say, 2 VMM in an application and you don't want to see the EMS
resource to be exhausted by the first VMM, leaving only disk virtual
memory for the second one, you can set the EmsToKeep parameter to a higher
value for the first initialized VMM than for the second one.

If EmsToKeep equals NoEms ($FFFF) the vmUseEms option is deactivated. This
way the virtual memory manager will use only disk to page out memory blocks.

DskToKeep specifies how much disk space the VMM should leave free at any
time for the application program (or for another VMM). The same remark as
for the EMS memory applies here too.

SwapFName is the name of the swap file. If this name is an empty string, the
vmUseDsk option is deactivated.

Example
VM.InitCustom(30000, 50, 500, 500, 150, 50, 0, '');
VM.LinkToDerefHandler;

Initializes VM with a RAM area of 30000 bytes, a minimum increment of 50
entries for the dynamic arrays, a maximum number of 500 entries in the
Descriptor Table (that is 500 VMM pointers) and in the freelists. 50 EMS
pages will be left free and the disk will not be used for paging out.

See Also
Init LinkToDerefHandler

--------------------------------------------------------------------------------
Done

Declaration
destructor Done; virtual;

Purpose
Destroy a virtual memory manager.

Description
All dynamic arrays used by the VMM are destroyed and the memory they used
is released to the heap. The RAM area memory is also released to the heap.
All EMS handles allocated by the VMM are deallocated. The swap file is
closed and deleted (unless the vmDeleteSwap option is off).

Example
VM.Done;

--------------------------------------------------------------------------------
GetMemV

Declaration
procedure GetMemV(var Pt; BlkSize : Word);

Purpose
Allocate a memory block in RAM area and return a VMM pointer in Pt.

Description
GetMemV first searches for a free block of size BlkSize in the RAM area. If
it doesn't find such a block it pages out memory blocks present in the RAM
area (beginning with the Less Recently Used) until sufficient memory space
has been made free to allocate the new block. If this process fails, it
returns a nil pointer. Otherwise it returns a VMM pointer in Pt. This
pointer is not a real pointer. The offset part is always $FFFF and the
segment part is a handle which will be used to enter the Descriptor Table
where information about the allocated block is stored.

The returned pointer cannot be used like a normal pointer. It must be
"dereferenced" thru the VmmDrf function.

The new handle is added to the tail of the LRU queue.

Example
var
P : ^string;
...
VM.GetMemV(P, Length(str)+1);
VmmDrf(P)^ := str;

Allocates enough space to hold str.

See Also
FreeMemV VmmDrf

--------------------------------------------------------------------------------
FreeMemV

Declaration
procedure FreeMemV(var Pt);

Purpose
Release memory used by Pt.

Description
The memory block used by Pt is deallocated. The corresponding entry in the
Descriptotr Table is marked as a free entry. A new entry is inserted in the
relevant freelist. Pt is set to nil.

You don't need to specify the size of the block because the Descriptor Table
keeps track of this. This is necessary to manage the freelists and much more
convenient for you.

Example
VM.FreeMemV(P);

See Also
GetMemV

--------------------------------------------------------------------------------
LinkToDerefHandler

Declaration
procedure LinkToDerefHandler;

Purpose
Make the DerefHandler aware of which VMM is active.

Description
Since the dereference handler is called by an INT instruction, we cannot
specify to which VMM it has to refer. Only one VMM can be active at a
given time. It is the last one which issued a call to LinkToDerefHandler.
This one line procedure stores the value of the SELF pointer into a global
variable which will be referred to by the dereference handler.

Using VMM.VmmDrf without linking the VMM to the dereference handler is
very much like using unitialized pointers. This will likely cause a program
failure.

Example
VM.LinkToDerefHandler;

See Also
VmmDrf

--------------------------------------------------------------------------------
Lock

Declaration
function Lock(var Pt; Lockit : Boolean) : Boolean;

Purpose
Prevent a memory block from being paged out.
Allow a block to be paged out.

Description

Lock updates the Descriptor Table entry corresponding to the passed VMM
pointer (Pt) and sets the "lock" bit if Lockit is true. The handle is
removed from the LRU queue. From now on, the block cannot be paged out any
more. It will remain in the RAM area. The block is unlocked by passing a
value of False in Lockit.

Lock will be used to make sure that a block remains in memory even when
another pointer is dereferenced. Lock works even if the block pointed to by
Pt is presently not in RAM. If you dereference the corresponding pointer,
the block will be moved to the RAM area and will stay there until you unlock
it.

Lock should be use with extreme care because locking one or several blocks
in the RAM area may cause the paging out process to fail if the dereferenced
pointer points to a block which is too big to fit into the remaining space.
You should make sure that the locked block(s) and the blocks that will be
paged into the RAM area have a total size smaller than the RAM area size. A
block should be locked in RAM for very short periods of time and unlocked as
soon as its presence in RAM is no more necessary.

To help you managing this you may want to use the ClearRamArea method before
locking blocks.

As we said a VMM is a complementary memory management system that should be
used only for temporarily storing medium sized data out of the heap not for
storing blocks that are used very often.

Example
if VM.Lock(MyPtr, true) then
...

See Also
ClearRamArea

--------------------------------------------------------------------------------
GetSize

Declaration
function GetSize(var Pt) : Word;

Purpose
Return the size of the memory block pointed to by Pt.

Description
GetSize looks into the Descriptor Table and returns the size stored in the
corresponding descriptor.

Example
S := VM.GetSize(MyPtr);

--------------------------------------------------------------------------------
ClearRamArea

Declaration
function ClearRamArea : Boolean;

Purpose
Page out all non locked memory blocks.

Description
ClearRamArea is a simple call to the internal method called PageOut,
requesting the system to page out all blocks stored in the RAM area unless
they are locked. After a call to ClearRamArea, if there is no locked blocks
you can be sure that the entire RAM area is free of any data. If you want to
make sure that 2 or 3 blocks can be present in RAM simultaneaously, use
ClearRamArea before dereferencing the corresponding pointers. ClearRamArea
returns true if all the RAM area has been cleared.

Example
if not VM.ClearRamArea then
Writeln('Some blocks are still locked in RAM.');

See Also
Lock

--------------------------------------------------------------------------------
RamMaxAvail

Declaration
function RamMaxAvail : LongInt;

Purpose
Return the size of the biggest free memory block in the RAM area.

Description
RamMaxAvail return the size of the last entry in the RamFreeList. Since all
freelists are always sorted in size order, the size of the last entry is the
size of the biggest free block.

Example
S := VM.RamMaxAvail;

--------------------------------------------------------------------------------
EmsMaxAvail

Declaration
function EmsMaxAvail : LongInt;

Purpose
Return the size of the biggest free memory block in Ems.

Description
EmsMaxAvail return the size of the last entry in the EmsFreeList or 64k if
sufficient EMS is available to allocate a 4 pages frame. Since all freelists
are always sorted in size order, the size of the last entry is the size of
the biggest free block.

Example
S := VM.EmsMaxAvail;

--------------------------------------------------------------------------------
DskMaxAvail

Declaration
function DskMaxAvail : LongInt;

Purpose
Return the size of the biggest free memory block on disk.

Description
DskMaxAvail return the size of the last entry in the DskFreeList or the
amount of disk space available minus DskToKeep. Since all freelists are
always sorted in size order, the size of the last entry is the size of the
biggest free block.

Example
S := VM.DskMaxAvail;

--------------------------------------------------------------------------------
vmOptionsOn

Declaration
procedure vmOptionsOn(OptionFlags : Word);

Purpose
Turn specified VMM options on.

Description

Turning vmUseDsk or vmUseEms options has no effect if there is not enough disk
or EMS space available.

Example
VM.vmOptionsOn(vmUseDsk)

--------------------------------------------------------------------------------
vmOptionsOff

Declaration
procedure vmOptionsOff(OptionFlags : Word);

Purpose
Turn specified VMM options off.

Description
Setting both vmUseDsk and vmUseEms options off is not possible. Doing so
would cause VMM to work in RAM only, that is with very limited resources.
For example, if you already set vmUseEms off and try to do the same with
vmUseDsk, it will remain on.

Example
VM.vmOptionsOff(vmDeleteSwap)

--------------------------------------------------------------------------------
vmOptionsAreOn

Declaration
function vmOptionsAreOn(OptionFlags : Word) : Boolean;

Purpose
Return TRUE if OptionCodes are enabled.

Description
This routine allows to determine whether a single option or multiple options
are on. The possible options for VMM are vmUseDsk, vmUseEms, vmDeleteSwap.

Example
if VM.vmOptionsAreOn(vmUseEms) then
{VM is using Ems...}

--------------------------------------------------------------------------------
GetStatus

Declaration
function GetStatus : Word;

Purpose
Return status code and reset internal result to zero.

Description
Each VMM maintains an internal variable that stores the current status of
the memory manager. GetStatus provides direct access to this variable. Like
IOResult, GetStatus clears the internal status variable after it returns the
current value.

When no error has occured GetStatus returns 0. Otherwise it returns an error
code as specified in the Error handling section. Expected error values
include:

epNonFatal+ecOutOfRamEntries
epNonFatal+ecOutOfEmsEntries
epNonFatal+ecOutOfDskEntries
epNonFatal+ecCantFreeEms
epFatal+ecOutOfDescEntries
epFatal+ecBadParam
epFatal+ecEmsAllocation
epFatal+ecEmsPageMapping
epFatal+any I/O error

Example
if not VM.ClearRamArea then
Error := GetStatus;
{process error}
...
end;

--------------------------------------------------------------------------------
PeekStatus

Declaration
function PeekStatus : Word;

Purpose
Return status and leave internal variable unchanged.

Description
Works like GetStatus but doesn't clear the internal status variable.

--------------------------------------------------------------------------------
Error

Declaration
procedure Error(Code : Word);

Purpose
Report an error.

Description
Applications do not call this routine directly. When a VMM method detects an
error, it calls the error method to report it. The default implementation of
this method simply assigns the error code to an internal status variable. An
object derived from VMM may override the Error method to provide different
error handling behavior.


D. Error handling

Beside the GetStatus and PeekStatus functions a VMM can generate runtime
errors. In some cases a program cannot recover from an error. If a bad
pointer is passed to VmmDrf or if dereferencing is not possible because the
EMS manager failed or because there are too much locked blocks in memory,
the only way out is a runtime error. This very much like runtime errors
occuring when the System.GetMem or System.FreeMem procedures fail.

Runtime errors generated by VMM are:

211 : Abstract method not overridden (should not occur).
212 : Non recoverable EMS error.
213 : Could not page out or bad pointer.
204 : Invalid pointer operation.
epFatal+I/O error : non recoverable disk error (only when dereferencing)

Before halting the program the VMM unit restores INT 66h. Another
mechanism which is not processed by the Exit procedure, deallocates all EMS
handles allocated by all VMMs before calling the RunError procedure.




 December 23, 2017  Add comments

Leave a Reply