xbmc
Event.h
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 #pragma once
10 
11 #include "threads/Condition.h"
12 
13 #include <initializer_list>
14 #include <memory>
15 #include <mutex>
16 #include <vector>
17 
18 // forward declare the CEventGroup
19 namespace XbmcThreads
20 {
21 class CEventGroup;
22 }
23 
35 class CEvent
36 {
37  bool manualReset;
38  volatile bool signaled;
39  unsigned int numWaits = 0;
40 
41  CCriticalSection groupListMutex; // lock for the groups list
42  std::unique_ptr<std::vector<XbmcThreads::CEventGroup*>> groups;
43 
45  CCriticalSection mutex;
46 
47  friend class XbmcThreads::CEventGroup;
48 
49  void addGroup(XbmcThreads::CEventGroup* group);
50  void removeGroup(XbmcThreads::CEventGroup* group);
51 
52  // helper for the two wait methods
53  inline bool prepReturn()
54  {
55  bool ret = signaled;
56  if (!manualReset && numWaits == 0)
57  signaled = false;
58  return ret;
59  }
60 
61  CEvent(const CEvent&) = delete;
62  CEvent& operator=(const CEvent&) = delete;
63 
64 public:
65  inline CEvent(bool manual = false, bool signaled_ = false)
66  : manualReset(manual), signaled(signaled_)
67  {
68  }
69 
70  inline void Reset()
71  {
72  std::unique_lock<CCriticalSection> lock(mutex);
73  signaled = false;
74  }
75  void Set();
76 
81  inline bool Signaled()
82  {
83  std::unique_lock<CCriticalSection> lock(mutex);
84  return signaled;
85  }
86 
93  template<typename Rep, typename Period>
94  inline bool Wait(std::chrono::duration<Rep, Period> duration)
95  {
96  std::unique_lock<CCriticalSection> lock(mutex);
97  numWaits++;
98  actualCv.wait(mutex, duration, std::bind(&CEvent::Signaled, this));
99  numWaits--;
100  return prepReturn();
101  }
102 
109  inline bool Wait()
110  {
111  std::unique_lock<CCriticalSection> lock(mutex);
112  numWaits++;
113  actualCv.wait(mutex, std::bind(&CEvent::Signaled, this));
114  numWaits--;
115  return prepReturn();
116  }
117 
123  inline int getNumWaits()
124  {
125  std::unique_lock<CCriticalSection> lock(mutex);
126  return numWaits;
127  }
128 };
129 
130 namespace XbmcThreads
131 {
139 {
140  std::vector<CEvent*> events;
141  CEvent* signaled{};
143  CCriticalSection mutex;
144 
145  unsigned int numWaits{0};
146 
147  // This is ONLY called from CEvent::Set.
148  inline void Set(CEvent* child)
149  {
150  std::unique_lock<CCriticalSection> l(mutex);
151  signaled = child;
152  actualCv.notifyAll();
153  }
154 
155  friend class ::CEvent;
156 
157  CEventGroup(const CEventGroup&) = delete;
158  CEventGroup& operator=(const CEventGroup&) = delete;
159 
160 public:
165  CEventGroup(std::initializer_list<CEvent*> events);
166 
167  ~CEventGroup();
168 
175  CEvent* wait();
176 
185  template<typename Rep, typename Period>
186  CEvent* wait(std::chrono::duration<Rep, Period> duration)
187  {
188  std::unique_lock<CCriticalSection> lock(mutex); // grab CEventGroup::mutex
189  numWaits++;
190 
191  // ==================================================
192  // This block checks to see if any child events are
193  // signaled and sets 'signaled' to the first one it
194  // finds.
195  // ==================================================
196  signaled = nullptr;
197  for (auto* cur : events)
198  {
199  std::unique_lock<CCriticalSection> lock2(cur->mutex);
200  if (cur->signaled)
201  signaled = cur;
202  }
203  // ==================================================
204 
205  if (!signaled)
206  {
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; });
210  else
211  actualCv.wait(mutex, duration, [this]() { return signaled != nullptr; });
212  } // at this point the CEventGroup::mutex is reacquired
213  numWaits--;
214 
215  // signaled should have been set by a call to CEventGroup::Set
216  CEvent* ret = signaled;
217  if (numWaits == 0)
218  {
219  if (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
224  }
225  return ret;
226  }
227 
233  inline int getNumWaits()
234  {
235  std::unique_lock<CCriticalSection> lock(mutex);
236  return numWaits;
237  }
238 };
239 } // namespace XbmcThreads
bool Signaled()
Returns true if Event has been triggered and not reset, false otherwise.
Definition: Event.h:81
This is an Event class built from a ConditionVariable.
Definition: Event.h:35
int getNumWaits()
This is mostly for testing.
Definition: Event.h:123
bool Wait(std::chrono::duration< Rep, Period > duration)
This will wait up to &#39;duration&#39; for the Event to be triggered.
Definition: Event.h:94
CEventGroup is a means of grouping CEvents to wait on them together.
Definition: Event.h:138
bool Wait()
This will wait for the Event to be triggered.
Definition: Event.h:109
Definition: SmartPlayList.cpp:137
This is a thin wrapper around std::condition_variable_any.
Definition: Condition.h:26
int getNumWaits()
This is mostly for testing.
Definition: Event.h:233
CEvent * wait(std::chrono::duration< Rep, Period > duration)
locking is ALWAYS done in this order: CEvent::groupListMutex -> CEventGroup::mutex -> CEvent::mutex ...
Definition: Event.h:186
Definition: RecursiveMutex.cpp:11