Commit db0922d9 authored by Marc Celani's avatar Marc Celani Committed by Dave Watson

ExceptionWrapper comments

Summary: Comments to discuss motivation

Test Plan: its comments

Reviewed By: delong.j@fb.com

FB internal diff: D1308259

@override-unit-failures
parent ed92986b
...@@ -24,11 +24,87 @@ ...@@ -24,11 +24,87 @@
namespace folly { 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<FacePlantException>();
* } else if (rc == FAIL_WHALE) {
* globalExceptionWrapper = make_exception_wrapper<FailWhaleException>();
* }
* }
*
* // 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<FacePlantException*>(ep);
* if (faceplant) {
* LOG(ERROR) << "FACEPLANT";
* } else {
* auto failwhale = dynamic_cast<FailWhaleException*>(ep);
* if (failwhale) {
* LOG(ERROR) << "FAILWHALE!";
* }
* }
* }
* }
*
*/
class exception_wrapper { class exception_wrapper {
public: public:
exception_wrapper() : throwfn_(nullptr) { } exception_wrapper() : throwfn_(nullptr) { }
void throwException() { void throwException() const {
if (throwfn_) { if (throwfn_) {
throwfn_(item_.get()); throwfn_(item_.get());
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment