1 /* 2 * Copyright (c) Facebook, Inc. and its affiliates. 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 * 19 * Author: Eric Niebler <eniebler@fb.com> 20 */ 21 22 #include <folly/Portability.h> 23 24 namespace folly { 25 26 template <class Fn> 27 struct exception_wrapper::arg_type_ 28 : public arg_type_<decltype(&Fn::operator())> {}; 29 template <class Ret, class Class, class Arg> 30 struct exception_wrapper::arg_type_<Ret (Class::*)(Arg)> { 31 using type = Arg; 32 }; 33 template <class Ret, class Class, class Arg> 34 struct exception_wrapper::arg_type_<Ret (Class::*)(Arg) const> { 35 using type = Arg; 36 }; 37 template <class Ret, class Arg> 38 struct exception_wrapper::arg_type_<Ret(Arg)> { 39 using type = Arg; 40 }; 41 template <class Ret, class Arg> 42 struct exception_wrapper::arg_type_<Ret (*)(Arg)> { 43 using type = Arg; 44 }; 45 template <class Ret, class Class> 46 struct exception_wrapper::arg_type_<Ret (Class::*)(...)> { 47 using type = AnyException; 48 }; 49 template <class Ret, class Class> 50 struct exception_wrapper::arg_type_<Ret (Class::*)(...) const> { 51 using type = AnyException; 52 }; 53 template <class Ret> 54 struct exception_wrapper::arg_type_<Ret(...)> { 55 using type = AnyException; 56 }; 57 template <class Ret> 58 struct exception_wrapper::arg_type_<Ret (*)(...)> { 59 using type = AnyException; 60 }; 61 62 #ifdef FOLLY_HAVE_NOEXCEPT_FUNCTION_TYPE 63 template <class Ret, class Class, class Arg> 64 struct exception_wrapper::arg_type_<Ret (Class::*)(Arg) noexcept> { 65 using type = Arg; 66 }; 67 template <class Ret, class Class, class Arg> 68 struct exception_wrapper::arg_type_<Ret (Class::*)(Arg) const noexcept> { 69 using type = Arg; 70 }; 71 template <class Ret, class Arg> 72 struct exception_wrapper::arg_type_<Ret(Arg) noexcept> { 73 using type = Arg; 74 }; 75 template <class Ret, class Arg> 76 struct exception_wrapper::arg_type_<Ret (*)(Arg) noexcept> { 77 using type = Arg; 78 }; 79 template <class Ret, class Class> 80 struct exception_wrapper::arg_type_<Ret (Class::*)(...) noexcept> { 81 using type = AnyException; 82 }; 83 template <class Ret, class Class> 84 struct exception_wrapper::arg_type_<Ret (Class::*)(...) const noexcept> { 85 using type = AnyException; 86 }; 87 template <class Ret> 88 struct exception_wrapper::arg_type_<Ret(...) noexcept> { 89 using type = AnyException; 90 }; 91 template <class Ret> 92 struct exception_wrapper::arg_type_<Ret (*)(...) noexcept> { 93 using type = AnyException; 94 }; 95 #endif 96 97 struct exception_wrapper::with_exception_from_fn_ { 98 template <typename, typename Fn> 99 using apply = arg_type<Fn>; 100 }; 101 102 struct exception_wrapper::with_exception_from_ex_ { 103 template <typename Ex, typename> 104 using apply = Ex; 105 }; 106 107 template <class Ret, class... Args> 108 inline Ret exception_wrapper::noop_(Args...) { 109 return Ret(); 110 } 111 112 inline std::type_info const* exception_wrapper::uninit_type_( 113 exception_wrapper const*) { 114 return &typeid(void); 115 } 116 117 template <class Ex, typename... As> 118 inline exception_wrapper::Buffer::Buffer(in_place_type_t<Ex>, As&&... as_) { 119 ::new (static_cast<void*>(&buff_)) Ex(std::forward<As>(as_)...); 120 } 121 122 template <class Ex> 123 inline Ex& exception_wrapper::Buffer::as() noexcept { 124 return *static_cast<Ex*>(static_cast<void*>(&buff_)); 125 } 126 template <class Ex> 127 inline Ex const& exception_wrapper::Buffer::as() const noexcept { 128 return *static_cast<Ex const*>(static_cast<void const*>(&buff_)); 129 } 130 131 inline std::exception const* exception_wrapper::as_exception_or_null_( 132 std::exception const& ex) { 133 return &ex; 134 } 135 inline std::exception const* exception_wrapper::as_exception_or_null_( 136 AnyException) { 137 return nullptr; 138 } 139 140 inline void exception_wrapper::ExceptionPtr::copy_( 141 exception_wrapper const* from, exception_wrapper* to) { 142 ::new (static_cast<void*>(&to->eptr_)) ExceptionPtr(from->eptr_); 143 } 144 inline void exception_wrapper::ExceptionPtr::move_( 145 exception_wrapper* from, exception_wrapper* to) { 146 ::new (static_cast<void*>(&to->eptr_)) ExceptionPtr(std::move(from->eptr_)); 147 delete_(from); 148 } 149 inline void exception_wrapper::ExceptionPtr::delete_(exception_wrapper* that) { 150 that->eptr_.~ExceptionPtr(); 151 that->vptr_ = &uninit_; 152 } 153 [[noreturn]] inline void exception_wrapper::ExceptionPtr::throw_( 154 exception_wrapper const* that) { 155 std::rethrow_exception(that->eptr_.ptr_); 156 } 157 inline std::type_info const* exception_wrapper::ExceptionPtr::type_( 158 exception_wrapper const* that) { 159 return exception_ptr_get_type(that->eptr_.ptr_); 160 } 161 inline std::exception const* exception_wrapper::ExceptionPtr::get_exception_( 162 exception_wrapper const* that) { 163 return exception_ptr_get_object<std::exception>(that->eptr_.ptr_); 164 } 165 inline exception_wrapper exception_wrapper::ExceptionPtr::get_exception_ptr_( 166 exception_wrapper const* that) { 167 return *that; 168 } 169 170 template <class Ex> 171 inline void exception_wrapper::InPlace<Ex>::copy_( 172 exception_wrapper const* from, exception_wrapper* to) { 173 ::new (static_cast<void*>(std::addressof(to->buff_.as<Ex>()))) 174 Ex(from->buff_.as<Ex>()); 175 } 176 template <class Ex> 177 inline void exception_wrapper::InPlace<Ex>::move_( 178 exception_wrapper* from, exception_wrapper* to) { 179 ::new (static_cast<void*>(std::addressof(to->buff_.as<Ex>()))) 180 Ex(std::move(from->buff_.as<Ex>())); 181 delete_(from); 182 } 183 template <class Ex> 184 inline void exception_wrapper::InPlace<Ex>::delete_(exception_wrapper* that) { 185 that->buff_.as<Ex>().~Ex(); 186 that->vptr_ = &uninit_; 187 } 188 template <class Ex> 189 [[noreturn]] inline void exception_wrapper::InPlace<Ex>::throw_( 190 exception_wrapper const* that) { 191 throw that->buff_.as<Ex>(); 192 } 193 template <class Ex> 194 inline std::type_info const* exception_wrapper::InPlace<Ex>::type_( 195 exception_wrapper const*) { 196 return &typeid(Ex); 197 } 198 template <class Ex> 199 inline std::exception const* exception_wrapper::InPlace<Ex>::get_exception_( 200 exception_wrapper const* that) { 201 return as_exception_or_null_(that->buff_.as<Ex>()); 202 } 203 template <class Ex> 204 inline exception_wrapper exception_wrapper::InPlace<Ex>::get_exception_ptr_( 205 exception_wrapper const* that) { 206 try { 207 throw_(that); 208 } catch (...) { 209 return exception_wrapper{std::current_exception()}; 210 } 211 } 212 213 template <class Ex> 214 [[noreturn]] inline void exception_wrapper::SharedPtr::Impl<Ex>::throw_() 215 const { 216 throw ex_; 217 } 218 template <class Ex> 219 inline std::exception const* 220 exception_wrapper::SharedPtr::Impl<Ex>::get_exception_() const noexcept { 221 return as_exception_or_null_(ex_); 222 } 223 template <class Ex> 224 inline exception_wrapper 225 exception_wrapper::SharedPtr::Impl<Ex>::get_exception_ptr_() const noexcept { 226 try { 227 throw_(); 228 } catch (...) { 229 return exception_wrapper{std::current_exception()}; 230 } 231 } 232 inline void exception_wrapper::SharedPtr::copy_( 233 exception_wrapper const* from, exception_wrapper* to) { 234 ::new (static_cast<void*>(std::addressof(to->sptr_))) SharedPtr(from->sptr_); 235 } 236 inline void exception_wrapper::SharedPtr::move_( 237 exception_wrapper* from, exception_wrapper* to) { 238 ::new (static_cast<void*>(std::addressof(to->sptr_))) 239 SharedPtr(std::move(from->sptr_)); 240 delete_(from); 241 } 242 inline void exception_wrapper::SharedPtr::delete_(exception_wrapper* that) { 243 that->sptr_.~SharedPtr(); 244 that->vptr_ = &uninit_; 245 } 246 [[noreturn]] inline void exception_wrapper::SharedPtr::throw_( 247 exception_wrapper const* that) { 248 that->sptr_.ptr_->throw_(); 249 folly::assume_unreachable(); 250 } 251 inline std::type_info const* exception_wrapper::SharedPtr::type_( 252 exception_wrapper const* that) { 253 return that->sptr_.ptr_->info_; 254 } 255 inline std::exception const* exception_wrapper::SharedPtr::get_exception_( 256 exception_wrapper const* that) { 257 return that->sptr_.ptr_->get_exception_(); 258 } 259 inline exception_wrapper exception_wrapper::SharedPtr::get_exception_ptr_( 260 exception_wrapper const* that) { 261 return that->sptr_.ptr_->get_exception_ptr_(); 262 } 263 264 template <class Ex, typename... As> 265 inline exception_wrapper::exception_wrapper( 266 ThrownTag, in_place_type_t<Ex>, As&&... as) 267 : eptr_{std::make_exception_ptr(Ex(std::forward<As>(as)...))}, 268 vptr_(&ExceptionPtr::ops_) {} 269 270 template <class Ex, typename... As> 271 inline exception_wrapper::exception_wrapper( 272 OnHeapTag, in_place_type_t<Ex>, As&&... as) 273 : sptr_{std::make_shared<SharedPtr::Impl<Ex>>(std::forward<As>(as)...)}, 274 vptr_(&SharedPtr::ops_) {} 275 276 template <class Ex, typename... As> 277 inline exception_wrapper::exception_wrapper( 278 InSituTag, in_place_type_t<Ex>, As&&... as) 279 : buff_{in_place_type<Ex>, std::forward<As>(as)...}, 280 vptr_(&InPlace<Ex>::ops_) {} 281 282 inline exception_wrapper::exception_wrapper(exception_wrapper&& that) noexcept 283 : exception_wrapper{} { 284 (vptr_ = that.vptr_)->move_(&that, this); // Move into *this, won't throw 285 } 286 287 inline exception_wrapper::exception_wrapper( 288 exception_wrapper const& that) noexcept 289 : exception_wrapper{} { 290 that.vptr_->copy_(&that, this); // Copy into *this, won't throw 291 vptr_ = that.vptr_; 292 } 293 294 // If `this == &that`, this move assignment operator leaves the object in a 295 // valid but unspecified state. 296 inline exception_wrapper& exception_wrapper::operator=( 297 exception_wrapper&& that) noexcept { 298 vptr_->delete_(this); // Free the current exception 299 (vptr_ = that.vptr_)->move_(&that, this); // Move into *this, won't throw 300 return *this; 301 } 302 303 inline exception_wrapper& exception_wrapper::operator=( 304 exception_wrapper const& that) noexcept { 305 exception_wrapper(that).swap(*this); 306 return *this; 307 } 308 309 inline exception_wrapper::~exception_wrapper() { 310 reset(); 311 } 312 313 template <class Ex> 314 inline exception_wrapper::exception_wrapper( 315 std::exception_ptr const& ptr, Ex& ex) noexcept 316 : exception_wrapper{folly::copy(ptr), ex} {} 317 318 template <class Ex> 319 inline exception_wrapper::exception_wrapper( 320 std::exception_ptr&& ptr, Ex& ex) noexcept 321 : eptr_{std::move(ptr)}, vptr_(&ExceptionPtr::ops_) { 322 assert(eptr_.ptr_); 323 (void)ex; 324 assert(exception_ptr_get_object<Ex>(eptr_.ptr_)); 325 assert(exception_ptr_get_object<Ex>(eptr_.ptr_) == &ex || kIsWindows); 326 } 327 328 namespace exception_wrapper_detail { 329 template <class Ex> 330 Ex&& dont_slice(Ex&& ex) { 331 assert(typeid(ex) == typeid(std::decay_t<Ex>) || 332 !"Dynamic and static exception types don't match. Exception would " 333 "be sliced when storing in exception_wrapper."); 334 return std::forward<Ex>(ex); 335 } 336 } // namespace exception_wrapper_detail 337 338 template < 339 class Ex, 340 class Ex_, 341 FOLLY_REQUIRES_DEF(Conjunction< 342 exception_wrapper::IsStdException<Ex_>, 343 exception_wrapper::IsRegularExceptionType<Ex_>>::value)> 344 inline exception_wrapper::exception_wrapper(Ex&& ex) 345 : exception_wrapper{ 346 PlacementOf<Ex_>{}, 347 in_place_type<Ex_>, 348 exception_wrapper_detail::dont_slice(std::forward<Ex>(ex))} {} 349 350 template < 351 class Ex, 352 class Ex_, 353 FOLLY_REQUIRES_DEF(exception_wrapper::IsRegularExceptionType<Ex_>::value)> 354 inline exception_wrapper::exception_wrapper(in_place_t, Ex&& ex) 355 : exception_wrapper{ 356 PlacementOf<Ex_>{}, 357 in_place_type<Ex_>, 358 exception_wrapper_detail::dont_slice(std::forward<Ex>(ex))} {} 359 360 template < 361 class Ex, 362 typename... As, 363 FOLLY_REQUIRES_DEF(exception_wrapper::IsRegularExceptionType<Ex>::value)> 364 inline exception_wrapper::exception_wrapper(in_place_type_t<Ex>, As&&... as) 365 : exception_wrapper{ 366 PlacementOf<Ex>{}, in_place_type<Ex>, std::forward<As>(as)...} {} 367 368 inline void exception_wrapper::swap(exception_wrapper& that) noexcept { 369 exception_wrapper tmp(std::move(that)); 370 that = std::move(*this); 371 *this = std::move(tmp); 372 } 373 374 inline exception_wrapper::operator bool() const noexcept { 375 return vptr_ != &uninit_; 376 } 377 378 inline bool exception_wrapper::operator!() const noexcept { 379 return !static_cast<bool>(*this); 380 } 381 382 inline void exception_wrapper::reset() { 383 vptr_->delete_(this); 384 } 385 386 inline bool exception_wrapper::has_exception_ptr() const noexcept { 387 return vptr_ == &ExceptionPtr::ops_; 388 } 389 390 inline std::exception* exception_wrapper::get_exception() noexcept { 391 return const_cast<std::exception*>(vptr_->get_exception_(this)); 392 } 393 inline std::exception const* exception_wrapper::get_exception() const noexcept { 394 return vptr_->get_exception_(this); 395 } 396 397 template <typename Ex> 398 inline Ex* exception_wrapper::get_exception() noexcept { 399 constexpr auto stdexcept = std::is_base_of<std::exception, Ex>::value; 400 if (vptr_ == &ExceptionPtr::ops_) { 401 return exception_ptr_get_object<Ex>(eptr_.ptr_); 402 } else if (!stdexcept || vptr_ == &uninit_) { 403 return nullptr; 404 } else { 405 using Target = conditional_t<stdexcept, Ex, std::exception>; 406 auto const ptr = dynamic_cast<Target*>(get_exception()); 407 return reinterpret_cast<Ex*>(ptr); 408 } 409 } 410 411 template <typename Ex> 412 inline Ex const* exception_wrapper::get_exception() const noexcept { 413 constexpr auto stdexcept = std::is_base_of<std::exception, Ex>::value; 414 if (vptr_ == &ExceptionPtr::ops_) { 415 return exception_ptr_get_object<Ex>(eptr_.ptr_); 416 } else if (!stdexcept || vptr_ == &uninit_) { 417 return nullptr; 418 } else { 419 using Target = conditional_t<stdexcept, Ex, std::exception>; 420 auto const ptr = dynamic_cast<Target const*>(get_exception()); 421 return reinterpret_cast<Ex const*>(ptr); 422 } 423 } 424 425 inline std::exception_ptr exception_wrapper::to_exception_ptr() noexcept { 426 if (*this) { 427 // Computing an exception_ptr is expensive so cache the result. 428 return (*this = vptr_->get_exception_ptr_(this)).eptr_.ptr_; 429 } 430 return {}; 431 } 432 inline std::exception_ptr exception_wrapper::to_exception_ptr() const noexcept { 433 return vptr_->get_exception_ptr_(this).eptr_.ptr_; 434 } 435 436 inline std::type_info const& exception_wrapper::none() noexcept { 437 return typeid(void); 438 } 439 440 inline std::type_info const& exception_wrapper::type() const noexcept { 441 return *vptr_->type_(this); 442 } 443 444 inline folly::fbstring exception_wrapper::what() const { 445 if (auto e = get_exception()) { 446 return class_name() + ": " + e->what(); 447 } 448 return class_name(); 449 } 450 451 inline folly::fbstring exception_wrapper::class_name() const { 452 auto& ti = type(); 453 return ti == none() ? "" : folly::demangle(ti); 454 } 455 456 template <class Ex> 457 inline bool exception_wrapper::is_compatible_with() const noexcept { 458 return get_exception<Ex>(); 459 } 460 461 [[noreturn]] inline void exception_wrapper::throw_exception() const { 462 vptr_->throw_(this); 463 onNoExceptionError(__func__); 464 } 465 466 template <class Ex> 467 [[noreturn]] inline void exception_wrapper::throw_with_nested(Ex&& ex) const { 468 try { 469 throw_exception(); 470 } catch (...) { 471 std::throw_with_nested(std::forward<Ex>(ex)); 472 } 473 } 474 475 template <class This, class Fn> 476 inline bool exception_wrapper::with_exception_( 477 This&, Fn fn_, tag_t<AnyException>) { 478 return void(fn_()), true; 479 } 480 481 template <class This, class Fn, typename Ex> 482 inline bool exception_wrapper::with_exception_(This& this_, Fn fn_, tag_t<Ex>) { 483 auto ptr = this_.template get_exception<remove_cvref_t<Ex>>(); 484 return ptr && (void(fn_(static_cast<Ex&>(*ptr))), true); 485 } 486 487 template <class Ex, class This, class Fn> 488 inline bool exception_wrapper::with_exception_(This& this_, Fn fn_) { 489 using from_fn = with_exception_from_fn_; 490 using from_ex = with_exception_from_ex_; 491 using from = conditional_t<std::is_void<Ex>::value, from_fn, from_ex>; 492 using type = typename from::template apply<Ex, Fn>; 493 return with_exception_(this_, fn_, tag<type>); 494 } 495 496 template <class This, class... CatchFns> 497 inline void exception_wrapper::handle_( 498 This& this_, char const* name, CatchFns&... fns) { 499 using _ = bool[]; 500 if (!this_) { 501 onNoExceptionError(name); 502 } 503 bool handled = false; 504 void(_{false, (handled = handled || with_exception_<void>(this_, fns))...}); 505 if (!handled) { 506 this_.throw_exception(); 507 } 508 } 509 510 template <class Ex, class Fn> 511 inline bool exception_wrapper::with_exception(Fn fn) { 512 return with_exception_<Ex>(*this, std::move(fn)); 513 } 514 template <class Ex, class Fn> 515 inline bool exception_wrapper::with_exception(Fn fn) const { 516 return with_exception_<Ex const>(*this, std::move(fn)); 517 } 518 519 template <class... CatchFns> 520 inline void exception_wrapper::handle(CatchFns... fns) { 521 handle_(*this, __func__, fns...); 522 } 523 template <class... CatchFns> 524 inline void exception_wrapper::handle(CatchFns... fns) const { 525 handle_(*this, __func__, fns...); 526 } 527 528 } // namespace folly 529