#include <new>
#include "folly/Preprocessor.h"
+#include "folly/detail/UncaughtExceptionCounter.h"
namespace folly {
bool dismissed_;
};
-template<typename FunctionType>
+template <typename FunctionType>
class ScopeGuardImpl : public ScopeGuardImplBase {
public:
explicit ScopeGuardImpl(const FunctionType& fn)
: function_(std::move(fn)) {}
ScopeGuardImpl(ScopeGuardImpl&& other)
- : ScopeGuardImplBase(std::move(other)),
- function_(std::move(other.function_)) {
+ : ScopeGuardImplBase(std::move(other))
+ , function_(std::move(other.function_)) {
}
~ScopeGuardImpl() noexcept {
FunctionType function_;
};
-template<typename FunctionType>
+template <typename FunctionType>
ScopeGuardImpl<typename std::decay<FunctionType>::type>
makeGuard(FunctionType&& fn) {
return ScopeGuardImpl<typename std::decay<FunctionType>::type>(
typedef ScopeGuardImplBase&& ScopeGuard;
namespace detail {
+
+#if defined(FOLLY_EXCEPTION_COUNT_USE_CXA_GET_GLOBALS) || \
+ defined(FOLLY_EXCEPTION_COUNT_USE_GETPTD)
+
+/**
+ * ScopeGuard used for executing a function when leaving the current scope
+ * depending on the presence of a new uncaught exception.
+ *
+ * If the executeOnException template parameter is true, the function is
+ * executed if a new uncaught exception is present at the end of the scope.
+ * If the parameter is false, then the function is executed if no new uncaught
+ * exceptions are present at the end of the scope.
+ *
+ * Used to implement SCOPE_FAIL and SCOPE_SUCCES below.
+ */
+template <typename FunctionType, bool executeOnException>
+class ScopeGuardForNewException {
+ public:
+ explicit ScopeGuardForNewException(const FunctionType& fn)
+ : function_(fn) {
+ }
+
+ explicit ScopeGuardForNewException(FunctionType&& fn)
+ : function_(std::move(fn)) {
+ }
+
+ ScopeGuardForNewException(ScopeGuardForNewException&& other)
+ : function_(std::move(other.function_))
+ , exceptionCounter_(std::move(other.exceptionCounter_)) {
+ }
+
+ ~ScopeGuardForNewException() noexcept {
+ if (executeOnException == exceptionCounter_.isNewUncaughtException()) {
+ execute();
+ }
+ }
+
+ private:
+ ScopeGuardForNewException(const ScopeGuardForNewException& other) = delete;
+
+ void* operator new(size_t) = delete;
+
+ void execute() noexcept { function_(); }
+
+ FunctionType function_;
+ UncaughtExceptionCounter exceptionCounter_;
+};
+
+/**
+ * Internal use for the macro SCOPE_FAIL below
+ */
+enum class ScopeGuardOnFail {};
+
+template <typename FunctionType>
+ScopeGuardForNewException<typename std::decay<FunctionType>::type, true>
+operator+(detail::ScopeGuardOnFail, FunctionType&& fn) {
+ return
+ ScopeGuardForNewException<typename std::decay<FunctionType>::type, true>(
+ std::forward<FunctionType>(fn));
+}
+
+/**
+ * Internal use for the macro SCOPE_SUCCESS below
+ */
+enum class ScopeGuardOnSuccess {};
+
+template <typename FunctionType>
+ScopeGuardForNewException<typename std::decay<FunctionType>::type, false>
+operator+(ScopeGuardOnSuccess, FunctionType&& fn) {
+ return
+ ScopeGuardForNewException<typename std::decay<FunctionType>::type, false>(
+ std::forward<FunctionType>(fn));
+}
+
+#endif // native uncaught_exception() supported
+
/**
* Internal use for the macro SCOPE_EXIT below
*/
auto FB_ANONYMOUS_VARIABLE(SCOPE_EXIT_STATE) \
= ::folly::detail::ScopeGuardOnExit() + [&]
+#if defined(FOLLY_EXCEPTION_COUNT_USE_CXA_GET_GLOBALS) || \
+ defined(FOLLY_EXCEPTION_COUNT_USE_GETPTD)
+#define SCOPE_FAIL \
+ auto FB_ANONYMOUS_VARIABLE(SCOPE_FAIL_STATE) \
+ = ::folly::detail::ScopeGuardOnFail() + [&]
+
+#define SCOPE_SUCCESS \
+ auto FB_ANONYMOUS_VARIABLE(SCOPE_SUCCESS_STATE) \
+ = ::folly::detail::ScopeGuardOnSuccess() + [&]
+#endif // native uncaught_exception() supported
+
#endif // FOLLY_SCOPEGUARD_H_
--- /dev/null
+/*
+ * Copyright 2013 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.
+ */
+
+#ifndef FOLLY_DETAIL_UNCAUGHTEXCEPTIONCOUNTER_H_
+#define FOLLY_DETAIL_UNCAUGHTEXCEPTIONCOUNTER_H_
+
+#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) // 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
+#else
+// Raise an error when trying to use this on unsupported platforms.
+#error "Unsupported platform, don't include this header."
+#endif
+
+
+namespace folly { namespace detail {
+
+/**
+ * Used to check if a new uncaught exception was thrown by monitoring the
+ * number of uncaught exceptions.
+ *
+ * Usage:
+ * - create a new UncaughtExceptionCounter object
+ * - call isNewUncaughtException() on the new object to check if a new
+ * uncaught exception was thrown since the object was created
+ */
+class UncaughtExceptionCounter {
+ public:
+ UncaughtExceptionCounter()
+ : exceptionCount_(getUncaughtExceptionCount()) {}
+
+ UncaughtExceptionCounter(const UncaughtExceptionCounter& other)
+ : exceptionCount_(other.exceptionCount_) {}
+
+ bool isNewUncaughtException() noexcept {
+ return getUncaughtExceptionCount() > 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));
+#endif
+}
+
+}} // namespaces
+
+#endif /* FOLLY_DETAIL_UNCAUGHTEXCEPTIONCOUNTER_H_ */
};
extern "C" {
-__cxa_eh_globals* __cxa_get_globals(void);
-__cxa_eh_globals* __cxa_get_globals_fast(void);
+__cxa_eh_globals* __cxa_get_globals(void) noexcept;
+__cxa_eh_globals* __cxa_get_globals_fast(void) noexcept;
}
} // namespace __cxxabiv1
}
}
+void testScopeFailAndScopeSuccess(ErrorBehavior error, bool expectFail) {
+ bool scopeFailExecuted = false;
+ bool scopeSuccessExecuted = false;
+
+ try {
+ SCOPE_FAIL { scopeFailExecuted = true; };
+ SCOPE_SUCCESS { scopeSuccessExecuted = true; };
+
+ try {
+ if (error == ErrorBehavior::HANDLED_ERROR) {
+ throw std::runtime_error("throwing an expected error");
+ } else if (error == ErrorBehavior::UNHANDLED_ERROR) {
+ throw "never throw raw strings";
+ }
+ } catch (const std::runtime_error&) {
+ }
+ } catch (...) {
+ // Outer catch to swallow the error for the UNHANDLED_ERROR behavior
+ }
+
+ EXPECT_EQ(expectFail, scopeFailExecuted);
+ EXPECT_EQ(!expectFail, scopeSuccessExecuted);
+}
+
+TEST(ScopeGuard, TEST_SCOPE_FAIL_AND_SCOPE_SUCCESS) {
+ testScopeFailAndScopeSuccess(ErrorBehavior::SUCCESS, false);
+ testScopeFailAndScopeSuccess(ErrorBehavior::HANDLED_ERROR, false);
+ testScopeFailAndScopeSuccess(ErrorBehavior::UNHANDLED_ERROR, true);
+}
+
int main(int argc, char** argv) {
testing::InitGoogleTest(&argc, argv);
google::ParseCommandLineFlags(&argc, &argv, true);