From: heksterb@cs.utwente.nl (Ben Hekster)
Subject: Re: Porting code using fork() to the Mac
Summary: CODE segment-unloading grow zone function
Organization: University of Twente, Dept. of Computer Science
Date: Tue, 18 Aug 1992 11:34:22 GMT


Hi--

I had a couple of requests to post my CODE segment-unloading grow zone
function, so here it is.

	As you can see, it's actually written in C++ (the Language of
the Gods), not in Assembly.  I've taken this code out of my class
library and extracted just (hopefully) all the relevant bits.  The
conversion to C shouldn't be much of a problem.  Though I suppose some
people may find it rather sparsely commented, that's the way I like
it.  I prefer to think that it's self-documenting...

	I wouldn't necessarily mind rewriting it in 68000 if I thought
it was worth it.  I haven't done any profiling on this code, so I'm not
yet convinced that it would be, though I can see some nested loops in
there and some possible `optimizations'.

	The reason why this doesn't really bother me is that the grow
zone function doesn't get called often, and only when you are operating
close to the limit of available memory.  One of the neat things about
this scheme is that you will never get called nor will any segment ever
be unloaded if you have enough memory available.  You only start paying
a performance penalty when you are nearly out of memory, which seems
reasonable.

	Remember, if you use this, you probably never have to call
UnloadSeg again.  Just mark any segments that you want to allow to be
purged as `purgeable'.  You may want to keep certain frequently used
segments resident (i.e., preload and not purgeable).  You MUST NOT make
the segment containing the grow zone function itself purgeable.

	Of course, if you can spot any problems with this, I'd be
obliged if you'd let me know about them.  I can come up with some
scenarios that might cause this to fail but they seem rather unlikely.
In particular I can't find it documented anywhere whether I can
actually count on A5 being valid in the gzProc.  I suppose that comes
down to not making any Memory Manager calls without A5 being valid,
but I don't think that's legal, so it looks like I'm safe.

	All I can say is that I and several beta testers have been
using this code in an application of mine for several months now
without any apparent problems.  Use at your own risk, &c. &c.

Enjoy!

P.S.:	View this code with an 8-character tab width.  Some of the lines
	are longer than 80 characters, but I've let them wrap instead
	of inserting line breaks or `fmt'ing the code

----- begins


class TApplication {
protected:
	static pascal long GrowZone(Size cbNeeded);		// grow zone function
	struct AddressRange { void *start, *end; };		// location of CODE in memory
	short		fNumCODEResources;			// number of CODE resources
	AddressRange	*fCODEAddresses;			// CODE addresses

public:
	static TApplication *gApplication;			// last instantiated application
	};



/*	TApplication
	Application constructor
*/
#pragma segment Initialization
TApplication::TApplication()
{
fCODEAddresses = NULL;

// etc.
...

// install grow zone function
fNumCODEResources = Count1Resources('CODE');
fCODEAddresses = (AddressRange*) NewPtr(fNumCODEResources * sizeof(AddressRange)); mfault;
if (fCODEAddresses)
	SetGrowZone(&TApplication::GrowZone);

// leave a pointer for anyone to find me
gApplication = this;
}


/*	~TApplication
*/
#pragma segment Termination
TApplication::~TApplication()
{
if (fCODEAddresses) DisposPtr((Ptr) fCODEAddresses);
}


/*	CurrentA5
	Returns the current value of A5
*/
#pragma parameter __D0 CurrentA5
void *CurrentA5() = 0x200d;					// MOVE.L A5,D0


/*	CurrentA7
	Returns the current value of A7
*/
#pragma parameter __D0 CurrentA7
void *CurrentA7() = 0x200f;					// MOVE.L A7,D0


/*	GrowZone
	Grow zone function
*/
#pragma segment Main
pascal long TApplication::GrowZone(
	Size		cbNeeded				// bytes needed
	)
{
(void) cbNeeded;						// not used
long unloadedSome = 0;

register AddressRange *codeAddress;
register short i;

void *mask = StripAddress((Ptr) 0xffffffff);

// find all purgeable but locked CODE resources in memory
register short numCODEResources = gApplication->fNumCODEResources;
char saveResLoad = *(char*) 0x0a5e;				// from SysEqu.h
SetResLoad(false);

for (i = 1, codeAddress = gApplication->fCODEAddresses; i <= numCODEResources; i++, codeAddress++) {
	codeAddress->start = NULL;

	// is the resource in memory?
	Handle codeH = Get1IndResource('CODE', i);
	if (codeH && *codeH)
		// is it a purgeable but locked resource?
		if ((HGetState(codeH) & 0xe0) == 0xe0) {
			// how big is it?
			Size codeSize = GetHandleSize(codeH);
			if (codeSize > 0) {
				// record the address range so it is eligible for purging
				codeAddress->start = (void*) ((long) *codeH & (long) mask);
				codeAddress->end = (char*) codeAddress->start + codeSize;
				}
			}
	}

if (saveResLoad) SetResLoad(true);

// examine the stack
Zone &applZone = *ApplicZone();
enum { CurStackBase = 0x0908 };
void
	*stackTop = CurrentA7(),				// stack to search
	*stackBase = *((void**) CurStackBase),
	*heapStart = &applZone.heapData,			// application zone data
	*heapEnd = applZone.bkLim;

// search the stack for references to loaded segments
enum { CurJTOffset = 0x0934 };
for (register char *a7 = (char*) stackTop; a7 < stackBase; a7 += 2) {
	// is the address on the stack within the application heap?
	register void *stackData = (void*) (*(long*) a7 & (long) mask);
	if (stackData >= heapStart && stackData < heapEnd)
		// is it in any of the code segments?
		for (i = 1, codeAddress = gApplication->fCODEAddresses; i < numCODEResources; i++, codeAddress++)
			if (codeAddress->start && stackData >= codeAddress->start && stackData < codeAddress->end) {
				// exclude it from unloading
				codeAddress->start = NULL;
				break;
				}
	}

// unload everything that wasn't referenced inside the stack
for (i = 1, codeAddress = gApplication->fCODEAddresses; i < numCODEResources; i++, codeAddress++)
	// may we unload it?
	if (codeAddress->start) {
		// ready to get it on!
		UnloadSeg((char*) CurrentA5() +
			(*(short*) CurJTOffset) +		// beginning of jump table
			(*(short*) codeAddress->start) +	// segment's first entry in jump table
			2					// routine address
			);
		unloadedSome++;
		}

return unloadedSome;
}

----- ends
-- 
Ben `Hackster' Hekster		| "He bides his time and thinks,
heksterb@cs.utwente.nl		|  'There must be more to life than this!'"
Xref: sparky comp.sys.mac.programmer:13937 comp.sys.mac.system:10931