From: Marc Celani Date: Fri, 2 May 2014 03:05:29 +0000 (-0700) Subject: ExceptionWrapper comments X-Git-Tag: v0.22.0~567 X-Git-Url: http://plrg.eecs.uci.edu/git/?p=folly.git;a=commitdiff_plain;h=db0922d9d8979570ce47b68d834b5d945ae3f845 ExceptionWrapper comments Summary: Comments to discuss motivation Test Plan: its comments Reviewed By: delong.j@fb.com FB internal diff: D1308259 @override-unit-failures --- diff --git a/folly/ExceptionWrapper.h b/folly/ExceptionWrapper.h index 0a232728..89ef616f 100644 --- a/folly/ExceptionWrapper.h +++ b/folly/ExceptionWrapper.h @@ -24,11 +24,87 @@ namespace folly { +/* + * Throwing exceptions can be a convenient way to handle errors. Storing + * exceptions in an exception_ptr makes it easy to handle exceptions in a + * different thread or at a later time. exception_ptr can also be used in a very + * generic result/exception wrapper. + * + * However, there are some issues with throwing exceptions and + * std::exception_ptr. These issues revolve around throw being expensive, + * particularly in a multithreaded environment (see + * ExceptionWrapperBenchmark.cpp). + * + * Imagine we have a library that has an API which returns a result/exception + * wrapper. Let's consider some approaches for implementing this wrapper. + * First, we could store a std::exception. This approach loses the derived + * exception type, which can make exception handling more difficult for users + * that prefer rethrowing the exception. We could use a folly::dynamic for every + * possible type of exception. This is not very flexible - adding new types of + * exceptions requires a change to the result/exception wrapper. We could use an + * exception_ptr. However, constructing an exception_ptr as well as accessing + * the error requires a call to throw. That means that there will be two calls + * to throw in order to process the exception. For performance sensitive + * applications, this may be unacceptable. + * + * exception_wrapper is designed to handle exception management for both + * convenience and high performance use cases. make_exception_wrapper is + * templated on derived type, allowing us to rethrow the exception properly for + * users that prefer convenience. exception_wrapper is flexible enough to accept + * any std::exception. For performance sensitive applications, exception_wrapper + * exposes a get() function. These users can use dynamic_cast to retrieve + * desired derived types (hence the decision to limit usage to just + * std::exception instead of void*). + * + * Example usage: + * + * exception_wrapper globalExceptionWrapper; + * + * // Thread1 + * void doSomethingCrazy() { + * int rc = doSomethingCrazyWithLameReturnCodes(); + * if (rc == NAILED_IT) { + * globalExceptionWrapper = exception_wrapper(); + * } else if (rc == FACE_PLANT) { + * globalExceptionWrapper = make_exception_wrapper(); + * } else if (rc == FAIL_WHALE) { + * globalExceptionWrapper = make_exception_wrapper(); + * } + * } + * + * // Thread2: Exceptions are ok! + * void processResult() { + * try { + * globalExceptionWrapper.throwException(); + * } catch (const FacePlantException& e) { + * LOG(ERROR) << "FACEPLANT!"; + * } catch (const FailWhaleException& e) { + * LOG(ERROR) << "FAILWHALE!"; + * } + * } + * + * // Thread2: Exceptions are bad! + * void processResult() { + * auto ep = globalExceptionWrapper.get(); + * if (ep) { + * auto faceplant = dynamic_cast(ep); + * if (faceplant) { + * LOG(ERROR) << "FACEPLANT"; + * } else { + * auto failwhale = dynamic_cast(ep); + * if (failwhale) { + * LOG(ERROR) << "FAILWHALE!"; + * } + * } + * } + * } + * + */ class exception_wrapper { public: exception_wrapper() : throwfn_(nullptr) { } - void throwException() { + void throwException() const { if (throwfn_) { throwfn_(item_.get()); }