Is fast enumeration exception-safe in files compiled with -fobjc-arc-exceptions? - ios

I'm considering using exceptions for internal error handling inside a library I'm writing. If they are used, the library will be built with arc and f-objc-arc-exceptions enabled.
One issue with obj-c exceptions is that care must be taken not to throw through any code you don't know to be exception-safe. When a file is compiled with -fobjc-arc-exceptions, is the enumeration code added in by the compiler exception safe?
As far as I can tell, it should be, because I can't see any reason for it to do anything that requires cleanup. But I'd be much happier using it if this behaviour is documented somewhere.
Edit: I should clarify that when I say "using exceptions for internal error handling", I'm talking about nonfatal errors. So "exception-safe" here means that there should be no memory leaked, no unreleased locks, etc.

Do not use exceptions for internal error handling.
At best, you'll end up with code that has a different design and feel than the system code. The border between the two will always require maintenance and consideration in that it adds a "which side of the exception behavior fence am I on?"
In reality, it will be a maintenance nightmare. Every time the code is refactored -- potentially to take advantage of new features in the system -- you'll constantly have to maintain the border between the two kinds of exception handling.
At worse, there will be a software update that changes an implementation detail and your app will start crashing.
The libraries were designed to not used exceptions for recoverable errors. Your code should follow the same pattern if you want to minimize the cost of ownership of that code.
There are a couple of libraries on the system that have used exceptions internally for recoverable errors. They are a constant source of bugs and maintenance headaches.
To answer your specific question, No, this is never safe:
#try {
for ( id x in array ) { ... do something that might #throw ... }
} #finally {
...
}
When you #throw out of fast enumeration, your are throwing through a frame of Apple code and the behavior is documented as undefined. In practice, such a simple pattern is likely going to work just fine, but "works by coincidence" has a significant maintenance cost.

Related

Uncaught exception handler on iOS for particular threads

For metric collection threads, we want them to never crash the app. For crashes from those threads, we want catch them and report to the server without crashing the app.
On Android or Java based app, we can set uncaught exception handler for a particular thread or a thread group. See the following example.
Is there similar mechanism on iOS platform for Objective runtime level uncaught exceptions? Is there any example code as reference?
val pipelineThread = Thread({
try {
intArrayOf(1, 2, 3, 4, 5).asList()
.parallelStream()
.map { e ->
e + 1
}.map {
throw RuntimeException("Pipeline map error.")
}.collect(Collectors.toList())
} catch (e: RuntimeException) {
// Exceptions thrown from pipeline will be handled in the same way
// as regular statements, even if it is parallel stream.
throw StreamPipelineException("Java parallel stream.", e)
}
}, "PipelineThread")
with(pipelineThread) {
setUncaughtExceptionHandler { p0, p1 ->
assert(p0.name.contentEquals("PipelineThread"))
assert(p1 is StreamPipelineException)
assert(p1.cause is RuntimeException)
assert(p1.message.contentEquals("Java parallel stream."))
}
}
I only find the top-level handler setting method.
For metric collection threads, we want them to never crash the app.
On Mac, you should move those threads into a separate process. This is how web browsers ensure that bugs impacting one tab do not impact other tabs or crash the whole browser. Usually the best tool for communicating between those processes is XPC.
On iOS, you will need to write your metric collection code carefully to avoid mistakes and not crash. You cannot spawn other processes directly in iOS today. (I keep hoping some day it will be possible. The OS can handle it. Third-party apps just aren't allowed to.)
NSSetUncaughtExceptionHandler is not intended to prevent crashes. It just gives a mechanism to record a little data or clean up certain things before crashing. Even in those cases, it must be used extremely carefully, since the system is in an undefined state during an uncaught exception. While in principle it's possible to write exception-safe Objective-C, the core frameworks are generally not. After raising an exception, there is no promise what state the system is in, and preconditions may be violated. As the docs note:
The Cocoa frameworks are generally not exception-safe. The general pattern is that exceptions are reserved for programmer error only, and the program catching such an exception should quit soon afterwards.
Assuming you're using NSThread or NSOperation for your "metric collection threads," you can create something similar to Java's approach using this, but I don't recommend it:
- (void)main {
#try {
... your code ...
} #catch ( NSException *e ) {
// Handle the exception and end the thread/operation
}
}
I don't recommend this because it can create difficult-to-solve bugs in distant parts of the program because the frameworks are not exception-safe. But I've seen it in enough production code to know it often works well enough. You have to decide if "app crashes and user relaunches it" is worse than "app doesn't crash but now behaves kind of weird and maybe corrupts some user data, but maybe doesn't."
But many Objective-C crashes are not due to exceptions in any case. They are due to signals, most commonly because of memory violations. In this case, the system is in an even less stable state. Even allocating or releasing memory may be invalid. (I once made the mistake of allocating memory in a signal handler, and deadlocked the program in a tight busy-loop. The Mac actually got hot. It would have been better to have just crashed.) For more, see How to prevent EXC_BAD_ACCESS from crashing an app?
Java incurs a lot of overhead, including a full virtual machine and extra bounds checks, to make it possible to catch the vast majority of error conditions (even ones you can't really recover from). It's a trade-off. It's still possible to have Java code that is not exception-safe and can fail to work correctly once an exception has been thrown, and it sometimes would be nice if Java would just crash and show you where the error is rather than programmers quietly swallowing the errors... but that's a different rant. Sometimes the sandbox makes things very nice.
Objective-C is built directly on C and allows accessing raw memory. If you do that incorrectly, it's undefined behavior and just about anything can happen, though usually the OS will kill the program. So even though it's possible to catch some kinds of exceptions, you cannot prevent all crashes.

How to set a new handler/responder for EXC_BAD_ACCESS crashes

I am looking for ways to override the responder/handler for EXC_BAD_ACCESS. This is how I've set the handler for signal crashes or NSException which works fine:
NSSetUncaughtExceptionHandler(newExceptionHandler)
signal(SIGABRT, newSignalHandler)
signal(SIGILL, newSignalHandler)
I tried this but this is not getting called:
signal(EXC_BAD_ACCESS, newSignalHandler)
Any idea?
As Carl mentions, intercepting crashing events on iOS (and macOS) is fraught with peril for variety of reasons. I was the Crashlytics SDK maintainer for a while, and I would recommend strongly against doing it.
But, it definitely is possible.
One thing that seems to trip people up a lot is the relationship between signals, exceptions (ObjC or C++), and mach exceptions. These are all very different things.
On iOS (and tvOS, macOS) the events that terminate a process are mach exceptions. These are the low-level events that you can, in fact, intercept. When you see that EXC_ prefix, you know you're looking at a mach exception. I believe these are all defined in mach/exception.h.
Now, iOS has an interesting implementation where if there are no mach exception handlers, the OS translates the event into a unix signal. The signal function can be used to intercept these. Since EXC_BAD_ACCESS is not a unix signal, it is not a valid argument to the signal function. But, you can add handlers to those signals listed, and they will give you roughly the same information.
Mach exceptions are a significantly more powerful and safer mechanism for intercepting these kinds of events. Unfortunately, they also require a dramatically more complex handling system. Signals have all kinds of problems, but they are a lot easier to use.
I would be interested to know what you're trying to do, in case perhaps there's a better way of achieving what you are after.
Again, I'd avoid going down this road. It just isn't worth your time. It is challenging to get things working at all, and when you do, you'll be lulled into a false sense of security. You might even think things are working right, because the times your code goes completely wrong, you'll never know and you'll just get weird reports from users of random hangs from time to time.
If you are not using an existing crash reporter (they are very hard to write on your own, when dealing with corrupted memory and the like), you might look to their sources to see which signals they are handling. For example, PLCrashReporter's PLCrashReporter.m hooks onto SIGABRT, SIGBUS, SIGFPE, SIGILL, SIGSEGV, and SIGTRAP, which seems to be the usual list for crash handlers. EXC_BAD_ACCESS should turn into either SIGBUS or SIGSEGV. Writing re-entrant code correctly in signal handlers is extremely difficult (can't use any ObjC or really most C APIs in there), so be careful -- though I guess if you are already crashing, can't do much more harm. But the more careful you are, the more exceptions you will handle without crashing further.

What's the deal with catching exceptions?

So I've read a lot about catching exceptions. Let's talk about this and iOS together. I've used it with Google Analytics to submit information about the crash and using that to fix bugs.
But this raises a question. Can catching these exceptions help prevent apps from crashing. Can you theoretically prevent that bit of code from crashing the app and keep the app open. Now I get the fact that it would probably be impossible to do if there was no memory to be used but it would still be nice to know about.
Sorry if this sounds like a stupid questions and I really should read more about it and do some more research. Any information would be helpful.
I do have a fairly decent knowledge of iOS obj-c for my age and am willing to look into what you have to say.
Thanks!
Exceptions on iOS should never be caught; they are fatal for a reason. Unlike most languages that have a rich exception hierarchy and multiple means of throwing/catching exceptions for the benefit of the program as a whole, Cocoa-Touch code is built around the principle that all exceptions are fatal. It is a mistake to think that you can catch an exception thrown through any frames of Apple-provided code and have your process continue unhindered. It is an even more grave mistake to catch and rethrow the exception for the purpose of logging.
The exceptions thrown by Cocoa-Touch indicate serious errors in program logic, or undefined and unresolvable state in an object. It is not OK to ignore them, or log them after catching them. They must be fixed and prevented from being thrown in the first place in order to truly guarantee your process remains stable.

Delphi 7 exception not caught

I have some really complicated legacy code I've been working on that crashes when collecting big chunks of data. I've been unable to find the exact reason for the crashes and am trying different ways to solve it or at least recover nicely. The last thing I did was enclose the crashing code in a
try
...
except
cleanup();
end;
just to make it behave. But the cleanup never gets done. Under what circumstances does an exception not get caught? This might be due to some memory overflow or something since the app is collecting quite a bit of data.
Oh and the exception I got before adding the try was "Access violation" (what else?) and the CPU window points to very low addresses. Any ideas or pointers would be much appreciated!
"Very low address" probably means that somebody tried to call a virtual method on an object that was not really there (i.e. was 'nil'). For example:
TStringList(nil).Clear;
The first part is very mysterious, though. I have no idea how that can happen.
I think you should try to catch that exception with madExcept. It has never failed me yet. (Disclaimer: I am not using D7.)
A trashed stack or a stack overflow can both cause irreparable harm to the structures on the stack that structured exception handling (SEH) in Windows uses to find the actual exception handlers.
If you have a buffer overflow in a buffer on the stack (e.g. a static array as a local variable but written beyond its end), and overwrite an exception record, then you can overwrite the "next" pointer, which points at the next exception record on the stack. If the pointer gets clobbered, there's nothing the OS can do to find the next exception handler and eventually reach your catch-all one.
Stack overflows are different: they can prevent calling functions at all, since every function call requires at least one dword of stack space for the return address.
you have a number of good answers. the wildest problems i've had to chase come from stack corruption issues like barry mentioned. i've seen stuff happen with the project's "Memory sizes" section on the linker page. i might be superstitious but it seemed like larger wasn't necessarily better. you might consider using the enhanced memory manager FastMM4--it's free & very helpful.
http://sourceforge.net/projects/fastmm/
i've used it with d7 and found some access to stale pointers and other evil things.
you may also wish to create a way to track valid objects and or instrument the code in other ways to have the code checking itself as it works.
when i'm seeing access to addresses like 0x00001000 or less, i think of access to a nil pointer. myStringList:=nil; myStringList.Clear;
when i'm seeing access to other addresses with much larger numbers, i think of stale pointers.
when things are strangely unstable & stack traces are proving to be nonsense and/or wildly varying, i know i have stack issues. one time it's in Controls.pas; next time it's in mmsys.pas, etc.
using the wrong calling convention to a DLL can really mess up your stack as well. this is because of the parameter passing/releasing when calling/returning from the DLL.
MadExcept will be helpful in finding the source of this, even if it shows nonsense...you'll win either way because you'll know where the problem is occurring or you'll know you have a stack issue.
is there any testing framework you can put on it to exercise it? i've found that to be very powerful because it makes it entirely repeatable.
i've fixed some pretty ugly problems this way.
I'll leave the reasons why the except might not work to Barry...
But I strongly suggest a simple strategy to narrow down the area where it happens.
Cut the big chunk in smaller parts surrounded by
try
OutputDebugString('entering part abc');
... // part abc code here
except
OutputDebugString('horror in part abc');
raise;
end;
...
try
OutputDebugString('entering in part xyz');
... // part xyz code here
except
OutputDebugString('horror in part xyz');
raise;
end;
and run your code with DebugView on the side... (works for apps without GUI as well like services).
You'll see which part is executed and if the exceptions are caught there.
I used to get this strange behabiour when calling some COM object that used a safecall calling convention. This object/method may raise an EOleException, not trapped by the usual try/except on the client code.
You should trap an EOleException and the handle it properly.
try
...
except
on E: EOleException do
...
end;
I don't know if it is the problem you are facing. But if it is, i recommend you to take a look at Implement error handling correctly, a very clarifiyng post about exception handling in delphi.
You can also enable your IDE Debug Options to stop on delhi exceptions e monitor the stack trace.
Is this perhaps a DLL or a COM object? If so, it is possible that the FPUExcpetion mask is being set by the host application to something different than Delphi is used to. An overflow, by default in Delphi produces an exception, but the FPUExcpetionmask can be set so that it doesn't, and the value is set to NAN. See the math.pas unit for more information on FPUExceptionmask
I've gotten exceptions in the initialization and finalization blocks of my code which madExcept doesn't even seem to even catch. This might occur if you're referencing external DLL's inside of that try block. I'm not certain of the reason.
Actually (and thanks to #Gung for informing me of the worthlessness of my ancient answer), I read this recently in the ancient O'Reilly Delphi Tome. You should put SysUtils as the first (or second after your non-standard memory manager unit) in your main form's DPR so that it's resident in memory with all it's Exception Catching goodness. Otherwise, if it's loaded from some other unit, it will be unloaded with that unit too and you can kiss built in exception handling goodbye.

How can I find out which exceptions a Delphi function might throw?

Is there a good way to find out which exceptions a procedure/function can raise in Delphi (including it's called procedures/functions)?
In Java you always have to declare which exceptions that can be thrown, but this is not the case in Delphi, which could lead to unhandled exceptions.
Are there any code analysis tools that detects unhandled exceptions?
(Edit: It is now obvious that the question referred only to design-time checking.)
New answer:
I cannot state whether there are any tools to check this for you. Pascal Analyzer, for one, does not.
I can tell you, however, that in most Delphi applications, even if there was a tool to check this for you, you would get no results.
Why? Because the main message loop in TApplication.Run() wraps all HandleMessage() calls in an exception handling block, which catches all exception types. Thus you will have implicit/default exception handling around 99.999% of code in most applications. And in most applications, this exception handling will be around 100% of your own code - the 0.001% of code which is not wrapped in exception handling will be the automatically generated code.
If there was a tool available to check this for you, you would need to rewrite Application.run() such that it does not include exception handling.
(Previous answer:
The Application.OnException event handler can be assigned to catch all exceptions that aren't handled by other exception handlers. Whilst this is run-time, and thus perhaps not exactly what you are after (it sounds like you want to identify them at design time), it does allow you to trap any exception not handled elsewhere. In conjunction with tools such as the JCLDebug stuff in the Jedi Code Library, you could log a stack trace to find out where & why an exception occurred, which would allow for further investigation and adding specific exception handling or prevention around the guilty code...)
My guess is that you're trying to make Delphi behave like Java, which is not a good approach. I'd advise not to worry too much about unhandled exceptions. In the worst case, they'll bubble up to the generic VCL exception handler and cause a Windows message dialog. In a normal application, they won't halt the application.
Well-written code would document the different exceptions that can be raised so you can handle them in a meaningful way. Catch-all handlers aren't recommended since there is really no way to know what to do if you don't know why an exception was raised. I can also highly recommend madExcept.
Except for a scan on the "raise" keyword, there's no language construct in Delphi that tells the casual reader which exceptions can be expected from a method.
At runtime, one could add a catch-all exception handler in every method, but that's not advisable, as it will slow down the speed of execution. (And it's cumbersome to do too).
Adding an exception-handling block to a method will add a few assembly instructions to it (even when the exception isn't triggered), which forms measureable slow-down when the method is called very often.
There do exist a few libraries that can help you in analyzing runtime exceptions, like madExcept, JclDebug, and EurekaLog. These tools can log all kinds of details about the exception, it's highly advisable to use one of those!
The short answers is there is no tool that does what you say, and even a scan for the raise keyword wouldn't get you there. EAccessViolation or EOutOfMemory are just two of a number of exceptions that could get raised just about anywhere.
One fundamental thing about Delphi is the exceptions are hierarchical: All defined language exceptions descend from Exception, although it is worth noting that it is actually possible to raise any TObject descendant.
If you want to catch every exception that is raised in a particular procedure, just wrap it in a try / except block, but as was mentioned this is not recommended.
// Other code . . .
try
SomeProcedure()
except // BAD IDEA!
ShowMessage('I caught them all!');
end;
That will catch everything, even instances of a raised TObject. Although I would argue that this is rarely the best course of action. Usually you want to use a try / finally block and then allow your global exception handler (or one final try / except block) to actually handle the exceptions.
I will second (or is it third) MadExcept. I have been using it successfully in several commercial applications without any problems. The nice thing about MadExcept is that it will generate a report for you with a full stack trace that will generally point you in the right direction as to what went wrong, and can even include a screenshot, as well has have this automatically emailed to you from the clients computer with a simple mouse click.
However, you don't want to use this for ALL exceptions, just to catch the ones you miss. For instance, if you open a database and the login fails, it would be better for you to catch and handle this one yourself rather than give the user the MadExcept default error in your application occured message.
Any exception not explicitly or generally handled at a specific level will trickle upwards in the call stack. The Delphi RTL (Run Time Library) will generate a set of different exception classes - (mathematical errors, access errors, class specific errors etc). You can chose to handle them specifically or generally in the different try except blocks.
You don't really need to declare any new exception classes unless you need to propagate a specific functional context with the exception.
As previous commenters wrote, you can also add a mother of all exception handlers like MadExcept or EurekaLog to catch the uncaught.
edit: This is a blanket insurance against unhandled exceptions
try
ThisFunctionMayFail;
except
// but it sure won't crash the application
on e:exception
do begin
// something sensible to handle the error
// or perhaps log and/or display the the generic e.description message
end
end;
Take a look at http://www.madshi.net/madExceptDescription.htm
For runtime try Eurekalog. I do not know whether a tool exists for design time. You will have more dificoulties even when you have third party code without source. There is no need in Delphi to catch exceptions, so you do not have to declare them like in Java.
What I wanted to say is that Delphi does not require that an exception is handled. It will just terminate the program. EurekaLog provides means to log handled and unhandled exceptions and provide a wealth of information on the sate of the program when the exception occured, including the line of code it occured at and the call stack at the time.
As Jim McKeeth points out, you can't get a definitive answer, but it seems to me that one could partially answer the question by some static analysis: given a particular function/procedure, construct a call graph. Check each of the functions in that call graph for a raise statement. That would tell you, for instance, that TIdTcpClient.ReadString can raise an EIdNotConnected (among others).
A clever analyser might also note that some code uses the / operator and include EDivByZero as a possibility, or that some procedure accesses an array and include ERangeError.
That answer's a bit tighter than simply grepping for "raise".
Finalization sections of units can raise exceptions too. These will slip by I think... and are also somewhat problematic.
I think Delphi IDE has a build-in "stack trace" or "stack tree" something like.
This question reminds me of Skybuck's TRussianRoulette game... google it, it's code and answer may help.

Resources