itod / pegkit

'Parsing Expression Grammar' toolkit for Cocoa/Objective-C
MIT License
392 stars 37 forks source link

Random hang on startup #41

Closed drekka closed 7 years ago

drekka commented 7 years ago

This may very well be something I've done, but I'm not sure how to diagnose it.

Randomly my startup of the app will hang. Always at the location in the attached screen shot. I'm using PEGKit on a background thread during startup so my thought at the moment is that there is some sort of lock kicking in. I'm just not sure what and where.

screen shot 2016-10-24 at 5 07 44 pm
itod commented 7 years ago

This is expected behavior. PEGKit uses exceptions for control flow. I know you are rolling your eyes right now, because everyone "knows" that using exceptions for control flow is the Wrong Way to Do Things.

Well, for most cases that advice ("don't use exceptions for control flow") is, of course, correct. However, PEGKit produces recursive-descent parsers with infinite look-ahead. When you use recursion to produce infinite look-ahead, you often have to backtrack up the stack after attempting an unsuccessful parsing path (in fact, this is happening constantly during parsing).

And the most elegant (perhaps only manageable?) way to control this type of flow is via exceptions.

For the (practical) research behind this theory, see Terence Parr's excellent Language Implementation Patterns. Parr is the creator of the industry-leading parsing toolkit ANTLR, which is a major influence on PEGKit's design.

Terence Parr's Slide deck

There's really no problem with this technique except one. And you've just discovered it. You basically cannot have an "All ObjC Exceptions" breakpoint enabled while your parsing is executing. That sucks, but it can be worked around fairly easily. You'll just have to disable that breakpoint (or all breakpoints) while this part of your app is running.

What I also do to work around this is to sprinkle Assertions all over my code (I think that is good advice anyway), and then I set a breakpoint for: -[NSAssertionHandler handleFailureInMethod:object:file:lineNumber:description:]. I leave breakpoints enabled, but turn off the "All ObjC Exceptions" breakpoint when dealing with code that parses.

drekka commented 7 years ago

HI Todd

Not sure I was clear. I don't have an all exceptions breakpoint active. I already figured out PEGKit was using exceptions for control flow previously so had it turned off.

This situation is randomly occurring. The app simple hangs at this point. Even with no breakpoints set at all. I'm leaning towards this being a Xcode beta issue, but I'm not sure. I think the breakpoint logged is actually coming from Reveal which is failing to start. So this whole thing might have nothing to do with PEGKit. It's just weird that it always seems to be hung on the throw.

I did some reading around the interwebs about what you say regarding exceptions for control flow. I'm expert, but from what I read (please excuse if this is incorrect :-) it appears that with 64bit code, entering a @try block is cheap, but throwing an exception is expensive. I also read some discussions about how throwing exceptions can lead to memory leaks as variables before the exception may not be cleaned up correctly, and that this can be a problem with ARC code. Which if true, could mean issues if you try and convert PEGKit to ARC.

I can see what you mean by having to litter the code with if's if you don't use exceptions. And thinking about it, it occurred to me that using something like Promise based patterns might solve the whole problem of multiple if's and the problem of having to use exceptions. Then again, without going into it, something like that might introduce a whole new set of problems.