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 |