Modify EventPosix to prevent spurious wakeups.

pthread_cond_{timedwait,wait} are allowed to spuriously wake up as if
they were signaled. To prevent this being interpreted as a "real"
signaling of the event (ThreadWrapper for instance depends on it being
an actual signal) we need to check whether the event was actually
signalled or not.

BUG=4413
R=andresp@webrtc.org, tommi@webrtc.org

Review URL: https://webrtc-codereview.appspot.com/49369004

Cr-Commit-Position: refs/heads/master@{#8752}
git-svn-id: http://webrtc.googlecode.com/svn/trunk@8752 4adac7df-926f-26a2-2b94-8c16560cd09d
This commit is contained in:
pbos@webrtc.org 2015-03-17 13:11:15 +00:00
parent a78a94e838
commit a846371ace
2 changed files with 44 additions and 98 deletions

View File

@ -26,64 +26,30 @@ const long int E6 = 1000000;
const long int E9 = 1000 * E6;
EventWrapper* EventPosix::Create() {
EventPosix* ptr = new EventPosix;
if (!ptr) {
return NULL;
}
const int error = ptr->Construct();
if (error) {
delete ptr;
return NULL;
}
return ptr;
return new EventPosix();
}
EventPosix::EventPosix()
: timer_thread_(0),
: event_set_(false),
timer_thread_(nullptr),
timer_event_(0),
created_at_(),
periodic_(false),
time_(0),
count_(0),
state_(kDown) {
}
int EventPosix::Construct() {
// Set start time to zero
memset(&created_at_, 0, sizeof(created_at_));
count_(0) {
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
int result = pthread_mutex_init(&mutex_, &attr);
if (result != 0) {
return -1;
}
pthread_mutex_init(&mutex_, &attr);
#ifdef WEBRTC_CLOCK_TYPE_REALTIME
result = pthread_cond_init(&cond_, 0);
if (result != 0) {
return -1;
}
pthread_cond_init(&cond_, 0);
#else
pthread_condattr_t cond_attr;
result = pthread_condattr_init(&cond_attr);
if (result != 0) {
return -1;
}
result = pthread_condattr_setclock(&cond_attr, CLOCK_MONOTONIC);
if (result != 0) {
return -1;
}
result = pthread_cond_init(&cond_, &cond_attr);
if (result != 0) {
return -1;
}
result = pthread_condattr_destroy(&cond_attr);
if (result != 0) {
return -1;
}
pthread_condattr_init(&cond_attr);
pthread_condattr_setclock(&cond_attr, CLOCK_MONOTONIC);
pthread_cond_init(&cond_, &cond_attr);
pthread_condattr_destroy(&cond_attr);
#endif
return 0;
}
EventPosix::~EventPosix() {
@ -92,24 +58,20 @@ EventPosix::~EventPosix() {
pthread_mutex_destroy(&mutex_);
}
// TODO(pbos): Make this void.
bool EventPosix::Set() {
if (0 != pthread_mutex_lock(&mutex_)) {
return false;
}
state_ = kUp;
// Release all waiting threads
pthread_cond_broadcast(&cond_);
CHECK_EQ(0, pthread_mutex_lock(&mutex_));
event_set_ = true;
pthread_cond_signal(&cond_);
pthread_mutex_unlock(&mutex_);
return true;
}
EventTypeWrapper EventPosix::Wait(unsigned long timeout) {
int ret_val = 0;
if (0 != pthread_mutex_lock(&mutex_)) {
return kEventError;
}
CHECK_EQ(0, pthread_mutex_lock(&mutex_));
if (kDown == state_) {
if (!event_set_) {
if (WEBRTC_EVENT_INFINITE != timeout) {
timespec end_at;
#ifndef WEBRTC_MAC
@ -133,52 +95,43 @@ EventTypeWrapper EventPosix::Wait(unsigned long timeout) {
end_at.tv_sec++;
end_at.tv_nsec -= E9;
}
ret_val = pthread_cond_timedwait(&cond_, &mutex_, &end_at);
while (ret_val == 0 && !event_set_)
ret_val = pthread_cond_timedwait(&cond_, &mutex_, &end_at);
} else {
ret_val = pthread_cond_wait(&cond_, &mutex_);
while (ret_val == 0 && !event_set_)
ret_val = pthread_cond_wait(&cond_, &mutex_);
}
}
// Be careful to only change the state if we're about to report that the
// event was signaled.
if (ret_val == 0) {
// state_ might already be kDown, in case of multiple waiters. That's OK.
state_ = kDown;
}
DCHECK(ret_val == 0 || ret_val == ETIMEDOUT);
// Reset and signal if set, regardless of why the thread woke up.
if (event_set_) {
ret_val = 0;
event_set_ = false;
}
pthread_mutex_unlock(&mutex_);
switch (ret_val) {
case 0:
return kEventSignaled;
case ETIMEDOUT:
return kEventTimeout;
default:
return kEventError;
}
return ret_val == 0 ? kEventSignaled : kEventTimeout;
}
EventTypeWrapper EventPosix::Wait(timespec& wake_at) {
EventTypeWrapper EventPosix::Wait(timespec* end_at) {
int ret_val = 0;
if (0 != pthread_mutex_lock(&mutex_)) {
return kEventError;
}
CHECK_EQ(0, pthread_mutex_lock(&mutex_));
if (kUp != state_) {
ret_val = pthread_cond_timedwait(&cond_, &mutex_, &wake_at);
}
state_ = kDown;
while (ret_val == 0 && !event_set_)
ret_val = pthread_cond_timedwait(&cond_, &mutex_, end_at);
DCHECK(ret_val == 0 || ret_val == ETIMEDOUT);
// Reset and signal if set, regardless of why the thread woke up.
if (event_set_) {
ret_val = 0;
event_set_ = false;
}
pthread_mutex_unlock(&mutex_);
switch (ret_val) {
case 0:
return kEventSignaled;
case ETIMEDOUT:
return kEventTimeout;
default:
return kEventError;
}
return ret_val == 0 ? kEventSignaled : kEventTimeout;
}
bool EventPosix::StartTimer(bool periodic, unsigned long time) {
@ -246,14 +199,8 @@ bool EventPosix::Process() {
}
pthread_mutex_unlock(&mutex_);
switch (timer_event_->Wait(end_at)) {
case kEventSignaled:
return true;
case kEventError:
return false;
case kEventTimeout:
break;
}
if (timer_event_->Wait(&end_at) == kEventSignaled)
return true;
pthread_mutex_lock(&mutex_);
if (periodic_ || count_ == 1)

View File

@ -39,15 +39,15 @@ class EventPosix : public EventWrapper {
private:
EventPosix();
int Construct();
static bool Run(ThreadObj obj);
bool Process();
EventTypeWrapper Wait(timespec& wake_at);
EventTypeWrapper Wait(timespec* end_at);
private:
pthread_cond_t cond_;
pthread_mutex_t mutex_;
bool event_set_;
ThreadWrapper* timer_thread_;
EventPosix* timer_event_;
@ -56,7 +56,6 @@ class EventPosix : public EventWrapper {
bool periodic_;
unsigned long time_; // In ms
unsigned long count_;
State state_;
};
} // namespace webrtc