WimpExtension document: Heap memory manager                           Doc 03
============================================================================

WimpExtension's heap manager is one of the module's most powerful, useful,
and - unavoidably - complicated features. This document is provided to
explain more about what a relocatable heap is, and how to use it.

Programming examples in this document are given in BASIC, because practically
everyone knows how to use it.

This document assumes that you know how to program in the RISC OS Wimp, and
that you understand terms such as "WimpSlot" (read the chapter "The Window
Manager" in the PRMs if you don't).

Read the "01-Manual" file for a brief description of the parameters for the
SWI WimpExt_Heap.


NB  This document is in two sections. The first section is relatively simple
--  and tells you all you will probably need to know about using the heap
    manager. The second section explains the advanced features of the heap
    manager. BE WARNED; this section is not simple. But, most of you won't
    need to read it, so if you find it confusing, ignore it.



Section 1
=========



Why do I need a heap?
---------------------

Any program which uses more than one block of memory which changes size needs
a heap manager. For example, Edit and Paint need a heap manager, because they
can have several files loaded at once, all of which can be changing size at
any time. The files are loaded into memory "blocks", which are provided by
the heap manager.



Why use a relocatable heap?
---------------------------

RISC OS provides a non-relocatable heap manager. This means that the memory
blocks are fixed in memory, and don't move about. The problem with this is
that it gets very wasteful on memory when you have finished using the block.
Say you loaded three files into a text editor:

  /--------\ Highest memory address
  | File 3 |
  |        |
  |--------|
  | File 2 |
  |        |
  |        |
  |--------|
  | File 1 |
  \--------/ Lowest memory address

This is fine, no problems so far. However, suppose the user decided to close
the file "File 2":

  /--------\ Highest memory address
  | File 3 |
  |        |
  |--------|
  | unused |
  |        |
  |        |
  |--------|
  | File 1 |
  \--------/ Lowest memory address

The block of memory in the middle is now unused, and is wasting space. A
non-relocatable heap manager can't do anything about this. Another example is
if the user adds something to File 2, making it longer. File 2 will no longer
fit in its original block - if you extended it then it would overwrite
File 3. Therefore the heap manager has to copy the whole file up in memory,
as shown in the diagram:

  /--------\ Highest memory address
  | File 2 |
  |        |
  |        |
  |        |
  |--------|
  | File 3 |
  |        |
  |--------|
  | unused |
  |        |
  |        |
  |--------|
  | File 1 |
  \--------/ Lowest memory address

WimpExtension provides a relocatable heap manager, however. This means that
the heap manager is allowed to move the blocks about, to try and reduce the
amount of wasted memory. In the situation above, for example, WimpExtension
would move File 2 and File 3 down in memory, like so:

  /--------\ Highest memory address
  | File 2 |
  |        |
  |        |
  |        |
  |--------|
  | File 3 |
  |        |
  |--------|
  | File 1 |
  \--------/ Lowest memory address

As you can see, this can save a lot of memory.



Anchors
-------

The only disadvantage of a reloctable heap is due to the fact that it IS
relocatable - the blocks of memory aren't fixed and may move about. A
question which immediately arises is "How do I find my memory block?" - if
the block may have moved, how do you find it?

The answer is by using an "anchor". An anchor is a word in memory. The
contents of this word is always a pointer to the start of the block. ie:

                      /-------\
                      |       |
  /--------\          |       |
  | anchor |--------->| block |
  \--------/          \-------/

The anchor always stays fixed in the same place in memory, so you always know
where to find it. So, to find the location of your block, you would use:

  block%=!anchor% : REM block% now points to the first byte in the block

The anchors are stored all together at the beginning of the heap.



Size of blocks
--------------

The word before the start of any block contains the size of the block in
bytes. So to find the size of a block, you would use:

  size%=!(block%-4)

or:

  size%=!( (!anchor%) -4)

(because block%=!anchor%).

Due to the way the heap manager works, the size of a block is always four
plus a multiple of eight (ie. 12, 20, 28, etc.). If you ask WimpExtension to
make a block with a size which isn't four plus a multiple of eight, then the
size will be rounded up by between one and seven bytes, to take it to the
next largest acceptable size.



Initialising the heap
---------------------

Before you can use any of the heap operations, you must initialise the heap.
This requires two parameters - the number of anchors to create initially, and
the address in memory of the base of the heap.

The anchors are stored all together at the beginning of the heap. Because you
need one anchor for every block in use, the number of anchors is effectively
the maximum number of blocks you can have in use at any one time. You can
create more anchors later, but it is best to create enough now, and then you
don't have to worry about it later. Remember that anchors do take memory
though, so don't go wild.

For example:

  SYS "WimpExt_Heap",0,base%,anchors%

Note that a heap which is empty (ie. no blocks are in use) takes absolutely
no memory at all.



Allocating blocks
-----------------

Whenever you ask WimpExtension to make you a new block (called "allocating"
a block), it gives you a pointer to the anchor for that block. You must store
the pointer to the anchor, NOT the pointer to the block, because the anchor
will never move, but the block may do. For example, to ask WimpExtension for
a block "size%" bytes long, you would use:

  SYS "WimpExt_Heap",2,,size% TO ,anchor%

If WimpExtension cannot create the block, because there is not enough free
memory, then the anchor returned will be zero. You need to check for this
before writing anything into the block. eg:

  SYS "WimpExt_Heap",2,,size% TO ,anchor%
  IF anchor%=0 THEN ERROR 1,"Not enough free memory"



Freeing blocks
--------------

When you have finished using a block, you use the "Free block" call to tell
WimpExtension that you don't need the block any more, and the memory it takes
up can be re-used. eg:

  SYS "WimpExt_Heap",3,anchor%

or:

  SYS "WimpExt_Heap",3,block%

The block will be added to WimpExtension's list of free blocks.



Reallocating blocks
-------------------

Sometimes you will want to re-size a block - this is called "reallocating"
the block. WimpExtension allows you to increase or decrease the size of any
block at any time. eg:

  SYS "WimpExt_Heap",4,block%,new_size% TO ,flag%

or:

  SYS "WimpExt_Heap",4,anchor%,new_size% TO ,flag%

If the flag is zero on exit then the re-size failed because there wasn't
enough free memory.

Note that this call may cause the specified block to move in memory. Only the
specific block that you are re-sizing can move, though - all the other blocks
will stay where they are.



Garbarge collection
-------------------

"Garbage collection" is the whole point of the relocatable heap. This is the
process of shuffling the blocks about in memory, so that no memory is wasted.
For example, if the heap was in the following state:

  /---------\ Highest memory address
  | block 2 |
  |         |
  |---------|
  |  free   |
  |         |
  |         |
  |---------|
  | block 1 |
  \---------/ Lowest memory address

then performing garbage collection would shuffle the blocks as follows:

  /---------\ Highest memory address
  | block 2 |
  |         |
  |---------|
  | block 1 |
  \---------/ Lowest memory address

This then allows WimpExtension to decrease your task's WimpSlot, thus making
this memory available to any other programs which might need it.

Two garbage collection calls are provided; the Tidy call:

  SYS "WimpExt_Heap",5

and the Compact call:

  SYS "WimpExt_Heap",6

The Tidy call just tidies the heap a little bit - the heap won't necessarily
immediately be shuffled into the ideal position, but the call is usually
quite fast. This call is intended to be called just before you call
Wimp_Poll. If you set bit 3 of R2 when you called WimpExt_Initialise, then
the heap will be automatically Tidied once a second, in WimpExt_PrePoll. Most
of the time, you can just set this bit, and then forget all about garbage
collection - it will all be handled for you.

The Compact call tidies the heap completely - the heap will immediately be
shuffled into the ideal position. This call is often slower than the Tidy
call, however, as it does more work.



How often should I tidy the heap?
---------------------------------

How often you tidy the heap depends on the average size of the blocks your
program uses. If your program uses lots of little blocks then you should tidy
the heap quite often. If it uses a smaller number of larger blocks then you
should tidy it less often.

For a text editor, for example, the once-a-second tidy provided automatically
should be fine. For something which uses smaller blocks, you should probably
tidy the heap more often.



Finding the anchor of a block
-----------------------------

If you have a pointer to a block, and you want to locate the block's anchor,
then a call is provided to do this:

  SYS "WimpExt_Heap",7,block% TO ,anchor%

An error is produced if block% is not a valid pointer to the start of a
block.



Fixing blocks
-------------

Sometimes you may want to temporarily 'fix' the heap. This makes it so that
all the blocks are fixed and cannot move. This is useful if you need to rely
on a block staying put at the same position in memory for a while.

To fix the heap, you would use:

  SYS "WimpExt_Heap",8

and to unfix it you would use:

  SYS "WimpExt_Heap",10

Note that WimpExtension keeps a count of the number of times you haved fixed
the heap, and only unfixes the heap when you tell it to the same number of
times. ie:

  SYS "WimpExt_Heap",8
  SYS "WimpExt_Heap",8
  SYS "WimpExt_Heap",10

would leave the heap fixed. The following, however, would leave the heap
unfixed, as the number of "unfixings" matches the number of "fixings":

  SYS "WimpExt_Heap",8
  SYS "WimpExt_Heap",8
  SYS "WimpExt_Heap",10
  SYS "WimpExt_Heap",10

You can force WimpExtension to unfix the heap straight away, regardless of
the number of times it has been fixed, using the following call:

  SYS "WimpExt_Heap",9

ie. The following would leave the heap unfixed:

  SYS "WimpExt_Heap",8
  SYS "WimpExt_Heap",8
  SYS "WimpExt_Heap",9



Creating more anchors
---------------------

WimpExtension allows you to create more anchors, even while the heap is in
use. Note that this call has to move the entire heap up in memory, so it can
be quite slow. For example:

  SYS "WimpExt_Heap",11,add%

"add%" extra anchors will be created. All the blocks will be moved.

WimpExtension also provides a call to allocate a block, automatically
creating more anchors if you've run out:

  SYS "WimpExt_Heap",12,,size% TO ,anchor%


Freeing all blocks
------------------

WimpExtension provides a call to free all the blocks in the heap:

  SYS "WimpExt_Heap",13

Every single allocated block will be freed, and all the memory released.



Describing the heap
-------------------

WimpExtension provides a call which provides various bits of information
about the heap:

  SYS "WimpExt_Heap",1 TO ,,largest%,free%,heapsize%,anchors%,blocks%

  largest%  : The size of the largest continuous area of memory in the heap
  free%     : The total amount of memory free in the heap
  heapsize% : The total amount of memory used by the heap
  anchors%  : The number of anchors which have been created
  blocks%   : The number of blocks which are currently allocated


-----------------------------------------------------------------------------
Here ends the simple part of this file.

From here on it is far more complicated, but you almost certainly won't need
to read it. So if you get confused, don't worry.


Section 2
=========

OK, tremble in fear, mortals, because if you thought that the last bit was
complicated, you're just gonna LOVE this. Now we get to talk about multiple
heaps, heaps within heaps, and heaps that can move about in memory (called
'shifting' heaps).

WimpExtension's heap manager supports multiple heaps. ie. You can have more
than one heap going at once. Each heap is treated as totally separate from
all the other heaps.

It also supports 'shifting' heaps, which can move about in memory. The
standard heap is NOT a shifting heap, because although the blocks themselves
can move about, the heap itself always stays in the same place.

The extra considerations involved when using shifting heaps are NOT trivial,
and are liable to cause severe headaches if you try to think about them for
too long. (That's what happens to me, anyway.)

Different heaps are referred to by their 'heap pointers'. A heap pointer
points to a block of memory set out as follows:

  +00  hp_heap       offset of heap data area
  +04  hp_free       offset of first free block, or 0 if no free blocks
  +08  hp_anchors    offset of first anchor
  +0C  hp_heapsize   total size of heap in bytes
  +10  hp_heapfix    'fixed' counter
  +14  hp_tidytime   monotonic time of last Tidy call
  +18  hp_heaptidy   tidy flag; 0 if heap is fully tidied, non-zero otherwise
  +1C  hp_created    created flag; 0 if heap is uncreated, non-zero otherwise
  +20  hp_base       pointer to base of heap memory
  +24  hp_pagesize   'page' size
  +28  hp_setsize    pointer to routine to change size of heap
  +2C  hp_numblocks  number of blocks claimed
  +30  hp_rootptr    pointer to pointer to base of heap memory
  +34  hp_misc       depends on what sort of heap this is
All offsets are from hp_base.

The following things are defined about the heap:
  * hp_base must be a multiple of 8
  * hp_free need not contain a sensible value if hp_created is zero
  * the anchors are in the area hp_anchors to hp_heap
  * each anchor is 8 bytes, 4 bytes pointer to block then 4 bytes task handle
  * hp_heapsize must be a multiple of 8
  * hp_heap must be a multiple of 8
  * hp_anchors must be a multiple of 8



hp_rootptr and hp_base
----------------------

hp_rootptr points to a pointer to the base of the heap. This must always be
where the heap actually is in memory. hp_base contains the position of the
heap in memory last time the heap manager looked. These addresses are used by
the relocation routine (see below) to update the anchors if the heap moves.
hp_rootptr does not provide a sufficient level of indirection if the heap is
nested inside a shifting heap, and you will have to update this value
manually if this is the case.



hp_setsize and hp_pagesize
--------------------------

hp_setsize is a routine that is called when WimpExtension wants to change the
size of the heap. It is entered in SVC mode as follows:
 R0  = size required
 R10 = heap pointer
 R13 = supervisor stack
 R14 = return address
Exit:
 R0  = 0 indicates failure, non-zero indicates success
 all other registers preserved

It should NOT return errors, just indicate success or failure with R0 as
shown. The routine should update hp_heapsize as necessary, and the base of
the heap as indicated by hp_rootptr if the heap has moved. It should NOT
change hp_base, as this is needed to relocate the anchors.

hp_pagesize is used to decide whether or not it is worth increasing or
decreasing the heap size. The heap size is always decreased or increased in
multiples of this value.



hp_misc
-------

hp_misc's value depends on what sort of heap this is. For the standard heap,
it is reserved and its value is undefined. For a heap within a heap, created
for you by WimpExtension, it contains the heap pointer of the parent heap. If
the heap was created by you, then you can use it for whatever purpose you
want.



Selecting a different heap
--------------------------

To select a different heap, use call 14:

  SYS "WimpExt_Heap",14,new_heap% TO old_heap%,new_heap%

new_heap% can take one of several values:
  -1     don't change the selected heap
  +1     select the standard heap
  other  new_heap% is a heap pointer; select this heap

On exit, old_heap% and new_heap% contain the heap pointers of the previous
and current heaps respectively, or 0 if no heap was/is selected.

After this call, all other WimpExt_Heap calls act on the selected heap.



Relocating the heap
-------------------

If you have moved the heap about in memory, then use the Relocate call to
update the anchors:

  SYS "WimpExt_Heap",15

This call is used automatically after WimpExtension calls hp_setsize, and on
entry to any of the other WimpExt_Heap calls, so you will only need to use it
if you move the heap at some other time and then need to reference an anchor
before using another WimpExt_Heap call.



Creating a heap within another heap
-----------------------------------

WimpExtension supports another call to create heaps. This one creates a heap
inside a heap block in another heap. It is used as follows:

  SYS "WimpExt_Heap",16,heapptr%,anchors%

heapptr% points to a block of memory which is filled in by the call with a
heap description block. anchors% is the number of anchors to create.



Creating custom heaps
---------------------

To create a heap to your own specification, simply make a heap description
block, and then select it as the current heap. If you set hp_created to zero,
then you won't have to set up anything in the heap memory itself.



Considerations for shifting heaps
---------------------------------

Shifting heaps are fun. Not only can all the blocks move, but the heap itself
can move as well. This means that the anchors can move. So you have to store
the offset of the anchor in the heap, rather than a pointer to the anchor
itself, and then calculate the address of the anchor when you need it by
referencing hp_base.

Basically, nothing at all in a shifting heap is fixed, EXCEPT the heap
pointer. BUT the heap pointer doesn't have to be fixed, you can move it
around as long as you re-select the heap...

When you start using shifting heaps inside shifting heaps, the excitement
knows no bounds... A shifting heap inside a block in a shifting heap inside
a block in a shifting heap inside a block in a shifting heap is actually
quite tricky to keep track of ;-).
