[comp.sys.mac.programmer] Another THINK Pascal gotcha - "With" instance vars

a_dent@fennel.cc.uwa.oz.au (01/25/91)

I found another way to crash Think Object Pascal applications!!!

In the manual, they warn against passing instance variables by reference, as
they may move by the time the called routine returns.  This is a very easy trap
to fall into, despite the warning.

They DIDN'T extend the warning to cover the "with" statement.

As every keen Mac Pascal programmer knows, if you dereference a handle using
"with handleName^^ do begin ...." you must lock the handle beforehand.

The same applies to using "with" to dereference record structures of instance
variables.  Structured instance variables are still part of a handle (the 
object itself) and so must be locked before the "with", using a 
HLock(handle(SELF)).

This is a far subtler bug than passing instance variables by reference as
the window of vulnerability is just whilst executing the code inside the
"with" and may be quite small (depending on what you call).  However, even
if you don't call things that move memory, the presence of networking drivers
such as Tops can cause memory moves inside ANY sequence of code.

We only found this one because the client started noticing crashes when they
added an LC and TOPS.

I suspect that the THINK environment does a lot of locking of objects behind
the scenes so this one probably also qualifies as a "only crashes compiled
applications" scenario.

Andy Dent                     A.D. Software phone 09 249 2719
Mac & VAX programmer          94 Bermuda Dve, Ballajura
a_dent@fennel.cc.uwa.oz       Western Australia  6066     
a_dent@fennel.cc.uwa.oz.AU (international)

jmunkki@hila.hut.fi (Juri Munkki) (01/26/91)

In article <1991Jan25.233122.2825@fennel.cc.uwa.oz.au> a_dent@fennel.cc.uwa.oz.au writes:
>"with" and may be quite small (depending on what you call).  However, even
>if you don't call things that move memory, the presence of networking drivers
>such as Tops can cause memory moves inside ANY sequence of code.

Most of us know that this is not true. It might be true of TOPS (I never
liked that product, so it's easy to convince me that it has bugs in it),
but in general drivers and other interrupt level routines do not move
memory manager blocks. You have to call a toolbox routine (and not just
any routine, although nowadays it's considered bad style to assume that
some routines do not move memory) to cause a change in memory manager
structures.

Even Inside Macintosh warns Pascal programmers about "with", so it really
shouldn't be a gotcha. It's just a common bug. Use a heap scrambler along
with discipline to find the bugs.

   ____________________________________________________________________________
  / Juri Munkki	    /  Helsinki University of Technology   /  Wind  / Project /
 / jmunkki@hut.fi  /  Computing Center Macintosh Support  /  Surf  /  STORM  /
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

siegel@endor.uucp (Rich Siegel) (01/26/91)

In article <1991Jan25.233122.2825@fennel.cc.uwa.oz.au> a_dent@fennel.cc.uwa.oz.au writes:
>I found another way to crash Think Object Pascal applications!!!
>
>In the manual, they warn against passing instance variables by reference, as
>they may move by the time the called routine returns.  This is a very easy trap
>to fall into, despite the warning.
>
>They DIDN'T extend the warning to cover the "with" statement.

	Sorry. :-) I'll see that it gets into the doc.

	Also, calling New( ) for an instance variable is a good way to
crash, since it involves taking the address of the instance variable. This
will be fixed in the next version of the compiler...

>I suspect that the THINK environment does a lot of locking of objects behind
>the scenes so this one probably also qualifies as a "only crashes compiled
>applications" scenario.

	In fact, the debugger does absolutely nothing "behind the scenes";
crashes like this are usually purely random.

R.



 Rich Siegel	Symantec Languages Group  Internet: siegel@endor.harvard.edu

"...she's dressed in yellow, she says 'Hello, come sit next to me, you
fine fellow..."

a_dent@fennel.cc.uwa.oz.au (01/29/91)

In article <1991Jan25.234200.7536@santra.uucp>, jmunkki@hila.hut.fi (Juri Munkki) writes:
> In article <1991Jan25.233122.2825@fennel.cc.uwa.oz.au> a_dent@fennel.cc.uwa.oz.au writes:
>>"with" and may be quite small (depending on what you call).  However, even
>>if you don't call things that move memory, the presence of networking drivers
>>such as Tops can cause memory moves inside ANY sequence of code.
> 
> Most of us know that this is not true. It might be true of TOPS (I never
> liked that product, so it's easy to convince me that it has bugs in it),
> but in general drivers and other interrupt level routines do not move
> memory manager blocks. You have to call a toolbox routine (and not just
> any routine, although nowadays it's considered bad style to assume that
> some routines do not move memory) to cause a change in memory manager
> structures.
This was my opinion.  Nevertheless, it moves!!  Anyway, I'm unlikely to have the
hours to sit and watch the heap and Heisenberg would probably get in the way.
Is there anyway a request to extend the System Heap could be caused by TOPS
and consequently affect the Application heap?  

> 
> Even Inside Macintosh warns Pascal programmers about "with", so it really
> shouldn't be a gotcha. It's just a common bug. Use a heap scrambler along
> with discipline to find the bugs.

I know about "with" and the dangers of handles etc.  The big point was that 
you can take some perfectly safe code (using "with" on structures that are
local variables) and if you put it into an Object Pascal method, operating on
instance variables, it is suddenly dangerous.  I think it is more dangerous
than passing instance variables as VAR parameters, because the "with"
window of vulnerability is so much smaller and so it is harder to track down.

I'd really like to see these two problems caught by the compiler, I can't see
it being too hard a job to parse :-|)  (hopeful big "cheesy grin", Rich).

> 
>    ____________________________________________________________________________
>   / Juri Munkki	    /  Helsinki University of Technology   /  Wind  / Project /
>  / jmunkki@hut.fi  /  Computing Center Macintosh Support  /  Surf  /  STORM  /
>  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Andy Dent                     A.D. Software phone 09 249 2719
Mac & VAX programmer          94 Bermuda Dve, Ballajura
a_dent@fennel.cc.uwa.oz       Western Australia  6066     
a_dent@fennel.cc.uwa.oz.AU (international)

a_dent@fennel.cc.uwa.oz.au (01/29/91)

In article <5479@husc6.harvard.edu>, siegel@endor.uucp (Rich Siegel) writes:
> 
> 	Also, calling New( ) for an instance variable is a good way to
> crash, since it involves taking the address of the instance variable. This
> will be fixed in the next version of the compiler...

Thanks a zillion - I do this all over the place!
(I think my app will probably survive a Heapscramble now, will try it & let
the net know if it still doesn't - there may be yet another "vulnerability"
out there in the undergrowth, Bud.)

> 
>>I suspect that the THINK environment does a lot of locking of objects behind
>>the scenes so this one probably also qualifies as a "only crashes compiled
>>applications" scenario.
> 
> 	In fact, the debugger does absolutely nothing "behind the scenes";
> crashes like this are usually purely random.
I have to admit a statician would sneer at the size of the sample on which I
ventured this opinion :-)

> 
>  Rich Siegel	Symantec Languages Group  Internet: siegel@endor.harvard.edu
Andy Dent                     A.D. Software phone 09 249 2719
Mac & VAX programmer          94 Bermuda Dve, Ballajura
a_dent@fennel.cc.uwa.oz       Western Australia  6066     
a_dent@fennel.cc.uwa.oz.AU (international)

keith@Apple.COM (Keith Rollin) (01/29/91)

In article <1991Jan25.233122.2825@fennel.cc.uwa.oz.au> a_dent@fennel.cc.uwa.oz.au writes:
>
>As every keen Mac Pascal programmer knows, if you dereference a handle using
>"with handleName^^ do begin ...." you must lock the handle beforehand.

You probably know this, but your general statement above isn't quite
right. You only need to lock your handle if something in the body of
the statement moves memory. For instance, a statement like the
following is OK:

	WITH myRectHandle^^ DO BEGIN
	    top := 0;
	    left := 0;
	    bottom := 100;
	    right := 200;
	END;

However, the following is bad:

	WITH myGDHandle^^ DO BEGIN
	    gdPMap := NewPixMap;
	    ....
	END;

This is because the WITH statemnt will generate a pointer to the GDevice
stored in the myGDHandle. However, NewPixMap will allocate memory, which
could move the GDevice, invalidating the pointer Pascal has generated to
it.

>...
>This is a far subtler bug than passing instance variables by reference as
>the window of vulnerability is just whilst executing the code inside the
>"with" and may be quite small (depending on what you call).  However, even
>if you don't call things that move memory, the presence of networking drivers
>such as Tops can cause memory moves inside ANY sequence of code.

This isn't quite true, either. Drivers can only move memory at well
defined times (like at accRun time, which is done when you call
SystemTask).  Drivers cannot move memory between two arbitrary
statements in your application, because that would mean that they'd be
moving memory at interrupt time. In other words, something like the
following is OK:

	myPtr := myHandle^;
	myValue := myPtr^;

You don't have to worry about anything - including drivers - moving
memory in between those two statements.

-- 
------------------------------------------------------------------------------
Keith Rollin  ---  Apple Computer, Inc.  ---  Developer Technical Support
INTERNET: keith@apple.com
    UUCP: {decwrl, hoptoad, nsc, sun, amdahl}!apple!keith
"Argue for your Apple, and sure enough, it's yours" - Keith Rollin, Contusions

lsr@Apple.com (Larry Rosenstein) (01/30/91)

In article <1991Jan29.021127.2840@fennel.cc.uwa.oz.au>, a_dent@fennel.cc.uwa.oz.au writes:
> 
> I know about "with" and the dangers of handles etc.  The big point was that 
> you can take some perfectly safe code (using "with" on structures that are
> local variables) and if you put it into an Object Pascal method, operating o

There's nothing in the definition of Object Pascal that requires that these
WITH statements be dangerous.  MPW Pascal handles this properly; it doesn't
load the dereferenced handle into a register, it dereferences it as necessary.

Lawson.English@p88.f15.n300.z1.fidonet.org (Lawson English) (02/01/91)

Keith Rollin writes in a message to All

KR> myPtr := myHandle^;  myValue := myPtr^; 
KR> You don't have to worry about anything - including drivers - 
KR> moving memory in between those two statements.

Correction: you SHOULDN'T have to worry about anything moving memory between
those two statements. Remember to drive defensively... Of course, the best defensive
driving is to not leave the driveway, so one must take a few chances...


Lawson
 

--  
Uucp: ...{gatech,ames,rutgers}!ncar!asuvax!stjhmc!300!15.88!Lawson.English
Internet: Lawson.English@p88.f15.n300.z1.fidonet.org

Jim.Spencer@p510.f22.n282.z1.mmug.edgar.mn.org (Jim Spencer) (02/04/91)

Lawson English writes in a message to All

KR> myPtr := myHandle^;  myValue := myPtr^; 
KR> You don't have to worry about anything - including drivers - 
KR> moving memory in between those two statements.
LE>  Correction: you SHOULDN'T have to worry about anything moving 
LE> memory between those two statements. Remember to drive defensively... 

Huh?  I don't profess to have anywhere near the knowledge of Keith but it sure looks to me like his statement is correct and the programmer shouldn't have to lose a moments sleep over this.  I don't even start to understand why you won't want to waste time "driving defensively" in this situation.  Locking and unlocking myHandle in this circumstance goes beyond careful: its downright wasteful.

If you are automatically, without thinking locking everything down before you dereference it, you are simply wasting both your customers' and your time.  This may not be as bad as the guy here who said he never locked his handles down but its not good practice.
 

--  
Jim Spencer - via The Minnesota Macintosh Users Group UUCP-Fido Gateway
UUCP: ...uunet!tcnet!kksys!edgar!mmug!22.510!Jim.Spencer
INET: Jim.Spencer@p510.f22.n282.z1.mmug.edgar.mn.org

andreww@uniwa.uwa.oz (Andrew John Williams) (02/06/91)

Jim.Spencer@p510.f22.n282.z1.mmug.edgar.mn.org (Jim Spencer) writes:

>Lawson English writes in a message to All

>KR> myPtr := myHandle^;  myValue := myPtr^; 
>KR> You don't have to worry about anything - including drivers - 
>KR> moving memory in between those two statements.
>LE>  Correction: you SHOULDN'T have to worry about anything moving 
>LE> memory between those two statements. Remember to drive defensively... 

>Huh?  I don't profess to have anywhere near the knowledge of Keith but it sure looks to me like his statement is correct and the programmer shouldn't have to lose a moments sleep over this.  I don't even start to understand why you won't want to waste time "driving defensively" in this situation.  Locking and unlocking myHandle in this circumstance goes beyond careful: its downright wasteful.

>If you are automatically, without thinking locking everything down before you dereference it, you are simply wasting both your customers' and your time.  This may not be as bad as the guy here who said he never locked his handles down but its not good practice.
> 

>--  
>Jim Spencer - via The Minnesota Macintosh Users Group UUCP-Fido Gateway
>UUCP: ...uunet!tcnet!kksys!edgar!mmug!22.510!Jim.Spencer
>INET: Jim.Spencer@p510.f22.n282.z1.mmug.edgar.mn.or

What happens if some program running in the background chooses that
moment to do some memory shuffling? Goodbye handle. Remember John's
three rules of Mac programming - 

1) Always set your grafports
2) Always lock your handles
3) Buy a decent keyboard

John West (stealing Andrew's account)
I am NOT a fish, I am NOT a fish
g

claytor@tandem.physics.upenn.edu (Nelson Claytor) (02/07/91)

In article <1991Feb6.060512.12890@uniwa.uwa.oz> andreww@uniwa.uwa.oz 
(Andrew John Williams) writes:
> What happens if some program running in the background chooses that
> moment to do some memory shuffling?

Umm...I don't think that a background program can shuffle *your* heap.  
How about replacing rule #2 with "Always read the Memory Manager chapters 
of Scott Knaster's books." :-)

Nelson

Nelson Claytor
claytor@tandem.physics.upenn.edu

Jim.Spencer@p510.f22.n282.z1.mmug.edgar.mn.org (Jim Spencer) (02/08/91)

Andrew John Williams writes in a message to All

AJW> What happens if some program running in the background chooses 
AJW> that moment to do some memory shuffling? Goodbye handle. Remember 
AJW> John's three rules of Mac programming - 

I fully admit I may misunderstand how Multifinder works but how is a program running in the background going to get time between these assignment statements?
 

--  
Jim Spencer - via The Minnesota Macintosh Users Group UUCP-Fido Gateway
UUCP: ...uunet!tcnet!kksys!edgar!mmug!22.510!Jim.Spencer
INET: Jim.Spencer@p510.f22.n282.z1.mmug.edgar.mn.org

phils@chaos.cs.brandeis.edu (Phil Shapiro) (02/12/91)

In article <1991Feb6.060512.12890@uniwa.uwa.oz> andreww@uniwa.uwa.oz (John West (stealing Andrew's account)) writes:
   [ discussion of HLock'ing during "WITH myHandle^^" ]

   What happens if some program running in the background chooses that
   moment to do some memory shuffling? Goodbye handle. Remember John's
   three rules of Mac programming - 

   1) Always set your grafports
   2) Always lock your handles
   3) Buy a decent keyboard

Please don't spread myths!  Handles don't move unless the Memory
Manager pushes them.  As a Mac programmer, you're promised by the
Memory Manager that this shuffling only occurs at certain times.

What *does* happen if some program running in the background chooses
some random moment to shuffle the heap?

This shuffling will only occur in that application's heap, not your
own.  Besides, here's what Tech Note #180 has to say about context
switching:

"For conceptual clarity, it is best to think of MultiFinder 6.0 and
earlier as using three types of switching: major, minor, and update.
All switching occurs at a well defined times, namely, when a call is
made to either _WaitNextEvent, _GetNextEvent, or _EventAvail."

So, if your application expects memory to move during those three
traps, then you should be OK under MultiFinder.

	-phil

--
   Phil Shapiro                           Technical Support Analyst
   Language Products Group                     Symantec Corporation
		Internet: phils@chaos.cs.brandeis.edu