1 | /* |
2 | Copyright (c) 2000, 2011, Oracle and/or its affiliates. |
3 | Copyright (c) 2010, 2011, Monty Program Ab |
4 | |
5 | This program is free software; you can redistribute it and/or modify |
6 | it under the terms of the GNU General Public License as published by |
7 | the Free Software Foundation; version 2 of the License. |
8 | |
9 | This program is distributed in the hope that it will be useful, |
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
12 | GNU General Public License for more details. |
13 | |
14 | You should have received a copy of the GNU General Public License |
15 | along with this program; if not, write to the Free Software |
16 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ |
17 | |
18 | /* This makes a wrapper for mutex handling to make it easier to debug mutex */ |
19 | |
20 | #include <my_global.h> |
21 | #if defined(TARGET_OS_LINUX) && !defined (__USE_UNIX98) |
22 | #define __USE_UNIX98 /* To get rw locks under Linux */ |
23 | #endif |
24 | |
25 | #ifdef SAFE_MUTEX |
26 | #define SAFE_MUTEX_DEFINED |
27 | #undef SAFE_MUTEX /* Avoid safe_mutex redefinitions */ |
28 | #endif |
29 | |
30 | #include "mysys_priv.h" |
31 | #include "my_static.h" |
32 | #include <m_string.h> |
33 | #include <hash.h> |
34 | |
35 | #ifndef DO_NOT_REMOVE_THREAD_WRAPPERS |
36 | /* Remove wrappers */ |
37 | #undef pthread_mutex_t |
38 | #undef pthread_mutex_init |
39 | #undef pthread_mutex_lock |
40 | #undef pthread_mutex_unlock |
41 | #undef pthread_mutex_trylock |
42 | #undef pthread_mutex_destroy |
43 | #undef pthread_cond_wait |
44 | #undef pthread_cond_timedwait |
45 | #undef safe_mutex_free_deadlock_data |
46 | #endif /* DO_NOT_REMOVE_THREAD_WRAPPERS */ |
47 | |
48 | #ifdef PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP |
49 | pthread_mutexattr_t my_fast_mutexattr; |
50 | #endif |
51 | #ifdef PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP |
52 | pthread_mutexattr_t my_errorcheck_mutexattr; |
53 | #endif |
54 | |
55 | #ifdef SAFE_MUTEX_DEFINED |
56 | static pthread_mutex_t THR_LOCK_mutex; |
57 | static ulong safe_mutex_count= 0; /* Number of mutexes created */ |
58 | static ulong safe_mutex_id= 0; |
59 | my_bool safe_mutex_deadlock_detector= 1; /* On by default */ |
60 | |
61 | #ifdef SAFE_MUTEX_DETECT_DESTROY |
62 | static struct st_safe_mutex_create_info_t *safe_mutex_create_root= NULL; |
63 | #endif |
64 | |
65 | static my_bool add_used_to_locked_mutex(safe_mutex_t *used_mutex, |
66 | safe_mutex_deadlock_t *locked_mutex); |
67 | static my_bool add_to_locked_mutex(safe_mutex_deadlock_t *locked_mutex, |
68 | safe_mutex_t *current_mutex); |
69 | static my_bool remove_from_locked_mutex(safe_mutex_t *mp, |
70 | safe_mutex_t *delete_mutex); |
71 | static my_bool remove_from_used_mutex(safe_mutex_deadlock_t *locked_mutex, |
72 | safe_mutex_t *mutex); |
73 | static void print_deadlock_warning(safe_mutex_t *new_mutex, |
74 | safe_mutex_t *conflicting_mutex); |
75 | #endif |
76 | |
77 | |
78 | /* Initialize all mutex handling */ |
79 | |
80 | void my_mutex_init() |
81 | { |
82 | /* Initialize mutex attributes */ |
83 | #ifdef PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP |
84 | /* |
85 | Set mutex type to "fast" a.k.a "adaptive" |
86 | |
87 | In this case the thread may steal the mutex from some other thread |
88 | that is waiting for the same mutex. This will save us some |
89 | context switches but may cause a thread to 'starve forever' while |
90 | waiting for the mutex (not likely if the code within the mutex is |
91 | short). |
92 | */ |
93 | pthread_mutexattr_init(&my_fast_mutexattr); |
94 | pthread_mutexattr_settype(&my_fast_mutexattr, |
95 | PTHREAD_MUTEX_ADAPTIVE_NP); |
96 | #endif |
97 | #ifdef PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP |
98 | /* |
99 | Set mutex type to "errorcheck" |
100 | */ |
101 | pthread_mutexattr_init(&my_errorcheck_mutexattr); |
102 | pthread_mutexattr_settype(&my_errorcheck_mutexattr, |
103 | PTHREAD_MUTEX_ERRORCHECK); |
104 | #endif |
105 | |
106 | #if defined(SAFE_MUTEX_DEFINED) |
107 | safe_mutex_global_init(); |
108 | #endif |
109 | } |
110 | |
111 | void my_mutex_end() |
112 | { |
113 | #ifdef PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP |
114 | pthread_mutexattr_destroy(&my_fast_mutexattr); |
115 | #endif |
116 | #ifdef PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP |
117 | pthread_mutexattr_destroy(&my_errorcheck_mutexattr); |
118 | #endif |
119 | } |
120 | |
121 | |
122 | /* Initialize safe_mutex handling */ |
123 | |
124 | #ifdef SAFE_MUTEX_DEFINED |
125 | void safe_mutex_global_init(void) |
126 | { |
127 | pthread_mutex_init(&THR_LOCK_mutex,MY_MUTEX_INIT_FAST); |
128 | safe_mutex_id= safe_mutex_count= 0; |
129 | safe_mutex_deadlock_detector= 1; |
130 | |
131 | #ifdef SAFE_MUTEX_DETECT_DESTROY |
132 | safe_mutex_create_root= 0; |
133 | #endif /* SAFE_MUTEX_DETECT_DESTROY */ |
134 | } |
135 | |
136 | static inline void remove_from_active_list(safe_mutex_t *mp) |
137 | { |
138 | if (!(mp->active_flags & (MYF_NO_DEADLOCK_DETECTION | MYF_TRY_LOCK))) |
139 | { |
140 | /* Remove mutex from active mutex linked list */ |
141 | if (mp->next) |
142 | mp->next->prev= mp->prev; |
143 | if (mp->prev) |
144 | mp->prev->next= mp->next; |
145 | else |
146 | *my_thread_var_mutex_in_use()= mp->next; |
147 | } |
148 | mp->prev= mp->next= 0; |
149 | } |
150 | |
151 | /* |
152 | We initialize the hashes for deadlock detection lazily. |
153 | This greatly helps with performance when lots of mutexes are initialized but |
154 | only a few of them are actually used (eg. InnoDB). |
155 | */ |
156 | |
157 | static int safe_mutex_lazy_init_deadlock_detection(safe_mutex_t *mp) |
158 | { |
159 | if (!my_multi_malloc(MY_FAE | MY_WME, |
160 | &mp->locked_mutex, sizeof(*mp->locked_mutex), |
161 | &mp->used_mutex, sizeof(*mp->used_mutex), NullS)) |
162 | { |
163 | /* Disable deadlock handling for this mutex */ |
164 | mp->create_flags|= MYF_NO_DEADLOCK_DETECTION; |
165 | mp->active_flags|= MYF_NO_DEADLOCK_DETECTION; |
166 | return 1; /* Error */ |
167 | } |
168 | |
169 | pthread_mutex_lock(&THR_LOCK_mutex); |
170 | mp->id= ++safe_mutex_id; |
171 | pthread_mutex_unlock(&THR_LOCK_mutex); |
172 | my_hash_init2(mp->locked_mutex, 64, &my_charset_bin, |
173 | 128, |
174 | offsetof(safe_mutex_deadlock_t, id), |
175 | sizeof(mp->id), |
176 | 0, 0, 0, HASH_UNIQUE); |
177 | my_hash_init2(mp->used_mutex, 64, &my_charset_bin, |
178 | 128, |
179 | offsetof(safe_mutex_t, id), |
180 | sizeof(mp->id), |
181 | 0, 0, 0, HASH_UNIQUE); |
182 | return 0; |
183 | } |
184 | |
185 | int safe_mutex_init(safe_mutex_t *mp, |
186 | const pthread_mutexattr_t *attr __attribute__((unused)), |
187 | const char *name, const char *file, uint line) |
188 | { |
189 | DBUG_ENTER("safe_mutex_init" ); |
190 | DBUG_PRINT("enter" ,("mutex: 0x%lx name: %s" , (ulong) mp, name)); |
191 | bzero((char*) mp,sizeof(*mp)); |
192 | pthread_mutex_init(&mp->global,MY_MUTEX_INIT_ERRCHK); |
193 | pthread_mutex_init(&mp->mutex,attr); |
194 | /* Mark that mutex is initialized */ |
195 | mp->file= file; |
196 | mp->line= line; |
197 | /* Skip the very common '&' prefix from the autogenerated name */ |
198 | mp->name= name[0] == '&' ? name + 1 : name; |
199 | |
200 | /* Deadlock detection is initialised only lazily, on first use. */ |
201 | |
202 | mp->create_flags= safe_mutex_deadlock_detector ? 0 : MYF_NO_DEADLOCK_DETECTION; |
203 | |
204 | #ifdef SAFE_MUTEX_DETECT_DESTROY |
205 | /* |
206 | Monitor the freeing of mutexes. This code depends on single thread init |
207 | and destroy |
208 | */ |
209 | if ((mp->info= (safe_mutex_info_t *) malloc(sizeof(safe_mutex_info_t)))) |
210 | { |
211 | struct st_safe_mutex_info_t *info= mp->info; |
212 | |
213 | info->init_file= file; |
214 | info->init_line= line; |
215 | info->prev= NULL; |
216 | info->next= NULL; |
217 | |
218 | pthread_mutex_lock(&THR_LOCK_mutex); |
219 | if ((info->next= safe_mutex_create_root)) |
220 | safe_mutex_create_root->prev= info; |
221 | safe_mutex_create_root= info; |
222 | safe_mutex_count++; |
223 | pthread_mutex_unlock(&THR_LOCK_mutex); |
224 | } |
225 | #else |
226 | pthread_mutex_lock(&THR_LOCK_mutex); |
227 | safe_mutex_count++; |
228 | pthread_mutex_unlock(&THR_LOCK_mutex); |
229 | #endif /* SAFE_MUTEX_DETECT_DESTROY */ |
230 | DBUG_RETURN(0); |
231 | } |
232 | |
233 | |
234 | int safe_mutex_lock(safe_mutex_t *mp, myf my_flags, const char *file, |
235 | uint line) |
236 | { |
237 | int error; |
238 | DBUG_PRINT("mutex" , ("%s (0x%lx) locking" , mp->name ? mp->name : "Null" , |
239 | (ulong) mp)); |
240 | if (!mp->file) |
241 | { |
242 | fprintf(stderr, |
243 | "safe_mutex: Trying to lock uninitialized mutex at %s, line %d\n" , |
244 | file, line); |
245 | fflush(stderr); |
246 | abort(); |
247 | } |
248 | |
249 | pthread_mutex_lock(&mp->global); |
250 | if (mp->count > 0) |
251 | { |
252 | /* |
253 | Check that we are not trying to lock mutex twice. This is an error |
254 | even if we are using 'try_lock' as it's not portably what happens |
255 | if you lock the mutex many times and this is in any case bad |
256 | behaviour that should not be encouraged |
257 | */ |
258 | if (pthread_equal(pthread_self(),mp->thread)) |
259 | { |
260 | fprintf(stderr, |
261 | "safe_mutex: Trying to lock mutex at %s, line %d, when the" |
262 | " mutex was already locked at %s, line %d in thread %s\n" , |
263 | file,line,mp->file, mp->line, my_thread_name()); |
264 | fflush(stderr); |
265 | abort(); |
266 | } |
267 | } |
268 | pthread_mutex_unlock(&mp->global); |
269 | |
270 | /* |
271 | If we are imitating trylock(), we need to take special |
272 | precautions. |
273 | |
274 | - We cannot use pthread_mutex_lock() only since another thread can |
275 | overtake this thread and take the lock before this thread |
276 | causing pthread_mutex_trylock() to hang. In this case, we should |
277 | just return EBUSY. Hence, we use pthread_mutex_trylock() to be |
278 | able to return immediately. |
279 | |
280 | - We cannot just use trylock() and continue execution below, since |
281 | this would generate an error and abort execution if the thread |
282 | was overtaken and trylock() returned EBUSY . In this case, we |
283 | instead just return EBUSY, since this is the expected behaviour |
284 | of trylock(). |
285 | */ |
286 | if (my_flags & MYF_TRY_LOCK) |
287 | { |
288 | error= pthread_mutex_trylock(&mp->mutex); |
289 | if (error == EBUSY) |
290 | return error; |
291 | } |
292 | else |
293 | error= pthread_mutex_lock(&mp->mutex); |
294 | |
295 | if (error || (error=pthread_mutex_lock(&mp->global))) |
296 | { |
297 | fprintf(stderr,"Got error %d when trying to lock mutex %s at %s, line %d\n" , |
298 | error, mp->name, file, line); |
299 | fflush(stderr); |
300 | abort(); |
301 | } |
302 | mp->thread= pthread_self(); |
303 | if (mp->count++) |
304 | { |
305 | fprintf(stderr,"safe_mutex: Error in thread libray: Got mutex %s at %s, " |
306 | "line %d more than 1 time\n" , mp->name, file,line); |
307 | fflush(stderr); |
308 | abort(); |
309 | } |
310 | mp->file= file; |
311 | mp->line= line; |
312 | mp->active_flags= mp->create_flags | my_flags; |
313 | pthread_mutex_unlock(&mp->global); |
314 | |
315 | /* Deadlock detection */ |
316 | |
317 | mp->prev= mp->next= 0; |
318 | if (!(mp->active_flags & (MYF_TRY_LOCK | MYF_NO_DEADLOCK_DETECTION)) && |
319 | (mp->used_mutex != NULL || !safe_mutex_lazy_init_deadlock_detection(mp))) |
320 | { |
321 | safe_mutex_t **mutex_in_use= my_thread_var_mutex_in_use(); |
322 | |
323 | if (!mutex_in_use) |
324 | { |
325 | /* thread has not called my_thread_init() */ |
326 | mp->active_flags|= MYF_NO_DEADLOCK_DETECTION; |
327 | } |
328 | else |
329 | { |
330 | safe_mutex_t *mutex_root; |
331 | if ((mutex_root= *mutex_in_use)) /* If not first locked */ |
332 | { |
333 | /* |
334 | Protect locked_mutex against changes if a mutex is deleted |
335 | */ |
336 | pthread_mutex_lock(&THR_LOCK_mutex); |
337 | |
338 | if (!my_hash_search(mutex_root->locked_mutex, (uchar*) &mp->id, 0)) |
339 | { |
340 | safe_mutex_deadlock_t *deadlock; |
341 | safe_mutex_t *mutex; |
342 | |
343 | /* Create object to store mutex info */ |
344 | if (!(deadlock= my_malloc(sizeof(*deadlock), |
345 | MYF(MY_ZEROFILL | MY_WME | MY_FAE)))) |
346 | goto abort_loop; |
347 | deadlock->name= mp->name; |
348 | deadlock->id= mp->id; |
349 | deadlock->mutex= mp; |
350 | /* The following is useful for debugging wrong mutex usage */ |
351 | deadlock->file= file; |
352 | deadlock->line= line; |
353 | |
354 | /* Check if potential deadlock */ |
355 | mutex= mutex_root; |
356 | do |
357 | { |
358 | if (my_hash_search(mp->locked_mutex, (uchar*) &mutex->id, 0)) |
359 | { |
360 | print_deadlock_warning(mp, mutex); |
361 | /* Mark wrong usage to avoid future warnings for same error */ |
362 | deadlock->warning_only= 1; |
363 | add_to_locked_mutex(deadlock, mutex_root); |
364 | DBUG_ASSERT(deadlock->count > 0); |
365 | goto abort_loop; |
366 | } |
367 | } |
368 | while ((mutex= mutex->next)); |
369 | |
370 | /* |
371 | Copy current mutex and all mutex that has been locked |
372 | after current mutex (mp->locked_mutex) to all mutex that |
373 | was locked before previous mutex (mutex_root->used_mutex) |
374 | |
375 | For example if A->B would have been done before and we |
376 | are now locking (C) in B->C, then we would add C into |
377 | B->locked_mutex and A->locked_mutex |
378 | */ |
379 | my_hash_iterate(mutex_root->used_mutex, |
380 | (my_hash_walk_action) add_used_to_locked_mutex, |
381 | deadlock); |
382 | |
383 | /* |
384 | Copy all current mutex and all mutex locked after current one |
385 | into the prev mutex |
386 | */ |
387 | add_used_to_locked_mutex(mutex_root, deadlock); |
388 | DBUG_ASSERT(deadlock->count > 0); |
389 | } |
390 | abort_loop: |
391 | pthread_mutex_unlock(&THR_LOCK_mutex); |
392 | } |
393 | /* Link mutex into mutex_in_use list */ |
394 | if ((mp->next= *mutex_in_use)) |
395 | (*mutex_in_use)->prev= mp; |
396 | *mutex_in_use= mp; |
397 | } |
398 | } |
399 | |
400 | DBUG_PRINT("mutex" , ("%s (0x%lx) locked" , mp->name, (ulong) mp)); |
401 | return error; |
402 | } |
403 | |
404 | |
405 | int safe_mutex_unlock(safe_mutex_t *mp,const char *file, uint line) |
406 | { |
407 | int error; |
408 | DBUG_PRINT("mutex" , ("%s (0x%lx) unlocking" , mp->name, (ulong) mp)); |
409 | pthread_mutex_lock(&mp->global); |
410 | if (mp->count == 0) |
411 | { |
412 | fprintf(stderr, |
413 | "safe_mutex: Trying to unlock mutex %s that wasn't locked at " |
414 | "%s, line %d\n" |
415 | "Last used at %s, line: %d\n" , |
416 | mp->name ? mp->name : "Null" , file, line, |
417 | mp->file ? mp->file : "Null" , mp->line); |
418 | fflush(stderr); |
419 | abort(); |
420 | } |
421 | if (!pthread_equal(pthread_self(),mp->thread)) |
422 | { |
423 | fprintf(stderr, |
424 | "safe_mutex: Trying to unlock mutex %s at %s, line %d that was " |
425 | "locked by " |
426 | "another thread at: %s, line: %d\n" , |
427 | mp->name, file, line, mp->file, mp->line); |
428 | fflush(stderr); |
429 | abort(); |
430 | } |
431 | mp->thread= 0; |
432 | mp->count--; |
433 | |
434 | remove_from_active_list(mp); |
435 | |
436 | #ifdef __WIN__ |
437 | pthread_mutex_unlock(&mp->mutex); |
438 | error=0; |
439 | #else |
440 | error=pthread_mutex_unlock(&mp->mutex); |
441 | if (error) |
442 | { |
443 | fprintf(stderr, |
444 | "safe_mutex: Got error: %d (%d) when trying to unlock mutex " |
445 | "%s at %s, line %d\n" , error, errno, mp->name, file, line); |
446 | fflush(stderr); |
447 | abort(); |
448 | } |
449 | #endif /* __WIN__ */ |
450 | pthread_mutex_unlock(&mp->global); |
451 | return error; |
452 | } |
453 | |
454 | |
455 | int safe_cond_wait(pthread_cond_t *cond, safe_mutex_t *mp, const char *file, |
456 | uint line) |
457 | { |
458 | int error; |
459 | safe_mutex_t save_state; |
460 | |
461 | pthread_mutex_lock(&mp->global); |
462 | if (mp->count == 0) |
463 | { |
464 | fprintf(stderr, |
465 | "safe_mutex: Trying to cond_wait on a unlocked mutex %s at %s, " |
466 | "line %d\n" , |
467 | mp->name ? mp->name : "Null" , file, line); |
468 | fflush(stderr); |
469 | abort(); |
470 | } |
471 | if (!pthread_equal(pthread_self(),mp->thread)) |
472 | { |
473 | fprintf(stderr, |
474 | "safe_mutex: Trying to cond_wait on a mutex %s at %s, line %d " |
475 | "that was locked by another thread at: %s, line: %d\n" , |
476 | mp->name, file, line, mp->file, mp->line); |
477 | fflush(stderr); |
478 | abort(); |
479 | } |
480 | |
481 | if (mp->count-- != 1) |
482 | { |
483 | fprintf(stderr, |
484 | "safe_mutex: Count was %d on locked mutex %s at %s, line %d\n" , |
485 | mp->count+1, mp->name, file, line); |
486 | fflush(stderr); |
487 | abort(); |
488 | } |
489 | save_state= *mp; |
490 | remove_from_active_list(mp); |
491 | pthread_mutex_unlock(&mp->global); |
492 | error=pthread_cond_wait(cond,&mp->mutex); |
493 | pthread_mutex_lock(&mp->global); |
494 | |
495 | if (error) |
496 | { |
497 | fprintf(stderr, |
498 | "safe_mutex: Got error: %d (%d) when doing a safe_mutex_wait on " |
499 | "%s at %s, line %d\n" , error, errno, mp->name, file, line); |
500 | fflush(stderr); |
501 | abort(); |
502 | } |
503 | /* Restore state as it was before */ |
504 | mp->thread= save_state.thread; |
505 | mp->active_flags= save_state.active_flags; |
506 | mp->next= save_state.next; |
507 | mp->prev= save_state.prev; |
508 | |
509 | if (mp->count++) |
510 | { |
511 | fprintf(stderr, |
512 | "safe_mutex: Count was %d in thread 0x%lx when locking mutex %s " |
513 | "at %s, line %d\n" , |
514 | mp->count-1, (ulong) my_thread_dbug_id(), mp->name, file, line); |
515 | fflush(stderr); |
516 | abort(); |
517 | } |
518 | mp->file= file; |
519 | mp->line=line; |
520 | pthread_mutex_unlock(&mp->global); |
521 | return error; |
522 | } |
523 | |
524 | |
525 | int safe_cond_timedwait(pthread_cond_t *cond, safe_mutex_t *mp, |
526 | const struct timespec *abstime, |
527 | const char *file, uint line) |
528 | { |
529 | int error; |
530 | safe_mutex_t save_state; |
531 | |
532 | pthread_mutex_lock(&mp->global); |
533 | if (mp->count != 1 || !pthread_equal(pthread_self(),mp->thread)) |
534 | { |
535 | fprintf(stderr, |
536 | "safe_mutex: Trying to cond_wait at %s, line %d on a not hold " |
537 | "mutex %s\n" , |
538 | file, line, mp->name ? mp->name : "Null" ); |
539 | fflush(stderr); |
540 | abort(); |
541 | } |
542 | mp->count--; /* Mutex will be released */ |
543 | save_state= *mp; |
544 | remove_from_active_list(mp); |
545 | pthread_mutex_unlock(&mp->global); |
546 | error=pthread_cond_timedwait(cond,&mp->mutex,abstime); |
547 | #ifdef EXTRA_DEBUG |
548 | if (error && (error != EINTR && error != ETIMEDOUT && error != ETIME)) |
549 | { |
550 | fprintf(stderr, |
551 | "safe_mutex: Got error: %d (%d) when doing a safe_mutex_timedwait " |
552 | "on %s at %s, line %d\n" , |
553 | error, errno, mp->name, file, line); |
554 | } |
555 | #endif /* EXTRA_DEBUG */ |
556 | pthread_mutex_lock(&mp->global); |
557 | /* Restore state as it was before */ |
558 | mp->thread= save_state.thread; |
559 | mp->active_flags= save_state.active_flags; |
560 | mp->next= save_state.next; |
561 | mp->prev= save_state.prev; |
562 | |
563 | if (mp->count++) |
564 | { |
565 | fprintf(stderr, |
566 | "safe_mutex: Count was %d in thread 0x%lx when locking mutex " |
567 | "%s at %s, line %d (error: %d (%d))\n" , |
568 | mp->count-1, (ulong) my_thread_dbug_id(), mp->name, file, line, |
569 | error, error); |
570 | fflush(stderr); |
571 | abort(); |
572 | } |
573 | mp->file= file; |
574 | mp->line=line; |
575 | pthread_mutex_unlock(&mp->global); |
576 | return error; |
577 | } |
578 | |
579 | |
580 | int safe_mutex_destroy(safe_mutex_t *mp, const char *file, uint line) |
581 | { |
582 | int error=0; |
583 | DBUG_ENTER("safe_mutex_destroy" ); |
584 | DBUG_PRINT("enter" , ("mutex: 0x%lx name: %s" , (ulong) mp, mp->name)); |
585 | if (!mp->file) |
586 | { |
587 | fprintf(stderr, |
588 | "safe_mutex: Trying to destroy uninitialized mutex at %s, line %d\n" , |
589 | file, line); |
590 | fflush(stderr); |
591 | abort(); |
592 | } |
593 | if (mp->count != 0) |
594 | { |
595 | fprintf(stderr, |
596 | "safe_mutex: Trying to destroy a mutex %s that was locked at %s, " |
597 | "line %d at %s, line %d\n" , |
598 | mp->name, mp->file, mp->line, file, line); |
599 | fflush(stderr); |
600 | abort(); |
601 | } |
602 | |
603 | /* Free all entries that points to this one */ |
604 | safe_mutex_free_deadlock_data(mp); |
605 | |
606 | #ifdef __WIN__ |
607 | pthread_mutex_destroy(&mp->global); |
608 | pthread_mutex_destroy(&mp->mutex); |
609 | #else |
610 | if (pthread_mutex_destroy(&mp->global)) |
611 | error=1; |
612 | if (pthread_mutex_destroy(&mp->mutex)) |
613 | error=1; |
614 | #endif /* __WIN__ */ |
615 | mp->file= 0; /* Mark destroyed */ |
616 | |
617 | #ifdef SAFE_MUTEX_DETECT_DESTROY |
618 | if (mp->info) |
619 | { |
620 | struct st_safe_mutex_info_t *info= mp->info; |
621 | pthread_mutex_lock(&THR_LOCK_mutex); |
622 | |
623 | if (info->prev) |
624 | info->prev->next = info->next; |
625 | else |
626 | safe_mutex_create_root = info->next; |
627 | if (info->next) |
628 | info->next->prev = info->prev; |
629 | safe_mutex_count--; |
630 | |
631 | pthread_mutex_unlock(&THR_LOCK_mutex); |
632 | free(info); |
633 | mp->info= NULL; /* Get crash if double free */ |
634 | } |
635 | #else |
636 | pthread_mutex_lock(&THR_LOCK_mutex); |
637 | safe_mutex_count--; |
638 | pthread_mutex_unlock(&THR_LOCK_mutex); |
639 | #endif /* SAFE_MUTEX_DETECT_DESTROY */ |
640 | DBUG_RETURN(error); |
641 | } |
642 | |
643 | |
644 | /** |
645 | Free all data related to deadlock detection |
646 | |
647 | This is also useful together with safemalloc when you don't want to |
648 | have reports of not freed memory for mysys mutexes. |
649 | */ |
650 | |
651 | void safe_mutex_free_deadlock_data(safe_mutex_t *mp) |
652 | { |
653 | /* Free all entries that points to this one */ |
654 | if (!(mp->create_flags & MYF_NO_DEADLOCK_DETECTION) && mp->used_mutex != NULL) |
655 | { |
656 | pthread_mutex_lock(&THR_LOCK_mutex); |
657 | my_hash_iterate(mp->used_mutex, |
658 | (my_hash_walk_action) remove_from_locked_mutex, |
659 | mp); |
660 | my_hash_iterate(mp->locked_mutex, |
661 | (my_hash_walk_action) remove_from_used_mutex, |
662 | mp); |
663 | pthread_mutex_unlock(&THR_LOCK_mutex); |
664 | |
665 | my_hash_free(mp->used_mutex); |
666 | my_hash_free(mp->locked_mutex); |
667 | my_free(mp->locked_mutex); |
668 | mp->create_flags|= MYF_NO_DEADLOCK_DETECTION; |
669 | } |
670 | } |
671 | |
672 | /* |
673 | Free global resources and check that all mutex has been destroyed |
674 | |
675 | SYNOPSIS |
676 | safe_mutex_end() |
677 | file Print errors on this file |
678 | |
679 | NOTES |
680 | We can't use DBUG_PRINT() here as we have in my_end() disabled |
681 | DBUG handling before calling this function. |
682 | |
683 | In MySQL one may get one warning for a mutex created in my_thr_init.c |
684 | This is ok, as this thread may not yet have been exited. |
685 | */ |
686 | |
687 | void safe_mutex_end(FILE *file __attribute__((unused))) |
688 | { |
689 | if (!safe_mutex_count) /* safetly */ |
690 | pthread_mutex_destroy(&THR_LOCK_mutex); |
691 | #ifdef SAFE_MUTEX_DETECT_DESTROY |
692 | if (!file) |
693 | return; |
694 | |
695 | if (safe_mutex_count) |
696 | { |
697 | fprintf(file, "Warning: Not destroyed mutex: %lu\n" , safe_mutex_count); |
698 | (void) fflush(file); |
699 | } |
700 | { |
701 | struct st_safe_mutex_info_t *ptr; |
702 | for (ptr= safe_mutex_create_root ; ptr ; ptr= ptr->next) |
703 | { |
704 | fprintf(file, "\tMutex %s initiated at line %4u in '%s'\n" , |
705 | ptr->name, ptr->init_line, ptr->init_file); |
706 | (void) fflush(file); |
707 | } |
708 | } |
709 | #endif /* SAFE_MUTEX_DETECT_DESTROY */ |
710 | } |
711 | |
712 | static my_bool add_used_to_locked_mutex(safe_mutex_t *used_mutex, |
713 | safe_mutex_deadlock_t *locked_mutex) |
714 | { |
715 | /* Add mutex to all parent of the current mutex */ |
716 | if (!locked_mutex->warning_only) |
717 | { |
718 | (void) my_hash_iterate(locked_mutex->mutex->locked_mutex, |
719 | (my_hash_walk_action) add_to_locked_mutex, |
720 | used_mutex); |
721 | /* mark that locked_mutex is locked after used_mutex */ |
722 | (void) add_to_locked_mutex(locked_mutex, used_mutex); |
723 | } |
724 | return 0; |
725 | } |
726 | |
727 | |
728 | /** |
729 | register that locked_mutex was locked after current_mutex |
730 | */ |
731 | |
732 | static my_bool add_to_locked_mutex(safe_mutex_deadlock_t *locked_mutex, |
733 | safe_mutex_t *current_mutex) |
734 | { |
735 | DBUG_ENTER("add_to_locked_mutex" ); |
736 | DBUG_PRINT("info" , ("inserting 0x%lx into 0x%lx (id: %lu -> %lu)" , |
737 | (ulong) locked_mutex, (long) current_mutex, |
738 | locked_mutex->id, current_mutex->id)); |
739 | if (my_hash_insert(current_mutex->locked_mutex, (uchar*) locked_mutex)) |
740 | { |
741 | /* Got mutex through two paths; ignore */ |
742 | DBUG_RETURN(0); |
743 | } |
744 | locked_mutex->count++; |
745 | if (my_hash_insert(locked_mutex->mutex->used_mutex, |
746 | (uchar*) current_mutex)) |
747 | { |
748 | DBUG_ASSERT(0); |
749 | } |
750 | DBUG_RETURN(0); |
751 | } |
752 | |
753 | |
754 | /** |
755 | Remove mutex from the locked mutex hash |
756 | @fn remove_from_used_mutex() |
757 | @param mp Mutex that has delete_mutex in it's locked_mutex hash |
758 | @param delete_mutex Mutex should be removed from the hash |
759 | |
760 | @notes |
761 | safe_mutex_deadlock_t entries in the locked hash are shared. |
762 | When counter goes to 0, we delete the safe_mutex_deadlock_t entry. |
763 | */ |
764 | |
765 | static my_bool remove_from_locked_mutex(safe_mutex_t *mp, |
766 | safe_mutex_t *delete_mutex) |
767 | { |
768 | safe_mutex_deadlock_t *found; |
769 | DBUG_ENTER("remove_from_locked_mutex" ); |
770 | DBUG_PRINT("enter" , ("delete_mutex: 0x%lx mutex: 0x%lx (id: %lu <- %lu)" , |
771 | (ulong) delete_mutex, (ulong) mp, |
772 | delete_mutex->id, mp->id)); |
773 | |
774 | found= (safe_mutex_deadlock_t *) my_hash_search(mp->locked_mutex, |
775 | (uchar*) &delete_mutex->id, 0); |
776 | DBUG_ASSERT(found); |
777 | if (found) |
778 | { |
779 | if (my_hash_delete(mp->locked_mutex, (uchar*) found)) |
780 | { |
781 | DBUG_ASSERT(0); |
782 | } |
783 | if (!--found->count) |
784 | my_free(found); |
785 | } |
786 | DBUG_RETURN(0); |
787 | } |
788 | |
789 | static my_bool remove_from_used_mutex(safe_mutex_deadlock_t *locked_mutex, |
790 | safe_mutex_t *mutex) |
791 | { |
792 | DBUG_ENTER("remove_from_used_mutex" ); |
793 | DBUG_PRINT("enter" , ("delete_mutex: 0x%lx mutex: 0x%lx (id: %lu <- %lu)" , |
794 | (ulong) mutex, (ulong) locked_mutex, |
795 | mutex->id, locked_mutex->id)); |
796 | if (my_hash_delete(locked_mutex->mutex->used_mutex, (uchar*) mutex)) |
797 | { |
798 | DBUG_ASSERT(0); |
799 | } |
800 | if (!--locked_mutex->count) |
801 | my_free(locked_mutex); |
802 | DBUG_RETURN(0); |
803 | } |
804 | |
805 | |
806 | static void print_deadlock_warning(safe_mutex_t *new_mutex, |
807 | safe_mutex_t *parent_mutex) |
808 | { |
809 | safe_mutex_t *mutex_root; |
810 | DBUG_ENTER("print_deadlock_warning" ); |
811 | DBUG_PRINT("enter" , ("mutex: %s parent: %s" , |
812 | new_mutex->name, parent_mutex->name)); |
813 | |
814 | fprintf(stderr, "safe_mutex: Found wrong usage of mutex " |
815 | "'%s' and '%s'\n" , |
816 | parent_mutex->name, new_mutex->name); |
817 | DBUG_PRINT("info" , ("safe_mutex: Found wrong usage of mutex " |
818 | "'%s' and '%s'" , |
819 | parent_mutex->name, new_mutex->name)); |
820 | fprintf(stderr, "Mutex currently locked (in reverse order):\n" ); |
821 | DBUG_PRINT("info" , ("Mutex currently locked (in reverse order):" )); |
822 | fprintf(stderr, "%-32.32s %s line %u\n" , new_mutex->name, new_mutex->file, |
823 | new_mutex->line); |
824 | DBUG_PRINT("info" , ("%-32.32s %s line %u\n" , new_mutex->name, |
825 | new_mutex->file, new_mutex->line)); |
826 | for (mutex_root= *my_thread_var_mutex_in_use() ; |
827 | mutex_root; |
828 | mutex_root= mutex_root->next) |
829 | { |
830 | fprintf(stderr, "%-32.32s %s line %u\n" , mutex_root->name, |
831 | mutex_root->file, mutex_root->line); |
832 | DBUG_PRINT("info" , ("%-32.32s %s line %u" , mutex_root->name, |
833 | mutex_root->file, mutex_root->line)); |
834 | } |
835 | fflush(stderr); |
836 | DBUG_ASSERT(my_assert_on_error == 0); |
837 | DBUG_VOID_RETURN; |
838 | } |
839 | |
840 | #endif |
841 | |