Index: ps/trunk/source/ps/Future.h =================================================================== --- ps/trunk/source/ps/Future.h +++ ps/trunk/source/ps/Future.h @@ -27,7 +27,7 @@ #include #include -template +template class PackagedTask; namespace FutureSharedStateDetail @@ -48,15 +48,14 @@ * Holds all relevant data. */ template -class SharedState : public ResultHolder +class Receiver : public ResultHolder { static constexpr bool VoidResult = std::is_same_v; public: - SharedState(std::function&& func) : - ResultHolder{std::nullopt}, - m_Func(std::move(func)) + Receiver() : + ResultHolder{std::nullopt} {} - ~SharedState() + ~Receiver() { // For safety, wait on started task completion, but not on pending ones (auto-cancelled). if (!Cancel()) @@ -66,8 +65,8 @@ } } - SharedState(const SharedState&) = delete; - SharedState(SharedState&&) = delete; + Receiver(const Receiver&) = delete; + Receiver(Receiver&&) = delete; bool IsDoneOrCanceled() const { @@ -122,8 +121,17 @@ std::atomic m_Status = Status::PENDING; std::mutex m_Mutex; std::condition_variable m_ConditionVariable; +}; + +template +struct SharedState +{ + SharedState(Callback&& callbackFunc) : + callback{std::forward(callbackFunc)} + {} - std::function m_Func; + Callback callback; + Receiver> receiver; }; } // namespace FutureSharedStateDetail @@ -150,7 +158,6 @@ static constexpr bool VoidResult = std::is_same_v; using Status = FutureSharedStateDetail::Status; - using SharedState = FutureSharedStateDetail::SharedState; public: Future() = default; Future(const Future& o) = delete; @@ -161,8 +168,8 @@ /** * Make the future wait for the result of @a func. */ - template - PackagedTask Wrap(T&& func); + template + PackagedTask Wrap(Callback&& callback); /** * Move the result out of the future, and invalidate the future. @@ -172,17 +179,17 @@ template std::enable_if_t, ResultType> Get() { - ENSURE(!!m_SharedState); + ENSURE(!!m_Receiver); Wait(); if constexpr (VoidResult) return; else { - ENSURE(m_SharedState->m_Status != Status::CANCELED); + ENSURE(m_Receiver->m_Status != Status::CANCELED); // This mark the state invalid - can't call Get again. - return m_SharedState->GetResult(); + return m_Receiver->GetResult(); } } @@ -191,7 +198,7 @@ */ bool IsReady() const { - return !!m_SharedState && m_SharedState->m_Status == Status::DONE; + return !!m_Receiver && m_Receiver->m_Status == Status::DONE; } /** @@ -199,13 +206,13 @@ */ bool Valid() const { - return !!m_SharedState && m_SharedState->m_Status != Status::CANCELED; + return !!m_Receiver && m_Receiver->m_Status != Status::CANCELED; } void Wait() { if (Valid()) - m_SharedState->Wait(); + m_Receiver->Wait(); } /** @@ -217,13 +224,13 @@ { if (!Valid()) return; - if (!m_SharedState->Cancel()) - m_SharedState->Wait(); - m_SharedState.reset(); + if (!m_Receiver->Cancel()) + m_Receiver->Wait(); + m_Receiver.reset(); } protected: - std::shared_ptr m_SharedState; + std::shared_ptr> m_Receiver; }; /** @@ -232,35 +239,39 @@ * This type is mostly just the shared state and the call operator, * handling the promise & continuation logic. */ -template +template class PackagedTask { - static constexpr bool VoidResult = std::is_same_v; public: PackagedTask() = delete; - PackagedTask(std::shared_ptr::SharedState> ss) : m_SharedState(std::move(ss)) {} + PackagedTask(std::shared_ptr> ss) : + m_SharedState(std::move(ss)) + {} void operator()() { - typename Future::Status expected = Future::Status::PENDING; - if (!m_SharedState->m_Status.compare_exchange_strong(expected, Future::Status::STARTED)) + FutureSharedStateDetail::Status expected = FutureSharedStateDetail::Status::PENDING; + if (!m_SharedState->receiver.m_Status.compare_exchange_strong(expected, + FutureSharedStateDetail::Status::STARTED)) + { return; + } - if constexpr (VoidResult) - m_SharedState->m_Func(); + if constexpr (std::is_void_v>) + m_SharedState->callback(); else - m_SharedState->emplace(m_SharedState->m_Func()); + m_SharedState->receiver.emplace(m_SharedState->callback()); // Because we might have threads waiting on us, we need to make sure that they either: // - don't wait on our condition variable // - receive the notification when we're done. // This requires locking the mutex (@see Wait). { - std::lock_guard lock(m_SharedState->m_Mutex); - m_SharedState->m_Status = Future::Status::DONE; + std::lock_guard lock(m_SharedState->receiver.m_Mutex); + m_SharedState->receiver.m_Status = FutureSharedStateDetail::Status::DONE; } - m_SharedState->m_ConditionVariable.notify_all(); + m_SharedState->receiver.m_ConditionVariable.notify_all(); // We no longer need the shared state, drop it immediately. m_SharedState.reset(); @@ -272,18 +283,19 @@ m_SharedState.reset(); } -protected: - std::shared_ptr::SharedState> m_SharedState; +private: + std::shared_ptr> m_SharedState; }; template -template -PackagedTask Future::Wrap(T&& func) +template +PackagedTask Future::Wrap(Callback&& callback) { - static_assert(std::is_same_v, ResultType>, + static_assert(std::is_same_v, ResultType>, "The return type of the wrapped function is not the same as the type the Future expects."); - m_SharedState = std::make_shared(std::move(func)); - return PackagedTask(m_SharedState); + auto temp = std::make_shared>(std::move(callback)); + m_Receiver = {temp, &temp->receiver}; + return PackagedTask(std::move(temp)); } #endif // INCLUDED_FUTURE Index: ps/trunk/source/ps/tests/test_Future.h =================================================================== --- ps/trunk/source/ps/tests/test_Future.h +++ ps/trunk/source/ps/tests/test_Future.h @@ -124,4 +124,21 @@ task2(); TS_ASSERT_EQUALS(future.Get(), 7); } + + void test_move_only_function() + { + Future future; + + class MoveOnlyType + { + public: + MoveOnlyType() = default; + MoveOnlyType(MoveOnlyType&) = delete; + MoveOnlyType& operator=(MoveOnlyType&) = delete; + MoveOnlyType(MoveOnlyType&&) = default; + MoveOnlyType& operator=(MoveOnlyType&&) = default; + }; + + future.Wrap([t = MoveOnlyType{}]{}); + } };