fixing compiler errors when compiling with Wunused-parameter
[folly.git] / folly / ExceptionWrapper.h
1 /*
2  * Copyright 2015 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/String.h>
24 #include <folly/detail/ExceptionWrapper.h>
25
26 namespace folly {
27
28 /*
29  * Throwing exceptions can be a convenient way to handle errors. Storing
30  * exceptions in an exception_ptr makes it easy to handle exceptions in a
31  * different thread or at a later time. exception_ptr can also be used in a very
32  * generic result/exception wrapper.
33  *
34  * However, there are some issues with throwing exceptions and
35  * std::exception_ptr. These issues revolve around throw being expensive,
36  * particularly in a multithreaded environment (see
37  * ExceptionWrapperBenchmark.cpp).
38  *
39  * Imagine we have a library that has an API which returns a result/exception
40  * wrapper. Let's consider some approaches for implementing this wrapper.
41  * First, we could store a std::exception. This approach loses the derived
42  * exception type, which can make exception handling more difficult for users
43  * that prefer rethrowing the exception. We could use a folly::dynamic for every
44  * possible type of exception. This is not very flexible - adding new types of
45  * exceptions requires a change to the result/exception wrapper. We could use an
46  * exception_ptr. However, constructing an exception_ptr as well as accessing
47  * the error requires a call to throw. That means that there will be two calls
48  * to throw in order to process the exception. For performance sensitive
49  * applications, this may be unacceptable.
50  *
51  * exception_wrapper is designed to handle exception management for both
52  * convenience and high performance use cases. make_exception_wrapper is
53  * templated on derived type, allowing us to rethrow the exception properly for
54  * users that prefer convenience. These explicitly named exception types can
55  * therefore be handled without any peformance penalty.  exception_wrapper is
56  * also flexible enough to accept any type. If a caught exception is not of an
57  * explicitly named type, then std::exception_ptr is used to preserve the
58  * exception state. For performance sensitive applications, the accessor methods
59  * can test or extract a pointer to a specific exception type with very little
60  * overhead.
61  *
62  * Example usage:
63  *
64  * exception_wrapper globalExceptionWrapper;
65  *
66  * // Thread1
67  * void doSomethingCrazy() {
68  *   int rc = doSomethingCrazyWithLameReturnCodes();
69  *   if (rc == NAILED_IT) {
70  *     globalExceptionWrapper = exception_wrapper();
71  *   } else if (rc == FACE_PLANT) {
72  *     globalExceptionWrapper = make_exception_wrapper<FacePlantException>();
73  *   } else if (rc == FAIL_WHALE) {
74  *     globalExceptionWrapper = make_exception_wrapper<FailWhaleException>();
75  *   }
76  * }
77  *
78  * // Thread2: Exceptions are ok!
79  * void processResult() {
80  *   try {
81  *     globalExceptionWrapper.throwException();
82  *   } catch (const FacePlantException& e) {
83  *     LOG(ERROR) << "FACEPLANT!";
84  *   } catch (const FailWhaleException& e) {
85  *     LOG(ERROR) << "FAILWHALE!";
86  *   }
87  * }
88  *
89  * // Thread2: Exceptions are bad!
90  * void processResult() {
91  *   auto ep = globalExceptionWrapper.get();
92  *   if (!ep.with_exception<FacePlantException>([&](
93  *     FacePlantException& faceplant) {
94  *       LOG(ERROR) << "FACEPLANT";
95  *     })) {
96  *     ep.with_exception<FailWhaleException>([&](
97  *       FailWhaleException& failwhale) {
98  *         LOG(ERROR) << "FAILWHALE!";
99  *       });
100  *   }
101  * }
102  *
103  */
104 class exception_wrapper {
105  protected:
106   template <typename Ex>
107   struct optimize;
108
109  public:
110   exception_wrapper() = default;
111
112   // Implicitly construct an exception_wrapper from a qualifying exception.
113   // See the optimize struct for details.
114   template <typename Ex, typename =
115     typename std::enable_if<optimize<typename std::decay<Ex>::type>::value>
116     ::type>
117   /* implicit */ exception_wrapper(Ex&& exn) {
118     typedef typename std::decay<Ex>::type DEx;
119     item_ = std::make_shared<DEx>(std::forward<Ex>(exn));
120     throwfn_ = folly::detail::Thrower<DEx>::doThrow;
121   }
122
123   // The following two constructors are meant to emulate the behavior of
124   // try_and_catch in performance sensitive code as well as to be flexible
125   // enough to wrap exceptions of unknown type. There is an overload that
126   // takes an exception reference so that the wrapper can extract and store
127   // the exception's type and what() when possible.
128   //
129   // The canonical use case is to construct an all-catching exception wrapper
130   // with minimal overhead like so:
131   //
132   //   try {
133   //     // some throwing code
134   //   } catch (const std::exception& e) {
135   //     // won't lose e's type and what()
136   //     exception_wrapper ew{std::current_exception(), e};
137   //   } catch (...) {
138   //     // everything else
139   //     exception_wrapper ew{std::current_exception()};
140   //   }
141   //
142   // try_and_catch is cleaner and preferable. Use it unless you're sure you need
143   // something like this instead.
144   template <typename Ex>
145   explicit exception_wrapper(std::exception_ptr eptr, Ex& exn) {
146     assign_eptr(eptr, exn);
147   }
148
149   explicit exception_wrapper(std::exception_ptr eptr) {
150     assign_eptr(eptr);
151   }
152
153   void throwException() const {
154     if (throwfn_) {
155       throwfn_(item_.get());
156     } else if (eptr_) {
157       std::rethrow_exception(eptr_);
158     }
159   }
160
161   explicit operator bool() const {
162     return item_ || eptr_;
163   }
164
165   // This implementation is similar to std::exception_ptr's implementation
166   // where two exception_wrappers are equal when the address in the underlying
167   // reference field both point to the same exception object.  The reference
168   // field remains the same when the exception_wrapper is copied or when
169   // the exception_wrapper is "rethrown".
170   bool operator==(const exception_wrapper& a) const {
171     if (item_) {
172       return a.item_ && item_.get() == a.item_.get();
173     } else {
174       return eptr_ == a.eptr_;
175     }
176   }
177
178   bool operator!=(const exception_wrapper& a) const {
179     return !(*this == a);
180   }
181
182   // This will return a non-nullptr only if the exception is held as a
183   // copy.  It is the only interface which will distinguish between an
184   // exception held this way, and by exception_ptr.  You probably
185   // shouldn't use it at all.
186   std::exception* getCopied() { return item_.get(); }
187   const std::exception* getCopied() const { return item_.get(); }
188
189   fbstring what() const {
190     if (item_) {
191       return exceptionStr(*item_);
192     } else if (eptr_) {
193       return estr_;
194     } else {
195       return fbstring();
196     }
197   }
198
199   fbstring class_name() const {
200     if (item_) {
201       auto& i = *item_;
202       return demangle(typeid(i));
203     } else if (eptr_) {
204       return ename_;
205     } else {
206       return fbstring();
207     }
208   }
209
210   template <class Ex>
211   bool is_compatible_with() const {
212     if (item_) {
213       return dynamic_cast<const Ex*>(item_.get());
214     } else if (eptr_) {
215       try {
216         std::rethrow_exception(eptr_);
217       } catch (std::exception& e) {
218         return dynamic_cast<const Ex*>(&e);
219       } catch (...) {
220         // fall through
221       }
222     }
223     return false;
224   }
225
226   // If this exception wrapper wraps an exception of type Ex, with_exception
227   // will call f with the wrapped exception as an argument and return true, and
228   // will otherwise return false.
229   template <class Ex, class F>
230   typename std::enable_if<
231     std::is_base_of<std::exception, typename std::decay<Ex>::type>::value,
232     bool>::type
233   with_exception(F f) {
234     return with_exception1<typename std::decay<Ex>::type>(f, this);
235   }
236
237   // Const overload
238   template <class Ex, class F>
239   typename std::enable_if<
240     std::is_base_of<std::exception, typename std::decay<Ex>::type>::value,
241     bool>::type
242   with_exception(F f) const {
243     return with_exception1<const typename std::decay<Ex>::type>(f, this);
244   }
245
246   // Overload for non-exceptions. Always rethrows.
247   template <class Ex, class F>
248   typename std::enable_if<
249     !std::is_base_of<std::exception, typename std::decay<Ex>::type>::value,
250     bool>::type
251   with_exception(F f) const {
252     try {
253       throwException();
254     } catch (typename std::decay<Ex>::type& e) {
255       f(e);
256       return true;
257     } catch (...) {
258       // fall through
259     }
260     return false;
261   }
262
263   std::exception_ptr getExceptionPtr() const {
264     if (eptr_) {
265       return eptr_;
266     }
267
268     try {
269       throwException();
270     } catch (...) {
271       return std::current_exception();
272     }
273     return std::exception_ptr();
274   }
275
276 protected:
277   template <typename Ex>
278   struct optimize {
279     static const bool value =
280       std::is_base_of<std::exception, Ex>::value &&
281       std::is_copy_assignable<Ex>::value &&
282       !std::is_abstract<Ex>::value;
283   };
284
285   template <typename Ex>
286   void assign_eptr(std::exception_ptr eptr, Ex& e) {
287     this->eptr_ = eptr;
288     this->estr_ = exceptionStr(e).toStdString();
289     this->ename_ = demangle(typeid(e)).toStdString();
290   }
291
292   void assign_eptr(std::exception_ptr eptr) {
293     this->eptr_ = eptr;
294   }
295
296   // Optimized case: if we know what type the exception is, we can
297   // store a copy of the concrete type, and a helper function so we
298   // can rethrow it.
299   std::shared_ptr<std::exception> item_;
300   void (*throwfn_)(std::exception*){nullptr};
301   // Fallback case: store the library wrapper, which is less efficient
302   // but gets the job done.  Also store exceptionPtr() the name of the
303   // exception type, so we can at least get those back out without
304   // having to rethrow.
305   std::exception_ptr eptr_;
306   std::string estr_;
307   std::string ename_;
308
309   template <class T, class... Args>
310   friend exception_wrapper make_exception_wrapper(Args&&... args);
311
312 private:
313   // What makes this useful is that T can be exception_wrapper* or
314   // const exception_wrapper*, and the compiler will use the
315   // instantiation which works with F.
316   template <class Ex, class F, class T>
317   static bool with_exception1(F f, T* that) {
318     if (that->item_) {
319       if (auto ex = dynamic_cast<Ex*>(that->item_.get())) {
320         f(*ex);
321         return true;
322       }
323     } else if (that->eptr_) {
324       try {
325         std::rethrow_exception(that->eptr_);
326       } catch (std::exception& e) {
327         if (auto ex = dynamic_cast<Ex*>(&e)) {
328           f(*ex);
329           return true;
330         }
331       } catch (...) {
332         // fall through
333       }
334     }
335     return false;
336   }
337 };
338
339 template <class T, class... Args>
340 exception_wrapper make_exception_wrapper(Args&&... args) {
341   exception_wrapper ew;
342   ew.item_ = std::make_shared<T>(std::forward<Args>(args)...);
343   ew.throwfn_ = folly::detail::Thrower<T>::doThrow;
344   return ew;
345 }
346
347 // For consistency with exceptionStr() functions in String.h
348 inline fbstring exceptionStr(const exception_wrapper& ew) {
349   return ew.what();
350 }
351
352 /*
353  * try_and_catch is a simple replacement for try {} catch(){} that allows you to
354  * specify which derived exceptions you would like to catch and store in an
355  * exception_wrapper.
356  *
357  * Because we cannot build an equivalent of std::current_exception(), we need
358  * to catch every derived exception that we are interested in catching.
359  *
360  * Exceptions should be listed in the reverse order that you would write your
361  * catch statements (that is, std::exception& should be first).
362  *
363  * NOTE: Although implemented as a derived class (for syntactic delight), don't
364  * be confused - you should not pass around try_and_catch objects!
365  *
366  * Example Usage:
367  *
368  * // This catches my runtime_error and if I call throwException() on ew, it
369  * // will throw a runtime_error
370  * auto ew = folly::try_and_catch<std::exception, std::runtime_error>([=]() {
371  *   if (badThingHappens()) {
372  *     throw std::runtime_error("ZOMG!");
373  *   }
374  * });
375  *
376  * // This will catch the exception and if I call throwException() on ew, it
377  * // will throw a std::exception
378  * auto ew = folly::try_and_catch<std::exception, std::runtime_error>([=]() {
379  *   if (badThingHappens()) {
380  *     throw std::exception();
381  *   }
382  * });
383  *
384  * // This will not catch the exception and it will be thrown.
385  * auto ew = folly::try_and_catch<std::runtime_error>([=]() {
386  *   if (badThingHappens()) {
387  *     throw std::exception();
388  *   }
389  * });
390  */
391
392 template <typename... Exceptions>
393 class try_and_catch;
394
395 template <typename LastException, typename... Exceptions>
396 class try_and_catch<LastException, Exceptions...> :
397     public try_and_catch<Exceptions...> {
398  public:
399   template <typename F>
400   explicit try_and_catch(F&& fn) : Base() {
401     call_fn(fn);
402   }
403
404  protected:
405   typedef try_and_catch<Exceptions...> Base;
406
407   try_and_catch() : Base() {}
408
409   template <typename Ex>
410   typename std::enable_if<!exception_wrapper::optimize<Ex>::value>::type
411   assign_exception(Ex& e, std::exception_ptr eptr) {
412     exception_wrapper::assign_eptr(eptr, e);
413   }
414
415   template <typename Ex>
416   typename std::enable_if<exception_wrapper::optimize<Ex>::value>::type
417   assign_exception(Ex& e, std::exception_ptr /*eptr*/) {
418     this->item_ = std::make_shared<Ex>(e);
419     this->throwfn_ = folly::detail::Thrower<Ex>::doThrow;
420   }
421
422   template <typename F>
423   void call_fn(F&& fn) {
424     try {
425       Base::call_fn(std::move(fn));
426     } catch (LastException& e) {
427       if (typeid(e) == typeid(LastException&)) {
428         assign_exception(e, std::current_exception());
429       } else {
430         exception_wrapper::assign_eptr(std::current_exception(), e);
431       }
432     }
433   }
434 };
435
436 template<>
437 class try_and_catch<> : public exception_wrapper {
438  public:
439   try_and_catch() = default;
440
441  protected:
442   template <typename F>
443   void call_fn(F&& fn) {
444     fn();
445   }
446 };
447 }
448 #endif