<html><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8"></head><body style="word-wrap: break-word; -webkit-nbsp-mode: space; line-break: after-white-space;" class=""><br class=""><div><br class=""><blockquote type="cite" class=""><div class="">On Sep 21, 2017, at 12:14 AM, John Holdsworth via swift-evolution <<a href="mailto:swift-evolution@swift.org" class="">swift-evolution@swift.org</a>> wrote:</div><br class="Apple-interchange-newline"><div class=""><meta http-equiv="Content-Type" content="text/html charset=utf-8" class=""><div style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;" class=""><div class="">Hi S/E,</div><div class=""><br class=""></div><div class="">I’ve raised a rather speculative PR suggesting a hook be added to stdlib</div><div class="">that would allow users to experiment with error recovery in their apps.</div><div class="">I’ve been asked to put it forward on Swift Evolution to gather opinions</div><div class="">from the wider community about such a design.</div><div class=""><br class=""></div><div class=""><a href="https://github.com/apple/swift/pull/12025" class="">https://github.com/apple/swift/pull/12025</a></div><div class=""><br class=""></div><div class="">Ultimately, it comes down to being able to do something like this:</div><div class=""><br class=""></div><div class=""><div class="" style="margin: 0px; font-size: 11px; line-height: normal; font-family: Menlo; background-color: rgb(255, 255, 255);"> <span class="" style="color: rgb(186, 45, 162);">do</span> {</div><div class="" style="margin: 0px; font-size: 11px; line-height: normal; font-family: Menlo; background-color: rgb(255, 255, 255);"> <span class="" style="color: rgb(186, 45, 162);">try</span> <span class="" style="color: rgb(79, 129, 135);">Fortify</span>.<span class="" style="color: rgb(49, 89, 93);">exec</span> {</div><div class="" style="margin: 0px; font-size: 11px; line-height: normal; font-family: Menlo; background-color: rgb(255, 255, 255);"> <span class="" style="color: rgb(186, 45, 162);">var</span> a: <span class="" style="color: rgb(112, 61, 170);">String</span>!</div><div class="" style="margin: 0px; font-size: 11px; line-height: normal; font-family: Menlo; background-color: rgb(255, 255, 255);"> a = a!</div><div class="" style="margin: 0px; font-size: 11px; line-height: normal; font-family: Menlo; background-color: rgb(255, 255, 255);"> }</div><div class="" style="margin: 0px; font-size: 11px; line-height: normal; font-family: Menlo; background-color: rgb(255, 255, 255);"> }</div><div class="" style="margin: 0px; font-size: 11px; line-height: normal; font-family: Menlo; background-color: rgb(255, 255, 255);"> <span class="" style="color: rgb(186, 45, 162);">catch</span> {</div><div class="" style="margin: 0px; font-size: 11px; line-height: normal; font-family: Menlo; background-color: rgb(255, 255, 255);"> <span class="" style="color: rgb(62, 30, 129);">NSLog</span>(<span class="" style="color: rgb(209, 47, 27);">"Caught exception: </span>\<span class="" style="color: rgb(209, 47, 27);">(</span>error<span class="" style="color: rgb(209, 47, 27);">)"</span>)</div><div class="" style="margin: 0px; font-size: 11px; line-height: normal; font-family: Menlo; background-color: rgb(255, 255, 255);"> }</div></div><div class=""><br class=""></div><div class="">This was primarily intended for user in "Swift on the server" but could also</div><div class="">help avoid crashes in the mobile domain. The rationale and mechanics</div><div class="">are written up at the following url:</div><div class=""><br class=""></div><div class=""><a href="http://johnholdsworth.com/fortify.html" class="">http://johnholdsworth.com/fortify.html</a></div><div class=""><br class=""></div><div class="">I'll accept this won’t be everybody’s cup of tea but at this stage this is</div><div class="">only an opt-in facilitating patch. Developers need not subject their apps</div><div class="">to this approach which requires a separate experimental implementation.</div><div class=""><br class=""></div><div class="">The recovery is reasonably well behaved except it will not recover </div><div class="">objects and system resources used in intermediate frames, It’s not</div><div class="">as random as something like, for example, trying to cancel a thread.</div><div class=""><br class=""></div><div class="">The debate about whether apps should try to soldier on when something</div><div class="">is clearly amiss is a stylistic one about which there will be a spectrum of</div><div class="">opinions. The arguments weigh more in favour in the server domain.</div></div></div></blockquote><br class=""></div><div>Thanks for raising this topic! Graceful partial recovery is important and useful, and although we've tended to invoke "actors" as the vague savior that will answer all the questions in this space, I think we can provide useful functionality with a smaller scope that won't interfere with future directions. At a language level, questions to answer include:</div><div><br class=""></div><div>- What can we guarantee about the process state after a trap?</div><div>- What does the interface for setting up a trap handler look like?</div><div><br class=""></div><div>Instead of thinking of a trap as completely invalidating and ending the program like we do today, we can think of it as deadlocking the current execution context (setting aside for a moment the question of what "execution context" means), as if it got stuck in an infinite loop. As you noted, this means we can't reclaim any memory, locks, or other resources currently being held by the trapped context, but other contexts can continue executing. In fantasy actor land, the definition of "execution context" would ideally be "current actor"; in the world today, we have a few choices. We could say that a trap takes down the current thread, though that might be a bit too much for single-threaded or workqueue-based architectures. Another alternative is to delimit the scope affected by a trap with a setjmp/longjmp-like mechanism, sort of like what you have, though that then requires care to ensure that state "above" the trap line isn't entangled with the invalidated state "below" the trap line.</div><div><br class=""></div><div>That leads into the question of what the interface for handling a trap should look like. Personally, I don't think trying to turn fatal errors into exceptions is the right answer, since that makes it way too easy to do the kinds of harmful things people do with Java runtime errors, SEH, etc. to swallow and ignore serious problems. I think it'd be better to have an interface that's clearly tuned toward supervisory reaction to unexpected failure, rather than one for routine handling of expected errors. It also potentially creates safety problems for the ownership model. It's tempting to think of the block passed to your `Fortify.exec` as nonescaping, but that's problematic with inouts:</div><div><br class=""></div><div><span class="Apple-tab-span" style="white-space:pre">        </span>var x: Int</div><div><span class="Apple-tab-span" style="white-space:pre">        </span>do {</div><div><span class="Apple-tab-span" style="white-space:pre">                </span>try execWhileTurningTrapsIntoErrors {</div><div><span class="Apple-tab-span" style="white-space:pre">                        </span>foo(&x)</div><div><span class="Apple-tab-span" style="white-space:pre">                </span>}</div><div><span class="Apple-tab-span" style="white-space:pre">        </span>} catch {</div><div><span class="Apple-tab-span" style="white-space:pre">                </span>print(x)</div><div><span class="Apple-tab-span" style="white-space:pre">        </span>}</div><br class=""><div class=""><span class="Apple-tab-span" style="white-space:pre">        </span>func foo(x: inout Int) { fatalError() }</div><div class=""><br class=""></div><div class="">The compiler will reason that x is statically exclusively held only during the call to `foo`, but that's not really the case—foo trapped and deadlocked in the middle of the access, and we essentially left it hanging and went and ran our catch handler with the inout access still active.</div><div class=""><br class=""></div><div class="">If we were to say that a trap takes down the current thread, then we could have a signal-like interface for installing a handler that's the last thing to run on the thread before taking it down, like this:</div><div class=""><br class=""></div><blockquote style="margin: 0 0 0 40px; border: none; padding: 0px;" class=""><div class="">func ifTrapOccursOnCurrentThread(_ do: @escaping () -> ())</div><div class=""><br class=""></div><div class="">ifTrapOccursOnCurrentThread {</div><div class=""> supervisor.notifyAboutTrap(on: pthread_self())</div><div class="">}</div><div class="">doStuff()</div><div class=""><br class=""></div></blockquote><div class="">A scoped handler could still be made to work, with an interface something like this:</div><div class=""><br class=""></div><blockquote style="margin: 0 0 0 40px; border: none; padding: 0px;" class=""><div class="">func run(_ body: @escaping () -> (), withTrapHandler: () -> ())</div><div class=""><br class=""></div><div class="">run({</div><div class=""> doStuff()</div><div class="">}, withTrapHandler: {</div> supervisor.notifyAboutTrap(on: pthread_self())<div class="">})</div><div class=""><br class=""></div></blockquote>The `@escaping` annotation on the body would prevent the compiler from making invalid static assumptions about lifetimes in the body that would be violated if it traps. IMO, the handler block also shouldn't receive any information about the trap other than that one happened—the runtime ought to handle logging the reason for a trap, and anything you do to plan your shutdown or continue running unrelated subtasks should have no other business knowing why the trap occurred.<div class=""><br class=""></div><div class="">At an implementation level, enabling trap handling would also require us to standardize on an ABI for handlable runtime errors. We currently don't have a standard mechanism here. Failures don't necessarily funnel through any fixed set of runtime entry points; the compiler also directly generates @llvm.trap() calls, and LLVM doesn't make any guarantee about how llvm.trap is implemented. It's also an open question whether things like C's abort(), null pointer dereferences, segfaults, etc. should be treated as runtime failures that can be handled by this mechanism.</div><div class=""><br class=""></div><div class="">-Joe</div></body></html>