1 /*
2  *  Copyright (C) 2005-2018 Team Kodi
3  *  This file is part of Kodi - https://kodi.tv
4  *
5  *  SPDX-License-Identifier: GPL-2.0-or-later
6  *  See LICENSES/README.md for more information.
7  */
8 
9 #include "ApplicationMessenger.h"
10 
11 #include "guilib/GUIMessage.h"
12 #include "messaging/IMessageTarget.h"
13 #include "threads/SingleLock.h"
14 #include "windowing/GraphicContext.h"
15 
16 #include <memory>
17 #include <utility>
18 
19 namespace KODI
20 {
21 namespace MESSAGING
22 {
23 
24 class CDelayedMessage : public CThread
25 {
26   public:
27     CDelayedMessage(ThreadMessage& msg, unsigned int delay);
28     void Process() override;
29 
30   private:
31     unsigned int   m_delay;
32     ThreadMessage  m_msg;
33 };
34 
CDelayedMessage(ThreadMessage & msg,unsigned int delay)35 CDelayedMessage::CDelayedMessage(ThreadMessage& msg, unsigned int delay) : CThread("DelayedMessage")
36 {
37   m_msg = msg;
38 
39   m_delay = delay;
40 }
41 
Process()42 void CDelayedMessage::Process()
43 {
44   CThread::Sleep(m_delay);
45 
46   if (!m_bStop)
47     CApplicationMessenger::GetInstance().PostMsg(m_msg.dwMessage, m_msg.param1, m_msg.param1, m_msg.lpVoid, m_msg.strParam, m_msg.params);
48 }
49 
50 
GetInstance()51 CApplicationMessenger& CApplicationMessenger::GetInstance()
52 {
53   static CApplicationMessenger appMessenger;
54   return appMessenger;
55 }
56 
57 CApplicationMessenger::CApplicationMessenger() = default;
58 
~CApplicationMessenger()59 CApplicationMessenger::~CApplicationMessenger()
60 {
61   Cleanup();
62 }
63 
Cleanup()64 void CApplicationMessenger::Cleanup()
65 {
66   CSingleLock lock (m_critSection);
67 
68   while (!m_vecMessages.empty())
69   {
70     ThreadMessage* pMsg = m_vecMessages.front();
71 
72     if (pMsg->waitEvent)
73       pMsg->waitEvent->Set();
74 
75     delete pMsg;
76     m_vecMessages.pop();
77   }
78 
79   while (!m_vecWindowMessages.empty())
80   {
81     ThreadMessage* pMsg = m_vecWindowMessages.front();
82 
83     if (pMsg->waitEvent)
84       pMsg->waitEvent->Set();
85 
86     delete pMsg;
87     m_vecWindowMessages.pop();
88   }
89 }
90 
SendMsg(ThreadMessage && message,bool wait)91 int CApplicationMessenger::SendMsg(ThreadMessage&& message, bool wait)
92 {
93   std::shared_ptr<CEvent> waitEvent;
94   std::shared_ptr<int> result;
95 
96   if (wait)
97   {
98     //Initialize result here as it's not needed for posted messages
99     message.result = std::make_shared<int>(-1);
100     // check that we're not being called from our application thread, else we'll be waiting
101     // forever!
102     if (m_guiThreadId != CThread::GetCurrentThreadId())
103     {
104       message.waitEvent.reset(new CEvent(true));
105       waitEvent = message.waitEvent;
106       result = message.result;
107     }
108     else
109     {
110       //OutputDebugString("Attempting to wait on a SendMessage() from our application thread will cause lockup!\n");
111       //OutputDebugString("Sending immediately\n");
112       ProcessMessage(&message);
113       return *message.result;
114     }
115   }
116 
117 
118   if (m_bStop)
119     return -1;
120 
121   ThreadMessage* msg = new ThreadMessage(std::move(message));
122 
123   CSingleLock lock (m_critSection);
124 
125   if (msg->dwMessage == TMSG_GUI_MESSAGE)
126     m_vecWindowMessages.push(msg);
127   else
128     m_vecMessages.push(msg);
129   lock.Leave();  // this releases the lock on the vec of messages and
130                  //   allows the ProcessMessage to execute and therefore
131                  //   delete the message itself. Therefore any access
132                  //   of the message itself after this point constitutes
133                  //   a race condition (yarc - "yet another race condition")
134                  //
135   if (waitEvent) // ... it just so happens we have a spare reference to the
136                  //  waitEvent ... just for such contingencies :)
137   {
138     // ensure the thread doesn't hold the graphics lock
139     CWinSystemBase* winSystem = CServiceBroker::GetWinSystem();
140     //! @todo This won't really help as winSystem can die every single
141     // moment on shutdown. A shared ptr would be a more valid solution
142     // depending on the design dependencies.
143     if (winSystem)
144     {
145       CSingleExit exit(winSystem->GetGfxContext());
146       waitEvent->Wait();
147     }
148     return *result;
149   }
150 
151   return -1;
152 }
153 
SendMsg(uint32_t messageId)154 int CApplicationMessenger::SendMsg(uint32_t messageId)
155 {
156    return SendMsg(ThreadMessage{ messageId }, true);
157 }
158 
SendMsg(uint32_t messageId,int param1,int param2,void * payload)159 int CApplicationMessenger::SendMsg(uint32_t messageId, int param1, int param2, void* payload)
160 {
161   return SendMsg(ThreadMessage{ messageId, param1, param2, payload }, true);
162 }
163 
SendMsg(uint32_t messageId,int param1,int param2,void * payload,std::string strParam)164 int CApplicationMessenger::SendMsg(uint32_t messageId, int param1, int param2, void* payload, std::string strParam)
165 {
166   return SendMsg(ThreadMessage{messageId, param1, param2, payload, std::move(strParam),
167                                std::vector<std::string>{}},
168                  true);
169 }
170 
SendMsg(uint32_t messageId,int param1,int param2,void * payload,std::string strParam,std::vector<std::string> params)171 int CApplicationMessenger::SendMsg(uint32_t messageId, int param1, int param2, void* payload, std::string strParam, std::vector<std::string> params)
172 {
173   return SendMsg(
174       ThreadMessage{messageId, param1, param2, payload, std::move(strParam), std::move(params)},
175       true);
176 }
177 
PostMsg(uint32_t messageId)178 void CApplicationMessenger::PostMsg(uint32_t messageId)
179 {
180   SendMsg(ThreadMessage{ messageId }, false);
181 }
182 
PostMsg(uint32_t messageId,int64_t param3)183 void CApplicationMessenger::PostMsg(uint32_t messageId, int64_t param3)
184 {
185   SendMsg(ThreadMessage{ messageId, param3 }, false);
186 }
187 
PostMsg(uint32_t messageId,int param1,int param2,void * payload)188 void CApplicationMessenger::PostMsg(uint32_t messageId, int param1, int param2, void* payload)
189 {
190   SendMsg(ThreadMessage{ messageId, param1, param2, payload }, false);
191 }
192 
PostMsg(uint32_t messageId,int param1,int param2,void * payload,std::string strParam)193 void CApplicationMessenger::PostMsg(uint32_t messageId, int param1, int param2, void* payload, std::string strParam)
194 {
195   SendMsg(ThreadMessage{messageId, param1, param2, payload, std::move(strParam),
196                         std::vector<std::string>{}},
197           false);
198 }
199 
PostMsg(uint32_t messageId,int param1,int param2,void * payload,std::string strParam,std::vector<std::string> params)200 void CApplicationMessenger::PostMsg(uint32_t messageId, int param1, int param2, void* payload, std::string strParam, std::vector<std::string> params)
201 {
202   SendMsg(ThreadMessage{messageId, param1, param2, payload, std::move(strParam), std::move(params)},
203           false);
204 }
205 
ProcessMessages()206 void CApplicationMessenger::ProcessMessages()
207 {
208   // process threadmessages
209   CSingleLock lock (m_critSection);
210   while (!m_vecMessages.empty())
211   {
212     ThreadMessage* pMsg = m_vecMessages.front();
213     //first remove the message from the queue, else the message could be processed more then once
214     m_vecMessages.pop();
215 
216     //Leave here as the message might make another
217     //thread call processmessages or sendmessage
218 
219     std::shared_ptr<CEvent> waitEvent = pMsg->waitEvent;
220     lock.Leave(); // <- see the large comment in SendMessage ^
221 
222     ProcessMessage(pMsg);
223 
224     if (waitEvent)
225       waitEvent->Set();
226     delete pMsg;
227 
228     lock.Enter();
229   }
230 }
231 
ProcessMessage(ThreadMessage * pMsg)232 void CApplicationMessenger::ProcessMessage(ThreadMessage *pMsg)
233 {
234   //special case for this that we handle ourselves
235   if (pMsg->dwMessage == TMSG_CALLBACK)
236   {
237     ThreadMessageCallback *callback = static_cast<ThreadMessageCallback*>(pMsg->lpVoid);
238     callback->callback(callback->userptr);
239     return;
240   }
241 
242   CSingleLock lock(m_critSection);
243   int mask = pMsg->dwMessage & TMSG_MASK_MESSAGE;
244 
245   auto target = m_mapTargets.at(mask);
246   if (target != nullptr)
247   {
248     CSingleExit exit(m_critSection);
249     target->OnApplicationMessage(pMsg);
250   }
251 }
252 
ProcessWindowMessages()253 void CApplicationMessenger::ProcessWindowMessages()
254 {
255   CSingleLock lock (m_critSection);
256   //message type is window, process window messages
257   while (!m_vecWindowMessages.empty())
258   {
259     ThreadMessage* pMsg = m_vecWindowMessages.front();
260     //first remove the message from the queue, else the message could be processed more then once
261     m_vecWindowMessages.pop();
262 
263     // leave here in case we make more thread messages from this one
264 
265     std::shared_ptr<CEvent> waitEvent = pMsg->waitEvent;
266     lock.Leave(); // <- see the large comment in SendMessage ^
267 
268     ProcessMessage(pMsg);
269     if (waitEvent)
270       waitEvent->Set();
271     delete pMsg;
272 
273     lock.Enter();
274   }
275 }
276 
SendGUIMessage(const CGUIMessage & message,int windowID,bool waitResult)277 void CApplicationMessenger::SendGUIMessage(const CGUIMessage &message, int windowID, bool waitResult)
278 {
279   ThreadMessage tMsg(TMSG_GUI_MESSAGE);
280   tMsg.param1 = windowID == WINDOW_INVALID ? 0 : windowID;
281   tMsg.lpVoid = new CGUIMessage(message);
282   SendMsg(std::move(tMsg), waitResult);
283 }
284 
RegisterReceiver(IMessageTarget * target)285 void CApplicationMessenger::RegisterReceiver(IMessageTarget* target)
286 {
287   CSingleLock lock(m_critSection);
288   m_mapTargets.insert(std::make_pair(target->GetMessageMask(), target));
289 }
290 
291 }
292 }
293