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