Discussion:
Exception handling
(too old to reply)
Casey McDermott
2018-06-14 02:22:36 UTC
Permalink
We are updating a large Carbon accounting app to Cocoa. There is a ton of
C++ model-layer code. It originally was based on PowerPlant, but the new interface is Objective-C
with many bridges to the C++.

Our Carbon event loop had a try/catch block, which caught most exceptions, and then
continued. It started as an expedient in early production, but it remained in production code
since it often allows users to continue, save their work, etc.

I know this is not standard use of C++ exceptions, but it has worked extremely well for a couple decades,
on both Mac & Windows.There are a few thousand sanity checks which give a message and
then throw. The error msg gives file & code line so it makes bug fixes very easy.
Most asserts will never be called, so we don't want to clutter the code with try/catch blocks.
Nearly always, the event loop is the best place to escape to.

Is there a way to override the event loop in Cocoa? Some other way we can escape to
the event loop and then continue from there?

Thanks,

Casey McDermott

_______________________________________________

Cocoa-dev mailing list (Cocoa-***@lists.apple.com)

Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com

Help/Unsubscribe/Update your Subscription:
https://lists.apple.com/mailman/options/cocoa-dev/gegs%40ml-in.narkive.net

This email sent to ***@ml-in.narkive.net
Quincey Morris
2018-06-14 02:53:26 UTC
Permalink
Post by Casey McDermott
Nearly always, the event loop is the best place to escape to.
This is not how current thinking goes, unless you mean something different from what I think you’re saying.

If you’re implementing sanity (“should not happen”) checks, then the belief that it’s safe to shrug and return to the event loop is just false. It would be true if no significant state was preserved across event loop iterations, but that’s not nearly true — and likely extremely *un*true for those APIs and libraries that are complicated enough for the checks to fail sometimes. It’s simply not safe to branch out of lower-level code unless *all* of the code has been explicitly designed for that usage style.

It’s better to simply log the error and crash.

For recoverable errors in Obj-C code, you’re better off just doing the work of returning a “success or not” value along with a NSError out parameter. It’s a bit of grunt work to add this to existing code, but it’s probably worthwhile. (This would include having failable class init’s that have an outError parameter, which is not a common pattern, though it should be.)

The real problem comes, however, when you consider multiple threads. You can certainly catch NSExceptions in a sendEvent: override on the main thread, but in modern Cocoa programming there are many situations where you’re running your code on a background thread (e.g. GCD), and there’s not always an event loop or any good place to catch exceptions.

The situation with C++ exceptions is a bit different. You can basically do whatever you want with those (including using them for flow control), but there’s still nowhere central to catch uncaught exceptions, and you still have to worry about multithreading.

I may be out on my lonesome here, but if you want a robust app, I really think you have exactly 2 error handling patterns:

1. Returning false/nil with an outError parameter for recoverable errors, and always testing the result at every level.

2. Throwing NSExceptions for unrecoverable errors, and letting the app crash immediately.


_______________________________________________

Cocoa-dev mailing list (Cocoa-***@lists.apple.com)

Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com

Help/Unsubscribe/Update your Subscription:
https://lists.apple.com/mailman/options/cocoa-dev/gegs%40ml-in.
Alastair Houghton
2018-06-14 09:03:06 UTC
Permalink
Post by Quincey Morris
Post by Casey McDermott
Nearly always, the event loop is the best place to escape to.
This is not how current thinking goes, unless you mean something different from what I think you’re saying.
Agreed, but with the proviso that the Cocoa framework actually catches and ignores exceptions itself in various contexts. This can be quite annoying when it happens, because it can result in odd behaviour and it isn’t always obvious that the cause was an exception.
Post by Quincey Morris
1. Returning false/nil with an outError parameter for recoverable errors, and always testing the result at every level.
2. Throwing NSExceptions for unrecoverable errors, and letting the app crash immediately.
No, you aren’t out on your own. That’s the tradition in the Objective-C world; either return a nil or NO result with an NSError parameter filled in appropriately, or throw an exception where a crash is probably appropriate.

There are a couple of places in the Cocoa frameworks where things don’t quite work that way; notably, Distributed Objects can throw exceptions when you send messages to remote objects — this makes sense, because the local proxy object is supposed to behave pretty much the same as the remote object, and could in general be of any type (therefore doesn’t necessarily have an NSError parameter available, or even a return code that could be used to indicate failure). If you’re using DO, you might therefore use Objective-C exceptions for some recoverable error handling (e.g. where a remote server has “gone away” and you therefore need to reconnect somehow or connect somewhere else).
Post by Quincey Morris
The situation with C++ exceptions is a bit different. You can basically do whatever you want with those (including using them for flow control), but there’s still nowhere central to catch uncaught exceptions, and you still have to worry about multithreading.
More to the point, there’s nowhere safe to catch uncaught exceptions; you can’t assume that you can safely throw a C++ exception through any library or framework, including the Cocoa and Core Foundation frameworks, even though on the 64-bit runtime Objective-C and C++ exceptions are (kind of) unified, because Objective-C code generally doesn’t expect exceptions and so if you throw them through it it’s unlikely to be in a good state afterwards.

Basically, if you’re calling C++ code from your Objective-C code, and that C++ code might throw exceptions, you’re going to want to call it within a try statement (you *can* use an Objective-C @try, in which case the @catch(…) case will catch C++ exceptions; but that probably won’t give you the granularity you want).

Kind regards,

Alastair.

--
http://alastairs-place.net

_______________________________________________

Cocoa-dev mailing list (Cocoa-***@lists.apple.com)

Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com

Help/Unsubscribe/Update your Subscription:
https://lists.apple.com/mailman/options/cocoa-dev/gegs%40ml-in.narkive.net

This emai
Jens Alfke
2018-06-14 17:06:16 UTC
Permalink
Post by Quincey Morris
The situation with C++ exceptions is a bit different.
It's actually exactly the same, since at the runtime level Obj-C exceptions are C++ exceptions.
Post by Quincey Morris
1. Returning false/nil with an outError parameter for recoverable errors, and always testing the result at every level.
2. Throwing NSExceptions for unrecoverable errors, and letting the app crash immediately.
You can throw exceptions (C++ or NSException) in your own code, provided that you catch and handle them before they unwind into system code*. (We use that approach in the C++ core code of the Cocoa framework I work on.)

—Jens

* well, libc++ is fine of course since it's exception-aware.
_______________________________________________

Cocoa-dev mailing list (Cocoa-***@lists.apple.com)

Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com

Help/Unsubscribe/Update your Subscription:
https://lists.apple.com/mailman/options/cocoa-dev/gegs%40ml-in.narkive.net

This email sent to ***@ml-
Jens Alfke
2018-06-14 17:00:13 UTC
Permalink
Post by Casey McDermott
Our Carbon event loop had a try/catch block, which caught most exceptions, and then
continued. It started as an expedient in early production, but it remained in production code
since it often allows users to continue, save their work, etc.
That's actually how Cocoa used to work. I can't recall whether an alert panel popped up or if it was just silent. The behavior changed to crashing sometime in the past, um, 8 years or so.
Post by Casey McDermott
Is there a way to override the event loop in Cocoa? Some other way we can escape to
the event loop and then continue from there?
There used to be either a callback or an overridable NSApplication method to handle an uncaught exception, but looking at current headers I can't find what I'm looking for; maybe it's been gone for a long time and removed?

—Jens
_______________________________________________

Cocoa-dev mailing list (Cocoa-***@lists.apple.com)

Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com

Help/Unsubscribe/Update your Subscription:
https://lists.apple.com/mailman/options/cocoa-dev/gegs%40ml-in.narkive.net

This ema
Alastair Houghton
2018-06-14 17:11:03 UTC
Permalink
Post by Jens Alfke
Post by Casey McDermott
Our Carbon event loop had a try/catch block, which caught most exceptions, and then
continued. It started as an expedient in early production, but it remained in production code
since it often allows users to continue, save their work, etc.
That's actually how Cocoa used to work. I can't recall whether an alert panel popped up or if it was just silent. The behavior changed to crashing sometime in the past, um, 8 years or so.
I don’t think it’s changed in any obvious way; the framework has always swallowed exceptions in certain contexts, but not in others. Obviously the precise detail may have changed over time, but it’s certainly crashed on exceptions for as long as I remember, outside of the places where they get ignored for whatever reason.
Post by Jens Alfke
Post by Casey McDermott
Is there a way to override the event loop in Cocoa? Some other way we can escape to
the event loop and then continue from there?
There used to be either a callback or an overridable NSApplication method to handle an uncaught exception, but looking at current headers I can't find what I'm looking for; maybe it's been gone for a long time and removed?
Perhaps you’re thinking of NSSetUncaughtExceptionHandler()? I don’t think there’s much you can usefully do from that, though, besides logging the exception.

Kind regards,

Alastair.

--
http://alastairs-place.net

_______________________________________________

Cocoa-dev mailing list (Cocoa-***@lists.apple.com)

Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com

Help/Unsubscribe/Update your Subscription:
https://lists.apple.com/mailman/options/cocoa-dev/gegs%40ml-in.narkive.net

This email
Jens Alfke
2018-06-14 17:16:51 UTC
Permalink
Post by Alastair Houghton
I don’t think it’s changed in any obvious way; the framework has always swallowed exceptions in certain contexts, but not in others. Obviously the precise detail may have changed over time, but it’s certainly crashed on exceptions for as long as I remember, outside of the places where they get ignored for whatever reason.
I definitely remember that when I started Cocoa programming in 2000, up until at least 2005, NSRunLoop caught exceptions and the app kept running. I was kind of surprised when that changed.
Post by Alastair Houghton
Perhaps you’re thinking of NSSetUncaughtExceptionHandler()? I don’t think there’s much you can usefully do from that, though, besides logging the exception.
Yes, that's it! I used to set that to capture the exception info (like the backtrace), and then schedule a delayed-perform to pop up the alert when it was safe. I have no idea whether it would still work.

—Jens
_______________________________________________

Cocoa-dev mailing list (Cocoa-***@lists.apple.com)

Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com

Help/Unsubscribe/Update your Subscription:
https://lists.apple.com/mailman/options/cocoa-dev/gegs%40ml-in.narkive.net

This email sent to
Casey McDermott
2018-06-14 18:33:37 UTC
Permalink
Thanks, this is all useful info. I checked NSExceptionHandler and
NSUncaughtExceptionHandler, but they are just for logging before termination.
NSRunLoop is accessible but there's not much there.

However, simply replacing the C++ throw with a Cocoa exception via
[NSException raise : errString format : errString]; seems to work great.
Apparently it unwinds the call stack and is swallowed in the run loop.

It doesn't use the error string but that's fine. We already show a dialog
before the throw, with file & line number. Helps enormously for fixing bugs.

Thanks for all the help,

Casey McDermott

--------------------------------------------
On Thu, 6/14/18, Alastair Houghton <***@alastairs-place.net> wrote:

Subject: Re: Exception handling
To: "Jens Alfke" <***@mooseyard.com>
Cc: "Casey McDermott" <***@turtlesoft.com>, "cocoa-dev list" <cocoa-***@lists.apple.com>
Date: Thursday, June 14, 2018, 1:11 PM

On 14 Jun
2018, at 18:00, Jens Alfke <***@mooseyard.com>
wrote:

On Jun 13, 2018, at 7:22
PM, Casey McDermott <***@turtlesoft.com>
wrote:

Our Carbon event loop had
a try/catch block, which caught most exceptions, and then
continued.  It started
as an expedient in early production, but it remained in
production code
since
it often allows users to continue, save their work, etc.
 

That's actually how
Cocoa used to work. I can't recall whether an alert
panel popped up or if it was just silent. The behavior
changed to crashing sometime in the past, um, 8 years or
so.

I don’t
think it’s changed in any obvious way; the framework has
always swallowed exceptions in certain contexts, but not in
others. Obviously the precise detail may have changed over
time, but it’s certainly crashed on exceptions for as long
as I remember, outside of the places where they get ignored
for whatever reason.
Is there a way to override
the event loop in Cocoa?  Some other way we can escape to

the event loop and
then continue from there?

There used to be either a callback or
an overridable NSApplication method to handle an uncaught
exception, but looking at current headers I can't find
what I'm looking for; maybe it's been gone for a
long time and removed?

Perhaps
you’re thinking of NSSetUncaughtExceptionHandler()? I
don’t think there’s much you can usefully do from that,
though, besides logging the exception.
Kind regards,
Alastair.
--http://alastairs-place.net

_______________________________________________

Cocoa-dev mailing list (Cocoa-***@lists.apple.com)

Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com

Help/Unsubscribe/Update your Subscription:
https://lists.apple.com/mailman/options/cocoa-dev/gegs%40ml-in.narkive.net

This email sent to ***@m
Georg Seifert
2018-06-14 19:03:32 UTC
Permalink
The hockeyApp framework has a lot explanation about this:

https://www.hockeyapp.net/help/sdk/mac/4.0.2/Classes/BITCrashExceptionApplication.html

Best
Georg Seifert
Post by Casey McDermott
Thanks, this is all useful info. I checked NSExceptionHandler and
NSUncaughtExceptionHandler, but they are just for logging before termination.
NSRunLoop is accessible but there's not much there.
However, simply replacing the C++ throw with a Cocoa exception via
[NSException raise : errString format : errString]; seems to work great.
Apparently it unwinds the call stack and is swallowed in the run loop.
It doesn't use the error string but that's fine. We already show a dialog
before the throw, with file & line number. Helps enormously for fixing bugs.
Thanks for all the help,
Casey McDermott
--------------------------------------------
Subject: Re: Exception handling
Date: Thursday, June 14, 2018, 1:11 PM
On 14 Jun
On Jun 13, 2018, at 7:22
Our Carbon event loop had
a try/catch block, which caught most exceptions, and then
continued. It started
as an expedient in early production, but it remained in
production code
since
it often allows users to continue, save their work, etc.
That's actually how
Cocoa used to work. I can't recall whether an alert
panel popped up or if it was just silent. The behavior
changed to crashing sometime in the past, um, 8 years or
so.
I don’t
think it’s changed in any obvious way; the framework has
always swallowed exceptions in certain contexts, but not in
others. Obviously the precise detail may have changed over
time, but it’s certainly crashed on exceptions for as long
as I remember, outside of the places where they get ignored
for whatever reason.
Is there a way to override
the event loop in Cocoa? Some other way we can escape to
the event loop and
then continue from there?
There used to be either a callback or
an overridable NSApplication method to handle an uncaught
exception, but looking at current headers I can't find
what I'm looking for; maybe it's been gone for a
long time and removed?
Perhaps
you’re thinking of NSSetUncaughtExceptionHandler()? I
don’t think there’s much you can usefully do from that,
though, besides logging the exception.
Kind regards,
Alastair.
--http://alastairs-place.net
_______________________________________________
Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com
https://lists.apple.com/mailman/options/cocoa-dev/georg.seifert%40gmx.de
_______________________________________________

Cocoa-dev mailing list (Cocoa-***@lists.apple.com)

Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com

Help/Unsubscribe/Update your Subscription:
https://lists.apple.com/mailman/options/cocoa-dev/gegs%40ml-in.narkive.net

This email sent to
Jens Alfke
2018-06-14 19:45:53 UTC
Permalink
Post by Casey McDermott
However, simply replacing the C++ throw with a Cocoa exception via
[NSException raise : errString format : errString]; seems to work great.
Yeah, I think that’s because the exception type being thrown is now NSException* instead of whatever C++ type you were throwing, and the backstop exception handlers in Cocoa only catch NSException*.

—Jens
_______________________________________________

Cocoa-dev mailing list (Cocoa-***@lists.apple.com)

Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com

Help/Unsubscribe/Update your Subscription:
https://lists.apple.com/mailman/options/cocoa-dev/gegs%40ml-in.narkive.net

This
Quincey Morris
2018-06-15 00:58:14 UTC
Permalink
Post by Casey McDermott
However, simply replacing the C++ throw with a Cocoa exception via
[NSException raise : errString format : errString]; seems to work great.
Apparently it unwinds the call stack and is swallowed in the run loop.
Yes, but do be careful about exceptions thrown off the main thread. In (say) GCD-owned threads, there’s nothing AFAIK to catch exceptions, and as someone already mentioned, NSExceptions can’t successfully cross dylib/framework boundaries.

_______________________________________________

Cocoa-dev mailing list (Cocoa-***@lists.apple.com)

Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com

Help/Unsubscribe/Update your Subscription:
https://lists.apple.com/mailman/options/cocoa-dev/gegs%40ml-in.narkive.net

This email sent
Jens Alfke
2018-06-15 18:30:01 UTC
Permalink
Post by Quincey Morris
as someone already mentioned, NSExceptions can’t successfully cross dylib/framework boundaries.
They can, actually; there is no problem with this at the ABI/runtime level.

I think what you mean is that most libraries/frameworks don't make guarantees about properly handling exceptions thrown into them, i.e. from a call into external code. Some C++ libraries do guarantee this (especially libc++), and even without guarantees a typical C++ lib using RAII will be relatively safe, but Objective-C code usually isn't written to be exception-safe, and C code of course can't be.

—Jens
_______________________________________________

Cocoa-dev mailing list (Cocoa-***@lists.apple.com)

Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com

Help/Unsubscribe/Update your Subscription:
https://lists.apple.com/mailman/options/cocoa-dev/gegs%40ml-in.
Alex Zavatone
2018-06-15 20:53:08 UTC
Permalink
Along those lines, here are some approaches we used on iOS to catch exceptions and to throw exceptions.

- (id)argument {
if (!_argument) {
@throw [NSException exceptionWithName:NSInternalInconsistencyException reason:@"Argument requested has yet to be captured." userInfo:nil];


if (![HMACData rnc_isEqualInConsistentTime:self.inData]) {
[self cleanupAndNotifyWithError:
[NSError errorWithDomain:kRNCryptorErrorDomain
code:kRNCryptorHMACMismatch
userInfo:[NSDictionary dictionaryWithObject:@"HMAC Mismatch" /* DNL */
forKey:NSLocalizedDescriptionKey]]];
return;
}

We also set NSSetUncaughtExceptionHandler declared in the appDelegate.

NSSetUncaughtExceptionHandler(&uncaughtExceptionHandler);

#pragma mark -
#pragma mark Exception Handler

void uncaughtExceptionHandler(NSException *exception) {
DLog(@"CRASH: %@", exception);
DLog(@"Stack Trace: %@", [exception callStackSymbols]);
// Internal error reporting
}


Hope this helps.
- Alex Zavatone
Post by Jens Alfke
Post by Quincey Morris
as someone already mentioned, NSExceptions can’t successfully cross dylib/framework boundaries.
They can, actually; there is no problem with this at the ABI/runtime level.
I think what you mean is that most libraries/frameworks don't make guarantees about properly handling exceptions thrown into them, i.e. from a call into external code. Some C++ libraries do guarantee this (especially libc++), and even without guarantees a typical C++ lib using RAII will be relatively safe, but Objective-C code usually isn't written to be exception-safe, and C code of course can't be.
—Jens
_______________________________________________
Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com
https://lists.apple.com/mailman/options/cocoa-dev/zav%40mac.com
_______________________________________________

Cocoa-dev mailing list (Cocoa-***@lists.apple.com)

Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com

Help/Unsubscribe/Update your Subscription:
https://lists.apple.com/mailman/options/cocoa-dev/gegs%40ml-in.narkive.net

This email sent
Alastair Houghton
2018-06-16 14:25:15 UTC
Permalink
Post by Jens Alfke
Post by Quincey Morris
as someone already mentioned, NSExceptions can’t successfully cross dylib/framework boundaries.
They can, actually; there is no problem with this at the ABI/runtime level.
I think what you mean is that most libraries/frameworks don't make guarantees about properly handling exceptions thrown into them, i.e. from a call into external code. Some C++ libraries do guarantee this (especially libc++), and even without guarantees a typical C++ lib using RAII will be relatively safe, but Objective-C code usually isn't written to be exception-safe, and C code of course can't be.
Quite, though in principle there’s no reason C code couldn’t be exception safe, it’s just that there’s no language support for it (except on Windows where there are extensions to support SEH), so the C code would have to know about the relevant runtime data structures and associated behaviour. In practice, it’s very unlikely you’d ever find exception-safe C code, except in a language runtime or - rarely - on Windows when it’s been written to use SEH.

Kind regards,

Alastair.

--
http://alastairs-place.net

_______________________________________________

Cocoa-dev mailing list (Cocoa-***@lists.apple.com)

Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com

Help/Unsubscribe/Update your Subscription:
https://lists.apple.com/mailman/options/cocoa-dev/gegs%40ml-in.narkive.net

This email sent t
Casey McDermott
2018-06-16 18:37:04 UTC
Permalink
BTW, raising an NSException inside a C++

try{}
catch(...){}

block also seems to work OK. It does get caught.

Casey McDermott

--------------------------------------------
On Sat, 6/16/18, Alastair Houghton <***@alastairs-place.net> wrote:

Subject: Re: Exception handling
To: "Jens Alfke" <***@mooseyard.com>
Cc: "Quincey Morris" <***@rivergatesoftware.com>, "Casey McDermott" <***@turtlesoft.com>, "cocoa-dev list" <cocoa-***@lists.apple.com>
Date: Saturday, June 16, 2018, 10:25 AM

On 15 Jun 2018, at 19:30, Jens
Post by Jens Alfke
On Jun 14, 2018,
as
someone already mentioned, NSExceptions can’t successfully
cross dylib/framework boundaries.
Post by Jens Alfke
They can, actually; there is no problem
with this at the ABI/runtime level.
Post by Jens Alfke
I think what you mean is that most
libraries/frameworks don't make guarantees about
properly handling exceptions thrown into them, i.e. from a
call into external code. Some C++ libraries do guarantee
this (especially libc++), and even without guarantees a
typical C++ lib using RAII will be relatively safe, but
Objective-C code usually isn't written to be
exception-safe, and C code of course can't be.

Quite, though in principle
there’s no reason C code couldn’t be exception safe,
it’s just that there’s no language support for it
(except on Windows where there are extensions to support
SEH), so the C code would have to know about the relevant
runtime data structures and associated behaviour.  In
practice, it’s very unlikely you’d ever find
exception-safe C code, except in a language runtime or -
rarely - on Windows when it’s been written to use SEH.

Kind regards,

Alastair.

--
http://alastairs-place.net

_______________________________________________

Cocoa-dev mailing list (Cocoa-***@lists.apple.com)

Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com

Help/Unsubscribe/Update your Subscription:
https://lists.apple.com/mailman/options/cocoa-dev/gegs%40ml-in.narkive.net

This email sent to ***@ml-

Continue reading on narkive:
Loading...