Codemod: use #include angle brackets in folly and thrift
[folly.git] / folly / ExceptionWrapper.h
1 /*
2  * Copyright 2014 Facebook, Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *   http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #ifndef FOLLY_EXCEPTIONWRAPPER_H
18 #define FOLLY_EXCEPTIONWRAPPER_H
19
20 #include <cassert>
21 #include <exception>
22 #include <memory>
23 #include <folly/detail/ExceptionWrapper.h>
24
25 namespace folly {
26
27 /*
28  * Throwing exceptions can be a convenient way to handle errors. Storing
29  * exceptions in an exception_ptr makes it easy to handle exceptions in a
30  * different thread or at a later time. exception_ptr can also be used in a very
31  * generic result/exception wrapper.
32  *
33  * However, there are some issues with throwing exceptions and
34  * std::exception_ptr. These issues revolve around throw being expensive,
35  * particularly in a multithreaded environment (see
36  * ExceptionWrapperBenchmark.cpp).
37  *
38  * Imagine we have a library that has an API which returns a result/exception
39  * wrapper. Let's consider some approaches for implementing this wrapper.
40  * First, we could store a std::exception. This approach loses the derived
41  * exception type, which can make exception handling more difficult for users
42  * that prefer rethrowing the exception. We could use a folly::dynamic for every
43  * possible type of exception. This is not very flexible - adding new types of
44  * exceptions requires a change to the result/exception wrapper. We could use an
45  * exception_ptr. However, constructing an exception_ptr as well as accessing
46  * the error requires a call to throw. That means that there will be two calls
47  * to throw in order to process the exception. For performance sensitive
48  * applications, this may be unacceptable.
49  *
50  * exception_wrapper is designed to handle exception management for both
51  * convenience and high performance use cases. make_exception_wrapper is
52  * templated on derived type, allowing us to rethrow the exception properly for
53  * users that prefer convenience. exception_wrapper is flexible enough to accept
54  * any std::exception. For performance sensitive applications, exception_wrapper
55  * exposes a get() function. These users can use dynamic_cast to retrieve
56  * desired derived types (hence the decision to limit usage to just
57  * std::exception instead of void*).
58  *
59  * Example usage:
60  *
61  * exception_wrapper globalExceptionWrapper;
62  *
63  * // Thread1
64  * void doSomethingCrazy() {
65  *   int rc = doSomethingCrazyWithLameReturnCodes();
66  *   if (rc == NAILED_IT) {
67  *     globalExceptionWrapper = exception_wrapper();
68  *   } else if (rc == FACE_PLANT) {
69  *     globalExceptionWrapper = make_exception_wrapper<FacePlantException>();
70  *   } else if (rc == FAIL_WHALE) {
71  *     globalExceptionWrapper = make_exception_wrapper<FailWhaleException>();
72  *   }
73  * }
74  *
75  * // Thread2: Exceptions are ok!
76  * void processResult() {
77  *   try {
78  *     globalExceptionWrapper.throwException();
79  *   } catch (const FacePlantException& e) {
80  *     LOG(ERROR) << "FACEPLANT!";
81  *   } catch (const FailWhaleException& e) {
82  *     LOG(ERROR) << "FAILWHALE!";
83  *   }
84  * }
85  *
86  * // Thread2: Exceptions are bad!
87  * void processResult() {
88  *   auto ep = globalExceptionWrapper.get();
89  *   if (ep) {
90  *     auto faceplant = dynamic_cast<FacePlantException*>(ep);
91  *     if (faceplant) {
92  *       LOG(ERROR) << "FACEPLANT";
93  *     } else {
94  *       auto failwhale = dynamic_cast<FailWhaleException*>(ep);
95  *       if (failwhale) {
96  *         LOG(ERROR) << "FAILWHALE!";
97  *       }
98  *     }
99  *   }
100  * }
101  *
102  */
103 class exception_wrapper {
104  public:
105   exception_wrapper() : throwfn_(nullptr) { }
106
107   void throwException() const {
108     if (throwfn_) {
109       throwfn_(item_.get());
110     }
111   }
112
113   std::exception* get() { return item_.get(); }
114   const std::exception* get() const { return item_.get(); }
115
116   std::exception* operator->() { return get(); }
117   const std::exception* operator->() const { return get(); }
118
119   std::exception& operator*() { assert(get()); return *get(); }
120   const std::exception& operator*() const { assert(get()); return *get(); }
121
122   explicit operator bool() const { return get(); }
123
124   std::exception_ptr getExceptionPtr() const {
125     try {
126       throwException();
127     } catch (...) {
128       return std::current_exception();
129     }
130     return std::exception_ptr();
131   }
132
133  protected:
134   std::shared_ptr<std::exception> item_;
135   void (*throwfn_)(std::exception*);
136
137   template <class T, class... Args>
138   friend exception_wrapper make_exception_wrapper(Args&&... args);
139 };
140
141 template <class T, class... Args>
142 exception_wrapper make_exception_wrapper(Args&&... args) {
143   exception_wrapper ew;
144   ew.item_ = std::make_shared<T>(std::forward<Args>(args)...);
145   ew.throwfn_ = folly::detail::Thrower<T>::doThrow;
146   return ew;
147 }
148
149 /*
150  * try_and_catch is a simple replacement for try {} catch(){} that allows you to
151  * specify which derived exceptions you would like to catch and store in an
152  * exception_wrapper.
153  *
154  * Because we cannot build an equivalent of std::current_exception(), we need
155  * to catch every derived exception that we are interested in catching.
156  *
157  * Exceptions should be listed in the reverse order that you would write your
158  * catch statements (that is, std::exception& should be first).
159  *
160  * NOTE: Although implemented as a derived class (for syntactic delight), don't
161  * be confused - you should not pass around try_and_catch objects!
162  *
163  * Example Usage:
164  *
165  * // This catches my runtime_error and if I call throwException() on ew, it
166  * // will throw a runtime_error
167  * auto ew = folly::try_and_catch<std::exception, std::runtime_error>([=]() {
168  *   if (badThingHappens()) {
169  *     throw std::runtime_error("ZOMG!");
170  *   }
171  * });
172  *
173  * // This will catch the exception and if I call throwException() on ew, it
174  * // will throw a std::exception
175  * auto ew = folly::try_and_catch<std::exception, std::runtime_error>([=]() {
176  *   if (badThingHappens()) {
177  *     throw std::exception();
178  *   }
179  * });
180  *
181  * // This will not catch the exception and it will be thrown.
182  * auto ew = folly::try_and_catch<std::runtime_error>([=]() {
183  *   if (badThingHappens()) {
184  *     throw std::exception();
185  *   }
186  * });
187  */
188
189 template <typename... Exceptions>
190 class try_and_catch;
191
192 template <typename LastException, typename... Exceptions>
193 class try_and_catch<LastException, Exceptions...> :
194     public try_and_catch<Exceptions...> {
195  public:
196   template <typename F>
197   explicit try_and_catch(F&& fn) : Base() {
198     call_fn(fn);
199   }
200
201  protected:
202   typedef try_and_catch<Exceptions...> Base;
203
204   try_and_catch() : Base() {}
205
206   template <typename F>
207   void call_fn(F&& fn) {
208     try {
209       Base::call_fn(std::move(fn));
210     } catch (const LastException& e) {
211       this->item_ = std::make_shared<LastException>(e);
212       this->throwfn_ = folly::detail::Thrower<LastException>::doThrow;
213     }
214   }
215 };
216
217 template<>
218 class try_and_catch<> : public exception_wrapper {
219  public:
220   try_and_catch() {}
221
222  protected:
223   template <typename F>
224   void call_fn(F&& fn) {
225     fn();
226   }
227 };
228 }
229 #endif