Team Fortress 2 Source Code as on 22/4/2020
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.

435 lines
11 KiB

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