1 #pragma once
2 
3 #include <QScopedPointer>
4 #include <QSharedPointer>
5 
6 #include "rigtorp/SPSCQueue.h"
7 
8 #include "util/class.h"
9 #include "util/reference.h"
10 
11 // MessagePipe represents one side of a TwoWayMessagePipe. The direction of the
12 // pipe is with respect to the owner so sender and receiver are
13 // perspective-dependent.
14 template <class SenderMessageType, class ReceiverMessageType>
15 class MessagePipe {
16   public:
MessagePipe(rigtorp::SPSCQueue<SenderMessageType> & receiver_messages,rigtorp::SPSCQueue<ReceiverMessageType> & sender_messages,BaseReferenceHolder * pTwoWayMessagePipeReference)17     MessagePipe(rigtorp::SPSCQueue<SenderMessageType>& receiver_messages,
18                 rigtorp::SPSCQueue<ReceiverMessageType>& sender_messages,
19                 BaseReferenceHolder* pTwoWayMessagePipeReference)
20             : m_receiver_messages(receiver_messages),
21               m_sender_messages(sender_messages),
22               m_pTwoWayMessagePipeReference(pTwoWayMessagePipeReference) {
23     }
24 
25     // Returns the number of ReceiverMessageType messages waiting to be read by
26     // the receiver. Non-blocking.
messageCount()27     int messageCount() const {
28         return m_sender_messages.size();
29     }
30 
31     // Try to read read a ReceiverMessageType written by the receiver
32     // addressed to the sender. Non-blocking.
readMessage(ReceiverMessageType * message)33     bool readMessage(ReceiverMessageType* message) {
34         auto front = m_sender_messages.front();
35         if (!front) {
36             return false;
37         }
38         *message = std::move(*front);
39         m_sender_messages.pop();
40         return true;
41     }
42 
43     // Writes a message to the receiver and returns true on success.
writeMessage(const SenderMessageType & message)44     bool writeMessage(const SenderMessageType& message) {
45         return m_receiver_messages.try_push(message);
46     }
47 
48   private:
49     rigtorp::SPSCQueue<SenderMessageType>& m_receiver_messages;
50     rigtorp::SPSCQueue<ReceiverMessageType>& m_sender_messages;
51     QScopedPointer<BaseReferenceHolder> m_pTwoWayMessagePipeReference;
52 
53 #define COMMA ,
54     DISALLOW_COPY_AND_ASSIGN(MessagePipe<SenderMessageType COMMA ReceiverMessageType>);
55 #undef COMMA
56 };
57 
58 // TwoWayMessagePipe is a bare-bones wrapper around the above rigtorp::SPSCQueue class that
59 // facilitates non-blocking two-way communication. To keep terminology clear,
60 // there are two sides to the message pipe, the sender side and the receiver
61 // side. The non-blocking aspect of the underlying rigtorp::SPSCQueue class requires that the
62 // sender methods and target methods each only be called from a single thread
63 // The most common use-case of this class is sending and receiving messages
64 // with the callback thread without the callback thread blocking.
65 //
66 // This class is an implementation detail and cannot be instantiated
67 // directly. Use makeTwoWayMessagePipe(...) to create a two-way pipe.
68 template <class SenderMessageType, class ReceiverMessageType>
69 class TwoWayMessagePipe {
70   public:
71     // Creates a TwoWayMessagePipe with SenderMessageType and
72     // ReceiverMessageType as the message types. Returns a pair of MessagePipes,
73     // the first is the sender's pipe (sends SenderMessageType and receives
74     // ReceiverMessageType messages) and the second is the receiver's pipe
75     // (sends ReceiverMessageType and receives SenderMessageType messages).
76     static QPair<MessagePipe<SenderMessageType, ReceiverMessageType>*,
makeTwoWayMessagePipe(int sender_fifo_size,int receiver_fifo_size)77                  MessagePipe<ReceiverMessageType, SenderMessageType>*> makeTwoWayMessagePipe(
78                      int sender_fifo_size,
79                      int receiver_fifo_size) {
80         QSharedPointer<TwoWayMessagePipe<SenderMessageType, ReceiverMessageType> > pipe(
81             new TwoWayMessagePipe<SenderMessageType, ReceiverMessageType>(
82                 sender_fifo_size, receiver_fifo_size));
83 
84         return QPair<MessagePipe<SenderMessageType, ReceiverMessageType>*,
85                      MessagePipe<ReceiverMessageType, SenderMessageType>*>(
86                          new MessagePipe<SenderMessageType, ReceiverMessageType>(
87                              pipe->m_receiver_messages, pipe->m_sender_messages,
88                              new ReferenceHolder<TwoWayMessagePipe<SenderMessageType, ReceiverMessageType> >(pipe)),
89                          new MessagePipe<ReceiverMessageType, SenderMessageType>(
90                              pipe->m_sender_messages, pipe->m_receiver_messages,
91                              new ReferenceHolder<TwoWayMessagePipe<SenderMessageType, ReceiverMessageType> >(pipe)));
92     }
93 
94   private:
TwoWayMessagePipe(int sender_fifo_size,int receiver_fifo_size)95     TwoWayMessagePipe(int sender_fifo_size, int receiver_fifo_size)
96             : m_receiver_messages(receiver_fifo_size),
97               m_sender_messages(sender_fifo_size) {
98     }
99 
100     // Messages waiting to be delivered to the receiver.
101     rigtorp::SPSCQueue<SenderMessageType> m_receiver_messages;
102     // Messages waiting to be delivered to the sender.
103     rigtorp::SPSCQueue<ReceiverMessageType> m_sender_messages;
104 
105     // This #define is because the macro gets confused by the template
106     // parameters.
107 #define COMMA ,
108     DISALLOW_COPY_AND_ASSIGN(TwoWayMessagePipe<SenderMessageType COMMA ReceiverMessageType>);
109 #undef COMMA
110 };
111