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