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

Generated on Sat Dec 23 02:07:11 2006 for Crypto++ by  doxygen 1.5.1-p1