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() {
(1) Event lock: "pthread_mutex_lock" locks "this->m".
(1) Event lock: "pthread_mutex_lock" locks "this->m".
(1) Event lock: "pthread_mutex_lock" locks "this->m".
(1) Event lock: "pthread_mutex_lock" locks "this->m".
(1) Event lock: "pthread_mutex_lock" locks "this->m".
(1) Event lock: "pthread_mutex_lock" locks "this->m".
123  	    int r = pthread_mutex_lock(&m);
124  	    // Allowed error codes for Mutex concept
(2) Event cond_false: Condition "r == 1", taking false branch.
(3) Event cond_false: Condition "r == 35", taking false branch.
(4) Event cond_false: Condition "r == 16", taking false branch.
(5) Event cond_false: Condition "r == 1 || r == 35 || r == 16", taking false branch.
(2) Event cond_false: Condition "r == 1", taking false branch.
(3) Event cond_false: Condition "r == 35", taking false branch.
(4) Event cond_false: Condition "r == 16", taking false branch.
(5) Event cond_false: Condition "r == 1 || r == 35 || r == 16", taking false branch.
(2) Event cond_false: Condition "r == 1", taking false branch.
(3) Event cond_false: Condition "r == 35", taking false branch.
(4) Event cond_false: Condition "r == 16", taking false branch.
(5) Event cond_false: Condition "r == 1 || r == 35 || r == 16", taking false branch.
(2) Event cond_false: Condition "r == 1", taking false branch.
(3) Event cond_false: Condition "r == 35", taking false branch.
(4) Event cond_false: Condition "r == 16", taking false branch.
(5) Event cond_false: Condition "r == 1 || r == 35 || r == 16", taking false branch.
(2) Event cond_false: Condition "r == 1", taking false branch.
(3) Event cond_false: Condition "r == 35", taking false branch.
(4) Event cond_false: Condition "r == 16", taking false branch.
(5) Event cond_false: Condition "r == 1 || r == 35 || r == 16", taking false branch.
(2) Event cond_false: Condition "r == 1", taking false branch.
(3) Event cond_false: Condition "r == 35", taking false branch.
(4) Event cond_false: Condition "r == 16", taking false branch.
(5) Event cond_false: Condition "r == 1 || r == 35 || r == 16", taking false branch.
125  	    if (unlikely(r == EPERM ||
126  			 r == EDEADLK ||
127  			 r == EBUSY)) {
128  	      throw std::system_error(r, std::generic_category());
(6) Event if_end: End of if statement.
(6) Event if_end: End of if statement.
(6) Event if_end: End of if statement.
(6) Event if_end: End of if statement.
(6) Event if_end: End of if statement.
(6) Event if_end: End of if statement.
129  	    }
(7) Event cond_true: Condition "r == 0", taking true branch.
(7) Event cond_true: Condition "r == 0", taking true branch.
(7) Event cond_true: Condition "r == 0", taking true branch.
(7) Event cond_true: Condition "r == 0", taking true branch.
(7) Event cond_true: Condition "r == 0", taking true branch.
(7) Event cond_true: Condition "r == 0", taking true branch.
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() {
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) {
(1) Event cond_true: Condition "this->enable_lockdep(no_lockdep)", taking true branch.
(1) Event cond_true: Condition "this->enable_lockdep(no_lockdep)", taking true branch.
(1) Event cond_true: Condition "this->enable_lockdep(no_lockdep)", taking true branch.
(1) Event cond_true: Condition "this->enable_lockdep(no_lockdep)", taking true branch.
(1) Event cond_true: Condition "this->enable_lockdep(no_lockdep)", taking true branch.
(1) Event cond_true: Condition "this->enable_lockdep(no_lockdep)", taking true branch.
181  	    if (enable_lockdep(no_lockdep))
182  	      _will_lock(recursive);
183  	
(2) Event cond_false: Condition "this->try_lock(no_lockdep)", taking false branch.
(2) Event cond_false: Condition "this->try_lock(no_lockdep)", taking false branch.
(2) Event cond_false: Condition "this->try_lock(no_lockdep)", taking false branch.
(2) Event cond_false: Condition "this->try_lock(no_lockdep)", taking false branch.
(2) Event cond_false: Condition "this->try_lock(no_lockdep)", taking false branch.
(2) Event cond_false: Condition "this->try_lock(no_lockdep)", taking false branch.
184  	    if (try_lock(no_lockdep))
(3) Event if_end: End of if statement.
(3) Event if_end: End of if statement.
(3) Event if_end: End of if statement.
(3) Event if_end: End of if statement.
(3) Event if_end: End of if statement.
(3) Event if_end: End of if statement.
185  	      return;
186  	
(4) Event lock: "lock_impl" locks "this->m". [details]
(4) Event lock: "lock_impl" locks "this->m". [details]
(4) Event lock: "lock_impl" locks "this->m". [details]
(4) Event lock: "lock_impl" locks "this->m". [details]
(4) Event lock: "lock_impl" locks "this->m". [details]
(4) Event lock: "lock_impl" locks "this->m". [details]
187  	    lock_impl();
(5) Event cond_true: Condition "this->enable_lockdep(no_lockdep)", taking true branch.
(5) Event cond_true: Condition "this->enable_lockdep(no_lockdep)", taking true branch.
(5) Event cond_true: Condition "this->enable_lockdep(no_lockdep)", taking true branch.
(5) Event cond_true: Condition "this->enable_lockdep(no_lockdep)", taking true branch.
(5) Event cond_true: Condition "this->enable_lockdep(no_lockdep)", taking true branch.
(5) Event cond_true: Condition "this->enable_lockdep(no_lockdep)", taking true branch.
188  	    if (enable_lockdep(no_lockdep))
189  	      _locked();
190  	    _post_lock();
191  	  }
192  	
193  	  void unlock(bool no_lockdep = false) {
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