/* * 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. */ #pragma once #include <atomic> #include <exception> #include <string> #include <type_traits> #include <utility> #include <folly/CPortability.h> #include <folly/CppAttributes.h> #include <folly/Likely.h> #include <folly/Portability.h> #include <folly/Traits.h> #include <folly/Utility.h> #include <folly/lang/Assume.h> #include <folly/lang/SafeAssert.h> #include <folly/lang/Thunk.h> #include <folly/lang/TypeInfo.h> namespace folly { /// throw_exception /// /// Throw an exception if exceptions are enabled, or terminate if compiled with /// -fno-exceptions. template <typename Ex> [[noreturn, FOLLY_ATTR_GNU_COLD]] FOLLY_NOINLINE void throw_exception(Ex&& ex) { … } /// terminate_with /// /// Terminates as if by forwarding to throw_exception but in a noexcept context. template <typename Ex> [[noreturn, FOLLY_ATTR_GNU_COLD]] FOLLY_NOINLINE void terminate_with( Ex&& ex) noexcept { … } namespace detail { struct throw_exception_arg_array_ { … }; struct throw_exception_arg_trivial_ { … }; struct throw_exception_arg_base_ { … }; throw_exception_arg_; throw_exception_arg_t; template <typename Ex, typename... Args> [[noreturn, FOLLY_ATTR_GNU_COLD]] FOLLY_NOINLINE void throw_exception_( Args... args) { … } template <typename Ex, typename... Args> [[noreturn, FOLLY_ATTR_GNU_COLD]] FOLLY_NOINLINE void terminate_with_( Args... args) noexcept { … } } // namespace detail /// throw_exception /// /// Construct and throw an exception if exceptions are enabled, or terminate if /// compiled with -fno-exceptions. /// /// Does not perfectly forward all its arguments. Instead, in the interest of /// minimizing common-case inline code size, decays its arguments as follows: /// * refs to arrays of char const are decayed to char const* /// * refs to arrays are otherwise invalid /// * refs to trivial types are decayed to values /// /// The reason for treating refs to arrays as invalid is to avoid having two /// behaviors for refs to arrays, one for the general case and one for where the /// inner type is char const. Having two behaviors can be surprising, so avoid. template <typename Ex, typename... Args> [[noreturn]] FOLLY_ERASE void throw_exception(Args&&... args) { … } /// terminate_with /// /// Terminates as if by forwarding to throw_exception within a noexcept context. template <typename Ex, typename... Args> [[noreturn]] FOLLY_ERASE void terminate_with(Args&&... args) { … } /// invoke_cold /// /// Invoke the provided function with the provided arguments. /// /// Usage note: /// Passing extra values as arguments rather than capturing them allows smaller /// inlined native code at the call-site. Passing function-pointers or function- /// references rather than general callables with captures allows allows smaller /// inlined native code at the call-site as well. /// /// Example: /// /// if (i < 0) { /// invoke_cold( /// [](int j) { /// std::string ret = doStepA(); /// doStepB(ret); /// doStepC(ret); /// }, /// i); /// } template < typename F, typename... A, typename FD = std::remove_pointer_t<std::decay_t<F>>, std::enable_if_t<!std::is_function<FD>::value, int> = 0, typename R = decltype(FOLLY_DECLVAL(F&&)(FOLLY_DECLVAL(A&&)...))> [[FOLLY_ATTR_GNU_COLD]] FOLLY_NOINLINE R invoke_cold(F&& f, A&&... a) // noexcept(noexcept(static_cast<F&&>(f)(static_cast<A&&>(a)...))) { … } template < typename F, typename... A, typename FD = std::remove_pointer_t<std::decay_t<F>>, std::enable_if_t<std::is_function<FD>::value, int> = 0, typename R = decltype(FOLLY_DECLVAL(F&&)(FOLLY_DECLVAL(A&&)...))> FOLLY_ERASE R invoke_cold(F&& f, A&&... a) // noexcept(noexcept(f(static_cast<A&&>(a)...))) { … } /// invoke_noreturn_cold /// /// Invoke the provided function with the provided arguments. If the invocation /// returns, terminate. /// /// May be used with throw_exception in cases where construction of the object /// to be thrown requires more than just invoking its constructor with a given /// sequence of arguments passed by reference - for example, if a string message /// must be computed before being passed to the constructor of the object to be /// thrown. /// /// Usage note: /// Passing extra values as arguments rather than capturing them allows smaller /// inlined native code at the call-site. /// /// Example: /// /// if (i < 0) { /// invoke_noreturn_cold( /// [](int j) { /// throw_exceptions(runtime_error(to<string>("invalid: ", j))); /// }, /// i); /// } template <typename F, typename... A> [[noreturn, FOLLY_ATTR_GNU_COLD]] FOLLY_NOINLINE void invoke_noreturn_cold(F&& f, A&&... a) noexcept( /* formatting */ noexcept(static_cast<F&&>(f)(static_cast<A&&>(a)...))) { … } /// catch_exception /// /// Invokes t; if exceptions are enabled (if not compiled with -fno-exceptions), /// catches a thrown exception e of type E and invokes c, forwarding e and any /// trailing arguments. /// /// Usage note: /// As a general rule, pass Ex const& rather than unqualified Ex as the explicit /// template argument E. The catch statement catches E without qualifiers so /// if E is Ex then that translates to catch (Ex), but if E is Ex const& then /// that translates to catch (Ex const&). /// /// Usage note: /// Passing extra values as arguments rather than capturing them allows smaller /// inlined native code at the call-site. /// /// Example: /// /// int input = // ... /// int def = 45; /// auto result = catch_exception<std::runtime_error const&>( /// [=] { /// if (input < 0) throw std::runtime_error("foo"); /// return input; /// }, /// [](auto&& e, int num) { return num; }, /// def); /// assert(result == input < 0 ? def : input); template < typename E, typename Try, typename Catch, typename... CatchA, typename R = std::common_type_t< decltype(FOLLY_DECLVAL(Try&&)()), decltype(FOLLY_DECLVAL(Catch&&)( FOLLY_DECLVAL(E&), FOLLY_DECLVAL(CatchA&&)...))>> FOLLY_ERASE_TRYCATCH R catch_exception(Try&& t, Catch&& c, CatchA&&... a) { … } /// catch_exception /// /// Invokes t; if exceptions are enabled (if not compiled with -fno-exceptions), /// catches a thrown exception of any type and invokes c, forwarding any /// trailing arguments. // /// Usage note: /// Passing extra values as arguments rather than capturing them allows smaller /// inlined native code at the call-site. /// /// Example: /// /// int input = // ... /// int def = 45; /// auto result = catch_exception( /// [=] { /// if (input < 0) throw 11; /// return input; /// }, /// [](int num) { return num; }, /// def); /// assert(result == input < 0 ? def : input); template < typename Try, typename Catch, typename... CatchA, typename R = std::common_type_t< decltype(FOLLY_DECLVAL(Try&&)()), decltype(FOLLY_DECLVAL(Catch&&)(FOLLY_DECLVAL(CatchA&&)...))>> FOLLY_ERASE_TRYCATCH R catch_exception(Try&& t, Catch&& c, CatchA&&... a) noexcept( noexcept(static_cast<Catch&&>(c)(static_cast<CatchA&&>(a)...))) { … } /// rethrow_current_exception /// /// Equivalent to: /// /// throw; [[noreturn]] FOLLY_ERASE void rethrow_current_exception() { … } namespace detail { unsigned int* uncaught_exceptions_ptr() noexcept; } // namespace detail /// uncaught_exceptions /// /// An accelerated version of std::uncaught_exceptions. /// /// mimic: std::uncaught_exceptions, c++17 [[FOLLY_ATTR_GNU_PURE]] FOLLY_EXPORT FOLLY_ALWAYS_INLINE int uncaught_exceptions() noexcept { … } /// current_exception /// /// An accelerated version of std::current_exception. /// /// mimic: std::current_exception, c++11 std::exception_ptr current_exception() noexcept; namespace detail { #if FOLLY_APPLE_IOS #if __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_12_0 inline constexpr bool exception_ptr_access_ct = false; #else inline constexpr bool exception_ptr_access_ct = true; #endif #else inline constexpr bool exception_ptr_access_ct = …; #endif // 0 unknown, 1 true, -1 false extern std::atomic<int> exception_ptr_access_rt_cache_; [[FOLLY_ATTR_GNU_COLD]] bool exception_ptr_access_rt_v_() noexcept; [[FOLLY_ATTR_GNU_COLD]] bool exception_ptr_access_rt_() noexcept; inline bool exception_ptr_access_rt() noexcept { … } inline std::nullptr_t exception_ptr_nullptr() { … } template <typename T, typename Catch> auto exception_ptr_catching(std::exception_ptr const& ptr, Catch catch_) { … } std::type_info const* exception_ptr_exception_typeid( std::exception const&) noexcept; std::type_info const* exception_ptr_get_type_( std::exception_ptr const& ptr) noexcept; void* exception_ptr_get_object_( std::exception_ptr const&, std::type_info const*) noexcept; } // namespace detail // exception_ptr_access // // Whether exception_ptr_get_type and template exception_ptr_get_object always // return the type or object or only do so when the stored object is of some // concrete type inheriting std::exception, and whether the non non-template // overloads of exception_ptr_get_object works at all. // // Non-authoritative. For some known platforms, inspection of exception-ptr // objects fails. This is likely to do with mismatch between the application // ABI and the system-provided libstdc++/libc++/cxxabi ABI. May falsely return // true on other platforms. [[FOLLY_ATTR_GNU_PURE]] inline bool exception_ptr_access() noexcept { … } // exception_ptr_get_type // // Returns the true runtime type info of the exception as stored. inline std::type_info const* exception_ptr_get_type( std::exception_ptr const& ptr) noexcept { … } // exception_ptr_get_object // // Returns the address of the stored exception as if it were upcast to the // given type, if it could be upcast to that type. If no type is passed, // returns the address of the stored exception without upcasting. // // Note that the stored exception is always a copy of the thrown exception, and // on some platforms caught exceptions may be copied from the stored exception. // The address is only the address of the object as stored, not as thrown and // not as caught. inline void* exception_ptr_get_object( std::exception_ptr const& ptr, std::type_info const* const target) noexcept { … } // exception_ptr_get_object // // Returns the true address of the exception as stored without upcasting. inline void* exception_ptr_get_object( // std::exception_ptr const& ptr) noexcept { … } // exception_ptr_get_object // // Returns the address of the stored exception as if it were upcast to the // given type, if it could be upcast to that type. template <typename T> T* exception_ptr_get_object(std::exception_ptr const& ptr) noexcept { … } /// exception_ptr_try_get_object_exact_fast /// /// Returns the address of the stored exception as if it were upcast to the /// given type, if its concrete type is exactly equal to one of the types passed /// in the tag. /// /// May hypothetically fail in cases where multipe type-info objects exist for /// any of the given types. Positives are true but negatives may be either true /// or false. template <typename T, typename... S> T* exception_ptr_try_get_object_exact_fast( std::exception_ptr const& ptr, tag_t<S...>) noexcept { … } /// exception_ptr_get_object_hint /// /// Returns the address of the stored exception as if it were upcast to the /// given type, if it could be upcast to that type. /// /// If its concrete type is exactly equal to one of the types passed in the tag, /// this may be faster than exception_ptr_get_object without the hint. template <typename T, typename... S> T* exception_ptr_get_object_hint( std::exception_ptr const& ptr, tag_t<S...> const hint) noexcept { … } namespace detail { struct make_exception_ptr_with_arg_ { … }; std::exception_ptr make_exception_ptr_with_( make_exception_ptr_with_arg_ const&, void*) noexcept; template <typename F> struct make_exception_ptr_with_fn_ { … }; } // namespace detail /// make_exception_ptr_with_fn /// make_exception_ptr_with /// /// Constructs a std::exception_ptr. On some platforms, this form may be more /// efficient than std::make_exception_ptr. In particular, even when the latter /// is optimized not actually to throw, catch, and call std::current_exception /// internally, it remains specified to take its parameter by-value and to copy /// its parameter internally. Many in-practice exception types, including those /// which ship with standard libraries implementations, have copy constructors /// which may atomically modify refcounts; others may allocate and copy string /// data. In the best-case scenario, folly::make_exception_ptr_with may avoid /// these costs. // /// There are three overloads, with overload selection unambiguous. /// * A single invocable argument. The argument is invoked and its return value /// is the managed exception. /// * Variadic arguments, the first of which is in_place_type<E>. An exception /// of type E is created in-place with the remaining arguments forwarded to /// the constructor of E, and it is the managed exception. /// * Two arguments, the first of which is in_place. The argument is moved or /// copied and the result is the managed exception. This form is the closest /// to std::make_exception_ptr. /// /// Example: /// /// std::exception_ptr eptr = make_exception_ptr_with( /// [] { return std::runtime_error("message string"); }); /// /// std::exception_ptr eptr = make_exception_ptr_with( /// std::in_place_type<std::runtime_error>, "message string"); /// /// std::exception_ptr eptr = make_exception_ptr_with( /// std::in_place, std::runtime_error("message string"); /// /// In each example above, the variable eptr holds a managed exception object of /// type std::runtime_error with a message string "message string" that would be /// returned by member what(). /// /// Note that a managed exception object can have any value type whatsoever; it /// is not required to have value type of or inheriting std::exception. This is /// the same principle as for throw statements and throw_exception above. struct make_exception_ptr_with_fn { … }; inline constexpr make_exception_ptr_with_fn make_exception_ptr_with{ … }; // exception_shared_string // // An immutable refcounted string, with the same layout as a pointer, suitable // for use in an exception. Exceptions are intended to cheaply nothrow-copy- // constructible and mostly do not need to optimize moves, and this affects how // exception messages are best stored. // // May be constructed with a literal string in a very particular form. If so // constructed, (a literal copy of) the literal string will be held with no // refcount required. class exception_shared_string { … }; } // namespace folly