Counter Strike : Global Offensive Source Code
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

397 lines
10 KiB

  1. // wait.cpp - written and placed in the public domain by Wei Dai
  2. #include "pch.h"
  3. #include "wait.h"
  4. #include "misc.h"
  5. #ifdef SOCKETS_AVAILABLE
  6. #ifdef USE_BERKELEY_STYLE_SOCKETS
  7. #include <errno.h>
  8. #include <sys/types.h>
  9. #include <sys/time.h>
  10. #include <unistd.h>
  11. #endif
  12. NAMESPACE_BEGIN(CryptoPP)
  13. unsigned int WaitObjectContainer::MaxWaitObjects()
  14. {
  15. #ifdef USE_WINDOWS_STYLE_SOCKETS
  16. return MAXIMUM_WAIT_OBJECTS * (MAXIMUM_WAIT_OBJECTS-1);
  17. #else
  18. return FD_SETSIZE;
  19. #endif
  20. }
  21. WaitObjectContainer::WaitObjectContainer(WaitObjectsTracer* tracer)
  22. : m_tracer(tracer), m_eventTimer(Timer::MILLISECONDS)
  23. , m_sameResultCount(0), m_noWaitTimer(Timer::MILLISECONDS)
  24. {
  25. Clear();
  26. m_eventTimer.StartTimer();
  27. }
  28. void WaitObjectContainer::Clear()
  29. {
  30. #ifdef USE_WINDOWS_STYLE_SOCKETS
  31. m_handles.clear();
  32. #else
  33. m_maxFd = 0;
  34. FD_ZERO(&m_readfds);
  35. FD_ZERO(&m_writefds);
  36. #endif
  37. m_noWait = false;
  38. m_firstEventTime = 0;
  39. }
  40. inline void WaitObjectContainer::SetLastResult(LastResultType result)
  41. {
  42. if (result == m_lastResult)
  43. m_sameResultCount++;
  44. else
  45. {
  46. m_lastResult = result;
  47. m_sameResultCount = 0;
  48. }
  49. }
  50. void WaitObjectContainer::DetectNoWait(LastResultType result, CallStack const& callStack)
  51. {
  52. if (result == m_lastResult && m_noWaitTimer.ElapsedTime() > 1000)
  53. {
  54. if (m_sameResultCount > m_noWaitTimer.ElapsedTime())
  55. {
  56. if (m_tracer)
  57. {
  58. std::string desc = "No wait loop detected - m_lastResult: ";
  59. desc.append(IntToString(m_lastResult)).append(", call stack:");
  60. for (CallStack const* cs = &callStack; cs; cs = cs->Prev())
  61. desc.append("\n- ").append(cs->Format());
  62. m_tracer->TraceNoWaitLoop(desc);
  63. }
  64. try { throw 0; } catch (...) {} // help debugger break
  65. }
  66. m_noWaitTimer.StartTimer();
  67. m_sameResultCount = 0;
  68. }
  69. }
  70. void WaitObjectContainer::SetNoWait(CallStack const& callStack)
  71. {
  72. DetectNoWait(LASTRESULT_NOWAIT, CallStack("WaitObjectContainer::SetNoWait()", &callStack));
  73. m_noWait = true;
  74. }
  75. void WaitObjectContainer::ScheduleEvent(double milliseconds, CallStack const& callStack)
  76. {
  77. if (milliseconds <= 3)
  78. DetectNoWait(LASTRESULT_SCHEDULED, CallStack("WaitObjectContainer::ScheduleEvent()", &callStack));
  79. double thisEventTime = m_eventTimer.ElapsedTimeAsDouble() + milliseconds;
  80. if (!m_firstEventTime || thisEventTime < m_firstEventTime)
  81. m_firstEventTime = thisEventTime;
  82. }
  83. #ifdef USE_WINDOWS_STYLE_SOCKETS
  84. struct WaitingThreadData
  85. {
  86. bool waitingToWait, terminate;
  87. HANDLE startWaiting, stopWaiting;
  88. const HANDLE *waitHandles;
  89. unsigned int count;
  90. HANDLE threadHandle;
  91. DWORD threadId;
  92. DWORD* error;
  93. };
  94. WaitObjectContainer::~WaitObjectContainer()
  95. {
  96. try // don't let exceptions escape destructor
  97. {
  98. if (!m_threads.empty())
  99. {
  100. HANDLE threadHandles[MAXIMUM_WAIT_OBJECTS];
  101. unsigned int i;
  102. for (i=0; i<m_threads.size(); i++)
  103. {
  104. WaitingThreadData &thread = *m_threads[i];
  105. while (!thread.waitingToWait) // spin until thread is in the initial "waiting to wait" state
  106. Sleep(0);
  107. thread.terminate = true;
  108. threadHandles[i] = thread.threadHandle;
  109. }
  110. PulseEvent(m_startWaiting);
  111. ::WaitForMultipleObjects((DWORD)m_threads.size(), threadHandles, TRUE, INFINITE);
  112. for (i=0; i<m_threads.size(); i++)
  113. CloseHandle(threadHandles[i]);
  114. CloseHandle(m_startWaiting);
  115. CloseHandle(m_stopWaiting);
  116. }
  117. }
  118. catch (...)
  119. {
  120. }
  121. }
  122. void WaitObjectContainer::AddHandle(HANDLE handle, CallStack const& callStack)
  123. {
  124. DetectNoWait(m_handles.size(), CallStack("WaitObjectContainer::AddHandle()", &callStack));
  125. m_handles.push_back(handle);
  126. }
  127. DWORD WINAPI WaitingThread(LPVOID lParam)
  128. {
  129. std::auto_ptr<WaitingThreadData> pThread((WaitingThreadData *)lParam);
  130. WaitingThreadData &thread = *pThread;
  131. std::vector<HANDLE> handles;
  132. while (true)
  133. {
  134. thread.waitingToWait = true;
  135. ::WaitForSingleObject(thread.startWaiting, INFINITE);
  136. thread.waitingToWait = false;
  137. if (thread.terminate)
  138. break;
  139. if (!thread.count)
  140. continue;
  141. handles.resize(thread.count + 1);
  142. handles[0] = thread.stopWaiting;
  143. std::copy(thread.waitHandles, thread.waitHandles+thread.count, handles.begin()+1);
  144. DWORD result = ::WaitForMultipleObjects((DWORD)handles.size(), &handles[0], FALSE, INFINITE);
  145. if (result == WAIT_OBJECT_0)
  146. continue; // another thread finished waiting first, so do nothing
  147. SetEvent(thread.stopWaiting);
  148. if (!(result > WAIT_OBJECT_0 && result < WAIT_OBJECT_0 + handles.size()))
  149. {
  150. assert(!"error in WaitingThread"); // break here so we can see which thread has an error
  151. *thread.error = ::GetLastError();
  152. }
  153. }
  154. return S_OK; // return a value here to avoid compiler warning
  155. }
  156. void WaitObjectContainer::CreateThreads(unsigned int count)
  157. {
  158. size_t currentCount = m_threads.size();
  159. if (currentCount == 0)
  160. {
  161. m_startWaiting = ::CreateEvent(NULL, TRUE, FALSE, NULL);
  162. m_stopWaiting = ::CreateEvent(NULL, TRUE, FALSE, NULL);
  163. }
  164. if (currentCount < count)
  165. {
  166. m_threads.resize(count);
  167. for (size_t i=currentCount; i<count; i++)
  168. {
  169. m_threads[i] = new WaitingThreadData;
  170. WaitingThreadData &thread = *m_threads[i];
  171. thread.terminate = false;
  172. thread.startWaiting = m_startWaiting;
  173. thread.stopWaiting = m_stopWaiting;
  174. thread.waitingToWait = false;
  175. thread.threadHandle = CreateThread(NULL, 0, &WaitingThread, &thread, 0, &thread.threadId);
  176. }
  177. }
  178. }
  179. bool WaitObjectContainer::Wait(unsigned long milliseconds)
  180. {
  181. if (m_noWait || (m_handles.empty() && !m_firstEventTime))
  182. {
  183. SetLastResult(LASTRESULT_NOWAIT);
  184. return true;
  185. }
  186. bool timeoutIsScheduledEvent = false;
  187. if (m_firstEventTime)
  188. {
  189. double timeToFirstEvent = SaturatingSubtract(m_firstEventTime, m_eventTimer.ElapsedTimeAsDouble());
  190. if (timeToFirstEvent <= milliseconds)
  191. {
  192. milliseconds = (unsigned long)timeToFirstEvent;
  193. timeoutIsScheduledEvent = true;
  194. }
  195. if (m_handles.empty() || !milliseconds)
  196. {
  197. if (milliseconds)
  198. Sleep(milliseconds);
  199. SetLastResult(timeoutIsScheduledEvent ? LASTRESULT_SCHEDULED : LASTRESULT_TIMEOUT);
  200. return timeoutIsScheduledEvent;
  201. }
  202. }
  203. if (m_handles.size() > MAXIMUM_WAIT_OBJECTS)
  204. {
  205. // too many wait objects for a single WaitForMultipleObjects call, so use multiple threads
  206. static const unsigned int WAIT_OBJECTS_PER_THREAD = MAXIMUM_WAIT_OBJECTS-1;
  207. unsigned int nThreads = (unsigned int)((m_handles.size() + WAIT_OBJECTS_PER_THREAD - 1) / WAIT_OBJECTS_PER_THREAD);
  208. if (nThreads > MAXIMUM_WAIT_OBJECTS) // still too many wait objects, maybe implement recursive threading later?
  209. throw Err("WaitObjectContainer: number of wait objects exceeds limit");
  210. CreateThreads(nThreads);
  211. DWORD error = S_OK;
  212. for (unsigned int i=0; i<m_threads.size(); i++)
  213. {
  214. WaitingThreadData &thread = *m_threads[i];
  215. while (!thread.waitingToWait) // spin until thread is in the initial "waiting to wait" state
  216. Sleep(0);
  217. if (i<nThreads)
  218. {
  219. thread.waitHandles = &m_handles[i*WAIT_OBJECTS_PER_THREAD];
  220. thread.count = UnsignedMin(WAIT_OBJECTS_PER_THREAD, m_handles.size() - i*WAIT_OBJECTS_PER_THREAD);
  221. thread.error = &error;
  222. }
  223. else
  224. thread.count = 0;
  225. }
  226. ResetEvent(m_stopWaiting);
  227. PulseEvent(m_startWaiting);
  228. DWORD result = ::WaitForSingleObject(m_stopWaiting, milliseconds);
  229. if (result == WAIT_OBJECT_0)
  230. {
  231. if (error == S_OK)
  232. return true;
  233. else
  234. throw Err("WaitObjectContainer: WaitForMultipleObjects in thread failed with error " + IntToString(error));
  235. }
  236. SetEvent(m_stopWaiting);
  237. if (result == WAIT_TIMEOUT)
  238. {
  239. SetLastResult(timeoutIsScheduledEvent ? LASTRESULT_SCHEDULED : LASTRESULT_TIMEOUT);
  240. return timeoutIsScheduledEvent;
  241. }
  242. else
  243. throw Err("WaitObjectContainer: WaitForSingleObject failed with error " + IntToString(::GetLastError()));
  244. }
  245. else
  246. {
  247. #if TRACE_WAIT
  248. static Timer t(Timer::MICROSECONDS);
  249. static unsigned long lastTime = 0;
  250. unsigned long timeBeforeWait = t.ElapsedTime();
  251. #endif
  252. DWORD result = ::WaitForMultipleObjects((DWORD)m_handles.size(), &m_handles[0], FALSE, milliseconds);
  253. #if TRACE_WAIT
  254. if (milliseconds > 0)
  255. {
  256. unsigned long timeAfterWait = t.ElapsedTime();
  257. OutputDebugString(("Handles " + IntToString(m_handles.size()) + ", Woke up by " + IntToString(result-WAIT_OBJECT_0) + ", Busied for " + IntToString(timeBeforeWait-lastTime) + " us, Waited for " + IntToString(timeAfterWait-timeBeforeWait) + " us, max " + IntToString(milliseconds) + "ms\n").c_str());
  258. lastTime = timeAfterWait;
  259. }
  260. #endif
  261. if (result >= WAIT_OBJECT_0 && result < WAIT_OBJECT_0 + m_handles.size())
  262. {
  263. if (result == m_lastResult)
  264. m_sameResultCount++;
  265. else
  266. {
  267. m_lastResult = result;
  268. m_sameResultCount = 0;
  269. }
  270. return true;
  271. }
  272. else if (result == WAIT_TIMEOUT)
  273. {
  274. SetLastResult(timeoutIsScheduledEvent ? LASTRESULT_SCHEDULED : LASTRESULT_TIMEOUT);
  275. return timeoutIsScheduledEvent;
  276. }
  277. else
  278. throw Err("WaitObjectContainer: WaitForMultipleObjects failed with error " + IntToString(::GetLastError()));
  279. }
  280. }
  281. #else // #ifdef USE_WINDOWS_STYLE_SOCKETS
  282. void WaitObjectContainer::AddReadFd(int fd, CallStack const& callStack) // TODO: do something with callStack
  283. {
  284. FD_SET(fd, &m_readfds);
  285. m_maxFd = STDMAX(m_maxFd, fd);
  286. }
  287. void WaitObjectContainer::AddWriteFd(int fd, CallStack const& callStack) // TODO: do something with callStack
  288. {
  289. FD_SET(fd, &m_writefds);
  290. m_maxFd = STDMAX(m_maxFd, fd);
  291. }
  292. bool WaitObjectContainer::Wait(unsigned long milliseconds)
  293. {
  294. if (m_noWait || (!m_maxFd && !m_firstEventTime))
  295. return true;
  296. bool timeoutIsScheduledEvent = false;
  297. if (m_firstEventTime)
  298. {
  299. double timeToFirstEvent = SaturatingSubtract(m_firstEventTime, m_eventTimer.ElapsedTimeAsDouble());
  300. if (timeToFirstEvent <= milliseconds)
  301. {
  302. milliseconds = (unsigned long)timeToFirstEvent;
  303. timeoutIsScheduledEvent = true;
  304. }
  305. }
  306. timeval tv, *timeout;
  307. if (milliseconds == INFINITE_TIME)
  308. timeout = NULL;
  309. else
  310. {
  311. tv.tv_sec = milliseconds / 1000;
  312. tv.tv_usec = (milliseconds % 1000) * 1000;
  313. timeout = &tv;
  314. }
  315. int result = select(m_maxFd+1, &m_readfds, &m_writefds, NULL, timeout);
  316. if (result > 0)
  317. return true;
  318. else if (result == 0)
  319. return timeoutIsScheduledEvent;
  320. else
  321. throw Err("WaitObjectContainer: select failed with error " + errno);
  322. }
  323. #endif
  324. // ********************************************************
  325. std::string CallStack::Format() const
  326. {
  327. return m_info;
  328. }
  329. std::string CallStackWithNr::Format() const
  330. {
  331. return std::string(m_info) + " / nr: " + IntToString(m_nr);
  332. }
  333. std::string CallStackWithStr::Format() const
  334. {
  335. return std::string(m_info) + " / " + std::string(m_z);
  336. }
  337. bool Waitable::Wait(unsigned long milliseconds, CallStack const& callStack)
  338. {
  339. WaitObjectContainer container;
  340. GetWaitObjects(container, callStack); // reduce clutter by not adding this func to stack
  341. return container.Wait(milliseconds);
  342. }
  343. NAMESPACE_END
  344. #endif