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(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 
safe_mutex_init(safe_mutex_t * mp,const pthread_mutexattr_t * attr,const char * name,const char * file,uint line)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 
safe_mutex_lock(safe_mutex_t * mp,myf my_flags,const char * file,uint line)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 
241   pthread_mutex_lock(&mp->global);
242   if (!mp->file)
243   {
244     fprintf(stderr,
245 	    "safe_mutex: Trying to lock uninitialized mutex at %s, line %d\n",
246 	    file, line);
247     fflush(stderr);
248     abort();
249   }
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 
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