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 #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 (...) {}
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
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)
00091 Sleep(0);
00092 thread.terminate = true;
00093 threadHandles[i] = thread.threadHandle;
00094 }
00095 PulseEvent(m_startWaiting);
00096 ::WaitForMultipleObjects((DWORD)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 (...) {}
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((DWORD)handles.size(), &handles[0], FALSE, INFINITE);
00144
00145 if (result == WAIT_OBJECT_0)
00146 continue;
00147 SetEvent(thread.stopWaiting);
00148 if (!(result > WAIT_OBJECT_0 && result < WAIT_OBJECT_0 + handles.size()))
00149 {
00150 assert(!"error in WaitingThread");
00151 *thread.error = ::GetLastError();
00152 }
00153 }
00154
00155 return S_OK;
00156 }
00157
00158 void WaitObjectContainer::CreateThreads(unsigned int count)
00159 {
00160 unsigned int currentCount = (unsigned int)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
00202 static const unsigned int WAIT_OBJECTS_PER_THREAD = MAXIMUM_WAIT_OBJECTS-1;
00203 unsigned int nThreads = unsigned int((m_handles.size() + WAIT_OBJECTS_PER_THREAD - 1) / WAIT_OBJECTS_PER_THREAD);
00204 if (nThreads > MAXIMUM_WAIT_OBJECTS)
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)
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, (unsigned int)(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((DWORD)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