Remove LiveTV menu.
[vuplus_xbmc] / xbmc / threads / Event.cpp
1 /*
2  *      Copyright (c) 2002 Frodo
3  *      Portions Copyright (c) by the authors of ffmpeg and xvid
4  *      Copyright (C) 2002-2013 Team XBMC
5  *      http://xbmc.org
6  *
7  *  This Program is free software; you can redistribute it and/or modify
8  *  it under the terms of the GNU General Public License as published by
9  *  the Free Software Foundation; either version 2, or (at your option)
10  *  any later version.
11  *
12  *  This Program is distributed in the hope that it will be useful,
13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  *  GNU General Public License for more details.
16  *
17  *  You should have received a copy of the GNU General Public License
18  *  along with XBMC; see the file COPYING.  If not, see
19  *  <http://www.gnu.org/licenses/>.
20  *
21  */
22
23 #include <stdarg.h>
24 #include <limits>
25
26 #include "Event.h"
27
28 void CEvent::addGroup(XbmcThreads::CEventGroup* group)
29 {
30   CSingleLock lock(groupListMutex);
31   if (groups == NULL)
32     groups = new std::vector<XbmcThreads::CEventGroup*>();
33
34   groups->push_back(group);
35 }
36
37 void CEvent::removeGroup(XbmcThreads::CEventGroup* group)
38 {
39   CSingleLock lock(groupListMutex);
40   if (groups)
41   {
42     for (std::vector<XbmcThreads::CEventGroup*>::iterator iter = groups->begin(); iter != groups->end(); iter++)
43     {
44       if ((*iter) == group)
45       {
46         groups->erase(iter);
47         break;
48       }
49     }
50
51     if (groups->size() <= 0)
52     {
53       delete groups;
54       groups = NULL;
55     }
56   }
57 }
58
59 // locking is ALWAYS done in this order:
60 //  CEvent::groupListMutex -> CEventGroup::mutex -> CEvent::mutex
61 void CEvent::Set()
62 {
63   // Originally I had this without locking. Thanks to FernetMenta who
64   // pointed out that this creates a race condition between setting
65   // checking the signal and calling wait() on the Wait call in the
66   // CEvent class. This now perfectly matches the boost example here:
67   // http://www.boost.org/doc/libs/1_41_0/doc/html/thread/synchronization.html#thread.synchronization.condvar_ref
68   {
69     CSingleLock slock(mutex);
70     signaled = true; 
71   }
72
73   condVar.notifyAll();
74
75   CSingleLock l(groupListMutex);
76   if (groups)
77   {
78     for (std::vector<XbmcThreads::CEventGroup*>::iterator iter = groups->begin(); 
79          iter != groups->end(); iter++)
80       (*iter)->Set(this);
81   }
82 }
83
84 namespace XbmcThreads
85 {
86   /**
87    * This will block until any one of the CEvents in the group are
88    * signaled at which point a pointer to that CEvents will be 
89    * returned.
90    */
91   CEvent* CEventGroup::wait() 
92   { 
93     return wait(std::numeric_limits<unsigned int>::max());
94   }
95
96   /**
97    * This will block until any one of the CEvents in the group are
98    * signaled or the timeout is reachec. If an event is signaled then
99    * it will return a pointer to that CEvent, otherwise it will return
100    * NULL.
101    */
102   // locking is ALWAYS done in this order:
103   //  CEvent::groupListMutex -> CEventGroup::mutex -> CEvent::mutex
104   //
105   // Notice that this method doesn't grab the CEvent::groupListMutex at all. This
106   // is fine. It just grabs the CEventGroup::mutex and THEN the individual 
107   // CEvent::mutex's
108   CEvent* CEventGroup::wait(unsigned int milliseconds)  
109   { 
110     CSingleLock lock(mutex); // grab CEventGroup::mutex
111     numWaits++; 
112
113     // ==================================================
114     // This block checks to see if any child events are 
115     // signaled and sets 'signaled' to the first one it
116     // finds.
117     // ==================================================
118     signaled = NULL;
119     for (std::vector<CEvent*>::iterator iter = events.begin();
120          signaled == NULL && iter != events.end(); iter++)
121     {
122       CEvent* cur = *iter;
123       if (cur->signaled) 
124         signaled = cur;
125     }
126     // ==================================================
127
128     if(!signaled)
129     {
130       // both of these release the CEventGroup::mutex
131       if (milliseconds == std::numeric_limits<unsigned int>::max())
132         condVar.wait(mutex); 
133       else
134         condVar.wait(mutex,milliseconds); 
135     } // at this point the CEventGroup::mutex is reacquired
136     numWaits--; 
137
138     // signaled should have been set by a call to CEventGroup::Set
139     CEvent* ret = signaled;
140     if (numWaits == 0) 
141     {
142       if (signaled)
143         // This acquires and releases the CEvent::mutex. This is fine since the
144         //  CEventGroup::mutex is already being held
145         signaled->WaitMSec(0); // reset the event if needed
146       signaled = NULL;  // clear the signaled if all the waiters are gone
147     }
148     return ret;
149   }
150
151   CEventGroup::CEventGroup(int num, CEvent* v1, ...) : signaled(NULL), condVar(actualCv,signaled), numWaits(0)
152   {
153     va_list ap;
154
155     va_start(ap, v1);
156     events.push_back(v1);
157     num--; // account for v1
158     for (;num > 0; num--)
159       events.push_back(va_arg(ap,CEvent*));
160     va_end(ap);
161
162     // we preping for a wait, so we need to set the group value on
163     // all of the CEvents. 
164     for (std::vector<CEvent*>::iterator iter = events.begin();
165          iter != events.end(); iter++)
166       (*iter)->addGroup(this);
167   }
168
169   CEventGroup::CEventGroup(CEvent* v1, ...) : signaled(NULL), condVar(actualCv,signaled), numWaits(0)
170   {
171     va_list ap;
172
173     va_start(ap, v1);
174     events.push_back(v1);
175     bool done = false;
176     while(!done)
177     {
178       CEvent* cur = va_arg(ap,CEvent*);
179       if (cur)
180         events.push_back(cur);
181       else
182         done = true;
183     }
184     va_end(ap);
185
186     // we preping for a wait, so we need to set the group value on
187     // all of the CEvents. 
188     for (std::vector<CEvent*>::iterator iter = events.begin();
189          iter != events.end(); iter++)
190       (*iter)->addGroup(this);
191   }
192
193   CEventGroup::~CEventGroup()
194   {
195     for (std::vector<CEvent*>::iterator iter = events.begin();
196          iter != events.end(); iter++)
197       (*iter)->removeGroup(this);
198   }
199 }