Chromium Code Reviews| Index: base/debug/synthetic_delay.cc |
| diff --git a/base/debug/synthetic_delay.cc b/base/debug/synthetic_delay.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..baec13bb7bd7be9fd0c122906206cada008d7f55 |
| --- /dev/null |
| +++ b/base/debug/synthetic_delay.cc |
| @@ -0,0 +1,118 @@ |
| +// Copyright (c) 2013 The Chromium Authors. All rights reserved. |
| +// Use of this source code is governed by a BSD-style license that can be |
| +// found in the LICENSE file. |
| + |
| +#include "base/debug/synthetic_delay.h" |
| +#include "base/debug/trace_event.h" |
| +#include "base/memory/singleton.h" |
| +#include "base/synchronization/lock.h" |
| +#include "base/threading/platform_thread.h" |
| + |
| +namespace base { |
| +namespace debug { |
| +namespace synthetic_delay_internal { |
| + |
| +SyntheticDelay* GetOrCreateDelay(const char* name, |
| + subtle::AtomicWord* impl_ptr) { |
| + SyntheticDelay* delay_impl = |
| + reinterpret_cast<SyntheticDelay*>(base::subtle::NoBarrier_Load(impl_ptr)); |
| + if (!delay_impl) { |
| + delay_impl = |
| + SyntheticDelayController::GetInstance()->GetOrCreateDelay(name); |
| + subtle::NoBarrier_Store(impl_ptr, |
| + reinterpret_cast<subtle::AtomicWord>(delay_impl)); |
| + } |
| + return delay_impl; |
| +} |
| + |
| +} // namespace synthetic_delay_internal |
| + |
| +ScopedSyntheticDelay::ScopedSyntheticDelay(const char* name, |
| + subtle::AtomicWord* impl_ptr) |
| + : delay_impl_(synthetic_delay_internal::GetOrCreateDelay(name, impl_ptr)) { |
| + delay_impl_->Begin(); |
| +} |
| + |
| +ScopedSyntheticDelay::~ScopedSyntheticDelay() { delay_impl_->End(); } |
| + |
| +SyntheticDelay::SyntheticDelay() : name_(NULL) {} |
| + |
| +void SyntheticDelay::Initialize(const char* name) { |
| + AutoLock lock(lock_); |
|
brianderson
2013/11/05 03:12:05
I don't think this lock is necessary, since it is
Sami
2013/11/22 18:29:13
True, removed.
|
| + DCHECK(!thread_state_.Get()); |
| + name_ = name; |
| +} |
| + |
| +void SyntheticDelay::SetTargetDuration(base::TimeDelta target_duration) { |
| + AutoLock lock(lock_); |
| + target_duration_ = target_duration; |
| +} |
| + |
| +void SyntheticDelay::Begin() { |
| + // Note that we check for a non-zero target duration without locking to keep |
| + // things quick for the common case when delays are disabled. Since the delay |
| + // calculation is done with a lock held, it will always be correct. The only |
| + // downside of this is that we may fail to apply some delays when the target |
| + // duration changes. |
| + if (!target_duration_.ToInternalValue()) |
| + return; |
| + |
| + // Store the start time in a thread local struct so the same delay can be |
| + // applied to multiple threads simultaneously. |
| + DCHECK(!thread_state_.Get()); |
| + scoped_ptr<ThreadState> thread_state(new ThreadState()); |
|
brianderson
2013/11/05 03:12:05
Can you avoid allocating a new ThreadState for eve
Sami
2013/11/22 18:29:13
Right, the choice is between avoiding the repeated
|
| + thread_state->start_time = base::TimeTicks::Now(); |
| + thread_state_.Set(thread_state.release()); |
| +} |
| + |
| +void SyntheticDelay::End() { |
| + if (!target_duration_.ToInternalValue()) |
| + return; |
| + |
| + DCHECK(thread_state_.Get()); |
| + scoped_ptr<ThreadState> thread_state(thread_state_.Get()); |
| + thread_state_.Set(NULL); |
| + |
| + AutoLock lock(lock_); |
| + base::TimeTicks now = base::TimeTicks::Now(); |
|
brianderson
2013/11/05 03:12:05
Can you use HighResNow? For the scheduling tests,
Sami
2013/11/22 18:29:13
Great point, changed.
|
| + base::TimeDelta delay = target_duration_ - (now - thread_state->start_time); |
|
brianderson
2013/11/05 03:12:05
I think you should be able to AutoLock just the li
Sami
2013/11/22 18:29:13
Done.
|
| + if (delay.ToInternalValue() <= 0) |
| + return; |
| + |
| + TRACE_EVENT0("synthetic_delay", name_); |
| + { |
| + AutoUnlock unlock(lock_); |
| + PlatformThread::Sleep(delay); |
| + } |
| +} |
| + |
| +SyntheticDelayController* SyntheticDelayController::GetInstance() { |
| + return Singleton<SyntheticDelayController, |
| + LeakySingletonTraits<SyntheticDelayController> >::get(); |
|
brianderson
2013/11/05 03:12:05
What does LeakySingletonTraits do?
Sami
2013/11/22 18:29:13
It's for allocating a singleton class that gets le
|
| +} |
| + |
| +SyntheticDelayController::SyntheticDelayController() : delay_count_(0) { |
| + for (int i = 0; i < kMaxSyntheticDelays; ++i) { |
| + ANNOTATE_BENIGN_RACE(&delays_[i].target_duration_, |
|
brianderson
2013/11/05 03:12:05
What does ANNOTATE_BENIGN_RACE do?
Sami
2013/11/22 18:29:13
It's another hint for dynamic code analyzers -- mo
|
| + "synthetic delay duration"); |
| + } |
| +} |
| + |
| +SyntheticDelay* SyntheticDelayController::GetOrCreateDelay(const char* name) { |
| + AutoLock lock(lock_); |
|
brianderson
2013/11/05 03:12:05
I think you can avoid taking this lock in the comm
Sami
2013/11/22 18:29:13
Great idea, done.
|
| + for (int i = 0; i < delay_count_; ++i) { |
| + if (!strcmp(name, delays_[i].name_)) |
| + return &delays_[i]; |
| + } |
| + |
| + DCHECK(delay_count_ < kMaxSyntheticDelays) |
| + << "must increase kMaxSyntheticDelays"; |
| + if (delay_count_ >= kMaxSyntheticDelays) |
| + return &dummy_delay_; |
| + |
| + delays_[delay_count_].Initialize(name); |
| + return &delays_[delay_count_++]; |
| +} |
| + |
| +} // namespace debug |
| +} // namespace base |