Index: base/task_scheduler/scheduler_lock.cc |
diff --git a/base/task_scheduler/scheduler_lock.cc b/base/task_scheduler/scheduler_lock.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..5f96a5f8e79954e678fceca9e4d8ea46a4da3452 |
--- /dev/null |
+++ b/base/task_scheduler/scheduler_lock.cc |
@@ -0,0 +1,128 @@ |
+// Copyright 2016 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/task_scheduler/scheduler_lock.h" |
+ |
+#include <algorithm> |
+#include <unordered_map> |
+#include <vector> |
+ |
+#include "base/logging.h" |
+#include "base/memory/singleton.h" |
+#include "base/threading/platform_thread.h" |
+ |
+#if DCHECK_IS_ON() |
+#define DCHECK_ON_ONLY(statement) statement |
+#else |
+#define DCHECK_ON_ONLY(statement) |
+#endif |
+ |
+namespace base { |
+namespace task_scheduler { |
+ |
+namespace { |
+ |
+class SafeAcquisitionTracker { |
+ public: |
+ static SafeAcquisitionTracker* GetInstance() { |
+ return base::Singleton<SafeAcquisitionTracker>::get(); |
+ } |
+ |
+ void RegisterLock( |
+ const SchedulerLock* const lock, |
+ const SchedulerLock* const predecessor) { |
+ AutoLock auto_lock(metadata_lock_); |
+ allowed_predecessors_[lock] = predecessor; |
+ } |
+ |
+ void UnregisterLock(const SchedulerLock* const lock) { |
+ AutoLock auto_lock(metadata_lock_); |
+ allowed_predecessors_.erase(lock); |
+ } |
+ |
+ void RecordAcquisition(const SchedulerLock* const lock) { |
+ AutoLock auto_lock(metadata_lock_); |
+ const PlatformThreadId id = PlatformThread::CurrentId(); |
+ AssertSafeAcquire(id, lock); |
+ LockVector& thread_locks = acquired_locks_[id]; |
+ const auto& iter = |
fdoray
2016/02/11 17:30:33
should it be const auto (no &)?
robliao
2016/02/11 22:59:04
Looks like it. Done.
|
+ std::find(thread_locks.begin(), thread_locks.end(), lock); |
+ // We don't allow for reentrant locks. |
+ DCHECK(iter == thread_locks.end()); |
fdoray
2016/02/11 17:30:33
Since predecessors are specified when locks are co
robliao
2016/02/11 22:59:04
That's a good point. When ahead and added a hard C
|
+ thread_locks.push_back(lock); |
+ } |
+ |
+ void RecordRelease(const SchedulerLock* const lock) { |
+ AutoLock auto_lock(metadata_lock_); |
+ const PlatformThreadId id = PlatformThread::CurrentId(); |
+ LockVector& thread_locks = acquired_locks_[id]; |
+ const auto& iter = |
fdoray
2016/02/11 17:30:33
should it be const auto (no &)?
robliao
2016/02/11 22:59:04
Done.
|
+ std::find(thread_locks.begin(), thread_locks.end(), lock); |
+ DCHECK(iter != thread_locks.end()); |
+ thread_locks.erase(iter); |
+ } |
+ |
+ private: |
+ friend struct base::DefaultSingletonTraits<SafeAcquisitionTracker>; |
+ using LockVector = std::vector<const SchedulerLock*>; |
+ using PredecessorMap = std::unordered_map< |
+ const SchedulerLock*, const SchedulerLock*>; |
+ using AcquisitionMap = |
+ std::unordered_map<base::PlatformThreadId, LockVector>; |
+ |
+ SafeAcquisitionTracker() = default; |
+ |
+ void AssertSafeAcquire(PlatformThreadId id, const SchedulerLock* const lock) { |
+ metadata_lock_.AssertAcquired(); |
+ LockVector& thread_locks = acquired_locks_[id]; |
fdoray
2016/02/11 17:30:33
const LockVector&
robliao
2016/02/11 22:59:04
Done.
|
+ |
+ // If the thread hasn't ever acquired a lock, this is inherently safe. |
+ if (thread_locks.empty()) |
+ return; |
+ |
+ // Otherwise, make sure that the previous lock acquired is an allowed |
+ // predecessor. |
+ const SchedulerLock* allowed_predecessor = allowed_predecessors_[lock]; |
+ DCHECK(thread_locks.back() == allowed_predecessor); |
+ } |
+ |
+ // Synchronizes everything in SafeAcquisitionTracker. |
+ base::Lock metadata_lock_; |
+ |
+ PredecessorMap allowed_predecessors_; |
+ AcquisitionMap acquired_locks_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(SafeAcquisitionTracker); |
+}; |
+ |
+} // namespace |
+ |
+SchedulerLock::SchedulerLock() : SchedulerLock(nullptr) {} |
+ |
+SchedulerLock::SchedulerLock(const SchedulerLock* predecessor) { |
+ DCHECK_ON_ONLY( |
+ SafeAcquisitionTracker::GetInstance()->RegisterLock(this, predecessor)); |
+} |
+ |
+SchedulerLock::~SchedulerLock() { |
+ DCHECK_ON_ONLY(SafeAcquisitionTracker::GetInstance()->UnregisterLock(this)); |
+} |
+ |
+void SchedulerLock::Acquire() { |
+ lock.Acquire(); |
+ DCHECK_ON_ONLY( |
+ SafeAcquisitionTracker::GetInstance()->RecordAcquisition(this)); |
+} |
+ |
+void SchedulerLock::Release() { |
+ lock.Release(); |
+ DCHECK_ON_ONLY(SafeAcquisitionTracker::GetInstance()->RecordRelease(this)); |
+} |
+ |
+Lock* SchedulerLock::RawLockForConditionVariable() { |
+ return &lock; |
+} |
+ |
+} // namespace task_scheduler |
+} // base |