[comp.sys.mac.programmer] ROM Unlocks handles

oster@dewey.soe.berkeley.edu (David Phillip Oster) (12/04/89)

In article <3838@atr-la.atr.co.jp> alain@atr-la.atr.co.jp (Alain de Cheveigne) writes:
_>If you use HGetState and HSetState instead, the routine leaves all handles
_>just the way they were before the routine was called.  The caller doesn't
_>have to know how the callee does his stuff, which is the way it should be.

_>But it seems that some ROM routines don't know it, and unlock handles 
_>behind one's back.  A list of those would be nice.

Alain, you have to understand "The Way od the Macintosh."  The ROM
expects you to only lock down what you absolutely have to, and that for as
short a time as possible.  To do otherwise is to risk running out of
usable memory because of fragmentation.  if you use a style like:
	HLock(handle);
	OneSystemCall();
	HUnlock(handle);
or even better:
	temp = (**hand).field;
	SystemCall(&temp);
	(**hand).field = temp;
you'll never have to worry about the ROM unlocking things.

alain@atr-la.atr.co.jp (Alain de Cheveigne) (12/05/89)

In article <32977@ucbvax.BERKELEY.EDU> oster@dewey.soe.berkeley.edu
(David Phillip Oster) writes:
>In article <3838@atr-la.atr.co.jp> alain@atr-la.atr.co.jp (Alain de Cheveigne) writes:
>>If you use HGetState and HSetState instead, the routine leaves all handles
>>just the way they were before the routine was called.  The caller doesn't
>>have to know how the callee does his stuff, which is the way it should be.
>>
>>But it seems that some ROM routines don't know it, and unlock handles 
>>behind one's back.  A list of those would be nice.
>
>Alain, you have to understand "The Way od the Macintosh."  The ROM
>expects you to only lock down what you absolutely have to, and that for as
>short a time as possible.  To do otherwise is to risk running out of
>usable memory because of fragmentation.  

I agree.  I'm assuming that the duration of my (short) subroutine is a
short time.  Granted, this assumption might be wrong if memory is
tight and some is needed during the subroutine.  But if I move the
handles up in the heap before locking them, it is unlikely they will
get in the way of heap compaction, because the subroutine is not likely
to free any space above them.

>If you use a style like:
>	 HLock(handle);
>	 OneSystemCall();
>	 HUnlock(handle);
>or even better:
>	 temp = (**hand).field;
>	 SystemCall(&temp);
>	 (**hand).field = temp;
>you'll never have to worry about the ROM unlocking things.

I run into two problems with these schemes:

a) The structures I use are deep.  Concretely: my program uses lots of
windows, which the user can create and dispose of in any order.  Data
is attached to each window in a relocable block, referred to by the
window refCon, and that block in turn refers to other relocatable data
and to another window (itself with data).  So reference to a given
field is done through many levels of indirection, most of them
relocatable...  I can't imagine locking and unlocking all this stuff
at every system call, so this rules out the first scheme.

b) Several of my data fields are big: text, signal arrays, and bit
images used by bitmaps (yes, I'm aware of the dangers of relocating
these...).  I don't want to copy the data each time it is used in a
system call.

Or should I...?  It took me lots of fumbling before I found a way to
cope with handle-locking in a reasonably clean way (that can survive
program modification, for example...).  But I can't say it satisfies
me.  If someone can point a better way, please do.

To make things clear, my present strategy is: a) On entry of a
subroutine, save states of all handles that might be dereferenced, 
b)MoveHHi() and HLock() them, c) do the processing, and d) Restore
handle states on exit (rather than unlock).

State saving/locking and restoring is done either handle by handle,
or on a window scale: a single routine locks all the data structures
associated with the window, after saving their state in a state
vector that is it returns.  Another routine restores the states on
exit.

Once the structures are locked, data fields are accessed through a
battery of macros that take the window as argument.  No need for
locking and unlocking, *except* after some system calls that unlock
their argument as a side effect (the subject of my original posting).


What justifies the hassle is this: I can now forget the insides of a
subroutine.  If I call it in some code, I don't have to remember to
re-lock or unlock every handle that it might have twiddled internally
(possibly as a side effect of another routine it called...).  If I
modify a subroutine, I don't have to go around checking for side
effects.

But hassle it is.  Is there a better way ?

Alain de Cheveigne,
alain@atr-la.atr.co.jp

oster@dewey.soe.berkeley.edu (David Phillip Oster) (12/06/89)

In article <3842@atr-la.atr.co.jp> alain@atr-la.atr.co.jp (Alain de Cheveigne) writes:
>But hassle it is.  Is there a better way ?

Yes.  What complexity, and slowness! (all those unnecessary MovHHi()s can
really slow a program down.)  I too use very similar data structrues:
handles in the RefCons of my windows that themselves reference other
handles.

If a field referenced by an O.S. call is small, copy it. If it is large,
lock the owning handle during the call.  Do not write your own routines to
take pointers into handles, instead, if you must do such a thing, do it by
passing a handle an an offset. (Often you can just pass the handle, since
the routine knows its offset.)

Here is an example, from a program that uses a window handle which
references two pixmaps. (I keep the current window handle in a global
variable called "theDoc", by analogy with "thePort".) Notice, no need for
any HLocks or MoveHHis.

/* DocDispose - discard our window
 */
DocDispose(){
	if(NIL != (**theDoc).newPix){
		if(NIL != (**(**theDoc).newPix).baseAddr){
			DisposPtr((**(**theDoc).newPix).baseAddr);
			(**(**theDoc).newPix).baseAddr = NIL;
		}
		DisposPixMap((**theDoc).newPix);
	}
	if(NIL != (**theDoc).oldPix){
		if(NIL != (**(**theDoc).oldPix).baseAddr){
			DisposPtr((**(**theDoc).oldPix).baseAddr);
			(**(**theDoc).oldPix).baseAddr = NIL;
		}
		DisposPixMap((**theDoc).oldPix);
	}
	DisposHandle((Handle) theDoc);
	DisposeWindow(thePort);
}

Actually, this does bring up a question. Inside Mac Vol 5 doesn't say
precisely which fields are disposed when you call DisposPixMap().
--- According to the Constitution, the Constitution is unconstitutional:
--- David Phillip Oster            --U.S.Constitution I.10.1: "No State shall
Arpa: oster@dewey.soe.berkeley.edu --enter into any treaty, alliance, or
Uucp: {uwvax,decvax}!ucbvax!oster%dewey.soe.berkeley.edu -- confederation..."

lippin@ronzoni.berkeley.edu (The Apathist) (12/06/89)

Recently alain@atr-la.atr.co.jp (Alain de Cheveigne) wrote:

>In article <32977@ucbvax.BERKELEY.EDU> oster@dewey.soe.berkeley.edu
>(David Phillip Oster) writes:

[Good stuff deleted]

>>If you use a style like:
>>	 HLock(handle);
>>	 OneSystemCall();
>>	 HUnlock(handle);
>>or even better:
>>	 temp = (**hand).field;
>>	 SystemCall(&temp);
>>	 (**hand).field = temp;
>>you'll never have to worry about the ROM unlocking things.

>I run into two problems with these schemes:
>
>a) The structures I use are deep.  Concretely: my program uses lots of
>windows, which the user can create and dispose of in any order.  Data
>is attached to each window in a relocable block, referred to by the
>window refCon, and that block in turn refers to other relocatable data
>and to another window (itself with data).  So reference to a given
>field is done through many levels of indirection, most of them
>relocatable...  I can't imagine locking and unlocking all this stuff
>at every system call, so this rules out the first scheme.
>
>b) Several of my data fields are big: text, signal arrays, and bit
>images used by bitmaps (yes, I'm aware of the dangers of relocating
>these...).  I don't want to copy the data each time it is used in a
>system call.

Most of the VAR-parameters the system wants are no larger than a long
word.  If these are located in relocatable blocks, I recommend using a
shadow variable on the stack for the call.

In the case of larger parameters by reference, I recommend locking the
block first, and restoring its restoring its state afterward.
HGetState/HSetState is sometimes better, but most handles are only
locked only in extreme circumstances, and HUnlock will suffice for
them.  Note that only the block containing the parameter needs to be
locked; "parent" blocks are free to slide around.

The other reason for locking a handle is so that you may carry around
a dereference to it.  In general, I avoid passing such a handle to any
major subroutine, be it my own or part of the operating system.  With
the obvious exceptions, I never pass a locked handle to the OS.  When
it becomes necessary to do so, I usually will unlock the block for the
duration of the call and recompute the dereference afterward.  Or
consider getting rid of the dereference entirely.

These rules won't cover every instance, but I find that the exceptions
are few and far between.

Finally, a note on MoveHHi -- this call is easy to overuse.  It's only
an advantage if a block is going to remain locked for a respectable
length of time; in particular, only when there's a chance of
significant memory allocation while the block is locked.  If used when
not necessary, it wastes time with frivolous heap shuffling.  Since
I avoid keeping handles locked for such periods, I use MoveHHi very
sparingly.

					--Tom Lippincott
					  lippin@math.berkeley.edu

	"It's a poor sort of memory that only works backwards."
					--The Red Queen

shebanow@Apple.COM (Andrew Shebanow) (12/07/89)

In article <32977@ucbvax.BERKELEY.EDU> oster@dewey.soe.berkeley.edu.UUCP (David Phillip Oster) writes:
>Alain, you have to understand "The Way od the Macintosh."  The ROM
>expects you to only lock down what you absolutely have to, and that for as
>short a time as possible.  To do otherwise is to risk running out of
>usable memory because of fragmentation.  if you use a style like:
>	HLock(handle);
>	OneSystemCall();
>	HUnlock(handle);
>or even better:
>	temp = (**hand).field;
>	SystemCall(&temp);
>	(**hand).field = temp;
>you'll never have to worry about the ROM unlocking things.


Ack! Please, please don't do this! If the system call expects a
Handle, you should never, ever pass it a fake handle. While your
technique will work on current Mac System Software, it will break
under System 7.0 if you are running in 32 Bit Mode. The new Memory
Manager doesn't store the handle flags in the high byte of the
Master Ptr anymore, so you will end up trashing your memory.

As far as it goes, there is (should be) very little code left in
the system which goes around unlocking things on you. Almost
everything has been fixed to use HGetState/HSetState.

For more info on this subject, see Tech Note 212 on 32 Bit Cleanliness.

Andrew Shebanow
MacDTS

lim@iris.ucdavis.edu (Lloyd Lim) (12/07/89)

In article <5640@internal.Apple.COM> shebanow@Apple.COM (Andrew Shebanow) writes:
>In article <32977@ucbvax.BERKELEY.EDU> oster@dewey.soe.berkeley.edu.UUCP (David Phillip Oster) writes:
>>[...]
>>	temp = (**hand).field;
>>	SystemCall(&temp);
>>	(**hand).field = temp;
>>[...]
>
>Ack! Please, please don't do this! If the system call expects a
>Handle, you should never, ever pass it a fake handle. While your
>technique will work on current Mac System Software, it will break
>[...]
>Andrew Shebanow
>MacDTS

Ack! Fake handle? What, where? The SystemCall just wants a pointer to a
variable (Pascal VAR parameter). Copying a field into a local temporary is
a perfectly valid way of eliminating moving memory problems. There are no
fake handles here.

Please read a little more carefully. DTS has not looked good lately with
the recent tech note errors and flame war postings.

+++
Lloyd Lim     Internet: lim@iris.ucdavis.edu (128.120.57.20)
              Compuserve: 72647,660
              US Mail: 146 Lysle Leach Hall, U.C. Davis, Davis, CA 95616

shebanow@Apple.COM (Andrew Shebanow) (12/07/89)

OK, I admit I overreacted. The technique David is using is
generally safe, but I thought that the way is was stated, it looked
like there was a potential problem with the code. However, this
problem would only occur if a dereferenced handle is stored in
the structure, which was the case I (mistakenly) thought he
was showing.

I apologize to David for accusing him of fake-handlism.

As for DTS's image on the net, all I can say is that I
hope that people have enough sense to keep the writings
of individual DTS people seperate in their heads. We don't
speak on UseNet as part of our job: it is just something
that we (collectively) do to encourage the spreading of
information, on our own time. Personally, I don't want to
be lumped into any group for my postings. What I write, I
write, and I want neither credit nor scorn for what Paul, Keith,
or any other DTS/Apple people choose to write.

Andy Shebanow
MacDTS

siegel@endor.harvard.edu (Rich Siegel) (12/08/89)

In article <5640@internal.Apple.COM> shebanow@Apple.COM (Andrew Shebanow) writes:

>>	temp = (**hand).field;
>>	SystemCall(&temp);
>>	(**hand).field = temp;
>
>Ack! Please, please don't do this! If the system call expects a
>Handle, you should never, ever pass it a fake handle. While your

	That's not a fake handle; it's a copy of a field of a handled block.
David made no assertions about the system call's parameters; in this particular
case, it looks like the system call takes some other type of argument, but
may move memory.

	In principle, you are correct, though. Fake handles are not a good
thing.

R.


~~~~~~~~~~~~~~~
 Rich Siegel
 Staff Software Developer
 Symantec Corporation, Language Products Group
 Internet: siegel@endor.harvard.edu
 UUCP: ..harvard!endor!siegel

"There is no personal problem which cannot be solved by sufficient
application of high explosives."

~~~~~~~~~~~~~~~