00001
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 (...) {}
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
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)
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;
00170 SetEvent(thread.stopWaiting);
00171 if (!(result > WAIT_OBJECT_0 && result < WAIT_OBJECT_0 + handles.size()))
00172 {
00173 assert(!"error in WaitingThread");
00174 *thread.error = ::GetLastError();
00175 }
00176 }
00177
00178 return S_OK;
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
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)
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)
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)
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)
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);
00392 return container.Wait(milliseconds);
00393 }
00394
00395 NAMESPACE_END
00396
00397 #endif