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(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(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 = 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 = (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, 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