- Main Page
- Namespaces
- Classes
- Files

# wait.cpp

    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