Coverage Report

Created: 2025-06-13 07:09

/src/server/mysys/my_thr_init.c
Line
Count
Source (jump to first uncovered line)
1
/* Copyright (c) 2000, 2011 Oracle and/or its affiliates.
2
   Copyright 2008, 2021, MariaDB Corporation.
3
4
   This program is free software; you can redistribute it and/or modify
5
   it under the terms of the GNU General Public License as published by
6
   the Free Software Foundation; version 2 of the License.
7
8
   This program is distributed in the hope that it will be useful,
9
   but WITHOUT ANY WARRANTY; without even the implied warranty of
10
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
   GNU General Public License for more details.
12
13
   You should have received a copy of the GNU General Public License
14
   along with this program; if not, write to the Free Software
15
   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335  USA */
16
17
/*
18
  Functions to handle initializating and allocating of all mysys & debug
19
  thread variables.
20
*/
21
22
#include "mysys_priv.h"
23
#include <m_string.h>
24
#include <signal.h>
25
26
mysql_mutex_t THR_LOCK_malloc, THR_LOCK_open,
27
              THR_LOCK_lock, THR_LOCK_myisam, THR_LOCK_heap,
28
              THR_LOCK_net, THR_LOCK_charset, THR_LOCK_threads,
29
              THR_LOCK_myisam_mmap;
30
31
mysql_cond_t  THR_COND_threads;
32
uint            THR_thread_count= 0;
33
uint    my_thread_end_wait_time= 5;
34
#if !defined(HAVE_LOCALTIME_R) || !defined(HAVE_GMTIME_R)
35
mysql_mutex_t LOCK_localtime_r;
36
#endif
37
#ifdef _MSC_VER
38
static void install_sigabrt_handler();
39
#endif
40
41
/** True if @c my_thread_global_init() has been called. */
42
static my_bool my_thread_global_init_done= 0;
43
44
45
/*
46
  These are mutexes not used by safe_mutex or my_thr_init.c
47
48
  We want to free these earlier than other mutex so that safe_mutex
49
  can detect if all mutex and memory is freed properly.
50
*/
51
52
static void my_thread_init_common_mutex(void)
53
0
{
54
0
  mysql_mutex_init(key_THR_LOCK_open, &THR_LOCK_open, MY_MUTEX_INIT_FAST);
55
0
  mysql_mutex_init(key_THR_LOCK_lock, &THR_LOCK_lock, MY_MUTEX_INIT_FAST);
56
0
  mysql_mutex_init(key_THR_LOCK_myisam, &THR_LOCK_myisam, MY_MUTEX_INIT_SLOW);
57
0
  mysql_mutex_init(key_THR_LOCK_myisam_mmap, &THR_LOCK_myisam_mmap, MY_MUTEX_INIT_FAST);
58
0
  mysql_mutex_init(key_THR_LOCK_heap, &THR_LOCK_heap, MY_MUTEX_INIT_FAST);
59
0
  mysql_mutex_init(key_THR_LOCK_net, &THR_LOCK_net, MY_MUTEX_INIT_FAST);
60
0
  mysql_mutex_init(key_THR_LOCK_charset, &THR_LOCK_charset, MY_MUTEX_INIT_FAST);
61
#if !defined(HAVE_LOCALTIME_R) || !defined(HAVE_GMTIME_R)
62
  mysql_mutex_init(key_LOCK_localtime_r, &LOCK_localtime_r, MY_MUTEX_INIT_SLOW);
63
#endif
64
0
}
65
66
void my_thread_destroy_common_mutex(void)
67
0
{
68
0
  mysql_mutex_destroy(&THR_LOCK_open);
69
0
  mysql_mutex_destroy(&THR_LOCK_lock);
70
0
  mysql_mutex_destroy(&THR_LOCK_myisam);
71
0
  mysql_mutex_destroy(&THR_LOCK_myisam_mmap);
72
0
  mysql_mutex_destroy(&THR_LOCK_heap);
73
0
  mysql_mutex_destroy(&THR_LOCK_net);
74
0
  mysql_mutex_destroy(&THR_LOCK_charset);
75
#if !defined(HAVE_LOCALTIME_R) || !defined(HAVE_GMTIME_R)
76
  mysql_mutex_destroy(&LOCK_localtime_r);
77
#endif
78
0
}
79
80
81
/*
82
  These mutexes are used by my_thread_init() and after
83
  my_thread_destroy_mutex()
84
*/
85
86
static void my_thread_init_internal_mutex(void)
87
0
{
88
0
  mysql_mutex_init(key_THR_LOCK_threads, &THR_LOCK_threads, MY_MUTEX_INIT_FAST);
89
0
  mysql_mutex_init(key_THR_LOCK_malloc, &THR_LOCK_malloc, MY_MUTEX_INIT_FAST);
90
0
  mysql_cond_init(key_THR_COND_threads, &THR_COND_threads, NULL);
91
0
}
92
93
void my_thread_destroy_internal_mutex(void)
94
0
{
95
0
  mysql_mutex_destroy(&THR_LOCK_threads);
96
0
  mysql_mutex_destroy(&THR_LOCK_malloc);
97
0
  mysql_cond_destroy(&THR_COND_threads);
98
0
}
99
100
static void my_thread_init_thr_mutex(struct st_my_thread_var *var)
101
0
{
102
0
  mysql_mutex_init(key_my_thread_var_mutex, &var->mutex, MY_MUTEX_INIT_FAST);
103
0
  mysql_cond_init(key_my_thread_var_suspend, &var->suspend, NULL);
104
0
}
105
106
static void my_thread_destory_thr_mutex(struct st_my_thread_var *var)
107
0
{
108
0
  mysql_mutex_destroy(&var->mutex);
109
0
  mysql_cond_destroy(&var->suspend);
110
0
}
111
112
113
/**
114
  Re-initialize components initialized early with @c my_thread_global_init.
115
  Some mutexes were initialized before the instrumentation.
116
  Destroy + create them again, now that the instrumentation
117
  is in place.
118
  This is safe, since this function() is called before creating new threads,
119
  so the mutexes are not in use.
120
*/
121
void my_thread_global_reinit(void)
122
0
{
123
0
  struct st_my_thread_var *tmp;
124
125
0
  DBUG_ASSERT(my_thread_global_init_done);
126
127
0
#ifdef HAVE_PSI_INTERFACE
128
0
  my_init_mysys_psi_keys();
129
0
#endif
130
131
0
  my_thread_destroy_common_mutex();
132
0
  my_thread_init_common_mutex();
133
134
0
  my_thread_destroy_internal_mutex();
135
0
  my_thread_init_internal_mutex();
136
137
0
  tmp= my_thread_var;
138
0
  DBUG_ASSERT(tmp);
139
140
0
  my_thread_destory_thr_mutex(tmp);
141
0
  my_thread_init_thr_mutex(tmp);
142
0
}
143
144
/*
145
  initialize thread environment
146
147
  SYNOPSIS
148
    my_thread_global_init()
149
150
  RETURN
151
    0  ok
152
*/
153
154
my_bool my_thread_global_init(void)
155
0
{
156
157
  /* Normally this should never be called twice */
158
0
  DBUG_ASSERT(my_thread_global_init_done == 0);
159
0
  if (my_thread_global_init_done)
160
0
    return 0;
161
0
  my_thread_global_init_done= 1;
162
163
  /* Mutex used by my_thread_init() and after my_thread_destroy_mutex() */
164
0
  my_thread_init_internal_mutex();
165
166
0
  if (my_thread_init())
167
0
    return 1;
168
169
170
0
  my_thread_init_common_mutex();
171
172
0
  return 0;
173
0
}
174
175
176
/**
177
   End the mysys thread system. Called when ending the last thread
178
*/
179
180
void my_thread_global_end(void)
181
0
{
182
0
  struct timespec abstime;
183
0
  my_bool all_threads_killed= 1;
184
185
0
  set_timespec(abstime, my_thread_end_wait_time);
186
0
  mysql_mutex_lock(&THR_LOCK_threads);
187
0
  while (THR_thread_count > 0)
188
0
  {
189
0
    int error= mysql_cond_timedwait(&THR_COND_threads, &THR_LOCK_threads,
190
0
                                    &abstime);
191
0
    if (error == ETIMEDOUT || error == ETIME)
192
0
    {
193
0
#ifdef HAVE_PTHREAD_KILL
194
      /*
195
        We shouldn't give an error here, because if we don't have
196
        pthread_kill(), programs like mysqld can't ensure that all threads
197
        are killed when we enter here.
198
      */
199
0
      if (THR_thread_count)
200
0
        fprintf(stderr,
201
0
                "Error in my_thread_global_end(): %d threads didn't exit\n",
202
0
                THR_thread_count);
203
0
#endif /* HAVE_PTHREAD_KILL */
204
#ifdef SAFEMALLOC
205
      /* We know we will have memory leaks, suppress the leak report */
206
      sf_leaking_memory= 1;
207
#endif /* SAFEMALLOC */
208
0
      all_threads_killed= 0;
209
0
      break;
210
0
    }
211
0
  }
212
0
  mysql_mutex_unlock(&THR_LOCK_threads);
213
214
0
  my_thread_destroy_common_mutex();
215
216
  /*
217
    Only destroy the mutex & conditions if we don't have other threads around
218
    that could use them.
219
  */
220
0
  if (all_threads_killed)
221
0
    my_thread_destroy_internal_mutex();
222
0
  my_thread_global_init_done= 0;
223
0
}
224
225
static my_thread_id thread_id= 0;
226
227
/*
228
  Allocate thread specific memory for the thread, used by mysys and dbug
229
230
  SYNOPSIS
231
    my_thread_init()
232
233
  NOTES
234
    We can't use mutex_locks here if we are using windows as
235
    we may have compiled the program with SAFE_MUTEX, in which
236
    case the checking of mutex_locks will not work until
237
    the pthread_self thread specific variable is initialized.
238
239
   This function may called multiple times for a thread, for example
240
   if one uses my_init() followed by mysql_server_init().
241
242
  RETURN
243
    0  ok
244
    1  Fatal error; mysys/dbug functions can't be used
245
*/
246
247
my_bool my_thread_init(void)
248
0
{
249
0
  struct st_my_thread_var *tmp;
250
0
  my_bool error=0;
251
252
0
  if (!my_thread_global_init_done)
253
0
    return 1; /* cannot proceed with uninitialized library */
254
255
#ifdef EXTRA_DEBUG_THREADS
256
  fprintf(stderr,"my_thread_init(): pthread_self: %p\n", pthread_self());
257
#endif  
258
259
0
  if (my_thread_var)
260
0
  {
261
#ifdef EXTRA_DEBUG_THREADS
262
    fprintf(stderr,"my_thread_init() called more than once in thread 0x%lx\n",
263
            (long) pthread_self());
264
#endif    
265
0
    goto end;
266
0
  }
267
268
#ifdef _MSC_VER
269
  install_sigabrt_handler();
270
#endif
271
272
0
  if (!(tmp= (struct st_my_thread_var *) calloc(1, sizeof(*tmp))))
273
0
  {
274
0
    error= 1;
275
0
    goto end;
276
0
  }
277
0
  set_mysys_var(tmp);
278
0
  tmp->pthread_self= pthread_self();
279
0
  my_thread_init_thr_mutex(tmp);
280
281
0
  tmp->stack_ends_here= (char*)&tmp +
282
0
                         STACK_DIRECTION * (long)my_thread_stack_size;
283
284
0
  mysql_mutex_lock(&THR_LOCK_threads);
285
0
  tmp->id= tmp->dbug_id= ++thread_id;
286
0
  ++THR_thread_count;
287
0
  mysql_mutex_unlock(&THR_LOCK_threads);
288
0
  tmp->init= 1;
289
#ifndef DBUG_OFF
290
  /* Generate unique name for thread */
291
  (void) my_thread_name();
292
#endif
293
294
0
end:
295
0
  return error;
296
0
}
297
298
299
/*
300
  Deallocate memory used by the thread for book-keeping
301
302
  SYNOPSIS
303
    my_thread_end()
304
305
  NOTE
306
    This may be called multiple times for a thread.
307
    This happens for example when one calls 'mysql_server_init()'
308
    mysql_server_end() and then ends with a mysql_end().
309
*/
310
311
void my_thread_end(void)
312
0
{
313
0
  struct st_my_thread_var *tmp;
314
0
  tmp= my_thread_var;
315
316
#ifdef EXTRA_DEBUG_THREADS
317
  fprintf(stderr,"my_thread_end(): tmp: %p  pthread_self: %p  thread_id: %ld\n",
318
    tmp, pthread_self(), tmp ? (long) tmp->id : 0L);
319
#endif  
320
321
  /*
322
    Remove the instrumentation for this thread.
323
    This must be done before trashing st_my_thread_var,
324
    because the LF_HASH depends on it.
325
  */
326
0
  PSI_CALL_delete_current_thread();
327
328
  /*
329
    We need to disable DBUG early for this thread to ensure that the
330
    the mutex calls doesn't enable it again
331
    To this we have to both do DBUG_POP() and also reset THR_KEY_mysys
332
    as the key is used by DBUG.
333
  */
334
0
  DBUG_POP();
335
0
  set_mysys_var(NULL);
336
337
0
  if (tmp && tmp->init)
338
0
  {
339
#if !defined(DBUG_OFF)
340
    /* tmp->dbug is allocated inside DBUG library */
341
    if (tmp->dbug)
342
    {
343
      free(tmp->dbug);
344
      tmp->dbug=0;
345
    }
346
#endif
347
0
    my_thread_destory_thr_mutex(tmp);
348
349
    /*
350
      Decrement counter for number of running threads. We are using this
351
      in my_thread_global_end() to wait until all threads have called
352
      my_thread_end and thus freed all memory they have allocated in
353
      my_thread_init() and DBUG_xxxx
354
    */
355
0
    mysql_mutex_lock(&THR_LOCK_threads);
356
0
    DBUG_ASSERT(THR_thread_count != 0);
357
0
    if (--THR_thread_count == 0)
358
0
      mysql_cond_signal(&THR_COND_threads);
359
0
    mysql_mutex_unlock(&THR_LOCK_threads);
360
361
    /* Trash variable so that we can detect false accesses to my_thread_var */
362
0
    tmp->init= 2;
363
0
    free(tmp);
364
0
  }
365
0
}
366
367
static MY_THREAD_LOCAL struct st_my_thread_var *my_thread_var_;
368
struct st_my_thread_var *_my_thread_var(void)
369
0
{
370
0
  return my_thread_var_;
371
0
}
372
373
void set_mysys_var(struct st_my_thread_var *mysys_var)
374
0
{
375
0
  my_thread_var_= mysys_var;
376
0
}
377
378
379
/****************************************************************************
380
  Get name of current thread.
381
****************************************************************************/
382
383
my_thread_id my_thread_dbug_id()
384
0
{
385
  /*
386
    We need to do this test as some system thread may not yet have called
387
    my_thread_init().
388
  */
389
0
  struct st_my_thread_var *tmp= my_thread_var;
390
0
  return tmp ? tmp->dbug_id : 0;
391
0
}
392
393
#ifdef DBUG_OFF
394
const char *my_thread_name(void)
395
0
{
396
0
  return "no_name";
397
0
}
398
399
#else
400
401
const char *my_thread_name(void)
402
{
403
  char name_buff[100];
404
  struct st_my_thread_var *tmp=my_thread_var;
405
  if (!tmp->name[0])
406
  {
407
    my_thread_id id= my_thread_dbug_id();
408
    snprintf(name_buff, sizeof(name_buff), "T@%lu", (ulong) id);
409
    strmake_buf(tmp->name, name_buff);
410
  }
411
  return tmp->name;
412
}
413
414
/* Return pointer to DBUG for holding current state */
415
416
extern void **my_thread_var_dbug()
417
{
418
  struct st_my_thread_var *tmp;
419
  if (!my_thread_global_init_done)
420
    return NULL;
421
  tmp= my_thread_var;
422
  return tmp && tmp->init ? &tmp->dbug : 0;
423
}
424
#endif /* DBUG_OFF */
425
426
/* Return pointer to mutex_in_use */
427
428
safe_mutex_t **my_thread_var_mutex_in_use()
429
0
{
430
0
  struct st_my_thread_var *tmp;
431
0
  if (!my_thread_global_init_done)
432
0
    return NULL;
433
0
  tmp= my_thread_var;
434
0
  return tmp ? &tmp->mutex_in_use : 0;
435
0
}
436
437
#ifdef _WIN32
438
/*
439
  In Visual Studio 2005 and later, default SIGABRT handler will overwrite
440
  any unhandled exception filter set by the application  and will try to
441
  call JIT debugger. This is not what we want, this we calling __debugbreak
442
  to stop in debugger, if process is being debugged or to generate 
443
  EXCEPTION_BREAKPOINT and then handle_segfault will do its magic.
444
*/
445
446
#if (_MSC_VER >= 1400)
447
static void my_sigabrt_handler(int sig)
448
{
449
  __debugbreak();
450
}
451
#endif /*_MSC_VER >=1400 */
452
453
static void install_sigabrt_handler(void)
454
{
455
#if (_MSC_VER >=1400)
456
  /*abort() should not override our exception filter*/
457
  _set_abort_behavior(0,_CALL_REPORTFAULT);
458
  signal(SIGABRT,my_sigabrt_handler);
459
#endif /* _MSC_VER >=1400 */
460
}
461
#endif
462
463
#ifdef HAVE_PSI_MUTEX_INTERFACE
464
ATTRIBUTE_COLD int psi_mutex_lock(mysql_mutex_t *that,
465
                                  const char *file, uint line)
466
0
{
467
0
  PSI_mutex_locker_state state;
468
0
  PSI_mutex_locker *locker= PSI_MUTEX_CALL(start_mutex_wait)
469
0
    (&state, that->m_psi, PSI_MUTEX_LOCK, file, line);
470
# ifdef SAFE_MUTEX
471
  int result= safe_mutex_lock(&that->m_mutex, FALSE, file, line);
472
# else
473
0
  int result= pthread_mutex_lock(&that->m_mutex);
474
0
# endif
475
0
  if (locker)
476
0
    PSI_MUTEX_CALL(end_mutex_wait)(locker, result);
477
0
  return result;
478
0
}
479
480
ATTRIBUTE_COLD int psi_mutex_trylock(mysql_mutex_t *that,
481
                                     const char *file, uint line)
482
0
{
483
0
  PSI_mutex_locker_state state;
484
0
  PSI_mutex_locker *locker= PSI_MUTEX_CALL(start_mutex_wait)
485
0
    (&state, that->m_psi, PSI_MUTEX_TRYLOCK, file, line);
486
# ifdef SAFE_MUTEX
487
  int result= safe_mutex_lock(&that->m_mutex, TRUE, file, line);
488
# else
489
0
  int result= pthread_mutex_trylock(&that->m_mutex);
490
0
# endif
491
0
  if (locker)
492
0
    PSI_MUTEX_CALL(end_mutex_wait)(locker, result);
493
0
  return result;
494
0
}
495
#endif /* HAVE_PSI_MUTEX_INTERFACE */
496
497
#ifdef HAVE_PSI_RWLOCK_INTERFACE
498
ATTRIBUTE_COLD
499
int psi_rwlock_rdlock(mysql_rwlock_t *that, const char *file, uint line)
500
0
{
501
0
  PSI_rwlock_locker_state state;
502
0
  PSI_rwlock_locker *locker= PSI_RWLOCK_CALL(start_rwlock_rdwait)
503
0
    (&state, that->m_psi, PSI_RWLOCK_READLOCK, file, line);
504
0
  int result= rw_rdlock(&that->m_rwlock);
505
0
  if (locker)
506
0
    PSI_RWLOCK_CALL(end_rwlock_rdwait)(locker, result);
507
0
  return result;
508
0
}
509
510
ATTRIBUTE_COLD
511
int psi_rwlock_tryrdlock(mysql_rwlock_t *that, const char *file, uint line)
512
0
{
513
0
  PSI_rwlock_locker_state state;
514
0
  PSI_rwlock_locker *locker= PSI_RWLOCK_CALL(start_rwlock_rdwait)
515
0
    (&state, that->m_psi, PSI_RWLOCK_TRYREADLOCK, file, line);
516
0
  int result= rw_tryrdlock(&that->m_rwlock);
517
0
  if (locker)
518
0
    PSI_RWLOCK_CALL(end_rwlock_rdwait)(locker, result);
519
0
  return result;
520
0
}
521
522
ATTRIBUTE_COLD
523
int psi_rwlock_trywrlock(mysql_rwlock_t *that, const char *file, uint line)
524
0
{
525
0
  PSI_rwlock_locker_state state;
526
0
  PSI_rwlock_locker *locker= PSI_RWLOCK_CALL(start_rwlock_wrwait)
527
0
    (&state, that->m_psi, PSI_RWLOCK_TRYWRITELOCK, file, line);
528
0
  int result= rw_trywrlock(&that->m_rwlock);
529
0
  if (locker)
530
0
    PSI_RWLOCK_CALL(end_rwlock_wrwait)(locker, result);
531
0
  return result;
532
0
}
533
534
ATTRIBUTE_COLD
535
int psi_rwlock_wrlock(mysql_rwlock_t *that, const char *file, uint line)
536
0
{
537
0
  PSI_rwlock_locker_state state;
538
0
  PSI_rwlock_locker *locker= PSI_RWLOCK_CALL(start_rwlock_wrwait)
539
0
    (&state, that->m_psi, PSI_RWLOCK_WRITELOCK, file, line);
540
0
  int result= rw_wrlock(&that->m_rwlock);
541
0
  if (locker)
542
0
    PSI_RWLOCK_CALL(end_rwlock_wrwait)(locker, result);
543
0
  return result;
544
0
}
545
546
# ifndef DISABLE_MYSQL_PRLOCK_H
547
ATTRIBUTE_COLD
548
int psi_prlock_rdlock(mysql_prlock_t *that, const char *file, uint line)
549
0
{
550
0
  PSI_rwlock_locker_state state;
551
0
  PSI_rwlock_locker *locker= PSI_RWLOCK_CALL(start_rwlock_rdwait)
552
0
    (&state, that->m_psi, PSI_RWLOCK_READLOCK, file, line);
553
0
  int result= rw_pr_rdlock(&that->m_prlock);
554
0
  if (locker)
555
0
    PSI_RWLOCK_CALL(end_rwlock_rdwait)(locker, result);
556
0
  return result;
557
0
}
558
559
ATTRIBUTE_COLD
560
int psi_prlock_wrlock(mysql_prlock_t *that, const char *file, uint line)
561
0
{
562
0
  PSI_rwlock_locker_state state;
563
0
  PSI_rwlock_locker *locker= PSI_RWLOCK_CALL(start_rwlock_wrwait)
564
0
    (&state, that->m_psi, PSI_RWLOCK_WRITELOCK, file, line);
565
0
  int result= rw_pr_wrlock(&that->m_prlock);
566
0
  if (locker)
567
0
    PSI_RWLOCK_CALL(end_rwlock_wrwait)(locker, result);
568
0
  return result;
569
0
}
570
# endif /* !DISABLE_MYSQL_PRLOCK_H */
571
#endif /* HAVE_PSI_RWLOCK_INTERFACE */
572
573
#ifdef HAVE_PSI_COND_INTERFACE
574
ATTRIBUTE_COLD int psi_cond_wait(mysql_cond_t *that, mysql_mutex_t *mutex,
575
                                 const char *file, uint line)
576
0
{
577
0
  PSI_cond_locker_state state;
578
0
  PSI_cond_locker *locker= PSI_COND_CALL(start_cond_wait)
579
0
    (&state, that->m_psi, mutex->m_psi, PSI_COND_WAIT, file, line);
580
0
  int result= my_cond_wait(&that->m_cond, &mutex->m_mutex);
581
0
  if (locker)
582
0
    PSI_COND_CALL(end_cond_wait)(locker, result);
583
0
  return result;
584
0
}
585
586
ATTRIBUTE_COLD int psi_cond_timedwait(mysql_cond_t *that, mysql_mutex_t *mutex,
587
                                      const struct timespec *abstime,
588
                                      const char *file, uint line)
589
0
{
590
0
  PSI_cond_locker_state state;
591
0
  PSI_cond_locker *locker= PSI_COND_CALL(start_cond_wait)
592
0
    (&state, that->m_psi, mutex->m_psi, PSI_COND_TIMEDWAIT, file, line);
593
0
  int result= my_cond_timedwait(&that->m_cond, &mutex->m_mutex, abstime);
594
0
  if (psi_likely(locker))
595
0
    PSI_COND_CALL(end_cond_wait)(locker, result);
596
0
  return result;
597
0
}
598
#endif /* HAVE_PSI_COND_INTERFACE */