--- /dev/null
+/*
+ * Copyright 2016 Facebook, Inc.
+ *
+ * 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 <exception>
+
+#if defined(__GNUG__) || defined(__CLANG__)
+#define FOLLY_EXCEPTION_COUNT_USE_CXA_GET_GLOBALS
+namespace __cxxabiv1 {
+// forward declaration (originally defined in unwind-cxx.h from from libstdc++)
+struct __cxa_eh_globals;
+// declared in cxxabi.h from libstdc++-v3
+extern "C" __cxa_eh_globals* __cxa_get_globals() noexcept;
+}
+#elif defined(_MSC_VER) && (_MSC_VER >= 1400) && \
+ (_MSC_VER < 1900) // MSVC++ 8.0 or greater
+#define FOLLY_EXCEPTION_COUNT_USE_GETPTD
+// forward declaration (originally defined in mtdll.h from MSVCRT)
+struct _tiddata;
+extern "C" _tiddata* _getptd(); // declared in mtdll.h from MSVCRT
+#elif defined(_MSC_VER) && (_MSC_VER >= 1900) // MSVC++ 2015
+#define FOLLY_EXCEPTION_COUNT_USE_STD
+#else
+// Raise an error when trying to use this on unsupported platforms.
+#error "Unsupported platform, don't include this header."
+#endif
+
+namespace folly {
+
+/**
+ * Returns the number of uncaught exceptions.
+ *
+ * This function is based on Evgeny Panasyuk's implementation from here:
+ * http://fburl.com/15190026
+ */
+inline int uncaught_exceptions() noexcept {
+#if defined(FOLLY_EXCEPTION_COUNT_USE_CXA_GET_GLOBALS)
+ // __cxa_get_globals returns a __cxa_eh_globals* (defined in unwind-cxx.h).
+ // The offset below returns __cxa_eh_globals::uncaughtExceptions.
+ return *(reinterpret_cast<unsigned int*>(static_cast<char*>(
+ static_cast<void*>(__cxxabiv1::__cxa_get_globals())) + sizeof(void*)));
+#elif defined(FOLLY_EXCEPTION_COUNT_USE_GETPTD)
+ // _getptd() returns a _tiddata* (defined in mtdll.h).
+ // The offset below returns _tiddata::_ProcessingThrow.
+ return *(reinterpret_cast<int*>(static_cast<char*>(
+ static_cast<void*>(_getptd())) + sizeof(void*) * 28 + 0x4 * 8));
+#elif defined(FOLLY_EXCEPTION_COUNT_USE_STD)
+ return std::uncaught_exceptions();
+#endif
+}
+
+} // namespaces
#include <exception>
-#if defined(__GNUG__) || defined(__CLANG__)
-#define FOLLY_EXCEPTION_COUNT_USE_CXA_GET_GLOBALS
-namespace __cxxabiv1 {
-// forward declaration (originally defined in unwind-cxx.h from from libstdc++)
-struct __cxa_eh_globals;
-// declared in cxxabi.h from libstdc++-v3
-extern "C" __cxa_eh_globals* __cxa_get_globals() noexcept;
-}
-#elif defined(_MSC_VER) && (_MSC_VER >= 1400) && \
- (_MSC_VER < 1900) // MSVC++ 8.0 or greater
-#define FOLLY_EXCEPTION_COUNT_USE_GETPTD
-// forward declaration (originally defined in mtdll.h from MSVCRT)
-struct _tiddata;
-extern "C" _tiddata* _getptd(); // declared in mtdll.h from MSVCRT
-#elif defined(_MSC_VER) && (_MSC_VER >= 1900) // MSVC++ 2015
-#define FOLLY_EXCEPTION_COUNT_USE_STD
-#else
-// Raise an error when trying to use this on unsupported platforms.
-#error "Unsupported platform, don't include this header."
-#endif
-
+#include <folly/UncaughtExceptions.h>
namespace folly { namespace detail {
class UncaughtExceptionCounter {
public:
UncaughtExceptionCounter() noexcept
- : exceptionCount_(getUncaughtExceptionCount()) {}
+ : exceptionCount_(folly::uncaught_exceptions()) {}
UncaughtExceptionCounter(const UncaughtExceptionCounter& other) noexcept
: exceptionCount_(other.exceptionCount_) {}
bool isNewUncaughtException() noexcept {
- return getUncaughtExceptionCount() > exceptionCount_;
+ return folly::uncaught_exceptions() > exceptionCount_;
}
private:
- int getUncaughtExceptionCount() noexcept;
-
int exceptionCount_;
};
-/**
- * Returns the number of uncaught exceptions.
- *
- * This function is based on Evgeny Panasyuk's implementation from here:
- * http://fburl.com/15190026
- */
-inline int UncaughtExceptionCounter::getUncaughtExceptionCount() noexcept {
-#if defined(FOLLY_EXCEPTION_COUNT_USE_CXA_GET_GLOBALS)
- // __cxa_get_globals returns a __cxa_eh_globals* (defined in unwind-cxx.h).
- // The offset below returns __cxa_eh_globals::uncaughtExceptions.
- return *(reinterpret_cast<unsigned int*>(static_cast<char*>(
- static_cast<void*>(__cxxabiv1::__cxa_get_globals())) + sizeof(void*)));
-#elif defined(FOLLY_EXCEPTION_COUNT_USE_GETPTD)
- // _getptd() returns a _tiddata* (defined in mtdll.h).
- // The offset below returns _tiddata::_ProcessingThrow.
- return *(reinterpret_cast<int*>(static_cast<char*>(
- static_cast<void*>(_getptd())) + sizeof(void*) * 28 + 0x4 * 8));
-#elif defined(FOLLY_EXCEPTION_COUNT_USE_STD)
- return std::uncaught_exceptions();
-#endif
-}
-
}} // namespaces
--- /dev/null
+/*
+ * Copyright 2016 Facebook, Inc.
+ *
+ * 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 <folly/Conv.h>
+#include <folly/UncaughtExceptions.h>
+#include <folly/portability/GTest.h>
+#include <glog/logging.h>
+
+/*
+ * Test helper class, when goes out of scope it validaes that
+ * folly::uncaught_exceptions() returns the specified
+ * value.
+ */
+class Validator {
+ public:
+ Validator(int expectedCount, const std::string& msg)
+ : expectedCount_(expectedCount), msg_(msg) {}
+
+ // Automatic validation during destruction.
+ ~Validator() {
+ validate();
+ }
+
+ // Invoke to validate explicitly.
+ void validate() {
+ LOG(INFO) << msg_ << ", expected " << expectedCount_ << ", is "
+ << folly::uncaught_exceptions();
+ EXPECT_EQ(expectedCount_, folly::uncaught_exceptions()) << msg_;
+ }
+
+ private:
+ const int32_t expectedCount_;
+ const std::string msg_;
+};
+
+TEST(UncaughtExceptions, no_exception) {
+ Validator validator(0, "no_exception");
+}
+
+TEST(UncaughtExceptions, no_uncaught_exception) {
+ Validator validator(0, "no_uncaught_exception");
+ try {
+ throw std::runtime_error("exception");
+ } catch (const std::runtime_error& e) {
+ validator.validate();
+ }
+}
+
+TEST(UncaughtExceptions, one_uncaught_exception) {
+ try {
+ Validator validator(1, "one_uncaught_exception");
+ throw std::runtime_error("exception");
+ } catch (const std::runtime_error& e) {
+ }
+}
+
+TEST(UncaughtExceptions, 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& e) {
+ EXPECT_EQ(0, folly::uncaught_exceptions());
+ Validator validatorRethrow(1, "catch_rethrow");
+ throw;
+ }
+ } catch (const std::runtime_error& e) {
+ EXPECT_EQ(0, folly::uncaught_exceptions());
+ }
+}
+
+[[noreturn]] void throwingFunction() {
+ Validator validator(1, "one_uncaught_exception_in_function");
+ throw std::runtime_error("exception");
+}
+
+TEST(UncaughtExceptions, 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 <size_t N, size_t I = N>
+struct ThrowInDestructor {
+ using InnerThrowInDestructor = ThrowInDestructor<N, I - 1>;
+ ThrowInDestructor() {}
+
+ ~ThrowInDestructor() {
+ try {
+ InnerThrowInDestructor stackObjectThrowingOnUnwind;
+ Validator validator(
+ N - I + 1, "validating in " + folly::to<std::string>(I));
+ LOG(INFO) << "throwing in ~ThrowInDestructor " << I;
+ throw std::logic_error("inner");
+ } catch (const std::logic_error& e) {
+ LOG(INFO) << "catching in ~ThrowInDestructor " << I << " expecting "
+ << N - I << ", it is " << folly::uncaught_exceptions();
+ EXPECT_EQ(N - I, folly::uncaught_exceptions());
+ }
+ }
+};
+
+/*
+ * Terminate recursion
+ */
+template <size_t N>
+struct ThrowInDestructor<N, 0> {
+ ThrowInDestructor() = default;
+ ~ThrowInDestructor() = default;
+};
+
+TEST(UncaughtExceptions, two_uncaught_exceptions) {
+ ThrowInDestructor<2> twoUncaughtExceptions;
+}
+
+TEST(UncaughtExceptions, ten_uncaught_exceptions) {
+ ThrowInDestructor<10> twoUncaughtExceptions;
+}
+
+struct ThrowingConstructor {
+ [[noreturn]] ThrowingConstructor() noexcept(false) {
+ throw std::runtime_error("exception");
+ }
+};
+
+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, folly::uncaught_exceptions());
+ }
+};
+
+TEST(UncaughtExceptions, one_exception_in_ctor_initializer_expression) {
+ EXPECT_THROW(
+ { InheritsThrowingConstructor inheritsThrowingConstructor; },
+ std::runtime_error);
+}