Commit ac3c7563 authored by rvargas's avatar rvargas Committed by Commit bot

Make sure that WaitableEvent::TimedWait works fine with large timeouts.

TimedWait(TimeDelta::Max()) should do the same as Wait() (although should not
be used, given that there is a dedicated method to do that).

More generally, TimedWait should accept random, unreasonable large timeouts.

BUG=465948
TESTS=base_unittests

Review URL: https://codereview.chromium.org/999753005

Cr-Commit-Position: refs/heads/master@{#322469}
parent 06ca24d5
...@@ -81,6 +81,8 @@ class BASE_EXPORT WaitableEvent { ...@@ -81,6 +81,8 @@ class BASE_EXPORT WaitableEvent {
// does not necessarily mean that max_time was exceeded. // does not necessarily mean that max_time was exceeded.
// //
// TimedWait can synchronise its own destruction like |Wait|. // TimedWait can synchronise its own destruction like |Wait|.
//
// Passing a negative |max_time| is not supported.
bool TimedWait(const TimeDelta& max_time); bool TimedWait(const TimeDelta& max_time);
#if defined(OS_WIN) #if defined(OS_WIN)
......
...@@ -151,14 +151,14 @@ class SyncWaiter : public WaitableEvent::Waiter { ...@@ -151,14 +151,14 @@ class SyncWaiter : public WaitableEvent::Waiter {
}; };
void WaitableEvent::Wait() { void WaitableEvent::Wait() {
bool result = TimedWait(TimeDelta::FromSeconds(-1)); bool result = TimedWait(TimeDelta::Max());
DCHECK(result) << "TimedWait() should never fail with infinite timeout"; DCHECK(result) << "TimedWait() should never fail with infinite timeout";
} }
bool WaitableEvent::TimedWait(const TimeDelta& max_time) { bool WaitableEvent::TimedWait(const TimeDelta& max_time) {
DCHECK_GE(max_time, TimeDelta());
base::ThreadRestrictions::AssertWaitAllowed(); base::ThreadRestrictions::AssertWaitAllowed();
const TimeTicks end_time(TimeTicks::Now() + max_time); const TimeTicks end_time(TimeTicks::Now() + max_time);
const bool finite_time = max_time.ToInternalValue() >= 0;
kernel_->lock_.Acquire(); kernel_->lock_.Acquire();
if (kernel_->signaled_) { if (kernel_->signaled_) {
...@@ -184,7 +184,7 @@ bool WaitableEvent::TimedWait(const TimeDelta& max_time) { ...@@ -184,7 +184,7 @@ bool WaitableEvent::TimedWait(const TimeDelta& max_time) {
for (;;) { for (;;) {
const TimeTicks current_time(TimeTicks::Now()); const TimeTicks current_time(TimeTicks::Now());
if (sw.fired() || (finite_time && current_time >= end_time)) { if (sw.fired() || current_time >= end_time) {
const bool return_value = sw.fired(); const bool return_value = sw.fired();
// We can't acquire @lock_ before releasing the SyncWaiter lock (because // We can't acquire @lock_ before releasing the SyncWaiter lock (because
...@@ -207,12 +207,7 @@ bool WaitableEvent::TimedWait(const TimeDelta& max_time) { ...@@ -207,12 +207,7 @@ bool WaitableEvent::TimedWait(const TimeDelta& max_time) {
return return_value; return return_value;
} }
if (finite_time) { sw.cv()->TimedWait(end_time - current_time);
const TimeDelta max_wait(end_time - current_time);
sw.cv()->TimedWait(max_wait);
} else {
sw.cv()->Wait();
}
} }
} }
......
...@@ -73,29 +73,27 @@ TEST(WaitableEventTest, WaitManyShortcut) { ...@@ -73,29 +73,27 @@ TEST(WaitableEventTest, WaitManyShortcut) {
class WaitableEventSignaler : public PlatformThread::Delegate { class WaitableEventSignaler : public PlatformThread::Delegate {
public: public:
WaitableEventSignaler(double seconds, WaitableEvent* ev) WaitableEventSignaler(TimeDelta delay, WaitableEvent* event)
: seconds_(seconds), : delay_(delay),
ev_(ev) { event_(event) {
} }
void ThreadMain() override { void ThreadMain() override {
PlatformThread::Sleep(TimeDelta::FromSecondsD(seconds_)); PlatformThread::Sleep(delay_);
ev_->Signal(); event_->Signal();
} }
private: private:
const double seconds_; const TimeDelta delay_;
WaitableEvent *const ev_; WaitableEvent* event_;
}; };
// Tests that a WaitableEvent can be safely deleted when |Wait| is done without
// additional synchronization.
TEST(WaitableEventTest, WaitAndDelete) { TEST(WaitableEventTest, WaitAndDelete) {
// This test tests that if a WaitableEvent can be safely deleted
// when |Wait| is done without additional synchrnization.
// If this test crashes, it is a bug.
WaitableEvent* ev = new WaitableEvent(false, false); WaitableEvent* ev = new WaitableEvent(false, false);
WaitableEventSignaler signaler(0.01, ev); WaitableEventSignaler signaler(TimeDelta::FromMilliseconds(10), ev);
PlatformThreadHandle thread; PlatformThreadHandle thread;
PlatformThread::Create(0, &signaler, &thread); PlatformThread::Create(0, &signaler, &thread);
...@@ -105,16 +103,14 @@ TEST(WaitableEventTest, WaitAndDelete) { ...@@ -105,16 +103,14 @@ TEST(WaitableEventTest, WaitAndDelete) {
PlatformThread::Join(thread); PlatformThread::Join(thread);
} }
// Tests that a WaitableEvent can be safely deleted when |WaitMany| is done
// without additional synchronization.
TEST(WaitableEventTest, WaitMany) { TEST(WaitableEventTest, WaitMany) {
// This test tests that if a WaitableEvent can be safely deleted
// when |WaitMany| is done without additional synchrnization.
// If this test crashes, it is a bug.
WaitableEvent* ev[5]; WaitableEvent* ev[5];
for (unsigned i = 0; i < 5; ++i) for (unsigned i = 0; i < 5; ++i)
ev[i] = new WaitableEvent(false, false); ev[i] = new WaitableEvent(false, false);
WaitableEventSignaler signaler(0.01, ev[2]); WaitableEventSignaler signaler(TimeDelta::FromMilliseconds(10), ev[2]);
PlatformThreadHandle thread; PlatformThreadHandle thread;
PlatformThread::Create(0, &signaler, &thread); PlatformThread::Create(0, &signaler, &thread);
...@@ -127,4 +123,22 @@ TEST(WaitableEventTest, WaitMany) { ...@@ -127,4 +123,22 @@ TEST(WaitableEventTest, WaitMany) {
EXPECT_EQ(2u, index); EXPECT_EQ(2u, index);
} }
// Tests that using TimeDelta::Max() on TimedWait() is not the same as passing
// a timeout of 0. (crbug.com/465948)
TEST(WaitableEventTest, TimedWait) {
WaitableEvent* ev = new WaitableEvent(false, false);
TimeDelta thread_delay = TimeDelta::FromMilliseconds(10);
WaitableEventSignaler signaler(thread_delay, ev);
PlatformThreadHandle thread;
TimeTicks start = TimeTicks::Now();
PlatformThread::Create(0, &signaler, &thread);
ev->TimedWait(TimeDelta::Max());
EXPECT_GE(TimeTicks::Now() - start, thread_delay);
delete ev;
PlatformThread::Join(thread);
}
} // namespace base } // namespace base
...@@ -4,10 +4,10 @@ ...@@ -4,10 +4,10 @@
#include "base/synchronization/waitable_event.h" #include "base/synchronization/waitable_event.h"
#include <math.h>
#include <windows.h> #include <windows.h>
#include "base/logging.h" #include "base/logging.h"
#include "base/numerics/safe_conversions.h"
#include "base/threading/thread_restrictions.h" #include "base/threading/thread_restrictions.h"
#include "base/time/time.h" #include "base/time/time.h"
...@@ -37,7 +37,7 @@ void WaitableEvent::Signal() { ...@@ -37,7 +37,7 @@ void WaitableEvent::Signal() {
} }
bool WaitableEvent::IsSignaled() { bool WaitableEvent::IsSignaled() {
return TimedWait(TimeDelta::FromMilliseconds(0)); return TimedWait(TimeDelta());
} }
void WaitableEvent::Wait() { void WaitableEvent::Wait() {
...@@ -50,13 +50,13 @@ void WaitableEvent::Wait() { ...@@ -50,13 +50,13 @@ void WaitableEvent::Wait() {
bool WaitableEvent::TimedWait(const TimeDelta& max_time) { bool WaitableEvent::TimedWait(const TimeDelta& max_time) {
base::ThreadRestrictions::AssertWaitAllowed(); base::ThreadRestrictions::AssertWaitAllowed();
DCHECK(max_time >= TimeDelta::FromMicroseconds(0)); DCHECK_GE(max_time, TimeDelta());
// Be careful here. TimeDelta has a precision of microseconds, but this API // Be careful here. TimeDelta has a precision of microseconds, but this API
// is in milliseconds. If there are 5.5ms left, should the delay be 5 or 6? // is in milliseconds. If there are 5.5ms left, should the delay be 5 or 6?
// It should be 6 to avoid returning too early. // It should be 6 to avoid returning too early.
double timeout = ceil(max_time.InMillisecondsF()); DWORD timeout = saturated_cast<DWORD>(max_time.InMillisecondsRoundedUp());
DWORD result = WaitForSingleObject(handle_.Get(),
static_cast<DWORD>(timeout)); DWORD result = WaitForSingleObject(handle_.Get(), timeout);
switch (result) { switch (result) {
case WAIT_OBJECT_0: case WAIT_OBJECT_0:
return true; return true;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment