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-1335 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
my_mutex_init()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
my_mutex_end()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
safe_mutex_global_init(void)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
remove_from_active_list(safe_mutex_t * mp)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
safe_mutex_lazy_init_deadlock_detection(safe_mutex_t * mp)157 static int safe_mutex_lazy_init_deadlock_detection(safe_mutex_t *mp)
158 {
159 if (!my_multi_malloc(PSI_NOT_INSTRUMENTED, 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(PSI_NOT_INSTRUMENTED, mp->locked_mutex, 64, &my_charset_bin, 128,
173 offsetof(safe_mutex_deadlock_t, id), sizeof(mp->id), 0, 0, 0,
174 HASH_UNIQUE);
175 my_hash_init2(PSI_NOT_INSTRUMENTED, mp->used_mutex, 64, &my_charset_bin, 128,
176 offsetof(safe_mutex_t, id), sizeof(mp->id), 0, 0, 0,
177 HASH_UNIQUE);
178 return 0;
179 }
180
safe_mutex_init(safe_mutex_t * mp,const pthread_mutexattr_t * attr,const char * name,const char * file,uint line)181 int safe_mutex_init(safe_mutex_t *mp,
182 const pthread_mutexattr_t *attr __attribute__((unused)),
183 const char *name, const char *file, uint line)
184 {
185 DBUG_ENTER("safe_mutex_init");
186 DBUG_PRINT("enter",("mutex: 0x%lx name: %s", (ulong) mp, name));
187 bzero((char*) mp,sizeof(*mp));
188 pthread_mutex_init(&mp->global,MY_MUTEX_INIT_ERRCHK);
189 pthread_mutex_init(&mp->mutex,attr);
190 /* Mark that mutex is initialized */
191 mp->file= file;
192 mp->line= line;
193 /* Skip the very common '&' prefix from the autogenerated name */
194 mp->name= name[0] == '&' ? name + 1 : name;
195
196 /* Deadlock detection is initialised only lazily, on first use. */
197
198 mp->create_flags= safe_mutex_deadlock_detector ? 0 : MYF_NO_DEADLOCK_DETECTION;
199
200 #ifdef SAFE_MUTEX_DETECT_DESTROY
201 /*
202 Monitor the freeing of mutexes. This code depends on single thread init
203 and destroy
204 */
205 if ((mp->info= (safe_mutex_info_t *) malloc(sizeof(safe_mutex_info_t))))
206 {
207 struct st_safe_mutex_info_t *info= mp->info;
208
209 info->init_file= file;
210 info->init_line= line;
211 info->prev= NULL;
212 info->next= NULL;
213
214 pthread_mutex_lock(&THR_LOCK_mutex);
215 if ((info->next= safe_mutex_create_root))
216 safe_mutex_create_root->prev= info;
217 safe_mutex_create_root= info;
218 safe_mutex_count++;
219 pthread_mutex_unlock(&THR_LOCK_mutex);
220 }
221 #else
222 pthread_mutex_lock(&THR_LOCK_mutex);
223 safe_mutex_count++;
224 pthread_mutex_unlock(&THR_LOCK_mutex);
225 #endif /* SAFE_MUTEX_DETECT_DESTROY */
226 DBUG_RETURN(0);
227 }
228
229
safe_mutex_lock(safe_mutex_t * mp,myf my_flags,const char * file,uint line)230 int safe_mutex_lock(safe_mutex_t *mp, myf my_flags, const char *file,
231 uint line)
232 {
233 int error;
234 DBUG_PRINT("mutex", ("%s (0x%lx) locking", mp->name ? mp->name : "Null",
235 (ulong) mp));
236 DBUG_PUSH_EMPTY;
237
238 pthread_mutex_lock(&mp->global);
239 if (!mp->file)
240 {
241 fprintf(stderr,
242 "safe_mutex: Trying to lock uninitialized mutex at %s, line %d\n",
243 file, line);
244 fflush(stderr);
245 abort();
246 }
247 if (mp->count > 0)
248 {
249 /*
250 Check that we are not trying to lock mutex twice. This is an error
251 even if we are using 'try_lock' as it's not portably what happens
252 if you lock the mutex many times and this is in any case bad
253 behaviour that should not be encouraged
254 */
255 if (pthread_equal(pthread_self(),mp->thread))
256 {
257 fprintf(stderr,
258 "safe_mutex: Trying to lock mutex at %s, line %d, when the"
259 " mutex was already locked at %s, line %d in thread %s\n",
260 file,line,mp->file, mp->line, my_thread_name());
261 fflush(stderr);
262 abort();
263 }
264 }
265 pthread_mutex_unlock(&mp->global);
266
267 /*
268 If we are imitating trylock(), we need to take special
269 precautions.
270
271 - We cannot use pthread_mutex_lock() only since another thread can
272 overtake this thread and take the lock before this thread
273 causing pthread_mutex_trylock() to hang. In this case, we should
274 just return EBUSY. Hence, we use pthread_mutex_trylock() to be
275 able to return immediately.
276
277 - We cannot just use trylock() and continue execution below, since
278 this would generate an error and abort execution if the thread
279 was overtaken and trylock() returned EBUSY . In this case, we
280 instead just return EBUSY, since this is the expected behaviour
281 of trylock().
282 */
283 if (my_flags & MYF_TRY_LOCK)
284 {
285 error= pthread_mutex_trylock(&mp->mutex);
286 if (error == EBUSY)
287 goto end;
288 }
289 else
290 error= pthread_mutex_lock(&mp->mutex);
291
292 if (error || (error=pthread_mutex_lock(&mp->global)))
293 {
294 fprintf(stderr,"Got error %d when trying to lock mutex %s at %s, line %d\n",
295 error, mp->name, file, line);
296 fflush(stderr);
297 abort();
298 }
299 mp->thread= pthread_self();
300 if (mp->count++)
301 {
302 fprintf(stderr,"safe_mutex: Error in thread libray: Got mutex %s at %s, "
303 "line %d more than 1 time\n", mp->name, file,line);
304 fflush(stderr);
305 abort();
306 }
307 mp->file= file;
308 mp->line= line;
309 mp->active_flags= mp->create_flags | my_flags;
310 pthread_mutex_unlock(&mp->global);
311
312 /* Deadlock detection */
313
314 mp->prev= mp->next= 0;
315 if (!(mp->active_flags & (MYF_TRY_LOCK | MYF_NO_DEADLOCK_DETECTION)) &&
316 (mp->used_mutex != NULL || !safe_mutex_lazy_init_deadlock_detection(mp)))
317 {
318 safe_mutex_t **mutex_in_use= my_thread_var_mutex_in_use();
319
320 if (!mutex_in_use)
321 {
322 /* thread has not called my_thread_init() */
323 mp->active_flags|= MYF_NO_DEADLOCK_DETECTION;
324 }
325 else
326 {
327 safe_mutex_t *mutex_root;
328 if ((mutex_root= *mutex_in_use)) /* If not first locked */
329 {
330 /*
331 Protect locked_mutex against changes if a mutex is deleted
332 */
333 pthread_mutex_lock(&THR_LOCK_mutex);
334
335 if (!my_hash_search(mutex_root->locked_mutex, (uchar*) &mp->id, 0))
336 {
337 safe_mutex_deadlock_t *deadlock;
338 safe_mutex_t *mutex;
339
340 /* Create object to store mutex info */
341 if (!(deadlock= my_malloc(PSI_NOT_INSTRUMENTED, sizeof(*deadlock),
342 MYF(MY_ZEROFILL | MY_WME | MY_FAE))))
343 goto abort_loop;
344 deadlock->name= mp->name;
345 deadlock->id= mp->id;
346 deadlock->mutex= mp;
347 /* The following is useful for debugging wrong mutex usage */
348 deadlock->file= file;
349 deadlock->line= line;
350
351 /* Check if potential deadlock */
352 mutex= mutex_root;
353 do
354 {
355 if (my_hash_search(mp->locked_mutex, (uchar*) &mutex->id, 0))
356 {
357 print_deadlock_warning(mp, mutex);
358 /* Mark wrong usage to avoid future warnings for same error */
359 deadlock->warning_only= 1;
360 add_to_locked_mutex(deadlock, mutex_root);
361 DBUG_ASSERT(deadlock->count > 0);
362 goto abort_loop;
363 }
364 }
365 while ((mutex= mutex->next));
366
367 /*
368 Copy current mutex and all mutex that has been locked
369 after current mutex (mp->locked_mutex) to all mutex that
370 was locked before previous mutex (mutex_root->used_mutex)
371
372 For example if A->B would have been done before and we
373 are now locking (C) in B->C, then we would add C into
374 B->locked_mutex and A->locked_mutex
375 */
376 my_hash_iterate(mutex_root->used_mutex,
377 (my_hash_walk_action) add_used_to_locked_mutex,
378 deadlock);
379
380 /*
381 Copy all current mutex and all mutex locked after current one
382 into the prev mutex
383 */
384 add_used_to_locked_mutex(mutex_root, deadlock);
385 DBUG_ASSERT(deadlock->count > 0);
386 }
387 abort_loop:
388 pthread_mutex_unlock(&THR_LOCK_mutex);
389 }
390 /* Link mutex into mutex_in_use list */
391 if ((mp->next= *mutex_in_use))
392 (*mutex_in_use)->prev= mp;
393 *mutex_in_use= mp;
394 }
395 }
396
397 end:
398 DBUG_POP_EMPTY;
399 if (!error)
400 DBUG_PRINT("mutex", ("%s (0x%lx) locked", mp->name, (ulong) mp));
401 return error;
402 }
403
404
safe_mutex_unlock(safe_mutex_t * mp,const char * file,uint line)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
safe_cond_wait(pthread_cond_t * cond,safe_mutex_t * mp,const char * file,uint line)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
safe_cond_timedwait(pthread_cond_t * cond,safe_mutex_t * mp,const struct timespec * abstime,const char * file,uint line)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
safe_mutex_destroy(safe_mutex_t * mp,const char * file,uint line)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
safe_mutex_free_deadlock_data(safe_mutex_t * mp)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
safe_mutex_end(FILE * file)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
add_used_to_locked_mutex(safe_mutex_t * used_mutex,safe_mutex_deadlock_t * locked_mutex)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
add_to_locked_mutex(safe_mutex_deadlock_t * locked_mutex,safe_mutex_t * current_mutex)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
remove_from_locked_mutex(safe_mutex_t * mp,safe_mutex_t * delete_mutex)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
remove_from_used_mutex(safe_mutex_deadlock_t * locked_mutex,safe_mutex_t * mutex)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
print_deadlock_warning(safe_mutex_t * new_mutex,safe_mutex_t * parent_mutex)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