Coverage Report

Created: 2024-06-18 07:03

/src/server/mysys/thr_mutex.c
Line
Count
Source (jump to first uncovered line)
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
80
void my_mutex_init()
81
0
{
82
  /* Initialize mutex attributes */
83
0
#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
0
  pthread_mutexattr_init(&my_fast_mutexattr);
94
0
  pthread_mutexattr_settype(&my_fast_mutexattr,
95
0
                            PTHREAD_MUTEX_ADAPTIVE_NP);
96
0
#endif
97
0
#ifdef PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP
98
  /*
99
    Set mutex type to "errorcheck"
100
  */
101
0
  pthread_mutexattr_init(&my_errorcheck_mutexattr);
102
0
  pthread_mutexattr_settype(&my_errorcheck_mutexattr,
103
0
                            PTHREAD_MUTEX_ERRORCHECK);
104
0
#endif
105
106
#if defined(SAFE_MUTEX_DEFINED)
107
  safe_mutex_global_init();
108
#endif
109
0
}
110
111
void my_mutex_end()
112
0
{
113
0
#ifdef PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP
114
0
  pthread_mutexattr_destroy(&my_fast_mutexattr);
115
0
#endif
116
0
#ifdef PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP
117
0
  pthread_mutexattr_destroy(&my_errorcheck_mutexattr);
118
0
#endif
119
0
}
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(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
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
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,
336
                            sizeof(mp->id)))
337
        {
338
          safe_mutex_deadlock_t *deadlock;
339
          safe_mutex_t *mutex;
340
341
          /* Create object to store mutex info */
342
          if (!(deadlock= my_malloc(PSI_NOT_INSTRUMENTED, sizeof(*deadlock),
343
                                    MYF(MY_ZEROFILL | MY_WME | MY_FAE))))
344
            goto abort_loop;
345
          deadlock->name= mp->name;
346
          deadlock->id= mp->id;
347
          deadlock->mutex= mp;
348
          /* The following is useful for debugging wrong mutex usage */
349
          deadlock->file= file;
350
          deadlock->line= line;
351
352
          /* Check if potential deadlock */
353
          mutex= mutex_root;
354
          do
355
          {
356
            if (my_hash_search(mp->locked_mutex, (uchar*) &mutex->id,
357
                               sizeof(mutex->id)))
358
            {
359
              print_deadlock_warning(mp, mutex);
360
              /* Mark wrong usage to avoid future warnings for same error */
361
              deadlock->warning_only= 1;
362
              add_to_locked_mutex(deadlock, mutex_root);
363
              DBUG_ASSERT(deadlock->count > 0);
364
              goto abort_loop;
365
            }
366
          }
367
          while ((mutex= mutex->next));
368
369
          /*
370
            Copy current mutex and all mutex that has been locked
371
            after current mutex (mp->locked_mutex) to all mutex that
372
            was locked before previous mutex (mutex_root->used_mutex)
373
374
            For example if A->B would have been done before and we
375
            are now locking (C) in B->C, then we would add C into
376
            B->locked_mutex and A->locked_mutex
377
          */
378
          my_hash_iterate(mutex_root->used_mutex,
379
                          (my_hash_walk_action) add_used_to_locked_mutex,
380
                          deadlock);
381
382
          /*
383
            Copy all current mutex and all mutex locked after current one
384
            into the prev mutex
385
          */
386
          add_used_to_locked_mutex(mutex_root, deadlock);
387
          DBUG_ASSERT(deadlock->count > 0);
388
        }
389
  abort_loop:
390
        pthread_mutex_unlock(&THR_LOCK_mutex);
391
      }
392
      /* Link mutex into mutex_in_use list */
393
      if ((mp->next= *mutex_in_use))
394
        (*mutex_in_use)->prev= mp;
395
      *mutex_in_use= mp;
396
    }
397
  }
398
399
end:
400
  DBUG_POP_EMPTY;
401
  if (!error)
402
    DBUG_PRINT("mutex", ("%s (0x%lx) locked", mp->name, (ulong) mp));
403
  return error;
404
}
405
406
407
int safe_mutex_unlock(safe_mutex_t *mp,const char *file, uint line)
408
{
409
  int error;
410
  DBUG_PRINT("mutex", ("%s (0x%lx) unlocking", mp->name, (ulong) mp));
411
  pthread_mutex_lock(&mp->global);
412
  if (mp->count == 0)
413
  {
414
    fprintf(stderr,
415
            "safe_mutex: Trying to unlock mutex %s that wasn't locked at "
416
            "%s, line %d\n"
417
            "Last used at %s, line: %d\n",
418
      mp->name ? mp->name : "Null", file, line,
419
            mp->file ? mp->file : "Null", mp->line);
420
    fflush(stderr);
421
    abort();
422
  }
423
  if (!pthread_equal(pthread_self(),mp->thread))
424
  {
425
    fprintf(stderr,
426
            "safe_mutex: Trying to unlock mutex %s at %s, line %d that was "
427
            "locked by "
428
            "another thread at: %s, line: %d\n",
429
      mp->name, file, line, mp->file, mp->line);
430
    fflush(stderr);
431
    abort();
432
  }
433
  mp->thread= 0;
434
  mp->count--;
435
436
  remove_from_active_list(mp);
437
438
#ifdef _WIN32
439
  pthread_mutex_unlock(&mp->mutex);
440
  error=0;
441
#else
442
  error=pthread_mutex_unlock(&mp->mutex);
443
  if (error)
444
  {
445
    fprintf(stderr,
446
            "safe_mutex: Got error: %d (%d) when trying to unlock mutex "
447
            "%s at %s, line %d\n", error, errno, mp->name, file, line);
448
    fflush(stderr);
449
    abort();
450
  }
451
#endif /* _WIN32 */
452
  pthread_mutex_unlock(&mp->global);
453
  return error;
454
}
455
456
457
int safe_cond_wait(pthread_cond_t *cond, safe_mutex_t *mp, const char *file,
458
       uint line)
459
{
460
  int error;
461
  safe_mutex_t save_state;
462
463
  pthread_mutex_lock(&mp->global);
464
  if (mp->count == 0)
465
  {
466
    fprintf(stderr,
467
            "safe_mutex: Trying to cond_wait on a unlocked mutex %s at %s, "
468
            "line %d\n",
469
            mp->name ? mp->name : "Null", file, line);
470
    fflush(stderr);
471
    abort();
472
  }
473
  if (!pthread_equal(pthread_self(),mp->thread))
474
  {
475
    fprintf(stderr,
476
            "safe_mutex: Trying to cond_wait on a mutex %s at %s, line %d "
477
            "that was locked by another thread at: %s, line: %d\n",
478
      mp->name, file, line, mp->file, mp->line);
479
    fflush(stderr);
480
    abort();
481
  }
482
483
  if (mp->count-- != 1)
484
  {
485
    fprintf(stderr,
486
            "safe_mutex:  Count was %d on locked mutex %s at %s, line %d\n",
487
      mp->count+1, mp->name, file, line);
488
    fflush(stderr);
489
    abort();
490
  }
491
  save_state= *mp;
492
  remove_from_active_list(mp);
493
  pthread_mutex_unlock(&mp->global);
494
  error=pthread_cond_wait(cond,&mp->mutex);
495
  pthread_mutex_lock(&mp->global);
496
497
  if (error)
498
  {
499
    fprintf(stderr,
500
            "safe_mutex: Got error: %d (%d) when doing a safe_mutex_wait on "
501
            "%s at %s, line %d\n", error, errno, mp->name, file, line);
502
    fflush(stderr);
503
    abort();
504
  }
505
  /* Restore state as it was before */
506
  mp->thread=       save_state.thread;
507
  mp->active_flags= save_state.active_flags;
508
  mp->next=         save_state.next;
509
  mp->prev=         save_state.prev;
510
511
  if (mp->count++)
512
  {
513
    fprintf(stderr,
514
      "safe_mutex:  Count was %d in thread 0x%lx when locking mutex %s "
515
            "at %s, line %d\n",
516
      mp->count-1, (ulong) my_thread_dbug_id(), mp->name, file, line);
517
    fflush(stderr);
518
    abort();
519
  }
520
  mp->file= file;
521
  mp->line=line;
522
  pthread_mutex_unlock(&mp->global);
523
  return error;
524
}
525
526
527
int safe_cond_timedwait(pthread_cond_t *cond, safe_mutex_t *mp,
528
      const struct timespec *abstime,
529
      const char *file, uint line)
530
{
531
  int error;
532
  safe_mutex_t save_state;
533
534
  pthread_mutex_lock(&mp->global);
535
  if (mp->count != 1 || !pthread_equal(pthread_self(),mp->thread))
536
  {
537
    fprintf(stderr,
538
            "safe_mutex: Trying to cond_wait at %s, line %d on a not hold "
539
            "mutex %s\n",
540
            file, line, mp->name ? mp->name : "Null");
541
    fflush(stderr);
542
    abort();
543
  }
544
  mp->count--;          /* Mutex will be released */
545
  save_state= *mp;
546
  remove_from_active_list(mp);
547
  pthread_mutex_unlock(&mp->global);
548
  error=pthread_cond_timedwait(cond,&mp->mutex,abstime);
549
#ifdef EXTRA_DEBUG
550
  if (error && (error != EINTR && error != ETIMEDOUT && error != ETIME))
551
  {
552
    fprintf(stderr,
553
            "safe_mutex: Got error: %d (%d) when doing a safe_mutex_timedwait "
554
            "on %s at %s, line %d\n",
555
            error, errno, mp->name, file, line);
556
  }
557
#endif /* EXTRA_DEBUG */
558
  pthread_mutex_lock(&mp->global);
559
  /* Restore state as it was before */
560
  mp->thread=       save_state.thread;
561
  mp->active_flags= save_state.active_flags;
562
  mp->next=         save_state.next;
563
  mp->prev=         save_state.prev;
564
565
  if (mp->count++)
566
  {
567
    fprintf(stderr,
568
      "safe_mutex:  Count was %d in thread 0x%lx when locking mutex "
569
            "%s at %s, line %d (error: %d (%d))\n",
570
      mp->count-1, (ulong) my_thread_dbug_id(), mp->name, file, line,
571
            error, error);
572
    fflush(stderr);
573
    abort();
574
  }
575
  mp->file= file;
576
  mp->line=line;
577
  pthread_mutex_unlock(&mp->global);
578
  return error;
579
}
580
581
582
int safe_mutex_destroy(safe_mutex_t *mp, const char *file, uint line)
583
{
584
  int error=0;
585
  DBUG_ENTER("safe_mutex_destroy");
586
  DBUG_PRINT("enter", ("mutex: 0x%lx  name: %s", (ulong) mp, mp->name));
587
  if (!mp->file)
588
  {
589
    fprintf(stderr,
590
      "safe_mutex: Trying to destroy uninitialized mutex at %s, line %d\n",
591
      file, line);
592
    fflush(stderr);
593
    abort();
594
  }
595
  if (mp->count != 0)
596
  {
597
    fprintf(stderr,
598
            "safe_mutex: Trying to destroy a mutex %s that was locked at %s, "
599
            "line %d at %s, line %d\n",
600
      mp->name, mp->file, mp->line, file, line);
601
    fflush(stderr);
602
    abort();
603
  }
604
605
  /* Free all entries that points to this one */
606
  safe_mutex_free_deadlock_data(mp);
607
608
#ifdef _WIN32 
609
  pthread_mutex_destroy(&mp->global);
610
  pthread_mutex_destroy(&mp->mutex);
611
#else
612
  if (pthread_mutex_destroy(&mp->global))
613
    error=1;
614
  if (pthread_mutex_destroy(&mp->mutex))
615
    error=1;
616
#endif /* _WIN32 */
617
  mp->file= 0;          /* Mark destroyed */
618
619
#ifdef SAFE_MUTEX_DETECT_DESTROY
620
  if (mp->info)
621
  {
622
    struct st_safe_mutex_info_t *info= mp->info;
623
    pthread_mutex_lock(&THR_LOCK_mutex);
624
625
    if (info->prev)
626
      info->prev->next = info->next;
627
    else
628
      safe_mutex_create_root = info->next;
629
    if (info->next)
630
      info->next->prev = info->prev;
631
    safe_mutex_count--;
632
633
    pthread_mutex_unlock(&THR_LOCK_mutex);
634
    free(info);
635
    mp->info= NULL;       /* Get crash if double free */
636
  }
637
#else
638
  pthread_mutex_lock(&THR_LOCK_mutex);
639
  safe_mutex_count--;
640
  pthread_mutex_unlock(&THR_LOCK_mutex);
641
#endif /* SAFE_MUTEX_DETECT_DESTROY */
642
  DBUG_RETURN(error);
643
}
644
645
646
/**
647
  Free all data related to deadlock detection
648
649
  This is also useful together with safemalloc when you don't want to
650
  have reports of not freed memory for mysys mutexes.
651
*/
652
653
void safe_mutex_free_deadlock_data(safe_mutex_t *mp)
654
{
655
  /* Free all entries that points to this one */
656
  if (!(mp->create_flags & MYF_NO_DEADLOCK_DETECTION) && mp->used_mutex != NULL)
657
  {
658
    pthread_mutex_lock(&THR_LOCK_mutex);
659
    my_hash_iterate(mp->used_mutex,
660
                    (my_hash_walk_action) remove_from_locked_mutex,
661
                    mp);
662
    my_hash_iterate(mp->locked_mutex,
663
                    (my_hash_walk_action) remove_from_used_mutex,
664
                    mp);
665
    pthread_mutex_unlock(&THR_LOCK_mutex);
666
667
    my_hash_free(mp->used_mutex);
668
    my_hash_free(mp->locked_mutex);
669
    my_free(mp->locked_mutex);
670
    mp->locked_mutex= 0;
671
    mp->create_flags|= MYF_NO_DEADLOCK_DETECTION;
672
  }
673
}
674
675
/*
676
  Free global resources and check that all mutex has been destroyed
677
678
  SYNOPSIS
679
    safe_mutex_end()
680
    file    Print errors on this file
681
682
  NOTES
683
    We can't use DBUG_PRINT() here as we have in my_end() disabled
684
    DBUG handling before calling this function.
685
686
   In MySQL one may get one warning for a mutex created in my_thr_init.c
687
   This is ok, as this thread may not yet have been exited.
688
*/
689
690
void safe_mutex_end(FILE *file __attribute__((unused)))
691
{
692
  if (!safe_mutex_count)      /* safetly */
693
    pthread_mutex_destroy(&THR_LOCK_mutex);
694
#ifdef SAFE_MUTEX_DETECT_DESTROY
695
  if (!file)
696
    return;
697
698
  if (safe_mutex_count)
699
  {
700
    fprintf(file, "Warning: Not destroyed mutex: %lu\n", safe_mutex_count);
701
    (void) fflush(file);
702
  }
703
  {
704
    struct st_safe_mutex_info_t *ptr;
705
    for (ptr= safe_mutex_create_root ; ptr ; ptr= ptr->next)
706
    {
707
      fprintf(file, "\tMutex %s initiated at line %4u in '%s'\n",
708
        ptr->name, ptr->init_line, ptr->init_file);
709
      (void) fflush(file);
710
    }
711
  }
712
#endif /* SAFE_MUTEX_DETECT_DESTROY */
713
}
714
715
static my_bool add_used_to_locked_mutex(safe_mutex_t *used_mutex,
716
                                        safe_mutex_deadlock_t *locked_mutex)
717
{
718
  /* Add mutex to all parent of the current mutex */
719
  if (!locked_mutex->warning_only)
720
  {
721
    (void) my_hash_iterate(locked_mutex->mutex->locked_mutex,
722
                           (my_hash_walk_action) add_to_locked_mutex,
723
                           used_mutex);
724
    /* mark that locked_mutex is locked after used_mutex */
725
    (void) add_to_locked_mutex(locked_mutex, used_mutex);
726
  }
727
  return 0;
728
}
729
730
731
/**
732
   register that locked_mutex was locked after current_mutex
733
*/
734
735
static my_bool add_to_locked_mutex(safe_mutex_deadlock_t *locked_mutex,
736
                                   safe_mutex_t *current_mutex)
737
{
738
  DBUG_ENTER("add_to_locked_mutex");
739
  DBUG_PRINT("info", ("inserting 0x%lx  into  0x%lx  (id: %lu -> %lu)",
740
                      (ulong) locked_mutex, (long) current_mutex,
741
                      locked_mutex->id, current_mutex->id));
742
  if (my_hash_insert(current_mutex->locked_mutex, (uchar*) locked_mutex))
743
  {
744
    /* Got mutex through two paths; ignore */
745
    DBUG_RETURN(0);
746
  }
747
  locked_mutex->count++;
748
  if (my_hash_insert(locked_mutex->mutex->used_mutex,
749
                     (uchar*) current_mutex))
750
  {
751
    DBUG_ASSERT(0);
752
  }
753
  DBUG_RETURN(0);
754
}
755
756
757
/**
758
  Remove mutex from the locked mutex hash
759
  @fn    remove_from_used_mutex()
760
  @param mp            Mutex that has delete_mutex in it's locked_mutex hash
761
  @param delete_mutex  Mutex should be removed from the hash
762
763
  @notes
764
    safe_mutex_deadlock_t entries in the locked hash are shared.
765
    When counter goes to 0, we delete the safe_mutex_deadlock_t entry.
766
*/
767
768
static my_bool remove_from_locked_mutex(safe_mutex_t *mp,
769
                                        safe_mutex_t *delete_mutex)
770
{
771
  safe_mutex_deadlock_t *found;
772
  DBUG_ENTER("remove_from_locked_mutex");
773
  DBUG_PRINT("enter", ("delete_mutex: 0x%lx  mutex: 0x%lx  (id: %lu <- %lu)",
774
                       (ulong) delete_mutex, (ulong) mp, 
775
                       delete_mutex->id, mp->id));
776
777
  found= (safe_mutex_deadlock_t *) my_hash_search(mp->locked_mutex,
778
                                                 (uchar*) &delete_mutex->id,
779
                                                 sizeof(delete_mutex->id));
780
  DBUG_ASSERT(found);
781
  if (found)
782
  {
783
    if (my_hash_delete(mp->locked_mutex, (uchar*) found))
784
    {
785
      DBUG_ASSERT(0);
786
    }
787
    if (!--found->count)
788
      my_free(found);
789
  }
790
  DBUG_RETURN(0);
791
}
792
793
static my_bool remove_from_used_mutex(safe_mutex_deadlock_t *locked_mutex,
794
                                      safe_mutex_t *mutex)
795
{
796
  DBUG_ENTER("remove_from_used_mutex");
797
  DBUG_PRINT("enter", ("delete_mutex: 0x%lx  mutex: 0x%lx  (id: %lu <- %lu)",
798
                       (ulong) mutex, (ulong) locked_mutex, 
799
                       mutex->id, locked_mutex->id));
800
  if (my_hash_delete(locked_mutex->mutex->used_mutex, (uchar*) mutex))
801
  {
802
    DBUG_ASSERT(0);
803
  }
804
  if (!--locked_mutex->count)
805
    my_free(locked_mutex);
806
  DBUG_RETURN(0);
807
}
808
809
810
static void print_deadlock_warning(safe_mutex_t *new_mutex,
811
                                   safe_mutex_t *parent_mutex)
812
{
813
  safe_mutex_t *mutex_root;
814
  DBUG_ENTER("print_deadlock_warning");
815
  DBUG_PRINT("enter", ("mutex: %s  parent: %s",
816
                       new_mutex->name, parent_mutex->name));
817
818
  fprintf(stderr, "safe_mutex: Found wrong usage of mutex "
819
          "'%s' and '%s'\n",
820
          parent_mutex->name, new_mutex->name);
821
  DBUG_PRINT("info", ("safe_mutex: Found wrong usage of mutex "
822
                      "'%s' and '%s'",
823
                      parent_mutex->name, new_mutex->name));
824
  fprintf(stderr, "Mutex currently locked (in reverse order):\n");
825
  DBUG_PRINT("info", ("Mutex currently locked (in reverse order):"));
826
  fprintf(stderr, "%-32.32s  %s  line %u\n", new_mutex->name, new_mutex->file,
827
          new_mutex->line);
828
  DBUG_PRINT("info", ("%-32.32s  %s  line %u\n", new_mutex->name,
829
                      new_mutex->file, new_mutex->line));
830
  for (mutex_root= *my_thread_var_mutex_in_use() ;
831
       mutex_root;
832
       mutex_root= mutex_root->next)
833
  {
834
    fprintf(stderr, "%-32.32s  %s  line %u\n", mutex_root->name,
835
            mutex_root->file, mutex_root->line);
836
    DBUG_PRINT("info", ("%-32.32s  %s  line %u", mutex_root->name,
837
                        mutex_root->file, mutex_root->line));
838
  }
839
  fflush(stderr);
840
  DBUG_ASSERT(my_assert_on_error == 0);
841
  DBUG_VOID_RETURN;
842
}
843
844
#endif