Thread-safe version of loopKeepAlive()
[folly.git] / folly / ExceptionWrapper.h
1 /*
2  * Copyright 2016 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 #pragma once
18
19 #include <cassert>
20 #include <exception>
21 #include <iostream>
22 #include <memory>
23 #include <folly/ExceptionString.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   // If the exception_wrapper does not contain an exception, std::terminate()
153   // is invoked to assure the [[noreturn]] behaviour.
154   [[noreturn]] void throwException() const {
155     if (throwfn_) {
156       throwfn_(item_.get());
157     } else if (eptr_) {
158       std::rethrow_exception(eptr_);
159     }
160     std::cerr
161         << "Cannot use `throwException` with an empty folly::exception_wrapper"
162         << std::endl;
163     std::terminate();
164   }
165
166   explicit operator bool() const {
167     return item_ || eptr_;
168   }
169
170   // This implementation is similar to std::exception_ptr's implementation
171   // where two exception_wrappers are equal when the address in the underlying
172   // reference field both point to the same exception object.  The reference
173   // field remains the same when the exception_wrapper is copied or when
174   // the exception_wrapper is "rethrown".
175   bool operator==(const exception_wrapper& a) const {
176     if (item_) {
177       return a.item_ && item_.get() == a.item_.get();
178     } else {
179       return eptr_ == a.eptr_;
180     }
181   }
182
183   bool operator!=(const exception_wrapper& a) const {
184     return !(*this == a);
185   }
186
187   // This will return a non-nullptr only if the exception is held as a
188   // copy.  It is the only interface which will distinguish between an
189   // exception held this way, and by exception_ptr.  You probably
190   // shouldn't use it at all.
191   std::exception* getCopied() { return item_.get(); }
192   const std::exception* getCopied() const { return item_.get(); }
193
194   fbstring what() const {
195     if (item_) {
196       return exceptionStr(*item_);
197     } else if (eptr_) {
198       return estr_;
199     } else {
200       return fbstring();
201     }
202   }
203
204   fbstring class_name() const {
205     if (item_) {
206       auto& i = *item_;
207       return demangle(typeid(i));
208     } else if (eptr_) {
209       return ename_;
210     } else {
211       return fbstring();
212     }
213   }
214
215   template <class Ex>
216   bool is_compatible_with() const {
217     if (item_) {
218       return dynamic_cast<const Ex*>(item_.get());
219     } else if (eptr_) {
220       try {
221         std::rethrow_exception(eptr_);
222       } catch (typename std::decay<Ex>::type&) {
223         return true;
224       } catch (...) {
225         // fall through
226       }
227     }
228     return false;
229   }
230
231   template <class F>
232   bool with_exception(F&& f) {
233     using arg_type = typename functor_traits<F>::arg_type_decayed;
234     return with_exception<arg_type>(std::forward<F>(f));
235   }
236
237   template <class F>
238   bool with_exception(F&& f) const {
239     using arg_type = typename functor_traits<F>::arg_type_decayed;
240     return with_exception<const arg_type>(std::forward<F>(f));
241   }
242
243   // If this exception wrapper wraps an exception of type Ex, with_exception
244   // will call f with the wrapped exception as an argument and return true, and
245   // will otherwise return false.
246   template <class Ex, class F>
247   typename std::enable_if<
248     std::is_base_of<std::exception, typename std::decay<Ex>::type>::value,
249     bool>::type
250   with_exception(F f) {
251     return with_exception1<typename std::decay<Ex>::type>(f, this);
252   }
253
254   // Const overload
255   template <class Ex, class F>
256   typename std::enable_if<
257     std::is_base_of<std::exception, typename std::decay<Ex>::type>::value,
258     bool>::type
259   with_exception(F f) const {
260     return with_exception1<const typename std::decay<Ex>::type>(f, this);
261   }
262
263   // Overload for non-exceptions. Always rethrows.
264   template <class Ex, class F>
265   typename std::enable_if<
266     !std::is_base_of<std::exception, typename std::decay<Ex>::type>::value,
267     bool>::type
268   with_exception(F f) const {
269     try {
270       if (*this) {
271         throwException();
272       }
273     } catch (typename std::decay<Ex>::type& e) {
274       f(e);
275       return true;
276     } catch (...) {
277       // fall through
278     }
279     return false;
280   }
281
282   std::exception_ptr getExceptionPtr() const {
283     if (eptr_) {
284       return eptr_;
285     }
286
287     try {
288       if (*this) {
289         throwException();
290       }
291     } catch (...) {
292       return std::current_exception();
293     }
294     return std::exception_ptr();
295   }
296
297 protected:
298   template <typename Ex>
299   struct optimize {
300     static const bool value =
301       std::is_base_of<std::exception, Ex>::value &&
302       std::is_copy_assignable<Ex>::value &&
303       !std::is_abstract<Ex>::value;
304   };
305
306   template <typename Ex>
307   void assign_eptr(std::exception_ptr eptr, Ex& e) {
308     this->eptr_ = eptr;
309     this->estr_ = exceptionStr(e).toStdString();
310     this->ename_ = demangle(typeid(e)).toStdString();
311   }
312
313   void assign_eptr(std::exception_ptr eptr) {
314     this->eptr_ = eptr;
315   }
316
317   // Optimized case: if we know what type the exception is, we can
318   // store a copy of the concrete type, and a helper function so we
319   // can rethrow it.
320   std::shared_ptr<std::exception> item_;
321   void (*throwfn_)(std::exception*){nullptr};
322   // Fallback case: store the library wrapper, which is less efficient
323   // but gets the job done.  Also store exceptionPtr() the name of the
324   // exception type, so we can at least get those back out without
325   // having to rethrow.
326   std::exception_ptr eptr_;
327   std::string estr_;
328   std::string ename_;
329
330   template <class T, class... Args>
331   friend exception_wrapper make_exception_wrapper(Args&&... args);
332
333 private:
334   template <typename F>
335   struct functor_traits {
336     template <typename T>
337     struct impl;
338     template <typename C, typename R, typename A>
339     struct impl<R(C::*)(A)> { using arg_type = A; };
340     template <typename C, typename R, typename A>
341     struct impl<R(C::*)(A) const> { using arg_type = A; };
342     using functor_decayed = typename std::decay<F>::type;
343     using functor_op = decltype(&functor_decayed::operator());
344     using arg_type = typename impl<functor_op>::arg_type;
345     using arg_type_decayed = typename std::decay<arg_type>::type;
346   };
347
348   // What makes this useful is that T can be exception_wrapper* or
349   // const exception_wrapper*, and the compiler will use the
350   // instantiation which works with F.
351   template <class Ex, class F, class T>
352   static bool with_exception1(F f, T* that) {
353     if (that->item_) {
354       if (auto ex = dynamic_cast<Ex*>(that->item_.get())) {
355         f(*ex);
356         return true;
357       }
358     } else if (that->eptr_) {
359       try {
360         std::rethrow_exception(that->eptr_);
361       } catch (Ex& e) {
362         f(e);
363         return true;
364       } catch (...) {
365         // fall through
366       }
367     }
368     return false;
369   }
370 };
371
372 template <class T, class... Args>
373 exception_wrapper make_exception_wrapper(Args&&... args) {
374   exception_wrapper ew;
375   ew.item_ = std::make_shared<T>(std::forward<Args>(args)...);
376   ew.throwfn_ = folly::detail::Thrower<T>::doThrow;
377   return ew;
378 }
379
380 // For consistency with exceptionStr() functions in String.h
381 inline fbstring exceptionStr(const exception_wrapper& ew) {
382   return ew.what();
383 }
384
385 /*
386  * try_and_catch is a simple replacement for try {} catch(){} that allows you to
387  * specify which derived exceptions you would like to catch and store in an
388  * exception_wrapper.
389  *
390  * Because we cannot build an equivalent of std::current_exception(), we need
391  * to catch every derived exception that we are interested in catching.
392  *
393  * Exceptions should be listed in the reverse order that you would write your
394  * catch statements (that is, std::exception& should be first).
395  *
396  * NOTE: Although implemented as a derived class (for syntactic delight), don't
397  * be confused - you should not pass around try_and_catch objects!
398  *
399  * Example Usage:
400  *
401  * // This catches my runtime_error and if I call throwException() on ew, it
402  * // will throw a runtime_error
403  * auto ew = folly::try_and_catch<std::exception, std::runtime_error>([=]() {
404  *   if (badThingHappens()) {
405  *     throw std::runtime_error("ZOMG!");
406  *   }
407  * });
408  *
409  * // This will catch the exception and if I call throwException() on ew, it
410  * // will throw a std::exception
411  * auto ew = folly::try_and_catch<std::exception, std::runtime_error>([=]() {
412  *   if (badThingHappens()) {
413  *     throw std::exception();
414  *   }
415  * });
416  *
417  * // This will not catch the exception and it will be thrown.
418  * auto ew = folly::try_and_catch<std::runtime_error>([=]() {
419  *   if (badThingHappens()) {
420  *     throw std::exception();
421  *   }
422  * });
423  */
424
425 template <typename... Exceptions>
426 class try_and_catch;
427
428 template <typename LastException, typename... Exceptions>
429 class try_and_catch<LastException, Exceptions...> :
430     public try_and_catch<Exceptions...> {
431  public:
432   template <typename F>
433   explicit try_and_catch(F&& fn) : Base() {
434     call_fn(fn);
435   }
436
437  protected:
438   typedef try_and_catch<Exceptions...> Base;
439
440   try_and_catch() : Base() {}
441
442   template <typename Ex>
443   typename std::enable_if<!exception_wrapper::optimize<Ex>::value>::type
444   assign_exception(Ex& e, std::exception_ptr eptr) {
445     exception_wrapper::assign_eptr(eptr, e);
446   }
447
448   template <typename Ex>
449   typename std::enable_if<exception_wrapper::optimize<Ex>::value>::type
450   assign_exception(Ex& e, std::exception_ptr /*eptr*/) {
451     this->item_ = std::make_shared<Ex>(e);
452     this->throwfn_ = folly::detail::Thrower<Ex>::doThrow;
453   }
454
455   template <typename F>
456   void call_fn(F&& fn) {
457     try {
458       Base::call_fn(std::move(fn));
459     } catch (LastException& e) {
460       if (typeid(e) == typeid(LastException&)) {
461         assign_exception(e, std::current_exception());
462       } else {
463         exception_wrapper::assign_eptr(std::current_exception(), e);
464       }
465     }
466   }
467 };
468
469 template<>
470 class try_and_catch<> : public exception_wrapper {
471  public:
472   try_and_catch() = default;
473
474  protected:
475   template <typename F>
476   void call_fn(F&& fn) {
477     fn();
478   }
479 };
480 }