]> git.proxmox.com Git - ceph.git/blob - ceph/src/common/mutex_debug.h
add subtree-ish sources for 12.0.3
[ceph.git] / ceph / src / common / mutex_debug.h
1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
3 /*
4 * Ceph - scalable distributed file system
5 *
6 * Copyright (C) 2004-2006 Sage Weil <sage@newdream.net>
7 *
8 * This is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License version 2.1, as published by the Free Software
11 * Foundation. See file COPYING.
12 *
13 */
14
15 #ifndef CEPH_COMMON_MUTEX_DEBUG_H
16 #define CEPH_COMMON_MUTEX_DEBUG_H
17
18 #include <system_error>
19 #include <thread>
20
21 #include <pthread.h>
22
23 #include "include/assert.h"
24
25 #include "ceph_time.h"
26 #include "likely.h"
27 #include "lockdep.h"
28
29 class CephContext;
30 class PerfCounters;
31
32 namespace ceph {
33 namespace mutex_debug_detail {
34 class mutex_debugging_base {
35 protected:
36 std::string name;
37 int id;
38 bool backtrace; // gather backtrace on lock acquisition
39
40 int nlock;
41 std::thread::id locked_by;
42 CephContext *cct;
43 PerfCounters *logger;
44
45 void _register();
46 void _will_lock(); // about to lock
47 void _locked(); // just locked
48 void _will_unlock(); // about to unlock
49
50 mutex_debugging_base(const std::string &n = std::string(), bool bt = false,
51 CephContext *cct = nullptr);
52 ~mutex_debugging_base();
53
54 ceph::mono_time before_lock_blocks();
55 void after_lock_blocks(ceph::mono_time start,
56 bool no_lockdep);
57
58 public:
59 bool is_locked() const {
60 return (nlock > 0);
61 }
62 bool is_locked_by_me() const {
63 return nlock > 0 && locked_by == std::this_thread::get_id();
64 }
65 operator bool() const {
66 return nlock > 0 && locked_by == std::this_thread::get_id();
67 }
68 };
69
70 template<typename Mutex>
71 class mutex_debugging : public mutex_debugging_base {
72 Mutex* impl;
73
74 public:
75 mutex_debugging(const std::string &n = std::string(), bool bt = false,
76 CephContext *cct = nullptr) :
77 mutex_debugging_base(n, bt, cct), impl(static_cast<Mutex*>(this)) {}
78
79 ~mutex_debugging() = default;
80
81 void _post_lock() {
82 if (!impl->recursive)
83 assert(nlock == 0);
84 locked_by = std::this_thread::get_id();
85 nlock++;
86 }
87
88 void _pre_unlock() {
89 assert(nlock > 0);
90 --nlock;
91 assert(locked_by == std::this_thread::get_id());
92 if (!impl->recursive)
93 assert(nlock == 0);
94 if (nlock == 0)
95 locked_by = std::thread::id();
96 }
97
98 bool try_lock(bool no_lockdep = false) {
99 bool locked = impl->try_lock_impl();
100 if (locked) {
101 if (g_lockdep && !no_lockdep)
102 _locked();
103 _post_lock();
104 }
105 return locked;
106 }
107
108 void lock(bool no_lockdep = false) {
109 if (g_lockdep && !no_lockdep)
110 _will_lock();
111
112 if (try_lock())
113 return;
114
115 auto t = before_lock_blocks();
116 impl->lock_impl();
117 after_lock_blocks(t, no_lockdep);
118 _post_lock();
119 }
120
121 void unlock(bool no_lockdep = false) {
122 _pre_unlock();
123 if (!no_lockdep && g_lockdep)
124 _will_unlock();
125 impl->unlock_impl();
126 }
127 };
128
129 // Since this is a /debugging/ mutex just define it in terms of the
130 // pthread error check mutex.
131 template<bool Recursive>
132 class mutex_debug_impl : public mutex_debugging<mutex_debug_impl<Recursive> > {
133 private:
134 pthread_mutex_t m;
135 public:
136 static constexpr bool recursive = Recursive;
137
138 // Mutex concept is DefaultConstructible
139 mutex_debug_impl(const std::string &n = std::string(), bool bt = false,
140 CephContext *cct = nullptr) :
141 mutex_debugging<mutex_debug_impl<Recursive> >(n, bt, cct) {
142 pthread_mutexattr_t a;
143 pthread_mutexattr_init(&a);
144 int r;
145 if (recursive)
146 r = pthread_mutexattr_settype(&a, PTHREAD_MUTEX_RECURSIVE);
147 else
148 r = pthread_mutexattr_settype(&a, PTHREAD_MUTEX_ERRORCHECK);
149 assert(r == 0);
150 r = pthread_mutex_init(&m, &a);
151 assert(r == 0);
152 }
153 // Mutex is Destructible
154 ~mutex_debug_impl() {
155 int r = pthread_mutex_destroy(&m);
156 assert(r == 0);
157 }
158
159 // Mutex concept is non-Copyable
160 mutex_debug_impl(const mutex_debug_impl&) = delete;
161 mutex_debug_impl& operator =(const mutex_debug_impl&) = delete;
162
163 // Mutex concept is non-Movable
164 mutex_debug_impl(mutex_debug_impl&&) = delete;
165 mutex_debug_impl& operator =(mutex_debug_impl&&) = delete;
166
167 void lock_impl() {
168 int r = pthread_mutex_lock(&m);
169 // Allowed error codes for Mutex concept
170 if (unlikely(r == EPERM ||
171 r == EDEADLK ||
172 r == EBUSY)) {
173 throw std::system_error(r, std::generic_category());
174 }
175 assert(r == 0);
176 }
177
178 void unlock_impl() noexcept {
179 int r = pthread_mutex_unlock(&m);
180 assert(r == 0);
181 }
182
183 bool try_lock_impl() {
184 int r = pthread_mutex_trylock(&m);
185 switch (r) {
186 case 0:
187 return true;
188 case EBUSY:
189 return false;
190 default:
191 throw std::system_error(r, std::generic_category());
192 }
193 }
194 };
195 } // namespace mutex_debug_detail
196 typedef mutex_debug_detail::mutex_debug_impl<false> mutex_debug;
197 typedef mutex_debug_detail::mutex_debug_impl<true> mutex_recursive_debug;
198 } // namespace ceph
199
200 #endif