]> git.proxmox.com Git - ceph.git/blob - ceph/src/common/mutex_debug.h
import 15.2.0 Octopus source
[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/ceph_assert.h"
24 #include "include/common_fwd.h"
25
26 #include "ceph_time.h"
27 #include "likely.h"
28 #include "lockdep.h"
29
30 namespace ceph {
31 namespace mutex_debug_detail {
32
33 class mutex_debugging_base
34 {
35 protected:
36 std::string group;
37 int id = -1;
38 bool lockdep; // track this mutex using lockdep_*
39 bool backtrace; // gather backtrace on lock acquisition
40
41 int nlock = 0;
42 std::thread::id locked_by = {};
43
44 bool _enable_lockdep() const {
45 return lockdep && g_lockdep;
46 }
47 void _register();
48 void _will_lock(bool recursive=false); // about to lock
49 void _locked(); // just locked
50 void _will_unlock(); // about to unlock
51
52 mutex_debugging_base(std::string group, bool ld = true, bool bt = false);
53 ~mutex_debugging_base();
54
55 public:
56 bool is_locked() const {
57 return (nlock > 0);
58 }
59 bool is_locked_by_me() const {
60 return nlock > 0 && locked_by == std::this_thread::get_id();
61 }
62 operator bool() const {
63 return nlock > 0 && locked_by == std::this_thread::get_id();
64 }
65 };
66
67 // Since this is a /debugging/ mutex just define it in terms of the
68 // pthread error check mutex.
69 template<bool Recursive>
70 class mutex_debug_impl : public mutex_debugging_base
71 {
72 private:
73 pthread_mutex_t m;
74
75 void _init() {
76 pthread_mutexattr_t a;
77 pthread_mutexattr_init(&a);
78 int r;
79 if (recursive)
80 r = pthread_mutexattr_settype(&a, PTHREAD_MUTEX_RECURSIVE);
81 else
82 r = pthread_mutexattr_settype(&a, PTHREAD_MUTEX_ERRORCHECK);
83 ceph_assert(r == 0);
84 r = pthread_mutex_init(&m, &a);
85 ceph_assert(r == 0);
86 }
87
88 bool enable_lockdep(bool no_lockdep) const {
89 if (recursive) {
90 return false;
91 } else if (no_lockdep) {
92 return false;
93 } else {
94 return _enable_lockdep();
95 }
96 }
97
98 public:
99 static constexpr bool recursive = Recursive;
100
101 mutex_debug_impl(std::string group, bool ld = true, bool bt = false)
102 : mutex_debugging_base(group, ld, bt) {
103 _init();
104 }
105
106 // Mutex is Destructible
107 ~mutex_debug_impl() {
108 int r = pthread_mutex_destroy(&m);
109 ceph_assert(r == 0);
110 }
111
112 // Mutex concept is non-Copyable
113 mutex_debug_impl(const mutex_debug_impl&) = delete;
114 mutex_debug_impl& operator =(const mutex_debug_impl&) = delete;
115
116 // Mutex concept is non-Movable
117 mutex_debug_impl(mutex_debug_impl&&) = delete;
118 mutex_debug_impl& operator =(mutex_debug_impl&&) = delete;
119
120 void lock_impl() {
121 int r = pthread_mutex_lock(&m);
122 // Allowed error codes for Mutex concept
123 if (unlikely(r == EPERM ||
124 r == EDEADLK ||
125 r == EBUSY)) {
126 throw std::system_error(r, std::generic_category());
127 }
128 ceph_assert(r == 0);
129 }
130
131 void unlock_impl() noexcept {
132 int r = pthread_mutex_unlock(&m);
133 ceph_assert(r == 0);
134 }
135
136 bool try_lock_impl() {
137 int r = pthread_mutex_trylock(&m);
138 switch (r) {
139 case 0:
140 return true;
141 case EBUSY:
142 return false;
143 default:
144 throw std::system_error(r, std::generic_category());
145 }
146 }
147 pthread_mutex_t* native_handle() {
148 return &m;
149 }
150
151 void _post_lock() {
152 if (!recursive)
153 ceph_assert(nlock == 0);
154 locked_by = std::this_thread::get_id();
155 nlock++;
156 }
157
158 void _pre_unlock() {
159 ceph_assert(nlock > 0);
160 --nlock;
161 ceph_assert(locked_by == std::this_thread::get_id());
162 if (!recursive)
163 ceph_assert(nlock == 0);
164 if (nlock == 0)
165 locked_by = std::thread::id();
166 }
167
168 bool try_lock(bool no_lockdep = false) {
169 bool locked = try_lock_impl();
170 if (locked) {
171 if (enable_lockdep(no_lockdep))
172 _locked();
173 _post_lock();
174 }
175 return locked;
176 }
177
178 void lock(bool no_lockdep = false) {
179 if (enable_lockdep(no_lockdep))
180 _will_lock(recursive);
181
182 if (try_lock(no_lockdep))
183 return;
184
185 lock_impl();
186 if (enable_lockdep(no_lockdep))
187 _locked();
188 _post_lock();
189 }
190
191 void unlock(bool no_lockdep = false) {
192 _pre_unlock();
193 if (enable_lockdep(no_lockdep))
194 _will_unlock();
195 unlock_impl();
196 }
197
198 };
199
200
201 } // namespace mutex_debug_detail
202 typedef mutex_debug_detail::mutex_debug_impl<false> mutex_debug;
203 typedef mutex_debug_detail::mutex_debug_impl<true> mutex_recursive_debug;
204 } // namespace ceph
205
206 #endif