MySQL 5.6.14 Source Code Document
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
SafeMutex.cpp
1 /* Copyright 2008, 2009 Sun Microsystems, Inc.
2  All rights reserved. Use is subject to license terms.
3 
4  This program is free software; you can redistribute it and/or modify
5  it under the terms of the GNU General Public License as published by
6  the Free Software Foundation; version 2 of the License.
7 
8  This program is distributed in the hope that it will be useful,
9  but WITHOUT ANY WARRANTY; without even the implied warranty of
10  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11  GNU General Public License for more details.
12 
13  You should have received a copy of the GNU General Public License
14  along with this program; if not, write to the Free Software
15  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */
16 
17 #include "SafeMutex.hpp"
18 
19 int
20 SafeMutex::create()
21 {
22  int ret;
23  if (m_initdone)
24  return err(ErrState, __LINE__);
25  ret = pthread_mutex_init(&m_mutex, 0);
26  if (ret != 0)
27  return err(ret, __LINE__);
28  ret = pthread_cond_init(&m_cond, 0);
29  if (ret != 0)
30  return err(ret, __LINE__);
31  m_initdone = true;
32  return 0;
33 }
34 
35 int
36 SafeMutex::destroy()
37 {
38  int ret;
39  if (!m_initdone)
40  return err(ErrState, __LINE__);
41  ret = pthread_cond_destroy(&m_cond);
42  if (ret != 0)
43  return err(ret, __LINE__);
44  ret = pthread_mutex_destroy(&m_mutex);
45  if (ret != 0)
46  return err(ret, __LINE__);
47  m_initdone = false;
48  return 0;
49 }
50 
51 int
52 SafeMutex::lock()
53 {
54  int ret;
55  if (m_simple) {
56  ret = pthread_mutex_lock(&m_mutex);
57  if (ret != 0)
58  return err(ret, __LINE__);
59  return 0;
60  }
61  ret = pthread_mutex_lock(&m_mutex);
62  if (ret != 0)
63  return err(ret, __LINE__);
64  return lock_impl();
65 }
66 
67 int
68 SafeMutex::lock_impl()
69 {
70  int ret;
71  pthread_t self = pthread_self();
72  assert(self != 0);
73  while (1) {
74  if (m_level == 0) {
75  assert(m_owner == 0);
76  m_owner = self;
77  } else if (m_owner != self) {
78  ret = pthread_cond_wait(&m_cond, &m_mutex);
79  if (ret != 0)
80  return err(ret, __LINE__);
81  continue;
82  }
83  if (!(m_level < m_limit))
84  return err(ErrLevel, __LINE__);
85  m_level++;
86  if (m_usage < m_level)
87  m_usage = m_level;
88  ret = pthread_cond_signal(&m_cond);
89  if (ret != 0)
90  return err(ret, __LINE__);
91  ret = pthread_mutex_unlock(&m_mutex);
92  if (ret != 0)
93  return err(ret, __LINE__);
94  break;
95  }
96  return 0;
97 }
98 
99 int
100 SafeMutex::unlock()
101 {
102  int ret;
103  if (m_simple) {
104  ret = pthread_mutex_unlock(&m_mutex);
105  if (ret != 0)
106  return err(ret, __LINE__);
107  return 0;
108  }
109  ret = pthread_mutex_lock(&m_mutex);
110  if (ret != 0)
111  return err(ret, __LINE__);
112  return unlock_impl();
113 }
114 
115 int
116 SafeMutex::unlock_impl()
117 {
118  int ret;
119  pthread_t self = pthread_self();
120  assert(self != 0);
121  if (m_owner != self)
122  return err(ErrOwner, __LINE__);
123  if (m_level == 0)
124  return err(ErrNolock, __LINE__);
125  m_level--;
126  if (m_level == 0) {
127  m_owner = 0;
128  ret = pthread_cond_signal(&m_cond);
129  if (ret != 0)
130  return err(ret, __LINE__);
131  }
132  ret = pthread_mutex_unlock(&m_mutex);
133  if (ret != 0)
134  return err(ret, __LINE__);
135  return 0;
136 }
137 
138 int
139 SafeMutex::err(int errcode, int errline)
140 {
141  assert(errcode != 0);
142  m_errcode = errcode;
143  m_errline = errline;
144  ndbout << *this << endl;
145 #ifdef UNIT_TEST
146  abort();
147 #endif
148  return errcode;
149 }
150 
151 NdbOut&
152 operator<<(NdbOut& out, const SafeMutex& sm)
153 {
154  out << sm.m_name << ":";
155  out << " level=" << sm.m_level;
156  out << " usage=" << sm.m_usage;
157  if (sm.m_errcode != 0) {
158  out << " errcode=" << sm.m_errcode;
159  out << " errline=" << sm.m_errline;
160  }
161  return out;
162 }
163 
164 #ifdef UNIT_TEST
165 
166 struct sm_thr {
167  SafeMutex* sm_ptr;
168  uint index;
169  uint loops;
170  uint limit;
171  pthread_t id;
172  sm_thr() : sm_ptr(0), index(0), loops(0), limit(0), id(0) {}
173  ~sm_thr() {}
174 };
175 
176 extern "C" { static void* sm_run(void* arg); }
177 
178 static void*
179 sm_run(void* arg)
180 {
181  sm_thr& thr = *(sm_thr*)arg;
182  assert(thr.sm_ptr != 0);
183  SafeMutex& sm = *thr.sm_ptr;
184  uint level = 0;
185  int dir = 0;
186  uint i;
187  for (i = 0; i < thr.loops; i++) {
188  int op = 0;
189  uint sel = uint(random()) % 10;
190  if (level == 0) {
191  dir = +1;
192  op = +1;
193  } else if (level == thr.limit) {
194  dir = -1;
195  op = -1;
196  } else if (dir == +1) {
197  op = sel != 0 ? +1 : -1;
198  } else if (dir == -1) {
199  op = sel != 0 ? -1 : +1;
200  } else {
201  assert(false);
202  }
203  if (op == +1) {
204  assert(level < thr.limit);
205  //ndbout << thr.index << ": lock" << endl;
206  int ret = sm.lock();
207  assert(ret == 0);
208  level++;
209  } else if (op == -1) {
210  //ndbout << thr.index << ": unlock" << endl;
211  int ret = sm.unlock();
212  assert(ret == 0);
213  assert(level != 0);
214  level--;
215  } else {
216  assert(false);
217  }
218  }
219  while (level > 0) {
220  int ret = sm.unlock();
221  assert(ret == 0);
222  level--;
223  }
224  return 0;
225 }
226 
227 int
228 main(int argc, char** argv)
229 {
230  const uint max_thr = 128;
231  struct sm_thr thr[max_thr];
232 
233  // threads - loops - max level - debug
234  uint num_thr = argc > 1 ? atoi(argv[1]) : 4;
235  assert(num_thr != 0 && num_thr <= max_thr);
236  uint loops = argc > 2 ? atoi(argv[2]) : 1000000;
237  uint limit = argc > 3 ? atoi(argv[3]) : 10;
238  assert(limit != 0);
239  bool debug = argc > 4 ? atoi(argv[4]) : true;
240 
241  ndbout << "threads=" << num_thr;
242  ndbout << " loops=" << loops;
243  ndbout << " max level=" << limit << endl;
244 
245  SafeMutex sm("test-mutex", limit, debug);
246  int ret;
247  ret = sm.create();
248  assert(ret == 0);
249 
250  uint i;
251  for (i = 0; i < num_thr; i++) {
252  thr[i].sm_ptr = &sm;
253  thr[i].index = i;
254  thr[i].loops = loops;
255  thr[i].limit = limit;
256  pthread_create(&thr[i].id, 0, &sm_run, &thr[i]);
257  ndbout << "create " << i << " id=" << thr[i].id << endl;
258  }
259  for (i = 0; i < num_thr; i++) {
260  void* value;
261  pthread_join(thr[i].id, &value);
262  ndbout << "join " << i << " id=" << thr[i].id << endl;
263  }
264 
265  ret = sm.destroy();
266  assert(ret == 0);
267  return 0;
268 }
269 
270 #endif