This is a fix for the CEventGroup deadlock.
[vuplus_xbmc] / xbmc / threads / Event.h
1 /*
2  *      Copyright (C) 2005-2011 Team XBMC
3  *      http://www.xbmc.org
4  *
5  *  This Program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2, or (at your option)
8  *  any later version.
9  *
10  *  This Program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with XBMC; see the file COPYING.  If not, write to
17  *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
18  *  http://www.gnu.org/copyleft/gpl.html
19  *
20  */
21
22 #pragma once
23
24 #include <vector>
25
26 #include "threads/Condition.h"
27
28 // forward declare the CEventGroup
29 namespace XbmcThreads
30 {
31   class CEventGroup;
32 }
33
34
35 /**
36  * This is an Event class built from a ConditionVariable. The Event adds the state
37  * that the condition is gating as well as the mutex/lock.
38  *
39  * This Event can be 'interruptible' (even though there is only a single place
40  * in the code that uses this behavior).
41  *
42  * This class manages 'spurious returns' from the condition variable.
43  */
44 class CEvent
45 {
46   bool manualReset;
47   bool signaled;
48   unsigned int numWaits;
49
50   CCriticalSection groupListMutex;
51   std::vector<XbmcThreads::CEventGroup*> * groups;
52
53   /**
54    * To satisfy the TightConditionVariable requirements and allow the 
55    *  predicate being monitored to include both the signaled and interrupted
56    *  states.
57    */
58   XbmcThreads::TightConditionVariable<bool&> condVar;
59   CCriticalSection mutex;
60
61   friend class XbmcThreads::CEventGroup;
62
63   void groupSet();
64   void addGroup(XbmcThreads::CEventGroup* group);
65   void removeGroup(XbmcThreads::CEventGroup* group);
66
67   void lockGroups();
68   void unlockGroups();
69
70   class GroupLock
71   {
72     CEvent* ths;
73   public:
74     inline GroupLock(CEvent* ths_) : ths(ths_) { ths->lockGroups(); }
75     inline ~GroupLock() { ths->unlockGroups(); }
76   };
77
78   // helper for the two wait methods
79   inline bool prepReturn() { bool ret = signaled; if (!manualReset && numWaits == 0) signaled = false; return ret; }
80
81   // block the ability to copy
82   inline CEvent& operator=(const CEvent& src) { return *this; }
83   inline CEvent(const CEvent& other): condVar(signaled) {}
84
85 public:
86   inline CEvent(bool manual = false, bool signaled_ = false) : 
87     manualReset(manual), signaled(signaled_), numWaits(0), groups(NULL), condVar(signaled) {}
88
89   inline void Reset() { CSingleLock lock(mutex); signaled = false; }
90   inline void Set() { GroupLock gl(this); CSingleLock lock(mutex); signaled = true; condVar.notifyAll(); groupSet(); }
91
92   /**
93    * This will wait up to 'milliSeconds' milliseconds for the Event
94    *  to be triggered. The method will return 'true' if the Event
95    *  was triggered. Otherwise it will return false.
96    */
97   inline bool WaitMSec(unsigned int milliSeconds) 
98   { CSingleLock lock(mutex); numWaits++; condVar.wait(mutex,milliSeconds); numWaits--; return prepReturn(); }
99
100   /**
101    * This will wait for the Event to be triggered. The method will return 
102    * 'true' if the Event was triggered. If it was either interrupted
103    * it will return false. Otherwise it will return false.
104    */
105   inline bool Wait()
106   { CSingleLock lock(mutex); numWaits++; condVar.wait(mutex); numWaits--; return prepReturn(); }
107
108 };
109
110 namespace XbmcThreads
111 {
112   /**
113    * CEventGroup is a means of grouping CEvents to wait on them together.
114    * It is equivalent to WaitOnMultipleObject that returns when "any" Event
115    * in the group signaled.
116    */
117   class CEventGroup
118   {
119     std::vector<CEvent*> events;
120     CEvent* signaled;
121     XbmcThreads::TightConditionVariable<CEvent*&> condVar;
122     CCriticalSection mutex;
123
124     unsigned int numWaits;
125
126     inline void Set(CEvent* child) { signaled = child; condVar.notifyAll(); }
127
128     friend class ::CEvent;
129
130     CEvent* anyEventsSignaled();
131
132   public:
133
134     /**
135      * Create a CEventGroup from a number of CEvents. num is the number
136      *  of Events that follow. E.g.:
137      *
138      *  CEventGroup g(3, event1, event2, event3);
139      */
140     CEventGroup(int num, CEvent* v1, ...);
141
142     /**
143      * Create a CEventGroup from a number of CEvents. The parameters
144      *  should form a NULL terminated list of CEvent*'s
145      *
146      *  CEventGroup g(event1, event2, event3, NULL);
147      */
148     CEventGroup(CEvent* v1, ...);
149     ~CEventGroup();
150
151     /**
152      * This will block until any one of the CEvents in the group are
153      * signaled at which point a pointer to that CEvents will be 
154      * returned.
155      */
156     CEvent* wait();
157
158     /**
159      * This will block until any one of the CEvents in the group are
160      * signaled or the timeout is reachec. If an event is signaled then
161      * it will return a pointer to that CEvent, otherwise it will return
162      * NULL.
163      */
164     CEvent* wait(unsigned int milliseconds);
165   };
166 }