[core] implement ask pattern in actor
This commit is contained in:
committed by
Ivo van Dongen
parent
3364992be9
commit
a9ac314a5d
@@ -6,6 +6,7 @@
|
||||
#include <mbgl/util/noncopyable.hpp>
|
||||
|
||||
#include <memory>
|
||||
#include <future>
|
||||
|
||||
namespace mbgl {
|
||||
|
||||
@@ -61,6 +62,17 @@ public:
|
||||
mailbox->push(actor::makeMessage(object, fn, std::forward<Args>(args)...));
|
||||
}
|
||||
|
||||
template <typename Fn, class... Args>
|
||||
auto ask(Fn fn, Args&&... args) {
|
||||
// Result type is deduced from the function's return type
|
||||
using ResultType = typename std::result_of<decltype(fn)(Object, Args...)>::type;
|
||||
|
||||
std::promise<ResultType> promise;
|
||||
auto future = promise.get_future();
|
||||
mailbox->push(actor::makeMessage(std::move(promise), object, fn, std::forward<Args>(args)...));
|
||||
return future;
|
||||
}
|
||||
|
||||
ActorRef<std::decay_t<Object>> self() {
|
||||
return ActorRef<std::decay_t<Object>>(object, mailbox);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include <mbgl/util/optional.hpp>
|
||||
|
||||
#include <future>
|
||||
#include <utility>
|
||||
|
||||
namespace mbgl {
|
||||
@@ -36,6 +39,31 @@ public:
|
||||
ArgsTuple argsTuple;
|
||||
};
|
||||
|
||||
template <class ResultType, class Object, class MemberFn, class ArgsTuple>
|
||||
class AskMessageImpl : public Message {
|
||||
public:
|
||||
AskMessageImpl(std::promise<ResultType> promise_, Object& object_, MemberFn memberFn_, ArgsTuple argsTuple_)
|
||||
: object(object_),
|
||||
memberFn(memberFn_),
|
||||
argsTuple(std::move(argsTuple_)),
|
||||
promise(std::move(promise_)) {
|
||||
}
|
||||
|
||||
void operator()() override {
|
||||
promise.set_value(ask(std::make_index_sequence<std::tuple_size<ArgsTuple>::value>()));
|
||||
}
|
||||
|
||||
template <std::size_t... I>
|
||||
ResultType ask(std::index_sequence<I...>) {
|
||||
return (object.*memberFn)(std::move(std::get<I>(argsTuple))...);
|
||||
}
|
||||
|
||||
Object& object;
|
||||
MemberFn memberFn;
|
||||
ArgsTuple argsTuple;
|
||||
std::promise<ResultType> promise;
|
||||
};
|
||||
|
||||
namespace actor {
|
||||
|
||||
template <class Object, class MemberFn, class... Args>
|
||||
@@ -44,5 +72,11 @@ std::unique_ptr<Message> makeMessage(Object& object, MemberFn memberFn, Args&&..
|
||||
return std::make_unique<MessageImpl<Object, MemberFn, decltype(tuple)>>(object, memberFn, std::move(tuple));
|
||||
}
|
||||
|
||||
template <class ResultType, class Object, class MemberFn, class... Args>
|
||||
std::unique_ptr<Message> makeMessage(std::promise<ResultType>&& promise, Object& object, MemberFn memberFn, Args&&... args) {
|
||||
auto tuple = std::make_tuple(std::forward<Args>(args)...);
|
||||
return std::make_unique<AskMessageImpl<ResultType, Object, MemberFn, decltype(tuple)>>(std::move(promise), object, memberFn, std::move(tuple));
|
||||
}
|
||||
|
||||
} // namespace actor
|
||||
} // namespace mbgl
|
||||
|
||||
@@ -281,3 +281,27 @@ TEST(Actor, NonConcurrentMailbox) {
|
||||
test.invoke(&Test::end);
|
||||
endedFuture.wait();
|
||||
}
|
||||
|
||||
TEST(Actor, Ask) {
|
||||
// Asking for a result
|
||||
|
||||
struct Test {
|
||||
|
||||
Test(ActorRef<Test>) {}
|
||||
|
||||
int doubleIt(int i) {
|
||||
return i * 2;
|
||||
}
|
||||
};
|
||||
|
||||
ThreadPool pool { 2 };
|
||||
Actor<Test> test(pool);
|
||||
|
||||
auto result = test.ask(&Test::doubleIt, 1);
|
||||
|
||||
ASSERT_TRUE(result.valid());
|
||||
|
||||
auto status = result.wait_for(std::chrono::seconds(1));
|
||||
ASSERT_EQ(std::future_status::ready, status);
|
||||
ASSERT_EQ(2, result.get());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user