|
From: | Dr. H. Nikolaus Schaller |
Subject: | Re: does GNUstep support exception as well as apple implementation? |
Date: | Fri, 4 Jun 2010 18:00:38 +0200 |
Am 04.06.2010 um 17:24 schrieb David Chisnall:
Hi Nikolaus, On 4 Jun 2010, at 16:04, Dr. H. Nikolaus Schaller wrote:Hi David, Am 04.06.2010 um 15:30 schrieb David Chisnall:On 4 Jun 2010, at 12:18, Shuduo Sang wrote:Does it mean the exception implementation of GNUstep is not complete as well as Apple runtime?There are two implementations of exception handling for use with GNUstep. One is the old NeXT-style implementation, using setjmp()/ longjmp(), which is slow, unsafe, and needs to die a quick and peaceful death. You may only use this with the NS_DURING family of macros.Can you explain that a little? My view is:Exceptions are exceptions and should never occur in normal operation. So it does not matter that they are slow when they got rised (is this currect English?). Or you have a fundamental design problem.Absolutely - they're called exceptions because they should happen in exceptional circumstances.So, IMHO the only "cost" is the setjmp() hidden by the NS_DURING macro which AFAIK fills an array with some pointers and values.setjmp() is required to dump all of the registers in the CPU into a buffer. This is not hugely expensive, but is a significantly greater cost than nothing. It's typically at least 16 or so instructions (including stores from FPU registers - more if you have a vector unit) every time that you go over an NS_DURING macro, even when no exception is thrown. This costs memory bandwidth, d-cache usage to store the buffer, and i-cache usage to store the string of instructions required for saving the CPU state.
Well, I usually follow a fix-if-you-have-a-problem strategy. So reducing potentially 16 instructions doesn't make it worth changing everything in language, compiler, runtime, code.
In contrast, the zero-cost exceptions cost nothing unless they are actually used. Entering an @try block does not involve executing any instructions. The only cost is a slight increase in the size of the binary, but because the unwinding tables are only used during unwinding they do not need to be loaded into memory unless an exception is thrown.When an exception is thrown with the zero-cost mechanism, the unwind library parses these tables and calls the correct personality function for every frame on the stack, asking it if it has cleanup code or if it has a handler. If a handler is not found, the unwind library just aborts. If there is a handler, then the intervening unwind code is called and then the handler.
Ok. This makes handling exceptions slower.
And what is specifically unsafe with using them? They have been in use for 20 years now.They are unsafe because they do not do anything like graceful unwinding of the stack. They just load a set of CPU registers from a buffer. This gives none of the intervening stack frames a chance to call cleanup code. For example, you can't release a lock or deallocate on-heap temporaries if an exception is thrown over your stack frame.
Ok. If you take care of it, it is fine. You just have to make sure tha t you double-wrap potential situations. E.g.:
NS_DURING lock=[[NSLock] alloc] init]; ... code that may raise an exception [lock unlock]; NS_HANDLER [lock unlock]; // explicit cleanup code[localException raise]; // re-raise after cleanup (i.e. unwind into next stack frame level)
NS_ENDHANDLERThe only thing is that you have to trust that code you don't have control over is following such a cleanup pattern. With the try/catch pattern, probability is lower but it does not prevent programmers to forget the cleanup code that is required. So I would conclude it is not inherently more unsafe than the zero-cost mechanism.
If you mix Objective-C and C++ this is especially bad. If you have C ++ stack frames, they will typically involve things like destructor calls that happen automatically when C++ on-stack variables go out of scope. This also happens with C code if you use __attribute__((cleanup)).
Well, if I had to choose I would more move Obj-C++ to a peaceful death... It leads to bloatware in compilers, debuggers and language concepts. The only Obj-C++ project I had to develop for a customer was a nightmare in debugging. If the client hadn't insisted on using Obj-C+ + I would have rewritten and debugged all the C++ libs he did want to reuse in much less time.
There is a reason why the NeXT documentation recommends against using exceptions for anything other than printing a message and aborting - they leave your program in an undefined state. Using setjmp()/longjmp() instructions is an absolutely terrible idea and should not be done unless there are absolutely no other options.
Unless you do it in the way as I have indicated above.
Note: Windows' SEH also involves a cost for entering and leaving exception blocks, but does not have the other problems of setjmp() exceptions.
Nevertheless, I understand that the try/catch/finally pattern inherited from JAVA is easier to understand and to maintain.
BR, Nikolaus
[Prev in Thread] | Current Thread | [Next in Thread] |