my_thr_init.c 11.3 KB
Newer Older
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi 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
5
   the Free Software Foundation; version 2 of the License.
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
6 7

   This program is distributed in the hope that it will be useful,
bk@work.mysql.com's avatar
bk@work.mysql.com committed
8
   but WITHOUT ANY WARRANTY; without even the implied warranty of
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi 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 */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
15 16

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

#include "mysys_priv.h"
#include <m_string.h>
23
#include <signal.h>
bk@work.mysql.com's avatar
bk@work.mysql.com 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,
bk@work.mysql.com's avatar
bk@work.mysql.com committed
32
	        THR_LOCK_lock,THR_LOCK_isam,THR_LOCK_myisam,THR_LOCK_heap,
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)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
38 39
pthread_mutex_t LOCK_localtime_r;
#endif
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
bk@work.mysql.com's avatar
bk@work.mysql.com committed
49

50
#ifdef NPTL_PTHREAD_EXIT_BUG                    /* see my_pthread.h */
51 52

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

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

66

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)
*/

bk@work.mysql.com's avatar
bk@work.mysql.com committed
80 81
my_bool my_thread_global_init(void)
{
82
  int pth_ret;
83 84
  thd_lib_detected= get_thread_lib();

85
  if ((pth_ret= pthread_key_create(&THR_KEY_mysys, NULL)) != 0)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
86
  {
87
    fprintf(stderr,"Can't initialize threads: error %d\n", pth_ret);
88
    return 1;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
89
  }
90 91
  
#ifdef NPTL_PTHREAD_EXIT_BUG
92 93 94
  /*
    BUG#24507: Race conditions inside current NPTL pthread_exit() 
    implementation.
95

96 97 98 99
    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.
100
  
101 102 103 104 105 106
    TODO: Remove this code when fixed versions of glibc6 are in common
    use.
  */
  {
    pthread_t       dummy_thread;
    pthread_attr_t  dummy_thread_attr;
107

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

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

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

120 121 122 123 124
    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).
125
  */
126 127 128
  pthread_mutexattr_init(&my_fast_mutexattr);
  pthread_mutexattr_settype(&my_fast_mutexattr,
                            PTHREAD_MUTEX_ADAPTIVE_NP);
129
#endif
130 131
#ifdef PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP
  /*
132
    Set mutex type to "errorcheck"
133 134 135 136 137
  */
  pthread_mutexattr_init(&my_errorcheck_mutexattr);
  pthread_mutexattr_settype(&my_errorcheck_mutexattr,
                            PTHREAD_MUTEX_ERRORCHECK);
#endif
138 139 140 141 142 143 144 145 146

  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);
147 148
  pthread_mutex_init(&THR_LOCK_threads,MY_MUTEX_INIT_FAST);
  pthread_cond_init(&THR_COND_threads, NULL);
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
149
#if defined( __WIN__) || defined(OS2)
150
  win_pthread_init();
bk@work.mysql.com's avatar
bk@work.mysql.com committed
151
#endif
152
#if !defined(HAVE_LOCALTIME_R) || !defined(HAVE_GMTIME_R)
153
  pthread_mutex_init(&LOCK_localtime_r,MY_MUTEX_INIT_SLOW);
154 155 156
#endif
#ifndef HAVE_GETHOSTBYNAME_R
  pthread_mutex_init(&LOCK_gethostbyname_r,MY_MUTEX_INIT_SLOW);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
157
#endif
158 159 160 161 162 163
  if (my_thread_init())
  {
    my_thread_global_end();			/* Clean up */
    return 1;
  }
  return 0;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
164 165
}

166

bk@work.mysql.com's avatar
bk@work.mysql.com committed
167 168
void my_thread_global_end(void)
{
169 170 171
  struct timespec abstime;
  my_bool all_threads_killed= 1;

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

190
  pthread_key_delete(THR_KEY_mysys);
191 192
#ifdef PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP
  pthread_mutexattr_destroy(&my_fast_mutexattr);
193 194 195
#endif
#ifdef PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP
  pthread_mutexattr_destroy(&my_errorcheck_mutexattr);
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
196 197 198 199 200 201 202 203 204
#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);
205 206 207 208 209
  if (all_threads_killed)
  {
    pthread_mutex_destroy(&THR_LOCK_threads);
    pthread_cond_destroy (&THR_COND_threads);
  }
210
#if !defined(HAVE_LOCALTIME_R) || !defined(HAVE_GMTIME_R)
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
211
  pthread_mutex_destroy(&LOCK_localtime_r);
212
#endif
213 214 215
#ifndef HAVE_GETHOSTBYNAME_R
  pthread_mutex_destroy(&LOCK_gethostbyname_r);
#endif
bk@work.mysql.com's avatar
bk@work.mysql.com committed
216 217
}

218
static my_thread_id thread_id= 0;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
219

220
/*
221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237
  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
238 239
*/

bk@work.mysql.com's avatar
bk@work.mysql.com committed
240 241 242
my_bool my_thread_init(void)
{
  struct st_my_thread_var *tmp;
243 244
  my_bool error=0;

245
#ifdef EXTRA_DEBUG_THREADS
246 247
  fprintf(stderr,"my_thread_init(): thread_id: 0x%lx\n",
          (ulong) pthread_self());
248
#endif  
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
249

bk@work.mysql.com's avatar
bk@work.mysql.com 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 255
    fprintf(stderr,"my_thread_init() called more than once in thread 0x%lx\n",
            (long) pthread_self());
256
#endif    
257
    goto end;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
258
  }
259
  if (!(tmp= (struct st_my_thread_var *) calloc(1, sizeof(*tmp))))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
260
  {
261 262
    error= 1;
    goto end;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
263 264 265
  }
  pthread_setspecific(THR_KEY_mysys,tmp);

266
#else /* defined(__WIN__) && !(defined(USE_TLS) */
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
267 268 269 270 271
  /*
    Skip initialization if the thread specific variable is already initialized
  */
  if (THR_KEY_mysys.id)
    goto end;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
272 273
  tmp= &THR_KEY_mysys;
#endif
hf@deer.(none)'s avatar
hf@deer.(none) committed
274
#if defined(__WIN__) && defined(EMBEDDED_LIBRARY)
275 276 277
  tmp->pthread_self= (pthread_t) getpid();
#else
  tmp->pthread_self= pthread_self();
hf@deer.(none)'s avatar
hf@deer.(none) committed
278
#endif
279
  pthread_mutex_init(&tmp->mutex,MY_MUTEX_INIT_FAST);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
280
  pthread_cond_init(&tmp->suspend, NULL);
281
  tmp->init= 1;
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
282

283 284 285 286
  pthread_mutex_lock(&THR_LOCK_threads);
  tmp->id= ++thread_id;
  ++THR_thread_count;
  pthread_mutex_unlock(&THR_LOCK_threads);
287 288 289 290 291
#ifndef DBUG_OFF
  /* Generate unique name for thread */
  (void) my_thread_name();
#endif

monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
292
end:
293
  return error;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
294 295
}

296

297 298 299 300 301 302 303 304 305 306 307 308
/*
  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().
*/

bk@work.mysql.com's avatar
bk@work.mysql.com committed
309 310
void my_thread_end(void)
{
311 312 313
  struct st_my_thread_var *tmp;
  tmp= my_pthread_getspecific(struct st_my_thread_var*,THR_KEY_mysys);

314
#ifdef EXTRA_DEBUG_THREADS
315 316
  fprintf(stderr,"my_thread_end(): tmp: 0x%lx  pthread_self: 0x%lx  thread_id: %ld\n",
	  (long) tmp, (long) pthread_self(), tmp ? (long) tmp->id : 0L);
317
#endif  
318
  if (tmp && tmp->init)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
319 320
  {
#if !defined(DBUG_OFF)
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
321
    /* tmp->dbug is allocated inside DBUG library */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
322 323 324 325 326 327
    if (tmp->dbug)
    {
      free(tmp->dbug);
      tmp->dbug=0;
    }
#endif
328
#if !defined(__bsdi__) && !defined(__OpenBSD__)
329
 /* bsdi and openbsd 3.5 dumps core here */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
330 331 332
    pthread_cond_destroy(&tmp->suspend);
#endif
    pthread_mutex_destroy(&tmp->mutex);
333
#if !defined(__WIN__) || defined(USE_TLS)
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
334
    free(tmp);
335 336
#else
    tmp->init= 0;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
337
#endif
338 339 340 341 342 343 344 345 346 347 348 349

    /*
      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);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
350
  }
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
351
  /* The following free has to be done, even if my_thread_var() is 0 */
352
#if !defined(__WIN__) || defined(USE_TLS)
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
353 354
  pthread_setspecific(THR_KEY_mysys,0);
#endif
bk@work.mysql.com's avatar
bk@work.mysql.com committed
355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371
}

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;
}

372

bk@work.mysql.com's avatar
bk@work.mysql.com committed
373
/****************************************************************************
374
  Get name of current thread.
bk@work.mysql.com's avatar
bk@work.mysql.com committed
375 376
****************************************************************************/

377
my_thread_id my_thread_dbug_id()
bk@work.mysql.com's avatar
bk@work.mysql.com committed
378 379 380 381 382
{
  return my_thread_var->id;
}

#ifdef DBUG_OFF
383
const char *my_thread_name(void)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
384 385 386 387 388 389
{
  return "no_name";
}

#else

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

404 405 406

static uint get_thread_lib(void)
{
407
#ifdef _CS_GNU_LIBPTHREAD_VERSION
408 409 410 411 412 413 414 415 416 417 418 419
  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;
}

bk@work.mysql.com's avatar
bk@work.mysql.com committed
420
#endif /* THREAD */