Add mechanizm for caching local and peer addresses in AsyncSSLSocket.
[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  *   globalExceptionWrapper.with_exception(
92  *       [&](FacePlantException& faceplant) {
93  *         LOG(ERROR) << "FACEPLANT";
94  *       }) ||
95  *   globalExceptionWrapper.with_exception(
96  *       [&](FailWhaleException& failwhale) {
97  *         LOG(ERROR) << "FAILWHALE!";
98  *       }) ||
99  *   LOG(FATAL) << "Unrecognized exception";
100  * }
101  *
102  */
103 class exception_wrapper {
104  protected:
105   template <typename Ex>
106   struct optimize;
107
108  public:
109   exception_wrapper() = default;
110
111   // Implicitly construct an exception_wrapper from a qualifying exception.
112   // See the optimize struct for details.
113   template <typename Ex, typename =
114     typename std::enable_if<optimize<typename std::decay<Ex>::type>::value>
115     ::type>
116   /* implicit */ exception_wrapper(Ex&& exn) {
117     typedef typename std::decay<Ex>::type DEx;
118     item_ = std::make_shared<DEx>(std::forward<Ex>(exn));
119     throwfn_ = folly::detail::Thrower<DEx>::doThrow;
120   }
121
122   // The following two constructors are meant to emulate the behavior of
123   // try_and_catch in performance sensitive code as well as to be flexible
124   // enough to wrap exceptions of unknown type. There is an overload that
125   // takes an exception reference so that the wrapper can extract and store
126   // the exception's type and what() when possible.
127   //
128   // The canonical use case is to construct an all-catching exception wrapper
129   // with minimal overhead like so:
130   //
131   //   try {
132   //     // some throwing code
133   //   } catch (const std::exception& e) {
134   //     // won't lose e's type and what()
135   //     exception_wrapper ew{std::current_exception(), e};
136   //   } catch (...) {
137   //     // everything else
138   //     exception_wrapper ew{std::current_exception()};
139   //   }
140   //
141   // try_and_catch is cleaner and preferable. Use it unless you're sure you need
142   // something like this instead.
143   template <typename Ex>
144   explicit exception_wrapper(std::exception_ptr eptr, Ex& exn) {
145     assign_eptr(eptr, exn);
146   }
147
148   explicit exception_wrapper(std::exception_ptr eptr) {
149     assign_eptr(eptr);
150   }
151
152   void throwException() const {
153     if (throwfn_) {
154       throwfn_(item_.get());
155     } else if (eptr_) {
156       std::rethrow_exception(eptr_);
157     }
158   }
159
160   explicit operator bool() const {
161     return item_ || eptr_;
162   }
163
164   // This implementation is similar to std::exception_ptr's implementation
165   // where two exception_wrappers are equal when the address in the underlying
166   // reference field both point to the same exception object.  The reference
167   // field remains the same when the exception_wrapper is copied or when
168   // the exception_wrapper is "rethrown".
169   bool operator==(const exception_wrapper& a) const {
170     if (item_) {
171       return a.item_ && item_.get() == a.item_.get();
172     } else {
173       return eptr_ == a.eptr_;
174     }
175   }
176
177   bool operator!=(const exception_wrapper& a) const {
178     return !(*this == a);
179   }
180
181   // This will return a non-nullptr only if the exception is held as a
182   // copy.  It is the only interface which will distinguish between an
183   // exception held this way, and by exception_ptr.  You probably
184   // shouldn't use it at all.
185   std::exception* getCopied() { return item_.get(); }
186   const std::exception* getCopied() const { return item_.get(); }
187
188   fbstring what() const {
189     if (item_) {
190       return exceptionStr(*item_);
191     } else if (eptr_) {
192       return estr_;
193     } else {
194       return fbstring();
195     }
196   }
197
198   fbstring class_name() const {
199     if (item_) {
200       auto& i = *item_;
201       return demangle(typeid(i));
202     } else if (eptr_) {
203       return ename_;
204     } else {
205       return fbstring();
206     }
207   }
208
209   template <class Ex>
210   bool is_compatible_with() const {
211     if (item_) {
212       return dynamic_cast<const Ex*>(item_.get());
213     } else if (eptr_) {
214       try {
215         std::rethrow_exception(eptr_);
216       } catch (typename std::decay<Ex>::type&) {
217         return true;
218       } catch (...) {
219         // fall through
220       }
221     }
222     return false;
223   }
224
225   template <class F>
226   bool with_exception(F&& f) {
227     using arg_type = typename functor_traits<F>::arg_type_decayed;
228     return with_exception<arg_type>(std::forward<F>(f));
229   }
230
231   template <class F>
232   bool with_exception(F&& f) const {
233     using arg_type = typename functor_traits<F>::arg_type_decayed;
234     return with_exception<const arg_type>(std::forward<F>(f));
235   }
236
237   // If this exception wrapper wraps an exception of type Ex, with_exception
238   // will call f with the wrapped exception as an argument and return true, and
239   // will otherwise return false.
240   template <class Ex, class F>
241   typename std::enable_if<
242     std::is_base_of<std::exception, typename std::decay<Ex>::type>::value,
243     bool>::type
244   with_exception(F f) {
245     return with_exception1<typename std::decay<Ex>::type>(f, this);
246   }
247
248   // Const overload
249   template <class Ex, class F>
250   typename std::enable_if<
251     std::is_base_of<std::exception, typename std::decay<Ex>::type>::value,
252     bool>::type
253   with_exception(F f) const {
254     return with_exception1<const typename std::decay<Ex>::type>(f, this);
255   }
256
257   // Overload for non-exceptions. Always rethrows.
258   template <class Ex, class F>
259   typename std::enable_if<
260     !std::is_base_of<std::exception, typename std::decay<Ex>::type>::value,
261     bool>::type
262   with_exception(F f) const {
263     try {
264       throwException();
265     } catch (typename std::decay<Ex>::type& e) {
266       f(e);
267       return true;
268     } catch (...) {
269       // fall through
270     }
271     return false;
272   }
273
274   std::exception_ptr getExceptionPtr() const {
275     if (eptr_) {
276       return eptr_;
277     }
278
279     try {
280       throwException();
281     } catch (...) {
282       return std::current_exception();
283     }
284     return std::exception_ptr();
285   }
286
287 protected:
288   template <typename Ex>
289   struct optimize {
290     static const bool value =
291       std::is_base_of<std::exception, Ex>::value &&
292       std::is_copy_assignable<Ex>::value &&
293       !std::is_abstract<Ex>::value;
294   };
295
296   template <typename Ex>
297   void assign_eptr(std::exception_ptr eptr, Ex& e) {
298     this->eptr_ = eptr;
299     this->estr_ = exceptionStr(e).toStdString();
300     this->ename_ = demangle(typeid(e)).toStdString();
301   }
302
303   void assign_eptr(std::exception_ptr eptr) {
304     this->eptr_ = eptr;
305   }
306
307   // Optimized case: if we know what type the exception is, we can
308   // store a copy of the concrete type, and a helper function so we
309   // can rethrow it.
310   std::shared_ptr<std::exception> item_;
311   void (*throwfn_)(std::exception*){nullptr};
312   // Fallback case: store the library wrapper, which is less efficient
313   // but gets the job done.  Also store exceptionPtr() the name of the
314   // exception type, so we can at least get those back out without
315   // having to rethrow.
316   std::exception_ptr eptr_;
317   std::string estr_;
318   std::string ename_;
319
320   template <class T, class... Args>
321   friend exception_wrapper make_exception_wrapper(Args&&... args);
322
323 private:
324   template <typename F>
325   struct functor_traits {
326     template <typename T>
327     struct impl;
328     template <typename C, typename R, typename A>
329     struct impl<R(C::*)(A)> { using arg_type = A; };
330     template <typename C, typename R, typename A>
331     struct impl<R(C::*)(A) const> { using arg_type = A; };
332     using functor_decayed = typename std::decay<F>::type;
333     using functor_op = decltype(&functor_decayed::operator());
334     using arg_type = typename impl<functor_op>::arg_type;
335     using arg_type_decayed = typename std::decay<arg_type>::type;
336   };
337
338   // What makes this useful is that T can be exception_wrapper* or
339   // const exception_wrapper*, and the compiler will use the
340   // instantiation which works with F.
341   template <class Ex, class F, class T>
342   static bool with_exception1(F f, T* that) {
343     if (that->item_) {
344       if (auto ex = dynamic_cast<Ex*>(that->item_.get())) {
345         f(*ex);
346         return true;
347       }
348     } else if (that->eptr_) {
349       try {
350         std::rethrow_exception(that->eptr_);
351       } catch (Ex& e) {
352         f(e);
353         return true;
354       } catch (...) {
355         // fall through
356       }
357     }
358     return false;
359   }
360 };
361
362 template <class T, class... Args>
363 exception_wrapper make_exception_wrapper(Args&&... args) {
364   exception_wrapper ew;
365   ew.item_ = std::make_shared<T>(std::forward<Args>(args)...);
366   ew.throwfn_ = folly::detail::Thrower<T>::doThrow;
367   return ew;
368 }
369
370 // For consistency with exceptionStr() functions in String.h
371 inline fbstring exceptionStr(const exception_wrapper& ew) {
372   return ew.what();
373 }
374
375 /*
376  * try_and_catch is a simple replacement for try {} catch(){} that allows you to
377  * specify which derived exceptions you would like to catch and store in an
378  * exception_wrapper.
379  *
380  * Because we cannot build an equivalent of std::current_exception(), we need
381  * to catch every derived exception that we are interested in catching.
382  *
383  * Exceptions should be listed in the reverse order that you would write your
384  * catch statements (that is, std::exception& should be first).
385  *
386  * NOTE: Although implemented as a derived class (for syntactic delight), don't
387  * be confused - you should not pass around try_and_catch objects!
388  *
389  * Example Usage:
390  *
391  * // This catches my runtime_error and if I call throwException() on ew, it
392  * // will throw a runtime_error
393  * auto ew = folly::try_and_catch<std::exception, std::runtime_error>([=]() {
394  *   if (badThingHappens()) {
395  *     throw std::runtime_error("ZOMG!");
396  *   }
397  * });
398  *
399  * // This will catch the exception and if I call throwException() on ew, it
400  * // will throw a std::exception
401  * auto ew = folly::try_and_catch<std::exception, std::runtime_error>([=]() {
402  *   if (badThingHappens()) {
403  *     throw std::exception();
404  *   }
405  * });
406  *
407  * // This will not catch the exception and it will be thrown.
408  * auto ew = folly::try_and_catch<std::runtime_error>([=]() {
409  *   if (badThingHappens()) {
410  *     throw std::exception();
411  *   }
412  * });
413  */
414
415 template <typename... Exceptions>
416 class try_and_catch;
417
418 template <typename LastException, typename... Exceptions>
419 class try_and_catch<LastException, Exceptions...> :
420     public try_and_catch<Exceptions...> {
421  public:
422   template <typename F>
423   explicit try_and_catch(F&& fn) : Base() {
424     call_fn(fn);
425   }
426
427  protected:
428   typedef try_and_catch<Exceptions...> Base;
429
430   try_and_catch() : Base() {}
431
432   template <typename Ex>
433   typename std::enable_if<!exception_wrapper::optimize<Ex>::value>::type
434   assign_exception(Ex& e, std::exception_ptr eptr) {
435     exception_wrapper::assign_eptr(eptr, e);
436   }
437
438   template <typename Ex>
439   typename std::enable_if<exception_wrapper::optimize<Ex>::value>::type
440   assign_exception(Ex& e, std::exception_ptr /*eptr*/) {
441     this->item_ = std::make_shared<Ex>(e);
442     this->throwfn_ = folly::detail::Thrower<Ex>::doThrow;
443   }
444
445   template <typename F>
446   void call_fn(F&& fn) {
447     try {
448       Base::call_fn(std::move(fn));
449     } catch (LastException& e) {
450       if (typeid(e) == typeid(LastException&)) {
451         assign_exception(e, std::current_exception());
452       } else {
453         exception_wrapper::assign_eptr(std::current_exception(), e);
454       }
455     }
456   }
457 };
458
459 template<>
460 class try_and_catch<> : public exception_wrapper {
461  public:
462   try_and_catch() = default;
463
464  protected:
465   template <typename F>
466   void call_fn(F&& fn) {
467     fn();
468   }
469 };
470 }
471 #endif