*
*/
class exception_wrapper {
+ protected:
+ template <typename Ex>
+ struct optimize;
+
public:
- exception_wrapper() : throwfn_(nullptr) { }
-
- // Implicitly construct an exception_wrapper from any std::exception
- template <typename T, typename =
- typename std::enable_if<std::is_base_of<std::exception, T>::value>::type>
- /* implicit */ exception_wrapper(T&& exn) {
- item_ = std::make_shared<T>(std::forward<T>(exn));
- throwfn_ = folly::detail::Thrower<T>::doThrow;
+ exception_wrapper() = default;
+
+ // Implicitly construct an exception_wrapper from a qualifying exception.
+ // See the optimize struct for details.
+ template <typename Ex, typename =
+ typename std::enable_if<optimize<Ex>::value>::type>
+ /* implicit */ exception_wrapper(Ex&& exn) {
+ item_ = std::make_shared<Ex>(std::forward<Ex>(exn));
+ throwfn_ = folly::detail::Thrower<Ex>::doThrow;
+ }
+
+ // The following two constructors are meant to emulate the behavior of
+ // try_and_catch in performance sensitive code as well as to be flexible
+ // enough to wrap exceptions of unknown type. There is an overload that
+ // takes an exception reference so that the wrapper can extract and store
+ // the exception's type and what() when possible.
+ //
+ // The canonical use case is to construct an all-catching exception wrapper
+ // with minimal overhead like so:
+ //
+ // try {
+ // // some throwing code
+ // } catch (const std::exception& e) {
+ // // won't lose e's type and what()
+ // exception_wrapper ew{std::current_exception(), e};
+ // } catch (...) {
+ // // everything else
+ // exception_wrapper ew{std::current_exception()};
+ // }
+ //
+ // try_and_catch is cleaner and preferable. Use it unless you're sure you need
+ // something like this instead.
+ template <typename Ex>
+ explicit exception_wrapper(std::exception_ptr eptr, Ex& exn) {
+ assign_eptr(eptr, exn);
+ }
+
+ explicit exception_wrapper(std::exception_ptr eptr) {
+ assign_eptr(eptr);
}
void throwException() const {
return false;
}
+ // If this exception wrapper wraps an exception of type Ex, with_exception
+ // will call f with the wrapped exception as an argument and return true, and
+ // will otherwise return false.
+ template <class Ex, class F>
+ typename std::enable_if<
+ std::is_base_of<std::exception, typename std::decay<Ex>::type>::value,
+ bool>::type
+ with_exception(F f) {
+ return with_exception1<typename std::decay<Ex>::type>(f, this);
+ }
+
+ // Const overload
template <class Ex, class F>
- bool with_exception(F f) {
- return with_exception1<Ex>(f, this);
+ typename std::enable_if<
+ std::is_base_of<std::exception, typename std::decay<Ex>::type>::value,
+ bool>::type
+ with_exception(F f) const {
+ return with_exception1<const typename std::decay<Ex>::type>(f, this);
}
+ // Overload for non-exceptions. Always rethrows.
template <class Ex, class F>
- bool with_exception(F f) const {
- return with_exception1<const Ex>(f, this);
+ typename std::enable_if<
+ !std::is_base_of<std::exception, typename std::decay<Ex>::type>::value,
+ bool>::type
+ with_exception(F f) const {
+ try {
+ throwException();
+ } catch (typename std::decay<Ex>::type& e) {
+ f(e);
+ return true;
+ } catch (...) {
+ // fall through
+ }
+ return false;
}
std::exception_ptr getExceptionPtr() const {
}
protected:
+ template <typename Ex>
+ struct optimize {
+ static const bool value =
+ std::is_base_of<std::exception, Ex>::value &&
+ std::is_copy_assignable<Ex>::value &&
+ !std::is_abstract<Ex>::value;
+ };
+
+ template <typename Ex>
+ void assign_eptr(std::exception_ptr eptr, Ex& e) {
+ this->eptr_ = eptr;
+ this->estr_ = exceptionStr(e).toStdString();
+ this->ename_ = demangle(typeid(e)).toStdString();
+ }
+
+ void assign_eptr(std::exception_ptr eptr) {
+ this->eptr_ = eptr;
+ }
+
// Optimized case: if we know what type the exception is, we can
// store a copy of the concrete type, and a helper function so we
// can rethrow it.
std::shared_ptr<std::exception> item_;
- void (*throwfn_)(std::exception*);
+ void (*throwfn_)(std::exception*){nullptr};
// Fallback case: store the library wrapper, which is less efficient
// but gets the job done. Also store exceptionPtr() the name of the
// exception type, so we can at least get those back out without
try_and_catch() : Base() {}
template <typename Ex>
- void assign_eptr(Ex& e) {
- this->eptr_ = std::current_exception();
- this->estr_ = exceptionStr(e).toStdString();
- this->ename_ = demangle(typeid(e)).toStdString();
- }
-
- template <typename Ex>
- struct optimize {
- static const bool value =
- std::is_base_of<std::exception, Ex>::value &&
- std::is_copy_assignable<Ex>::value &&
- !std::is_abstract<Ex>::value;
- };
-
- template <typename Ex>
- typename std::enable_if<!optimize<Ex>::value>::type
- assign_exception(Ex& e) {
- assign_eptr(e);
+ typename std::enable_if<!exception_wrapper::optimize<Ex>::value>::type
+ assign_exception(Ex& e, std::exception_ptr eptr) {
+ exception_wrapper::assign_eptr(eptr, e);
}
template <typename Ex>
- typename std::enable_if<optimize<Ex>::value>::type
- assign_exception(Ex& e) {
+ typename std::enable_if<exception_wrapper::optimize<Ex>::value>::type
+ assign_exception(Ex& e, std::exception_ptr eptr) {
this->item_ = std::make_shared<Ex>(e);
this->throwfn_ = folly::detail::Thrower<Ex>::doThrow;
}
Base::call_fn(std::move(fn));
} catch (LastException& e) {
if (typeid(e) == typeid(LastException&)) {
- assign_exception(e);
+ assign_exception(e, std::current_exception());
} else {
- assign_eptr(e);
+ exception_wrapper::assign_eptr(std::current_exception(), e);
}
}
}
setCallback_(
[p, funcm](Try<T>&& t) mutable {
p->fulfil([&]() {
- return (*funcm)(std::move(t));
- });
+ return (*funcm)(std::move(t));
+ });
});
return std::move(f);
setCallback_(
[p, funcm](Try<T>&& t) mutable {
if (t.hasException()) {
- p->setException(t.getException());
+ p->setException(std::move(t.exception()));
} else {
p->fulfil([&]() {
return (*funcm)(std::move(t.value()));
setCallback_(
[p, funcm](Try<T>&& t) mutable {
if (t.hasException()) {
- p->setException(t.getException());
+ p->setException(std::move(t.exception()));
} else {
p->fulfil([&]() {
return (*funcm)();
auto f2 = (*funcm)(std::move(t));
// that didn't throw, now we can steal p
f2.setCallback_([p](Try<B>&& b) mutable {
- p->fulfilTry(std::move(b));
- });
+ p->fulfilTry(std::move(b));
+ });
+ } catch (const std::exception& e) {
+ p->setException(exception_wrapper(std::current_exception(), e));
} catch (...) {
- p->setException(std::current_exception());
+ p->setException(exception_wrapper(std::current_exception()));
}
});
setCallback_(
[p, funcm](Try<T>&& t) mutable {
if (t.hasException()) {
- p->setException(t.getException());
+ p->setException(std::move(t.exception()));
} else {
try {
auto f2 = (*funcm)(std::move(t.value()));
f2.setCallback_([p](Try<B>&& b) mutable {
- p->fulfilTry(std::move(b));
- });
+ p->fulfilTry(std::move(b));
+ });
+ } catch (const std::exception& e) {
+ p->setException(exception_wrapper(std::current_exception(), e));
} catch (...) {
- p->setException(std::current_exception());
+ p->setException(exception_wrapper(std::current_exception()));
}
}
});
setCallback_(
[p, funcm](Try<T>&& t) mutable {
if (t.hasException()) {
- p->setException(t.getException());
+ p->setException(t.exception());
} else {
try {
auto f2 = (*funcm)();
f2.setCallback_([p](Try<B>&& b) mutable {
- p->fulfilTry(std::move(b));
- });
+ p->fulfilTry(std::move(b));
+ });
+ } catch (const std::exception& e) {
+ p->setException(exception_wrapper(std::current_exception(), e));
} catch (...) {
- p->setException(std::current_exception());
+ p->setException(exception_wrapper(std::current_exception()));
}
}
});
auto pm = folly::makeMoveWrapper(std::move(p));
auto funcm = folly::makeMoveWrapper(std::move(func));
setCallback_([pm, funcm](Try<T>&& t) mutable {
- try {
- t.throwIfFailed();
- } catch (Exn& e) {
- pm->fulfil([&]{
- return (*funcm)(e);
- });
- return;
- } catch (...) {
- // fall through
+ if (!t.template withException<Exn>([&] (Exn& e) {
+ pm->fulfil([&]{
+ return (*funcm)(e);
+ });
+ })) {
+ pm->fulfilTry(std::move(t));
}
- pm->fulfilTry(std::move(t));
});
return f;
auto pm = folly::makeMoveWrapper(std::move(p));
auto funcm = folly::makeMoveWrapper(std::move(func));
setCallback_([pm, funcm](Try<T>&& t) mutable {
- try {
- t.throwIfFailed();
- } catch (Exn& e) {
- try {
- auto f2 = (*funcm)(e);
- f2.setCallback_([pm](Try<T>&& t2) mutable {
- pm->fulfilTry(std::move(t2));
- });
- } catch (...) {
- pm->setException(std::current_exception());
- }
- return;
- } catch (...) {
- // fall through
+ if (!t.template withException<Exn>([&] (Exn& e) {
+ try {
+ auto f2 = (*funcm)(e);
+ f2.setCallback_([pm](Try<T>&& t2) mutable {
+ pm->fulfilTry(std::move(t2));
+ });
+ } catch (const std::exception& e) {
+ pm->setException(exception_wrapper(std::current_exception(), e));
+ } catch (...) {
+ pm->setException(exception_wrapper(std::current_exception()));
+ }
+ })) {
+ pm->fulfilTry(std::move(t));
}
- pm->fulfilTry(std::move(t));
});
return f;
}
template <class T>
-void Future<T>::raise(std::exception_ptr exception) {
- core_->raise(exception);
+void Future<T>::raise(exception_wrapper exception) {
+ core_->raise(std::move(exception));
}
// makeFuture
return std::move(f);
}
+template <class T>
+Future<T> makeFuture(exception_wrapper ew) {
+ Promise<T> p;
+ p.setException(std::move(ew));
+ return p.getFuture();
+}
+
template <class T, class E>
typename std::enable_if<std::is_base_of<std::exception, E>::value,
Future<T>>::type
makeFuture(E const& e) {
Promise<T> p;
auto f = p.getFuture();
- p.fulfil([&]() -> T { throw e; });
+ p.setException(make_exception_wrapper<E>(e));
return std::move(f);
}
template <class T>
Future<T> makeFuture(Try<T>&& t) {
- try {
+ if (t.hasException()) {
+ return makeFuture<T>(std::move(t.exception()));
+ } else {
return makeFuture<T>(std::move(t.value()));
- } catch (...) {
- return makeFuture<T>(std::current_exception());
}
}
template <>
inline Future<void> makeFuture(Try<void>&& t) {
- try {
- t.throwIfFailed();
+ if (t.hasException()) {
+ return makeFuture<void>(std::move(t.exception()));
+ } else {
return makeFuture();
- } catch (...) {
- return makeFuture<void>(std::current_exception());
}
}
t.value();
p.setException(TimedOut());
} catch (std::exception const& e) {
- p.setException(std::current_exception());
+ p.setException(exception_wrapper(std::current_exception(), e));
+ } catch (...) {
+ p.setException(exception_wrapper(std::current_exception()));
}
baton.post();
}
try {
t.throwIfFailed();
ctx->promise.setException(std::move(ctx->exception));
- } catch (std::exception const&) {
- ctx->promise.setException(std::current_exception());
+ } catch (std::exception const& e) {
+ ctx->promise.setException(
+ exception_wrapper(std::current_exception(), e));
+ } catch (...) {
+ ctx->promise.setException(
+ exception_wrapper(std::current_exception()));
}
}
});
#include <vector>
#include <folly/MoveWrapper.h>
+#include <folly/wangle/futures/Deprecated.h>
#include <folly/wangle/futures/Promise.h>
#include <folly/wangle/futures/Try.h>
#include <folly/wangle/futures/WangleException.h>
template <class E>
void raise(E&& exception) {
- raise(std::make_exception_ptr(std::forward<E>(exception)));
+ raise(make_exception_wrapper<typename std::remove_reference<E>::type>(
+ std::move(exception)));
}
/// Raise an interrupt. If the promise holder has an interrupt
/// preventing the asynchronous operation (if in time), and the promise
/// holder setting an exception on the future. (That may happen
/// asynchronously, of course.)
- void raise(std::exception_ptr interrupt);
+ void raise(exception_wrapper interrupt);
void cancel() {
raise(FutureCancellation());
///
/// auto f = makeFuture<string>(std::current_exception());
template <class T>
-Future<T> makeFuture(std::exception_ptr const& e);
+Future<T> makeFuture(std::exception_ptr const& e) DEPRECATED;
+
+/// Make a failed Future from an exception_wrapper.
+template <class T>
+Future<T> makeFuture(exception_wrapper ew);
/** Make a Future from an exception type E that can be passed to
std::make_exception_ptr(). */
template <class T>
template <class E>
-void Promise<T>::setException(E const& e) {
- setException(std::make_exception_ptr<E>(e));
+typename std::enable_if<std::is_base_of<std::exception, E>::value>::type
+Promise<T>::setException(E const& e) {
+ setException(make_exception_wrapper<E>(e));
}
template <class T>
void Promise<T>::setException(std::exception_ptr const& e) {
+ try {
+ std::rethrow_exception(e);
+ } catch (const std::exception& e) {
+ setException(exception_wrapper(std::current_exception(), e));
+ } catch (...) {
+ setException(exception_wrapper(std::current_exception()));
+ }
+}
+
+template <class T>
+void Promise<T>::setException(exception_wrapper ew) {
throwIfFulfilled();
- core_->setResult(Try<T>(e));
+ core_->setResult(Try<T>(std::move(ew)));
}
template <class T>
void Promise<T>::setInterruptHandler(
- std::function<void(std::exception_ptr const&)> fn) {
+ std::function<void(exception_wrapper const&)> fn) {
core_->setInterruptHandler(std::move(fn));
}
#pragma once
+#include <folly/wangle/futures/Deprecated.h>
#include <folly/wangle/futures/Try.h>
#include <functional>
once, thereafter Future already retrieved exception will be raised. */
Future<T> getFuture();
+ /** Fulfil the Promise with an exception_wrapper */
+ void setException(exception_wrapper ew);
+
/** Fulfil the Promise with an exception_ptr, e.g.
try {
...
p.setException(std::current_exception());
}
*/
- void setException(std::exception_ptr const&);
+ void setException(std::exception_ptr const&) DEPRECATED;
/** Fulfil the Promise with an exception type E, which can be passed to
std::make_exception_ptr(). Useful for originating exceptions. If you
- caught an exception the exception_ptr form is more appropriate.
+ caught an exception the exception_wrapper form is more appropriate.
*/
- template <class E> void setException(E const&);
+ template <class E>
+ typename std::enable_if<std::is_base_of<std::exception, E>::value>::type
+ setException(E const&);
/// Set an interrupt handler to handle interrupts. See the documentation for
/// Future::raise(). Your handler can do whatever it wants, but if you
/// bother to set one then you probably will want to fulfil the promise with
/// an exception (or special value) indicating how the interrupt was
/// handled.
- void setInterruptHandler(std::function<void(std::exception_ptr const&)>);
+ void setInterruptHandler(std::function<void(exception_wrapper const&)>);
/** Fulfil this Promise (only for Promise<void>) */
void setValue();
if (contains_ == Contains::VALUE) {
new (&value_)T(std::move(t.value_));
} else if (contains_ == Contains::EXCEPTION) {
- new (&e_)std::exception_ptr(t.e_);
+ new (&e_)std::unique_ptr<exception_wrapper>(std::move(t.e_));
}
}
if (contains_ == Contains::VALUE) {
new (&value_)T(std::move(t.value_));
} else if (contains_ == Contains::EXCEPTION) {
- new (&e_)std::exception_ptr(t.e_);
+ new (&e_)std::unique_ptr<exception_wrapper>(std::move(t.e_));
}
return *this;
}
if (contains_ == Contains::VALUE) {
value_.~T();
} else if (contains_ == Contains::EXCEPTION) {
- e_.~exception_ptr();
+ e_.~unique_ptr<exception_wrapper>();
}
}
void Try<T>::throwIfFailed() const {
if (contains_ != Contains::VALUE) {
if (contains_ == Contains::EXCEPTION) {
- std::rethrow_exception(e_);
+ e_->throwException();
} else {
throw UsingUninitializedTry();
}
void Try<void>::throwIfFailed() const {
if (!hasValue_) {
- std::rethrow_exception(e_);
+ e_->throwException();
}
}
makeTryFunction(F&& f) {
typedef typename std::result_of<F()>::type ResultType;
try {
- auto value = f();
- return Try<ResultType>(std::move(value));
+ return Try<ResultType>(f());
+ } catch (std::exception& e) {
+ return Try<ResultType>(exception_wrapper(std::current_exception(), e));
} catch (...) {
- return Try<ResultType>(std::current_exception());
+ return Try<ResultType>(exception_wrapper(std::current_exception()));
}
}
try {
f();
return Try<void>();
+ } catch (std::exception& e) {
+ return Try<void>(exception_wrapper(std::current_exception(), e));
} catch (...) {
- return Try<void>(std::current_exception());
+ return Try<void>(exception_wrapper(std::current_exception()));
}
}
#include <type_traits>
#include <exception>
#include <algorithm>
+#include <folly/ExceptionWrapper.h>
#include <folly/Likely.h>
+#include <folly/Memory.h>
+#include <folly/wangle/futures/Deprecated.h>
#include <folly/wangle/futures/WangleException.h>
namespace folly { namespace wangle {
Try() : contains_(Contains::NOTHING) {}
explicit Try(const T& v) : contains_(Contains::VALUE), value_(v) {}
explicit Try(T&& v) : contains_(Contains::VALUE), value_(std::move(v)) {}
- explicit Try(std::exception_ptr e) : contains_(Contains::EXCEPTION), e_(e) {}
+ explicit Try(exception_wrapper e)
+ : contains_(Contains::EXCEPTION),
+ e_(folly::make_unique<exception_wrapper>(std::move(e))) {}
+ explicit Try(std::exception_ptr ep) DEPRECATED
+ : contains_(Contains::EXCEPTION) {
+ try {
+ std::rethrow_exception(ep);
+ } catch (const std::exception& e) {
+ e_ = folly::make_unique<exception_wrapper>(std::current_exception(), e);
+ } catch (...) {
+ e_ = folly::make_unique<exception_wrapper>(std::current_exception());
+ }
+ }
// move
Try(Try<T>&& t);
bool hasValue() const { return contains_ == Contains::VALUE; }
bool hasException() const { return contains_ == Contains::EXCEPTION; }
- std::exception_ptr getException() const {
+ template <class Ex>
+ bool hasException() const {
+ return hasException() && e_->is_compatible_with<Ex>();
+ }
+
+ exception_wrapper& exception() {
if (UNLIKELY(!hasException())) {
- throw WangleException(
- "getException(): Try does not contain an exception");
+ throw WangleException("exception(): Try does not contain an exception");
}
- return e_;
+ return *e_;
+ }
+
+ template <class Ex, class F>
+ bool withException(F func) const {
+ if (!hasException()) {
+ return false;
+ }
+ return e_->with_exception<Ex>(std::move(func));
}
private:
Contains contains_;
union {
T value_;
- std::exception_ptr e_;
+ std::unique_ptr<exception_wrapper> e_;
};
};
class Try<void> {
public:
Try() : hasValue_(true) {}
- explicit Try(std::exception_ptr e) : hasValue_(false), e_(e) {}
+ explicit Try(exception_wrapper e)
+ : hasValue_(false),
+ e_(folly::make_unique<exception_wrapper>(std::move(e))) {}
+ explicit Try(std::exception_ptr ep) DEPRECATED : hasValue_(false) {
+ try {
+ std::rethrow_exception(ep);
+ } catch (const std::exception& e) {
+ e_ = folly::make_unique<exception_wrapper>(std::current_exception(), e);
+ } catch (...) {
+ e_ = folly::make_unique<exception_wrapper>(std::current_exception());
+ }
+ }
+
+ Try& operator=(const Try<void>& t) {
+ hasValue_ = t.hasValue_;
+ if (t.e_) {
+ e_ = folly::make_unique<exception_wrapper>(*t.e_);
+ }
+ return *this;
+ }
+ Try(const Try<void>& t) {
+ *this = t;
+ }
void value() const { throwIfFailed(); }
void operator*() const { return value(); }
bool hasValue() const { return hasValue_; }
bool hasException() const { return !hasValue_; }
- std::exception_ptr getException() const {
+ template <class Ex>
+ bool hasException() const {
+ return hasException() && e_->is_compatible_with<Ex>();
+ }
+
+ exception_wrapper& exception() {
if (UNLIKELY(!hasException())) {
- throw WangleException(
- "getException(): Try does not contain an exception");
+ throw WangleException("exception(): Try does not contain an exception");
+ }
+ return *e_;
+ }
+
+ template <class Ex, class F>
+ bool withException(F func) const {
+ if (!hasException()) {
+ return false;
}
- return e_;
+ return e_->with_exception<Ex>(std::move(func));
}
private:
bool hasValue_;
- std::exception_ptr e_;
+ std::unique_ptr<exception_wrapper> e_{nullptr};
};
/**
// Called by a destructing Promise
void detachPromise() {
if (!ready()) {
- setResult(Try<T>(std::make_exception_ptr(BrokenPromise())));
+ setResult(Try<T>(exception_wrapper(BrokenPromise())));
}
detachOne();
}
executor_ = x;
}
- void raise(std::exception_ptr const& e) {
+ void raise(exception_wrapper const& e) {
FSM_START
case State::Interruptible:
FSM_UPDATE2(State::Interrupted,
- [&]{ interrupt_ = e; },
- [&]{ interruptHandler_(interrupt_); });
+ [&]{ interrupt_ = folly::make_unique<exception_wrapper>(e); },
+ [&]{ interruptHandler_(*interrupt_); });
break;
case State::Waiting:
case State::Interrupted:
FSM_UPDATE(State::Interrupted,
- [&]{ interrupt_ = e; });
+ [&]{ interrupt_ = folly::make_unique<exception_wrapper>(e); });
break;
case State::Done:
FSM_END
}
- void setInterruptHandler(std::function<void(std::exception_ptr const&)> fn) {
+ void setInterruptHandler(std::function<void(exception_wrapper const&)> fn) {
FSM_START
case State::Waiting:
case State::Interruptible:
break;
case State::Interrupted:
- fn(interrupt_);
+ fn(*interrupt_);
FSM_BREAK
case State::Done:
std::atomic<unsigned char> detached_ {0};
std::atomic<bool> active_ {true};
std::atomic<Executor*> executor_ {nullptr};
- std::exception_ptr interrupt_;
- std::function<void(std::exception_ptr const&)> interruptHandler_;
+ std::unique_ptr<exception_wrapper> interrupt_;
+ std::function<void(exception_wrapper const&)> interruptHandler_;
};
template <typename... Ts>
producer.join();
}
+BENCHMARK_DRAW_LINE();
+
+// The old way. Throw an exception, and rethrow to access it upstream.
+void throwAndCatchImpl() {
+ makeFuture()
+ .then([](Try<void>&&){ throw std::runtime_error("oh no"); })
+ .then([](Try<void>&& t) {
+ try {
+ t.value();
+ } catch(const std::runtime_error& e) {
+ // ...
+ return;
+ }
+ CHECK(false);
+ });
+}
+
+// Not much better. Throw an exception, and access it via the wrapper upstream.
+// Actually a little worse due to wrapper overhead. then() won't know that the
+// exception is a runtime_error, so will have to store it as an exception_ptr
+// anyways. withException will therefore have to rethrow. Note that if we threw
+// std::exception instead, we would see some wins, as that's the type then()
+// will try to wrap, so no exception_ptrs/rethrows are necessary.
+void throwAndCatchWrappedImpl() {
+ makeFuture()
+ .then([](Try<void>&&){ throw std::runtime_error("oh no"); })
+ .then([](Try<void>&& t) {
+ auto caught = t.withException<std::runtime_error>(
+ [](const std::runtime_error& e){
+ // ...
+ });
+ CHECK(caught);
+ });
+}
+
+// Better. Wrap an exception, and rethrow to access it upstream.
+void throwWrappedAndCatchImpl() {
+ makeFuture()
+ .then([](Try<void>&&){
+ return makeFuture<void>(std::runtime_error("oh no"));
+ })
+ .then([](Try<void>&& t) {
+ try {
+ t.value();
+ } catch(const std::runtime_error& e) {
+ // ...
+ return;
+ }
+ CHECK(false);
+ });
+}
+
+// The new way. Wrap an exception, and access it via the wrapper upstream
+void throwWrappedAndCatchWrappedImpl() {
+ makeFuture()
+ .then([](Try<void>&&){
+ return makeFuture<void>(std::runtime_error("oh no"));
+ })
+ .then([](Try<void>&& t){
+ auto caught = t.withException<std::runtime_error>(
+ [](const std::runtime_error& e){
+ // ...
+ });
+ CHECK(caught);
+ });
+}
+
+// Simulate heavy contention on func
+void contend(void(*func)()) {
+ folly::BenchmarkSuspender s;
+ const int N = 100;
+ const int iters = 1000;
+ pthread_barrier_t barrier;
+ pthread_barrier_init(&barrier, nullptr, N+1);
+ std::vector<std::thread> threads;
+ for (int i = 0; i < N; i++) {
+ threads.push_back(std::thread([&](){
+ pthread_barrier_wait(&barrier);
+ for (int j = 0; j < iters; j++) {
+ func();
+ }
+ }));
+ }
+ pthread_barrier_wait(&barrier);
+ s.dismiss();
+ for (auto& t : threads) {
+ t.join();
+ }
+ s.rehire();
+ pthread_barrier_destroy(&barrier);
+}
+
+BENCHMARK(throwAndCatch) {
+ throwAndCatchImpl();
+}
+
+BENCHMARK_RELATIVE(throwAndCatchWrapped) {
+ throwAndCatchWrappedImpl();
+}
+
+BENCHMARK_RELATIVE(throwWrappedAndCatch) {
+ throwWrappedAndCatchImpl();
+}
+
+BENCHMARK_RELATIVE(throwWrappedAndCatchWrapped) {
+ throwWrappedAndCatchWrappedImpl();
+}
+
+BENCHMARK_DRAW_LINE();
+
+BENCHMARK(throwAndCatchContended) {
+ contend(throwAndCatchImpl);
+}
+
+BENCHMARK_RELATIVE(throwAndCatchWrappedContended) {
+ contend(throwAndCatchWrappedImpl);
+}
+
+BENCHMARK_RELATIVE(throwWrappedAndCatchContended) {
+ contend(throwWrappedAndCatchImpl);
+}
+
+BENCHMARK_RELATIVE(throwWrappedAndCatchWrappedContended) {
+ contend(throwWrappedAndCatchWrappedImpl);
+}
+
int main(int argc, char** argv) {
gflags::ParseCommandLineFlags(&argc, &argv, true);
folly::runBenchmarks();
try {
throw eggs;
} catch (...) {
- p.setException(std::current_exception());
+ p.setException(exception_wrapper(std::current_exception()));
}
EXPECT_THROW(f.value(), eggs_t);
}
#include <folly/wangle/futures/Promise.h>
using namespace folly::wangle;
+using folly::exception_wrapper;
TEST(Interrupts, raise) {
std::runtime_error eggs("eggs");
Promise<void> p;
- p.setInterruptHandler([&](std::exception_ptr e) {
- EXPECT_THROW(std::rethrow_exception(e), decltype(eggs));
+ p.setInterruptHandler([&](const exception_wrapper& e) {
+ EXPECT_THROW(e.throwException(), decltype(eggs));
});
p.getFuture().raise(eggs);
}
TEST(Interrupts, cancel) {
Promise<void> p;
- p.setInterruptHandler([&](std::exception_ptr e) {
- EXPECT_THROW(std::rethrow_exception(e), FutureCancellation);
+ p.setInterruptHandler([&](const exception_wrapper& e) {
+ EXPECT_THROW(e.throwException(), FutureCancellation);
});
p.getFuture().cancel();
}
TEST(Interrupts, handleThenInterrupt) {
Promise<int> p;
bool flag = false;
- p.setInterruptHandler([&](std::exception_ptr e) { flag = true; });
+ p.setInterruptHandler([&](const exception_wrapper& e) { flag = true; });
p.getFuture().cancel();
EXPECT_TRUE(flag);
}
Promise<int> p;
bool flag = false;
p.getFuture().cancel();
- p.setInterruptHandler([&](std::exception_ptr e) { flag = true; });
+ p.setInterruptHandler([&](const exception_wrapper& e) { flag = true; });
EXPECT_TRUE(flag);
}
TEST(Interrupts, interruptAfterFulfilNoop) {
Promise<void> p;
bool flag = false;
- p.setInterruptHandler([&](std::exception_ptr e) { flag = true; });
+ p.setInterruptHandler([&](const exception_wrapper& e) { flag = true; });
p.setValue();
p.getFuture().cancel();
EXPECT_FALSE(flag);
TEST(Interrupts, secondInterruptNoop) {
Promise<void> p;
int count = 0;
- p.setInterruptHandler([&](std::exception_ptr e) { count++; });
+ p.setInterruptHandler([&](const exception_wrapper& e) { count++; });
auto f = p.getFuture();
f.cancel();
f.cancel();
};
auto result = makeTryFunction(func);
- EXPECT_TRUE(result.hasException());
+ EXPECT_TRUE(result.hasException<std::runtime_error>());
}
TEST(Try, makeTryFunctionVoid) {
};
auto result = makeTryFunction(func);
- EXPECT_TRUE(result.hasException());
+ EXPECT_TRUE(result.hasException<std::runtime_error>());
}