This repository has been archived by the owner on Aug 8, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[core] Use an actor model for tile worker concurrency
- Loading branch information
1 parent
0bd66d4
commit 41bbd4e
Showing
41 changed files
with
1,191 additions
and
818 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
#pragma once | ||
|
||
#include <mbgl/actor/mailbox.hpp> | ||
#include <mbgl/actor/message.hpp> | ||
#include <mbgl/actor/actor_ref.hpp> | ||
#include <mbgl/util/noncopyable.hpp> | ||
|
||
#include <memory> | ||
|
||
namespace mbgl { | ||
|
||
/* | ||
An `Actor<O>` is an owning reference to an asynchronous object of type `O`: an "actor". | ||
Communication with an actor happens via message passing: you send a message to the object | ||
(using `invoke`), passing a pointer to the member function to call and arguments which | ||
are then forwarded to the actor. | ||
The actor receives messages sent to it asynchronously, in a manner defined its `Scheduler`. | ||
To store incoming messages before their receipt, each actor has a `Mailbox`, which acts as | ||
a FIFO queue. Messages sent from actor S to actor R are guaranteed to be processed in the | ||
order sent. However, relative order of messages sent by two *different* actors S1 and S2 | ||
to R is *not* guaranteed (and can't be: S1 and S2 may be acting asynchronously with respect | ||
to each other). | ||
Construction and destruction of an actor is currently synchronous: the corresponding `O` | ||
object is constructed synchronously by the `Actor` constructor, and destructed synchronously | ||
by the `~Actor` destructor, after ensuring that the `O` is not currently receiving an | ||
asynchronous message. (Construction and destruction may change to be asynchronous in the | ||
future.) | ||
An `Actor<O>` can be converted to an `ActorRef<O>`, a non-owning value object representing | ||
a (weak) reference to the actor. Messages can be sent via the `Ref` as well. | ||
It's safe -- and encouraged -- to pass `Ref`s between actors via messages. This is how two-way | ||
communication and other forms of collaboration between multiple actors is accomplished. | ||
It's safe for a `Ref` to outlive its `Actor` -- the reference is "weak", and does not extend | ||
the lifetime of the owning Actor, and sending a message to a `Ref` whose `Actor` has died is | ||
a no-op. (In the future, a dead-letters queue or log may be implemented.) | ||
Please don't send messages that contain shared pointers or references. That subverts the | ||
purpose of the actor model: prohibiting direct concurrent access to shared state. | ||
*/ | ||
|
||
template <class Object> | ||
class Actor : public util::noncopyable { | ||
public: | ||
template <class... Args> | ||
Actor(Scheduler& scheduler, Args&&... args_) | ||
: mailbox(std::make_shared<Mailbox>(scheduler)), | ||
object(self(), std::forward<Args>(args_)...) { | ||
} | ||
|
||
~Actor() { | ||
mailbox->close(); | ||
} | ||
|
||
template <typename Fn, class... Args> | ||
void invoke(Fn fn, Args&&... args) { | ||
mailbox->push(actor::makeMessage(object, fn, std::forward<Args>(args)...)); | ||
} | ||
|
||
ActorRef<std::decay_t<Object>> self() { | ||
return ActorRef<std::decay_t<Object>>(object, mailbox); | ||
} | ||
|
||
operator ActorRef<std::decay_t<Object>>() { | ||
return self(); | ||
} | ||
|
||
private: | ||
std::shared_ptr<Mailbox> mailbox; | ||
Object object; | ||
}; | ||
|
||
} // namespace mbgl |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
#pragma once | ||
|
||
#include <mbgl/actor/mailbox.hpp> | ||
#include <mbgl/actor/message.hpp> | ||
|
||
#include <memory> | ||
|
||
namespace mbgl { | ||
|
||
/* | ||
An `ActorRef<O>` is a *non*-owning, weak reference to an actor of type `O`. You can send it | ||
messages just like an `Actor<O>`. It's a value object: safe to copy and pass between actors | ||
via messages. | ||
An `ActorRef<O>` does not extend the lifetime of the corresponding `Actor<O>`. That's determined | ||
entirely by whichever object owns the `Actor<O>` -- the actor's "supervisor". | ||
It's safe for a `Ref` to outlive its `Actor` -- the reference is "weak", and does not extend | ||
the lifetime of the owning Actor, and sending a message to a `Ref` whose `Actor` has died is | ||
a no-op. (In the future, a dead-letters queue or log may be implemented.) | ||
*/ | ||
|
||
template <class Object> | ||
class ActorRef { | ||
public: | ||
ActorRef(Object& object_, std::weak_ptr<Mailbox> weakMailbox_) | ||
: object(object_), | ||
weakMailbox(std::move(weakMailbox_)) { | ||
} | ||
|
||
template <typename Fn, class... Args> | ||
void invoke(Fn fn, Args&&... args) { | ||
if (auto mailbox = weakMailbox.lock()) { | ||
mailbox->push(actor::makeMessage(object, fn, std::forward<Args>(args)...)); | ||
} | ||
} | ||
|
||
private: | ||
Object& object; | ||
std::weak_ptr<Mailbox> weakMailbox; | ||
}; | ||
|
||
} // namespace mbgl |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
#include <mbgl/actor/mailbox.hpp> | ||
#include <mbgl/actor/message.hpp> | ||
#include <mbgl/actor/scheduler.hpp> | ||
|
||
#include <cassert> | ||
|
||
namespace mbgl { | ||
|
||
Mailbox::Mailbox(Scheduler& scheduler_) | ||
: scheduler(scheduler_) { | ||
} | ||
|
||
void Mailbox::push(std::unique_ptr<Message> message) { | ||
assert(!closing); | ||
|
||
std::lock_guard<std::mutex> queueLock(queueMutex); | ||
bool wasEmpty = queue.empty(); | ||
queue.push(std::move(message)); | ||
if (wasEmpty) { | ||
scheduler.schedule(shared_from_this()); | ||
} | ||
} | ||
|
||
void Mailbox::close() { | ||
// Block until the scheduler is guaranteed not to be executing receive(). | ||
std::lock_guard<std::mutex> closingLock(closingMutex); | ||
closing = true; | ||
} | ||
|
||
void Mailbox::receive() { | ||
std::lock_guard<std::mutex> closingLock(closingMutex); | ||
|
||
if (closing) { | ||
return; | ||
} | ||
|
||
std::unique_ptr<Message> message; | ||
bool wasEmpty; | ||
|
||
{ | ||
std::lock_guard<std::mutex> queueLock(queueMutex); | ||
assert(!queue.empty()); | ||
message = std::move(queue.front()); | ||
queue.pop(); | ||
wasEmpty = queue.empty(); | ||
} | ||
|
||
(*message)(); | ||
|
||
if (!wasEmpty) { | ||
scheduler.schedule(shared_from_this()); | ||
} | ||
} | ||
|
||
} // namespace mbgl |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
#pragma once | ||
|
||
#include <memory> | ||
#include <mutex> | ||
#include <queue> | ||
|
||
namespace mbgl { | ||
|
||
class Scheduler; | ||
class Message; | ||
|
||
class Mailbox : public std::enable_shared_from_this<Mailbox> { | ||
public: | ||
Mailbox(Scheduler&); | ||
|
||
void push(std::unique_ptr<Message>); | ||
|
||
void close(); | ||
void receive(); | ||
|
||
private: | ||
Scheduler& scheduler; | ||
|
||
std::mutex closingMutex; | ||
bool closing { false }; | ||
|
||
std::mutex queueMutex; | ||
std::queue<std::unique_ptr<Message>> queue; | ||
}; | ||
|
||
} // namespace mbgl |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
#pragma once | ||
|
||
#include <utility> | ||
|
||
namespace mbgl { | ||
|
||
// A movable type-erasing function wrapper. This allows to store arbitrary invokable | ||
// things (like std::function<>, or the result of a movable-only std::bind()) in the queue. | ||
// Source: http://stackoverflow.com/a/29642072/331379 | ||
class Message { | ||
public: | ||
virtual ~Message() = default; | ||
virtual void operator()() = 0; | ||
}; | ||
|
||
template <class Object, class MemberFn, class ArgsTuple> | ||
class MessageImpl : public Message { | ||
public: | ||
MessageImpl(Object& object_, MemberFn memberFn_, ArgsTuple argsTuple_) | ||
: object(object_), | ||
memberFn(memberFn_), | ||
argsTuple(std::move(argsTuple_)) { | ||
} | ||
|
||
void operator()() override { | ||
invoke(std::make_index_sequence<std::tuple_size<ArgsTuple>::value>()); | ||
} | ||
|
||
template <std::size_t... I> | ||
void invoke(std::index_sequence<I...>) { | ||
(object.*memberFn)(std::move(std::get<I>(argsTuple))...); | ||
} | ||
|
||
Object& object; | ||
MemberFn memberFn; | ||
ArgsTuple argsTuple; | ||
}; | ||
|
||
namespace actor { | ||
|
||
template <class Object, class MemberFn, class... Args> | ||
std::unique_ptr<Message> makeMessage(Object& object, MemberFn memberFn, Args&&... args) { | ||
auto tuple = std::make_tuple(std::forward<Args>(args)...); | ||
return std::make_unique<MessageImpl<Object, MemberFn, decltype(tuple)>>(object, memberFn, std::move(tuple)); | ||
} | ||
|
||
} // namespace actor | ||
} // namespace mbgl |
Oops, something went wrong.