00001
00002
00003 #include "pch.h"
00004 #include "config.h"
00005
00006 #if CRYPTOPP_MSC_VERSION
00007 # pragma warning(disable: 4189)
00008 #endif
00009
00010 #include "wait.h"
00011 #include "misc.h"
00012 #include "smartptr.h"
00013
00014 #ifdef SOCKETS_AVAILABLE
00015
00016 #ifdef USE_BERKELEY_STYLE_SOCKETS
00017 #include <errno.h>
00018 #include <sys/types.h>
00019 #include <sys/time.h>
00020 #include <unistd.h>
00021 #endif
00022
00023 NAMESPACE_BEGIN(CryptoPP)
00024
00025 unsigned int WaitObjectContainer::MaxWaitObjects()
00026 {
00027 #ifdef USE_WINDOWS_STYLE_SOCKETS
00028 return MAXIMUM_WAIT_OBJECTS * (MAXIMUM_WAIT_OBJECTS-1);
00029 #else
00030 return FD_SETSIZE;
00031 #endif
00032 }
00033
00034 WaitObjectContainer::WaitObjectContainer(WaitObjectsTracer* tracer)
00035 : m_tracer(tracer), m_eventTimer(Timer::MILLISECONDS), m_lastResult(0)
00036 , m_sameResultCount(0), m_noWaitTimer(Timer::MILLISECONDS)
00037 {
00038 Clear();
00039 m_eventTimer.StartTimer();
00040 }
00041
00042 void WaitObjectContainer::Clear()
00043 {
00044 #ifdef USE_WINDOWS_STYLE_SOCKETS
00045 m_handles.clear();
00046 #else
00047 m_maxFd = 0;
00048 FD_ZERO(&m_readfds);
00049 FD_ZERO(&m_writefds);
00050 #endif
00051 m_noWait = false;
00052 m_firstEventTime = 0;
00053 }
00054
00055 inline void WaitObjectContainer::SetLastResult(LastResultType result)
00056 {
00057 if (result == m_lastResult)
00058 m_sameResultCount++;
00059 else
00060 {
00061 m_lastResult = result;
00062 m_sameResultCount = 0;
00063 }
00064 }
00065
00066 void WaitObjectContainer::DetectNoWait(LastResultType result, CallStack const& callStack)
00067 {
00068 if (result == m_lastResult && m_noWaitTimer.ElapsedTime() > 1000)
00069 {
00070 if (m_sameResultCount > m_noWaitTimer.ElapsedTime())
00071 {
00072 if (m_tracer)
00073 {
00074 std::string desc = "No wait loop detected - m_lastResult: ";
00075 desc.append(IntToString(m_lastResult)).append(", call stack:");
00076 for (CallStack const* cs = &callStack; cs; cs = cs->Prev())
00077 desc.append("\n- ").append(cs->Format());
00078 m_tracer->TraceNoWaitLoop(desc);
00079 }
00080 try { throw 0; } catch (...) {}
00081 }
00082
00083 m_noWaitTimer.StartTimer();
00084 m_sameResultCount = 0;
00085 }
00086 }
00087
00088 void WaitObjectContainer::SetNoWait(CallStack const& callStack)
00089 {
00090 DetectNoWait(LastResultType(LASTRESULT_NOWAIT), CallStack("WaitObjectContainer::SetNoWait()", &callStack));
00091 m_noWait = true;
00092 }
00093
00094 void WaitObjectContainer::ScheduleEvent(double milliseconds, CallStack const& callStack)
00095 {
00096 if (milliseconds <= 3)
00097 DetectNoWait(LastResultType(LASTRESULT_SCHEDULED), CallStack("WaitObjectContainer::ScheduleEvent()", &callStack));
00098 double thisEventTime = m_eventTimer.ElapsedTimeAsDouble() + milliseconds;
00099 if (!m_firstEventTime || thisEventTime < m_firstEventTime)
00100 m_firstEventTime = thisEventTime;
00101 }
00102
00103 #ifdef USE_WINDOWS_STYLE_SOCKETS
00104
00105 struct WaitingThreadData
00106 {
00107 bool waitingToWait, terminate;
00108 HANDLE startWaiting, stopWaiting;
00109 const HANDLE *waitHandles;
00110 unsigned int count;
00111 HANDLE threadHandle;
00112 DWORD threadId;
00113 DWORD* error;
00114 };
00115
00116 WaitObjectContainer::~WaitObjectContainer()
00117 {
00118 try
00119 {
00120 if (!m_threads.empty())
00121 {
00122 HANDLE threadHandles[MAXIMUM_WAIT_OBJECTS] = {0};
00123
00124 unsigned int i;
00125 for (i=0; i<m_threads.size(); i++)
00126 {
00127
00128 if(!m_threads[i]) continue;
00129
00130 WaitingThreadData &thread = *m_threads[i];
00131 while (!thread.waitingToWait)
00132 Sleep(0);
00133 thread.terminate = true;
00134 threadHandles[i] = thread.threadHandle;
00135 }
00136
00137 BOOL bResult = PulseEvent(m_startWaiting);
00138 assert(bResult != 0); CRYPTOPP_UNUSED(bResult);
00139
00140
00141 DWORD dwResult = ::WaitForMultipleObjects((DWORD)m_threads.size(), threadHandles, TRUE, INFINITE);
00142 assert((dwResult >= WAIT_OBJECT_0) && (dwResult < (DWORD)m_threads.size()));
00143
00144 for (i=0; i<m_threads.size(); i++)
00145 {
00146
00147 if (!threadHandles[i]) continue;
00148
00149 bResult = CloseHandle(threadHandles[i]);
00150 assert(bResult != 0);
00151 }
00152
00153 bResult = CloseHandle(m_startWaiting);
00154 assert(bResult != 0);
00155 bResult = CloseHandle(m_stopWaiting);
00156 assert(bResult != 0);
00157 }
00158 }
00159 catch (const Exception&)
00160 {
00161 assert(0);
00162 }
00163 }
00164
00165 void WaitObjectContainer::AddHandle(HANDLE handle, CallStack const& callStack)
00166 {
00167 DetectNoWait(m_handles.size(), CallStack("WaitObjectContainer::AddHandle()", &callStack));
00168 m_handles.push_back(handle);
00169 }
00170
00171 DWORD WINAPI WaitingThread(LPVOID lParam)
00172 {
00173 member_ptr<WaitingThreadData> pThread((WaitingThreadData *)lParam);
00174 WaitingThreadData &thread = *pThread;
00175 std::vector<HANDLE> handles;
00176
00177 while (true)
00178 {
00179 thread.waitingToWait = true;
00180 DWORD result = ::WaitForSingleObject(thread.startWaiting, INFINITE);
00181 assert(result != WAIT_FAILED);
00182
00183 thread.waitingToWait = false;
00184 if (thread.terminate)
00185 break;
00186 if (!thread.count)
00187 continue;
00188
00189 handles.resize(thread.count + 1);
00190 handles[0] = thread.stopWaiting;
00191 std::copy(thread.waitHandles, thread.waitHandles+thread.count, handles.begin()+1);
00192
00193 result = ::WaitForMultipleObjects((DWORD)handles.size(), &handles[0], FALSE, INFINITE);
00194 assert(result != WAIT_FAILED);
00195
00196 if (result == WAIT_OBJECT_0)
00197 continue;
00198 SetEvent(thread.stopWaiting);
00199 if (!(result > WAIT_OBJECT_0 && result < WAIT_OBJECT_0 + handles.size()))
00200 {
00201 assert(!"error in WaitingThread");
00202 *thread.error = ::GetLastError();
00203 }
00204 }
00205
00206 return S_OK;
00207 }
00208
00209 void WaitObjectContainer::CreateThreads(unsigned int count)
00210 {
00211 size_t currentCount = m_threads.size();
00212 if (currentCount == 0)
00213 {
00214 m_startWaiting = ::CreateEvent(NULL, TRUE, FALSE, NULL);
00215 m_stopWaiting = ::CreateEvent(NULL, TRUE, FALSE, NULL);
00216 }
00217
00218 if (currentCount < count)
00219 {
00220 m_threads.resize(count);
00221 for (size_t i=currentCount; i<count; i++)
00222 {
00223
00224 if(!m_threads[i]) continue;
00225
00226 m_threads[i] = new WaitingThreadData;
00227 WaitingThreadData &thread = *m_threads[i];
00228 thread.terminate = false;
00229 thread.startWaiting = m_startWaiting;
00230 thread.stopWaiting = m_stopWaiting;
00231 thread.waitingToWait = false;
00232 thread.threadHandle = CreateThread(NULL, 0, &WaitingThread, &thread, 0, &thread.threadId);
00233 }
00234 }
00235 }
00236
00237 bool WaitObjectContainer::Wait(unsigned long milliseconds)
00238 {
00239 if (m_noWait || (m_handles.empty() && !m_firstEventTime))
00240 {
00241 SetLastResult(LastResultType(LASTRESULT_NOWAIT));
00242 return true;
00243 }
00244
00245 bool timeoutIsScheduledEvent = false;
00246
00247 if (m_firstEventTime)
00248 {
00249 double timeToFirstEvent = SaturatingSubtract(m_firstEventTime, m_eventTimer.ElapsedTimeAsDouble());
00250
00251 if (timeToFirstEvent <= milliseconds)
00252 {
00253 milliseconds = (unsigned long)timeToFirstEvent;
00254 timeoutIsScheduledEvent = true;
00255 }
00256
00257 if (m_handles.empty() || !milliseconds)
00258 {
00259 if (milliseconds)
00260 Sleep(milliseconds);
00261 SetLastResult(timeoutIsScheduledEvent ? LASTRESULT_SCHEDULED : LASTRESULT_TIMEOUT);
00262 return timeoutIsScheduledEvent;
00263 }
00264 }
00265
00266 if (m_handles.size() > MAXIMUM_WAIT_OBJECTS)
00267 {
00268
00269 static const unsigned int WAIT_OBJECTS_PER_THREAD = MAXIMUM_WAIT_OBJECTS-1;
00270 unsigned int nThreads = (unsigned int)((m_handles.size() + WAIT_OBJECTS_PER_THREAD - 1) / WAIT_OBJECTS_PER_THREAD);
00271 if (nThreads > MAXIMUM_WAIT_OBJECTS)
00272 throw Err("WaitObjectContainer: number of wait objects exceeds limit");
00273 CreateThreads(nThreads);
00274 DWORD error = S_OK;
00275
00276 for (unsigned int i=0; i<m_threads.size(); i++)
00277 {
00278
00279 if(!m_threads[i]) continue;
00280
00281 WaitingThreadData &thread = *m_threads[i];
00282 while (!thread.waitingToWait)
00283 Sleep(0);
00284 if (i<nThreads)
00285 {
00286 thread.waitHandles = &m_handles[i*WAIT_OBJECTS_PER_THREAD];
00287 thread.count = UnsignedMin(WAIT_OBJECTS_PER_THREAD, m_handles.size() - i*WAIT_OBJECTS_PER_THREAD);
00288 thread.error = &error;
00289 }
00290 else
00291 thread.count = 0;
00292 }
00293
00294 ResetEvent(m_stopWaiting);
00295 PulseEvent(m_startWaiting);
00296
00297 DWORD result = ::WaitForSingleObject(m_stopWaiting, milliseconds);
00298 assert(result != WAIT_FAILED);
00299
00300 if (result == WAIT_OBJECT_0)
00301 {
00302 if (error == S_OK)
00303 return true;
00304 else
00305 throw Err("WaitObjectContainer: WaitForMultipleObjects in thread failed with error " + IntToString(error));
00306 }
00307 SetEvent(m_stopWaiting);
00308 if (result == WAIT_TIMEOUT)
00309 {
00310 SetLastResult(timeoutIsScheduledEvent ? LASTRESULT_SCHEDULED : LASTRESULT_TIMEOUT);
00311 return timeoutIsScheduledEvent;
00312 }
00313 else
00314 throw Err("WaitObjectContainer: WaitForSingleObject failed with error " + IntToString(::GetLastError()));
00315 }
00316 else
00317 {
00318 #if TRACE_WAIT
00319 static Timer t(Timer::MICROSECONDS);
00320 static unsigned long lastTime = 0;
00321 unsigned long timeBeforeWait = t.ElapsedTime();
00322 #endif
00323 DWORD result = ::WaitForMultipleObjects((DWORD)m_handles.size(), &m_handles[0], FALSE, milliseconds);
00324 #if TRACE_WAIT
00325 if (milliseconds > 0)
00326 {
00327 unsigned long timeAfterWait = t.ElapsedTime();
00328 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());
00329 lastTime = timeAfterWait;
00330 }
00331 #endif
00332 if (result >= WAIT_OBJECT_0 && result < WAIT_OBJECT_0 + m_handles.size())
00333 {
00334 if (result == m_lastResult)
00335 m_sameResultCount++;
00336 else
00337 {
00338 m_lastResult = result;
00339 m_sameResultCount = 0;
00340 }
00341 return true;
00342 }
00343 else if (result == WAIT_TIMEOUT)
00344 {
00345 SetLastResult(timeoutIsScheduledEvent ? LASTRESULT_SCHEDULED : LASTRESULT_TIMEOUT);
00346 return timeoutIsScheduledEvent;
00347 }
00348 else
00349 throw Err("WaitObjectContainer: WaitForMultipleObjects failed with error " + IntToString(::GetLastError()));
00350 }
00351 }
00352
00353 #else // #ifdef USE_WINDOWS_STYLE_SOCKETS
00354
00355 void WaitObjectContainer::AddReadFd(int fd, CallStack const& callStack)
00356 {
00357 CRYPTOPP_UNUSED(callStack);
00358 FD_SET(fd, &m_readfds);
00359 m_maxFd = STDMAX(m_maxFd, fd);
00360 }
00361
00362 void WaitObjectContainer::AddWriteFd(int fd, CallStack const& callStack)
00363 {
00364 CRYPTOPP_UNUSED(callStack);
00365 FD_SET(fd, &m_writefds);
00366 m_maxFd = STDMAX(m_maxFd, fd);
00367 }
00368
00369 bool WaitObjectContainer::Wait(unsigned long milliseconds)
00370 {
00371 if (m_noWait || (!m_maxFd && !m_firstEventTime))
00372 return true;
00373
00374 bool timeoutIsScheduledEvent = false;
00375
00376 if (m_firstEventTime)
00377 {
00378 double timeToFirstEvent = SaturatingSubtract(m_firstEventTime, m_eventTimer.ElapsedTimeAsDouble());
00379 if (timeToFirstEvent <= milliseconds)
00380 {
00381 milliseconds = (unsigned long)timeToFirstEvent;
00382 timeoutIsScheduledEvent = true;
00383 }
00384 }
00385
00386 timeval tv, *timeout;
00387
00388 if (milliseconds == INFINITE_TIME)
00389 timeout = NULL;
00390 else
00391 {
00392 tv.tv_sec = milliseconds / 1000;
00393 tv.tv_usec = (milliseconds % 1000) * 1000;
00394 timeout = &tv;
00395 }
00396
00397 int result = select(m_maxFd+1, &m_readfds, &m_writefds, NULL, timeout);
00398
00399 if (result > 0)
00400 return true;
00401 else if (result == 0)
00402 return timeoutIsScheduledEvent;
00403 else
00404 throw Err("WaitObjectContainer: select failed with error " + IntToString(errno));
00405 }
00406
00407 #endif
00408
00409
00410
00411 std::string CallStack::Format() const
00412 {
00413 return m_info;
00414 }
00415
00416 std::string CallStackWithNr::Format() const
00417 {
00418 return std::string(m_info) + " / nr: " + IntToString(m_nr);
00419 }
00420
00421 std::string CallStackWithStr::Format() const
00422 {
00423 return std::string(m_info) + " / " + std::string(m_z);
00424 }
00425
00426 bool Waitable::Wait(unsigned long milliseconds, CallStack const& callStack)
00427 {
00428 WaitObjectContainer container;
00429 GetWaitObjects(container, callStack);
00430 return container.Wait(milliseconds);
00431 }
00432
00433 NAMESPACE_END
00434
00435 #endif