Coverage Report

Created: 2025-06-13 07:09

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