[comp.sys.mac.programmer] ROM doesn't unlock Handles

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

Some time ago I posted an article claiming that some ROM calls leave
handles unlocked (causing a bug if code expects a locked handle to
stay locked after the call).

Larry Rosenstein asked me to produce an example.  I set traps in my
code to catch the bug, and after 3 weeks it still hasn't turned up.

I guess I misinterpreted some other bug.  Here are my apologies for
maligning the ROM.

Luckily, a posting by shebanow@Apple.COM (Andrew Shebanow) offers me
an honorable way out:

>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.

Which implies that it hasn't always been so.  So maybe it wasn't my
imagination after all...


My original posting had another aim: find the wisest way to deal with
locking and unlocking handles, especially in code that contains many
nested subroutines.

Here's a synthesis of the follow-ups and e-mail I received.

First, why lock a Handle?  Because the Memory Manager might move the
block it contains.  The MM will update the master pointer, so the
Handle is still valid, but it but won't update the pointers that you
(or the compiler) might be using to access the data.

This happens in seemingliy harmless code.  Tim Maroney gives an
example (marsup is an unlocked handle):

>(*marsup)->wombat = NewHandle(WOMBAT_SIZE);
>
>The compiler is free to evaluate the address of the left-hand side
>before it performs the right-hand side.  If NewHandle moves memory,
>then its return value will be assigned into outer space.

There's a tech note that says that code like that needs to be preceded
by MoveHHi(marsup); HLock(marsup), and followed by HUnlock(marsup).

The trouble with this is that you use four operations instead of one.
Your code gets longer, wastes time, and becomes pretty unreadable.  So
it's natural to try to cut down on the locking.

When not to lock?  Ordinary code that does't a) call the ROM or b)
call routines in another segment is safe.  Furthermore, IM-IV and V
contain lists of Routines that May Move or Purge Memory, implying that
all other ROM routines are safe.

However, as pete@hub.UUCP (Pete Gontier) writes:
>DTS' official position now is that there are so many patched traps running
>around calling the Memory Manager that you need to lock and unlock handles
>around ANY trap call. The list, in other words, is no longer valid.

The follow-up on this boiled down to:

From: shebanow@Apple.COM (Andrew Shebanow):
>Anyhow, this statement of DTS's position is a bit of an oversimplification.
>
>It is true that the lists of traps that may move or purge memory are out
>of date, and that traps which currently are considered "safe" may become
>"unsafe" sometime in the future. Our recommendation is that developers
>err on the side of caution: if you are passing a dereferenced field
>of a handle by address, either use a temporary or lock the handle. I
>do feel that there are some calls which are "safe" to use dereferenced
>handles with, like BlockMove, some string manipulation calls, etc. Use
>your better judgment (or "Toolbox Karma", if you will).

Which is pretty lame.  Either a call is safe or it isn't.  If DTS
can't guarantee it, who am I to judge, and how?  Trial and error?

Other (non-ROM) subroutines are supposedly safe if they don't call the
ROM.  But to be sure of this, you have to know what's inside a
routine.  This defeats the principle of information hiding, so the
code tends to be unreliable (even Apple can't seem to keep track of
such side effects).

In addition, a call to a "harmless" subroutine can move memory if the
subroutine is in an unloaded segment.  Bottom line: don't trust *any*
routine, either Apple's or your own.


Another way to avoid locking is to use a temp variable, as recommends
tim@hoptoad.uucp (Tim Maroney):

>Handle h = NewHandle(WOMBAT_SIZE);
>(*marsup)->wombat = h;
>
>Slightly more stack overhead, but you avoid trap overhead for locking
>and unlocking, and also avoid all the state-restoration hassles that
>have been mentioned lately here.

This scheme was recommended by many, and emerged as the best solution
for short fields.  But it isn't obvious what to do if the field is
large: a string, or an array, or data for a BitMap or PixMap.

Temp variables for big data fields require space (stack?  heap?) and
copying takes time.  In the case of a string, copying with strcpy()
might move memory if strcpy() happened to be in an unloaded segment.
BlockMove() is said to be safe (but then who knows...).

It seems better to lock a handle to access big fields.

This brings us to the question of state restoring.  Suppose you have a
long piece of code that needs a pointer to data in a handle.  You
don't want to keep locking and unlocking, so you call Hlock() once at
the beginning, and HUnlock() at the end.  Now, if your code calls a
routine that also locks and unlocks the handle, then that handle is
left *unlocked* in the following code.  This situation is difficult to
identify.

Avoiding it, if you only have HLock() and HUnlock(), requires some
coordination between sub-routine implementation and calling code (for
example the routine relies on the calling code to lock).  This runs
counter to the principle of information hiding, and makes for fragile
code.

That's where HGetState() and HSetState() come in handy.  When locking
can't be avoided, precede HLock() with a call to HGetState(), and
replace HUnlock() with HSetState().  Your code is guaranteed to leave
the state as it found it.


Some last advice:

From: earleh@eleazar.dartmouth.edu (Earle R. Horton):
>     One thing to remember when using HGetState and HSetState is that these
>are not available on early (64k ROM) machines, so you have to test for
>machine type or trap availability before using them.  For an application
>program which probably owns all the Handles it deals with, HLock and HUnlock
>are appropriate nearly all the time.  For code resources which manipulate
>data owned by applications or the system, H[GS]etState is a more application-
>friendly technique, if available.

For 64k ROM machines you could save the high-order byte of the handle.
Code that does either this or calls HGetState() according to a flag
should fit ok in a macro.


From: lippin@ronzoni.berkeley.edu (The Apathist) (Tom Lippincott0):
>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.


From: tim@hoptoad.uucp (Tim Maroney):
>>HLock and HUnlock are probably the best solution in this case, or the
>>HGetState and HSetState calls. 
>
>Nope, there's more overhead involved in the trap calls than in the
>copying of eight bytes.  And there's also less "cognitive overhead"
>if you avoid locking handles, since you never have to keep track
>of when things are supposed to be locked.

Reducing "cognitive overhead" is *the* idea.


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

beard@ux1.lbl.gov (Patrick C Beard) (12/27/89)

In article <3875@atr-la.atr.co.jp> alain@atr-la.atr.co.jp (Alain de Cheveigne) writes:
>My original posting had another aim: find the wisest way to deal with
>locking and unlocking handles, especially in code that contains many
>nested subroutines.
[discussion of ways to avoid locking deleted]
>Another way to avoid locking is to use a temp variable, as recommends
>tim@hoptoad.uucp (Tim Maroney):
>
>>Handle h = NewHandle(WOMBAT_SIZE);
>>(*marsup)->wombat = h;
>>
>>Slightly more stack overhead, but you avoid trap overhead for locking
>>and unlocking, and also avoid all the state-restoration hassles that
>>have been mentioned lately here.
>
>This scheme was recommended by many, and emerged as the best solution
>for short fields.  But it isn't obvious what to do if the field is
>large: a string, or an array, or data for a BitMap or PixMap.

Now wait one second.  We are talking about _Handles_.  The overhead required
to keep a temporary copy of a Handle, regardless of how large the allocated
memory block is, is 4 bytes!  Therefore, your analysis is incorrect.  You
would never need more than one temporary handle variable to keep track of
any size field (if that field is allocated as a Handle).  Thus, Tim and 
everybody else who said this are correct.  This is the best way to avoid
locking handles.

>
>Temp variables for big data fields require space (stack?  heap?) and
>copying takes time.  In the case of a string, copying with strcpy()
>might move memory if strcpy() happened to be in an unloaded segment.
>BlockMove() is said to be safe (but then who knows...).
>
>It seems better to lock a handle to access big fields.
>

Bogus conclusion.

>This brings us to the question of state restoring.  Suppose you have a
>long piece of code that needs a pointer to data in a handle.  You
>don't want to keep locking and unlocking, so you call Hlock() once at
>the beginning, and HUnlock() at the end.  Now, if your code calls a
>routine that also locks and unlocks the handle, then that handle is
>left *unlocked* in the following code.  This situation is difficult to
>identify.

Why are you passing this Handle around between more than a few routines?
If you are writing fairly modular code, you should be able to minimize
the number of places this is a problem.

I've been programming the Mac for a couple of years and I've never thought
memory management was so hard.  I've just followed these simple rules:

1.  Never lock a handle if possible.
2.  Never, never, keep a copy of a dereferenced handle around, you might be
tempted to use it.  These cause a whole new breed of dangling references.
3.  Allocate memory that is never going to be relocated as a pointer.  For
example, allocate lots of WindowRecords and GrafPorts early in a program
as a single NewPtr call.  This will keep the heap clean, and you don't have
to worry about Handle states, and your code will be faster.

With virtual memory on the horizon, Handles may not be long for this world.


-------------------------------------------------------------------------------
-  Patrick Beard, Macintosh Programmer                        (beard@lbl.gov) -
-  Berkeley Systems, Inc.  ".......<dead air>.......Good day!" - Paul Harvey  -
-------------------------------------------------------------------------------

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

In article <4525@helios.ee.lbl.gov>$@$G(Jbeard@ux1.lbl.gov (Patrick C
Beard) writes:
>In article <3875@atr-la.atr.co.jp> alain@atr-la.atr.co.jp (Alain de Cheveigne) writes:
[...]
>>Another way to avoid locking is to use a temp variable, as recommends
>>tim@hoptoad.uucp (Tim Maroney):
>>>Handle h = NewHandle(WOMBAT_SIZE);
>>>(*marsup)->wombat = h;
[...]
>>This scheme was recommended by many, and emerged as the best solution
>>for short fields.  But it isn't obvious what to do if the field is
>>large: a string, or an array, or data for a BitMap or PixMap.
>
>Now wait one second.  We are talking about _Handles_.  The overhead required
>to keep a temporary copy of a Handle, regardless of how large the allocated
>memory block is, is 4 bytes!  Therefore, your analysis is incorrect.  You
>would never need more than one temporary handle variable to keep track of
>any size field (if that field is allocated as a Handle).  Thus, Tim and 
>everybody else who said this are correct.  This is the best way to avoid
>locking handles.

Groan!  Why on earth should one want to copy the _Handle_ itself?

I must not have made myself clear: what needs copying is not the 4
byte Handle, but the *contents* of the block.  That's the stuff that
can move.

But I admit that Tim's example was unfortunate, as it involves two
Handles: wombat, that needs locking, and h, the 4 byte value of which
needs copying because HewHandle() moves memory.

>>It seems better to lock a handle to access big fields.
>>
>
>Bogus conclusion.

Please read more carefully.


>3.  Allocate memory that is never going to be relocated as a pointer.  For
>example, allocate lots of WindowRecords and GrafPorts early in a program
>as a single NewPtr call.  This will keep the heap clean, and you don't have
>to worry about Handle states, and your code will be faster.

What you're telling me is that the safe way to use Handles is to use
Pointers instead.

This far from covers all needs.  All depends on the order in which you
allocate and release memory.  Schematically:

- if you need memory throughout the program, you can use global space
(though you might not want to, for other reasons),

- if memory allocated in a routine is allways released on exit (meaning
that memory is released in strictly the inverse order 


- if 

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

Ooops! Butter fingers! My last message escaped unfinished.  Sorry.
Here is the end of what I had to say:

[...]

This far from covers all needs.  All depends on the order in which you
allocate and release memory.  Schematically:

- if you need memory throughout the program, you can use global space
(though you might not want to, for other reasons),

- if memory allocated in a routine is always released on exit
(meaning that memory is released in strictly the inverse order in
which it was allocated), it can go on the stack,

- if you want memory to stay around after a routine returns (say, a
window creation routine) and you're unlikely to need to deallocate it,
then use a Pointer,

- if you need to allocate and deallocate in any order, then you must
use Handles to avoid heap fragmentation.

By this reasoning, WindowPtrs should have been Handles, since the user 
can open and close them in any order.  

The choice between Handle and Pointer in a program depends on the
program's overall structure, and the kind of user interaction you wish
to allow.  If you can get away with Pointers, then by all means do.


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

d88-jwa@nada.kth.se (Jon Watte) (12/28/89)

In article <3889@atr-la.atr.co.jp> alain@atr-la.atr.co.jp (Alain de Cheveigne) writes:
>By this reasoning, WindowPtrs should have been Handles, since the user 
>can open and close them in any order.  

Well, for "historical reasons" they aren't...

A good thing to do is to

...
  tempH = NewHandle(sizeof(WindowRecord));
  MoveHHi(tempH);
  HLock(tempH);
  theWindow = GetNewWindow(4711, (GrafPtr) *tempH, 0L);
  SetWRefCon(theWindow, tempH);

...

  tempH = GetWRefCon(theWindow);
  CloseWindow(theWindow);
  DisposHandle(tempH);

But, alas, that means you can't use the WRefCon for something else...
It DOES avoid cluttering the heap, though. If you don't intend to
close the window, you may ignore putting the handle into the WRefCon.

>The choice between Handle and Pointer in a program depends on the
>program's overall structure, and the kind of user interaction you wish
>to allow.  If you can get away with Pointers, then by all means do.

Pointers should be allocated before anything else, or at least when
all handles are unlocked. And they DO fragment the heap too often to
be very popular with me.

-- 
   ---  Stay alert !  -  Trust no one !  -  Keep your laser handy !  ---
             h+@nada.kth.se  ==  h+@proxxi.se  ==  Jon Watte
                    longer .sig available on request

tim@hoptoad.uucp (Tim Maroney) (12/28/89)

In article <3875@atr-la.atr.co.jp> alain@atr-la.atr.co.jp (Alain de Cheveigne)
writes:
>>>Another way to avoid locking is to use a temp variable, as recommends
>>>tim@hoptoad.uucp (Tim Maroney):
>>>>Handle h = NewHandle(WOMBAT_SIZE);
>>>>(*marsup)->wombat = h;
>>>This scheme was recommended by many, and emerged as the best solution
>>>for short fields.  But it isn't obvious what to do if the field is
>>>large: a string, or an array, or data for a BitMap or PixMap.

In article <4525@helios.ee.lbl.gov>$@$G(Jbeard@ux1.lbl.gov (Patrick C
Beard) writes:
>>Now wait one second.  We are talking about _Handles_.  The overhead required
>>to keep a temporary copy of a Handle, regardless of how large the allocated
>>memory block is, is 4 bytes!  Therefore, your analysis is incorrect.  You
>>would never need more than one temporary handle variable to keep track of
>>any size field (if that field is allocated as a Handle).

In article <3888@atr-la.atr.co.jp> alain@atr-la.atr.co.jp (Alain de Cheveigne)
writes:
>Groan!  Why on earth should one want to copy the _Handle_ itself?
>
>I must not have made myself clear: what needs copying is not the 4
>byte Handle, but the *contents* of the block.  That's the stuff that
>can move.

You still haven't made yourself clear; I really haven't the faintest idea
what you're talking about.  Could you provide a code example?

>But I admit that Tim's example was unfortunate, as it involves two
>Handles: wombat, that needs locking, and h, the 4 byte value of which
>needs copying because HewHandle() moves memory.

Well, it's big of you to "admit" this.  However, you are wrong.  That
was what the example was all about -- assigning into a relocatable
structure can fail if the assignment may have the side effect of moving
memory.  If the "copying" does not involve anything that moves memory
-- for instance, if you are doing a BlockMove on a string, or a
conventional "=" assignment on a structure -- then you don't have to
either lock the handle or use a temporary variable.

And after reading the above quoted paragraph five times, I have to
wonder if *you* know what you mean.  wombat does not require locking.
You can't lock a structure field; that makes no sense.  h does not
"require copying because NewHandle moves memory"; I don't even know
what that is supposed to mean.  It is there because the statement

(*marsup)->wombat = NewHandle(WOMBAT_SIZE);

may be evaluated left to right.  First, the address of
(*marsup)->wombat is calculated.  Second, NewHandle() is called.  This
second step may cause the first step's address to become invalid, since
(*marsup) may change.  The way around this is to use a temporary
variable, because this inverts the order.  First NewHandle() is called
and its result saved on the stack; then the address of
(*marsup)->wombat is taken and the saved value is assigned into it.

>>3.  Allocate memory that is never going to be relocated as a pointer.  For
>>example, allocate lots of WindowRecords and GrafPorts early in a program
>>as a single NewPtr call.  This will keep the heap clean, and you don't have
>>to worry about Handle states, and your code will be faster.

>What you're telling me is that the safe way to use Handles is to use
>Pointers instead.

No he isn't.  WindowRecords and GrafPorts *have* to be pointers in the
Mac OS.
-- 
Tim Maroney, Mac Software Consultant, sun!hoptoad!tim, tim@toad.com

"God must be a Boogie Man." -- Joni Mitchell

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

>>I must not have made myself clear: what needs copying is not the 4
>>byte Handle, but the *contents* of the block.  That's the stuff that
>>can move.
>
>You still haven't made yourself clear; I really haven't the faintest idea
>what you're talking about.  Could you provide a code example?

OK, here's a piece of code:

	/* saveBits is a Handle containing a BitMap record
	  followed by the bit image data.  It needs locking
	  because CopyBits can move memory. */

	state = HGetState((Handle) saveBits);

	HLock((Handle) saveBits);
	fixBitMap(saveBits); /* the baseAddr field is incorrect if the 
				block has moved, fixBitMap() corrects it */

	CopyBits(&(w->portBits),*saveBits,
		&(w->portRect),&(**saveBits).bounds,srcCopy,NULL);

	HSetState((Handle) saveBits, state);	/* restore state */

The "contents of the block" in the quotation above would refer here to
the BitMap record and its associated bit image data.  Using the "copy
to temp" method to avoid locking would require a big piece of stack or
non-relocatable heap, time to copy, and the faith that whatever you
use to copy won't scramble the heap.  BlockMove() is said to be safe,
but I'm less sure about calling strcpy() on a string in a Handle.

I'm not saying one shouldn't copy to temps, even in this case.  Maybe
it's the cleanest way.  I don't know.  I'm here to learn.

The reason for putting the offscreen bitmaps in Handles is that they
are allocated and released as the user creates and disposes windows.
As the user is expected to use many windows (up to say, 50 in a
session), using Pointers instead would badly fragment the heap.  I'm
less worried about WindowPtrs because they are all the same size, and
I expect new ones to tend to occupy the slots left by old ones.


>>But I admit that Tim's example was unfortunate, as it involves two
>>Handles: wombat, that needs locking, and h, the 4 byte value of which
>>needs copying because HewHandle() moves memory.
[...]
>And after reading the above quoted paragraph five times, I have to
>wonder if *you* know what you mean.  wombat does not require locking.
>You can't lock a structure field; that makes no sense.  h does not
>"require copying because NewHandle moves memory"; I don't even know
>what that is supposed to mean.  It is there because the statement
>
>(*marsup)->wombat = NewHandle(WOMBAT_SIZE);
>
>may be evaluated left to right.  First, the address of

Mea culpa. I mistyped wombat for marsup.  The posting escaped me
before I could check.  The phrasing wasn't too good either.  It's
indeed not _wombat_ that might need locking, but _marsup_.  It should
make sense this way:

"Tim's example was unfortunate because it involves two Handles: one
whose fields might move in memory (marsup), and another returned by
NewHandle().  The latter is the example of a 4 byte value that is
copied to temp h, to avoid having to lock marsup."

A different choice for that value (say, a Pointer returned by
NewPtr()) would have made the example clearer.

>>>3.  Allocate memory that is never going to be relocated as a pointer.  For
>>>example, allocate lots of WindowRecords and GrafPorts early in a program
>>>as a single NewPtr call.  This will keep the heap clean, and you don't have
>>>to worry about Handle states, and your code will be faster.
>
>>What you're telling me is that the safe way to use Handles is to use
>>Pointers instead.
>
>No he isn't.  WindowRecords and GrafPorts *have* to be pointers in the
>Mac OS.

I *know* that.  Presenting as a choice something the OS enforces
anyway wouldn't make sense anyhow.  Recall that the discussion is
about Handles.  I interpret Patrick's point as saying one should plan
out memory needs as far as possible, and allocate memory early, in
non-relocatable blocks.  Good point, but not relevant to Handle
locking in cases where Handles are called for.

Anyhow, my apologies again for posting a badly written article.

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

tim@hoptoad.uucp (Tim Maroney) (12/30/89)

In article <3899@atr-la.atr.co.jp> alain@atr-la.atr.co.jp (Alain de Cheveigne)
writes:
>	/* saveBits is a Handle containing a BitMap record
>	  followed by the bit image data.  It needs locking
>	  because CopyBits can move memory. */
>
>	state = HGetState((Handle) saveBits);
>	HLock((Handle) saveBits);
>	fixBitMap(saveBits);
>	CopyBits(&(w->portBits),*saveBits,
>		&(w->portRect),&(**saveBits).bounds,srcCopy,NULL);
>	HSetState((Handle) saveBits, state);	/* restore state */
>
>The "contents of the block" in the quotation above would refer here to
>the BitMap record and its associated bit image data.  Using the "copy
>to temp" method to avoid locking would require a big piece of stack or
>non-relocatable heap, time to copy, and the faith that whatever you
>use to copy won't scramble the heap.  BlockMove() is said to be safe,
>but I'm less sure about calling strcpy() on a string in a Handle.

A bitmap is only 14 bytes.  The compiler can move it using three
MOVE.Ls and a MOVE.W.  I'm pretty sure that's faster than calling a
trap -- that is, even the overhead of trap dispatch is more expensive
than that, to say nothing of what the trap does once you get to it.

I see what you mean now, and of course some locking does need to be
done.  For instance, if the baseAddr field of your bitmap is made
relocatable, as you seem to be doing and as some code I'm now working
on does, then you want to lock it, extract the pointer, stick it in the
bitmap, and then revert to the previous state after CopyBits.  Not only
is it big, but it's variable-sized.  But a BitMap structure is cheaper
to copy into a temp variable than to save/lock/restore, e.g.,

BitMap map = **saveBits;       /* four instructions to copy */

>The reason for putting the offscreen bitmaps in Handles is that they
>are allocated and released as the user creates and disposes windows.
>As the user is expected to use many windows (up to say, 50 in a
>session), using Pointers instead would badly fragment the heap.

Right.  Good choice.

>I'm less worried about WindowPtrs because they are all the same size, and
>I expect new ones to tend to occupy the slots left by old ones.

Well, maybe and maybe not.  I throw caution to the winds and go ahead
and allocate all my windows in the heap, but it's just bad design in
the MacOS that they aren't relocatable, and they *can* cause heap
fragmentation.  You can't touch GrafPort's at the interrupt level
anyway, because of the clipRgn and visRgn fields, so there's no reason
the whole structure shouldn't be relocatable.  No doubt by the time
the designers realized this, they had all this hand-optimized assembly
language code in the OS already written and it would have been
impossible to modify.  Another reason to use C to write operating
systems.

(I understand the MacOS was written largely in Lisa Pascal, then
hand-optimized because the compiler was too stupid to do it.  Changing
a data structure from pointer to handle is not too hard in a compiler,
because the compiler will pick up all the places that need to be
changed, but a naive Pascal compiler won't put out fast enough code for
many OS modules, or small enough code to put an entire OS in 64K.  So
all the code would need to be re-hand-optimized, a process which is
likely to introduce many new errors and take a very long time.  A naive
C compiler can be fast enough with careful register declarations and
expression phrasing.  Optimizing compilers for either language should
work just about as well.)
-- 
Tim Maroney, Mac Software Consultant, sun!hoptoad!tim, tim@toad.com

"The Diabolonian position is new to the London playgoer of today, but not to
 lovers of serious literature.  From Prometheus to the Wagnerian Siegfried,
 some enemy of the gods, unterrified champion of those oppressed by them, has
 always towered among the heroes of the loftiest poetry."
    - Shaw, "On Diabolonian Ethics"

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

In article <9433@hoptoad.uucp> tim@hoptoad.uucp (Tim Maroney) writes:
>In article <3899@atr-la.atr.co.jp> alain@atr-la.atr.co.jp (Alain de Cheveigne)
>writes:
>>	/* saveBits is a Handle containing a BitMap record
>>	  followed by the bit image data.  It needs locking
>>	  because CopyBits can move memory. */
>>
>>	state = HGetState((Handle) saveBits);
>>	HLock((Handle) saveBits);
>>	fixBitMap(saveBits);
>>	CopyBits(&(w->portBits),*saveBits,
>>		&(w->portRect),&(**saveBits).bounds,srcCopy,NULL);
>>	HSetState((Handle) saveBits, state);	/* restore state */
>>
>>The "contents of the block" in the quotation above would refer here to
>>the BitMap record and its associated bit image data.  Using the "copy
>>to temp" method to avoid locking would require a big piece of stack or
>>non-relocatable heap, time to copy, and the faith that whatever you
>>use to copy won't scramble the heap.  BlockMove() is said to be safe,
>>but I'm less sure about calling strcpy() on a string in a Handle.
>
>A bitmap is only 14 bytes.  The compiler can move it using three
>MOVE.Ls and a MOVE.W.  I'm pretty sure that's faster than calling a
>trap -- that is, even the overhead of trap dispatch is more expensive
>than that, to say nothing of what the trap does once you get to it.

If you read carefully the comment that opens the code, you'll see that
the Handle saveBits contains a bitmap *and* the associated bit image
data, pointed to by the baseAddr field.  This is much larger than 14
bytes.  

The purpose of the fixBitMap() routine is to make sure the baseAddr
field is correct once the Handle is locked.

I might have added that this code illustrates a point I tried to make
earlier.  Suppose that the implementation of fixBitMap() requires
locking saveBits (it doesn't really, but we're supposing), and uses
HLock() and HUnlock():

	void fixBitMap(bitMapH)
	BitMap **bitMapH;
	{
		HLock(bitMapH);
		[do stuff]
		HUnlock(bitMapH);	/* don't do this */
	}

In the calling code, saveBits remains unlocked for CopyBits(), and we
die.  This is an insidious bug, because the function call hides the
unlocking from the calling code.

This is why I advocate the use of HGetState() and HSetState(), in
*all* cases where locking/unlocking is called for.  You never know
where your code will be called from.

And I wish someone had told me when I started out programming the Mac.


>>I'm less worried about WindowPtrs because they are all the same size, and
>>I expect new ones to tend to occupy the slots left by old ones.
>
>Well, maybe and maybe not.  I throw caution to the winds and go ahead
>and allocate all my windows in the heap, but it's just bad design in
>the MacOS that they aren't relocatable, and they *can* cause heap
>fragmentation.  

I think WindowPtrs are the only Pointers I allocate dynamically (and
DialogPtrs, but dialogs usually go away soon).  I read somewhere that
the Memory Manager puts pointers as far down in memory as possible.
So I think that that part of the heap should consist mainly of
WindowPtrs, or holes of the same size.  But I haven't checked.  

I wonder if there's a better way.


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

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

In article <2632@draken.nada.kth.se>, d88-jwa@nada.kth.se (Jon Watte) writes:
>In article <3889@atr-la.atr.co.jp> alain@atr-la.atr.co.jp (Alain de Cheveigne) writes:
>>By this reasoning, WindowPtrs should have been Handles, since the user 
>>can open and close them in any order.  

To be precise, I should have written, "allocate and deallocate"
instead of "open and close", since a "closed" window (e.g. hidden with
HideWindow())can still be allocated.  Mais vous m'aviez compris...

>Well, for "historical reasons" they aren't...
>
>A good thing to do is to
>
>...
>  tempH = NewHandle(sizeof(WindowRecord));
>  MoveHHi(tempH);
>  HLock(tempH);
>  theWindow = GetNewWindow(4711, (GrafPtr) *tempH, 0L);
>  SetWRefCon(theWindow, tempH);
>
>...
>
>  tempH = GetWRefCon(theWindow);
>  CloseWindow(theWindow);
>  DisposHandle(tempH);
>
>But, alas, that means you can't use the WRefCon for something else...

Wow!  I always wanted to put a window record in a Handle, but never
dared.  Do you actually sometimes unlock the Handle and let it float?

If you don't, I fail to see the advantage of a non-relocatable block
at the top of the heap over one at the bottom.  

If you do unlock, don't you risk confusing the Window Manager or
Multifinder when the block actually moves?

Using the refcon is not a big problem, because you can make it point
to a structure (preferably relocatable) that contains a field that
points back to your Handle, along with other fields for whatever else
you need to associate with the window.



Alain de Cheveigne
atr-la.atr.co.jp

tim@hoptoad.uucp (Tim Maroney) (01/01/90)

In article <3902@atr-la.atr.co.jp> alain@atr-la.atr.co.jp (Alain de Cheveigne)
writes:
>>  tempH = NewHandle(sizeof(WindowRecord));
>>  MoveHHi(tempH);
>>  HLock(tempH);
>>  theWindow = GetNewWindow(4711, (GrafPtr) *tempH, 0L);
>>  SetWRefCon(theWindow, tempH);
>>
>>  tempH = GetWRefCon(theWindow);
>>  CloseWindow(theWindow);
>>  DisposHandle(tempH);
>>
>>But, alas, that means you can't use the WRefCon for something else...
>
>Wow!  I always wanted to put a window record in a Handle, but never
>dared.  Do you actually sometimes unlock the Handle and let it float?

That's expressly forbidden.  Some people were doing this in their grow
zone routines before the II, and their code broke under color systems.
The auxiliary window list counts on the pointers staying the same.  I
suppose you could fix up the auxiliary window list pointers now, but
then Apple could institute yet another table that stashes window
pointers (perhaps in the Layer Manager) and you'd be SOL again.

>If you don't, I fail to see the advantage of a non-relocatable block
>at the top of the heap over one at the bottom.  

I don't understand that either.  In fact, it looks as if you're likely
to be creating big holes at the top of the heap.  I don't see why it's
better to have them at the top.

>If you do unlock, don't you risk confusing the Window Manager or
>Multifinder when the block actually moves?

Spot on, old chap.

>Using the refcon is not a big problem, because you can make it point
>to a structure (preferably relocatable) that contains a field that
>points back to your Handle, along with other fields for whatever else
>you need to associate with the window.

I don't know why your correspondent said that you can't use the window
refCon for anything else if you use it to store this handle.  First,
you don't have to store the handle at all; you can use RecoverHandle on
the window pointer to find the handle.  And as you pointed out, if you
do store it, it can be stored in a struct stored in the refCon.
-- 
Tim Maroney, Mac Software Consultant, sun!hoptoad!tim, tim@toad.com

These are not my opinions, those of my ex-employers, my old schools, my
relatives, my friends, or really any rational person whatsoever.

casseres@apple.com (David Casseres) (01/03/90)

In article <3875@atr-la.atr.co.jp> alain@atr-la.atr.co.jp (Alain de 
Cheveigne) writes:
> From: shebanow@Apple.COM (Andrew Shebanow):
> >It is true that the lists of traps that may move or purge memory are out
> >of date, and that traps which currently are considered "safe" may become
> >"unsafe" sometime in the future. Our recommendation is that developers
> >err on the side of caution: if you are passing a dereferenced field
> >of a handle by address, either use a temporary or lock the handle. I
> >do feel that there are some calls which are "safe" to use dereferenced
> >handles with, like BlockMove, some string manipulation calls, etc. Use
> >your better judgment (or "Toolbox Karma", if you will).
> 
> Which is pretty lame.  Either a call is safe or it isn't.  If DTS
> can't guarantee it, who am I to judge, and how?  Trial and error?

A big part of the problem is patches.  Apple can (in principle) tell you 
exactly which traps are safe, but this doesn't help if your code winds up 
running on a system where safe traps have been patched by inits or other 
third-party code, and the patches are not safe.  Personally, I assume that 
no trap call is "safe."

> Other (non-ROM) subroutines are supposedly safe if they don't call the
> ROM.  But to be sure of this, you have to know what's inside a
> routine.  This defeats the principle of information hiding, so the
> code tends to be unreliable (even Apple can't seem to keep track of
> such side effects).
> 
> In addition, a call to a "harmless" subroutine can move memory if the
> subroutine is in an unloaded segment.  Bottom line: don't trust *any*
> routine, either Apple's or your own.

That's the bottom line, all right, but I want to point out that a great 
deal of pain can be avoided by a style of code design that finesses the 
problem by passing around handles, not pointers.  The whole point of a 
handle is that it is always valid.  Pass handles, and dereference them 
only when you have to; then you only have to lock occasionally.  Design 
your heap data structures accordingly.

In the kind of code you're worrying about, I'd say the overhead of 
dereferencing a handle many times instead of just once is going to be less 
than the overhead of a great deal of locking and unlocking.  Certainly the 
cognitive overhead is going to be less, and readability will be much 
better.

It's true that the Toolbox, especially older parts like QuickDraw, doesn't 
always encourage this style of coding, but this is not a major problem.

David Casseres

Exclaimer:  Hey!