6 #if CRYPTOPP_MSC_VERSION
7 # pragma warning(disable: 4189)
10 #if !defined(NO_OS_DEPENDENCE) && (defined(SOCKETS_AVAILABLE) || defined(WINDOWS_PIPES_AVAILABLE))
17 #if defined(CRYPTOPP_WIN32_AVAILABLE)
18 # if ((WINVER >= 0x0602 ) || (_WIN32_WINNT >= 0x0602 ))
19 # include <synchapi.h>
20 # include <ioapiset.h>
21 # define USE_WINDOWS8_API
25 #ifdef USE_BERKELEY_STYLE_SOCKETS
27 #include <sys/types.h>
32 #if defined(CRYPTOPP_MSAN)
33 # include <sanitizer/msan_interface.h>
38 unsigned int WaitObjectContainer::MaxWaitObjects()
40 #ifdef USE_WINDOWS_STYLE_SOCKETS
41 return MAXIMUM_WAIT_OBJECTS * (MAXIMUM_WAIT_OBJECTS-1);
49 #ifdef USE_WINDOWS_STYLE_SOCKETS
50 m_startWaiting(0), m_stopWaiting(0),
52 m_firstEventTime(0.0f), m_eventTimer(
Timer::MILLISECONDS), m_lastResult(0),
53 m_sameResultCount(0), m_noWaitTimer(
Timer::MILLISECONDS)
56 m_eventTimer.StartTimer();
59 void WaitObjectContainer::Clear()
61 #ifdef USE_WINDOWS_STYLE_SOCKETS
68 __msan_unpoison(&m_readfds,
sizeof(m_readfds));
69 __msan_unpoison(&m_writefds,
sizeof(m_writefds));
76 inline void WaitObjectContainer::SetLastResult(LastResultType result)
78 if (result == m_lastResult)
82 m_lastResult = result;
83 m_sameResultCount = 0;
87 void WaitObjectContainer::DetectNoWait(LastResultType result,
CallStack const& callStack)
89 if (result == m_lastResult && m_noWaitTimer.ElapsedTime() > 1000)
91 if (m_sameResultCount > m_noWaitTimer.ElapsedTime())
95 std::string desc =
"No wait loop detected - m_lastResult: ";
96 desc.append(
IntToString(m_lastResult)).append(
", call stack:");
97 for (
CallStack const* cs = &callStack; cs; cs = cs->Prev())
98 desc.append(
"\n- ").append(cs->Format());
99 m_tracer->TraceNoWaitLoop(desc);
101 try {
throw 0; }
catch (...) {}
104 m_noWaitTimer.StartTimer();
105 m_sameResultCount = 0;
109 void WaitObjectContainer::SetNoWait(
CallStack const& callStack)
111 DetectNoWait(LastResultType(LASTRESULT_NOWAIT),
CallStack(
"WaitObjectContainer::SetNoWait()", &callStack));
115 void WaitObjectContainer::ScheduleEvent(
double milliseconds,
CallStack const& callStack)
117 if (milliseconds <= 3)
118 DetectNoWait(LastResultType(LASTRESULT_SCHEDULED),
CallStack(
"WaitObjectContainer::ScheduleEvent()", &callStack));
119 double thisEventTime = m_eventTimer.ElapsedTimeAsDouble() + milliseconds;
120 if (!m_firstEventTime || thisEventTime < m_firstEventTime)
121 m_firstEventTime = thisEventTime;
124 #ifdef USE_WINDOWS_STYLE_SOCKETS
128 bool waitingToWait, terminate;
129 HANDLE startWaiting, stopWaiting;
130 const HANDLE *waitHandles;
137 WaitObjectContainer::~WaitObjectContainer()
141 if (!m_threads.empty())
143 HANDLE threadHandles[MAXIMUM_WAIT_OBJECTS] = {0};
146 for (i=0; i<m_threads.size(); i++)
149 if(!m_threads[i])
continue;
152 while (!thread.waitingToWait)
154 thread.terminate =
true;
155 threadHandles[i] = thread.threadHandle;
158 BOOL bResult = PulseEvent(m_startWaiting);
162 #if defined(USE_WINDOWS8_API)
163 DWORD dwResult = ::WaitForMultipleObjectsEx((DWORD)m_threads.size(), threadHandles, TRUE, INFINITE, FALSE);
166 DWORD dwResult = ::WaitForMultipleObjects((DWORD)m_threads.size(), threadHandles, TRUE, INFINITE);
170 for (i=0; i<m_threads.size(); i++)
173 if (!threadHandles[i])
continue;
175 bResult = CloseHandle(threadHandles[i]);
179 bResult = CloseHandle(m_startWaiting);
181 bResult = CloseHandle(m_stopWaiting);
191 void WaitObjectContainer::AddHandle(HANDLE handle,
CallStack const& callStack)
193 DetectNoWait(m_handles.size(),
CallStack(
"WaitObjectContainer::AddHandle()", &callStack));
194 m_handles.push_back(handle);
197 DWORD WINAPI WaitingThread(LPVOID lParam)
201 std::vector<HANDLE> handles;
205 thread.waitingToWait =
true;
206 #if defined(USE_WINDOWS8_API)
207 DWORD result = ::WaitForSingleObjectEx(thread.startWaiting, INFINITE, FALSE);
210 DWORD result = ::WaitForSingleObject(thread.startWaiting, INFINITE);
214 thread.waitingToWait =
false;
215 if (thread.terminate)
220 handles.resize(thread.count + 1);
221 handles[0] = thread.stopWaiting;
222 std::copy(thread.waitHandles, thread.waitHandles+thread.count, handles.begin()+1);
224 #if defined(USE_WINDOWS8_API)
225 result = ::WaitForMultipleObjectsEx((DWORD)handles.size(), &handles[0], FALSE, INFINITE, FALSE);
228 result = ::WaitForMultipleObjects((DWORD)handles.size(), &handles[0], FALSE, INFINITE);
232 if (result == WAIT_OBJECT_0)
234 SetEvent(thread.stopWaiting);
235 if (!(result > WAIT_OBJECT_0 && result < WAIT_OBJECT_0 + handles.size()))
238 *thread.error = ::GetLastError();
245 void WaitObjectContainer::CreateThreads(
unsigned int count)
247 size_t currentCount = m_threads.size();
248 if (currentCount == 0)
250 m_startWaiting = ::CreateEvent(NULL, TRUE, FALSE, NULL);
251 m_stopWaiting = ::CreateEvent(NULL, TRUE, FALSE, NULL);
254 if (currentCount < count)
256 m_threads.resize(count);
257 for (
size_t i=currentCount; i<count; i++)
260 if(!m_threads[i])
continue;
264 thread.terminate =
false;
265 thread.startWaiting = m_startWaiting;
266 thread.stopWaiting = m_stopWaiting;
267 thread.waitingToWait =
false;
268 thread.threadHandle = CreateThread(NULL, 0, &WaitingThread, &thread, 0, &thread.threadId);
273 bool WaitObjectContainer::Wait(
unsigned long milliseconds)
275 if (m_noWait || (m_handles.empty() && !m_firstEventTime))
277 SetLastResult(LastResultType(LASTRESULT_NOWAIT));
281 bool timeoutIsScheduledEvent =
false;
283 if (m_firstEventTime)
285 double timeToFirstEvent =
SaturatingSubtract(m_firstEventTime, m_eventTimer.ElapsedTimeAsDouble());
287 if (timeToFirstEvent <= milliseconds)
289 milliseconds = (
unsigned long)timeToFirstEvent;
290 timeoutIsScheduledEvent =
true;
293 if (m_handles.empty() || !milliseconds)
297 SetLastResult(timeoutIsScheduledEvent ? LASTRESULT_SCHEDULED : LASTRESULT_TIMEOUT);
298 return timeoutIsScheduledEvent;
302 if (m_handles.size() > MAXIMUM_WAIT_OBJECTS)
305 static const unsigned int WAIT_OBJECTS_PER_THREAD = MAXIMUM_WAIT_OBJECTS-1;
306 unsigned int nThreads = (
unsigned int)((m_handles.size() + WAIT_OBJECTS_PER_THREAD - 1) / WAIT_OBJECTS_PER_THREAD);
307 if (nThreads > MAXIMUM_WAIT_OBJECTS)
308 throw Err(
"WaitObjectContainer: number of wait objects exceeds limit");
309 CreateThreads(nThreads);
312 for (
unsigned int i=0; i<m_threads.size(); i++)
315 if(!m_threads[i])
continue;
318 while (!thread.waitingToWait)
322 thread.waitHandles = &m_handles[i*WAIT_OBJECTS_PER_THREAD];
323 thread.count =
UnsignedMin(WAIT_OBJECTS_PER_THREAD, m_handles.size() - i*WAIT_OBJECTS_PER_THREAD);
324 thread.error = &error;
330 ResetEvent(m_stopWaiting);
331 PulseEvent(m_startWaiting);
333 #if defined(USE_WINDOWS8_API)
334 DWORD result = ::WaitForSingleObjectEx(m_stopWaiting, milliseconds, FALSE);
337 DWORD result = ::WaitForSingleObject(m_stopWaiting, milliseconds);
341 if (result == WAIT_OBJECT_0)
346 throw Err(
"WaitObjectContainer: WaitForMultipleObjects in thread failed with error " +
IntToString(error));
348 SetEvent(m_stopWaiting);
349 if (result == WAIT_TIMEOUT)
351 SetLastResult(timeoutIsScheduledEvent ? LASTRESULT_SCHEDULED : LASTRESULT_TIMEOUT);
352 return timeoutIsScheduledEvent;
355 throw Err(
"WaitObjectContainer: WaitForSingleObject failed with error " +
IntToString(::GetLastError()));
360 static Timer t(Timer::MICROSECONDS);
361 static unsigned long lastTime = 0;
362 unsigned long timeBeforeWait = t.ElapsedTime();
364 #if defined(USE_WINDOWS8_API)
365 DWORD result = ::WaitForMultipleObjectsEx((DWORD)m_handles.size(), &m_handles[0], FALSE, milliseconds, FALSE);
368 DWORD result = ::WaitForMultipleObjects((DWORD)m_handles.size(), &m_handles[0], FALSE, milliseconds);
372 if (milliseconds > 0)
374 unsigned long timeAfterWait = t.ElapsedTime();
375 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());
376 lastTime = timeAfterWait;
379 if (result < WAIT_OBJECT_0 + m_handles.size())
381 if (result == m_lastResult)
385 m_lastResult = result;
386 m_sameResultCount = 0;
390 else if (result == WAIT_TIMEOUT)
392 SetLastResult(timeoutIsScheduledEvent ? LASTRESULT_SCHEDULED : LASTRESULT_TIMEOUT);
393 return timeoutIsScheduledEvent;
396 throw Err(
"WaitObjectContainer: WaitForMultipleObjects failed with error " +
IntToString(::GetLastError()));
400 #else // #ifdef USE_WINDOWS_STYLE_SOCKETS
402 void WaitObjectContainer::AddReadFd(
int fd,
CallStack const& callStack)
404 CRYPTOPP_UNUSED(callStack);
405 FD_SET(fd, &m_readfds);
406 m_maxFd =
STDMAX(m_maxFd, fd);
409 void WaitObjectContainer::AddWriteFd(
int fd,
CallStack const& callStack)
411 CRYPTOPP_UNUSED(callStack);
412 FD_SET(fd, &m_writefds);
413 m_maxFd =
STDMAX(m_maxFd, fd);
416 bool WaitObjectContainer::Wait(
unsigned long milliseconds)
418 if (m_noWait || (!m_maxFd && !m_firstEventTime))
421 bool timeoutIsScheduledEvent =
false;
423 if (m_firstEventTime)
425 double timeToFirstEvent =
SaturatingSubtract(m_firstEventTime, m_eventTimer.ElapsedTimeAsDouble());
426 if (timeToFirstEvent <= milliseconds)
428 milliseconds = (
unsigned long)timeToFirstEvent;
429 timeoutIsScheduledEvent =
true;
433 timeval tv, *timeout;
439 tv.tv_sec = milliseconds / 1000;
440 tv.tv_usec = (milliseconds % 1000) * 1000;
444 int result = select(m_maxFd+1, &m_readfds, &m_writefds, NULL, timeout);
448 else if (result == 0)
449 return timeoutIsScheduledEvent;
451 throw Err(
"WaitObjectContainer: select failed with error " +
IntToString(errno));
458 std::string CallStack::Format()
const
463 std::string CallStackWithNr::Format()
const
465 return std::string(m_info) +
" / nr: " +
IntToString(m_nr);
468 std::string CallStackWithStr::Format()
const
470 return std::string(m_info) +
" / " + std::string(m_z);
477 return container.Wait(milliseconds);