/* * Copyright (c) Meta Platforms, Inc. and affiliates. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include namespace folly { struct exception_wrapper::with_exception_from_fn_ { struct impl_var_ { template using apply = void; }; struct impl_arg_ { template using apply = typename function_traits::template argument<0>; }; struct impl_bye_; template < typename Sig, typename Traits = function_traits, std::size_t NArgs = Traits::template arguments::value> using impl_ = conditional_t< Traits::is_variadic, impl_var_, conditional_t>; template using member_ = typename member_pointer_traits::member_type; template struct arg_type_; template struct arg_type_::value>, Sig> { using type = typename impl_::template apply; }; template struct arg_type_::value>, Ptr> : arg_type_> {}; template struct arg_type_, Obj> : arg_type_> {}; // void if Fn is a variadic callable; otherwise the first arg type template using apply = _t>; }; struct exception_wrapper::with_exception_from_ex_ { template using apply = Ex; }; // The libc++ and cpplib implementations do not have a move constructor or a // move-assignment operator. To avoid refcount operations, we must improvise. // The libstdc++ implementation has a move constructor and a move-assignment // operator but having this does no harm. inline std::exception_ptr exception_wrapper::extract_( std::exception_ptr&& ptr) noexcept { constexpr auto sz = sizeof(std::exception_ptr); // assume relocatability on all platforms // assume nrvo for performance std::exception_ptr ret; std::memcpy(static_cast(&ret), &ptr, sz); std::memset(static_cast(&ptr), 0, sz); return ret; } inline exception_wrapper::exception_wrapper(exception_wrapper&& that) noexcept : ptr_{extract_(std::move(that.ptr_))} {} inline exception_wrapper::exception_wrapper( std::exception_ptr const& ptr) noexcept : ptr_{ptr} {} inline exception_wrapper::exception_wrapper(std::exception_ptr&& ptr) noexcept : ptr_{extract_(std::move(ptr))} {} template < class Ex, class Ex_, FOLLY_REQUIRES_DEF(Conjunction< exception_wrapper::IsStdException, exception_wrapper::IsRegularExceptionType>::value)> inline exception_wrapper::exception_wrapper(Ex&& ex) : ptr_{make_exception_ptr_with(std::in_place, std::forward(ex))} {} template < class Ex, class Ex_, FOLLY_REQUIRES_DEF(exception_wrapper::IsRegularExceptionType::value)> inline exception_wrapper::exception_wrapper(std::in_place_t, Ex&& ex) : ptr_{make_exception_ptr_with(std::in_place, std::forward(ex))} {} template < class Ex, typename... As, FOLLY_REQUIRES_DEF(exception_wrapper::IsRegularExceptionType::value)> inline exception_wrapper::exception_wrapper( std::in_place_type_t, As&&... as) : ptr_{make_exception_ptr_with( std::in_place_type, std::forward(as)...)} {} inline exception_wrapper& exception_wrapper::operator=( exception_wrapper&& that) noexcept { // assume relocatability on all platforms constexpr auto sz = sizeof(std::exception_ptr); std::exception_ptr tmp; std::memcpy(static_cast(&tmp), &ptr_, sz); std::memcpy(static_cast(&ptr_), &that.ptr_, sz); std::memset(static_cast(&that.ptr_), 0, sz); return *this; } inline void exception_wrapper::swap(exception_wrapper& that) noexcept { // assume relocatability on all platforms constexpr auto sz = sizeof(std::exception_ptr); aligned_storage_for_t storage; std::memcpy(&storage, &ptr_, sz); std::memcpy(static_cast(&ptr_), &that.ptr_, sz); std::memcpy(static_cast(&that.ptr_), &storage, sz); } inline exception_wrapper::operator bool() const noexcept { return !!ptr_; } inline bool exception_wrapper::operator!() const noexcept { return !ptr_; } inline void exception_wrapper::reset() { ptr_ = {}; } inline bool exception_wrapper::has_exception_ptr() const noexcept { return !!ptr_; } inline std::exception* exception_wrapper::get_exception() noexcept { return exception_ptr_get_object(ptr_); } inline std::exception const* exception_wrapper::get_exception() const noexcept { return exception_ptr_get_object(ptr_); } template inline Ex* exception_wrapper::get_exception() noexcept { return exception_ptr_get_object_hint(ptr_, tag); } template inline Ex const* exception_wrapper::get_exception() const noexcept { return exception_ptr_get_object_hint(ptr_, tag); } inline std::exception_ptr exception_wrapper::to_exception_ptr() const noexcept { return ptr_; } inline std::exception_ptr const& exception_wrapper::exception_ptr_ref() const noexcept { return ptr_; } inline std::type_info const* exception_wrapper::type() const noexcept { return exception_ptr_get_type(ptr_); } inline folly::fbstring exception_wrapper::what() const { if (auto e = get_exception()) { return class_name() + ": " + e->what(); } return class_name(); } inline folly::fbstring exception_wrapper::class_name() const { auto const* const ti = type(); return !*this ? "" : !ti ? "" : folly::demangle(*ti); } template inline bool exception_wrapper::is_compatible_with() const noexcept { return exception_ptr_get_object(ptr_); } [[noreturn]] inline void exception_wrapper::throw_exception() const { ptr_ ? std::rethrow_exception(ptr_) : onNoExceptionError(__func__); } template [[noreturn]] inline void exception_wrapper::throw_with_nested(Ex&& ex) const { try { throw_exception(); } catch (...) { std::throw_with_nested(std::forward(ex)); } } template inline bool exception_wrapper::with_exception_(This&, Fn fn_, tag_t) { return void(fn_()), true; } template inline bool exception_wrapper::with_exception_(This& this_, Fn fn_, tag_t) { auto ptr = this_.template get_exception>(); return ptr && (void(fn_(static_cast(*ptr))), true); } template inline bool exception_wrapper::with_exception_(This& this_, Fn fn_) { using from_fn = with_exception_from_fn_; using from_ex = with_exception_from_ex_; using from = conditional_t::value, from_fn, from_ex>; using type = typename from::template apply; return with_exception_(this_, std::move(fn_), tag); } template inline void exception_wrapper::handle_( This& this_, char const* name, CatchFns&... fns) { using _ = bool[]; if (!this_) { onNoExceptionError(name); } bool handled = false; void(_{false, (handled = handled || with_exception_(this_, fns))...}); if (!handled) { this_.throw_exception(); } } template inline bool exception_wrapper::with_exception(Fn fn) { return with_exception_(*this, std::move(fn)); } template inline bool exception_wrapper::with_exception(Fn fn) const { return with_exception_(*this, std::move(fn)); } template inline void exception_wrapper::handle(CatchFns... fns) { handle_(*this, __func__, fns...); } template inline void exception_wrapper::handle(CatchFns... fns) const { handle_(*this, __func__, fns...); } } // namespace folly