From c03f25d8392641f56b58640f2c5ddd3fc31b8961 Mon Sep 17 00:00:00 2001 From: Gabriel Corona Date: Fri, 3 Jun 2016 12:20:56 +0200 Subject: [PATCH 1/1] Use a custom Result value for simgrid::smix::kernel() This avoids allocating a shared state (by std::promise) on the heap for each such call. --- include/simgrid/simix.hpp | 8 +- include/xbt/future.hpp | 166 +++++++++++++++++++++++++++++++++++++- 2 files changed, 166 insertions(+), 8 deletions(-) diff --git a/include/simgrid/simix.hpp b/include/simgrid/simix.hpp index 1122d16cfb..1f88d9730b 100644 --- a/include/simgrid/simix.hpp +++ b/include/simgrid/simix.hpp @@ -41,16 +41,16 @@ typename std::result_of::type kernel(F&& code) if (SIMIX_is_maestro()) return std::forward(code)(); - // If we are in the application, pass the code to the maestro which is + // If we are in the application, pass the code to the maestro which // executes it for us and reports the result. We use a std::future which // conveniently handles the success/failure value for us. typedef typename std::result_of::type R; - std::promise promise; + simgrid::xbt::Result result; simcall_run_kernel([&]{ xbt_assert(SIMIX_is_maestro(), "Not in maestro"); - simgrid::xbt::fulfillPromise(promise, std::forward(code)); + simgrid::xbt::fulfillPromise(result, std::forward(code)); }); - return promise.get_future().get(); + return result.get(); } class Context; diff --git a/include/xbt/future.hpp b/include/xbt/future.hpp index d5a31a55ce..7fdfb0b807 100644 --- a/include/xbt/future.hpp +++ b/include/xbt/future.hpp @@ -7,16 +7,173 @@ #ifndef XBT_FUTURE_HPP #define XBT_FUTURE_HPP -#include +#include + #include #include namespace simgrid { namespace xbt { +/** A value or an exception + * + * The API is similar to the one of future and promise. + **/ +template +class Result { + enum class ResultStatus { + invalid, + value, + exception, + }; +public: + Result() {} + ~Result() { this->reset(); } + + // Copy (if T is copyable) and move: + Result(Result const& that) + { + (*this) = that; + } + Result& operator=(Result const& that) + { + this->reset(); + switch (that.status_) { + case ResultStatus::invalid: + break; + case ResultStatus::valid: + new (&value_) T(that.value); + break; + case ResultStatus::exception: + new (&exception_) T(that.exception); + break; + } + return *this; + } + Result(Result&& that) + { + *this = std::move(that); + } + Result& operator=(Result&& that) + { + this->reset(); + switch (that.status_) { + case ResultStatus::invalid: + break; + case ResultStatus::valid: + new (&value_) T(std::move(that.value)); + that.value.~T(); + break; + case ResultStatus::exception: + new (&exception_) T(std::move(that.exception)); + that.exception.~exception_ptr(); + break; + } + that.status_ = ResultStatus::invalid; + return *this; + } + + bool is_valid() + { + return status_ != ResultStatus::invalid; + } + void reset() + { + switch(status_) { + case ResultStatus::invalid: + break; + case ResultStatus::value: + value_.~T(); + break; + case ResultStatus::exception: + exception_.~exception_ptr(); + break; + } + status_ = ResultStatus::invalid; + } + void set_exception(std::exception_ptr e) + { + this->reset(); + new (&exception_) std::exception_ptr(std::move(e)); + status_ = ResultStatus::exception; + } + void set_value(T&& value) + { + this->reset(); + new (&value_) T(std::move(value)); + status_ = ResultStatus::value; + } + void set_value(T const& value) + { + this->reset(); + new (&value_) T(value); + status_ = ResultStatus::value; + } + + /** Extract the value from the future + * + * After this the value is invalid. + **/ + T get() + { + switch(status_) { + case ResultStatus::invalid: + throw std::logic_error("Invalid result"); + case ResultStatus::value: { + T value = std::move(value_); + value_.~T(); + status_ = ResultStatus::invalid; + return std::move(value); + } + case ResultStatus::exception: { + std::exception_ptr exception = std::move(exception_); + exception_.~exception_ptr(); + status_ = ResultStatus::invalid; + std::rethrow_exception(std::move(exception)); + break; + } + } + } +private: + ResultStatus status_ = ResultStatus::invalid; + union { + T value_; + std::exception_ptr exception_; + }; +}; + +template<> +class Result : public Result +{ +public: + void set_value() + { + Result::set_value(nullptr); + } + void get() + { + Result::get(); + } +}; + +template +class Result : public Result> +{ +public: + void set_value(T& value) + { + Result>::set_value(std::ref(value)); + } + T& get() + { + return Result>::get(); + } +}; + /** Fulfill a promise by executing a given code */ template -void fulfillPromise(std::promise& promise, F code) +auto fulfillPromise(R& promise, F&& code) +-> decltype(promise.set_value(code())) { try { promise.set_value(code()); @@ -31,8 +188,9 @@ void fulfillPromise(std::promise& promise, F code) * This is a special version for `std::promise` because the default * version does not compile in this case. */ -template -void fulfillPromise(std::promise& promise, F code) +template +auto fulfillPromise(P& promise, F&& code) +-> decltype(promise.set_value()) { try { (code)(); -- 2.20.1