diff --git a/folly/lang/BUCK b/folly/lang/BUCK index f5a2d27fdf9..a0d42f484db 100644 --- a/folly/lang/BUCK +++ b/folly/lang/BUCK @@ -265,8 +265,6 @@ cpp_library( srcs = ["UncaughtExceptions.cpp"], headers = ["UncaughtExceptions.h"], exported_deps = [ - "//folly:cpp_attributes", - "//folly:likely", - "//folly:portability", + ":exception", ], ) diff --git a/folly/lang/Exception.cpp b/folly/lang/Exception.cpp index bc6a2249019..756272c6448 100644 --- a/folly/lang/Exception.cpp +++ b/folly/lang/Exception.cpp @@ -22,6 +22,41 @@ #include +#if defined(__GLIBCXX__) || defined(_LIBCPP_VERSION) + +namespace __cxxabiv1 { + +struct __cxa_eh_globals { + void* caught_exceptions_; + unsigned int uncaught_exceptions_; +}; + +#if defined(__GLIBCXX__) +extern "C" [[gnu::const]] __cxa_eh_globals* __cxa_get_globals() noexcept; +#else +extern "C" __cxa_eh_globals* __cxa_get_globals(); +#endif + +} // namespace __cxxabiv1 + +#endif + +namespace folly { + +namespace detail { + +unsigned int* uncaught_exceptions_ptr() noexcept { + assert(kIsGlibcxx || kIsLibcpp); +#if defined(__GLIBCXX__) || defined(_LIBCPP_VERSION) + return &__cxxabiv1::__cxa_get_globals()->uncaught_exceptions_; +#endif + return nullptr; +} + +} // namespace detail + +} // namespace folly + // Accesses std::type_info and std::exception_ptr internals. Since these vary // by platform and library, import or copy the structure and function // signatures from each platform and library. diff --git a/folly/lang/Exception.h b/folly/lang/Exception.h index dad6094bb13..c4639b8afcd 100644 --- a/folly/lang/Exception.h +++ b/folly/lang/Exception.h @@ -310,6 +310,32 @@ catch_exception(Try&& t, Catch&& c, CatchA&&... a) noexcept( #endif } +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 { +#if defined(__APPLE__) + return std::uncaught_exceptions(); +#elif defined(_CPPLIB_VER) + return std::uncaught_exceptions(); +#elif defined(__has_feature) && !FOLLY_HAS_FEATURE(cxx_thread_local) + return std::uncaught_exceptions(); +#else + thread_local unsigned int* ct; + return to_signed( + FOLLY_LIKELY(!!ct) ? *ct : *(ct = detail::uncaught_exceptions_ptr())); +#endif +} + namespace detail { #if FOLLY_APPLE_IOS #if __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_12_0 diff --git a/folly/lang/UncaughtExceptions.cpp b/folly/lang/UncaughtExceptions.cpp index 25326224628..b32416bdcca 100644 --- a/folly/lang/UncaughtExceptions.cpp +++ b/folly/lang/UncaughtExceptions.cpp @@ -15,31 +15,3 @@ */ #include - -#if defined(__GLIBCXX__) || defined(_LIBCPP_VERSION) - -namespace __cxxabiv1 { -struct __cxa_eh_globals { - void* caught_exceptions_; - unsigned int uncaught_exceptions_; -}; -extern "C" __cxa_eh_globals* __cxa_get_globals(); -} // namespace __cxxabiv1 - -#endif - -namespace folly { - -namespace detail { - -unsigned int* uncaught_exceptions_ptr() noexcept { - assert(kIsGlibcxx || kIsLibcpp); -#if defined(__GLIBCXX__) || defined(_LIBCPP_VERSION) - return &__cxxabiv1::__cxa_get_globals()->uncaught_exceptions_; -#endif - return nullptr; -} - -} // namespace detail - -} // namespace folly diff --git a/folly/lang/UncaughtExceptions.h b/folly/lang/UncaughtExceptions.h index b00187c2042..e7b41b03445 100644 --- a/folly/lang/UncaughtExceptions.h +++ b/folly/lang/UncaughtExceptions.h @@ -16,38 +16,4 @@ #pragma once -#include -#include - -#include -#include -#include - -namespace folly { - -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 { -#if defined(__APPLE__) - return std::uncaught_exceptions(); -#elif defined(_CPPLIB_VER) - return std::uncaught_exceptions(); -#elif defined(__has_feature) && !FOLLY_HAS_FEATURE(cxx_thread_local) - return std::uncaught_exceptions(); -#else - thread_local unsigned int* ct; - return FOLLY_LIKELY(!!ct) ? *ct : *(ct = detail::uncaught_exceptions_ptr()); -#endif -} - -} // namespace folly +#include diff --git a/folly/lang/test/BUCK b/folly/lang/test/BUCK index 966f98660f4..198ea189169 100644 --- a/folly/lang/test/BUCK +++ b/folly/lang/test/BUCK @@ -220,27 +220,3 @@ cpp_unittest( "//folly/portability:gtest", ], ) - -cpp_benchmark( - name = "uncaught_exceptions_bench", - srcs = ["UncaughtExceptionsBench.cpp"], - deps = [ - "//folly:benchmark", - "//folly/lang:hint", - "//folly/lang:keep", - "//folly/lang:uncaught_exceptions", - ], -) - -cpp_unittest( - name = "uncaught_exceptions_test", - srcs = ["UncaughtExceptionsTest.cpp"], - headers = [], - supports_static_listing = False, - deps = [ - "//folly:conv", - "//folly/functional:invoke", - "//folly/lang:uncaught_exceptions", - "//folly/portability:gtest", - ], -) diff --git a/folly/lang/test/ExceptionBench.cpp b/folly/lang/test/ExceptionBench.cpp index 5e6ba823a08..6bae12f87dd 100644 --- a/folly/lang/test/ExceptionBench.cpp +++ b/folly/lang/test/ExceptionBench.cpp @@ -21,6 +21,14 @@ #include #include +extern "C" FOLLY_KEEP int check_std_uncaught_exceptions() { + return std::uncaught_exceptions(); +} + +extern "C" FOLLY_KEEP int check_folly_uncaught_exceptions() { + return folly::uncaught_exceptions(); +} + namespace { template @@ -68,6 +76,26 @@ check_folly_exception_ptr_get_object_hint_vmi( // return folly::exception_ptr_get_object_hint(ptr, folly::tag); } +BENCHMARK(std_uncaught_exceptions, iters) { + int s = 0; + while (iters--) { + int u = std::uncaught_exceptions(); + folly::compiler_must_not_predict(u); + s ^= u; + } + folly::compiler_must_not_elide(s); +} + +BENCHMARK(folly_uncaught_exceptions, iters) { + int s = 0; + while (iters--) { + int u = folly::uncaught_exceptions(); + folly::compiler_must_not_predict(u); + s ^= u; + } + folly::compiler_must_not_elide(s); +} + BENCHMARK(get_object_fail, iters) { folly::BenchmarkSuspender braces; bool result = false; diff --git a/folly/lang/test/ExceptionTest.cpp b/folly/lang/test/ExceptionTest.cpp index 57cb6599046..a31d059e201 100644 --- a/folly/lang/test/ExceptionTest.cpp +++ b/folly/lang/test/ExceptionTest.cpp @@ -208,6 +208,26 @@ TEST_F(ExceptionTest, rethrow_current_exception) { std::runtime_error); } +TEST_F(ExceptionTest, uncaught_exception) { + struct dtor { + unsigned expected; + explicit dtor(unsigned e) noexcept : expected{e} {} + ~dtor() { + EXPECT_EQ(expected, std::uncaught_exceptions()); + EXPECT_EQ(expected, folly::uncaught_exceptions()); + } + }; + try { + dtor obj{0}; + } catch (...) { + } + try { + dtor obj{1}; + throw std::exception(); + } catch (...) { + } +} + TEST_F(ExceptionTest, exception_ptr_empty) { auto ptr = std::exception_ptr(); EXPECT_EQ(nullptr, folly::exception_ptr_get_type(ptr)); diff --git a/folly/lang/test/UncaughtExceptionsBench.cpp b/folly/lang/test/UncaughtExceptionsBench.cpp deleted file mode 100644 index 544a4f20be9..00000000000 --- a/folly/lang/test/UncaughtExceptionsBench.cpp +++ /dev/null @@ -1,49 +0,0 @@ -/* - * 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 - -#include -#include -#include - -extern "C" FOLLY_KEEP int check_folly_uncaught_exceptions() { - return folly::uncaught_exceptions(); -} - -BENCHMARK(std_uncaught_exceptions, iters) { - int s = 0; - while (iters--) { - int u = std::uncaught_exceptions(); - folly::compiler_must_not_predict(u); - s ^= u; - } - folly::compiler_must_not_elide(s); -} - -BENCHMARK(folly_uncaught_exceptions, iters) { - int s = 0; - while (iters--) { - int u = folly::uncaught_exceptions(); - folly::compiler_must_not_predict(u); - s ^= u; - } - folly::compiler_must_not_elide(s); -} - -int main() { - folly::runBenchmarks(); -} diff --git a/folly/lang/test/UncaughtExceptionsTest.cpp b/folly/lang/test/UncaughtExceptionsTest.cpp deleted file mode 100644 index e05977b8c80..00000000000 --- a/folly/lang/test/UncaughtExceptionsTest.cpp +++ /dev/null @@ -1,187 +0,0 @@ -/* - * 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 - -#include -#include -#include - -FOLLY_CREATE_QUAL_INVOKER(invoke_std, std::uncaught_exceptions); -FOLLY_CREATE_QUAL_INVOKER(invoke_folly, folly::uncaught_exceptions); - -template -struct UncaughtExceptionsTest : testing::TestWithParam {}; -TYPED_TEST_SUITE_P(UncaughtExceptionsTest); - -/* - * Test helper class, when goes out of scope it validaes that - * folly::uncaught_exceptions() returns the specified - * value. - */ -template -class Validator { - public: - const Impl impl{}; - - Validator(int expectedCount, const std::string& msg) - : expectedCount_(expectedCount), msg_(msg) {} - - // Automatic validation during destruction. - ~Validator() { validate(); } - - // Invoke to validate explicitly. - void validate() { EXPECT_EQ(expectedCount_, impl()) << msg_; } - - private: - const int32_t expectedCount_; - const std::string msg_; -}; - -TYPED_TEST_P(UncaughtExceptionsTest, no_exception) { - Validator validator(0, "no_exception"); -} - -TYPED_TEST_P(UncaughtExceptionsTest, no_uncaught_exception) { - Validator validator(0, "no_uncaught_exception"); - try { - throw std::runtime_error("exception"); - } catch (const std::runtime_error&) { - validator.validate(); - } -} - -TYPED_TEST_P(UncaughtExceptionsTest, one_uncaught_exception) { - try { - Validator validator(1, "one_uncaught_exception"); - throw std::runtime_error("exception"); - } catch (const std::runtime_error&) { - } -} - -TYPED_TEST_P(UncaughtExceptionsTest, catch_rethrow) { - try { - Validator validatorOuter(1, "catch_rethrow_outer"); - try { - Validator validatorInner(1, "catch_rethrow_inner"); - throw std::runtime_error("exception"); - } catch (const std::runtime_error&) { - EXPECT_EQ(0, TypeParam{}()); - Validator validatorRethrow(1, "catch_rethrow"); - throw; - } - } catch (const std::runtime_error&) { - EXPECT_EQ(0, TypeParam{}()); - } -} - -template -[[noreturn]] void throwingFunction() { - Validator validator(1, "one_uncaught_exception_in_function"); - throw std::runtime_error("exception"); -} - -TYPED_TEST_P(UncaughtExceptionsTest, one_uncaught_exception_in_function) { - EXPECT_THROW({ throwingFunction(); }, std::runtime_error); -} - -/* - * Test helper class. Generates N wrapped classes/objects. - * The destructor N of the most outer class creates the N-1 - * object, and N - 1 object destructor creating the N-2 object, - * and so on. Each destructor throws an exception after creating - * the inner object on the stack, thus the number of exceptions - * accumulates while the stack is unwinding. It's used to test - * the folly::uncaught_exceptions() with value >= 2. - */ -template -struct ThrowInDestructor { - using InnerThrowInDestructor = ThrowInDestructor; - - Impl impl; - - ThrowInDestructor() {} - - ~ThrowInDestructor() { - try { - InnerThrowInDestructor stackObjectThrowingOnUnwind; - (void)stackObjectThrowingOnUnwind; - Validator validator( - N - I + 1, "validating in " + folly::to(I)); - throw std::logic_error("inner"); - } catch (const std::logic_error&) { - EXPECT_EQ(N - I, impl()); - } - } -}; - -/* - * Terminate recursion - */ -template -struct ThrowInDestructor { - ThrowInDestructor() = default; - ~ThrowInDestructor() = default; -}; - -TYPED_TEST_P(UncaughtExceptionsTest, two_uncaught_exceptions) { - ThrowInDestructor twoUncaughtExceptions; -} - -TYPED_TEST_P(UncaughtExceptionsTest, ten_uncaught_exceptions) { - ThrowInDestructor twoUncaughtExceptions; -} - -struct ThrowingConstructor { - [[noreturn]] ThrowingConstructor() noexcept(false) { - throw std::runtime_error("exception"); - } -}; - -template -struct InheritsThrowingConstructor : public Validator, - public ThrowingConstructor { - InheritsThrowingConstructor() try : Validator - (1, "one_exception_in_ctor_initializer_expression"), - ThrowingConstructor() {} - catch (...) { - // This is being re-thrown once the catch block ends, so I guess - // it's similar to a catch/throw; (re-throw) behavior and thus the value - // is 0. - EXPECT_EQ(0, Impl{}()); - } -}; - -TYPED_TEST_P( - UncaughtExceptionsTest, one_exception_in_ctor_initializer_expression) { - EXPECT_THROW( - { InheritsThrowingConstructor inheritsThrowingConstructor; }, - std::runtime_error); -} - -REGISTER_TYPED_TEST_SUITE_P( - UncaughtExceptionsTest, - no_exception, - no_uncaught_exception, - one_uncaught_exception, - catch_rethrow, - one_uncaught_exception_in_function, - two_uncaught_exceptions, - ten_uncaught_exceptions, - one_exception_in_ctor_initializer_expression); - -INSTANTIATE_TYPED_TEST_SUITE_P(std, UncaughtExceptionsTest, invoke_std); -INSTANTIATE_TYPED_TEST_SUITE_P(folly, UncaughtExceptionsTest, invoke_folly);