You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@celix.apache.org by GitBox <gi...@apache.org> on 2020/04/20 09:22:11 UTC

[GitHub] [celix] pnoltes opened a new pull request #203: Feature/osgi promise

pnoltes opened a new pull request #203:
URL: https://github.com/apache/celix/pull/203


   Initial PR for an experimental C++11 Promise implementation.
   
   The current implementation uses a value oriented design. Some parts are still missing and the used thread pool just to get the discussion rolling. 
   
   Focus of the PR is not a complete OSGi Promise implementation, but whether the design decision are valid (value oriented, focus on std::function instead of F&& template args).
   
   @Oipo and @glimmerveen Could you also have a look?
   


----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [celix] Oipo commented on issue #203: Feature/osgi promise

Posted by GitBox <gi...@apache.org>.
Oipo commented on issue #203:
URL: https://github.com/apache/celix/pull/203#issuecomment-616543989


   > > Also, I'd prefer if this library would be separate from celix, so that it is usable without adding celix to your project.
   > 
   > Same setup as etcdlib?
   
   I was actually thinking of a separate repository, which I had expected for etcdlib as well. However, perhaps that's something for a future date.
   
   > > What is the reason to focus on std::function instead of template args?
   > 
   > I personally find the API much clearer without template arg if they can be avoided, so that why I prefer the use of std::function. What is the benefit of using template args?
   
   Depending on situation, there is a sizeable performance impact on using std::function. See https://stackoverflow.com/a/14678298 for a proper explanation.
   
   What I do think though, and why I think libraries such as [continuable](https://github.com/Naios/continuable) are in C++14, is that C++11 doesn't support some template niceties that may make implementing template lambdas difficult for celix.


----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [celix] abroekhuis commented on issue #203: Feature/osgi promise

Posted by GitBox <gi...@apache.org>.
abroekhuis commented on issue #203:
URL: https://github.com/apache/celix/pull/203#issuecomment-616973985


   I can't comment on the specifi C++ constructs and possibilities, but the API looks good to me. 
   
   I agree that it makes sense to put this in a separate library, that way we can always easily move it to an own project if wanted. 


----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [celix] pnoltes commented on a change in pull request #203: Feature/osgi promise

Posted by GitBox <gi...@apache.org>.
pnoltes commented on a change in pull request #203:
URL: https://github.com/apache/celix/pull/203#discussion_r412448723



##########
File path: misc/experimental/promise/api/celix/Promise.h
##########
@@ -0,0 +1,512 @@
+/**
+ *Licensed to the Apache Software Foundation (ASF) under one
+ *or more contributor license agreements.  See the NOTICE file
+ *distributed with this work for additional information
+ *regarding copyright ownership.  The ASF licenses this file
+ *to you under the Apache License, Version 2.0 (the
+ *"License"); you may not use this file except in compliance
+ *with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *Unless required by applicable law or agreed to in writing,
+ *software distributed under the License is distributed on an
+ *"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ *specific language governing permissions and limitations
+ *under the License.
+ */
+
+#pragma once
+
+#include "celix/impl/SharedPromiseState.h"
+
+namespace celix {
+
+    /**
+     * A Promise of a value.
+     * <p>
+     * A Promise represents a future value. It handles the interactions for
+     * asynchronous processing. A {@link Deferred} object can be used to create a
+     * Promise and later resolve the Promise. A Promise is used by the caller of an
+     * asynchronous function to get the result or handle the error. The caller can
+     * either get a callback when the Promise is resolved with a value or an error,
+     * or the Promise can be used in chaining. In chaining, callbacks are provided
+     * that receive the resolved Promise, and a new Promise is generated that
+     * resolves based upon the result of a callback.
+     * <p>
+     * Both {@link #onResolve(Runnable) callbacks} and
+     * {@link #then(Success, Failure) chaining} can be repeated any number of times,
+     * even after the Promise has been resolved.
+     * <p>
+     * Example callback usage:
+     *
+     * <pre>
+     * celix::Promise&lt;std::string&gt; foo{};
+     * foo.onResolve([]{ std::cout << "resolved" << std::endl; });
+     * </pre>
+     *
+     *
+     * @tparam <T> The value type associated with this Promise.
+     * @ThreadSafe
+     */
+    template<typename T>
+    class Promise {
+    public:
+        using type = T;
+
+        explicit Promise(std::shared_ptr<celix::impl::SharedPromiseState<T>> s);
+
+//        ~Promise() {
+//            //TODO maybe make a special detach call to state if the count is 1
+//            //state->detachIfNeeded(state); //create a callback with ref to self if share_ptr count is 1
+//        }
+
+        /**
+         * Returns whether this Promise has been resolved.
+         *
+         * <p>
+         * This Promise may be successfully resolved or resolved with a failure.
+         *
+         * @return {@code true} if this Promise was resolved either successfully or
+         *         with a failure; {@code false} if this Promise is unresolved.
+         */
+        bool isDone() const;
+
+        //
+        /**
+         * Returns whether this Promise has been resolved and whether it resolved successfully.
+         * NOTE although not part of the OSGi spec, IMO this is clearer than (isDone() && !getFailure())
+         *
+         *
+         * @return {@code true} if this Promise was resolved successfully.
+         *         {@code false} if this Promise is unresolved or resolved with a failure.
+         */
+        bool isSuccessfullyResolved() const;
+
+        /**
+         * Returns the failure of this Promise.
+         *
+         * <p>
+         * If this Promise is not {@link #isDone() resolved}, this method must block
+         * and wait for this Promise to be resolved before completing.
+         *
+         * <p>
+         * If this Promise was resolved with a failure, this method returns with the
+         * failure of this Promise. If this Promise was successfully resolved, this
+         * method must return {@code null}.
+         *
+         * @return The failure of this resolved Promise or {@code null} if this
+         *         Promise was successfully resolved.
+         * @throws InterruptedException If the current thread was interrupted while
+         *         waiting.
+         */
+        std::exception_ptr getFailure() const;
+
+        /**
+         * Returns the value of this Promise.
+         *
+         * <p>
+         * If this Promise is not {@link #isDone() resolved}, this method must block
+         * and wait for this Promise to be resolved before completing.
+         *
+         * <p>
+         * If this Promise was successfully resolved, this method returns with the
+         * value of this Promise. If this Promise was resolved with a failure, this
+         * method must throw an {@code InvocationTargetException} with the
+         * {@link #getFailure() failure exception} as the cause.
+         *
+         * @return The value of this resolved Promise.
+         * @throws InvocationTargetException If this Promise was resolved with a
+         *         failure. The cause of the {@code InvocationTargetException} is
+         *         the failure exception.
+         * @throws InterruptedException If the current thread was interrupted while
+         *         waiting.
+         */
+        const T& getValue() const;
+
+        //TODO is a move value needed? Howto handle this with resolve callbacks
+        T moveValue();
+
+        /**
+         * Wait till the promise is resolved.
+         *
+         * <p>
+         * If this Promise is not {@link #isDone() resolved}, this method must block
+         * and wait for this Promise to be resolved before completing.
+         *
+         */
+        void wait() const; //NOTE not part of the OSGI promise, wait till resolved (used in testing)

Review comment:
       getValue return a possible value or throws an exception. 
   
   Mainly for testing you just want to wait till a promise is resolved. The actual asserts/checks are done on success/failure callbacks.




----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [celix] pnoltes commented on issue #203: Feature/osgi promise

Posted by GitBox <gi...@apache.org>.
pnoltes commented on issue #203:
URL: https://github.com/apache/celix/pull/203#issuecomment-616522465


   > What is the reason to focus on std::function instead of template args?
   
   I personally find the API much clearer without template arg if they can be avoided, so that why I prefer the use of std::function. What is the benefit of using template args?


----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [celix] pnoltes commented on issue #203: Feature/osgi promise

Posted by GitBox <gi...@apache.org>.
pnoltes commented on issue #203:
URL: https://github.com/apache/celix/pull/203#issuecomment-616521697


   > Also, I'd prefer if this library would be separate from celix, so that it is usable without adding celix to your project.
   
   Same setup as etcdlib?


----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [celix] pnoltes commented on issue #203: Feature/osgi promise

Posted by GitBox <gi...@apache.org>.
pnoltes commented on issue #203:
URL: https://github.com/apache/celix/pull/203#issuecomment-617587629


   > > > Also, I'd prefer if this library would be separate from celix, so that it is usable without adding celix to your project.
   > > 
   > > 
   > > Same setup as etcdlib?
   > 
   > I was actually thinking of a separate repository, which I had expected for etcdlib as well. However, perhaps that's something for a future date.
   > 
   > > > What is the reason to focus on std::function instead of template args?
   > > 
   > > 
   > > I personally find the API much clearer without template arg if they can be avoided, so that why I prefer the use of std::function. What is the benefit of using template args?
   > 
   > Depending on situation, there is a sizeable performance impact on using std::function. See https://stackoverflow.com/a/14678298 for a proper explanation and especially a [comment](https://stackoverflow.com/a/15045156) further down explaining that it depends on compiler as well.
   > 
   > What I do think though, and why I think libraries such as [continuable](https://github.com/Naios/continuable) are in C++14, is that C++11 doesn't support some template niceties that may make implementing template lambdas difficult for celix.
   
   Ok, good info.
   Note that eventually all callbacks are stored as std::function<void()> in the SharedPromiseState.
   With the current setup this cannot be template, because callbacks with different signatures (e.g.a success, failure, thenAccept, etc callbacks) are simplified as std::function<void()> and stored.
   
   That being said, the Promise API can be updated to accept template args. For this PR I will add this as remark to the readme. A future update will then address this. 


----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [celix] pnoltes commented on a change in pull request #203: Feature/osgi promise

Posted by GitBox <gi...@apache.org>.
pnoltes commented on a change in pull request #203:
URL: https://github.com/apache/celix/pull/203#discussion_r412718722



##########
File path: misc/experimental/promise/api/celix/impl/SharedPromiseState.h
##########
@@ -0,0 +1,508 @@
+/**
+ *Licensed to the Apache Software Foundation (ASF) under one
+ *or more contributor license agreements.  See the NOTICE file
+ *distributed with this work for additional information
+ *regarding copyright ownership.  The ASF licenses this file
+ *to you under the Apache License, Version 2.0 (the
+ *"License"); you may not use this file except in compliance
+ *with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *Unless required by applicable law or agreed to in writing,
+ *software distributed under the License is distributed on an
+ *"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ *specific language governing permissions and limitations
+ *under the License.
+ */
+
+#pragma once
+
+#include <functional>
+#include <chrono>
+#include <mutex>
+#include <condition_variable>
+#include <vector>
+#include <thread>
+
+#include <tbb/task_arena.h>
+
+#include "celix/PromiseInvocationException.h"
+#include "celix/PromiseTimeoutException.h"
+
+namespace celix {
+    namespace impl {
+
+        template<typename T>
+        class SharedPromiseState {
+        public:
+            typedef typename std::aligned_storage<sizeof(T), std::alignment_of<T>::value>::type DataType;
+
+            explicit SharedPromiseState(const tbb::task_arena &executor = {});
+
+            ~SharedPromiseState();
+
+            void resolve(T &&value);
+
+            void resolve(const T& value);
+
+            void fail(std::exception_ptr p);
+
+            void fail(const std::exception &e);
+
+            void tryResolve(T &&value);
+
+            void tryFail(std::exception_ptr p);
+
+            const T &getValue() const; //copy
+            T moveValue(); //move
+            std::exception_ptr getFailure() const;
+
+            void wait() const;
+
+            bool isDone() const;
+
+            bool isSuccessfullyResolved() const;
+
+            void addOnSuccessConsumeCallback(std::function<void(T)> callback);
+
+            void addOnFailureConsumeCallback(std::function<void(const std::exception &)> callback);
+
+            void addOnResolve(std::function<void(bool succeeded, T *val, std::exception_ptr exp)> callback);
+
+            template<typename Rep, typename Period>
+            std::shared_ptr<SharedPromiseState<T>> delay(std::chrono::duration<Rep, Period> duration);
+
+            std::shared_ptr<SharedPromiseState<T>> recover(std::function<T()> recover);
+
+            std::shared_ptr<SharedPromiseState<T>> filter(std::function<bool(T)> predicate);
+
+            std::shared_ptr<SharedPromiseState<T>> fallbackTo(std::shared_ptr<SharedPromiseState<T>> fallbackTo);
+
+            void resolveWith(std::shared_ptr<SharedPromiseState<T>> with);
+
+            template<typename R>
+            std::shared_ptr<SharedPromiseState<R>> map(std::function<R(T)> mapper);
+
+            std::shared_ptr<SharedPromiseState<T>> thenAccept(std::function<void(T)> consumer);
+
+            template<typename Rep, typename Period>
+            static std::shared_ptr<SharedPromiseState<T>>
+            timeout(std::shared_ptr<SharedPromiseState<T>> state, std::chrono::duration<Rep, Period> duration);
+
+            void addChain(std::function<void()> chainFunction);
+
+            tbb::task_arena getExecutor() const;
+
+//        template<typename R>
+//        std::shared_ptr<SharedPromiseState<R>> then(std::function<void()> success, std::function<void()> failure);
+        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);
+
+            /**
+             * 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 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;
+            bool dataMoved = false;
+            std::vector<std::function<void()>> chain{}; //chain tasks are executed on thread pool.
+            std::exception_ptr exp{nullptr};
+            DataType data{};
+        };
+    }
+}
+
+
+/*********************************************************************************
+ Implementation
+*********************************************************************************/
+
+template<typename T>
+inline celix::impl::SharedPromiseState<T>::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)};
+    exp = nullptr;
+    complete(lck);
+}
+
+
+template<typename T>
+inline void celix::impl::SharedPromiseState<T>::resolve(const T& value) {
+    std::unique_lock<std::mutex> lck{mutex};
+    if (done) {
+        throw celix::PromiseInvocationException("Cannot resolve Promise. Promise is already done");
+    }
+    new(&data) T{value};
+    exp = nullptr;
+    complete(lck);
+}
+
+template<typename T>
+inline void celix::impl::SharedPromiseState<T>::fail(std::exception_ptr e) {
+    std::unique_lock<std::mutex> lck{mutex};
+    if (done) {
+        throw celix::PromiseInvocationException("Cannot fail Promise. Promise is already done");
+    }
+    exp = e;
+    complete(lck);
+}
+
+template<typename T>
+inline void celix::impl::SharedPromiseState<T>::fail(const std::exception& e) {
+    fail(std::make_exception_ptr(e));
+}
+
+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));
+        exp = nullptr;
+        complete(lck);
+    }
+}
+
+template<typename T>
+inline void celix::impl::SharedPromiseState<T>::tryFail(std::exception_ptr e) {
+    std::unique_lock<std::mutex> lck{mutex};
+    if (!done) {
+        exp = e;
+        complete(lck);
+    }
+}
+
+template<typename T>
+inline bool celix::impl::SharedPromiseState<T>::isDone() const {
+    std::lock_guard<std::mutex> lck{mutex};
+    return done;
+}
+
+template<typename T>
+inline bool celix::impl::SharedPromiseState<T>::isSuccessfullyResolved() const {
+    std::lock_guard<std::mutex> lck{mutex};
+    return done && !exp;
+}
+
+
+template<typename T>
+inline void celix::impl::SharedPromiseState<T>::waitForAndCheckData(std::unique_lock<std::mutex>& lck, bool expectValid) const {
+    if (!lck.owns_lock()) {
+        lck.lock();
+    }
+    cond.wait(lck, [this]{return done;});
+    if (expectValid && exp) {
+        throw celix::PromiseInvocationException{"Expected a succeeded promise, but promise failed"};
+    } else if(!expectValid && !exp && !dataMoved) {
+        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 {
+    std::unique_lock<std::mutex> lck{mutex};
+    waitForAndCheckData(lck, true);
+    const T* ptr = reinterpret_cast<const T*>(&data);
+    return *ptr;
+}
+
+template<typename T>
+inline tbb::task_arena celix::impl::SharedPromiseState<T>::getExecutor() const {

Review comment:
       Correct, I will update this. 




----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [celix] pnoltes commented on a change in pull request #203: Feature/osgi promise

Posted by GitBox <gi...@apache.org>.
pnoltes commented on a change in pull request #203:
URL: https://github.com/apache/celix/pull/203#discussion_r412714431



##########
File path: misc/experimental/promise/api/celix/Promise.h
##########
@@ -0,0 +1,512 @@
+/**
+ *Licensed to the Apache Software Foundation (ASF) under one
+ *or more contributor license agreements.  See the NOTICE file
+ *distributed with this work for additional information
+ *regarding copyright ownership.  The ASF licenses this file
+ *to you under the Apache License, Version 2.0 (the
+ *"License"); you may not use this file except in compliance
+ *with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *Unless required by applicable law or agreed to in writing,
+ *software distributed under the License is distributed on an
+ *"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ *specific language governing permissions and limitations
+ *under the License.
+ */
+
+#pragma once
+
+#include "celix/impl/SharedPromiseState.h"
+
+namespace celix {
+
+    /**
+     * A Promise of a value.
+     * <p>
+     * A Promise represents a future value. It handles the interactions for
+     * asynchronous processing. A {@link Deferred} object can be used to create a
+     * Promise and later resolve the Promise. A Promise is used by the caller of an
+     * asynchronous function to get the result or handle the error. The caller can
+     * either get a callback when the Promise is resolved with a value or an error,
+     * or the Promise can be used in chaining. In chaining, callbacks are provided
+     * that receive the resolved Promise, and a new Promise is generated that
+     * resolves based upon the result of a callback.
+     * <p>
+     * Both {@link #onResolve(Runnable) callbacks} and
+     * {@link #then(Success, Failure) chaining} can be repeated any number of times,
+     * even after the Promise has been resolved.
+     * <p>
+     * Example callback usage:
+     *
+     * <pre>
+     * celix::Promise&lt;std::string&gt; foo{};
+     * foo.onResolve([]{ std::cout << "resolved" << std::endl; });
+     * </pre>
+     *
+     *
+     * @tparam <T> The value type associated with this Promise.
+     * @ThreadSafe
+     */
+    template<typename T>
+    class Promise {
+    public:
+        using type = T;
+
+        explicit Promise(std::shared_ptr<celix::impl::SharedPromiseState<T>> s);
+
+//        ~Promise() {
+//            //TODO maybe make a special detach call to state if the count is 1
+//            //state->detachIfNeeded(state); //create a callback with ref to self if share_ptr count is 1
+//        }
+
+        /**
+         * Returns whether this Promise has been resolved.
+         *
+         * <p>
+         * This Promise may be successfully resolved or resolved with a failure.
+         *
+         * @return {@code true} if this Promise was resolved either successfully or
+         *         with a failure; {@code false} if this Promise is unresolved.
+         */
+        bool isDone() const;

Review comment:
       Yes, but the OSGi spec uses isDone. As it is now, I tried to follow the spec as close as possible.




----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [celix] glimmerveen commented on a change in pull request #203: Feature/osgi promise

Posted by GitBox <gi...@apache.org>.
glimmerveen commented on a change in pull request #203:
URL: https://github.com/apache/celix/pull/203#discussion_r411900232



##########
File path: misc/experimental/promise/api/celix/impl/SharedPromiseState.h
##########
@@ -0,0 +1,508 @@
+/**
+ *Licensed to the Apache Software Foundation (ASF) under one
+ *or more contributor license agreements.  See the NOTICE file
+ *distributed with this work for additional information
+ *regarding copyright ownership.  The ASF licenses this file
+ *to you under the Apache License, Version 2.0 (the
+ *"License"); you may not use this file except in compliance
+ *with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *Unless required by applicable law or agreed to in writing,
+ *software distributed under the License is distributed on an
+ *"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ *specific language governing permissions and limitations
+ *under the License.
+ */
+
+#pragma once
+
+#include <functional>
+#include <chrono>
+#include <mutex>
+#include <condition_variable>
+#include <vector>
+#include <thread>
+
+#include <tbb/task_arena.h>
+
+#include "celix/PromiseInvocationException.h"
+#include "celix/PromiseTimeoutException.h"
+
+namespace celix {
+    namespace impl {
+
+        template<typename T>
+        class SharedPromiseState {
+        public:
+            typedef typename std::aligned_storage<sizeof(T), std::alignment_of<T>::value>::type DataType;
+
+            explicit SharedPromiseState(const tbb::task_arena &executor = {});
+
+            ~SharedPromiseState();
+
+            void resolve(T &&value);
+
+            void resolve(const T& value);
+
+            void fail(std::exception_ptr p);
+
+            void fail(const std::exception &e);
+
+            void tryResolve(T &&value);
+
+            void tryFail(std::exception_ptr p);
+
+            const T &getValue() const; //copy
+            T moveValue(); //move
+            std::exception_ptr getFailure() const;
+
+            void wait() const;
+
+            bool isDone() const;
+
+            bool isSuccessfullyResolved() const;
+
+            void addOnSuccessConsumeCallback(std::function<void(T)> callback);
+
+            void addOnFailureConsumeCallback(std::function<void(const std::exception &)> callback);
+
+            void addOnResolve(std::function<void(bool succeeded, T *val, std::exception_ptr exp)> callback);
+
+            template<typename Rep, typename Period>
+            std::shared_ptr<SharedPromiseState<T>> delay(std::chrono::duration<Rep, Period> duration);
+
+            std::shared_ptr<SharedPromiseState<T>> recover(std::function<T()> recover);
+
+            std::shared_ptr<SharedPromiseState<T>> filter(std::function<bool(T)> predicate);
+
+            std::shared_ptr<SharedPromiseState<T>> fallbackTo(std::shared_ptr<SharedPromiseState<T>> fallbackTo);
+
+            void resolveWith(std::shared_ptr<SharedPromiseState<T>> with);
+
+            template<typename R>
+            std::shared_ptr<SharedPromiseState<R>> map(std::function<R(T)> mapper);
+
+            std::shared_ptr<SharedPromiseState<T>> thenAccept(std::function<void(T)> consumer);
+
+            template<typename Rep, typename Period>
+            static std::shared_ptr<SharedPromiseState<T>>
+            timeout(std::shared_ptr<SharedPromiseState<T>> state, std::chrono::duration<Rep, Period> duration);
+
+            void addChain(std::function<void()> chainFunction);
+
+            tbb::task_arena getExecutor() const;
+
+//        template<typename R>
+//        std::shared_ptr<SharedPromiseState<R>> then(std::function<void()> success, std::function<void()> failure);
+        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);
+
+            /**
+             * 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 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;
+            bool dataMoved = false;
+            std::vector<std::function<void()>> chain{}; //chain tasks are executed on thread pool.
+            std::exception_ptr exp{nullptr};
+            DataType data{};
+        };
+    }
+}
+
+
+/*********************************************************************************
+ Implementation
+*********************************************************************************/
+
+template<typename T>
+inline celix::impl::SharedPromiseState<T>::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)};
+    exp = nullptr;
+    complete(lck);
+}
+
+
+template<typename T>
+inline void celix::impl::SharedPromiseState<T>::resolve(const T& value) {
+    std::unique_lock<std::mutex> lck{mutex};
+    if (done) {
+        throw celix::PromiseInvocationException("Cannot resolve Promise. Promise is already done");
+    }
+    new(&data) T{value};
+    exp = nullptr;
+    complete(lck);
+}
+
+template<typename T>
+inline void celix::impl::SharedPromiseState<T>::fail(std::exception_ptr e) {
+    std::unique_lock<std::mutex> lck{mutex};
+    if (done) {
+        throw celix::PromiseInvocationException("Cannot fail Promise. Promise is already done");
+    }
+    exp = e;
+    complete(lck);
+}
+
+template<typename T>
+inline void celix::impl::SharedPromiseState<T>::fail(const std::exception& e) {
+    fail(std::make_exception_ptr(e));
+}
+
+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));
+        exp = nullptr;
+        complete(lck);
+    }
+}
+
+template<typename T>
+inline void celix::impl::SharedPromiseState<T>::tryFail(std::exception_ptr e) {
+    std::unique_lock<std::mutex> lck{mutex};
+    if (!done) {
+        exp = e;
+        complete(lck);
+    }
+}
+
+template<typename T>
+inline bool celix::impl::SharedPromiseState<T>::isDone() const {
+    std::lock_guard<std::mutex> lck{mutex};
+    return done;
+}
+
+template<typename T>
+inline bool celix::impl::SharedPromiseState<T>::isSuccessfullyResolved() const {
+    std::lock_guard<std::mutex> lck{mutex};
+    return done && !exp;
+}
+
+
+template<typename T>
+inline void celix::impl::SharedPromiseState<T>::waitForAndCheckData(std::unique_lock<std::mutex>& lck, bool expectValid) const {
+    if (!lck.owns_lock()) {
+        lck.lock();
+    }
+    cond.wait(lck, [this]{return done;});
+    if (expectValid && exp) {
+        throw celix::PromiseInvocationException{"Expected a succeeded promise, but promise failed"};
+    } else if(!expectValid && !exp && !dataMoved) {
+        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 {
+    std::unique_lock<std::mutex> lck{mutex};
+    waitForAndCheckData(lck, true);
+    const T* ptr = reinterpret_cast<const T*>(&data);
+    return *ptr;
+}
+
+template<typename T>
+inline tbb::task_arena celix::impl::SharedPromiseState<T>::getExecutor() const {

Review comment:
       Conceptually the Executor belongs to the PromiseFactory, and the Promise instances obtain the Executor from there.




----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [celix] Oipo edited a comment on issue #203: Feature/osgi promise

Posted by GitBox <gi...@apache.org>.
Oipo edited a comment on issue #203:
URL: https://github.com/apache/celix/pull/203#issuecomment-616543989


   > > Also, I'd prefer if this library would be separate from celix, so that it is usable without adding celix to your project.
   > 
   > Same setup as etcdlib?
   
   I was actually thinking of a separate repository, which I had expected for etcdlib as well. However, perhaps that's something for a future date.
   
   > > What is the reason to focus on std::function instead of template args?
   > 
   > I personally find the API much clearer without template arg if they can be avoided, so that why I prefer the use of std::function. What is the benefit of using template args?
   
   Depending on situation, there is a sizeable performance impact on using std::function. See https://stackoverflow.com/a/14678298 for a proper explanation and especially a [comment](https://stackoverflow.com/a/15045156) further down explaining that it depends on compiler as well.
   
   What I do think though, and why I think libraries such as [continuable](https://github.com/Naios/continuable) are in C++14, is that C++11 doesn't support some template niceties that may make implementing template lambdas difficult for celix.


----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [celix] pnoltes commented on a change in pull request #203: Feature/osgi promise

Posted by GitBox <gi...@apache.org>.
pnoltes commented on a change in pull request #203:
URL: https://github.com/apache/celix/pull/203#discussion_r412720423



##########
File path: misc/experimental/promise/api/celix/PromiseFactory.h
##########
@@ -0,0 +1,50 @@
+/**
+ *Licensed to the Apache Software Foundation (ASF) under one
+ *or more contributor license agreements.  See the NOTICE file
+ *distributed with this work for additional information
+ *regarding copyright ownership.  The ASF licenses this file
+ *to you under the Apache License, Version 2.0 (the
+ *"License"); you may not use this file except in compliance
+ *with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *Unless required by applicable law or agreed to in writing,
+ *software distributed under the License is distributed on an
+ *"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ *specific language governing permissions and limitations
+ *under the License.
+ */
+
+#pragma once
+
+#include "celix/Deferred.h"
+
+namespace celix {
+
+
+    class PromiseFactory {

Review comment:
       I will look into this and see if I can update this already in PR. 
   Else this will be done in a separate PR (I expect still a few more PRs, before this implementation can be considered complete).




----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [celix] glimmerveen commented on issue #203: Feature/osgi promise

Posted by GitBox <gi...@apache.org>.
glimmerveen commented on issue #203:
URL: https://github.com/apache/celix/pull/203#issuecomment-616981241


   Looks very nice, and is already quite feature-rich considering this is an initial version!


----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [celix] pnoltes commented on pull request #203: Feature/osgi promise

Posted by GitBox <gi...@apache.org>.
pnoltes commented on pull request #203:
URL: https://github.com/apache/celix/pull/203#issuecomment-628507849


   @abroekhuis  and @glimmerveen ping: Could you approve/disapprove the PR,  I would like to merge this PR.


----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [celix] Oipo commented on issue #203: Feature/osgi promise

Posted by GitBox <gi...@apache.org>.
Oipo commented on issue #203:
URL: https://github.com/apache/celix/pull/203#issuecomment-616501767


   What is the reason to focus on std::function instead of template args?


----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [celix] glimmerveen commented on a change in pull request #203: Feature/osgi promise

Posted by GitBox <gi...@apache.org>.
glimmerveen commented on a change in pull request #203:
URL: https://github.com/apache/celix/pull/203#discussion_r411899002



##########
File path: misc/experimental/promise/api/celix/PromiseFactory.h
##########
@@ -0,0 +1,50 @@
+/**
+ *Licensed to the Apache Software Foundation (ASF) under one
+ *or more contributor license agreements.  See the NOTICE file
+ *distributed with this work for additional information
+ *regarding copyright ownership.  The ASF licenses this file
+ *to you under the Apache License, Version 2.0 (the
+ *"License"); you may not use this file except in compliance
+ *with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *Unless required by applicable law or agreed to in writing,
+ *software distributed under the License is distributed on an
+ *"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ *specific language governing permissions and limitations
+ *under the License.
+ */
+
+#pragma once
+
+#include "celix/Deferred.h"
+
+namespace celix {
+
+
+    class PromiseFactory {

Review comment:
       Adding a `resolved` and `failed` factory methods should be relatively easy, and increases usability in areas where the result is not calculated asynchronously.




----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [celix] pnoltes commented on a change in pull request #203: Feature/osgi promise

Posted by GitBox <gi...@apache.org>.
pnoltes commented on a change in pull request #203:
URL: https://github.com/apache/celix/pull/203#discussion_r412448723



##########
File path: misc/experimental/promise/api/celix/Promise.h
##########
@@ -0,0 +1,512 @@
+/**
+ *Licensed to the Apache Software Foundation (ASF) under one
+ *or more contributor license agreements.  See the NOTICE file
+ *distributed with this work for additional information
+ *regarding copyright ownership.  The ASF licenses this file
+ *to you under the Apache License, Version 2.0 (the
+ *"License"); you may not use this file except in compliance
+ *with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *Unless required by applicable law or agreed to in writing,
+ *software distributed under the License is distributed on an
+ *"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ *specific language governing permissions and limitations
+ *under the License.
+ */
+
+#pragma once
+
+#include "celix/impl/SharedPromiseState.h"
+
+namespace celix {
+
+    /**
+     * A Promise of a value.
+     * <p>
+     * A Promise represents a future value. It handles the interactions for
+     * asynchronous processing. A {@link Deferred} object can be used to create a
+     * Promise and later resolve the Promise. A Promise is used by the caller of an
+     * asynchronous function to get the result or handle the error. The caller can
+     * either get a callback when the Promise is resolved with a value or an error,
+     * or the Promise can be used in chaining. In chaining, callbacks are provided
+     * that receive the resolved Promise, and a new Promise is generated that
+     * resolves based upon the result of a callback.
+     * <p>
+     * Both {@link #onResolve(Runnable) callbacks} and
+     * {@link #then(Success, Failure) chaining} can be repeated any number of times,
+     * even after the Promise has been resolved.
+     * <p>
+     * Example callback usage:
+     *
+     * <pre>
+     * celix::Promise&lt;std::string&gt; foo{};
+     * foo.onResolve([]{ std::cout << "resolved" << std::endl; });
+     * </pre>
+     *
+     *
+     * @tparam <T> The value type associated with this Promise.
+     * @ThreadSafe
+     */
+    template<typename T>
+    class Promise {
+    public:
+        using type = T;
+
+        explicit Promise(std::shared_ptr<celix::impl::SharedPromiseState<T>> s);
+
+//        ~Promise() {
+//            //TODO maybe make a special detach call to state if the count is 1
+//            //state->detachIfNeeded(state); //create a callback with ref to self if share_ptr count is 1
+//        }
+
+        /**
+         * Returns whether this Promise has been resolved.
+         *
+         * <p>
+         * This Promise may be successfully resolved or resolved with a failure.
+         *
+         * @return {@code true} if this Promise was resolved either successfully or
+         *         with a failure; {@code false} if this Promise is unresolved.
+         */
+        bool isDone() const;
+
+        //
+        /**
+         * Returns whether this Promise has been resolved and whether it resolved successfully.
+         * NOTE although not part of the OSGi spec, IMO this is clearer than (isDone() && !getFailure())
+         *
+         *
+         * @return {@code true} if this Promise was resolved successfully.
+         *         {@code false} if this Promise is unresolved or resolved with a failure.
+         */
+        bool isSuccessfullyResolved() const;
+
+        /**
+         * Returns the failure of this Promise.
+         *
+         * <p>
+         * If this Promise is not {@link #isDone() resolved}, this method must block
+         * and wait for this Promise to be resolved before completing.
+         *
+         * <p>
+         * If this Promise was resolved with a failure, this method returns with the
+         * failure of this Promise. If this Promise was successfully resolved, this
+         * method must return {@code null}.
+         *
+         * @return The failure of this resolved Promise or {@code null} if this
+         *         Promise was successfully resolved.
+         * @throws InterruptedException If the current thread was interrupted while
+         *         waiting.
+         */
+        std::exception_ptr getFailure() const;
+
+        /**
+         * Returns the value of this Promise.
+         *
+         * <p>
+         * If this Promise is not {@link #isDone() resolved}, this method must block
+         * and wait for this Promise to be resolved before completing.
+         *
+         * <p>
+         * If this Promise was successfully resolved, this method returns with the
+         * value of this Promise. If this Promise was resolved with a failure, this
+         * method must throw an {@code InvocationTargetException} with the
+         * {@link #getFailure() failure exception} as the cause.
+         *
+         * @return The value of this resolved Promise.
+         * @throws InvocationTargetException If this Promise was resolved with a
+         *         failure. The cause of the {@code InvocationTargetException} is
+         *         the failure exception.
+         * @throws InterruptedException If the current thread was interrupted while
+         *         waiting.
+         */
+        const T& getValue() const;
+
+        //TODO is a move value needed? Howto handle this with resolve callbacks
+        T moveValue();
+
+        /**
+         * Wait till the promise is resolved.
+         *
+         * <p>
+         * If this Promise is not {@link #isDone() resolved}, this method must block
+         * and wait for this Promise to be resolved before completing.
+         *
+         */
+        void wait() const; //NOTE not part of the OSGI promise, wait till resolved (used in testing)

Review comment:
       getValue return a possible value or throws an exception. 
   
   Mainly for testing you just want to wait till a promise is resolved, the check are then on a success/failure callback.




----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [celix] pnoltes merged pull request #203: Feature/osgi promise

Posted by GitBox <gi...@apache.org>.
pnoltes merged pull request #203:
URL: https://github.com/apache/celix/pull/203


   


----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [celix] Oipo commented on issue #203: Feature/osgi promise

Posted by GitBox <gi...@apache.org>.
Oipo commented on issue #203:
URL: https://github.com/apache/celix/pull/203#issuecomment-616507618


   Also, I'd prefer if this library would be separate from celix, so that it is usable without adding celix to your project.


----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [celix] abroekhuis commented on a change in pull request #203: Feature/osgi promise

Posted by GitBox <gi...@apache.org>.
abroekhuis commented on a change in pull request #203:
URL: https://github.com/apache/celix/pull/203#discussion_r411892733



##########
File path: misc/experimental/promise/api/celix/Promise.h
##########
@@ -0,0 +1,512 @@
+/**
+ *Licensed to the Apache Software Foundation (ASF) under one
+ *or more contributor license agreements.  See the NOTICE file
+ *distributed with this work for additional information
+ *regarding copyright ownership.  The ASF licenses this file
+ *to you under the Apache License, Version 2.0 (the
+ *"License"); you may not use this file except in compliance
+ *with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *Unless required by applicable law or agreed to in writing,
+ *software distributed under the License is distributed on an
+ *"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ *specific language governing permissions and limitations
+ *under the License.
+ */
+
+#pragma once
+
+#include "celix/impl/SharedPromiseState.h"
+
+namespace celix {
+
+    /**
+     * A Promise of a value.
+     * <p>
+     * A Promise represents a future value. It handles the interactions for
+     * asynchronous processing. A {@link Deferred} object can be used to create a
+     * Promise and later resolve the Promise. A Promise is used by the caller of an
+     * asynchronous function to get the result or handle the error. The caller can
+     * either get a callback when the Promise is resolved with a value or an error,
+     * or the Promise can be used in chaining. In chaining, callbacks are provided
+     * that receive the resolved Promise, and a new Promise is generated that
+     * resolves based upon the result of a callback.
+     * <p>
+     * Both {@link #onResolve(Runnable) callbacks} and
+     * {@link #then(Success, Failure) chaining} can be repeated any number of times,
+     * even after the Promise has been resolved.
+     * <p>
+     * Example callback usage:
+     *
+     * <pre>
+     * celix::Promise&lt;std::string&gt; foo{};
+     * foo.onResolve([]{ std::cout << "resolved" << std::endl; });
+     * </pre>
+     *
+     *
+     * @tparam <T> The value type associated with this Promise.
+     * @ThreadSafe
+     */
+    template<typename T>
+    class Promise {
+    public:
+        using type = T;
+
+        explicit Promise(std::shared_ptr<celix::impl::SharedPromiseState<T>> s);
+
+//        ~Promise() {
+//            //TODO maybe make a special detach call to state if the count is 1
+//            //state->detachIfNeeded(state); //create a callback with ref to self if share_ptr count is 1
+//        }
+
+        /**
+         * Returns whether this Promise has been resolved.
+         *
+         * <p>
+         * This Promise may be successfully resolved or resolved with a failure.
+         *
+         * @return {@code true} if this Promise was resolved either successfully or
+         *         with a failure; {@code false} if this Promise is unresolved.
+         */
+        bool isDone() const;

Review comment:
       The doc states "resolved", method uses "done". Does it make sense to align this?

##########
File path: misc/experimental/promise/api/celix/Promise.h
##########
@@ -0,0 +1,512 @@
+/**
+ *Licensed to the Apache Software Foundation (ASF) under one
+ *or more contributor license agreements.  See the NOTICE file
+ *distributed with this work for additional information
+ *regarding copyright ownership.  The ASF licenses this file
+ *to you under the Apache License, Version 2.0 (the
+ *"License"); you may not use this file except in compliance
+ *with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *Unless required by applicable law or agreed to in writing,
+ *software distributed under the License is distributed on an
+ *"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ *specific language governing permissions and limitations
+ *under the License.
+ */
+
+#pragma once
+
+#include "celix/impl/SharedPromiseState.h"
+
+namespace celix {
+
+    /**
+     * A Promise of a value.
+     * <p>
+     * A Promise represents a future value. It handles the interactions for
+     * asynchronous processing. A {@link Deferred} object can be used to create a
+     * Promise and later resolve the Promise. A Promise is used by the caller of an
+     * asynchronous function to get the result or handle the error. The caller can
+     * either get a callback when the Promise is resolved with a value or an error,
+     * or the Promise can be used in chaining. In chaining, callbacks are provided
+     * that receive the resolved Promise, and a new Promise is generated that
+     * resolves based upon the result of a callback.
+     * <p>
+     * Both {@link #onResolve(Runnable) callbacks} and
+     * {@link #then(Success, Failure) chaining} can be repeated any number of times,
+     * even after the Promise has been resolved.
+     * <p>
+     * Example callback usage:
+     *
+     * <pre>
+     * celix::Promise&lt;std::string&gt; foo{};
+     * foo.onResolve([]{ std::cout << "resolved" << std::endl; });
+     * </pre>
+     *
+     *
+     * @tparam <T> The value type associated with this Promise.
+     * @ThreadSafe
+     */
+    template<typename T>
+    class Promise {
+    public:
+        using type = T;
+
+        explicit Promise(std::shared_ptr<celix::impl::SharedPromiseState<T>> s);
+
+//        ~Promise() {
+//            //TODO maybe make a special detach call to state if the count is 1
+//            //state->detachIfNeeded(state); //create a callback with ref to self if share_ptr count is 1
+//        }
+
+        /**
+         * Returns whether this Promise has been resolved.
+         *
+         * <p>
+         * This Promise may be successfully resolved or resolved with a failure.
+         *
+         * @return {@code true} if this Promise was resolved either successfully or
+         *         with a failure; {@code false} if this Promise is unresolved.
+         */
+        bool isDone() const;
+
+        //
+        /**
+         * Returns whether this Promise has been resolved and whether it resolved successfully.
+         * NOTE although not part of the OSGi spec, IMO this is clearer than (isDone() && !getFailure())
+         *
+         *
+         * @return {@code true} if this Promise was resolved successfully.
+         *         {@code false} if this Promise is unresolved or resolved with a failure.
+         */
+        bool isSuccessfullyResolved() const;
+
+        /**
+         * Returns the failure of this Promise.
+         *
+         * <p>
+         * If this Promise is not {@link #isDone() resolved}, this method must block
+         * and wait for this Promise to be resolved before completing.
+         *
+         * <p>
+         * If this Promise was resolved with a failure, this method returns with the
+         * failure of this Promise. If this Promise was successfully resolved, this
+         * method must return {@code null}.
+         *
+         * @return The failure of this resolved Promise or {@code null} if this
+         *         Promise was successfully resolved.
+         * @throws InterruptedException If the current thread was interrupted while
+         *         waiting.
+         */
+        std::exception_ptr getFailure() const;
+
+        /**
+         * Returns the value of this Promise.
+         *
+         * <p>
+         * If this Promise is not {@link #isDone() resolved}, this method must block
+         * and wait for this Promise to be resolved before completing.
+         *
+         * <p>
+         * If this Promise was successfully resolved, this method returns with the
+         * value of this Promise. If this Promise was resolved with a failure, this
+         * method must throw an {@code InvocationTargetException} with the
+         * {@link #getFailure() failure exception} as the cause.
+         *
+         * @return The value of this resolved Promise.
+         * @throws InvocationTargetException If this Promise was resolved with a
+         *         failure. The cause of the {@code InvocationTargetException} is
+         *         the failure exception.
+         * @throws InterruptedException If the current thread was interrupted while
+         *         waiting.
+         */
+        const T& getValue() const;
+
+        //TODO is a move value needed? Howto handle this with resolve callbacks
+        T moveValue();
+
+        /**
+         * Wait till the promise is resolved.
+         *
+         * <p>
+         * If this Promise is not {@link #isDone() resolved}, this method must block
+         * and wait for this Promise to be resolved before completing.
+         *
+         */
+        void wait() const; //NOTE not part of the OSGI promise, wait till resolved (used in testing)

Review comment:
       Why not use getValue? As it also waits.

##########
File path: misc/experimental/promise/api/celix/Promise.h
##########
@@ -0,0 +1,512 @@
+/**
+ *Licensed to the Apache Software Foundation (ASF) under one
+ *or more contributor license agreements.  See the NOTICE file
+ *distributed with this work for additional information
+ *regarding copyright ownership.  The ASF licenses this file
+ *to you under the Apache License, Version 2.0 (the
+ *"License"); you may not use this file except in compliance
+ *with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *Unless required by applicable law or agreed to in writing,
+ *software distributed under the License is distributed on an
+ *"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ *specific language governing permissions and limitations
+ *under the License.
+ */
+
+#pragma once
+
+#include "celix/impl/SharedPromiseState.h"
+
+namespace celix {
+
+    /**
+     * A Promise of a value.
+     * <p>
+     * A Promise represents a future value. It handles the interactions for
+     * asynchronous processing. A {@link Deferred} object can be used to create a
+     * Promise and later resolve the Promise. A Promise is used by the caller of an
+     * asynchronous function to get the result or handle the error. The caller can
+     * either get a callback when the Promise is resolved with a value or an error,
+     * or the Promise can be used in chaining. In chaining, callbacks are provided
+     * that receive the resolved Promise, and a new Promise is generated that
+     * resolves based upon the result of a callback.
+     * <p>
+     * Both {@link #onResolve(Runnable) callbacks} and
+     * {@link #then(Success, Failure) chaining} can be repeated any number of times,
+     * even after the Promise has been resolved.
+     * <p>
+     * Example callback usage:
+     *
+     * <pre>
+     * celix::Promise&lt;std::string&gt; foo{};
+     * foo.onResolve([]{ std::cout << "resolved" << std::endl; });
+     * </pre>
+     *
+     *
+     * @tparam <T> The value type associated with this Promise.
+     * @ThreadSafe
+     */
+    template<typename T>
+    class Promise {
+    public:
+        using type = T;
+
+        explicit Promise(std::shared_ptr<celix::impl::SharedPromiseState<T>> s);
+
+//        ~Promise() {
+//            //TODO maybe make a special detach call to state if the count is 1
+//            //state->detachIfNeeded(state); //create a callback with ref to self if share_ptr count is 1
+//        }
+
+        /**
+         * Returns whether this Promise has been resolved.
+         *
+         * <p>
+         * This Promise may be successfully resolved or resolved with a failure.
+         *
+         * @return {@code true} if this Promise was resolved either successfully or
+         *         with a failure; {@code false} if this Promise is unresolved.
+         */
+        bool isDone() const;
+
+        //
+        /**
+         * Returns whether this Promise has been resolved and whether it resolved successfully.
+         * NOTE although not part of the OSGi spec, IMO this is clearer than (isDone() && !getFailure())
+         *
+         *
+         * @return {@code true} if this Promise was resolved successfully.
+         *         {@code false} if this Promise is unresolved or resolved with a failure.
+         */
+        bool isSuccessfullyResolved() const;
+
+        /**
+         * Returns the failure of this Promise.
+         *
+         * <p>
+         * If this Promise is not {@link #isDone() resolved}, this method must block
+         * and wait for this Promise to be resolved before completing.
+         *
+         * <p>
+         * If this Promise was resolved with a failure, this method returns with the
+         * failure of this Promise. If this Promise was successfully resolved, this
+         * method must return {@code null}.
+         *
+         * @return The failure of this resolved Promise or {@code null} if this
+         *         Promise was successfully resolved.
+         * @throws InterruptedException If the current thread was interrupted while
+         *         waiting.
+         */
+        std::exception_ptr getFailure() const;
+
+        /**
+         * Returns the value of this Promise.
+         *
+         * <p>
+         * If this Promise is not {@link #isDone() resolved}, this method must block
+         * and wait for this Promise to be resolved before completing.
+         *
+         * <p>
+         * If this Promise was successfully resolved, this method returns with the
+         * value of this Promise. If this Promise was resolved with a failure, this
+         * method must throw an {@code InvocationTargetException} with the
+         * {@link #getFailure() failure exception} as the cause.
+         *
+         * @return The value of this resolved Promise.
+         * @throws InvocationTargetException If this Promise was resolved with a
+         *         failure. The cause of the {@code InvocationTargetException} is
+         *         the failure exception.
+         * @throws InterruptedException If the current thread was interrupted while
+         *         waiting.
+         */
+        const T& getValue() const;
+
+        //TODO is a move value needed? Howto handle this with resolve callbacks
+        T moveValue();
+
+        /**
+         * Wait till the promise is resolved.
+         *
+         * <p>
+         * If this Promise is not {@link #isDone() resolved}, this method must block
+         * and wait for this Promise to be resolved before completing.
+         *
+         */
+        void wait() const; //NOTE not part of the OSGI promise, wait till resolved (used in testing)
+
+        /**
+         * Register a callback to be called with the result of this Promise when
+         * this Promise is resolved successfully. The callback will not be called if
+         * this Promise is resolved with a failure.
+         * <p>
+         * This method may be called at any time including before and after this
+         * Promise has been resolved.
+         * <p>
+         * Resolving this Promise <i>happens-before</i> any registered callback is
+         * called. That is, in a registered callback, {@link #isDone()} must return
+         * {@code true} and {@link #getValue()} and {@link #getFailure()} must not
+         * block.
+         * <p>
+         * A callback may be called on a different thread than the thread which
+         * registered the callback. So the callback must be thread safe but can rely
+         * upon that the registration of the callback <i>happens-before</i> the
+         * registered callback is called.
+         *
+         * @param success The Consumer callback that receives the value of this
+         *            Promise. Must be valid.
+         * @return This Promise.
+         */
+        Promise<T>& onSuccess(std::function<void(T)> success);
+
+        /**
+         * Register a callback to be called with the failure for this Promise when
+         * this Promise is resolved with a failure. The callback will not be called
+         * if this Promise is resolved successfully.
+         * <p>
+         * This method may be called at any time including before and after this
+         * Promise has been resolved.
+         * <p>
+         * Resolving this Promise <i>happens-before</i> any registered callback is
+         * called. That is, in a registered callback, {@link #isDone()} must return
+         * {@code true} and {@link #getValue()} and {@link #getFailure()} must not
+         * block.
+         * <p>
+         * A callback may be called on a different thread than the thread which
+         * registered the callback. So the callback must be thread safe but can rely
+         * upon that the registration of the callback <i>happens-before</i> the
+         * registered callback is called.
+         *
+         * @param failure The Consumer callback that receives the failure of this
+         *            Promise. Must be valid.
+         * @return This Promise.
+         */
+        Promise<T>& onFailure(std::function<void(const std::exception&)> failure);
+
+        /**
+         * Register a callback to be called when this Promise is resolved.
+         * <p/>
+         * The specified callback is called when this Promise is resolved either successfully or with a failure.
+         * <p/>
+         * This method may be called at any time including before and after this Promise has been resolved.
+         * <p/>
+         * Resolving this Promise happens-before any registered callback is called. That is, in a registered callback,
+         * isDone() must return true and getValue() and getFailure() must not block.
+         * <p/>
+         * A callback may be called on a different thread than the thread which registered the callback. So the callback
+         * must be thread safe but can rely upon that the registration of the callback happens-before the registered
+         * callback is called.
+         *
+         * @param callback A callback to be called when this Promise is resolved. Must not be null.
+         * @return This Promise.
+         */
+        Promise<T>& onResolve(std::function<void()> callback);
+
+
+        /**
+         * Recover from a failure of this Promise with a recovery value.
+         * <p/>
+         * If this Promise is successfully resolved, the returned Promise will be resolved with the value of this Promise.
+         * <p/>
+         * If this Promise is resolved with a failure, the specified Function is applied to this Promise to produce a
+         * recovery value.
+         * <p/>
+         * If the specified Function throws an exception, the returned Promise will be failed with that exception.
+         * <p/>
+         * To recover from a failure of this Promise with a recovery value, the recoverWith(Function) method must be
+         * used. The specified Function for recoverWith(Function) can return Promises.resolved(null) to supply the desired
+         * null value.
+         * <p/>
+         * This method may be called at any time including before and after this Promise has been resolved.
+         *
+         * @param recovery If this Promise resolves with a failure, the specified Function is called to produce a recovery
+         *                 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);
+
+
+        /**
+         * Chain a new Promise to this Promise with a consumer callback that receives the value of this Promise when it is successfully resolved.
+         * The specified Consumer is called when this Promise is resolved successfully.
+         *
+         * This method returns a new Promise which is chained to this Promise. The returned Promise must be resolved when this Promise is resolved after the specified callback is executed. If the callback throws an exception, the returned Promise is failed with that exception. Otherwise the returned Promise is resolved with the success value from this Promise.
+         * This method may be called at any time including before and after this Promise has been resolved.
+         * Resolving this Promise happens-before any registered callback is called. That is, in a registered callback, isDone() must return true and getValue() and getFailure() must not block.
+         * A callback may be called on a different thread than the thread which registered the callback. So the callback must be thread safe but can rely upon that the registration of the callback happens-before the registered callback is called.
+         *
+         * @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);
+
+        /**
+         * Fall back to the value of the specified Promise if this Promise fails.
+         * <p/>
+         * If this Promise is successfully resolved, the returned Promise will be resolved with the value of this Promise.
+         * <p/>
+         * If this Promise is resolved with a failure, the successful result of the specified Promise is used to resolve the
+         * returned Promise. If the specified Promise is resolved with a failure, the returned Promise will be failed with
+         * the failure of this Promise rather than the failure of the specified Promise.
+         * <p/>
+         * This method may be called at any time including before and after this Promise has been resolved.
+         *
+         * @param fallback The Promise whose value will be used to resolve the returned Promise if this Promise resolves
+         *                 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);
+
+        /**
+         * Map the value of this Promise.
+         * <p/>
+         * If this Promise is successfully resolved, the returned Promise will be resolved with the value of specified
+         * Function as applied to the value of this Promise. If the specified Function throws an exception, the returned
+         * Promise will be failed with the exception.
+         * <p/>
+         * If this Promise is resolved with a failure, the returned Promise will be failed with that failure.
+         * <p/>
+         * This method may be called at any time including before and after this Promise has been resolved.
+         *
+         * @tparam R     The value type associated with the returned Promise.
+         * @param mapper The Function that will map the value of this Promise to the value that will be used to resolve the
+         *               returned Promise. Must not be null.
+         * @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);
+
+        /**
+         * Filter the value of this Promise.
+         * <p/>
+         * If this Promise is successfully resolved, the returned Promise will either be resolved with the value of this
+         * Promise if the specified Predicate accepts that value or failed with a NoSuchElementException if the specified
+         * Predicate does not accept that value. If the specified Predicate throws an exception, the returned Promise will
+         * be failed with the exception.
+         * <p/>
+         * If this Promise is resolved with a failure, the returned Promise will be failed with that failure.
+         * <p/>
+         * This method may be called at any time including before and after this Promise has been resolved.
+         *
+         * @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);
+
+        /**
+         * Time out the resolution of this Promise.
+         * <p>
+         * If this Promise is successfully resolved before the timeout, the returned
+         * Promise is resolved with the value of this Promise. If this Promise is
+         * resolved with a failure before the timeout, the returned Promise is
+         * resolved with the failure of this Promise. If the timeout is reached
+         * before this Promise is resolved, the returned Promise is failed with a
+         * {@link TimeoutException}.
+         *
+         * @param duration  The time to wait. Zero and negative
+         *            time is treated as an immediate timeout.
+         * @return A Promise that is resolved when either this Promise is resolved
+         *         or the specified timeout is reached.
+         */
+        template<typename Rep, typename Period>
+        Promise<T> timeout(std::chrono::duration<Rep, Period> duration);

Review comment:
       I was wondering if a timeout on the getValue made sense, but this is a better solution.




----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org