You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@celix.apache.org by oi...@apache.org on 2020/08/17 20:48:08 UTC
[celix] branch master updated: Feature/cpp17 promise (#272)
This is an automated email from the ASF dual-hosted git repository.
oipo pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/celix.git
The following commit(s) were added to refs/heads/master by this push:
new efee9f2 Feature/cpp17 promise (#272)
efee9f2 is described below
commit efee9f2d16413de73fa094cbe29dd29d65840a35
Author: Michael de Lang <ki...@gmail.com>
AuthorDate: Mon Aug 17 22:47:58 2020 +0200
Feature/cpp17 promise (#272)
Use C++17 as much as possible for promises
---
CMakeLists.txt | 8 +-
misc/experimental/promise/CMakeLists.txt | 6 +-
misc/experimental/promise/api/celix/Deferred.h | 25 +-
misc/experimental/promise/api/celix/Promise.h | 77 ++--
.../promise/api/celix/PromiseFactory.h | 10 +-
.../promise/api/celix/PromiseInvocationException.h | 6 +-
.../promise/api/celix/impl/SharedPromiseState.h | 418 +++++++++++----------
.../promise/gtest/src/PromiseTestSuite.cc | 114 +++++-
.../promise/gtest/src/VoidPromiseTestSuite.cc | 15 +-
misc/experimental/promise/src/PromiseExamples.cc | 2 +-
10 files changed, 423 insertions(+), 258 deletions(-)
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 9ede833..75fd9bd 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -69,8 +69,12 @@ if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
endif()
if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
- set(CMAKE_C_FLAGS "-Wno-unused-result -Wno-format-truncation -Wno-stringop-truncation -Wno-stringop-overflow ${CMAKE_C_FLAGS}")
- set(CMAKE_CXX_FLAGS "-Wno-unused-result -Wno-format-truncation -Wno-stringop-truncation -Wno-stringop-overflow ${CMAKE_CXX_FLAGS}")
+ set(CMAKE_C_FLAGS "-Wno-unused-result -Wno-format-truncation -Wno-stringop-overflow ${CMAKE_C_FLAGS}")
+ set(CMAKE_CXX_FLAGS "-Wno-unused-result -Wno-format-truncation -Wno-stringop-overflow ${CMAKE_CXX_FLAGS}")
+ if(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 8.0)
+ set(CMAKE_C_FLAGS "-Wno-stringop-truncation ${CMAKE_C_FLAGS}")
+ set(CMAKE_CXX_FLAGS "-Wno-stringop-truncation ${CMAKE_CXX_FLAGS}")
+ endif()
endif()
if (ENABLE_MORE_WARNINGS)
diff --git a/misc/experimental/promise/CMakeLists.txt b/misc/experimental/promise/CMakeLists.txt
index c4ccac9..2d547a4 100644
--- a/misc/experimental/promise/CMakeLists.txt
+++ b/misc/experimental/promise/CMakeLists.txt
@@ -28,7 +28,7 @@ if (NOT COMMAND celix_subproject)
include(GNUInstallDirs)
- set(CMAKE_CXX_FLAGS "-std=c++11 ${CMAKE_CXX_FLAGS}")
+ set(CMAKE_CXX_FLAGS "-std=c++17 ${CMAKE_CXX_FLAGS}")
set(CMAKE_CXX_FLAGS_DEBUG "-g -DDEBUG ${CMAKE_CXX_FLAGS_DEBUG}")
set(PROMISE_STANDALONE ON)
@@ -52,7 +52,7 @@ if (PROMISE OR PROMISE_STANDALONE)
$<INSTALL_INTERFACE:include/celix/promise>
)
target_link_libraries(Promise INTERFACE TBB::tbb Threads::Threads)
- target_compile_options(Promise INTERFACE -frtti) #Note needed for TBB
+ target_compile_options(Promise INTERFACE -frtti -std=c++17) #Note -frtti needed for TBB
add_library(Celix::Promise ALIAS Promise)
add_executable(PromiseExamples src/PromiseExamples.cc)
@@ -73,4 +73,4 @@ if (PROMISE OR PROMISE_STANDALONE)
DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/CelixPromise" COMPONENT cmake)
endif ()
-endif ()
\ No newline at end of file
+endif ()
diff --git a/misc/experimental/promise/api/celix/Deferred.h b/misc/experimental/promise/api/celix/Deferred.h
index 85ef15c..711620d 100644
--- a/misc/experimental/promise/api/celix/Deferred.h
+++ b/misc/experimental/promise/api/celix/Deferred.h
@@ -27,8 +27,18 @@
#include <tbb/task.h>
#include <tbb/task_group.h>
-#include <tbb/task_scheduler_init.h>
#include <tbb/task_scheduler_observer.h>
+#if __has_include(<tbb/global_control.h>)
+
+#if TBB_INTERFACE_VERSION_MAJOR < 12
+#define TBB_PREVIEW_GLOBAL_CONTROL 1
+#endif
+
+#include <tbb/global_control.h>
+#else
+// deprecated in newer versions of TBB
+#include <tbb/task_scheduler_init.h>
+#endif
namespace celix {
@@ -96,7 +106,7 @@ namespace celix {
*
* @return The Promise associated with this Deferred.
*/
- Promise<T> getPromise();
+ [[nodiscard]] Promise<T> getPromise();
/**
* Successfully resolve the Promise associated with this Deferred.
@@ -188,7 +198,7 @@ namespace celix {
*
* @return The Promise associated with this Deferred.
*/
- Promise<void> getPromise();
+ [[nodiscard]] Promise<void> getPromise();
/**
* Successfully resolve the Promise associated with this Deferred.
@@ -257,11 +267,9 @@ inline celix::Promise<void> celix::Deferred<void>::getPromise() {
template<typename T>
template<typename U>
inline void celix::Deferred<T>::resolveWith(celix::Promise<U> with) {
- auto s = state;
- with.onResolve([s, with]{
+ with.onResolve([s = state, with] () mutable {
if (with.isSuccessfullyResolved()) {
- U val = with.getValue();
- s->resolve(std::forward<T>(val));
+ s->resolve(with.moveOrGetValue());
} else {
s->fail(with.getFailure());
}
@@ -269,8 +277,7 @@ inline void celix::Deferred<T>::resolveWith(celix::Promise<U> with) {
}
inline void celix::Deferred<void>::resolveWith(celix::Promise<void> with) {
- auto s = state;
- with.onResolve([s, with]{
+ with.onResolve([s = state, with]{
if (with.isSuccessfullyResolved()) {
with.getValue();
s->resolve();
diff --git a/misc/experimental/promise/api/celix/Promise.h b/misc/experimental/promise/api/celix/Promise.h
index 8b58f26..dd8a501 100644
--- a/misc/experimental/promise/api/celix/Promise.h
+++ b/misc/experimental/promise/api/celix/Promise.h
@@ -71,7 +71,7 @@ namespace celix {
* @return {@code true} if this Promise was resolved either successfully or
* with a failure; {@code false} if this Promise is unresolved.
*/
- bool isDone() const;
+ [[nodiscard]] bool isDone() const;
//
/**
@@ -82,7 +82,7 @@ namespace celix {
* @return {@code true} if this Promise was resolved successfully.
* {@code false} if this Promise is unresolved or resolved with a failure.
*/
- bool isSuccessfullyResolved() const;
+ [[nodiscard]] bool isSuccessfullyResolved() const;
/**
* Returns the failure of this Promise.
@@ -101,7 +101,7 @@ namespace celix {
* @throws InterruptedException If the current thread was interrupted while
* waiting.
*/
- std::exception_ptr getFailure() const;
+ [[nodiscard]] std::exception_ptr getFailure() const;
/**
* Returns the value of this Promise.
@@ -123,10 +123,10 @@ namespace celix {
* @throws InterruptedException If the current thread was interrupted while
* waiting.
*/
+ T& getValue();
const T& getValue() const;
- //TODO is a move value needed? Howto handle this with resolve callbacks
- T moveValue();
+ [[nodiscard]] T moveOrGetValue();
/**
* Wait till the promise is resolved.
@@ -226,7 +226,7 @@ namespace celix {
* value to be used to resolve the returned Promise. Must not be null.
* @return A Promise that resolves with the value of this Promise or recovers from the failure of this Promise.
*/
- Promise<T> recover(std::function<T()> recover);
+ [[nodiscard]] Promise<T> recover(std::function<T()> recover);
/**
@@ -241,7 +241,7 @@ namespace celix {
* @param the consumer callback
* @returns A new Promise which is chained to this Promise. The returned Promise must be resolved when this Promise is resolved after the specified Consumer is executed.
*/
- Promise<T> thenAccept(std::function<void(T)> consumer);
+ [[nodiscard]] Promise<T> thenAccept(std::function<void(T)> consumer);
/**
* Fall back to the value of the specified Promise if this Promise fails.
@@ -258,7 +258,7 @@ namespace celix {
* with a failure. Must not be null.
* @return A Promise that returns the value of this Promise or falls back to the value of the specified Promise.
*/
- Promise<T> fallbackTo(celix::Promise<T> fallback);
+ [[nodiscard]] Promise<T> fallbackTo(celix::Promise<T> fallback);
/**
* Map the value of this Promise.
@@ -277,7 +277,7 @@ namespace celix {
* @return A Promise that returns the value of this Promise as mapped by the specified Function.
*/
template<typename R>
- celix::Promise<R> map(std::function<R(T)> mapper);
+ [[nodiscard]] celix::Promise<R> map(std::function<R(T)> mapper);
/**
* Filter the value of this Promise.
@@ -294,7 +294,7 @@ namespace celix {
* @param predicate The Predicate to evaluate the value of this Promise.
* @return A Promise that filters the value of this Promise.
*/
- Promise<T> filter(std::function<bool(T)> predicate);
+ [[nodiscard]] Promise<T> filter(std::function<bool(T)> predicate);
/**
* Time out the resolution of this Promise.
@@ -312,7 +312,7 @@ namespace celix {
* or the specified timeout is reached.
*/
template<typename Rep, typename Period>
- Promise<T> timeout(std::chrono::duration<Rep, Period> duration);
+ [[nodiscard]] Promise<T> timeout(std::chrono::duration<Rep, Period> duration);
/**
* Delay after the resolution of this Promise.
@@ -326,7 +326,7 @@ namespace celix {
* is resolved and the specified delay has elapsed.
*/
template<typename Rep, typename Period>
- Promise<T> delay(std::chrono::duration<Rep, Period> duration);
+ [[nodiscard]] Promise<T> delay(std::chrono::duration<Rep, Period> duration);
/**
* FlatMap the value of this Promise.
@@ -346,7 +346,7 @@ namespace celix {
*/
// TODO
// template<typename R>
-// celix::Promise<R> flatMap(std::function<celix::Promise<R>(T)> mapper);
+// [[nodiscard]] celix::Promise<R> flatMap(std::function<celix::Promise<R>(T)> mapper);
/**
* Chain a new Promise to this Promise with success and failure callbacks.
@@ -381,7 +381,21 @@ namespace celix {
* is resolved after the specified Success or Failure callback, if any, is executed
*/
template<typename U>
- celix::Promise<U> then(std::function<celix::Promise<U>(celix::Promise<T>)> success, std::function<void(celix::Promise<T>)> failure = {});
+ [[nodiscard]] celix::Promise<U> then(std::function<celix::Promise<U>(celix::Promise<T>)> success, std::function<void(celix::Promise<T>)> failure = {});
+
+ /**
+ * Convenience operator calling getValue()
+ */
+ constexpr const T&
+ operator*() const
+ { return this->getValue(); }
+
+ /**
+ * Convenience operator calling getValue()
+ */
+ constexpr T&
+ operator*()
+ { return this->getValue(); }
private:
const std::shared_ptr<celix::impl::SharedPromiseState<T>> state;
@@ -395,11 +409,11 @@ namespace celix {
explicit Promise(std::shared_ptr<celix::impl::SharedPromiseState<void>> s);
- bool isDone() const;
+ [[nodiscard]] bool isDone() const;
- bool isSuccessfullyResolved() const;
+ [[nodiscard]] bool isSuccessfullyResolved() const;
- std::exception_ptr getFailure() const;
+ [[nodiscard]] std::exception_ptr getFailure() const;
bool getValue() const; // NOLINT(modernize-use-nodiscard)
@@ -411,23 +425,23 @@ namespace celix {
Promise<void>& onResolve(std::function<void()> callback);
- Promise<void> recover(std::function<void()> recover);
+ [[nodiscard]] Promise<void> recover(std::function<void()> recover);
- Promise<void> thenAccept(std::function<void()> consumer);
+ [[nodiscard]] Promise<void> thenAccept(std::function<void()> consumer);
- Promise<void> fallbackTo(celix::Promise<void> fallback);
+ [[nodiscard]] Promise<void> fallbackTo(celix::Promise<void> fallback);
template<typename R>
- celix::Promise<R> map(std::function<R()> mapper);
+ [[nodiscard]] celix::Promise<R> map(std::function<R()> mapper);
template<typename Rep, typename Period>
- Promise<void> timeout(std::chrono::duration<Rep, Period> duration);
+ [[nodiscard]] Promise<void> timeout(std::chrono::duration<Rep, Period> duration);
template<typename Rep, typename Period>
- Promise<void> delay(std::chrono::duration<Rep, Period> duration);
+ [[nodiscard]] Promise<void> delay(std::chrono::duration<Rep, Period> duration);
template<typename U>
- celix::Promise<U> then(std::function<celix::Promise<U>(celix::Promise<void>)> success, std::function<void(celix::Promise<void>)> failure = {});
+ [[nodiscard]] celix::Promise<U> then(std::function<celix::Promise<U>(celix::Promise<void>)> success, std::function<void(celix::Promise<void>)> failure = {});
private:
const std::shared_ptr<celix::impl::SharedPromiseState<void>> state;
};
@@ -446,6 +460,11 @@ inline celix::Promise<void>::Promise(std::shared_ptr<celix::impl::SharedPromiseS
}
template<typename T>
+inline T& celix::Promise<T>::getValue() {
+ return state->getValue();
+}
+
+template<typename T>
inline const T& celix::Promise<T>::getValue() const {
return state->getValue();
}
@@ -455,8 +474,8 @@ inline bool celix::Promise<void>::getValue() const {
}
template<typename T>
-inline T celix::Promise<T>::moveValue() {
- return state->moveValue();
+inline T celix::Promise<T>::moveOrGetValue() {
+ return state->moveOrGetValue();
}
template<typename T>
@@ -597,10 +616,9 @@ inline void celix::Promise<void>::wait() const {
template<typename T>
template<typename U>
inline celix::Promise<U> celix::Promise<T>::then(std::function<celix::Promise<U>(celix::Promise<T>)> success, std::function<void(celix::Promise<T>)> failure) {
- auto s = state;
auto p = std::make_shared<celix::impl::SharedPromiseState<U>>(state->getExecutor());
- auto chain = [s, p, success, failure]() {
+ auto chain = [s = state, p, success = std::move(success), failure = std::move(failure)]() {
//chain is called when s is resolved
if (s->isSuccessfullyResolved()) {
try {
@@ -623,10 +641,9 @@ inline celix::Promise<U> celix::Promise<T>::then(std::function<celix::Promise<U>
template<typename U>
inline celix::Promise<U> celix::Promise<void>::then(std::function<celix::Promise<U>(celix::Promise<void>)> success, std::function<void(celix::Promise<void>)> failure) {
- auto s = state;
auto p = std::make_shared<celix::impl::SharedPromiseState<U>>(state->getExecutor());
- auto chain = [s, p, success, failure]() {
+ auto chain = [s = state, p, success = std::move(success), failure = std::move(failure)]() {
//chain is called when s is resolved
if (s->isSuccessfullyResolved()) {
try {
diff --git a/misc/experimental/promise/api/celix/PromiseFactory.h b/misc/experimental/promise/api/celix/PromiseFactory.h
index a9b81a0..23a23b2 100644
--- a/misc/experimental/promise/api/celix/PromiseFactory.h
+++ b/misc/experimental/promise/api/celix/PromiseFactory.h
@@ -30,18 +30,18 @@ namespace celix {
//TODO ctor with callbackExecutor and scheduledExecutor
template<typename T>
- celix::Deferred<T> deferred();
+ [[nodiscard]] celix::Deferred<T> deferred();
template<typename T>
- celix::Promise<T> failed(const std::exception& e);
+ [[nodiscard]] celix::Promise<T> failed(const std::exception& e);
template<typename T>
- celix::Promise<T> failed(std::exception_ptr ptr);
+ [[nodiscard]] celix::Promise<T> failed(std::exception_ptr ptr);
template<typename T>
- celix::Promise<T> resolved(T&& value);
+ [[nodiscard]] celix::Promise<T> resolved(T&& value);
- celix::Promise<void> resolved();
+ [[nodiscard]] celix::Promise<void> resolved();
//TODO rest
private:
diff --git a/misc/experimental/promise/api/celix/PromiseInvocationException.h b/misc/experimental/promise/api/celix/PromiseInvocationException.h
index b529224..76aa350 100644
--- a/misc/experimental/promise/api/celix/PromiseInvocationException.h
+++ b/misc/experimental/promise/api/celix/PromiseInvocationException.h
@@ -20,6 +20,7 @@
#pragma once
#include <exception>
+#include <utility>
namespace celix {
@@ -27,6 +28,7 @@ namespace celix {
class PromiseInvocationException : public std::exception {
public:
explicit PromiseInvocationException(const char* what) : w{what} {}
+ explicit PromiseInvocationException(std::string what) : w{std::move(what)} {}
PromiseInvocationException(const PromiseInvocationException&) = delete;
PromiseInvocationException(PromiseInvocationException&&) noexcept = default;
@@ -34,9 +36,9 @@ namespace celix {
PromiseInvocationException& operator=(const PromiseInvocationException&) = delete;
PromiseInvocationException& operator=(PromiseInvocationException&&) noexcept = default;
- const char* what() const noexcept override { return w; }
+ const char* what() const noexcept override { return w.c_str(); }
private:
- const char* w;
+ std::string w;
};
}
diff --git a/misc/experimental/promise/api/celix/impl/SharedPromiseState.h b/misc/experimental/promise/api/celix/impl/SharedPromiseState.h
index 7c6ab1c..6c1dd70 100644
--- a/misc/experimental/promise/api/celix/impl/SharedPromiseState.h
+++ b/misc/experimental/promise/api/celix/impl/SharedPromiseState.h
@@ -26,175 +26,180 @@
#include <utility>
#include <vector>
#include <thread>
+#include <optional>
#include <tbb/task_arena.h>
#include "celix/PromiseInvocationException.h"
#include "celix/PromiseTimeoutException.h"
-namespace celix {
- namespace impl {
+namespace celix::impl {
- template<typename T>
- class SharedPromiseState {
- public:
- typedef typename std::aligned_storage<sizeof(T), std::alignment_of<T>::value>::type DataType;
+ template<typename T>
+ class SharedPromiseState {
+ // Pointers make using promises properly unnecessarily complicated.
+ static_assert(!std::is_pointer_v<T>, "Cannot use pointers with promises.");
+ public:
+ explicit SharedPromiseState(const tbb::task_arena &executor = {});
- explicit SharedPromiseState(const tbb::task_arena &executor = {});
+ ~SharedPromiseState() = default;
- ~SharedPromiseState();
+ void resolve(T&& value);
- void resolve(T &&value);
+ void resolve(const T& value);
- void resolve(const T& value);
+ void fail(std::exception_ptr e);
- void fail(std::exception_ptr p);
+ void fail(const std::exception &e);
- void fail(const std::exception &e);
+ void tryResolve(T &&value);
- void tryResolve(T &&value);
+ void tryFail(std::exception_ptr e);
- void tryFail(std::exception_ptr p);
+ // copy/move depending on situation
+ T& getValue() &;
+ const T& getValue() const &;
+ [[nodiscard]] T&& getValue() &&;
+ [[nodiscard]] const T&& getValue() const &&;
- const T &getValue() const; //copy
- T moveValue(); //move
- std::exception_ptr getFailure() const;
+ // move if T is moveable
+ [[nodiscard]] T moveOrGetValue();
- void wait() const;
+ [[nodiscard]] std::exception_ptr getFailure() const;
- bool isDone() const;
+ void wait() const;
- bool isSuccessfullyResolved() const;
+ [[nodiscard]] bool isDone() const;
- void addOnSuccessConsumeCallback(std::function<void(T)> callback);
+ [[nodiscard]] bool isSuccessfullyResolved() const;
- void addOnFailureConsumeCallback(std::function<void(const std::exception &)> callback);
+ void addOnSuccessConsumeCallback(std::function<void(T)> callback);
- void addOnResolve(std::function<void(bool succeeded, T *val, std::exception_ptr exp)> callback);
+ void addOnFailureConsumeCallback(std::function<void(const std::exception &)> callback);
- template<typename Rep, typename Period>
- std::shared_ptr<SharedPromiseState<T>> delay(std::chrono::duration<Rep, Period> duration);
+ void addOnResolve(std::function<void(std::optional<T> val, std::exception_ptr exp)> callback);
- std::shared_ptr<SharedPromiseState<T>> recover(std::function<T()> recover);
+ template<typename Rep, typename Period>
+ [[nodiscard]] std::shared_ptr<SharedPromiseState<T>> delay(std::chrono::duration<Rep, Period> duration);
- std::shared_ptr<SharedPromiseState<T>> filter(std::function<bool(T)> predicate);
+ [[nodiscard]] std::shared_ptr<SharedPromiseState<T>> recover(std::function<T()> recover);
- std::shared_ptr<SharedPromiseState<T>> fallbackTo(std::shared_ptr<SharedPromiseState<T>> fallbackTo);
+ [[nodiscard]] std::shared_ptr<SharedPromiseState<T>> filter(std::function<bool(const T&)> predicate);
- void resolveWith(std::shared_ptr<SharedPromiseState<T>> with);
+ [[nodiscard]] std::shared_ptr<SharedPromiseState<T>> fallbackTo(std::shared_ptr<SharedPromiseState<T>> fallbackTo);
- template<typename R>
- std::shared_ptr<SharedPromiseState<R>> map(std::function<R(T)> mapper);
+ void resolveWith(std::shared_ptr<SharedPromiseState<T>> with);
- std::shared_ptr<SharedPromiseState<T>> thenAccept(std::function<void(T)> consumer);
+ template<typename R>
+ [[nodiscard]] std::shared_ptr<SharedPromiseState<R>> map(std::function<R(T)> mapper);
- template<typename Rep, typename Period>
- static std::shared_ptr<SharedPromiseState<T>>
- timeout(std::shared_ptr<SharedPromiseState<T>> state, std::chrono::duration<Rep, Period> duration);
+ [[nodiscard]] std::shared_ptr<SharedPromiseState<T>> thenAccept(std::function<void(T)> consumer);
- void addChain(std::function<void()> chainFunction);
+ template<typename Rep, typename Period>
+ [[nodiscard]] static std::shared_ptr<SharedPromiseState<T>>
+ timeout(std::shared_ptr<SharedPromiseState<T>> state, std::chrono::duration<Rep, Period> duration);
- tbb::task_arena getExecutor() const;
- private:
- /**
- * Complete the resolving and call the registered tasks
- * A reference to the possible locked unique_lock.
- */
- void complete(std::unique_lock<std::mutex> &lck);
+ void addChain(std::function<void()> chainFunction);
- /**
- * Wait for data and check if it resolved as expected (expects mutex locked)
- */
- void waitForAndCheckData(std::unique_lock<std::mutex> &lck, bool expectValid) const;
+ tbb::task_arena getExecutor() const;
+ private:
+ /**
+ * Complete the resolving and call the registered tasks
+ * A reference to the possible locked unique_lock.
+ */
+ void complete(std::unique_lock<std::mutex> &lck);
- tbb::task_arena executor; //TODO look into different thread pool libraries
- //TODO add ScheduledExecutorService like object
+ /**
+ * Wait for data and check if it resolved as expected (expects mutex locked)
+ */
+ void waitForAndCheckData(std::unique_lock<std::mutex> &lck, bool expectValid) const;
- mutable std::mutex mutex{}; //protects below
- mutable std::condition_variable cond{};
- bool done = false;
- bool dataMoved = false;
- std::vector<std::function<void()>> chain{}; //chain tasks are executed on thread pool.
- std::exception_ptr exp{nullptr};
- DataType data{};
- };
+ tbb::task_arena executor; //TODO look into different thread pool libraries
+ //TODO add ScheduledExecutorService like object
- template<>
- class SharedPromiseState<void> {
- public:
- explicit SharedPromiseState(const tbb::task_arena &executor = {});
+ mutable std::mutex mutex{}; //protects below
+ mutable std::condition_variable cond{};
+ bool done = false;
+ bool dataMoved = false;
+ std::vector<std::function<void()>> chain{}; //chain tasks are executed on thread pool.
+ std::exception_ptr exp{nullptr};
+ std::optional<T> data{};
+ };
- ~SharedPromiseState() = default;
+ template<>
+ class SharedPromiseState<void> {
+ public:
+ explicit SharedPromiseState(const tbb::task_arena &executor = {});
- void resolve();
+ ~SharedPromiseState() = default;
- void fail(std::exception_ptr p);
+ void resolve();
- void fail(const std::exception &e);
+ void fail(std::exception_ptr e);
- void tryResolve();
+ void fail(const std::exception &e);
- void tryFail(std::exception_ptr p);
+ void tryResolve();
- bool getValue() const; //copy
- std::exception_ptr getFailure() const;
+ void tryFail(std::exception_ptr e);
- void wait() const;
+ bool getValue() const;
+ std::exception_ptr getFailure() const;
- bool isDone() const;
+ void wait() const;
- bool isSuccessfullyResolved() const;
+ bool isDone() const;
- void addOnSuccessConsumeCallback(std::function<void()> callback);
+ bool isSuccessfullyResolved() const;
- void addOnFailureConsumeCallback(std::function<void(const std::exception &)> callback);
+ void addOnSuccessConsumeCallback(std::function<void()> callback);
- void addOnResolve(std::function<void(bool succeeded, std::exception_ptr exp)> callback);
+ void addOnFailureConsumeCallback(std::function<void(const std::exception &)> callback);
- template<typename Rep, typename Period>
- std::shared_ptr<SharedPromiseState<void>> delay(std::chrono::duration<Rep, Period> duration);
+ void addOnResolve(std::function<void(std::optional<std::exception_ptr> exp)> callback);
- std::shared_ptr<SharedPromiseState<void>> recover(std::function<void()> recover);
+ template<typename Rep, typename Period>
+ std::shared_ptr<SharedPromiseState<void>> delay(std::chrono::duration<Rep, Period> duration);
- std::shared_ptr<SharedPromiseState<void>> fallbackTo(std::shared_ptr<SharedPromiseState<void>> fallbackTo);
+ std::shared_ptr<SharedPromiseState<void>> recover(std::function<void()> recover);
- void resolveWith(std::shared_ptr<SharedPromiseState<void>> with);
+ std::shared_ptr<SharedPromiseState<void>> fallbackTo(std::shared_ptr<SharedPromiseState<void>> fallbackTo);
- template<typename R>
- std::shared_ptr<SharedPromiseState<R>> map(std::function<R(void)> mapper);
+ void resolveWith(std::shared_ptr<SharedPromiseState<void>> with);
- std::shared_ptr<SharedPromiseState<void>> thenAccept(std::function<void()> consumer);
+ template<typename R>
+ std::shared_ptr<SharedPromiseState<R>> map(std::function<R(void)> mapper);
- template<typename Rep, typename Period>
- static std::shared_ptr<SharedPromiseState<void>>
- timeout(std::shared_ptr<SharedPromiseState<void>> state, std::chrono::duration<Rep, Period> duration);
+ std::shared_ptr<SharedPromiseState<void>> thenAccept(std::function<void()> consumer);
- void addChain(std::function<void()> chainFunction);
+ template<typename Rep, typename Period>
+ static std::shared_ptr<SharedPromiseState<void>>
+ timeout(std::shared_ptr<SharedPromiseState<void>> state, std::chrono::duration<Rep, Period> duration);
- tbb::task_arena getExecutor() const;
- private:
- /**
- * Complete the resolving and call the registered tasks
- * A reference to the possible locked unique_lock.
- */
- void complete(std::unique_lock<std::mutex> &lck);
+ void addChain(std::function<void()> chainFunction);
- /**
- * Wait for data and check if it resolved as expected (expects mutex locked)
- */
- void waitForAndCheckData(std::unique_lock<std::mutex> &lck, bool expectValid) const;
+ tbb::task_arena getExecutor() const;
+ private:
+ /**
+ * Complete the resolving and call the registered tasks
+ * A reference to the possible locked unique_lock.
+ */
+ void complete(std::unique_lock<std::mutex> &lck);
- tbb::task_arena executor; //TODO look into different thread pool libraries
- //TODO add ScheduledExecutorService like object
+ /**
+ * Wait for data and check if it resolved as expected (expects mutex locked)
+ */
+ void waitForAndCheckData(std::unique_lock<std::mutex> &lck, bool expectValid) const;
- mutable std::mutex mutex{}; //protects below
- mutable std::condition_variable cond{};
- bool done = false;
- bool dataMoved = false;
- std::vector<std::function<void()>> chain{}; //chain tasks are executed on thread pool.
- std::exception_ptr exp{nullptr};
- };
- }
+ tbb::task_arena executor; //TODO look into different thread pool libraries
+ //TODO add ScheduledExecutorService like object
+
+ mutable std::mutex mutex{}; //protects below
+ mutable std::condition_variable cond{};
+ bool done = false;
+ std::vector<std::function<void()>> chain{}; //chain tasks are executed on thread pool.
+ std::exception_ptr exp{nullptr};
+ };
}
@@ -208,25 +213,17 @@ inline celix::impl::SharedPromiseState<T>::SharedPromiseState(const tbb::task_ar
inline celix::impl::SharedPromiseState<void>::SharedPromiseState(const tbb::task_arena& _executor) : executor{_executor} {}
template<typename T>
-inline celix::impl::SharedPromiseState<T>::~SharedPromiseState() {
- std::unique_lock<std::mutex> lck{mutex};
-
- //Note for now, not waiting until promise is met.
- //Else if a deferred goes out of scope without resolving, a wait will block
- //cond.wait(lck, [this]{return done;});
-
- if (done && !exp && !dataMoved) {
- static_cast<T*>(static_cast<void*>(&data))->~T();
- }
-}
-
-template<typename T>
inline void celix::impl::SharedPromiseState<T>::resolve(T&& value) {
std::unique_lock<std::mutex> lck{mutex};
if (done) {
throw celix::PromiseInvocationException("Cannot resolve Promise. Promise is already done");
}
- new(&data) T{std::forward<T>(value)};
+ dataMoved = false;
+ if constexpr (std::is_move_constructible_v<T>) {
+ data = std::forward<T>(value);
+ } else {
+ data = value;
+ }
exp = nullptr;
complete(lck);
}
@@ -238,7 +235,8 @@ inline void celix::impl::SharedPromiseState<T>::resolve(const T& value) {
if (done) {
throw celix::PromiseInvocationException("Cannot resolve Promise. Promise is already done");
}
- new(&data) T{value};
+ dataMoved = false;
+ data = value;
exp = nullptr;
complete(lck);
}
@@ -284,7 +282,8 @@ template<typename T>
inline void celix::impl::SharedPromiseState<T>::tryResolve(T&& value) {
std::unique_lock<std::mutex> lck{mutex};
if (!done) {
- new(&data) T(std::forward<T>(value));
+ dataMoved = false;
+ data = std::forward<T>(value);
exp = nullptr;
complete(lck);
}
@@ -345,7 +344,15 @@ inline void celix::impl::SharedPromiseState<T>::waitForAndCheckData(std::unique_
}
cond.wait(lck, [this]{return done;});
if (expectValid && exp) {
- throw celix::PromiseInvocationException{"Expected a succeeded promise, but promise failed"};
+ std::string what;
+ try {
+ std::rethrow_exception(exp);
+ } catch (const std::exception &e) {
+ what = e.what();
+ } catch (...) {
+ what = "unknown exception";
+ }
+ throw celix::PromiseInvocationException{"Expected a succeeded promise, but promise failed with message \"" + what + "\""};
} else if(!expectValid && !exp && !dataMoved) {
throw celix::PromiseInvocationException{"Expected a failed promise, but promise succeeded"};
} else if (dataMoved) {
@@ -359,20 +366,46 @@ inline void celix::impl::SharedPromiseState<void>::waitForAndCheckData(std::uniq
}
cond.wait(lck, [this]{return done;});
if (expectValid && exp) {
- throw celix::PromiseInvocationException{"Expected a succeeded promise, but promise failed"};
- } else if(!expectValid && !exp && !dataMoved) {
+ std::string what;
+ try {
+ std::rethrow_exception(exp);
+ } catch (const std::exception &e) {
+ what = e.what();
+ } catch (...) {
+ what = "unknown exception";
+ }
+ throw celix::PromiseInvocationException{"Expected a succeeded promise, but promise failed with message \"" + what + "\""};
+ } else if(!expectValid && !exp) {
throw celix::PromiseInvocationException{"Expected a failed promise, but promise succeeded"};
- } else if (dataMoved) {
- throw celix::PromiseInvocationException{"Invalid use of promise, data is moved and not available anymore!"};
}
}
template<typename T>
-inline const T& celix::impl::SharedPromiseState<T>::getValue() const {
+inline T& celix::impl::SharedPromiseState<T>::getValue() & {
+ std::unique_lock<std::mutex> lck{mutex};
+ waitForAndCheckData(lck, true);
+ return *data;
+}
+
+template<typename T>
+inline const T& celix::impl::SharedPromiseState<T>::getValue() const & {
std::unique_lock<std::mutex> lck{mutex};
waitForAndCheckData(lck, true);
- const T* ptr = reinterpret_cast<const T*>(&data);
- return *ptr;
+ return *data;
+}
+
+template<typename T>
+inline T&& celix::impl::SharedPromiseState<T>::getValue() && {
+ std::unique_lock<std::mutex> lck{mutex};
+ waitForAndCheckData(lck, true);
+ return std::move(*data);
+}
+
+template<typename T>
+inline const T&& celix::impl::SharedPromiseState<T>::getValue() const && {
+ std::unique_lock<std::mutex> lck{mutex};
+ waitForAndCheckData(lck, true);
+ return std::move(*data);
}
inline bool celix::impl::SharedPromiseState<void>::getValue() const {
@@ -382,6 +415,18 @@ inline bool celix::impl::SharedPromiseState<void>::getValue() const {
}
template<typename T>
+inline T celix::impl::SharedPromiseState<T>::moveOrGetValue() {
+ std::unique_lock<std::mutex> lck{mutex};
+ waitForAndCheckData(lck, true);
+ if constexpr (std::is_move_constructible_v<T>) {
+ dataMoved = true;
+ return std::move(*data);
+ } else {
+ return *data;
+ }
+}
+
+template<typename T>
inline tbb::task_arena celix::impl::SharedPromiseState<T>::getExecutor() const {
return executor;
}
@@ -391,15 +436,6 @@ inline tbb::task_arena celix::impl::SharedPromiseState<void>::getExecutor() cons
}
template<typename T>
-inline T celix::impl::SharedPromiseState<T>::moveValue() {
- std::unique_lock<std::mutex> lck{mutex};
- waitForAndCheckData(lck, true);
- dataMoved = true;
- T* ptr = reinterpret_cast<T*>(&data);
- return T{std::move(*ptr)};
-};
-
-template<typename T>
inline void celix::impl::SharedPromiseState<T>::wait() const {
std::unique_lock<std::mutex> lck{mutex};
cond.wait(lck, [this]{return done;});
@@ -425,9 +461,9 @@ inline std::exception_ptr celix::impl::SharedPromiseState<void>::getFailure() co
template<typename T>
inline void celix::impl::SharedPromiseState<T>::resolveWith(std::shared_ptr<SharedPromiseState<T>> with) {
- with->addOnResolve([this](bool succeeded, T* v, std::exception_ptr e) {
- if (succeeded) {
- tryResolve(std::forward<T>(*v));
+ with->addOnResolve([this](std::optional<T> v, std::exception_ptr e) {
+ if (v) {
+ tryResolve(std::move(*v));
} else {
tryFail(std::move(e));
}
@@ -435,11 +471,11 @@ inline void celix::impl::SharedPromiseState<T>::resolveWith(std::shared_ptr<Shar
}
inline void celix::impl::SharedPromiseState<void>::resolveWith(std::shared_ptr<SharedPromiseState<void>> with) {
- with->addOnResolve([this](bool succeeded, std::exception_ptr e) {
- if (succeeded) {
+ with->addOnResolve([this](std::optional<std::exception_ptr> e) {
+ if (!e) {
tryResolve();
} else {
- tryFail(std::move(e));
+ tryFail(std::move(*e));
}
});
}
@@ -474,11 +510,11 @@ template<typename Rep, typename Period>
inline std::shared_ptr<celix::impl::SharedPromiseState<T>> celix::impl::SharedPromiseState<T>::delay(std::chrono::duration<Rep, Period> duration) {
auto p = std::make_shared<celix::impl::SharedPromiseState<T>>(executor);
- addOnResolve([p, duration](bool succeeded, T* v, std::exception_ptr e) {
+ addOnResolve([p, duration](std::optional<T> v, std::exception_ptr e) {
std::this_thread::sleep_for(duration); //TODO use scheduler instead of sleep on thread (using unnecessary resources)
try {
- if (succeeded) {
- p->resolve(std::forward<T>(*v));
+ if (v) {
+ p->resolve(std::move(*v));
} else {
p->fail(std::move(e));
}
@@ -496,13 +532,13 @@ template<typename Rep, typename Period>
inline std::shared_ptr<celix::impl::SharedPromiseState<void>> celix::impl::SharedPromiseState<void>::delay(std::chrono::duration<Rep, Period> duration) {
auto p = std::make_shared<celix::impl::SharedPromiseState<void>>(executor);
- addOnResolve([p, duration](bool succeeded, std::exception_ptr e) {
+ addOnResolve([p, duration](std::optional<std::exception_ptr> e) {
std::this_thread::sleep_for(duration); //TODO use scheduler instead of sleep on thread (using unnecessary resources)
try {
- if (succeeded) {
+ if (!e) {
p->resolve();
} else {
- p->fail(std::move(e));
+ p->fail(std::move(*e));
}
} catch (celix::PromiseInvocationException&) {
//somebody already resolved p?
@@ -521,9 +557,9 @@ inline std::shared_ptr<celix::impl::SharedPromiseState<T>> celix::impl::SharedPr
}
auto p = std::make_shared<celix::impl::SharedPromiseState<T>>(executor);
- addOnResolve([p, recover](bool succeeded, T *v, const std::exception_ptr& /*e*/) {
- if (succeeded) {
- p->resolve(std::forward<T>(*v));
+ addOnResolve([p, recover = std::move(recover)](std::optional<T> v, const std::exception_ptr& /*e*/) {
+ if (v) {
+ p->resolve(std::move(*v));
} else {
try {
p->resolve(recover());
@@ -541,8 +577,8 @@ inline std::shared_ptr<celix::impl::SharedPromiseState<void>> celix::impl::Share
}
auto p = std::make_shared<celix::impl::SharedPromiseState<void>>(executor);
- addOnResolve([p, recover](bool succeeded, const std::exception_ptr& /*e*/) {
- if (succeeded) {
+ addOnResolve([p, recover = std::move(recover)](std::optional<std::exception_ptr> e) {
+ if (!e) {
p->resolve();
} else {
try {
@@ -557,17 +593,16 @@ inline std::shared_ptr<celix::impl::SharedPromiseState<void>> celix::impl::Share
}
template<typename T>
-inline std::shared_ptr<celix::impl::SharedPromiseState<T>> celix::impl::SharedPromiseState<T>::filter(std::function<bool(T)> predicate) {
+inline std::shared_ptr<celix::impl::SharedPromiseState<T>> celix::impl::SharedPromiseState<T>::filter(std::function<bool(const T&)> predicate) {
if (!predicate) {
throw celix::PromiseInvocationException{"provided predicate callback is not valid"};
}
auto p = std::make_shared<celix::impl::SharedPromiseState<T>>(executor);
- auto chainFunction = [this, p, predicate] {
+ auto chainFunction = [this, p, predicate = std::move(predicate)] {
if (isSuccessfullyResolved()) {
- T val = getValue();
try {
- if (predicate(std::forward<T>(val))) {
- p->resolve(std::forward<T>(val));
+ if (predicate(getValue())) {
+ p->resolve(moveOrGetValue());
} else {
throw celix::PromiseInvocationException{"predicate does not accept value"};
}
@@ -586,14 +621,12 @@ inline std::shared_ptr<celix::impl::SharedPromiseState<T>> celix::impl::SharedPr
template<typename T>
inline std::shared_ptr<celix::impl::SharedPromiseState<T>> celix::impl::SharedPromiseState<T>::fallbackTo(std::shared_ptr<celix::impl::SharedPromiseState<T>> fallbackTo) {
auto p = std::make_shared<celix::impl::SharedPromiseState<T>>(executor);
- auto chainFunction = [this, p, fallbackTo] {
+ auto chainFunction = [this, p, fallbackTo = std::move(fallbackTo)] {
if (isSuccessfullyResolved()) {
- T val = getValue();
- p->resolve(std::forward<T>(val));
+ p->resolve(moveOrGetValue());
} else {
if (fallbackTo->isSuccessfullyResolved()) {
- T val = fallbackTo->getValue();
- p->resolve(std::forward<T>(val));
+ p->resolve(fallbackTo->moveOrGetValue());
} else {
p->fail(getFailure());
}
@@ -605,7 +638,7 @@ inline std::shared_ptr<celix::impl::SharedPromiseState<T>> celix::impl::SharedPr
inline std::shared_ptr<celix::impl::SharedPromiseState<void>> celix::impl::SharedPromiseState<void>::fallbackTo(std::shared_ptr<celix::impl::SharedPromiseState<void>> fallbackTo) {
auto p = std::make_shared<celix::impl::SharedPromiseState<void>>(executor);
- auto chainFunction = [this, p, fallbackTo] {
+ auto chainFunction = [this, p, fallbackTo = std::move(fallbackTo)] {
if (isSuccessfullyResolved()) {
getValue();
p->resolve();
@@ -626,7 +659,6 @@ template<typename T>
inline void celix::impl::SharedPromiseState<T>::addChain(std::function<void()> chainFunction) {
std::function<void()> localChain{};
{
-
std::lock_guard<std::mutex> lck{mutex};
if (!done) {
chain.push_back(std::move(chainFunction));
@@ -642,7 +674,6 @@ inline void celix::impl::SharedPromiseState<T>::addChain(std::function<void()> c
inline void celix::impl::SharedPromiseState<void>::addChain(std::function<void()> chainFunction) {
std::function<void()> localChain{};
{
-
std::lock_guard<std::mutex> lck{mutex};
if (!done) {
chain.push_back(std::move(chainFunction));
@@ -662,11 +693,10 @@ inline std::shared_ptr<celix::impl::SharedPromiseState<R>> celix::impl::SharedPr
throw celix::PromiseInvocationException("provided mapper is not valid");
}
auto p = std::make_shared<celix::impl::SharedPromiseState<R>>(executor);
- auto chainFunction = [this, p, mapper] {
+ auto chainFunction = [this, p, mapper = std::move(mapper)] {
try {
if (isSuccessfullyResolved()) {
- R val = mapper(getValue());
- p->resolve(std::forward<R>(val));
+ p->resolve(mapper(moveOrGetValue()));
} else {
p->fail(getFailure());
}
@@ -684,12 +714,11 @@ inline std::shared_ptr<celix::impl::SharedPromiseState<R>> celix::impl::SharedPr
throw celix::PromiseInvocationException("provided mapper is not valid");
}
auto p = std::make_shared<celix::impl::SharedPromiseState<R>>(executor);
- auto chainFunction = [this, p, mapper] {
+ auto chainFunction = [this, p, mapper = std::move(mapper)] {
try {
if (isSuccessfullyResolved()) {
getValue();
- R val = mapper();
- p->resolve(std::forward<R>(val));
+ p->resolve(mapper());
} else {
p->fail(getFailure());
}
@@ -707,12 +736,11 @@ inline std::shared_ptr<celix::impl::SharedPromiseState<T>> celix::impl::SharedPr
throw celix::PromiseInvocationException("provided consumer is not valid");
}
auto p = std::make_shared<celix::impl::SharedPromiseState<T>>(executor);
- auto chainFunction = [this, p, consumer] {
+ auto chainFunction = [this, p, consumer = std::move(consumer)] {
if (isSuccessfullyResolved()) {
try {
- T val = getValue();
- consumer(std::forward<T>(val));
- p->resolve(std::forward<T>(val));
+ consumer(getValue());
+ p->resolve(moveOrGetValue());
} catch (...) {
p->fail(std::current_exception());
}
@@ -729,7 +757,7 @@ inline std::shared_ptr<celix::impl::SharedPromiseState<void>> celix::impl::Share
throw celix::PromiseInvocationException("provided consumer is not valid");
}
auto p = std::make_shared<celix::impl::SharedPromiseState<void>>(executor);
- auto chainFunction = [this, p, consumer] {
+ auto chainFunction = [this, p, consumer = std::move(consumer)] {
if (isSuccessfullyResolved()) {
try {
getValue();
@@ -747,35 +775,37 @@ inline std::shared_ptr<celix::impl::SharedPromiseState<void>> celix::impl::Share
}
template<typename T>
-inline void celix::impl::SharedPromiseState<T>::addOnResolve(std::function<void(bool succeeded, T* val, std::exception_ptr exp)> callback) {
- std::function<void()> task = [this, callback] {
+inline void celix::impl::SharedPromiseState<T>::addOnResolve(std::function<void(std::optional<T> val, std::exception_ptr exp)> callback) {
+ std::function<void()> task = [this, callback = std::move(callback)] {
std::exception_ptr e = nullptr;
- T* val = nullptr;
{
std::lock_guard<std::mutex> lck{mutex};
e = exp;
- val = e ? nullptr : reinterpret_cast<T*>(&data);
}
- callback(!e, val, e);
+ if(e) {
+ callback({}, e);
+ } else {
+ callback(getValue(), e);
+ }
};
addChain(task);
}
-inline void celix::impl::SharedPromiseState<void>::addOnResolve(std::function<void(bool succeeded, std::exception_ptr exp)> callback) {
- std::function<void()> task = [this, callback] {
+inline void celix::impl::SharedPromiseState<void>::addOnResolve(std::function<void(std::optional<std::exception_ptr> exp)> callback) {
+ std::function<void()> task = [this, callback = std::move(callback)] {
std::exception_ptr e = nullptr;
{
std::lock_guard<std::mutex> lck{mutex};
e = exp;
}
- callback(!e, e);
+ callback(e);
};
addChain(task);
}
template<typename T>
inline void celix::impl::SharedPromiseState<T>::addOnSuccessConsumeCallback(std::function<void(T)> callback) {
- std::function<void()> task = [this, callback] {
+ std::function<void()> task = [this, callback = std::move(callback)] {
if (isSuccessfullyResolved()) {
callback(getValue());
}
@@ -784,7 +814,7 @@ inline void celix::impl::SharedPromiseState<T>::addOnSuccessConsumeCallback(std:
}
inline void celix::impl::SharedPromiseState<void>::addOnSuccessConsumeCallback(std::function<void()> callback) {
- std::function<void()> task = [this, callback] {
+ std::function<void()> task = [this, callback = std::move(callback)] {
if (isSuccessfullyResolved()) {
getValue();
callback();
@@ -795,7 +825,7 @@ inline void celix::impl::SharedPromiseState<void>::addOnSuccessConsumeCallback(s
template<typename T>
inline void celix::impl::SharedPromiseState<T>::addOnFailureConsumeCallback(std::function<void(const std::exception&)> callback) {
- std::function<void()> task = [this, callback] {
+ std::function<void()> task = [this, callback = std::move(callback)] {
if (!isSuccessfullyResolved()) {
try {
std::rethrow_exception(getFailure());
@@ -812,7 +842,7 @@ inline void celix::impl::SharedPromiseState<T>::addOnFailureConsumeCallback(std:
}
inline void celix::impl::SharedPromiseState<void>::addOnFailureConsumeCallback(std::function<void(const std::exception&)> callback) {
- std::function<void()> task = [this, callback] {
+ std::function<void()> task = [this, callback = std::move(callback)] {
if (!isSuccessfullyResolved()) {
try {
std::rethrow_exception(getFailure());
diff --git a/misc/experimental/promise/gtest/src/PromiseTestSuite.cc b/misc/experimental/promise/gtest/src/PromiseTestSuite.cc
index 1b99c0d..dbed789 100644
--- a/misc/experimental/promise/gtest/src/PromiseTestSuite.cc
+++ b/misc/experimental/promise/gtest/src/PromiseTestSuite.cc
@@ -20,6 +20,7 @@
#include <gtest/gtest.h>
#include <future>
+#include <utility>
#include "celix/PromiseFactory.h"
@@ -30,22 +31,70 @@ public:
celix::PromiseFactory factory{ tbb::task_arena{5, 1} };
};
+struct MovableInt {
+ MovableInt(int _val) : val(_val) {}
+ operator int() const { return val; }
+ int val;
+};
+
+struct NonMovableInt {
+ NonMovableInt(int _val) : val(_val) {}
+ operator int() const { return val; }
+ NonMovableInt(NonMovableInt&&) = delete;
+ NonMovableInt(const NonMovableInt&) = default;
+ NonMovableInt& operator=(NonMovableInt&&) = delete;
+ NonMovableInt& operator=(const NonMovableInt&) = default;
+
+ int val;
+};
+
+struct NonTrivialType {
+ NonTrivialType(std::string _val) : val(std::move(_val)) {}
+ operator std::string() const { return val; }
+ NonTrivialType(NonTrivialType&&) = default;
+ NonTrivialType(const NonTrivialType&) = default;
+ NonTrivialType& operator=(NonTrivialType&&) = default;
+ NonTrivialType& operator=(const NonTrivialType&) = default;
+
+
+ NonTrivialType(const char *c) : val(c) {}
+
+ NonTrivialType& operator=(const char *c) {
+ val = c;
+ return *this;
+ }
+
+ bool operator==(const char *c) const {
+ return c == val;
+ }
+
+ std::string val;
+};
+
+bool operator==( const char *c, const NonTrivialType &ntt) {
+ return c == ntt.val;
+}
+
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunused-result"
+#endif
TEST_F(PromiseTestSuite, simplePromise) {
- auto deferred = factory.deferred<long>();
- std::thread t{[deferred] () mutable { //TODO TBD make deferred a shared_ptr to prevent need for mutable?
+ auto deferred = factory.deferred<NonTrivialType>();
+ std::thread t{[&deferred] () {
std::this_thread::sleep_for(std::chrono::milliseconds{50});
- deferred.resolve(42);
+ deferred.resolve("test");
}};
auto promise = deferred.getPromise();
- EXPECT_EQ(42, promise.getValue()); //block until ready
+ EXPECT_EQ("test", promise.getValue()); //block until ready
EXPECT_TRUE(promise.isDone()); //got value, so promise is done
EXPECT_ANY_THROW(promise.getFailure()); //succeeded, so no exception available
- EXPECT_EQ(42, promise.getValue()); //note multiple call are valid;
+ EXPECT_EQ("test", promise.getValue()); //note multiple call are valid;
- EXPECT_EQ(42, promise.moveValue()); //data is now moved
+ EXPECT_EQ("test", promise.moveOrGetValue()); //data is now moved
EXPECT_THROW(promise.getValue(), celix::PromiseInvocationException); //data is already moved -> exception
t.join();
}
@@ -53,7 +102,7 @@ TEST_F(PromiseTestSuite, simplePromise) {
TEST_F(PromiseTestSuite, failingPromise) {
auto deferred = factory.deferred<long>();
auto cpy = deferred;
- std::thread t{[deferred] () mutable {
+ std::thread t{[&deferred] () {
deferred.fail(std::logic_error{"failing"});
}};
auto promise = deferred.getPromise();
@@ -371,4 +420,53 @@ TEST_F(PromiseTestSuite, failedResolvedWithPromiseFactory) {
auto p2 = factory.resolved(42);
EXPECT_TRUE(p2.isDone());
EXPECT_EQ(42, p2.getValue());
-}
\ No newline at end of file
+}
+
+TEST_F(PromiseTestSuite, movableStruct) {
+ auto deferred = factory.deferred<MovableInt>();
+ std::thread t{[&deferred] () {
+ std::this_thread::sleep_for(std::chrono::milliseconds{50});
+ deferred.resolve(42);
+ }};
+ auto promise = deferred.getPromise();
+ EXPECT_EQ(42, promise.getValue());
+ EXPECT_EQ(42, *promise);
+ t.join();
+}
+
+TEST_F(PromiseTestSuite, movableStructTemporary) {
+ auto deferred = factory.deferred<MovableInt>();
+ std::thread t{[&deferred] () {
+ std::this_thread::sleep_for(std::chrono::milliseconds{50});
+ deferred.resolve(42);
+ }};
+ EXPECT_EQ(42, deferred.getPromise().getValue());
+ t.join();
+}
+
+TEST_F(PromiseTestSuite, nonMovableStruct) {
+ auto deferred = factory.deferred<NonMovableInt>();
+ std::thread t{[&deferred] () {
+ std::this_thread::sleep_for(std::chrono::milliseconds{50});
+ deferred.resolve(42);
+ }};
+ auto promise = deferred.getPromise();
+ EXPECT_EQ(42, promise.getValue());
+ EXPECT_EQ(42, *promise);
+ t.join();
+}
+
+TEST_F(PromiseTestSuite, nonMovableStructTemporary) {
+ auto deferred = factory.deferred<NonMovableInt>();
+ std::thread t{[&deferred] () {
+ std::this_thread::sleep_for(std::chrono::milliseconds{50});
+ deferred.resolve(42);
+ }};
+ EXPECT_EQ(42, deferred.getPromise().getValue());
+ t.join();
+}
+
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
+
diff --git a/misc/experimental/promise/gtest/src/VoidPromiseTestSuite.cc b/misc/experimental/promise/gtest/src/VoidPromiseTestSuite.cc
index 679aaa5..56d1f73 100644
--- a/misc/experimental/promise/gtest/src/VoidPromiseTestSuite.cc
+++ b/misc/experimental/promise/gtest/src/VoidPromiseTestSuite.cc
@@ -30,11 +30,14 @@ public:
celix::PromiseFactory factory{ tbb::task_arena{5, 1} };
};
-
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunused-result"
+#endif
TEST_F(VoidPromiseTestSuite, simplePromise) {
auto deferred = factory.deferred<void>();
- std::thread t{[deferred] () mutable { //TODO TBD make deferred a shared_ptr to prevent need for mutable?
+ std::thread t{[&deferred] () {
std::this_thread::sleep_for(std::chrono::milliseconds{50});
deferred.resolve();
}};
@@ -50,7 +53,7 @@ TEST_F(VoidPromiseTestSuite, simplePromise) {
TEST_F(VoidPromiseTestSuite, failingPromise) {
auto deferred = factory.deferred<void>();
auto cpy = deferred;
- std::thread t{[deferred] () mutable {
+ std::thread t{[&deferred] () {
deferred.fail(std::logic_error{"failing"});
}};
auto promise = deferred.getPromise();
@@ -349,4 +352,8 @@ TEST_F(VoidPromiseTestSuite, failedResolvedWithPromiseFactory) {
auto p2 = factory.resolved();
EXPECT_TRUE(p2.isDone());
EXPECT_TRUE(p2.getValue());
-}
\ No newline at end of file
+}
+
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
diff --git a/misc/experimental/promise/src/PromiseExamples.cc b/misc/experimental/promise/src/PromiseExamples.cc
index d344381..de7d5f7 100644
--- a/misc/experimental/promise/src/PromiseExamples.cc
+++ b/misc/experimental/promise/src/PromiseExamples.cc
@@ -68,4 +68,4 @@ int main() {
});
p1.wait();
p2.wait();
-}
\ No newline at end of file
+}