Tuesday, April 25, 2006

Don't RETURN Inside WITH

At GLGDW, I mentioned during Marcia Akins' excellent Best Practices for Class Design session that one of the leading causes of C5 errors is using RETURN inside WITH structures. Given the number of people that came up to me after the session, this clearly isn't well-known, so here's the scoop.

I'm not sure exactly what happens when you use a WITH structure, but clearly VFP stores a reference to the object specified in the WITH statement somewhere. Obviously, the reference must be removed at some point or else the object couldn't release, but I suspect there's a memory leak under these conditions, and that when enough of these memory leaks happen, you end up with a C5 error. The insidious thing about memory leaks is that the C5 error can occur far away in both code and time from the original source, so they're next to impossible to track down for mere mortals like me who don't do C code debugging.

I first started looking into this about 18 months ago. We had fairly regular reports of C5 errors from people going into or out of the Report Designer from within Stonefield Query. The problem is that it wasn't reproducible -- I could never get it to happen when I tried (I did have it happen a couple of times when I didn't want it to, such as during demos!). I went through the code related to the Report Designer with a fine-tooth comb and couldn't see anything that could cause this. Then I remembered some weird behavior from years earlier: if I used RETURN within a WITH statement, under some conditions, the object specified wouldn't release. That problem was fixed in a later version of VFP, so I'd forgotten about it, but it occurred to me that I'd used that a lot in my code even though it really isn't a good practice. So, I spent a day or two refactoring every single instance of RETURN inside WITH so the RETURN statement was after the ENDWITH. Since I couldn't reproduce the C5 errors on demand, it was hard to know whether this worked or not, but we haven't had a single C5 error reported since we released that version of Stonefield Query. And in thinking about what may be going on underneath the covers, it makes sense to me that this was likely the culprit.

So, a heads-up for everyone: if you're getting C5 errors and are pulling your hair out trying to track them down, look at all your WITH structures and move any RETURN statements below the ENDWITH. Not only is it good programming practice, it may kill those maddening problems.


Anonymous said...

Thank you, thank you so much !
I've been dealing with this problem since more than a year, happening maybe 3 times in a month, in several machines.

Just checked the code... and voila !

Thanks for sharing !
Cesar Chalom

Mike Feltman said...

FWIW, RETRY and COMRETURNERROR can cause the same problem.

Anonymous said...

And the thought occurred to me today that this means you should not use WITH..ENDWITH inside a TRY..CATCH structure as hitting an exception would certainly jump past the ENDWITH without executing it. Unless TRY..CATCH is smart enough to do proper garbage collection.