summaryrefslogtreecommitdiffstats
path: root/base/task_scheduler/scheduler_lock_impl.cc
blob: 609ea222596e082cb55317554cd6dd5231318388 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
// 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_storage.h"

namespace base {
namespace internal {

namespace {

class SafeAcquisitionTracker {
 public:
  SafeAcquisitionTracker() : tls_acquired_locks_(&OnTLSDestroy) {}

  void RegisterLock(
      const SchedulerLockImpl* const lock,
      const SchedulerLockImpl* const predecessor) {
    DCHECK_NE(lock, predecessor) << "Reentrant locks are unsupported.";
    AutoLock auto_lock(allowed_predecessor_map_lock_);
    allowed_predecessor_map_[lock] = predecessor;
    AssertSafePredecessor(lock);
  }

  void UnregisterLock(const SchedulerLockImpl* const lock) {
    AutoLock auto_lock(allowed_predecessor_map_lock_);
    allowed_predecessor_map_.erase(lock);
  }

  void RecordAcquisition(const SchedulerLockImpl* const lock) {
    AssertSafeAcquire(lock);
    GetAcquiredLocksOnCurrentThread()->push_back(lock);
  }

  void RecordRelease(const SchedulerLockImpl* const lock) {
    LockVector* acquired_locks = GetAcquiredLocksOnCurrentThread();
    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);
  }

 private:
  using LockVector = std::vector<const SchedulerLockImpl*>;
  using PredecessorMap = std::unordered_map<
      const SchedulerLockImpl*, const SchedulerLockImpl*>;

  // This asserts that the lock is safe to acquire. This means that this should
  // be run before actually recording the acquisition.
  void AssertSafeAcquire(const SchedulerLockImpl* const lock) {
    const LockVector* acquired_locks = GetAcquiredLocksOnCurrentThread();

    // If the thread currently holds no locks, this is inherently safe.
    if (acquired_locks->empty())
      return;

    // Otherwise, make sure that the previous lock acquired is an allowed
    // predecessor.
    AutoLock auto_lock(allowed_predecessor_map_lock_);
    const SchedulerLockImpl* allowed_predecessor =
        allowed_predecessor_map_.at(lock);
    DCHECK_EQ(acquired_locks->back(), allowed_predecessor);
  }

  void AssertSafePredecessor(const SchedulerLockImpl* lock) const {
    allowed_predecessor_map_lock_.AssertAcquired();
    for (const SchedulerLockImpl* predecessor =
             allowed_predecessor_map_.at(lock);
         predecessor != nullptr;
         predecessor = allowed_predecessor_map_.at(predecessor)) {
      DCHECK_NE(predecessor, lock) <<
          "Scheduler lock predecessor cycle detected.";
    }
  }

  LockVector* GetAcquiredLocksOnCurrentThread() {
    if (!tls_acquired_locks_.Get())
      tls_acquired_locks_.Set(new LockVector);

    return reinterpret_cast<LockVector*>(tls_acquired_locks_.Get());
  }

  static void OnTLSDestroy(void* value) {
    delete reinterpret_cast<LockVector*>(value);
  }

  // Synchronizes access to |allowed_predecessor_map_|.
  Lock allowed_predecessor_map_lock_;

  // A map of allowed predecessors.
  PredecessorMap allowed_predecessor_map_;

  // A thread-local slot holding a vector of locks currently acquired on the
  // current thread.
  ThreadLocalStorage::Slot 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