[comp.sys.mac.programmer] HLock

mikeoro@hubcap.clemson.edu (Michael K O'Rourke) (12/16/89)

I know this has been discussed before, but could someone tell me when
it is necessary to use HLock?  If i have a handle within a CDEF do i need
to do so?  What if i only access the handle thru a pointer, i.e. :

void doSomething(theControl)
ControlHandle theControl;
{
ControlPtr cp;

cp = *theControl;
if (cp->contrlRect ...... and so on.......


Thanks,
Michael

6500stom@hub.UUCP (12/16/89)

> I know this has been discussed before, but could someone tell me when
> it is necessary to use HLock?  If i have a handle within a CDEF do i need
> to do so?  What if i only access the handle thru a pointer, i.e. :
> 
> void doSomething(theControl)
> ControlHandle theControl;
> {
> ControlPtr cp;
> 
> cp = *theControl;
> if (cp->contrlRect ...... and so on.......

It doesn't matter whether you dereference it or not in C.  You might
as well have done (**theControl).contrlRect instead of cp->contrlRect.
The way to remember if you need to lock your handle is if you pass
a field in the handle to any procedure that might move memory--even
if its one of your own routines in a different segment.
For example, if you do
RectRgn(myRgn, (**theControl).contrlRect) you need to lock theControl
down because RectRgn might move the handle before its done with the
contrlRect.
/            Josh Pritikin             T Ignorance is bliss.           \
| Mail:      6500stom@ucsbuxa.ucsb.edu | I must be in hell.            |
| AppleLink: Josh.P                    | My room is proof that entropy |
\ GEnie:     J.Pritikin                ! exists.                       /

6600pete@hub.UUCP (12/16/89)

From article <3331@hub.UUCP>, by 6500stom@hub.UUCP ():
> The way to remember if you need to lock your handle is if you pass
> a field in the handle to any procedure that might move memory--even
> if its one of your own routines in a different segment.

Oh, Joshling, I can't bear it. I tell you how to post and immediately you
go spreading misinformation! :-)

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. For
the record, the following situations involve locked handles:

   passing elements of records pointed to by dereferenced handles as Pascal
      "var" parameters
   assigning return values from function-style traps to elements of records
      pointed to by dereferenced handles
   inside Pascal "with" statements which include handles in the "with"

Can anyone think of any other situations?
-------------------------------------------------------------------------------
Pete Gontier   | InterNet: 6600pete@ucsbuxa.ucsb.edu, BitNet: 6600pete@ucsbuxa
Editor, Macker | Online Macintosh Programming Journal; mail for subscription
Hire this kid  | Mac, DOS, C, Pascal, asm, excellent communication skills

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

In article <3331@hub.UUCP> 6500stom@hub.UUCP writes:
>> I know this has been discussed before, but could someone tell me when
>> it is necessary to use HLock?  If i have a handle within a CDEF do i need
>> to do so?  What if i only access the handle thru a pointer, i.e. :
>> 
>> ControlPtr cp;
>> cp = *theControl;
>> if (cp->contrlRect ...... and so on.......
>
>It doesn't matter whether you dereference it or not in C.  You might
>as well have done (**theControl).contrlRect instead of cp->contrlRect.

Sort of.  I would never do this, and I strongly recommend against it.
As soon as anything happens which could move memory, cp becomes an
invalid pointer, and has to be refreshed from theControl.  It would be
awfully easy to forget this; it's asking for errors which wouldn't happen
if you always used (*theControl)-> instead of cp-> .

So, when do you need to lock down handles?  Very rarely.  You cited the
main case, Josh:

>The way to remember if you need to lock your handle is if you pass
>a field in the handle to any procedure that might move memory--even
>if its one of your own routines in a different segment.
>For example, if you do
>RectRgn(myRgn, (**theControl).contrlRect) you need to lock theControl
>down because RectRgn might move the handle before its done with the
>contrlRect.

Another case, which some people may remember I posted an ill-considered
flame about a few months ago, is:

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

However, I don't like to lock handles when it can be easily avoided.
I would do your case without locking by using a temp variable, and the
same for my case.

Rect r;
r = (*theControl)->contrlRect;
RectRgn(myRgn, &r);

and

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.

One final point: heap scramble mode in a debugger, while very slow, is
excellent at discovering when you have not respected handle
dereferencing issues.  It is a good idea to do as complete a test run
as you can stand under heap scramble every few weeks or even days.
-- 
Tim Maroney, Mac Software Consultant, sun!hoptoad!tim, tim@toad.com

"Our newest idol, the Superman, celebrating the death of godhead, may be
 younger than the hills; but he is as old as the shepherds."
    - Shaw, "On Diabolonian Ethics"

freek@fwi.uva.nl (Freek Wiedijk) (12/17/89)

In article <3332@hub.UUCP> 6600pete@hub.UUCP 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.

You mean that nowadays even BlockMove moves memory? :-)

I would think that if I patch a trap that may NOT move memory, and that
patch moves memory, then that patch has a bug!
--
Freek "the Pistol Major" Wiedijk                  Path: uunet!fwi.uva.nl!freek
#P:+/ = #+/P?*+/ = i<<*+/P?*+/ = +/i<<**P?*+/ = +/(i<<*P?)*+/ = +/+/(i<<*P?)**

omullarn@oracle.oracle.com (Oliver Mullarney) (12/19/89)

In article <9313@hoptoad.uucp> tim@hoptoad.UUCP (Tim Maroney) writes:
>
>Rect r;
>r = (*theControl)->contrlRect;
>RectRgn(myRgn, &r);
>
as a way of avoiding using HLock and HUnlock. Not in this case. What you
are handing to RectRgn is a pointer into a relocatable block. It may look
like a legitimate pointer, but it is not, so this *can* crap out.

HLock and HUnlock are probably the best solution in this case, or the
HGetState and HSetState calls. 

| Oliver Mullarney     |  "I have knowledge of Digital Watches,  and soon I   |
| Oracle Corporation   |   shall have knowledge of Video Cassette Recorders"  |
| omullarn@oracle.com  |                                      Time Bandits    |
 --------------- "Universally acknowledged to work just fine" ----------------
| Oliver Mullarney     |  "I have knowledge of Digital Watches,  and soon I   |
| Oracle Corporation   |   shall have knowledge of Video Cassette Recorders"  |
| omullarn@oracle.com  |                                      Time Bandits    |
 --------------- "Universally acknowledged to work just fine" ----------------

6600pete@hub.UUCP (12/19/89)

From article <292@fwi.uva.nl>, by freek@fwi.uva.nl (Freek Wiedijk):
> I would think that if I patch a trap that may NOT move memory, and that
> patch moves memory, then that patch has a bug!

I agree. Unfortunately, work-arounds are a fact of life. Fortunately, this
particular work-around doesn't introduce any more kludgy behavior, as
work-arounds are wont to do.
-------------------------------------------------------------------------------
Pete Gontier   | InterNet: 6600pete@ucsbuxa.ucsb.edu, BitNet: 6600pete@ucsbuxa
Editor, Macker | Online Macintosh Programming Journal; mail for subscription
Hire this kid  | Mac, DOS, C, Pascal, asm, excellent communication skills

earleh@eleazar.dartmouth.edu (Earle R. Horton) (12/19/89)

In article <1989Dec19.001739.17771@oracle.com> omullarn@oracle.com
	(Oliver Mullarney) writes:
...
>HLock and HUnlock are probably the best solution in this case, or the
>HGetState and HSetState calls. 

     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.

Earle R. Horton
ZZ
:wq
.STOP
^C^C^C  ^Z^Z^Z exit/save
(emacs-save-files-and-die nil t)

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

In article <9313@hoptoad.uucp> tim@hoptoad.UUCP (Tim Maroney) writes:
>>Rect r;
>>r = (*theControl)->contrlRect;
>>RectRgn(myRgn, &r);

In article <1989Dec19.001739.17771@oracle.com> omullarn@oracle.com
(Oliver Mullarney) writes:
>as a way of avoiding using HLock and HUnlock. Not in this case. What you
>are handing to RectRgn is a pointer into a relocatable block. It may look
>like a legitimate pointer, but it is not, so this *can* crap out.

Wrong.  Look at the code again.  It's a pointer to a rectangle on the
stack, which is in no way relocatable.  Copying the rectangle out of the
relocatable block onto the stack prevents the passing of a pointer into
a relocatable block.  You seem to have read this as "Rect *r;
r = &(*theControl)->contrlRect; RectRgn(myRgn, r);" which would
indeed be unsafe -- but that's not what I typed.

>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.
-- 
Tim Maroney, Mac Software Consultant, sun!hoptoad!tim, tim@toad.com

FROM THE FOOL FILE:
"The negro slaves of the South are the happiest, and, in some sense, the
 freest people in the world.  The children and the aged and infirm work not
 at all, and yet have all the comforts and neccessaries of life provided for
 them." -- George Fitzhugh, CANNIBALS ALL! OR, SLAVES WITHOUT MASTERS, 1857

lippin@skippy.berkeley.edu (The Apathist) (12/19/89)

Recently 6600pete@hub.UUCP wrote:
>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. For
>the record, the following situations involve locked handles:
>
>   passing elements of records pointed to by dereferenced handles as Pascal
>      "var" parameters
>   assigning return values from function-style traps to elements of records
>      pointed to by dereferenced handles
>   inside Pascal "with" statements which include handles in the "with"
>
>Can anyone think of any other situations?

How about: 
    In any piece of code that may be interrupted by some task that
	makes a toolbox call.

Which, of course, is anything that runs without masking out
interrupts.

Seriously, if this were DTS's position, they would be saying that it's
never legal to look into an unlocked handle.  Coding around this would
impose ridiculous penalties on performance.  Luckily, they don't have
to go this far, because we can be sure that interrupt routines don't
move or purge handles.  Why?  Because they don't use traps that are on
the danger list.

On the other hand, they do want to say that nothing is forever -- once
upon a time, SysBeep was a memory-safe trap.  When all else fails,
they just have to rely on your toolbox karma.

					--Tom Lippincott
					  lippin@math.berkeley.edu

"Just a moment... Just a moment... I'm picking up a fault in the AE-35
 subunit.  It will go one hundred percent failure within seventy-two hours."
					--HAL

jwright@cs.iastate.edu (Jim Wright) (12/20/89)

tim@hoptoad.UUCP (Tim Maroney) writes:
| One final point: heap scramble mode in a debugger, while very slow, is
| excellent at discovering when you have not respected handle
| dereferencing issues.  It is a good idea to do as complete a test run
| as you can stand under heap scramble every few weeks or even days.

I just checked this, but I'm seeking confirmation.  Think Pascal does
NOT have a heap scramble mode.  T/F?  What is the best way to get this
heap scramble?

--
Jim Wright
jwright@atanasoff.cs.iastate.edu

6600pete@hub.UUCP (12/20/89)

From article <1989Dec19.091303.13316@agate.berkeley.edu>, by lippin@skippy.berkeley.edu (The Apathist):
>>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.

> Seriously, if this were DTS's position...

I've seen it twice now posted by DTS employees with no disclaimers
attached. With explicit declarations that it was official dogma,
no less. (No, I don't log these things to disk.)

I know it sounds ugly, but that's what's been said.

Perhaps it IS the case that one should never write into an unlocked
hanlde by any means. (Note: I said PERHAPS.)

Additional input from DTS would be helpful, I'm sure.
-------------------------------------------------------------------------------
Pete Gontier   | InterNet: 6600pete@ucsbuxa.ucsb.edu, BitNet: 6600pete@ucsbuxa
Editor, Macker | Online Macintosh Programming Journal; mail for subscription
Hire this kid  | Mac, DOS, C, Pascal, asm, excellent communication skills

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

In article <3373@hub.UUCP> 6600pete@hub.UUCP writes:
>From article <1989Dec19.091303.13316@agate.berkeley.edu>, by lippin@skippy.berkeley.edu (The Apathist):
>>>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.
>Additional input from DTS would be helpful, I'm sure.
>-------------------------------------------------------------------------------
>Pete Gontier   | InterNet: 6600pete@ucsbuxa.ucsb.edu, BitNet: 6600pete@ucsbuxa
>Editor, Macker | Online Macintosh Programming Journal; mail for subscription
>Hire this kid  | Mac, DOS, C, Pascal, asm, excellent communication skills

Your wish is my command...

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

Tim Maroney is absolutely correct about the efficiency of temp variables
vs HLock/HGetState calls: temps are much, much faster. Also, people who
are doing code like this:

  HLock(h);
  p = *h;
  p->x = 1;
  p->y = 2;
  HUnlock(h);

are wasting cycles. The HLock/Unlock here is totally unnecessary, since
the compiler isn't going to generate code which will move or purge memory.

For more information about unsafe references, I would encourage everyone
to read Scott Knasters most excellent book, "How to Write Macintosh
Software."

Have fun,

Andrew Shebanow
MacDTS

chewy@apple.com (Paul Snively) (12/21/89)

In article <3373@hub.UUCP> 6600pete@hub.UUCP writes:
> From article <1989Dec19.091303.13316@agate.berkeley.edu>, by 
lippin@skippy.berkeley.edu (The Apathist):
> >>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.
> 
> > Seriously, if this were DTS's position...
> 
> I've seen it twice now posted by DTS employees with no disclaimers
> attached. With explicit declarations that it was official dogma,
> no less. (No, I don't log these things to disk.)
> 
> I know it sounds ugly, but that's what's been said.
> 
> Perhaps it IS the case that one should never write into an unlocked
> hanlde by any means. (Note: I said PERHAPS.)
> 
> Additional input from DTS would be helpful, I'm sure.

Then again, maybe not...

To the best of my knowledge, DTS hasn't formed an official position with 
locking and unlocking handles other than that you definitely need to do it 
if the trap(s) involved might shuffle memory around.

We DO tend to tell people that with all the evil patches running around 
out there that you can't possibly know a priori whether a trap might move 
memory or not, so just code defensively.  Assume the worst.  That sort of 
thing.  It's more a "you might wanna consider this" than a "don't do it."

As has been pointed out in earlier posts, using a debugger with a heap 
scramble option can be very helpful.  Generally speaking, a heap scramble 
function will ALWAYS shuffle the heap anytime that the heap COULD BE 
shuffled.  The definition of this phrase is implementation-dependent; in 
TMON's case, it means whenever the handful of traps that could move memory 
are called either directly or indirectly.

Another good thing to do is to have some utility (a debugger or an INIT) 
that stuffs address zero with some magical longword that's guaranteed to 
cause a Bus Error if accessed on any machine.  It's useful for tracking 
down those intermitent NIL-handle/pointer references.

__________________________________________________________________________
Just because I work for Apple Computer, Inc. doesn't mean that they 
believe what I believe or vice-versa.
__________________________________________________________________________
C++ -- The language in which only friends can access your private members.
__________________________________________________________________________

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

in article <5896@internal.Apple.COM>$@$G(Jchewy@apple.com (Paul Snively) says:

>Another good thing to do is to have some utility (a debugger or an INIT) 
>that stuffs address zero with some magical longword that's guaranteed to 
>cause a Bus Error if accessed on any machine.  It's useful for tracking 
>down those intermitent NIL-handle/pointer references.

My own Christmas wish is a program that you could run under MF with
the program you're debugging, and that would allocate all the
remaining memory and check it for over-writing.

The idea that a bug from my program might be walking all over another
program's memory space (or vice-versa) is unnerving.



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