Fixed a serious flaw in wrapper for posix condition variable wait

function.  Because synergy doesn't use posix cancellation, it
cannot wake up a thread waiting on a condition variable.  So
the wrapper would wake up periodically to test if the thread was
cancelled (according to synergy's cancellation state) then go
back to waiting.  Well the condition could be signalled while
we're testing and be lost and we'd never return from the wait.
So now we wake up after a maximum timeout and return to the
caller.  The caller must check for this spurious wakeup but all
callers should do this anyway so we're okay there.
This commit is contained in:
crs 2004-11-11 19:23:14 +00:00
parent c135432040
commit 014578b875
1 changed files with 30 additions and 60 deletions

View File

@ -213,73 +213,43 @@ bool
CArchMultithreadPosix::waitCondVar(CArchCond cond,
CArchMutex mutex, double timeout)
{
// we can't wait on a condition variable and also wake it up for
// cancellation since we don't use posix cancellation. so we
// must wake up periodically to check for cancellation. we
// can't simply go back to waiting after the check since the
// condition may have changed and we'll have lost the signal.
// so we have to return to the caller. since the caller will
// always check for spurious wakeups the only drawback here is
// performance: we're waking up a lot more than desired.
static const double maxCancellationLatency = 0.1;
if (timeout < 0.0 || timeout > maxCancellationLatency) {
timeout = maxCancellationLatency;
}
// see if we should cancel this thread
testCancelThread();
// get final time
struct timeval now;
gettimeofday(&now, NULL);
struct timespec finalTime;
finalTime.tv_sec = now.tv_sec;
finalTime.tv_nsec = now.tv_usec * 1000;
if (timeout >= 0.0) {
const long timeout_sec = (long)timeout;
const long timeout_nsec = (long)(1.0e+9 * (timeout - timeout_sec));
finalTime.tv_sec += timeout_sec;
finalTime.tv_nsec += timeout_nsec;
if (finalTime.tv_nsec >= 1000000000) {
finalTime.tv_nsec -= 1000000000;
finalTime.tv_sec += 1;
}
finalTime.tv_sec = now.tv_sec;
finalTime.tv_nsec = now.tv_usec * 1000;
long timeout_sec = (long)timeout;
long timeout_nsec = (long)(1.0e+9 * (timeout - timeout_sec));
finalTime.tv_sec += timeout_sec;
finalTime.tv_nsec += timeout_nsec;
if (finalTime.tv_nsec >= 1000000000) {
finalTime.tv_nsec -= 1000000000;
finalTime.tv_sec += 1;
}
// repeat until we reach the final time
int status;
for (;;) {
// get current time
gettimeofday(&now, NULL);
struct timespec endTime;
endTime.tv_sec = now.tv_sec;
endTime.tv_nsec = now.tv_usec * 1000;
// wait
int status = pthread_cond_timedwait(&cond->m_cond,
&mutex->m_mutex, &finalTime);
// done if past final timeout
if (timeout >= 0.0) {
if (endTime.tv_sec > finalTime.tv_sec ||
(endTime.tv_sec == finalTime.tv_sec &&
endTime.tv_nsec >= finalTime.tv_nsec)) {
status = ETIMEDOUT;
break;
}
}
// compute the next timeout
endTime.tv_nsec += 50000000;
if (endTime.tv_nsec >= 1000000000) {
endTime.tv_nsec -= 1000000000;
endTime.tv_sec += 1;
}
// don't wait past final timeout
if (timeout >= 0.0) {
if (endTime.tv_sec > finalTime.tv_sec ||
(endTime.tv_sec == finalTime.tv_sec &&
endTime.tv_nsec >= finalTime.tv_nsec)) {
endTime = finalTime;
}
}
// see if we should cancel this thread
testCancelThread();
// wait
status = pthread_cond_timedwait(&cond->m_cond,
&mutex->m_mutex, &endTime);
// check for cancel again
testCancelThread();
// check wait status
if (status != ETIMEDOUT && status != EINTR) {
break;
}
}
// check for cancel again
testCancelThread();
switch (status) {
case 0: