Skip to content

Instantly share code, notes, and snippets.

@jorendorff
Created February 10, 2012 20:04
Show Gist options
  • Save jorendorff/1792357 to your computer and use it in GitHub Desktop.
Save jorendorff/1792357 to your computer and use it in GitHub Desktop.
/*
* This is for writing C++ code that does the equivalent of JS try/finally.
* Normal infallible C++ cleanup code should just go in a C++ destructor.
* This is for cleanup that should be *skipped* in the unusual case that
* the try block exits with an uncatchable error (OOM or timeout).
*
* Use it like this:
*
* bool ok;
* ... YOUR TRY-BLOCK CODE HERE ...
* if (AutoFinallyBlock afb(cx, ok)) {
* bool cleanupOK;
* ... YOUR CLEANUP CODE HERE ...
* ok = afb.leave(closedOK);
* }
* return ok;
*
*/
class AutoFinallyBlock {
private:
JSContext *cx;
Value exception;
enum { OK = JS_GENERIC_MAGIC, DONE = JS_THIS_POISON };
public:
AutoFinallyBlock(JSContext *cx, bool ok) : cx(cx), ok(ok) {
if (ok) {
/* Case 1: try-block exited successfully. */
JS_ASSERT(!cx->isExceptionPending());
exception.setMagic(OK);
} else if (cx->isExceptionPending()) {
/* Case 2: try-block threw an exception. */
exception = cx->getPendingException();
cx->clearPendingException();
} else {
/*
* Case 3: try-block died with an uncatchable error. We're done
* here; the caller will skip the finally block.
*/
exception.setMagic(DONE);
}
}
operator bool() const {
return !exception.isParticularMagic(DONE);
}
void debugSetDone() {
#ifdef DEBUG
exception.setMagic(DONE);
#endif
}
void leave(bool cleanupOK) {
/*
* There are four possible cases:
* was ok and now cleanupOK - do nothing, return true
* was ok but now !cleanupOK - do nothing, return false
* had exception and now cleanupOK - restore exception, return false
* had exception and now !cleanupOK - do nothing, return false
*
* This method MUST NOT be called if we had an uncatchable error.
* The isMagic call here enforces that with an assertion.
*/
if (exception.isMagic(JS_EVERYTHING_IS_FINE)) {
debugSetDone();
return cleanupOK;
}
if (cleanupOK)
cx->setPendingException(exception);
debugSetDone();
return false;
}
~AutoFinallyBlock() {
JS_ASSERT(exception.isParticularMagic(DONE));
}
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment