Add support for getting the current thread's name
[folly.git] / folly / ExceptionWrapper-inl.h
1 /*
2  * Copyright 2017-present 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  *
18  * Author: Eric Niebler <eniebler@fb.com>
19  */
20
21 namespace folly {
22
23 template <class Fn>
24 struct exception_wrapper::arg_type_
25     : public arg_type_<decltype(&Fn::operator())> {
26 };
27 template <class Ret, class Class, class Arg>
28 struct exception_wrapper::arg_type_<Ret (Class::*)(Arg)> {
29   using type = Arg;
30 };
31 template <class Ret, class Class, class Arg>
32 struct exception_wrapper::arg_type_<Ret (Class::*)(Arg) const> {
33   using type = Arg;
34 };
35 template <class Ret, class Arg>
36 struct exception_wrapper::arg_type_<Ret (Arg)> {
37   using type = Arg;
38 };
39 template <class Ret, class Arg>
40 struct exception_wrapper::arg_type_<Ret (*)(Arg)> {
41   using type = Arg;
42 };
43 template <class Ret, class Class>
44 struct exception_wrapper::arg_type_<Ret (Class::*)(...)> {
45   using type = AnyException;
46 };
47 template <class Ret, class Class>
48 struct exception_wrapper::arg_type_<Ret (Class::*)(...) const> {
49   using type = AnyException;
50 };
51 template <class Ret>
52 struct exception_wrapper::arg_type_<Ret (...)> {
53   using type = AnyException;
54 };
55 template <class Ret>
56 struct exception_wrapper::arg_type_<Ret (*)(...)> {
57   using type = AnyException;
58 };
59
60 template <class Ret, class... Args>
61 inline Ret exception_wrapper::noop_(Args...) {
62   return Ret();
63 }
64
65 inline std::type_info const* exception_wrapper::uninit_type_(
66     exception_wrapper const*) {
67   return &typeid(void);
68 }
69
70 template <class Ex, class DEx>
71 inline exception_wrapper::Buffer::Buffer(in_place_t, Ex&& ex) {
72   ::new (static_cast<void*>(&buff_)) DEx(std::forward<Ex>(ex));
73 }
74
75 template <class Ex>
76 inline Ex& exception_wrapper::Buffer::as() noexcept {
77   return *static_cast<Ex*>(static_cast<void*>(&buff_));
78 }
79 template <class Ex>
80 inline Ex const& exception_wrapper::Buffer::as() const noexcept {
81   return *static_cast<Ex const*>(static_cast<void const*>(&buff_));
82 }
83
84 inline std::exception const* exception_wrapper::as_exception_or_null_(
85     std::exception const& ex) {
86   return &ex;
87 }
88 inline std::exception const* exception_wrapper::as_exception_or_null_(
89     AnyException) {
90   return nullptr;
91 }
92
93 inline std::uintptr_t exception_wrapper::ExceptionPtr::as_int_(
94     std::exception const& e) {
95   return reinterpret_cast<std::uintptr_t>(&e);
96 }
97 inline std::uintptr_t exception_wrapper::ExceptionPtr::as_int_(AnyException e) {
98   return reinterpret_cast<std::uintptr_t>(e.typeinfo_) + 1;
99 }
100 inline bool exception_wrapper::ExceptionPtr::has_exception_() const {
101   return 0 == exception_or_type_ % 2;
102 }
103 inline std::exception const* exception_wrapper::ExceptionPtr::as_exception_()
104     const {
105   return reinterpret_cast<std::exception const*>(exception_or_type_);
106 }
107 inline std::type_info const* exception_wrapper::ExceptionPtr::as_type_() const {
108   return reinterpret_cast<std::type_info const*>(exception_or_type_ - 1);
109 }
110
111 inline void exception_wrapper::ExceptionPtr::copy_(
112     exception_wrapper const* from, exception_wrapper* to) {
113   ::new (static_cast<void*>(&to->eptr_)) ExceptionPtr(from->eptr_);
114 }
115 inline void exception_wrapper::ExceptionPtr::move_(
116     exception_wrapper* from, exception_wrapper* to) {
117   ::new (static_cast<void*>(&to->eptr_))
118       ExceptionPtr(std::move(from->eptr_));
119   delete_(from);
120 }
121 inline void exception_wrapper::ExceptionPtr::delete_(
122     exception_wrapper* that) {
123   that->eptr_.~ExceptionPtr();
124   that->vptr_ = &uninit_;
125 }
126 [[noreturn]] inline void exception_wrapper::ExceptionPtr::throw_(
127     exception_wrapper const* that) {
128   std::rethrow_exception(that->eptr_.ptr_);
129 }
130 inline std::type_info const* exception_wrapper::ExceptionPtr::type_(
131     exception_wrapper const* that) {
132   if (auto e = get_exception_(that)) {
133     return &typeid(*e);
134   }
135   return that->eptr_.as_type_();
136 }
137 inline std::exception const* exception_wrapper::ExceptionPtr::get_exception_(
138     exception_wrapper const* that) {
139   return that->eptr_.has_exception_() ? that->eptr_.as_exception_()
140                                       : nullptr;
141 }
142 inline exception_wrapper exception_wrapper::ExceptionPtr::get_exception_ptr_(
143     exception_wrapper const* that) {
144   return *that;
145 }
146
147 template <class Ex>
148 inline void exception_wrapper::InPlace<Ex>::copy_(
149     exception_wrapper const* from, exception_wrapper* to) {
150   ::new (static_cast<void*>(std::addressof(to->buff_.as<Ex>())))
151       Ex(from->buff_.as<Ex>());
152 }
153 template <class Ex>
154 inline void exception_wrapper::InPlace<Ex>::move_(
155     exception_wrapper* from, exception_wrapper* to) {
156   ::new (static_cast<void*>(std::addressof(to->buff_.as<Ex>())))
157       Ex(std::move(from->buff_.as<Ex>()));
158   delete_(from);
159 }
160 template <class Ex>
161 inline void exception_wrapper::InPlace<Ex>::delete_(
162     exception_wrapper* that) {
163   that->buff_.as<Ex>().~Ex();
164   that->vptr_ = &uninit_;
165 }
166 template <class Ex>
167 [[noreturn]] inline void exception_wrapper::InPlace<Ex>::throw_(
168     exception_wrapper const* that) {
169   throw that->buff_.as<Ex>(); // @nolint
170 }
171 template <class Ex>
172 inline std::type_info const* exception_wrapper::InPlace<Ex>::type_(
173     exception_wrapper const*) {
174   return &typeid(Ex);
175 }
176 template <class Ex>
177 inline std::exception const* exception_wrapper::InPlace<Ex>::get_exception_(
178     exception_wrapper const* that) {
179   return as_exception_or_null_(that->buff_.as<Ex>());
180 }
181 template <class Ex>
182 inline exception_wrapper exception_wrapper::InPlace<Ex>::get_exception_ptr_(
183     exception_wrapper const* that) {
184   try {
185     throw_(that);
186   } catch (Ex const& ex) {
187     return exception_wrapper{std::current_exception(), ex};
188   }
189 }
190
191 template <class Ex>
192 [[noreturn]] inline void
193 exception_wrapper::SharedPtr::Impl<Ex>::throw_() const {
194   throw ex_; // @nolint
195 }
196 template <class Ex>
197 inline std::exception const*
198 exception_wrapper::SharedPtr::Impl<Ex>::get_exception_() const noexcept {
199   return as_exception_or_null_(ex_);
200 }
201 template <class Ex>
202 inline exception_wrapper
203 exception_wrapper::SharedPtr::Impl<Ex>::get_exception_ptr_() const noexcept {
204   try {
205     throw_();
206   } catch (Ex& ex) {
207     return exception_wrapper{std::current_exception(), ex};
208   }
209 }
210 inline void exception_wrapper::SharedPtr::copy_(
211     exception_wrapper const* from, exception_wrapper* to) {
212   ::new (static_cast<void*>(std::addressof(to->sptr_)))
213       SharedPtr(from->sptr_);
214 }
215 inline void exception_wrapper::SharedPtr::move_(
216     exception_wrapper* from, exception_wrapper* to) {
217   ::new (static_cast<void*>(std::addressof(to->sptr_)))
218       SharedPtr(std::move(from->sptr_));
219   delete_(from);
220 }
221 inline void exception_wrapper::SharedPtr::delete_(
222     exception_wrapper* that) {
223   that->sptr_.~SharedPtr();
224   that->vptr_ = &uninit_;
225 }
226 [[noreturn]] inline void exception_wrapper::SharedPtr::throw_(
227     exception_wrapper const* that) {
228   that->sptr_.ptr_->throw_();
229   folly::assume_unreachable();
230 }
231 inline std::type_info const* exception_wrapper::SharedPtr::type_(
232     exception_wrapper const* that) {
233   return that->sptr_.ptr_->info_;
234 }
235 inline std::exception const* exception_wrapper::SharedPtr::get_exception_(
236     exception_wrapper const* that) {
237   return that->sptr_.ptr_->get_exception_();
238 }
239 inline exception_wrapper exception_wrapper::SharedPtr::get_exception_ptr_(
240     exception_wrapper const* that) {
241   return that->sptr_.ptr_->get_exception_ptr_();
242 }
243
244 template <class Ex, class DEx>
245 inline exception_wrapper::exception_wrapper(Ex&& ex, OnHeapTag)
246     : sptr_{std::make_shared<SharedPtr::Impl<DEx>>(std::forward<Ex>(ex))},
247       vptr_(&SharedPtr::ops_) {}
248
249 template <class Ex, class DEx>
250 inline exception_wrapper::exception_wrapper(Ex&& ex, InSituTag)
251     : buff_{in_place, std::forward<Ex>(ex)}, vptr_(&InPlace<DEx>::ops_) {}
252
253 inline exception_wrapper::exception_wrapper(exception_wrapper&& that) noexcept
254     : exception_wrapper{} {
255   (vptr_ = that.vptr_)->move_(&that, this); // Move into *this, won't throw
256 }
257
258 inline exception_wrapper::exception_wrapper(
259     exception_wrapper const& that) : exception_wrapper{} {
260   that.vptr_->copy_(&that, this); // could throw
261   vptr_ = that.vptr_;
262 }
263
264 // If `this == &that`, this move assignment operator leaves the object in a
265 // valid but unspecified state.
266 inline exception_wrapper& exception_wrapper::operator=(
267     exception_wrapper&& that) noexcept {
268   vptr_->delete_(this); // Free the current exception
269   (vptr_ = that.vptr_)->move_(&that, this); // Move into *this, won't throw
270   return *this;
271 }
272
273 inline exception_wrapper& exception_wrapper::operator=(
274     exception_wrapper const& that) {
275   exception_wrapper(that).swap(*this);
276   return *this;
277 }
278
279 inline exception_wrapper::~exception_wrapper() {
280   reset();
281 }
282
283 template <class Ex>
284 inline exception_wrapper::exception_wrapper(std::exception_ptr ptr, Ex& ex)
285     : eptr_{std::move(ptr), ExceptionPtr::as_int_(ex)},
286       vptr_(&ExceptionPtr::ops_) {
287   assert(eptr_.ptr_);
288 }
289
290 namespace exception_wrapper_detail {
291 template <class Ex>
292 Ex&& dont_slice(Ex&& ex) {
293   assert(typeid(ex) == typeid(_t<std::decay<Ex>>) ||
294        !"Dynamic and static exception types don't match. Exception would "
295         "be sliced when storing in exception_wrapper.");
296   return std::forward<Ex>(ex);
297 }
298 }
299
300 template <
301     class Ex,
302     class Ex_,
303     FOLLY_REQUIRES_DEF(
304         Conjunction<
305             exception_wrapper::IsStdException<Ex_>,
306             exception_wrapper::IsRegularExceptionType<Ex_>>::value)>
307 inline exception_wrapper::exception_wrapper(Ex&& ex)
308     : exception_wrapper{
309         exception_wrapper_detail::dont_slice(std::forward<Ex>(ex)),
310         PlacementOf<Ex_>{}} {
311 }
312
313 template <
314     class Ex,
315     class Ex_,
316     FOLLY_REQUIRES_DEF(
317         exception_wrapper::IsRegularExceptionType<Ex_>::value)>
318 inline exception_wrapper::exception_wrapper(in_place_t, Ex&& ex)
319     : exception_wrapper{
320         exception_wrapper_detail::dont_slice(std::forward<Ex>(ex)),
321         PlacementOf<Ex_>{}} {
322 }
323
324 inline void exception_wrapper::swap(exception_wrapper& that) noexcept {
325   exception_wrapper tmp(std::move(that));
326   that = std::move(*this);
327   *this = std::move(tmp);
328 }
329
330 inline exception_wrapper::operator bool() const noexcept {
331   return vptr_ != &uninit_;
332 }
333
334 inline bool exception_wrapper::operator!() const noexcept {
335   return !static_cast<bool>(*this);
336 }
337
338 inline void exception_wrapper::reset() {
339   vptr_->delete_(this);
340 }
341
342 inline bool exception_wrapper::has_exception_ptr() const noexcept {
343   return vptr_ == &ExceptionPtr::ops_;
344 }
345
346 inline std::exception* exception_wrapper::get_exception() noexcept {
347   return const_cast<std::exception*>(vptr_->get_exception_(this));
348 }
349 inline std::exception const* exception_wrapper::get_exception() const noexcept {
350   return vptr_->get_exception_(this);
351 }
352
353 inline std::exception_ptr const& exception_wrapper::to_exception_ptr()
354     noexcept {
355   // Computing an exception_ptr is expensive so cache the result.
356   return (*this = vptr_->get_exception_ptr_(this)).eptr_.ptr_;
357 }
358 inline std::exception_ptr exception_wrapper::to_exception_ptr() const noexcept {
359   return vptr_->get_exception_ptr_(this).eptr_.ptr_;
360 }
361
362 inline std::type_info const& exception_wrapper::none() noexcept {
363   return typeid(void);
364 }
365 inline std::type_info const& exception_wrapper::unknown() noexcept {
366   return typeid(Unknown);
367 }
368
369 inline std::type_info const& exception_wrapper::type() const noexcept {
370   return *vptr_->type_(this);
371 }
372
373 inline folly::fbstring exception_wrapper::what() const {
374   if (auto e = get_exception()) {
375     return class_name() + ": " + e->what();
376   }
377   return class_name();
378 }
379
380 inline folly::fbstring exception_wrapper::class_name() const {
381   auto& ti = type();
382   return ti == none()
383       ? ""
384       : ti == unknown() ? "<unknown exception>" : folly::demangle(ti);
385 }
386
387 template <class Ex>
388 inline bool exception_wrapper::is_compatible_with() const noexcept {
389   return with_exception([](Ex const&) {});
390 }
391
392 [[noreturn]] inline void exception_wrapper::throw_exception() const {
393   vptr_->throw_(this);
394   onNoExceptionError();
395 }
396
397 template <class CatchFn, bool IsConst>
398 struct exception_wrapper::ExceptionTypeOf {
399   using type = arg_type<_t<std::decay<CatchFn>>>;
400   static_assert(
401       std::is_reference<type>::value,
402       "Always catch exceptions by reference.");
403   static_assert(
404       !IsConst || std::is_const<_t<std::remove_reference<type>>>::value,
405       "handle() or with_exception() called on a const exception_wrapper "
406       "and asked to catch a non-const exception. Handler will never fire. "
407       "Catch exception by const reference to fix this.");
408 };
409
410 // Nests a throw in the proper try/catch blocks
411 template <bool IsConst>
412 struct exception_wrapper::HandleReduce {
413   bool* handled_;
414
415   template <
416       class ThrowFn,
417       class CatchFn,
418       FOLLY_REQUIRES(!IsCatchAll<CatchFn>::value)>
419   auto operator()(ThrowFn&& th, CatchFn& ca) const {
420     using Ex = _t<ExceptionTypeOf<CatchFn, IsConst>>;
421     return [ th = std::forward<ThrowFn>(th), &ca, handled_ = handled_ ] {
422       try {
423         th();
424       } catch (Ex& e) {
425         // If we got here because a catch function threw, rethrow.
426         if (*handled_) {
427           throw;
428         }
429         *handled_ = true;
430         ca(e);
431       }
432     };
433   }
434
435   template <
436       class ThrowFn,
437       class CatchFn,
438       FOLLY_REQUIRES(IsCatchAll<CatchFn>::value)>
439   auto operator()(ThrowFn&& th, CatchFn& ca) const {
440     return [ th = std::forward<ThrowFn>(th), &ca, handled_ = handled_ ] {
441       try {
442         th();
443       } catch (...) {
444         // If we got here because a catch function threw, rethrow.
445         if (*handled_) {
446           throw;
447         }
448         *handled_ = true;
449         ca();
450       }
451     };
452   }
453 };
454
455 // When all the handlers expect types derived from std::exception, we can
456 // sometimes invoke the handlers without throwing any exceptions.
457 template <bool IsConst>
458 struct exception_wrapper::HandleStdExceptReduce {
459   using StdEx = AddConstIf<IsConst, std::exception>;
460
461   template <
462       class ThrowFn,
463       class CatchFn,
464       FOLLY_REQUIRES(!IsCatchAll<CatchFn>::value)>
465   auto operator()(ThrowFn&& th, CatchFn& ca) const {
466     using Ex = _t<ExceptionTypeOf<CatchFn, IsConst>>;
467     return [ th = std::forward<ThrowFn>(th), &ca ](auto&& continuation)
468         -> StdEx* {
469       if (auto e = const_cast<StdEx*>(th(continuation))) {
470         if (auto e2 = dynamic_cast<_t<std::add_pointer<Ex>>>(e)) {
471           ca(*e2);
472         } else {
473           return e;
474         }
475       }
476       return nullptr;
477     };
478   }
479
480   template <
481       class ThrowFn,
482       class CatchFn,
483       FOLLY_REQUIRES(IsCatchAll<CatchFn>::value)>
484   auto operator()(ThrowFn&& th, CatchFn& ca) const {
485     return [ th = std::forward<ThrowFn>(th), &ca ](auto&&) -> StdEx* {
486       // The following continuation causes ca() to execute if *this contains
487       // an exception /not/ derived from std::exception.
488       auto continuation = [&ca](StdEx* e) {
489         return e != nullptr ? e : ((void)ca(), nullptr);
490       };
491       if (th(continuation) != nullptr) {
492         ca();
493       }
494       return nullptr;
495     };
496   }
497 };
498
499 // Called when some types in the catch clauses are not derived from
500 // std::exception.
501 template <class This, class... CatchFns>
502 inline void exception_wrapper::handle_(
503     std::false_type, This& this_, CatchFns&... fns) {
504   bool handled = false;
505   auto impl = exception_wrapper_detail::fold(
506       HandleReduce<std::is_const<This>::value>{&handled},
507       [&] { this_.throw_exception(); },
508       fns...);
509   impl();
510 }
511
512 // Called when all types in the catch clauses are either derived from
513 // std::exception or a catch-all clause.
514 template <class This, class... CatchFns>
515 inline void exception_wrapper::handle_(
516     std::true_type, This& this_, CatchFns&... fns) {
517   using StdEx = exception_wrapper_detail::
518       AddConstIf<std::is_const<This>::value, std::exception>;
519   auto impl = exception_wrapper_detail::fold(
520       HandleStdExceptReduce<std::is_const<This>::value>{},
521       [&](auto&& continuation) {
522         return continuation(
523             const_cast<StdEx*>(this_.vptr_->get_exception_(&this_)));
524       },
525       fns...);
526   // This continuation gets evaluated if CatchFns... does not include a
527   // catch-all handler. It is a no-op.
528   auto continuation = [](StdEx* ex) { return ex; };
529   if (StdEx* e = impl(continuation)) {
530     throw *e; // Not handled. Throw.
531   }
532 }
533
534 namespace exception_wrapper_detail {
535 template <class Ex, class Fn>
536 struct catch_fn {
537   Fn fn_;
538   auto operator()(Ex& ex) {
539     return fn_(ex);
540   }
541 };
542
543 template <class Ex, class Fn>
544 inline catch_fn<Ex, Fn> catch_(Ex*, Fn fn) {
545   return {std::move(fn)};
546 }
547 template <class Fn>
548 inline Fn catch_(void const*, Fn fn) {
549   return fn;
550 }
551 } // namespace exception_wrapper_detail
552
553 template <class Ex, class This, class Fn>
554 inline bool exception_wrapper::with_exception_(This& this_, Fn fn_) {
555   if (!this_) {
556     return false;
557   }
558   bool handled = true;
559   auto fn = exception_wrapper_detail::catch_(
560       static_cast<Ex*>(nullptr), std::move(fn_));
561   auto&& all = [&](...) { handled = false; };
562   handle_(IsStdException<arg_type<decltype(fn)>>{}, this_, fn, all);
563   return handled;
564 }
565
566 template <class Ex, class Fn>
567 inline bool exception_wrapper::with_exception(Fn fn) {
568   return with_exception_<Ex>(*this, std::move(fn));
569 }
570 template <class Ex, class Fn>
571 inline bool exception_wrapper::with_exception(Fn fn) const {
572   return with_exception_<Ex const>(*this, std::move(fn));
573 }
574
575 template <class... CatchFns>
576 inline void exception_wrapper::handle(CatchFns... fns) {
577   using AllStdEx =
578       exception_wrapper_detail::AllOf<IsStdException, arg_type<CatchFns>...>;
579   if (!*this) {
580     onNoExceptionError();
581   }
582   this->handle_(AllStdEx{}, *this, fns...);
583 }
584 template <class... CatchFns>
585 inline void exception_wrapper::handle(CatchFns... fns) const {
586   using AllStdEx =
587       exception_wrapper_detail::AllOf<IsStdException, arg_type<CatchFns>...>;
588   if (!*this) {
589     onNoExceptionError();
590   }
591   this->handle_(AllStdEx{}, *this, fns...);
592 }
593
594 } // namespace folly