Index: base/task_scheduler/scheduler_lock_impl.cc |
diff --git a/base/task_scheduler/scheduler_lock_impl.cc b/base/task_scheduler/scheduler_lock_impl.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..36d920d0caeee3a1fa6128b3ff186439ad078a1b |
--- /dev/null |
+++ b/base/task_scheduler/scheduler_lock_impl.cc |
@@ -0,0 +1,150 @@ |
+// 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_impl.h" |
+ |
+#include <algorithm> |
+#include <unordered_map> |
+#include <vector> |
+ |
+#include "base/lazy_instance.h" |
+#include "base/logging.h" |
+#include "base/synchronization/condition_variable.h" |
+#include "base/threading/platform_thread.h" |
+#include "base/threading/thread_local.h" |
+ |
+namespace base { |
+namespace internal { |
+ |
+namespace { |
+ |
+class SafeAcquisitionTracker { |
+ public: |
+ SafeAcquisitionTracker() = default; |
+ |
+ void RegisterLock( |
+ const SchedulerLockImpl* const lock, |
+ const SchedulerLockImpl* const predecessor) { |
+ // Reentrant locks are unsupported. |
+ DCHECK(lock != predecessor); |
+ AutoLock auto_lock(metadata_lock_); |
+ allowed_predecessor_map_[lock] = predecessor; |
+ AssertSafePredecessor(lock); |
+ } |
+ |
+ void UnregisterLock(const SchedulerLockImpl* const lock) { |
+ AutoLock auto_lock(metadata_lock_); |
+ allowed_predecessor_map_.erase(lock); |
+ } |
+ |
+ void RecordAcquisition(const SchedulerLockImpl* const lock) { |
+ AutoLock auto_lock(metadata_lock_); |
gab
2016/02/22 16:54:12
Move AutoLock into AssertSafeAcquire (on the line
robliao
2016/02/22 18:38:09
Done.
|
+ const PlatformThreadId id = PlatformThread::CurrentId(); |
+ AssertSafeAcquire(id, lock); |
+ if (!tls_acquired_locks_.Get()) |
+ tls_acquired_locks_.Set(new LockVector); |
+ |
+ tls_acquired_locks_.Get()->push_back(lock); |
+ } |
+ |
+ void RecordRelease(const SchedulerLockImpl* const lock) { |
+ AutoLock auto_lock(metadata_lock_); |
gab
2016/02/22 16:54:12
No need to lock, right?
robliao
2016/02/22 18:38:09
Indeed. It's all TLS only. Removed.
|
+ LockVector* acquired_locks = tls_acquired_locks_.Get(); |
gab
2016/02/22 16:54:12
DCHECK(acquired_locks);
robliao
2016/02/22 18:38:09
DCHECK isn't necessary here as we'll simply crash
gab
2016/02/22 19:56:47
Right but DCHECK explicitly documents expectations
robliao
2016/02/22 20:22:51
I used to be on the DCHECK pointers side as well u
gab
2016/02/22 20:36:31
To me it acts as explicit documentation whereas im
robliao
2016/02/22 21:21:02
Discussed offline and rendered moot with the allow
|
+ const auto iter_at_lock = |
+ std::find(acquired_locks->begin(), acquired_locks->end(), lock); |
+ DCHECK(iter_at_lock != acquired_locks->end()); |
+ acquired_locks->erase(iter_at_lock); |
+ if (acquired_locks->empty()) { |
+ tls_acquired_locks_.Set(nullptr); |
+ delete acquired_locks; |
gab
2016/02/22 16:54:12
Assuming this thread remains active, it will soon
robliao
2016/02/22 18:38:09
The destruction of a lock does not necessarily cor
gab
2016/02/22 19:56:47
Duh of course, I did mean on destruction of thread
robliao
2016/02/22 20:22:51
The right way to do this is to introduce the destr
robliao
2016/02/22 20:37:34
Looks like I incorrectly assumed that this API was
|
+ } |
+ } |
+ |
+ private: |
+ using LockVector = std::vector<const SchedulerLockImpl*>; |
+ using PredecessorMap = std::unordered_map< |
+ const SchedulerLockImpl*, const SchedulerLockImpl*>; |
+ using AcquisitionMap = |
gab
2016/02/22 16:54:12
rm unused type
robliao
2016/02/22 18:38:09
Done.
|
+ std::unordered_map<base::PlatformThreadId, LockVector>; |
fdoray
2016/02/22 16:45:21
not used anymore
robliao
2016/02/22 18:38:09
Done.
|
+ |
+ // This asserts that the lock is safe to acquire. This means that this should |
+ // be run before actually recording the acquisition. |
+ void AssertSafeAcquire(PlatformThreadId id, |
+ const SchedulerLockImpl* const lock) const { |
+ const LockVector* acquired_locks = GetThreadAcquiredLocks(); |
+ |
+ // If the thread currently holds no locks, this is inherently safe. |
+ if (!acquired_locks) |
+ return; |
+ |
+ // We're getting ready to look at the allowed_predecessor_map_. |
gab
2016/02/22 16:54:12
When the line below is changed to an AutoLock as s
robliao
2016/02/22 18:38:09
Done.
|
+ metadata_lock_.AssertAcquired(); |
+ |
+ // Otherwise, make sure that the previous lock acquired is an allowed |
+ // predecessor. |
+ const SchedulerLockImpl* allowed_predecessor = |
+ allowed_predecessor_map_.at(lock); |
+ DCHECK(acquired_locks->back() == allowed_predecessor); |
+ } |
+ |
+ void AssertSafePredecessor(const SchedulerLockImpl* lock) const { |
+ metadata_lock_.AssertAcquired(); |
+ for (const SchedulerLockImpl* predecessor = |
+ allowed_predecessor_map_.at(lock); |
+ predecessor != nullptr; |
+ predecessor = allowed_predecessor_map_.at(predecessor)) { |
+ if (predecessor == lock) |
+ NOTREACHED(); |
+ } |
+ } |
+ |
+ const LockVector* GetThreadAcquiredLocks() const { |
+ // ThreadLocalPointer.Get() has no const version. |
+ return const_cast<SafeAcquisitionTracker*>(this)->tls_acquired_locks_.Get(); |
gab
2016/02/22 16:54:12
I'd prefer to inline this at single call site, oth
robliao
2016/02/22 18:38:09
Done.
|
+ } |
+ |
+ // Synchronizes everything in SafeAcquisitionTracker. |
+ Lock metadata_lock_; |
gab
2016/02/22 16:54:12
// Synchronized access to |allowed_predecessor_map
robliao
2016/02/22 18:38:09
Done.
|
+ |
+ PredecessorMap allowed_predecessor_map_; |
+ ThreadLocalPointer<LockVector> tls_acquired_locks_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(SafeAcquisitionTracker); |
+}; |
+ |
+LazyInstance<SafeAcquisitionTracker>::Leaky g_safe_acquisition_tracker = |
+ LAZY_INSTANCE_INITIALIZER; |
+ |
+} // namespace |
+ |
+SchedulerLockImpl::SchedulerLockImpl() : SchedulerLockImpl(nullptr) {} |
+ |
+SchedulerLockImpl::SchedulerLockImpl(const SchedulerLockImpl* predecessor) { |
+ g_safe_acquisition_tracker.Get().RegisterLock(this, predecessor); |
+} |
+ |
+SchedulerLockImpl::~SchedulerLockImpl() { |
+ g_safe_acquisition_tracker.Get().UnregisterLock(this); |
+} |
+ |
+void SchedulerLockImpl::Acquire() { |
+ lock_.Acquire(); |
+ g_safe_acquisition_tracker.Get().RecordAcquisition(this); |
+} |
+ |
+void SchedulerLockImpl::Release() { |
+ lock_.Release(); |
+ g_safe_acquisition_tracker.Get().RecordRelease(this); |
+} |
+ |
+void SchedulerLockImpl::AssertAcquired() const { |
+ lock_.AssertAcquired(); |
+} |
+ |
+scoped_ptr<ConditionVariable> SchedulerLockImpl::CreateConditionVariable() { |
+ return scoped_ptr<ConditionVariable>(new ConditionVariable(&lock_)); |
+} |
+ |
+} // namespace internal |
+} // base |