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