CPPMyth
Library to interoperate with MythTV server
Loading...
Searching...
No Matches
mytheventhandler.cpp
1/*
2 * Copyright (C) 2014 Jean-Luc Barriere
3 *
4 * This Program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2, or (at your option)
7 * any later version.
8 *
9 * This Program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; see the file COPYING. If not, write to
16 * the Free Software Foundation, 51 Franklin Street, Fifth Floor, Boston,
17 * MA 02110-1301 USA
18 * http://www.gnu.org/copyleft/gpl.html
19 *
20 */
21
22#include "mytheventhandler.h"
23#include "private/debug.h"
24#include "proto/mythprotoevent.h"
25#include "private/os/threads/thread.h"
26#include "private/os/threads/event.h"
27#include "private/cppdef.h"
28#include "private/builtin.h"
29
30#include <vector>
31#include <map>
32#include <list>
33
34using namespace Myth;
35
40
41EventHandler::EventHandlerThread::EventHandlerThread(const std::string& server, unsigned port)
42: m_server(server)
43, m_port(port)
44{
45}
46
47EventHandler::EventHandlerThread::~EventHandlerThread()
48{
49}
50
55
56namespace Myth
57{
58 class SubscriptionHandlerThread : private OS::Thread
59 {
60 public:
61 SubscriptionHandlerThread(EventSubscriber *handle, unsigned subid);
62 virtual ~SubscriptionHandlerThread();
63 EventSubscriber *GetHandle() { return m_handle; }
64 bool IsRunning() { return OS::Thread::is_running(); }
65 void PostMessage(const EventMessagePtr& msg);
66
67 private:
68 EventSubscriber *m_handle;
69 unsigned m_subId;
70 mutable OS::Mutex m_mutex;
71 OS::Event m_queueContent;
72 std::list<EventMessagePtr> m_msgQueue;
73
74 bool Start();
75 void Stop();
76 void *process();
77 };
78}
79
80SubscriptionHandlerThread::SubscriptionHandlerThread(EventSubscriber *handle, unsigned subid)
81: OS::Thread()
82, m_handle(handle)
83, m_subId(subid)
84, m_mutex()
85, m_queueContent()
86, m_msgQueue()
87{
88 if (m_handle && Start())
89 DBG(DBG_DEBUG, "%s: subscription is started (%p:%u)\n", __FUNCTION__, m_handle, m_subId);
90 else
91 DBG(DBG_ERROR, "%s: subscription failed (%p:%u)\n", __FUNCTION__, m_handle, m_subId);
92}
93
94SubscriptionHandlerThread::~SubscriptionHandlerThread()
95{
96 Stop();
97 m_handle = nullptr;
98}
99
100bool SubscriptionHandlerThread::Start()
101{
102 if (OS::Thread::is_running())
103 return true;
104 return OS::Thread::start_thread();
105}
106
107void SubscriptionHandlerThread::Stop()
108{
109 if (OS::Thread::is_running())
110 {
111 DBG(DBG_DEBUG, "%s: subscription thread (%p:%u)\n", __FUNCTION__, m_handle, m_subId);
112 // Set stopping. don't wait as we need to signal the thread first
113 OS::Thread::stop_thread(false);
114 m_queueContent.notify_one();
115 // Wait for thread to stop
116 OS::Thread::stop_thread(true);
117 DBG(DBG_DEBUG, "%s: subscription thread (%p:%u) stopped\n", __FUNCTION__, m_handle, m_subId);
118 }
119}
120
121void SubscriptionHandlerThread::PostMessage(const EventMessagePtr& msg)
122{
123 // Critical section
124 OS::LockGuard lock(m_mutex);
125 m_msgQueue.push_back(msg);
126 m_queueContent.notify_one();
127}
128
129void *SubscriptionHandlerThread::process()
130{
131 while (!is_stopped())
132 {
133 while (!m_msgQueue.empty() && !is_stopped())
134 {
135 // Critical section
136 m_mutex.lock();
137 EventMessagePtr msg = m_msgQueue.front();
138 m_msgQueue.pop_front();
139 m_mutex.unlock();
140 // Do work
141 m_handle->HandleBackendMessage(msg);
142 }
143 // The tread is woken up by m_queueContent.Signal();
144 m_queueContent.wait();
145 }
146 return nullptr;
147}
148
153
154namespace Myth
155{
156 class BasicEventHandler : public EventHandler::EventHandlerThread, private OS::Thread
157 {
158 public:
159 BasicEventHandler(const std::string& server, unsigned port);
160 virtual ~BasicEventHandler();
161 // Implements MythEventHandlerThread
162 virtual bool Start();
163 virtual void Stop();
164 virtual void Reset();
165 virtual bool HasStarted();
166 virtual bool IsConnected();
167 virtual unsigned CreateSubscription(EventSubscriber *sub);
168 virtual bool SubscribeForEvent(unsigned subid, EVENT_t event);
169 virtual void RevokeSubscription(unsigned subid);
170 virtual void RevokeAllSubscriptions(EventSubscriber *sub);
171
172 private:
173 OS::Mutex m_mutex;
174 ProtoEvent *m_event;
175 bool m_reset;
176 // About subscriptions
177 typedef std::map<EVENT_t, std::list<unsigned> > subscriptionsByEvent_t;
178 subscriptionsByEvent_t m_subscriptionsByEvent;
179 typedef std::map<unsigned, SubscriptionHandlerThread*> subscriptions_t;
180 subscriptions_t m_subscriptions;
181
182 void DispatchEvent(const EventMessagePtr& msg);
183 virtual void* process(void);
184 void AnnounceStatus(const char *status);
185 void AnnounceTimer();
186 void RetryConnect();
187 };
188}
189
190BasicEventHandler::BasicEventHandler(const std::string& server, unsigned port)
191: EventHandlerThread(server, port), OS::Thread()
192, m_event(new ProtoEvent(server,port))
193, m_reset(false)
194{
195}
196
197BasicEventHandler::~BasicEventHandler()
198{
199 Stop();
200 {
201 OS::LockGuard lock(m_mutex);
202 for (subscriptions_t::iterator it = m_subscriptions.begin(); it != m_subscriptions.end(); ++it)
203 delete it->second;
204 m_subscriptions.clear();
205 m_subscriptionsByEvent.clear();
206 }
207 SAFE_DELETE(m_event);
208}
209
210bool BasicEventHandler::Start()
211{
212 if (OS::Thread::is_running())
213 return true;
214 return OS::Thread::start_thread();
215}
216
217void BasicEventHandler::Stop()
218{
219 if (OS::Thread::is_running())
220 {
221 DBG(DBG_DEBUG, "%s: event handler thread (%p)\n", __FUNCTION__, this);
222 OS::Thread::stop_thread(true);
223 DBG(DBG_DEBUG, "%s: event handler thread (%p) stopped\n", __FUNCTION__, this);
224 }
225 if (m_event->IsOpen())
226 m_event->Close();
227}
228
229void BasicEventHandler::Reset()
230{
231 // Hold reset
232 m_reset = true;
233}
234
235bool BasicEventHandler::HasStarted()
236{
237 return OS::Thread::is_running();
238}
239
240bool BasicEventHandler::IsConnected()
241{
242 return m_event->IsOpen();
243}
244
245unsigned BasicEventHandler::CreateSubscription(EventSubscriber* sub)
246{
247 unsigned id = 0;
248 OS::LockGuard lock(m_mutex);
249 subscriptions_t::const_reverse_iterator it = m_subscriptions.rbegin();
250 if (it != m_subscriptions.rend())
251 id = it->first;
252 SubscriptionHandlerThread *handler = new SubscriptionHandlerThread(sub, ++id);
253 if (handler->IsRunning())
254 {
255 m_subscriptions.insert(std::make_pair(id, handler));
256 return id;
257 }
258 // Handler didn't start
259 delete handler;
260 return 0;
261}
262
263bool BasicEventHandler::SubscribeForEvent(unsigned subid, EVENT_t event)
264{
265 OS::LockGuard lock(m_mutex);
266 // Only for registered subscriber
267 if (m_subscriptions.find(subid) == m_subscriptions.end())
268 return false;
269 std::list<unsigned>& sevt = m_subscriptionsByEvent[event];
270 std::list<unsigned>::const_iterator it = sevt.begin();
271 while (it != sevt.end())
272 {
273 if (*it == subid)
274 return true;
275 ++it;
276 }
277 sevt.push_back(subid);
278 return true;
279}
280
281void BasicEventHandler::RevokeSubscription(unsigned subid)
282{
283 OS::LockGuard lock(m_mutex);
284 subscriptions_t::iterator it = m_subscriptions.find(subid);
285 if (it != m_subscriptions.end())
286 {
287 delete it->second;
288 m_subscriptions.erase(it);
289 }
290}
291
292void BasicEventHandler::RevokeAllSubscriptions(EventSubscriber *sub)
293{
294 OS::LockGuard lock(m_mutex);
295 std::vector<subscriptions_t::iterator> its;
296 for (subscriptions_t::iterator it = m_subscriptions.begin(); it != m_subscriptions.end(); ++it)
297 {
298 if (sub == it->second->GetHandle())
299 its.push_back(it);
300 }
301 for (std::vector<subscriptions_t::iterator>::const_iterator it = its.begin(); it != its.end(); ++it)
302 {
303 delete (*it)->second;
304 m_subscriptions.erase(*it);
305 }
306}
307
308void BasicEventHandler::DispatchEvent(const EventMessagePtr& msg)
309{
310 OS::LockGuard lock(m_mutex);
311 std::vector<std::list<unsigned>::iterator> revoked;
312 std::list<unsigned>& sevt = m_subscriptionsByEvent[msg->event];
313 std::list<unsigned>::iterator itsevt = sevt.begin();
314 std::list<unsigned>::iterator itsend = sevt.end();
315 while (itsevt != itsend)
316 {
317 subscriptions_t::const_iterator itsub = m_subscriptions.find(*itsevt);
318 if (itsub != m_subscriptions.end())
319 itsub->second->PostMessage(msg);
320 else
321 revoked.push_back(itsevt);
322 ++itsevt;
323 }
324 std::vector<std::list<unsigned>::iterator>::const_iterator itr;
325 for (itr = revoked.begin(); itr != revoked.end(); ++itr)
326 m_subscriptionsByEvent[msg->event].erase(*itr);
327}
328
329void *BasicEventHandler::process()
330{
331 // Try to connect
332 if (m_event->Open())
333 AnnounceStatus(EVENTHANDLER_CONNECTED);
334 while (!OS::Thread::is_stopped())
335 {
336 int r;
337 EventMessage *msg = nullptr;
338 r = m_event->RcvBackendMessage(EVENTHANDLER_TIMEOUT, &msg);
339 if (r > 0)
340 DispatchEvent(EventMessagePtr(msg));
341 else if (r < 0)
342 {
343 AnnounceStatus(EVENTHANDLER_DISCONNECTED);
344 RetryConnect();
345 }
346 else
347 {
348 AnnounceTimer();
349 // Reconnect if any held reset
350 if (m_reset)
351 {
352 m_reset = false;
353 m_event->Close();
354 RetryConnect();
355 }
356 }
357 }
358 AnnounceStatus(EVENTHANDLER_STOPPED);
359 // Close connection
360 m_event->Close();
361 return nullptr;
362}
363
364void BasicEventHandler::AnnounceStatus(const char *status)
365{
366 DBG(DBG_DEBUG, "%s: (%p) %s\n", __FUNCTION__, this, status);
367 EventMessage *msg = new EventMessage();
368 msg->event = EVENT_HANDLER_STATUS;
369 msg->subject.push_back(status);
370 msg->subject.push_back(m_server);
371 DispatchEvent(EventMessagePtr(msg));
372}
373
374void BasicEventHandler::AnnounceTimer()
375{
376 EventMessage *msg = new EventMessage();
377 msg->event = EVENT_HANDLER_TIMER;
378 msg->subject.push_back("");
379 DispatchEvent(EventMessagePtr(msg));
380}
381
382void BasicEventHandler::RetryConnect()
383{
384 int c = 0;
385 while (!OS::Thread::is_stopped())
386 {
387 if (--c < 0)
388 {
389 if (m_event->Open())
390 {
391 AnnounceStatus(EVENTHANDLER_CONNECTED);
392 m_reset = false; // Release to break any loop
393 break;
394 }
395 c = 10; // Retry after 5 seconds
396 DBG(DBG_INFO, "%s: could not open event socket (%d)\n", __FUNCTION__, m_event->GetSocketErrNo());
397 AnnounceStatus(EVENTHANDLER_NOTCONNECTED);
398 }
399 usleep(500000);
400 }
401}
402
407
408EventHandler::EventHandler(const std::string& server, unsigned port)
409: m_imp()
410{
411 // Choose implementation
412 m_imp = EventHandlerThreadPtr(new BasicEventHandler(server, port));
413}
This is the main namespace that encloses all public classes.
Definition mythcontrol.h:30