summaryrefslogtreecommitdiff
path: root/base/task_scheduler/scheduler_lock_impl.cc
blob: d60f25939ba77a1fa85634d2ea96bb035809f05f (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
145
146
147
148
149
150
151
152
153
154
155
156
// 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_);
    // Using at() is exception-safe here as |lock| was registered already.
    const SchedulerLockImpl* allowed_predecessor =
        allowed_predecessor_map_.at(lock);
    DCHECK_EQ(acquired_locks->back(), allowed_predecessor);
  }

  // Asserts that |lock|'s registered predecessor is safe. Because
  // SchedulerLocks are registered at construction time and any predecessor
  // specified on a SchedulerLock must already exist, the first registered
  // SchedulerLock in a potential chain must have a null predecessor and is thus
  // cycle-free. Any subsequent SchedulerLock with a predecessor must come from
  // the set of registered SchedulerLocks. Since the registered SchedulerLocks
  // only contain cycle-free SchedulerLocks, this subsequent SchedulerLock is
  // itself cycle-free and may be safely added to the registered SchedulerLock
  // set.
  void AssertSafePredecessor(const SchedulerLockImpl* lock) const {
    allowed_predecessor_map_lock_.AssertAcquired();
    // Using at() is exception-safe here as |lock| was registered already.
    const SchedulerLockImpl* predecessor = allowed_predecessor_map_.at(lock);
    if (predecessor) {
      DCHECK(allowed_predecessor_map_.find(predecessor) !=
             allowed_predecessor_map_.end())
          << "SchedulerLock was registered before its predecessor. "
          << "Potential 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();
}

std::unique_ptr<ConditionVariable>
SchedulerLockImpl::CreateConditionVariable() {
  return std::unique_ptr<ConditionVariable>(new ConditionVariable(&lock_));
}

}  // namespace internal
}  // base