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
49pthread_mutexattr_t my_fast_mutexattr;
50#endif
51#ifdef PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP
52pthread_mutexattr_t my_errorcheck_mutexattr;
53#endif
54
55#ifdef SAFE_MUTEX_DEFINED
56static pthread_mutex_t THR_LOCK_mutex;
57static ulong safe_mutex_count= 0; /* Number of mutexes created */
58static ulong safe_mutex_id= 0;
59my_bool safe_mutex_deadlock_detector= 1; /* On by default */
60
61#ifdef SAFE_MUTEX_DETECT_DESTROY
62static struct st_safe_mutex_create_info_t *safe_mutex_create_root= NULL;
63#endif
64
65static my_bool add_used_to_locked_mutex(safe_mutex_t *used_mutex,
66 safe_mutex_deadlock_t *locked_mutex);
67static my_bool add_to_locked_mutex(safe_mutex_deadlock_t *locked_mutex,
68 safe_mutex_t *current_mutex);
69static my_bool remove_from_locked_mutex(safe_mutex_t *mp,
70 safe_mutex_t *delete_mutex);
71static my_bool remove_from_used_mutex(safe_mutex_deadlock_t *locked_mutex,
72 safe_mutex_t *mutex);
73static 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
80void 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
111void 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
125void 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
136static 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
157static 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
185int 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
234int 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
405int 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
455int 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
525int 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
580int 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
651void 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
687void 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
712static 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
732static 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
765static 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
789static 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
806static 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