Main Page | Namespace List | Class Hierarchy | Alphabetical List | Class List | File List | 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

Generated on Wed Jul 21 19:15:36 2004 for Crypto++ by doxygen 1.3.7-20040704