my_thr_init.c 11.3 KB
Newer Older
unknown's avatar
unknown committed
1 2 3 4
/* Copyright (C) 2000 MySQL AB

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
unknown's avatar
unknown committed
5
   the Free Software Foundation; version 2 of the License.
unknown's avatar
unknown committed
6 7

   This program is distributed in the hope that it will be useful,
unknown's avatar
unknown committed
8
   but WITHOUT ANY WARRANTY; without even the implied warranty of
unknown's avatar
unknown committed
9 10 11 12 13 14
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
unknown's avatar
unknown committed
15 16

/*
17 18
  Functions to handle initializating and allocationg of all mysys & debug
  thread variables.
unknown's avatar
unknown committed
19 20 21 22
*/

#include "mysys_priv.h"
#include <m_string.h>
23
#include <signal.h>
unknown's avatar
unknown committed
24 25 26 27 28 29

#ifdef THREAD
#ifdef USE_TLS
pthread_key(struct st_my_thread_var*, THR_KEY_mysys);
#else
pthread_key(struct st_my_thread_var, THR_KEY_mysys);
30
#endif /* USE_TLS */
31
pthread_mutex_t THR_LOCK_malloc,THR_LOCK_open,
unknown's avatar
unknown committed
32
	        THR_LOCK_lock,THR_LOCK_isam,THR_LOCK_myisam,THR_LOCK_heap,
unknown's avatar
unknown committed
33 34 35 36
                THR_LOCK_net, THR_LOCK_charset, THR_LOCK_threads;
pthread_cond_t  THR_COND_threads;
uint            THR_thread_count= 0;
uint 		my_thread_end_wait_time= 5;
37
#if !defined(HAVE_LOCALTIME_R) || !defined(HAVE_GMTIME_R)
unknown's avatar
unknown committed
38 39
pthread_mutex_t LOCK_localtime_r;
#endif
unknown's avatar
unknown committed
40 41 42
#ifndef HAVE_GETHOSTBYNAME_R
pthread_mutex_t LOCK_gethostbyname_r;
#endif
43 44 45
#ifdef PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP
pthread_mutexattr_t my_fast_mutexattr;
#endif
46 47 48
#ifdef PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP
pthread_mutexattr_t my_errorcheck_mutexattr;
#endif
unknown's avatar
unknown committed
49

50 51 52 53 54 55 56
#ifdef NPTL_PTHREAD_EXIT_BUG /* see my_pthread.h */

/*
 Dummy thread spawned in my_thread_global_init() below to avoid
 race conditions in NPTL pthread_exit code.
*/

57 58
static pthread_handler_t
nptl_pthread_exit_hack_handler(void *arg __attribute__((unused)))
59 60 61 62 63 64 65 66
{
  /* Do nothing! */
  pthread_exit(0);
  return 0;
}

#endif

67 68
static uint get_thread_lib(void);

69 70 71 72 73 74 75 76 77 78 79
/*
  initialize thread environment

  SYNOPSIS
    my_thread_global_init()

  RETURN
    0  ok
    1  error (Couldn't create THR_KEY_mysys)
*/

unknown's avatar
unknown committed
80 81
my_bool my_thread_global_init(void)
{
82
  int pth_ret;
83 84
  thd_lib_detected= get_thread_lib();

unknown's avatar
unknown committed
85
  if ((pth_ret= pthread_key_create(&THR_KEY_mysys, NULL)) != 0)
unknown's avatar
unknown committed
86
  {
unknown's avatar
unknown committed
87
    fprintf(stderr,"Can't initialize threads: error %d\n", pth_ret);
88
    return 1;
unknown's avatar
unknown committed
89
  }
90

91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107
#ifdef NPTL_PTHREAD_EXIT_BUG
  /*
    BUG#24507: Race conditions inside current NPTL pthread_exit()
    implementation.

    To avoid a possible segmentation fault during concurrent
    executions of pthread_exit(), a dummy thread is spawned which
    initializes internal variables of pthread lib. See bug description
    for a full explanation.

    TODO: Remove this code when fixed versions of glibc6 are in common
    use.
  */
  if (thd_lib_detected == THD_LIB_NPTL)
  {
    pthread_t       dummy_thread;
    pthread_attr_t  dummy_thread_attr;
108

109 110
    pthread_attr_init(&dummy_thread_attr);
    pthread_attr_setdetachstate(&dummy_thread_attr, PTHREAD_CREATE_DETACHED);
111

112 113 114
    pthread_create(&dummy_thread,&dummy_thread_attr,
                   nptl_pthread_exit_hack_handler, NULL);
  }
115
#endif
116

117
#ifdef PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP
118
  /*
119 120
    Set mutex type to "fast" a.k.a "adaptive"

unknown's avatar
unknown committed
121 122 123 124 125
    In this case the thread may steal the mutex from some other thread
    that is waiting for the same mutex.  This will save us some
    context switches but may cause a thread to 'starve forever' while
    waiting for the mutex (not likely if the code within the mutex is
    short).
126
  */
127 128 129
  pthread_mutexattr_init(&my_fast_mutexattr);
  pthread_mutexattr_settype(&my_fast_mutexattr,
                            PTHREAD_MUTEX_ADAPTIVE_NP);
130
#endif
131 132
#ifdef PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP
  /*
unknown's avatar
unknown committed
133
    Set mutex type to "errorcheck"
134 135 136 137 138
  */
  pthread_mutexattr_init(&my_errorcheck_mutexattr);
  pthread_mutexattr_settype(&my_errorcheck_mutexattr,
                            PTHREAD_MUTEX_ERRORCHECK);
#endif
139 140 141 142 143 144 145 146 147

  pthread_mutex_init(&THR_LOCK_malloc,MY_MUTEX_INIT_FAST);
  pthread_mutex_init(&THR_LOCK_open,MY_MUTEX_INIT_FAST);
  pthread_mutex_init(&THR_LOCK_lock,MY_MUTEX_INIT_FAST);
  pthread_mutex_init(&THR_LOCK_isam,MY_MUTEX_INIT_SLOW);
  pthread_mutex_init(&THR_LOCK_myisam,MY_MUTEX_INIT_SLOW);
  pthread_mutex_init(&THR_LOCK_heap,MY_MUTEX_INIT_FAST);
  pthread_mutex_init(&THR_LOCK_net,MY_MUTEX_INIT_FAST);
  pthread_mutex_init(&THR_LOCK_charset,MY_MUTEX_INIT_FAST);
unknown's avatar
unknown committed
148
  pthread_mutex_init(&THR_LOCK_threads,MY_MUTEX_INIT_FAST);
unknown's avatar
unknown committed
149
  pthread_cond_init(&THR_COND_threads, NULL);
unknown's avatar
unknown committed
150
#if defined( __WIN__) || defined(OS2)
unknown's avatar
unknown committed
151
  win_pthread_init();
unknown's avatar
unknown committed
152
#endif
153
#if !defined(HAVE_LOCALTIME_R) || !defined(HAVE_GMTIME_R)
154
  pthread_mutex_init(&LOCK_localtime_r,MY_MUTEX_INIT_SLOW);
unknown's avatar
unknown committed
155 156 157
#endif
#ifndef HAVE_GETHOSTBYNAME_R
  pthread_mutex_init(&LOCK_gethostbyname_r,MY_MUTEX_INIT_SLOW);
unknown's avatar
unknown committed
158
#endif
159 160 161 162 163 164
  if (my_thread_init())
  {
    my_thread_global_end();			/* Clean up */
    return 1;
  }
  return 0;
unknown's avatar
unknown committed
165 166
}

167

unknown's avatar
unknown committed
168 169
void my_thread_global_end(void)
{
unknown's avatar
unknown committed
170 171 172
  struct timespec abstime;
  my_bool all_threads_killed= 1;

173
  set_timespec(abstime, my_thread_end_wait_time);
unknown's avatar
unknown committed
174
  pthread_mutex_lock(&THR_LOCK_threads);
175
  while (THR_thread_count > 0)
unknown's avatar
unknown committed
176 177 178 179 180 181
  {
    int error= pthread_cond_timedwait(&THR_COND_threads, &THR_LOCK_threads,
                                      &abstime);
    if (error == ETIMEDOUT || error == ETIME)
    {
      if (THR_thread_count)
unknown's avatar
unknown committed
182 183
        fprintf(stderr,
                "Error in my_thread_global_end(): %d threads didn't exit\n",
unknown's avatar
unknown committed
184 185
                THR_thread_count);
      all_threads_killed= 0;
186
      break;
unknown's avatar
unknown committed
187 188 189 190
    }
  }
  pthread_mutex_unlock(&THR_LOCK_threads);

191
  pthread_key_delete(THR_KEY_mysys);
192 193
#ifdef PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP
  pthread_mutexattr_destroy(&my_fast_mutexattr);
194 195 196
#endif
#ifdef PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP
  pthread_mutexattr_destroy(&my_errorcheck_mutexattr);
unknown's avatar
unknown committed
197 198 199 200 201 202 203 204 205
#endif
  pthread_mutex_destroy(&THR_LOCK_malloc);
  pthread_mutex_destroy(&THR_LOCK_open);
  pthread_mutex_destroy(&THR_LOCK_lock);
  pthread_mutex_destroy(&THR_LOCK_isam);
  pthread_mutex_destroy(&THR_LOCK_myisam);
  pthread_mutex_destroy(&THR_LOCK_heap);
  pthread_mutex_destroy(&THR_LOCK_net);
  pthread_mutex_destroy(&THR_LOCK_charset);
unknown's avatar
unknown committed
206 207 208 209 210
  if (all_threads_killed)
  {
    pthread_mutex_destroy(&THR_LOCK_threads);
    pthread_cond_destroy (&THR_COND_threads);
  }
211
#if !defined(HAVE_LOCALTIME_R) || !defined(HAVE_GMTIME_R)
unknown's avatar
unknown committed
212
  pthread_mutex_destroy(&LOCK_localtime_r);
213
#endif
unknown's avatar
unknown committed
214 215 216
#ifndef HAVE_GETHOSTBYNAME_R
  pthread_mutex_destroy(&LOCK_gethostbyname_r);
#endif
unknown's avatar
unknown committed
217 218 219 220
}

static long thread_id=0;

unknown's avatar
unknown committed
221
/*
unknown's avatar
unknown committed
222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238
  Allocate thread specific memory for the thread, used by mysys and dbug

  SYNOPSIS
    my_thread_init()

  NOTES
    We can't use mutex_locks here if we are using windows as
    we may have compiled the program with SAFE_MUTEX, in which
    case the checking of mutex_locks will not work until
    the pthread_self thread specific variable is initialized.

   This function may called multiple times for a thread, for example
   if one uses my_init() followed by mysql_server_init().

  RETURN
    0  ok
    1  Fatal error; mysys/dbug functions can't be used
unknown's avatar
unknown committed
239 240
*/

unknown's avatar
unknown committed
241 242 243
my_bool my_thread_init(void)
{
  struct st_my_thread_var *tmp;
244 245
  my_bool error=0;

unknown's avatar
unknown committed
246
#ifdef EXTRA_DEBUG_THREADS
247 248
  fprintf(stderr,"my_thread_init(): thread_id=%ld\n",pthread_self());
#endif  
unknown's avatar
unknown committed
249

unknown's avatar
unknown committed
250 251 252
#if !defined(__WIN__) || defined(USE_TLS)
  if (my_pthread_getspecific(struct st_my_thread_var *,THR_KEY_mysys))
  {
253
#ifdef EXTRA_DEBUG_THREADS
254
    fprintf(stderr,"my_thread_init() called more than once in thread %ld\n",
255
	        pthread_self());
256
#endif    
257
    goto end;
unknown's avatar
unknown committed
258
  }
259
  if (!(tmp= (struct st_my_thread_var *) calloc(1, sizeof(*tmp))))
unknown's avatar
unknown committed
260
  {
261 262
    error= 1;
    goto end;
unknown's avatar
unknown committed
263 264 265
  }
  pthread_setspecific(THR_KEY_mysys,tmp);

unknown's avatar
unknown committed
266
#else /* defined(__WIN__) && !(defined(USE_TLS) */
unknown's avatar
unknown committed
267 268 269 270 271
  /*
    Skip initialization if the thread specific variable is already initialized
  */
  if (THR_KEY_mysys.id)
    goto end;
unknown's avatar
unknown committed
272 273
  tmp= &THR_KEY_mysys;
#endif
unknown's avatar
unknown committed
274 275 276
#if defined(__WIN__) && defined(EMBEDDED_LIBRARY)
  tmp->thread_self= (pthread_t)getpid();
#endif
277
  pthread_mutex_init(&tmp->mutex,MY_MUTEX_INIT_FAST);
unknown's avatar
unknown committed
278
  pthread_cond_init(&tmp->suspend, NULL);
279
  tmp->init= 1;
unknown's avatar
unknown committed
280

unknown's avatar
unknown committed
281 282 283 284
  pthread_mutex_lock(&THR_LOCK_threads);
  tmp->id= ++thread_id;
  ++THR_thread_count;
  pthread_mutex_unlock(&THR_LOCK_threads);
unknown's avatar
unknown committed
285
end:
286
  return error;
unknown's avatar
unknown committed
287 288
}

289

unknown's avatar
unknown committed
290 291 292 293 294 295 296 297 298 299 300 301
/*
  Deallocate memory used by the thread for book-keeping

  SYNOPSIS
    my_thread_end()

  NOTE
    This may be called multiple times for a thread.
    This happens for example when one calls 'mysql_server_init()'
    mysql_server_end() and then ends with a mysql_end().
*/

unknown's avatar
unknown committed
302 303
void my_thread_end(void)
{
304 305 306
  struct st_my_thread_var *tmp;
  tmp= my_pthread_getspecific(struct st_my_thread_var*,THR_KEY_mysys);

unknown's avatar
unknown committed
307
#ifdef EXTRA_DEBUG_THREADS
308 309
  fprintf(stderr,"my_thread_end(): tmp: 0x%lx  thread_id=%ld\n",
	  (long) tmp, pthread_self());
310
#endif  
311
  if (tmp && tmp->init)
unknown's avatar
unknown committed
312 313
  {
#if !defined(DBUG_OFF)
unknown's avatar
unknown committed
314
    /* tmp->dbug is allocated inside DBUG library */
unknown's avatar
unknown committed
315 316 317 318 319 320
    if (tmp->dbug)
    {
      free(tmp->dbug);
      tmp->dbug=0;
    }
#endif
321 322
#if !defined(__bsdi__) && !defined(__OpenBSD__) || defined(HAVE_mit_thread)
 /* bsdi and openbsd 3.5 dumps core here */
unknown's avatar
unknown committed
323 324 325
    pthread_cond_destroy(&tmp->suspend);
#endif
    pthread_mutex_destroy(&tmp->mutex);
unknown's avatar
unknown committed
326
#if (!defined(__WIN__) && !defined(OS2)) || defined(USE_TLS)
unknown's avatar
unknown committed
327
    free(tmp);
328 329
#else
    tmp->init= 0;
unknown's avatar
unknown committed
330
#endif
331 332 333 334 335 336 337 338 339 340 341 342

    /*
      Decrement counter for number of running threads. We are using this
      in my_thread_global_end() to wait until all threads have called
      my_thread_end and thus freed all memory they have allocated in
      my_thread_init() and DBUG_xxxx
    */
    pthread_mutex_lock(&THR_LOCK_threads);
    DBUG_ASSERT(THR_thread_count != 0);
    if (--THR_thread_count == 0)
      pthread_cond_signal(&THR_COND_threads);
   pthread_mutex_unlock(&THR_LOCK_threads);
unknown's avatar
unknown committed
343
  }
unknown's avatar
unknown committed
344 345 346 347
  /* The following free has to be done, even if my_thread_var() is 0 */
#if (!defined(__WIN__) && !defined(OS2)) || defined(USE_TLS)
  pthread_setspecific(THR_KEY_mysys,0);
#endif
unknown's avatar
unknown committed
348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364
}

struct st_my_thread_var *_my_thread_var(void)
{
  struct st_my_thread_var *tmp=
    my_pthread_getspecific(struct st_my_thread_var*,THR_KEY_mysys);
#if defined(USE_TLS)
  /* This can only happen in a .DLL */
  if (!tmp)
  {
    my_thread_init();
    tmp=my_pthread_getspecific(struct st_my_thread_var*,THR_KEY_mysys);
  }
#endif
  return tmp;
}

365

unknown's avatar
unknown committed
366
/****************************************************************************
367
  Get name of current thread.
unknown's avatar
unknown committed
368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383
****************************************************************************/

#define UNKNOWN_THREAD -1

long my_thread_id()
{
#if defined(HAVE_PTHREAD_GETSEQUENCE_NP)
  return pthread_getsequence_np(pthread_self());
#elif (defined(__sun) || defined(__sgi) || defined(__linux__)) && !defined(HAVE_mit_thread)
  return pthread_self();
#else
  return my_thread_var->id;
#endif
}

#ifdef DBUG_OFF
384
const char *my_thread_name(void)
unknown's avatar
unknown committed
385 386 387 388 389 390
{
  return "no_name";
}

#else

391
const char *my_thread_name(void)
unknown's avatar
unknown committed
392 393 394 395 396 397 398 399 400 401 402 403 404
{
  char name_buff[100];
  struct st_my_thread_var *tmp=my_thread_var;
  if (!tmp->name[0])
  {
    long id=my_thread_id();
    sprintf(name_buff,"T@%ld", id);
    strmake(tmp->name,name_buff,THREAD_NAME_SIZE);
  }
  return tmp->name;
}
#endif /* DBUG_OFF */

405 406 407

static uint get_thread_lib(void)
{
408
#ifdef _CS_GNU_LIBPTHREAD_VERSION
409 410 411 412 413 414 415 416 417 418 419 420
  char buff[64];
    
  confstr(_CS_GNU_LIBPTHREAD_VERSION, buff, sizeof(buff));

  if (!strncasecmp(buff, "NPTL", 4))
    return THD_LIB_NPTL;
  if (!strncasecmp(buff, "linuxthreads", 12))
    return THD_LIB_LT;
#endif
  return THD_LIB_OTHER;
}

unknown's avatar
unknown committed
421
#endif /* THREAD */