Main Page | Namespace List | Class Hierarchy | Alphabetical List | Class List | File List | Namespace Members | Class Members | File Members

# 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 #define TRACE_WAIT 0
    00017
    00018 #if TRACE_WAIT
    00019 #include "hrtimer.h"
    00020 #endif
    00021
    00022 NAMESPACE_BEGIN(CryptoPP)
    00023
    00024 unsigned int WaitObjectContainer::MaxWaitObjects()
    00025 {
    00026 #ifdef USE_WINDOWS_STYLE_SOCKETS
    00027         return MAXIMUM_WAIT_OBJECTS * (MAXIMUM_WAIT_OBJECTS-1);
    00028 #else
    00029         return FD_SETSIZE;
    00030 #endif
    00031 }
    00032
    00033 WaitObjectContainer::WaitObjectContainer()
    00034 #if CRYPTOPP_DETECT_NO_WAIT
    00035         : m_sameResultCount(0), m_timer(Timer::MILLISECONDS)
    00036 #endif
    00037 {
    00038         Clear();
    00039 }
    00040
    00041 void WaitObjectContainer::Clear()
    00042 {
    00043 #ifdef USE_WINDOWS_STYLE_SOCKETS
    00044         m_handles.clear();
    00045 #else
    00046         m_maxFd = 0;
    00047         FD_ZERO(&m_readfds);
    00048         FD_ZERO(&m_writefds);
    00049 #endif
    00050         m_noWait = false;
    00051 }
    00052
    00053 void WaitObjectContainer::SetNoWait()
    00054 {
    00055 #if CRYPTOPP_DETECT_NO_WAIT
    00056         if (-1 == m_lastResult && m_timer.ElapsedTime() > 1000)
    00057         {
    00058                 if (m_sameResultCount > m_timer.ElapsedTime())
    00059                         try {throw 0;} catch (...) {}   // possible no-wait loop, break in debugger
    00060                 m_timer.StartTimer();
    00061         }
    00062 #endif
    00063         m_noWait = true;
    00064 }
    00065
    00066 #ifdef USE_WINDOWS_STYLE_SOCKETS
    00067
    00068 struct WaitingThreadData
    00069 {
    00070         bool waitingToWait, terminate;
    00071         HANDLE startWaiting, stopWaiting;
    00072         const HANDLE *waitHandles;
    00073         unsigned int count;
    00074         HANDLE threadHandle;
    00075         DWORD threadId;
    00076         DWORD* error;
    00077 };
    00078
    00079 WaitObjectContainer::~WaitObjectContainer()
    00080 {
    00081         try             // don't let exceptions escape destructor
    00082         {
    00083                 if (!m_threads.empty())
    00084                 {
    00085                         HANDLE threadHandles[MAXIMUM_WAIT_OBJECTS];
    00086                         unsigned int i;
    00087                         for (i=0; i<m_threads.size(); i++)
    00088                         {
    00089                                 WaitingThreadData &thread = *m_threads[i];
    00090                                 while (!thread.waitingToWait)   // spin until thread is in the initial "waiting to wait" state
    00091                                         Sleep(0);
    00092                                 thread.terminate = true;
    00093                                 threadHandles[i] = thread.threadHandle;
    00094                         }
    00095                         PulseEvent(m_startWaiting);
    00096                         ::WaitForMultipleObjects(m_threads.size(), threadHandles, TRUE, INFINITE);
    00097                         for (i=0; i<m_threads.size(); i++)
    00098                                 CloseHandle(threadHandles[i]);
    00099                         CloseHandle(m_startWaiting);
    00100                         CloseHandle(m_stopWaiting);
    00101                 }
    00102         }
    00103         catch (...)
    00104         {
    00105         }
    00106 }
    00107
    00108
    00109 void WaitObjectContainer::AddHandle(HANDLE handle)
    00110 {
    00111 #if CRYPTOPP_DETECT_NO_WAIT
    00112         if (m_handles.size() == m_lastResult && m_timer.ElapsedTime() > 1000)
    00113         {
    00114                 if (m_sameResultCount > m_timer.ElapsedTime())
    00115                         try {throw 0;} catch (...) {}   // possible no-wait loop, break in debugger
    00116                 m_timer.StartTimer();
    00117         }
    00118 #endif
    00119         m_handles.push_back(handle);
    00120 }
    00121
    00122 DWORD WINAPI WaitingThread(LPVOID lParam)
    00123 {
    00124         std::auto_ptr<WaitingThreadData> pThread((WaitingThreadData *)lParam);
    00125         WaitingThreadData &thread = *pThread;
    00126         std::vector<HANDLE> handles;
    00127
    00128         while (true)
    00129         {
    00130                 thread.waitingToWait = true;
    00131                 ::WaitForSingleObject(thread.startWaiting, INFINITE);
    00132                 thread.waitingToWait = false;
    00133
    00134                 if (thread.terminate)
    00135                         break;
    00136                 if (!thread.count)
    00137                         continue;
    00138
    00139                 handles.resize(thread.count + 1);
    00140                 handles[0] = thread.stopWaiting;
    00141                 std::copy(thread.waitHandles, thread.waitHandles+thread.count, handles.begin()+1);
    00142
    00143                 DWORD result = ::WaitForMultipleObjects(handles.size(), &handles[0], FALSE, INFINITE);
    00144
    00145                 if (result == WAIT_OBJECT_0)
    00146                         continue;       // another thread finished waiting first, so do nothing
    00147                 SetEvent(thread.stopWaiting);
    00148                 if (!(result > WAIT_OBJECT_0 && result < WAIT_OBJECT_0 + handles.size()))
    00149                 {
    00150                         assert(!"error in WaitingThread");      // break here so we can see which thread has an error
    00151                         *thread.error = ::GetLastError();
    00152                 }
    00153         }
    00154
    00155         return S_OK;    // return a value here to avoid compiler warning
    00156 }
    00157
    00158 void WaitObjectContainer::CreateThreads(unsigned int count)
    00159 {
    00160         unsigned int currentCount = m_threads.size();
    00161         if (currentCount == 0)
    00162         {
    00163                 m_startWaiting = ::CreateEvent(NULL, TRUE, FALSE, NULL);
    00164                 m_stopWaiting = ::CreateEvent(NULL, TRUE, FALSE, NULL);
    00165         }
    00166
    00167         if (currentCount < count)
    00168         {
    00169                 m_threads.resize(count);
    00170                 for (unsigned int i=currentCount; i<count; i++)
    00171                 {
    00172                         m_threads[i] = new WaitingThreadData;
    00173                         WaitingThreadData &thread = *m_threads[i];
    00174                         thread.terminate = false;
    00175                         thread.startWaiting = m_startWaiting;
    00176                         thread.stopWaiting = m_stopWaiting;
    00177                         thread.waitingToWait = false;
    00178                         thread.threadHandle = CreateThread(NULL, 0, &WaitingThread, &thread, 0, &thread.threadId);
    00179                 }
    00180         }
    00181 }
    00182
    00183 bool WaitObjectContainer::Wait(unsigned long milliseconds)
    00184 {
    00185         if (m_noWait || m_handles.empty())
    00186         {
    00187 #if CRYPTOPP_DETECT_NO_WAIT
    00188                 if (-1 == m_lastResult)
    00189                         m_sameResultCount++;
    00190                 else
    00191                 {
    00192                         m_lastResult = -1;
    00193                         m_sameResultCount = 0;
    00194                 }
    00195 #endif
    00196                 return true;
    00197         }
    00198
    00199         if (m_handles.size() > MAXIMUM_WAIT_OBJECTS)
    00200         {
    00201                 // too many wait objects for a single WaitForMultipleObjects call, so use multiple threads
    00202                 static const unsigned int WAIT_OBJECTS_PER_THREAD = MAXIMUM_WAIT_OBJECTS-1;
    00203                 unsigned int nThreads = (m_handles.size() + WAIT_OBJECTS_PER_THREAD - 1) / WAIT_OBJECTS_PER_THREAD;
    00204                 if (nThreads > MAXIMUM_WAIT_OBJECTS)    // still too many wait objects, maybe implement recursive threading later?
    00205                         throw Err("WaitObjectContainer: number of wait objects exceeds limit");
    00206                 CreateThreads(nThreads);
    00207                 DWORD error = S_OK;
    00208
    00209                 for (unsigned int i=0; i<m_threads.size(); i++)
    00210                 {
    00211                         WaitingThreadData &thread = *m_threads[i];
    00212                         while (!thread.waitingToWait)   // spin until thread is in the initial "waiting to wait" state
    00213                                 Sleep(0);
    00214                         if (i<nThreads)
    00215                         {
    00216                                 thread.waitHandles = &m_handles[i*WAIT_OBJECTS_PER_THREAD];
    00217                                 thread.count = STDMIN(WAIT_OBJECTS_PER_THREAD, m_handles.size() - i*WAIT_OBJECTS_PER_THREAD);
    00218                                 thread.error = &error;
    00219                         }
    00220                         else
    00221                                 thread.count = 0;
    00222                 }
    00223
    00224                 ResetEvent(m_stopWaiting);
    00225                 PulseEvent(m_startWaiting);
    00226
    00227                 DWORD result = ::WaitForSingleObject(m_stopWaiting, milliseconds);
    00228                 if (result == WAIT_OBJECT_0)
    00229                 {
    00230                         if (error == S_OK)
    00231                                 return true;
    00232                         else
    00233                                 throw Err("WaitObjectContainer: WaitForMultipleObjects failed with error " + IntToString(error));
    00234                 }
    00235                 SetEvent(m_stopWaiting);
    00236                 if (result == WAIT_TIMEOUT)
    00237                         return false;
    00238                 else
    00239                         throw Err("WaitObjectContainer: WaitForSingleObject failed with error " + IntToString(::GetLastError()));
    00240         }
    00241         else
    00242         {
    00243 #if TRACE_WAIT
    00244                 static Timer t(Timer::MICROSECONDS);
    00245                 static unsigned long lastTime = 0;
    00246                 unsigned long timeBeforeWait = t.ElapsedTime();
    00247 #endif
    00248                 DWORD result = ::WaitForMultipleObjects(m_handles.size(), &m_handles[0], FALSE, milliseconds);
    00249 #if TRACE_WAIT
    00250                 if (milliseconds > 0)
    00251                 {
    00252                         unsigned long timeAfterWait = t.ElapsedTime();
    00253                         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());
    00254                         lastTime = timeAfterWait;
    00255                 }
    00256 #endif
    00257                 if (result >= WAIT_OBJECT_0 && result < WAIT_OBJECT_0 + m_handles.size())
    00258                 {
    00259 #if CRYPTOPP_DETECT_NO_WAIT
    00260                         if (result == m_lastResult)
    00261                                 m_sameResultCount++;
    00262                         else
    00263                         {
    00264                                 m_lastResult = result;
    00265                                 m_sameResultCount = 0;
    00266                         }
    00267 #endif
    00268                         return true;
    00269                 }
    00270                 else if (result == WAIT_TIMEOUT)
    00271                         return false;
    00272                 else
    00273                         throw Err("WaitObjectContainer: WaitForMultipleObjects failed with error " + IntToString(::GetLastError()));
    00274         }
    00275 }
    00276
    00277 #else
    00278
    00279 void WaitObjectContainer::AddReadFd(int fd)
    00280 {
    00281         FD_SET(fd, &m_readfds);
    00282         m_maxFd = STDMAX(m_maxFd, fd);
    00283 }
    00284
    00285 void WaitObjectContainer::AddWriteFd(int fd)
    00286 {
    00287         FD_SET(fd, &m_writefds);
    00288         m_maxFd = STDMAX(m_maxFd, fd);
    00289 }
    00290
    00291 bool WaitObjectContainer::Wait(unsigned long milliseconds)
    00292 {
    00293         if (m_noWait || m_maxFd == 0)
    00294                 return true;
    00295
    00296         timeval tv, *timeout;
    00297
    00298         if (milliseconds == INFINITE_TIME)
    00299                 timeout = NULL;
    00300         else
    00301         {
    00302                 tv.tv_sec = milliseconds / 1000;
    00303                 tv.tv_usec = (milliseconds % 1000) * 1000;
    00304                 timeout = &tv;
    00305         }
    00306
    00307         int result = select(m_maxFd+1, &m_readfds, &m_writefds, NULL, timeout);
    00308
    00309         if (result > 0)
    00310                 return true;
    00311         else if (result == 0)
    00312                 return false;
    00313         else
    00314                 throw Err("WaitObjectContainer: select failed with error " + errno);
    00315 }
    00316
    00317 #endif
    00318
    00319 // ********************************************************
    00320
    00321 bool Waitable::Wait(unsigned long milliseconds)
    00322 {
    00323         WaitObjectContainer container;
    00324         GetWaitObjects(container);
    00325         return container.Wait(milliseconds);
    00326 }
    00327
    00328 NAMESPACE_END
    00329
    00330 #endif