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