2 * Copyright (C) 2005-2018 Team Kodi
3 * This file is part of Kodi - https://kodi.tv
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 * See LICENSES/README.md for more information.
11 #include "threads/Condition.h"
13 #include <initializer_list>
18 // forward declare the CEventGroup
25 * @brief This is an Event class built from a ConditionVariable. The Event adds the state
26 * that the condition is gating as well as the mutex/lock.
28 * This Event can be 'interruptible' (even though there is only a single place
29 * in the code that uses this behavior).
31 * This class manages 'spurious returns' from the condition variable.
38 volatile bool signaled
;
39 unsigned int numWaits
= 0;
41 CCriticalSection groupListMutex
; // lock for the groups list
42 std::unique_ptr
<std::vector
<XbmcThreads::CEventGroup
*>> groups
;
44 XbmcThreads::ConditionVariable actualCv
;
45 CCriticalSection mutex
;
47 friend class XbmcThreads::CEventGroup
;
49 void addGroup(XbmcThreads::CEventGroup
* group
);
50 void removeGroup(XbmcThreads::CEventGroup
* group
);
52 // helper for the two wait methods
53 inline bool prepReturn()
56 if (!manualReset
&& numWaits
== 0)
61 CEvent(const CEvent
&) = delete;
62 CEvent
& operator=(const CEvent
&) = delete;
65 inline CEvent(bool manual
= false, bool signaled_
= false)
66 : manualReset(manual
), signaled(signaled_
)
72 std::unique_lock
<CCriticalSection
> lock(mutex
);
78 * @brief Returns true if Event has been triggered and not reset, false otherwise.
81 inline bool Signaled()
83 std::unique_lock
<CCriticalSection
> lock(mutex
);
88 * @brief This will wait up to 'duration' for the Event to be
89 * triggered. The method will return 'true' if the Event
90 * was triggered. Otherwise it will return false.
93 template<typename Rep
, typename Period
>
94 inline bool Wait(std::chrono::duration
<Rep
, Period
> duration
)
96 std::unique_lock
<CCriticalSection
> lock(mutex
);
98 actualCv
.wait(mutex
, duration
, std::bind(&CEvent::Signaled
, this));
104 * @brief This will wait for the Event to be triggered. The method will return
105 * 'true' if the Event was triggered. If it was either interrupted
106 * it will return false. Otherwise it will return false.
111 std::unique_lock
<CCriticalSection
> lock(mutex
);
113 actualCv
.wait(mutex
, std::bind(&CEvent::Signaled
, this));
119 * @brief This is mostly for testing. It allows a thread to make sure there are
120 * the right amount of other threads waiting.
123 inline int getNumWaits()
125 std::unique_lock
<CCriticalSection
> lock(mutex
);
130 namespace XbmcThreads
133 * @brief CEventGroup is a means of grouping CEvents to wait on them together.
134 * It is equivalent to WaitOnMultipleObject that returns when "any" Event
135 * in the group signaled.
140 std::vector
<CEvent
*> events
;
142 XbmcThreads::ConditionVariable actualCv
;
143 CCriticalSection mutex
;
145 unsigned int numWaits
{0};
147 // This is ONLY called from CEvent::Set.
148 inline void Set(CEvent
* child
)
150 std::unique_lock
<CCriticalSection
> l(mutex
);
152 actualCv
.notifyAll();
155 friend class ::CEvent
;
157 CEventGroup(const CEventGroup
&) = delete;
158 CEventGroup
& operator=(const CEventGroup
&) = delete;
162 * @brief Create a CEventGroup from a number of CEvents.
165 CEventGroup(std::initializer_list
<CEvent
*> events
);
170 * @brief This will block until any one of the CEvents in the group are
171 * signaled at which point a pointer to that CEvents will be
178 * @brief locking is ALWAYS done in this order:
179 * CEvent::groupListMutex -> CEventGroup::mutex -> CEvent::mutex
181 * Notice that this method doesn't grab the CEvent::groupListMutex at all. This
182 * is fine. It just grabs the CEventGroup::mutex and THEN the individual
185 template<typename Rep
, typename Period
>
186 CEvent
* wait(std::chrono::duration
<Rep
, Period
> duration
)
188 std::unique_lock
<CCriticalSection
> lock(mutex
); // grab CEventGroup::mutex
191 // ==================================================
192 // This block checks to see if any child events are
193 // signaled and sets 'signaled' to the first one it
195 // ==================================================
197 for (auto* cur
: events
)
199 std::unique_lock
<CCriticalSection
> lock2(cur
->mutex
);
203 // ==================================================
207 // both of these release the CEventGroup::mutex
208 if (duration
== std::chrono::duration
<Rep
, Period
>::max())
209 actualCv
.wait(mutex
, [this]() { return signaled
!= nullptr; });
211 actualCv
.wait(mutex
, duration
, [this]() { return signaled
!= nullptr; });
212 } // at this point the CEventGroup::mutex is reacquired
215 // signaled should have been set by a call to CEventGroup::Set
216 CEvent
* ret
= signaled
;
220 // This acquires and releases the CEvent::mutex. This is fine since the
221 // CEventGroup::mutex is already being held
222 signaled
->Wait(std::chrono::duration
<Rep
, Period
>::zero()); // reset the event if needed
223 signaled
= nullptr; // clear the signaled if all the waiters are gone
229 * @brief This is mostly for testing. It allows a thread to make sure there are
230 * the right amount of other threads waiting.
233 inline int getNumWaits()
235 std::unique_lock
<CCriticalSection
> lock(mutex
);
239 } // namespace XbmcThreads