summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIvo van Dongen <info@ivovandongen.nl>2017-07-21 11:26:40 +0300
committerIvo van Dongen <ivovandongen@users.noreply.github.com>2017-07-24 19:30:02 +0300
commit81e9ed7fdd65ca4816ac6a76139d6c18d5182a21 (patch)
tree17fbcc599c43394afd24c73a83bd0f8a7fc4cce9
parenta9ac314a5d28bec2a26cd6889364f81f811d4cd4 (diff)
downloadqtlocation-mapboxgl-81e9ed7fdd65ca4816ac6a76139d6c18d5182a21.tar.gz
[core] add ask pattern to actor ref
-rw-r--r--include/mbgl/actor/actor_ref.hpp21
-rw-r--r--test/actor/actor_ref.test.cpp58
2 files changed, 76 insertions, 3 deletions
diff --git a/include/mbgl/actor/actor_ref.hpp b/include/mbgl/actor/actor_ref.hpp
index aeb5bb4507..958ee3777c 100644
--- a/include/mbgl/actor/actor_ref.hpp
+++ b/include/mbgl/actor/actor_ref.hpp
@@ -35,6 +35,27 @@ public:
}
}
+ 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();
+
+ if (auto mailbox = weakMailbox.lock()) {
+ mailbox->push(
+ actor::makeMessage(
+ std::move(promise), *object, fn, std::forward<Args>(args)...
+ )
+ );
+ } else {
+ promise.set_exception(std::make_exception_ptr(std::runtime_error("Actor has gone away")));
+ }
+
+ return future;
+ }
+
private:
Object* object;
std::weak_ptr<Mailbox> weakMailbox;
diff --git a/test/actor/actor_ref.test.cpp b/test/actor/actor_ref.test.cpp
index 78721c965e..52b0de295b 100644
--- a/test/actor/actor_ref.test.cpp
+++ b/test/actor/actor_ref.test.cpp
@@ -3,12 +3,9 @@
#include <mbgl/test/util.hpp>
-#include <chrono>
-#include <functional>
#include <future>
using namespace mbgl;
-using namespace std::chrono_literals;
TEST(ActorRef, CanOutliveActor) {
// An ActorRef can outlive its actor. Doing does not extend the actor's lifetime.
@@ -40,3 +37,58 @@ TEST(ActorRef, CanOutliveActor) {
EXPECT_TRUE(died);
test.invoke(&Test::receive);
}
+
+TEST(ActorRef, Ask) {
+ // Ask returns a Future eventually returning the result
+
+ struct Test {
+
+ Test(ActorRef<Test>) {}
+
+ int gimme() {
+ return 20;
+ }
+
+ int echo(int i) {
+ return i;
+ }
+ };
+
+ ThreadPool pool { 1 };
+ Actor<Test> actor(pool);
+ ActorRef<Test> ref = actor.self();
+
+ EXPECT_EQ(20, ref.ask(&Test::gimme).get());
+ EXPECT_EQ(30, ref.ask(&Test::echo, 30).get());
+}
+
+
+TEST(ActorRef, AskOnDestroyedActor) {
+ // Tests behavior when calling ask() after the
+ // Actor has gone away. Should set a exception_ptr.
+
+ struct Test {
+ bool& died;
+
+ Test(ActorRef<Test>, bool& died_) : died(died_) {}
+
+ ~Test() {
+ died = true;
+ }
+
+ int receive() {
+ return 1;
+ }
+ };
+ bool died = false;
+
+ ThreadPool pool { 1 };
+ auto actor = std::make_unique<Actor<Test>>(pool, died);
+ ActorRef<Test> ref = actor->self();
+
+ actor.reset();
+ EXPECT_TRUE(died);
+
+ auto result = ref.ask(&Test::receive);
+ EXPECT_ANY_THROW(result.get());
+}