Commit c2b3ac89 authored by Gabriel Charette's avatar Gabriel Charette Committed by Commit Bot

[WaitableEvent] Remove TimedWaitUntil

It only had two callers and it'll become error prone once
ScopedTaskEnvironment's MOCK_TIME mode always mocks TimeTicks::Now().
TimeDelta based TimedWait()'s will remain real-time-all-the-time but
TimeWaitUntil() could be passed
TimeTicks::Now() + TimeDelta::FromSeconds(1) but wait forever or return
right away because Now() is mocked and is effectively a random value
compared to system time.

Context for MOCK_TIME : https://chromium-review.googlesource.com/c/chromium/src/+/1707870

This also enabled an opportunity to clean up the implementations
to invoke TimeTicks::Now() even less than before. All system calls
for waiting are based on delta. So we always use the initial
|wait_delta| and we only need to lookup Now() if the API can
spuriously return too early and |wait_delta| isn't Max().

R=kylechar@chromium.org

Bug: 905412
Change-Id: Ic73ee7ea286d023d2adee624478ff650f94767a3
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1708841Reviewed-by: default avatarkylechar <kylechar@chromium.org>
Reviewed-by: default avatarJoe Mason <joenotcharles@google.com>
Commit-Queue: Gabriel Charette <gab@chromium.org>
Auto-Submit: Gabriel Charette <gab@chromium.org>
Cr-Commit-Position: refs/heads/master@{#682232}
parent e200d762
...@@ -34,7 +34,6 @@ ...@@ -34,7 +34,6 @@
namespace base { namespace base {
class TimeDelta; class TimeDelta;
class TimeTicks;
// A WaitableEvent can be a useful thread synchronization tool when you want to // A WaitableEvent can be a useful thread synchronization tool when you want to
// allow one thread to wait for another thread to finish some work. For // allow one thread to wait for another thread to finish some work. For
...@@ -98,17 +97,12 @@ class BASE_EXPORT WaitableEvent { ...@@ -98,17 +97,12 @@ class BASE_EXPORT WaitableEvent {
void Wait(); void Wait();
// Wait up until wait_delta has passed for the event to be signaled. Returns // Wait up until wait_delta has passed for the event to be signaled. Returns
// true if the event was signaled. // true if the event was signaled. Handles spurious wakeups and guarantees
// that |wait_delta| will have elapsed if this returns false.
// //
// TimedWait can synchronise its own destruction like |Wait|. // TimedWait can synchronise its own destruction like |Wait|.
bool TimedWait(const TimeDelta& wait_delta); bool TimedWait(const TimeDelta& wait_delta);
// Wait up until end_time deadline has passed for the event to be signaled.
// Return true if the event was signaled.
//
// TimedWaitUntil can synchronise its own destruction like |Wait|.
bool TimedWaitUntil(const TimeTicks& end_time);
#if defined(OS_WIN) #if defined(OS_WIN)
HANDLE handle() const { return handle_.Get(); } HANDLE handle() const { return handle_.Get(); }
#endif #endif
......
...@@ -18,6 +18,7 @@ ...@@ -18,6 +18,7 @@
#include "base/posix/eintr_wrapper.h" #include "base/posix/eintr_wrapper.h"
#include "base/threading/scoped_blocking_call.h" #include "base/threading/scoped_blocking_call.h"
#include "base/threading/thread_restrictions.h" #include "base/threading/thread_restrictions.h"
#include "base/time/time.h"
#include "build/build_config.h" #include "build/build_config.h"
namespace base { namespace base {
...@@ -103,15 +104,14 @@ bool WaitableEvent::IsSignaled() { ...@@ -103,15 +104,14 @@ bool WaitableEvent::IsSignaled() {
} }
void WaitableEvent::Wait() { void WaitableEvent::Wait() {
bool result = TimedWaitUntil(TimeTicks::Max()); 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& wait_delta) { bool WaitableEvent::TimedWait(const TimeDelta& wait_delta) {
return TimedWaitUntil(TimeTicks::Now() + wait_delta); if (wait_delta <= TimeDelta())
} return IsSignaled();
bool WaitableEvent::TimedWaitUntil(const TimeTicks& end_time) {
// Record the event that this thread is blocking upon (for hang diagnosis) and // Record the event that this thread is blocking upon (for hang diagnosis) and
// consider blocked for scheduling purposes. Ignore this for non-blocking // consider blocked for scheduling purposes. Ignore this for non-blocking
// WaitableEvents. // WaitableEvents.
...@@ -128,7 +128,7 @@ bool WaitableEvent::TimedWaitUntil(const TimeTicks& end_time) { ...@@ -128,7 +128,7 @@ bool WaitableEvent::TimedWaitUntil(const TimeTicks& end_time) {
mach_msg_option_t options = MACH_RCV_MSG; mach_msg_option_t options = MACH_RCV_MSG;
if (!end_time.is_max()) if (!wait_delta.is_max())
options |= MACH_RCV_TIMEOUT | MACH_RCV_INTERRUPT; options |= MACH_RCV_TIMEOUT | MACH_RCV_INTERRUPT;
mach_msg_size_t rcv_size = sizeof(msg); mach_msg_size_t rcv_size = sizeof(msg);
...@@ -139,21 +139,32 @@ bool WaitableEvent::TimedWaitUntil(const TimeTicks& end_time) { ...@@ -139,21 +139,32 @@ bool WaitableEvent::TimedWaitUntil(const TimeTicks& end_time) {
rcv_size = 0; rcv_size = 0;
} }
kern_return_t kr; // TimeTicks takes care of overflow but we special case is_max() nonetheless
mach_msg_timeout_t timeout = MACH_MSG_TIMEOUT_NONE; // to avoid invoking Now() unnecessarily (same for the increment step of the
do { // for loop if the condition variable returns early).
if (!end_time.is_max()) { // Ref: https://crbug.com/910524#c7
timeout = std::max<int64_t>( const TimeTicks end_time =
0, (end_time - TimeTicks::Now()).InMillisecondsRoundedUp()); wait_delta.is_max() ? TimeTicks::Max() : TimeTicks::Now() + wait_delta;
} // Fake |kr| value to boostrap the for loop.
kern_return_t kr = MACH_RCV_INTERRUPTED;
for (mach_msg_timeout_t timeout = wait_delta.is_max()
? MACH_MSG_TIMEOUT_NONE
: wait_delta.InMillisecondsRoundedUp();
// If the thread is interrupted during mach_msg(), the system call will
// be restarted. However, the libsyscall wrapper does not adjust the
// timeout by the amount of time already waited. Using MACH_RCV_INTERRUPT
// will instead return from mach_msg(), so that the call can be retried
// with an adjusted timeout.
kr == MACH_RCV_INTERRUPTED;
timeout =
end_time.is_max()
? MACH_MSG_TIMEOUT_NONE
: std::max<int64_t>(
0,
(end_time - TimeTicks::Now()).InMillisecondsRoundedUp())) {
kr = mach_msg(&msg.header, options, 0, rcv_size, receive_right_->Name(), kr = mach_msg(&msg.header, options, 0, rcv_size, receive_right_->Name(),
timeout, MACH_PORT_NULL); timeout, MACH_PORT_NULL);
// If the thread is interrupted during mach_msg(), the system call }
// will be restarted. However, the libsyscall wrapper does not adjust
// the timeout by the amount of time already waited.
// Using MACH_RCV_INTERRUPT will instead return from mach_msg(),
// so that the call can be retried with an adjusted timeout.
} while (kr == MACH_RCV_INTERRUPTED);
if (kr == KERN_SUCCESS) { if (kr == KERN_SUCCESS) {
return true; return true;
......
...@@ -37,7 +37,7 @@ class TraceWaitableEvent { ...@@ -37,7 +37,7 @@ class TraceWaitableEvent {
bool TimedWaitUntil(const TimeTicks& end_time) { bool TimedWaitUntil(const TimeTicks& end_time) {
ElapsedTimer timer; ElapsedTimer timer;
const bool signaled = event_.TimedWaitUntil(end_time); const bool signaled = event_.TimedWait(end_time - timer.Begin());
total_wait_time_ += timer.Elapsed(); total_wait_time_ += timer.Elapsed();
++wait_samples_; ++wait_samples_;
return signaled; return signaled;
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
#include "base/synchronization/waitable_event.h" #include "base/synchronization/waitable_event.h"
#include "base/threading/scoped_blocking_call.h" #include "base/threading/scoped_blocking_call.h"
#include "base/threading/thread_restrictions.h" #include "base/threading/thread_restrictions.h"
#include "base/time/time.h"
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// A WaitableEvent on POSIX is implemented as a wait-list. Currently we don't // A WaitableEvent on POSIX is implemented as a wait-list. Currently we don't
...@@ -152,17 +153,14 @@ class SyncWaiter : public WaitableEvent::Waiter { ...@@ -152,17 +153,14 @@ class SyncWaiter : public WaitableEvent::Waiter {
}; };
void WaitableEvent::Wait() { void WaitableEvent::Wait() {
bool result = TimedWaitUntil(TimeTicks::Max()); 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& wait_delta) { bool WaitableEvent::TimedWait(const TimeDelta& wait_delta) {
// TimeTicks takes care of overflow including the cases when wait_delta if (wait_delta <= TimeDelta())
// is a maximum value. return IsSignaled();
return TimedWaitUntil(TimeTicks::Now() + wait_delta);
}
bool WaitableEvent::TimedWaitUntil(const TimeTicks& end_time) {
// Record the event that this thread is blocking upon (for hang diagnosis) and // Record the event that this thread is blocking upon (for hang diagnosis) and
// consider it blocked for scheduling purposes. Ignore this for non-blocking // consider it blocked for scheduling purposes. Ignore this for non-blocking
// WaitableEvents. // WaitableEvents.
...@@ -174,8 +172,6 @@ bool WaitableEvent::TimedWaitUntil(const TimeTicks& end_time) { ...@@ -174,8 +172,6 @@ bool WaitableEvent::TimedWaitUntil(const TimeTicks& end_time) {
scoped_blocking_call.emplace(BlockingType::MAY_BLOCK); scoped_blocking_call.emplace(BlockingType::MAY_BLOCK);
} }
const bool finite_time = !end_time.is_max();
kernel_->lock_.Acquire(); kernel_->lock_.Acquire();
if (kernel_->signaled_) { if (kernel_->signaled_) {
if (!kernel_->manual_reset_) { if (!kernel_->manual_reset_) {
...@@ -196,45 +192,45 @@ bool WaitableEvent::TimedWaitUntil(const TimeTicks& end_time) { ...@@ -196,45 +192,45 @@ bool WaitableEvent::TimedWaitUntil(const TimeTicks& end_time) {
Enqueue(&sw); Enqueue(&sw);
kernel_->lock_.Release(); kernel_->lock_.Release();
// We are violating locking order here by holding the SyncWaiter lock but not // We are violating locking order here by holding the SyncWaiter lock but not
// the WaitableEvent lock. However, this is safe because we don't lock @lock_ // the WaitableEvent lock. However, this is safe because we don't lock |lock_|
// again before unlocking it. // again before unlocking it.
for (;;) { // TimeTicks takes care of overflow but we special case is_max() nonetheless
// Only sample Now() if waiting for a |finite_time|. // to avoid invoking Now() unnecessarily (same for the increment step of the
Optional<TimeTicks> current_time; // for loop if the condition variable returns early).
if (finite_time) // Ref: https://crbug.com/910524#c7
current_time = TimeTicks::Now(); const TimeTicks end_time =
wait_delta.is_max() ? TimeTicks::Max() : TimeTicks::Now() + wait_delta;
for (TimeDelta remaining = wait_delta; remaining > TimeDelta() && !sw.fired();
remaining = end_time.is_max() ? TimeDelta::Max()
: end_time - TimeTicks::Now()) {
if (end_time.is_max())
sw.cv()->Wait();
else
sw.cv()->TimedWait(remaining);
}
if (sw.fired() || (finite_time && *current_time >= end_time)) { // Get the SyncWaiter signaled state before releasing the lock.
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 of
// of locking order), however, in between the two a signal could be fired // locking order), however, in between the two a signal could be fired and
// and @sw would accept it, however we will still return false, so the // |sw| would accept it, however we will still return false, so the signal
// signal would be lost on an auto-reset WaitableEvent. Thus we call // would be lost on an auto-reset WaitableEvent. Thus we call Disable which
// Disable which makes sw::Fire return false. // makes sw::Fire return false.
sw.Disable(); sw.Disable();
sw.lock()->Release(); sw.lock()->Release();
// This is a bug that has been enshrined in the interface of // This is a bug that has been enshrined in the interface of WaitableEvent
// WaitableEvent now: |Dequeue| is called even when |sw.fired()| is true, // now: |Dequeue| is called even when |sw.fired()| is true, even though it'll
// even though it'll always return false in that case. However, taking // always return false in that case. However, taking the lock ensures that
// the lock ensures that |Signal| has completed before we return and // |Signal| has completed before we return and means that a WaitableEvent can
// means that a WaitableEvent can synchronise its own destruction. // synchronise its own destruction.
kernel_->lock_.Acquire(); kernel_->lock_.Acquire();
kernel_->Dequeue(&sw, &sw); kernel_->Dequeue(&sw, &sw);
kernel_->lock_.Release(); kernel_->lock_.Release();
return return_value; return return_value;
}
if (finite_time) {
const TimeDelta max_wait(end_time - *current_time);
sw.cv()->TimedWait(max_wait);
} else {
sw.cv()->Wait();
}
}
} }
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
......
...@@ -235,40 +235,31 @@ TEST(WaitableEventTest, SubMsTimedWait) { ...@@ -235,40 +235,31 @@ TEST(WaitableEventTest, SubMsTimedWait) {
EXPECT_GE(TimeTicks::Now() - start_time, delay); EXPECT_GE(TimeTicks::Now() - start_time, delay);
} }
// Tests that TimedWaitUntil can be safely used with various end_time deadline // Tests that timeouts of zero return immediately (true if already signaled,
// values. // false otherwise).
TEST(WaitableEventTest, TimedWaitUntil) { TEST(WaitableEventTest, ZeroTimeout) {
WaitableEvent ev(WaitableEvent::ResetPolicy::AUTOMATIC, WaitableEvent ev;
WaitableEvent::InitialState::NOT_SIGNALED); TimeTicks start_time = TimeTicks::Now();
EXPECT_FALSE(ev.TimedWait(TimeDelta()));
TimeTicks start_time(TimeTicks::Now()); EXPECT_LT(TimeTicks::Now() - start_time, TimeDelta::FromMilliseconds(1));
TimeDelta delay = TimeDelta::FromMilliseconds(10);
// Should be OK to wait for the current time or time in the past.
// That should end promptly and be equivalent to IsSignalled.
EXPECT_FALSE(ev.TimedWaitUntil(start_time));
EXPECT_FALSE(ev.TimedWaitUntil(start_time - delay));
// Should be OK to wait for zero TimeTicks().
EXPECT_FALSE(ev.TimedWaitUntil(TimeTicks()));
// Waiting for a time in the future shouldn't end before the deadline
// if the event isn't signalled.
EXPECT_FALSE(ev.TimedWaitUntil(start_time + delay));
EXPECT_GE(TimeTicks::Now() - start_time, delay);
// Test that passing TimeTicks::Max to TimedWaitUntil is valid and isn't ev.Signal();
// the same as passing TimeTicks(). Also verifies that signaling event
// ends the wait promptly.
WaitableEventSignaler signaler(delay, &ev);
PlatformThreadHandle thread;
start_time = TimeTicks::Now(); start_time = TimeTicks::Now();
PlatformThread::Create(0, &signaler, &thread); EXPECT_TRUE(ev.TimedWait(TimeDelta()));
EXPECT_LT(TimeTicks::Now() - start_time, TimeDelta::FromMilliseconds(1));
}
EXPECT_TRUE(ev.TimedWaitUntil(TimeTicks::Max())); // Same as ZeroTimeout for negative timeouts.
EXPECT_GE(TimeTicks::Now() - start_time, delay); TEST(WaitableEventTest, NegativeTimeout) {
WaitableEvent ev;
TimeTicks start_time = TimeTicks::Now();
EXPECT_FALSE(ev.TimedWait(TimeDelta::FromMilliseconds(-10)));
EXPECT_LT(TimeTicks::Now() - start_time, TimeDelta::FromMilliseconds(1));
PlatformThread::Join(thread); ev.Signal();
start_time = TimeTicks::Now();
EXPECT_TRUE(ev.TimedWait(TimeDelta::FromMilliseconds(-10)));
EXPECT_LT(TimeTicks::Now() - start_time, TimeDelta::FromMilliseconds(1));
} }
} // namespace base } // namespace base
...@@ -72,44 +72,8 @@ void WaitableEvent::Wait() { ...@@ -72,44 +72,8 @@ void WaitableEvent::Wait() {
DCHECK_EQ(WAIT_OBJECT_0, result); DCHECK_EQ(WAIT_OBJECT_0, result);
} }
namespace {
// Helper function called from TimedWait and TimedWaitUntil.
bool WaitUntil(HANDLE handle, const TimeTicks& now, const TimeTicks& end_time) {
TimeDelta delta = end_time - now;
DCHECK_GT(delta, TimeDelta());
do {
// On Windows, waiting for less than 1 ms results in WaitForSingleObject
// returning promptly which may result in the caller code spinning.
// We need to ensure that we specify at least the minimally possible 1 ms
// delay unless the initial timeout was exactly zero.
delta = std::max(delta, TimeDelta::FromMilliseconds(1));
// Truncate the timeout to milliseconds.
DWORD timeout_ms = saturated_cast<DWORD>(delta.InMilliseconds());
DWORD result = WaitForSingleObject(handle, timeout_ms);
DCHECK(result == WAIT_OBJECT_0 || result == WAIT_TIMEOUT)
<< "Unexpected WaitForSingleObject result " << result;
switch (result) {
case WAIT_OBJECT_0:
return true;
case WAIT_TIMEOUT:
// TimedWait can time out earlier than the specified |timeout| on
// Windows. To make this consistent with the posix implementation we
// should guarantee that TimedWait doesn't return earlier than the
// specified |max_time| and wait again for the remaining time.
delta = end_time - TimeTicks::Now();
break;
}
} while (delta > TimeDelta());
return false;
}
} // namespace
bool WaitableEvent::TimedWait(const TimeDelta& wait_delta) { bool WaitableEvent::TimedWait(const TimeDelta& wait_delta) {
DCHECK_GE(wait_delta, TimeDelta()); if (wait_delta <= TimeDelta())
if (wait_delta.is_zero())
return IsSignaled(); return IsSignaled();
// Record the event that this thread is blocking upon (for hang diagnosis) and // Record the event that this thread is blocking upon (for hang diagnosis) and
...@@ -123,32 +87,36 @@ bool WaitableEvent::TimedWait(const TimeDelta& wait_delta) { ...@@ -123,32 +87,36 @@ bool WaitableEvent::TimedWait(const TimeDelta& wait_delta) {
scoped_blocking_call.emplace(BlockingType::MAY_BLOCK); scoped_blocking_call.emplace(BlockingType::MAY_BLOCK);
} }
TimeTicks now(TimeTicks::Now()); // TimeTicks takes care of overflow but we special case is_max() nonetheless
// TimeTicks takes care of overflow including the cases when wait_delta // to avoid invoking Now() unnecessarily.
// is a maximum value. // WaitForSingleObject(handle_.Get(), INFINITE) doesn't spuriously wakeup so
return WaitUntil(handle_.Get(), now, now + wait_delta); // we don't need to worry about is_max() for the increment phase of the loop.
} const TimeTicks end_time =
wait_delta.is_max() ? TimeTicks::Max() : TimeTicks::Now() + wait_delta;
bool WaitableEvent::TimedWaitUntil(const TimeTicks& end_time) { for (TimeDelta remaining = wait_delta; remaining > TimeDelta();
if (end_time.is_null()) remaining = end_time - TimeTicks::Now()) {
return IsSignaled(); // Truncate the timeout to milliseconds, rounded up to avoid spinning
// (either by returning too early or because a < 1ms timeout on Windows
// Record the event that this thread is blocking upon (for hang diagnosis) and // tends to return immediately).
// consider it blocked for scheduling purposes. Ignore this for non-blocking const DWORD timeout_ms =
// WaitableEvents. remaining.is_max()
Optional<debug::ScopedEventWaitActivity> event_activity; ? INFINITE
Optional<internal::ScopedBlockingCallWithBaseSyncPrimitives> : saturated_cast<DWORD>(remaining.InMillisecondsRoundedUp());
scoped_blocking_call; const DWORD result = WaitForSingleObject(handle_.Get(), timeout_ms);
if (waiting_is_blocking_) { DCHECK(result == WAIT_OBJECT_0 || result == WAIT_TIMEOUT)
event_activity.emplace(this); << "Unexpected WaitForSingleObject result " << result;
scoped_blocking_call.emplace(BlockingType::MAY_BLOCK); switch (result) {
case WAIT_OBJECT_0:
return true;
case WAIT_TIMEOUT:
// TimedWait can time out earlier than the specified |timeout| on
// Windows. To make this consistent with the posix implementation we
// should guarantee that TimedWait doesn't return earlier than the
// specified |max_time| and wait again for the remaining time.
continue;
} }
}
TimeTicks now(TimeTicks::Now()); return false;
if (end_time <= now)
return IsSignaled();
return WaitUntil(handle_.Get(), now, end_time);
} }
// static // static
......
...@@ -103,7 +103,6 @@ class ScopedTaskEnvironment { ...@@ -103,7 +103,6 @@ class ScopedTaskEnvironment {
// MOCK_TIME as expected, e.g.: // MOCK_TIME as expected, e.g.:
// PlatformThread::Sleep // PlatformThread::Sleep
// WaitableEvent::TimedWait // WaitableEvent::TimedWait
// WaitableEvent::TimedWaitUntil
// ConditionVariable::TimedWait // ConditionVariable::TimedWait
// //
// TODO(crbug.com/905412): Make MOCK_TIME always mock Time/TimeTicks::Now(). // TODO(crbug.com/905412): Make MOCK_TIME always mock Time/TimeTicks::Now().
......
...@@ -707,13 +707,14 @@ void ReportInstalledExtensions(JsonParserAPI* json_parser, ...@@ -707,13 +707,14 @@ void ReportInstalledExtensions(JsonParserAPI* json_parser,
GetNonWhitelistedDefaultExtensions(json_parser, &default_extension_policies, GetNonWhitelistedDefaultExtensions(json_parser, &default_extension_policies,
&default_extensions_done); &default_extensions_done);
// Wait for all asynchronous parsing to be done // Wait for all asynchronous parsing to be done with a single timeout for all
// phases combined.
const base::TimeTicks end_time = const base::TimeTicks end_time =
base::TimeTicks::Now() + base::TimeTicks::Now() +
base::TimeDelta::FromMilliseconds(kParseAttemptTimeoutMilliseconds); base::TimeDelta::FromMilliseconds(kParseAttemptTimeoutMilliseconds);
extension_settings_done.TimedWaitUntil(end_time); extension_settings_done.TimedWait(end_time - base::TimeTicks::Now());
master_preferences_done.TimedWaitUntil(end_time); master_preferences_done.TimedWait(end_time - base::TimeTicks::Now());
default_extensions_done.TimedWaitUntil(end_time); default_extensions_done.TimedWait(end_time - base::TimeTicks::Now());
// Log extensions that were found // Log extensions that were found
for (const ExtensionPolicyRegistryEntry& policy : for (const ExtensionPolicyRegistryEntry& policy :
......
...@@ -92,7 +92,8 @@ ForceInstalledExtensionScannerImpl::GetForceInstalledExtensions( ...@@ -92,7 +92,8 @@ ForceInstalledExtensionScannerImpl::GetForceInstalledExtensions(
result.emplace(std::move(extension)); result.emplace(std::move(extension));
} }
non_whitelist_default_extensions_done.TimedWaitUntil(end_time); non_whitelist_default_extensions_done.TimedWait(end_time -
base::TimeTicks::Now());
while (policy_files_default_extensions.size() > 0) { while (policy_files_default_extensions.size() > 0) {
ExtensionPolicyFile file = ExtensionPolicyFile file =
std::move(policy_files_default_extensions.back()); std::move(policy_files_default_extensions.back());
...@@ -109,7 +110,7 @@ ForceInstalledExtensionScannerImpl::GetForceInstalledExtensions( ...@@ -109,7 +110,7 @@ ForceInstalledExtensionScannerImpl::GetForceInstalledExtensions(
result.emplace(std::move(extension)); result.emplace(std::move(extension));
} }
settings_force_installed_done.TimedWaitUntil(end_time); settings_force_installed_done.TimedWait(end_time - base::TimeTicks::Now());
while (policy_registry_entries_force_installed.size() > 0) { while (policy_registry_entries_force_installed.size() > 0) {
ExtensionPolicyRegistryEntry entry = ExtensionPolicyRegistryEntry entry =
std::move(policy_registry_entries_force_installed.back()); std::move(policy_registry_entries_force_installed.back());
...@@ -126,7 +127,7 @@ ForceInstalledExtensionScannerImpl::GetForceInstalledExtensions( ...@@ -126,7 +127,7 @@ ForceInstalledExtensionScannerImpl::GetForceInstalledExtensions(
result.emplace(std::move(extension)); result.emplace(std::move(extension));
} }
master_preferences_done.TimedWaitUntil(end_time); master_preferences_done.TimedWait(end_time - base::TimeTicks::Now());
while (policy_files_master_preferences.size() > 0) { while (policy_files_master_preferences.size() > 0) {
ExtensionPolicyFile file = ExtensionPolicyFile file =
std::move(policy_files_master_preferences.back()); std::move(policy_files_master_preferences.back());
......
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