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> 40 #ifdef USE_WINDOWS_STYLE_SOCKETS 41 return MAXIMUM_WAIT_OBJECTS * (MAXIMUM_WAIT_OBJECTS-1);
47 WaitObjectContainer::WaitObjectContainer(WaitObjectsTracer* tracer)
49 #ifdef USE_WINDOWS_STYLE_SOCKETS
50 m_startWaiting(0), m_stopWaiting(0),
52 m_firstEventTime(0.0
f), 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 126 struct WaitingThreadData
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;
151 WaitingThreadData &thread = *m_threads[i];
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);
171 for (i=0; i<m_threads.size(); i++)
174 if (!threadHandles[i])
continue;
176 bResult = CloseHandle(threadHandles[i]);
180 bResult = CloseHandle(m_startWaiting);
182 bResult = CloseHandle(m_stopWaiting);
192 void WaitObjectContainer::AddHandle(HANDLE handle,
CallStack const& callStack)
194 DetectNoWait(m_handles.size(),
CallStack(
"WaitObjectContainer::AddHandle()", &callStack));
195 m_handles.push_back(handle);
198 DWORD WINAPI WaitingThread(LPVOID lParam)
201 WaitingThreadData &thread = *pThread;
202 std::vector<HANDLE> handles;
206 thread.waitingToWait =
true;
207 #if defined(USE_WINDOWS8_API) 208 DWORD result = ::WaitForSingleObjectEx(thread.startWaiting, INFINITE, FALSE);
211 DWORD result = ::WaitForSingleObject(thread.startWaiting, INFINITE);
215 thread.waitingToWait =
false;
216 if (thread.terminate)
221 handles.resize(thread.count + 1);
222 handles[0] = thread.stopWaiting;
223 std::copy(thread.waitHandles, thread.waitHandles+thread.count, handles.begin()+1);
225 #if defined(USE_WINDOWS8_API) 226 result = ::WaitForMultipleObjectsEx((DWORD)handles.size(), &handles[0], FALSE, INFINITE, FALSE);
229 result = ::WaitForMultipleObjects((DWORD)handles.size(), &handles[0], FALSE, INFINITE);
233 if (result == WAIT_OBJECT_0)
235 SetEvent(thread.stopWaiting);
236 if (!(result > WAIT_OBJECT_0 && result < WAIT_OBJECT_0 + handles.size()))
239 *thread.error = ::GetLastError();
246 void WaitObjectContainer::CreateThreads(
unsigned int count)
248 size_t currentCount = m_threads.size();
249 if (currentCount == 0)
251 m_startWaiting = ::CreateEvent(NULL, TRUE, FALSE, NULL);
252 m_stopWaiting = ::CreateEvent(NULL, TRUE, FALSE, NULL);
255 if (currentCount < count)
257 m_threads.resize(count);
258 for (
size_t i=currentCount; i<
count; i++)
261 if(!m_threads[i])
continue;
263 m_threads[i] =
new WaitingThreadData;
264 WaitingThreadData &thread = *m_threads[i];
265 thread.terminate =
false;
266 thread.startWaiting = m_startWaiting;
267 thread.stopWaiting = m_stopWaiting;
268 thread.waitingToWait =
false;
269 thread.threadHandle = CreateThread(NULL, 0, &WaitingThread, &thread, 0, &thread.threadId);
274 bool WaitObjectContainer::Wait(
unsigned long milliseconds)
276 if (m_noWait || (m_handles.empty() && !m_firstEventTime))
278 SetLastResult(LastResultType(LASTRESULT_NOWAIT));
282 bool timeoutIsScheduledEvent =
false;
284 if (m_firstEventTime)
286 double timeToFirstEvent =
SaturatingSubtract(m_firstEventTime, m_eventTimer.ElapsedTimeAsDouble());
288 if (timeToFirstEvent <= milliseconds)
290 milliseconds = (
unsigned long)timeToFirstEvent;
291 timeoutIsScheduledEvent =
true;
294 if (m_handles.empty() || !milliseconds)
298 SetLastResult(timeoutIsScheduledEvent ? LASTRESULT_SCHEDULED : LASTRESULT_TIMEOUT);
299 return timeoutIsScheduledEvent;
303 if (m_handles.size() > MAXIMUM_WAIT_OBJECTS)
306 static const unsigned int WAIT_OBJECTS_PER_THREAD = MAXIMUM_WAIT_OBJECTS-1;
307 unsigned int nThreads = (
unsigned int)((m_handles.size() + WAIT_OBJECTS_PER_THREAD - 1) / WAIT_OBJECTS_PER_THREAD);
308 if (nThreads > MAXIMUM_WAIT_OBJECTS)
309 throw Err(
"WaitObjectContainer: number of wait objects exceeds limit");
310 CreateThreads(nThreads);
313 for (
unsigned int i=0; i<m_threads.size(); i++)
316 if(!m_threads[i])
continue;
318 WaitingThreadData &thread = *m_threads[i];
319 while (!thread.waitingToWait)
323 thread.waitHandles = &m_handles[i*WAIT_OBJECTS_PER_THREAD];
324 thread.count =
UnsignedMin(WAIT_OBJECTS_PER_THREAD, m_handles.size() - i*WAIT_OBJECTS_PER_THREAD);
325 thread.error = &
error;
331 ResetEvent(m_stopWaiting);
332 PulseEvent(m_startWaiting);
334 #if defined(USE_WINDOWS8_API) 335 DWORD result = ::WaitForSingleObjectEx(m_stopWaiting, milliseconds, FALSE);
338 DWORD result = ::WaitForSingleObject(m_stopWaiting, milliseconds);
342 if (result == WAIT_OBJECT_0)
347 throw Err(
"WaitObjectContainer: WaitForMultipleObjects in thread failed with error " +
IntToString(error));
349 SetEvent(m_stopWaiting);
350 if (result == WAIT_TIMEOUT)
352 SetLastResult(timeoutIsScheduledEvent ? LASTRESULT_SCHEDULED : LASTRESULT_TIMEOUT);
353 return timeoutIsScheduledEvent;
356 throw Err(
"WaitObjectContainer: WaitForSingleObject failed with error " +
IntToString(::GetLastError()));
362 static unsigned long lastTime = 0;
363 unsigned long timeBeforeWait = t.ElapsedTime();
365 #if defined(USE_WINDOWS8_API) 366 DWORD result = ::WaitForMultipleObjectsEx((DWORD)m_handles.size(), &m_handles[0], FALSE, milliseconds, FALSE);
369 DWORD result = ::WaitForMultipleObjects((DWORD)m_handles.size(), &m_handles[0], FALSE, milliseconds);
373 if (milliseconds > 0)
375 unsigned long timeAfterWait = t.ElapsedTime();
376 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());
377 lastTime = timeAfterWait;
380 if (result < WAIT_OBJECT_0 + m_handles.size())
382 if (result == m_lastResult)
386 m_lastResult = result;
387 m_sameResultCount = 0;
391 else if (result == WAIT_TIMEOUT)
393 SetLastResult(timeoutIsScheduledEvent ? LASTRESULT_SCHEDULED : LASTRESULT_TIMEOUT);
394 return timeoutIsScheduledEvent;
397 throw Err(
"WaitObjectContainer: WaitForMultipleObjects failed with error " +
IntToString(::GetLastError()));
401 #else // #ifdef USE_WINDOWS_STYLE_SOCKETS 403 void WaitObjectContainer::AddReadFd(
int fd,
CallStack const& callStack)
406 FD_SET(fd, &m_readfds);
407 m_maxFd =
STDMAX(m_maxFd, fd);
410 void WaitObjectContainer::AddWriteFd(
int fd,
CallStack const& callStack)
413 FD_SET(fd, &m_writefds);
414 m_maxFd =
STDMAX(m_maxFd, fd);
417 bool WaitObjectContainer::Wait(
unsigned long milliseconds)
419 if (m_noWait || (!m_maxFd && !m_firstEventTime))
422 bool timeoutIsScheduledEvent =
false;
424 if (m_firstEventTime)
426 double timeToFirstEvent =
SaturatingSubtract(m_firstEventTime, m_eventTimer.ElapsedTimeAsDouble());
427 if (timeToFirstEvent <= milliseconds)
429 milliseconds = (
unsigned long)timeToFirstEvent;
430 timeoutIsScheduledEvent =
true;
434 timeval tv, *timeout;
440 tv.tv_sec = milliseconds / 1000;
441 tv.tv_usec = (milliseconds % 1000) * 1000;
445 int result = select(m_maxFd+1, &m_readfds, &m_writefds, NULL, timeout);
449 else if (result == 0)
450 return timeoutIsScheduledEvent;
452 throw Err(
"WaitObjectContainer: select failed with error " +
IntToString(errno));
466 return std::string(m_info) +
" / nr: " +
IntToString(m_nr);
471 return std::string(m_info) +
" / " + std::string(m_z);
477 GetWaitObjects(container, callStack);
478 return container.Wait(milliseconds);
Base class for all exceptions thrown by the library.
bool error(const char *fmt, const Args &...args)
Utility functions for the Crypto++ library.
#define NAMESPACE_BEGIN(x)
Classes for automatic resource management.
Library configuration file.
Pointer that overloads operator ->
T1 SaturatingSubtract(const T1 &a, const T2 &b)
Performs a saturating subtract clamped at 0.
const T1 UnsignedMin(const T1 &a, const T2 &b)
Safe comparison of values that could be neagtive and incorrectly promoted.
#define CRYPTOPP_ASSERT(exp)
const unsigned long INFINITE_TIME
Represents infinite time.
bool Wait(unsigned long milliseconds, CallStack const &callStack)
Wait on this object.
#define CRYPTOPP_UNUSED(x)
std::string IntToString(T value, unsigned int base=10)
Converts a value to a string.
const T & STDMAX(const T &a, const T &b)
Replacement function for std::max.