Skip to content

Commit

Permalink
Back out "exception_wrapper thrown variant via abi/runtime"
Browse files Browse the repository at this point in the history
Summary: D26331579 (aa7f74a) was the offending diff that caused an insta crash on Messenger Android and Instagram Android. Backing out for now.

Reviewed By: JunqiWang

Differential Revision: D28308563

fbshipit-source-id: 87a547d9ba7cb3b33a4ddee9e273943f8379d990
  • Loading branch information
Crystal Jin authored and facebook-github-bot committed May 8, 2021
1 parent 270a797 commit ba405c6
Show file tree
Hide file tree
Showing 4 changed files with 166 additions and 57 deletions.
86 changes: 68 additions & 18 deletions folly/ExceptionWrapper-inl.h
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,56 @@ inline std::exception const* exception_wrapper::as_exception_or_null_(
return nullptr;
}

static_assert(
!kMicrosoftAbiVer || (kMicrosoftAbiVer >= 1900 && kMicrosoftAbiVer <= 2000),
"exception_wrapper is untested and possibly broken on your version of "
"MSVC");

inline std::uintptr_t exception_wrapper::ExceptionPtr::as_int_(
std::exception_ptr const& ptr, std::exception const& e) noexcept {
if (!kMicrosoftAbiVer) {
return reinterpret_cast<std::uintptr_t>(&e);
} else {
// On Windows, as of MSVC2017, all thrown exceptions are copied to the stack
// first. Thus, we cannot depend on exception references associated with an
// exception_ptr to be live for the duration of the exception_ptr. We need
// to directly access the heap allocated memory inside the exception_ptr.
//
// std::exception_ptr is an opaque reinterpret_cast of
// std::shared_ptr<__ExceptionPtr>
// __ExceptionPtr is a non-virtual class with two members, a union and a
// bool. The union contains the now-undocumented EHExceptionRecord, which
// contains a struct which contains a void* which points to the heap
// allocated exception.
// We derive the offset to pExceptionObject via manual means.
FOLLY_PACK_PUSH
struct Win32ExceptionPtr {
char offset[8 + 4 * sizeof(void*)];
void* exceptionObject;
} FOLLY_PACK_ATTR;
FOLLY_PACK_POP

auto* win32ExceptionPtr =
reinterpret_cast<std::shared_ptr<Win32ExceptionPtr> const*>(&ptr)
->get();
return reinterpret_cast<std::uintptr_t>(win32ExceptionPtr->exceptionObject);
}
}
inline std::uintptr_t exception_wrapper::ExceptionPtr::as_int_(
std::exception_ptr const&, AnyException e) noexcept {
return reinterpret_cast<std::uintptr_t>(e.typeinfo_) + 1;
}
inline bool exception_wrapper::ExceptionPtr::has_exception_() const {
return 0 == exception_or_type_ % 2;
}
inline std::exception const* exception_wrapper::ExceptionPtr::as_exception_()
const {
return reinterpret_cast<std::exception const*>(exception_or_type_);
}
inline std::type_info const* exception_wrapper::ExceptionPtr::as_type_() const {
return reinterpret_cast<std::type_info const*>(exception_or_type_ - 1);
}

inline void exception_wrapper::ExceptionPtr::copy_(
exception_wrapper const* from, exception_wrapper* to) {
::new (static_cast<void*>(&to->eptr_)) ExceptionPtr(from->eptr_);
Expand All @@ -146,11 +196,14 @@ inline void exception_wrapper::ExceptionPtr::delete_(exception_wrapper* that) {
}
inline std::type_info const* exception_wrapper::ExceptionPtr::type_(
exception_wrapper const* that) {
return exception_ptr_get_type(that->eptr_.ptr_);
if (auto e = get_exception_(that)) {
return &typeid(*e);
}
return that->eptr_.as_type_();
}
inline std::exception const* exception_wrapper::ExceptionPtr::get_exception_(
exception_wrapper const* that) {
return exception_ptr_get_object<std::exception>(that->eptr_.ptr_);
return that->eptr_.has_exception_() ? that->eptr_.as_exception_() : nullptr;
}
inline exception_wrapper exception_wrapper::ExceptionPtr::get_exception_ptr_(
exception_wrapper const* that) {
Expand Down Expand Up @@ -195,8 +248,8 @@ inline exception_wrapper exception_wrapper::InPlace<Ex>::get_exception_ptr_(
exception_wrapper const* that) {
try {
throw_(that);
} catch (...) {
return exception_wrapper{std::current_exception()};
} catch (Ex const& ex) {
return exception_wrapper{std::current_exception(), ex};
}
}

Expand All @@ -215,8 +268,8 @@ inline exception_wrapper
exception_wrapper::SharedPtr::Impl<Ex>::get_exception_ptr_() const noexcept {
try {
throw_();
} catch (...) {
return exception_wrapper{std::current_exception()};
} catch (Ex& ex) {
return exception_wrapper{std::current_exception(), ex};
}
}
inline void exception_wrapper::SharedPtr::copy_(
Expand Down Expand Up @@ -254,7 +307,7 @@ inline exception_wrapper exception_wrapper::SharedPtr::get_exception_ptr_(
template <class Ex, typename... As>
inline exception_wrapper::exception_wrapper(
ThrownTag, in_place_type_t<Ex>, As&&... as)
: eptr_{std::make_exception_ptr(Ex(std::forward<As>(as)...))},
: eptr_{std::make_exception_ptr(Ex(std::forward<As>(as)...)), reinterpret_cast<std::uintptr_t>(std::addressof(typeid(Ex))) + 1u},
vptr_(&ExceptionPtr::ops_) {}

template <class Ex, typename... As>
Expand Down Expand Up @@ -302,17 +355,9 @@ inline exception_wrapper::~exception_wrapper() {

template <class Ex>
inline exception_wrapper::exception_wrapper(
std::exception_ptr const& ptr, Ex& ex) noexcept
: exception_wrapper{folly::copy(ptr), ex} {}

template <class Ex>
inline exception_wrapper::exception_wrapper(
std::exception_ptr&& ptr, Ex& ex) noexcept
: eptr_{std::move(ptr)}, vptr_(&ExceptionPtr::ops_) {
std::exception_ptr ptr, Ex& ex) noexcept
: eptr_{ptr, ExceptionPtr::as_int_(ptr, ex)}, vptr_(&ExceptionPtr::ops_) {
assert(eptr_.ptr_);
(void)ex;
assert(exception_ptr_get_object<Ex>(eptr_.ptr_));
assert(exception_ptr_get_object<Ex>(eptr_.ptr_) == &ex || kIsWindows);
}

namespace exception_wrapper_detail {
Expand Down Expand Up @@ -412,6 +457,9 @@ inline std::exception_ptr exception_wrapper::to_exception_ptr() const noexcept {
inline std::type_info const& exception_wrapper::none() noexcept {
return typeid(void);
}
inline std::type_info const& exception_wrapper::unknown() noexcept {
return typeid(Unknown);
}

inline std::type_info const& exception_wrapper::type() const noexcept {
return *vptr_->type_(this);
Expand All @@ -426,7 +474,9 @@ inline folly::fbstring exception_wrapper::what() const {

inline folly::fbstring exception_wrapper::class_name() const {
auto& ti = type();
return ti == none() ? "" : folly::demangle(ti);
return ti == none() ? ""
: ti == unknown() ? "<unknown exception>"
: folly::demangle(ti);
}

template <class Ex>
Expand Down
41 changes: 34 additions & 7 deletions folly/ExceptionWrapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,23 +37,50 @@ exception_wrapper::VTable const exception_wrapper::ExceptionPtr::ops_{
exception_wrapper::VTable const exception_wrapper::SharedPtr::ops_{
copy_, move_, delete_, throw_, type_, get_exception_, get_exception_ptr_};

namespace {
std::exception const* get_std_exception_(std::exception_ptr eptr) noexcept {
try {
std::rethrow_exception(eptr);
} catch (const std::exception& ex) {
return &ex;
} catch (...) {
return nullptr;
}
}
} // namespace

exception_wrapper exception_wrapper::from_exception_ptr(
std::exception_ptr const& ptr) noexcept {
return from_exception_ptr(folly::copy(ptr));
}

exception_wrapper exception_wrapper::from_exception_ptr(
std::exception_ptr&& ptr) noexcept {
return !ptr ? exception_wrapper() : exception_wrapper(std::move(ptr));
if (!ptr) {
return exception_wrapper();
}
try {
std::rethrow_exception(std::move(ptr));
} catch (std::exception& e) {
return exception_wrapper(std::current_exception(), e);
} catch (...) {
return exception_wrapper(std::current_exception());
}
}

exception_wrapper::exception_wrapper(std::exception_ptr const& ptr) noexcept
: exception_wrapper{folly::copy(ptr)} {}

exception_wrapper::exception_wrapper(std::exception_ptr&& ptr) noexcept {
exception_wrapper::exception_wrapper(std::exception_ptr ptr) noexcept
: exception_wrapper{} {
if (ptr) {
::new (&eptr_) ExceptionPtr{std::move(ptr)};
vptr_ = &ExceptionPtr::ops_;
if (auto e = get_std_exception_(ptr)) {
LOG(DFATAL)
<< "Performance error: Please construct exception_wrapper with a "
"reference to the std::exception along with the "
"std::exception_ptr.";
*this = exception_wrapper{std::move(ptr), *e};
} else {
Unknown uk;
*this = exception_wrapper{ptr, uk};
}
}
}

Expand Down
52 changes: 38 additions & 14 deletions folly/ExceptionWrapper.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,15 +31,13 @@
#include <utility>

#include <folly/CPortability.h>
#include <folly/CppAttributes.h>
#include <folly/Demangle.h>
#include <folly/ExceptionString.h>
#include <folly/FBString.h>
#include <folly/Portability.h>
#include <folly/Traits.h>
#include <folly/Utility.h>
#include <folly/lang/Assume.h>
#include <folly/lang/Exception.h>

#ifdef __GNUC__
#pragma GCC diagnostic push
Expand Down Expand Up @@ -179,7 +177,10 @@ class exception_wrapper final {
// 1. An small object stored in-situ.
// 2. A larger object stored on the heap and referenced with a
// std::shared_ptr.
// 3. A std::exception_ptr.
// 3. A std::exception_ptr, together with either:
// a. A pointer to the referenced std::exception object, or
// b. A pointer to a std::type_info object for the referenced exception,
// or for an unspecified type if the type is unknown.
// This is accomplished with the help of a union and a pointer to a hand-
// rolled virtual table. This virtual table contains pointers to functions
// that know which field of the union is active and do the proper action.
Expand Down Expand Up @@ -211,6 +212,8 @@ class exception_wrapper final {
using IsCatchAll =
std::is_same<arg_type<std::decay_t<CatchFn>>, AnyException>;

struct Unknown {};

// Sadly, with the gcc-4.9 platform, std::logic_error and std::runtime_error
// do not fit here. They also don't have noexcept copy-ctors, so the internal
// storage wouldn't be used anyway. For the gcc-5 platform, both logic_error
Expand Down Expand Up @@ -252,7 +255,19 @@ class exception_wrapper final {

struct ExceptionPtr {
std::exception_ptr ptr_;

std::uintptr_t exception_or_type_; // odd for type_info
static_assert(
1 < alignof(std::exception) && 1 < alignof(std::type_info),
"Surprise! std::exception and std::type_info don't have alignment "
"greater than one. as_int_ below will not work!");

static std::uintptr_t as_int_(
std::exception_ptr const& ptr, std::exception const& e) noexcept;
static std::uintptr_t as_int_(
std::exception_ptr const& ptr, AnyException e) noexcept;
bool has_exception_() const;
std::exception const* as_exception_() const;
std::type_info const* as_type_() const;
static void copy_(exception_wrapper const* from, exception_wrapper* to);
static void move_(exception_wrapper* from, exception_wrapper* to);
static void delete_(exception_wrapper* that);
Expand Down Expand Up @@ -393,19 +408,19 @@ class exception_wrapper final {

~exception_wrapper();

//! \pre `ptr` is empty, or it holds a reference to an exception that is not
//! derived from `std::exception`.
//! \post `!ptr || bool(*this)`
//! \post `hasThrownException() == bool(ptr)`
explicit exception_wrapper(std::exception_ptr const& ptr) noexcept;
explicit exception_wrapper(std::exception_ptr&& ptr) noexcept;
//! \post `hasThrownException() == true`
//! \post `type() == unknown()`
explicit exception_wrapper(std::exception_ptr ptr) noexcept;

//! \pre `ptr` holds a reference to `ex`.
//! \post `hasThrownException() == true`
//! \post `bool(*this)`
//! \post `type() == typeid(ex)`
template <class Ex>
exception_wrapper(std::exception_ptr const& ptr, Ex& ex) noexcept;
template <class Ex>
exception_wrapper(std::exception_ptr&& ptr, Ex& ex) noexcept;
exception_wrapper(std::exception_ptr ptr, Ex& ex) noexcept;

//! \pre `typeid(ex) == typeid(typename decay<Ex>::type)`
//! \post `bool(*this)`
Expand Down Expand Up @@ -491,16 +506,23 @@ class exception_wrapper final {
//! \return the `typeid` of an unspecified type used by
//! `exception_wrapper::type()` to denote an empty `exception_wrapper`.
static std::type_info const& none() noexcept;
//! \return the `typeid` of an unspecified type used by
//! `exception_wrapper::type()` to denote an `exception_wrapper` that
//! holds an exception of unknown type.
static std::type_info const& unknown() noexcept;

//! Returns the `typeid` of the wrapped exception object. If there is no
//! wrapped exception object, returns `exception_wrapper::none()`.
//! wrapped exception object, returns `exception_wrapper::none()`. If
//! this instance wraps an exception of unknown type not derived from
//! `std::exception`, returns `exception_wrapper::unknown()`.
std::type_info const& type() const noexcept;

//! \return If `get_exception() != nullptr`, `class_name() + ": " +
//! get_exception()->what()`; otherwise, `class_name()`.
folly::fbstring what() const;

//! \return If `!*this`, the empty string; otherwise,
//! \return If `!*this`, the empty string; otherwise, if
//! `type() == unknown()`, the string `"<unknown exception>"`; otherwise,
//! the result of `type().name()` after demangling.
folly::fbstring class_name() const;

Expand Down Expand Up @@ -636,8 +658,8 @@ template <typename F, typename Ex, typename... Exs>
inline exception_wrapper try_and_catch_(F&& f) {
try {
return try_and_catch_<F, Exs...>(std::forward<F>(f));
} catch (Ex&) {
return exception_wrapper(std::current_exception());
} catch (Ex& ex) {
return exception_wrapper(std::current_exception(), ex);
}
}
} // namespace detail
Expand Down Expand Up @@ -687,6 +709,8 @@ exception_wrapper try_and_catch(F&& fn) noexcept {
try {
static_cast<F&&>(fn)();
return exception_wrapper{};
} catch (std::exception const& ex) {
return exception_wrapper{std::current_exception(), ex};
} catch (...) {
return exception_wrapper{std::current_exception()};
}
Expand Down
Loading

0 comments on commit ba405c6

Please sign in to comment.