{"id":376,"date":"2010-06-14T00:35:13","date_gmt":"2010-06-14T07:35:13","guid":{"rendered":"http:\/\/www.airs.com\/blog\/?p=376"},"modified":"2010-06-14T00:35:13","modified_gmt":"2010-06-14T07:35:13","slug":"gccgo-panicrecover","status":"publish","type":"post","link":"https:\/\/www.airs.com\/blog\/archives\/376","title":{"rendered":"gccgo panic\/recover"},"content":{"rendered":"<p>Back in March Go picked up a dynamic exception mechanism.  Previously, when code called the <code>panic<\/code> function, it simply aborted your program.  Now, it walks up the stack to the top of the currently running goroutine, running all deferred functions.  More interestingly, if a deferred function calls the new <code>recover<\/code> function, the panic is interrupted, and stops walking the stack at that point.  Execution then continues normally from the point where <code>recover<\/code> was called.  The <code>recover<\/code> function returns the argument passed to <code>panic<\/code>, or <code>nil<\/code> if there is no panic in effect.<\/p>\n<p>I just completed the implementation of this in gccgo.  It turned out to be fairly complex, so I&#8217;m writing some notes here on how it works.<\/p>\n<p>The language requires that panic runs the deferred functions before unwinding the stack.  This means that if the deferred function calls <code>runtime.Callers<\/code> (which doesn&#8217;t work in gccgo, but never mind, it will eventually) it gets a full backtrace of where the call to panic occurred.  If the language did not work that way, it would be difficult to use <code>recover<\/code> as a general error handling mechanism, because there would be no good way to dump a stack trace.  Building up a stack trace through each deferred function call would be inefficient.<\/p>\n<p>The language also requires that <code>recover<\/code> only return a value when it is called directly from a function run by a <code>defer<\/code> statement.  Otherwise it would be difficult for a deferred function to call a function which uses <code>panic<\/code> and <code>recover<\/code> for error handling; the <code>recover<\/code> might pick up the <code>panic<\/code> for its caller, which would be confusing.<\/p>\n<p>As a general gccgo principle I wanted to avoid requiring new gcc backend features.  That raised some difficulty in implementing these Go language requirements.  How can the <code>recover<\/code> function know whether it is being invoked directly by a function started by <code>defer<\/code>?  In 6g, walking up the stack is efficient.  The <code>panic<\/code> function can record its stack position, and the <code>recover<\/code> function can verify that it is at the correct distance below.  In gccgo, there is no mechanism for reliably walking up the stack other than exception stack unwinding, which does not provide a helpful API.  Even if it did, gccgo&#8217;s split stack code can introduce random additional stack frames which are painful to account for.  And there is no good way for <code>panic<\/code> to mark the stack in gccgo.<\/p>\n<p>What I did instead was have the <code>defer<\/code> statement check whether the function is it deferring might call <code>recover<\/code> (e.g., it definitely calls <code>recover<\/code>, or it is a function pointer so we don&#8217;t know).  In that case, the <code>defer<\/code> statement arranges to have the deferred thunk record the return address of the deferred function at the top of the defer stack.  This value is obtained via gcc&#8217;s address-of-label extension, so no new feature was required.  This gives us a value which a function which calls <code>recover<\/code> can check, because a function can always reliably determine its own return address via gcc&#8217;s <code>__builtin_return_address<\/code> function.<\/p>\n<p>However, if the stack is split, then <code>__builtin_return_address<\/code> will return the address of the stack splitting cleanup code rather than the real caller.  To avoid that problem, a function which calls <code>recover<\/code> is split into two parts.  The first part is a small thunk which is marked to not permit its stack to be split.  This thunk gets its return address and checks whether it is being invoked directly from defer.  It passes this as a new boolean parameter to the real function, which does permit a split stack.  The real function checks the new parameter before calling <code>recover<\/code>; if it is false, it just produces a <code>nil<\/code> rather than calling <code>recover<\/code>.  The real function is marked uninlinable, to ensure that it is not inlined into its only call site, which could blow out the stack.<\/p>\n<p>That is sufficient to let us know whether <code>recover<\/code> should return a panic value if there is one, at the cost of having an extra thunk for every function which calls <code>recover<\/code>.  Now we can look at the <code>panic<\/code> function.  It walks up the defer stack, calling functions as it goes.  When a function sucessfully calls <code>recover<\/code>, the panic stack is marked.  This stops the calls to the deferred functions, and starts a stack unwind phase.  The stack unwinding is done exactly the way that g++ handles exceptions.  The g++ exception mechanism is general and cross-language, so this part was relatively easy.  This means that every function that calls <code>recover<\/code> has an exception handler.  The exception handlers are all the same: if this is the function in which <code>recover<\/code> returned a value, then simply return from the current function, effectively stopping the stack unwind.  If this is not the function in which <code>recover<\/code> returned a value, then resume the stack unwinding, just as though the exception were rethrown in C++.<\/p>\n<p>This system is somewhat baroque but it appears to be working.  Everything is reasonably efficient except for a call to <code>recover<\/code> which does not return <code>nil<\/code>; that is as expensive as a C++ exception.  Perhaps I will think of ways to simplify it over time.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Back in March Go picked up a dynamic exception mechanism. Previously, when code called the panic function, it simply aborted your program. Now, it walks up the stack to the top of the currently running goroutine, running all deferred functions. More interestingly, if a deferred function calls the new recover function, the panic is interrupted, [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[6],"tags":[],"class_list":["post-376","post","type-post","status-publish","format-standard","hentry","category-programming"],"_links":{"self":[{"href":"https:\/\/www.airs.com\/blog\/wp-json\/wp\/v2\/posts\/376","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.airs.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.airs.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.airs.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.airs.com\/blog\/wp-json\/wp\/v2\/comments?post=376"}],"version-history":[{"count":1,"href":"https:\/\/www.airs.com\/blog\/wp-json\/wp\/v2\/posts\/376\/revisions"}],"predecessor-version":[{"id":377,"href":"https:\/\/www.airs.com\/blog\/wp-json\/wp\/v2\/posts\/376\/revisions\/377"}],"wp:attachment":[{"href":"https:\/\/www.airs.com\/blog\/wp-json\/wp\/v2\/media?parent=376"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.airs.com\/blog\/wp-json\/wp\/v2\/categories?post=376"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.airs.com\/blog\/wp-json\/wp\/v2\/tags?post=376"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}