1 #include <mbgl/actor/mailbox.hpp>
2 #include <mbgl/actor/message.hpp>
3 #include <mbgl/actor/scheduler.hpp>
4 
5 #include <cassert>
6 
7 namespace mbgl {
8 
Mailbox()9 Mailbox::Mailbox() {
10 }
11 
Mailbox(Scheduler & scheduler_)12 Mailbox::Mailbox(Scheduler& scheduler_)
13     : scheduler(&scheduler_) {
14 }
15 
open(Scheduler & scheduler_)16 void Mailbox::open(Scheduler& scheduler_) {
17     assert(!scheduler);
18 
19     // As with close(), block until neither receive() nor push() are in progress, and acquire the two
20     // mutexes in the same order.
21     std::lock_guard<std::recursive_mutex> receivingLock(receivingMutex);
22     std::lock_guard<std::mutex> pushingLock(pushingMutex);
23 
24     scheduler = &scheduler_;
25 
26     if (closed) {
27         return;
28     }
29 
30     if (!queue.empty()) {
31         (*scheduler)->schedule(shared_from_this());
32     }
33 }
34 
close()35 void Mailbox::close() {
36     // Block until neither receive() nor push() are in progress. Two mutexes are used because receive()
37     // must not block send(). Of the two, the receiving mutex must be acquired first, because that is
38     // the order that an actor will obtain them when it self-sends a message, and consistent lock
39     // acquisition order prevents deadlocks.
40     // The receiving mutex is recursive to allow a mailbox (and thus the actor) to close itself.
41     std::lock_guard<std::recursive_mutex> receivingLock(receivingMutex);
42     std::lock_guard<std::mutex> pushingLock(pushingMutex);
43 
44     closed = true;
45 }
46 
isOpen() const47 bool Mailbox::isOpen() const { return bool(scheduler); }
48 
49 
push(std::unique_ptr<Message> message)50 void Mailbox::push(std::unique_ptr<Message> message) {
51     std::lock_guard<std::mutex> pushingLock(pushingMutex);
52 
53     if (closed) {
54         return;
55     }
56 
57     std::lock_guard<std::mutex> queueLock(queueMutex);
58     bool wasEmpty = queue.empty();
59     queue.push(std::move(message));
60     if (wasEmpty && scheduler) {
61         (*scheduler)->schedule(shared_from_this());
62     }
63 }
64 
receive()65 void Mailbox::receive() {
66     std::lock_guard<std::recursive_mutex> receivingLock(receivingMutex);
67 
68     assert(scheduler);
69 
70     if (closed) {
71         return;
72     }
73 
74     std::unique_ptr<Message> message;
75     bool wasEmpty;
76 
77     {
78         std::lock_guard<std::mutex> queueLock(queueMutex);
79         assert(!queue.empty());
80         message = std::move(queue.front());
81         queue.pop();
82         wasEmpty = queue.empty();
83     }
84 
85     (*message)();
86 
87     if (!wasEmpty) {
88         (*scheduler)->schedule(shared_from_this());
89     }
90 }
91 
maybeReceive(std::weak_ptr<Mailbox> mailbox)92 void Mailbox::maybeReceive(std::weak_ptr<Mailbox> mailbox) {
93     if (auto locked = mailbox.lock()) {
94         locked->receive();
95     }
96 }
97 
98 } // namespace mbgl
99