~drizzle-trunk/drizzle/development

1 by brian
clean slate
1
/* Copyright (C) 2000 MySQL AB
2
3
   This program is free software; you can redistribute it and/or modify
4
   it under the terms of the GNU General Public License as published by
5
   the Free Software Foundation; version 2 of the License.
6
7
   This program is distributed in the hope that it will be useful,
8
   but WITHOUT ANY WARRANTY; without even the implied warranty of
9
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10
   GNU General Public License for more details.
11
12
   You should have received a copy of the GNU General Public License
13
   along with this program; if not, write to the Free Software
14
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
15
16
/*
17
  Functions to handle initializating and allocationg of all mysys & debug
18
  thread variables.
19
*/
20
21
#include "mysys_priv.h"
22
#include <m_string.h>
23
#include <signal.h>
24
25
#ifdef THREAD
26
#ifdef USE_TLS
27
pthread_key(struct st_my_thread_var*, THR_KEY_mysys);
28
#else
29
pthread_key(struct st_my_thread_var, THR_KEY_mysys);
30
#endif /* USE_TLS */
31
pthread_mutex_t THR_LOCK_malloc,THR_LOCK_open,
32
	        THR_LOCK_lock,THR_LOCK_isam,THR_LOCK_myisam,THR_LOCK_heap,
33
                THR_LOCK_net, THR_LOCK_charset, THR_LOCK_threads, THR_LOCK_time;
34
pthread_cond_t  THR_COND_threads;
35
uint            THR_thread_count= 0;
36
uint 		my_thread_end_wait_time= 5;
37
#if !defined(HAVE_LOCALTIME_R) || !defined(HAVE_GMTIME_R)
38
pthread_mutex_t LOCK_localtime_r;
39
#endif
40
#ifndef HAVE_GETHOSTBYNAME_R
41
pthread_mutex_t LOCK_gethostbyname_r;
42
#endif
43
#ifdef PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP
44
pthread_mutexattr_t my_fast_mutexattr;
45
#endif
46
#ifdef PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP
47
pthread_mutexattr_t my_errorcheck_mutexattr;
48
#endif
49
50
#ifdef TARGET_OS_LINUX
51
52
/*
53
  Dummy thread spawned in my_thread_global_init() below to avoid
54
  race conditions in NPTL pthread_exit code.
55
*/
56
57
static pthread_handler_t
58
nptl_pthread_exit_hack_handler(void *arg __attribute((unused)))
59
{
60
  /* Do nothing! */
61
  pthread_exit(0);
62
  return 0;
63
}
64
65
#endif /* TARGET_OS_LINUX */
66
67
68
static uint get_thread_lib(void);
69
70
/*
71
  initialize thread environment
72
73
  SYNOPSIS
74
    my_thread_global_init()
75
76
  RETURN
77
    0  ok
78
    1  error (Couldn't create THR_KEY_mysys)
79
*/
80
81
my_bool my_thread_global_init(void)
82
{
83
  int pth_ret;
84
  thd_lib_detected= get_thread_lib();
85
86
  if ((pth_ret= pthread_key_create(&THR_KEY_mysys, NULL)) != 0)
87
  {
88
    fprintf(stderr,"Can't initialize threads: error %d\n", pth_ret);
89
    return 1;
90
  }
91
92
#ifdef TARGET_OS_LINUX
93
  /*
94
    BUG#24507: Race conditions inside current NPTL pthread_exit()
95
    implementation.
96
97
    To avoid a possible segmentation fault during concurrent
98
    executions of pthread_exit(), a dummy thread is spawned which
99
    initializes internal variables of pthread lib. See bug description
100
    for a full explanation.
101
102
    TODO: Remove this code when fixed versions of glibc6 are in common
103
    use.
104
  */
105
  if (thd_lib_detected == THD_LIB_NPTL)
106
  {
107
    pthread_t       dummy_thread;
108
    pthread_attr_t  dummy_thread_attr;
109
110
    pthread_attr_init(&dummy_thread_attr);
111
    pthread_attr_setdetachstate(&dummy_thread_attr, PTHREAD_CREATE_DETACHED);
112
113
    pthread_create(&dummy_thread,&dummy_thread_attr,
114
                   nptl_pthread_exit_hack_handler, NULL);
115
  }
116
#endif /* TARGET_OS_LINUX */
117
118
#ifdef PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP
119
  /*
120
    Set mutex type to "fast" a.k.a "adaptive"
121
122
    In this case the thread may steal the mutex from some other thread
123
    that is waiting for the same mutex.  This will save us some
124
    context switches but may cause a thread to 'starve forever' while
125
    waiting for the mutex (not likely if the code within the mutex is
126
    short).
127
  */
128
  pthread_mutexattr_init(&my_fast_mutexattr);
129
  pthread_mutexattr_settype(&my_fast_mutexattr,
130
                            PTHREAD_MUTEX_ADAPTIVE_NP);
131
#endif
132
#ifdef PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP
133
  /*
134
    Set mutex type to "errorcheck"
135
  */
136
  pthread_mutexattr_init(&my_errorcheck_mutexattr);
137
  pthread_mutexattr_settype(&my_errorcheck_mutexattr,
138
                            PTHREAD_MUTEX_ERRORCHECK);
139
#endif
140
141
  pthread_mutex_init(&THR_LOCK_malloc,MY_MUTEX_INIT_FAST);
142
  pthread_mutex_init(&THR_LOCK_open,MY_MUTEX_INIT_FAST);
143
  pthread_mutex_init(&THR_LOCK_lock,MY_MUTEX_INIT_FAST);
144
  pthread_mutex_init(&THR_LOCK_isam,MY_MUTEX_INIT_SLOW);
145
  pthread_mutex_init(&THR_LOCK_myisam,MY_MUTEX_INIT_SLOW);
146
  pthread_mutex_init(&THR_LOCK_heap,MY_MUTEX_INIT_FAST);
147
  pthread_mutex_init(&THR_LOCK_net,MY_MUTEX_INIT_FAST);
148
  pthread_mutex_init(&THR_LOCK_charset,MY_MUTEX_INIT_FAST);
149
  pthread_mutex_init(&THR_LOCK_threads,MY_MUTEX_INIT_FAST);
150
  pthread_mutex_init(&THR_LOCK_time,MY_MUTEX_INIT_FAST);
151
  pthread_cond_init(&THR_COND_threads, NULL);
152
#if !defined(HAVE_LOCALTIME_R) || !defined(HAVE_GMTIME_R)
153
  pthread_mutex_init(&LOCK_localtime_r,MY_MUTEX_INIT_SLOW);
154
#endif
155
#ifndef HAVE_GETHOSTBYNAME_R
156
  pthread_mutex_init(&LOCK_gethostbyname_r,MY_MUTEX_INIT_SLOW);
157
#endif
158
  if (my_thread_init())
159
  {
160
    my_thread_global_end();			/* Clean up */
161
    return 1;
162
  }
163
  return 0;
164
}
165
166
167
void my_thread_global_end(void)
168
{
169
  struct timespec abstime;
170
  my_bool all_threads_killed= 1;
171
172
  set_timespec(abstime, my_thread_end_wait_time);
173
  pthread_mutex_lock(&THR_LOCK_threads);
174
  while (THR_thread_count > 0)
175
  {
176
    int error= pthread_cond_timedwait(&THR_COND_threads, &THR_LOCK_threads,
177
                                      &abstime);
178
    if (error == ETIMEDOUT || error == ETIME)
179
    {
180
#ifdef HAVE_PTHREAD_KILL
181
      /*
182
        We shouldn't give an error here, because if we don't have
183
        pthread_kill(), programs like mysqld can't ensure that all threads
184
        are killed when we enter here.
185
      */
186
      if (THR_thread_count)
187
        fprintf(stderr,
188
                "Error in my_thread_global_end(): %d threads didn't exit\n",
189
                THR_thread_count);
190
#endif
191
      all_threads_killed= 0;
192
      break;
193
    }
194
  }
195
  pthread_mutex_unlock(&THR_LOCK_threads);
196
197
  pthread_key_delete(THR_KEY_mysys);
198
#ifdef PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP
199
  pthread_mutexattr_destroy(&my_fast_mutexattr);
200
#endif
201
#ifdef PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP
202
  pthread_mutexattr_destroy(&my_errorcheck_mutexattr);
203
#endif
204
  pthread_mutex_destroy(&THR_LOCK_malloc);
205
  pthread_mutex_destroy(&THR_LOCK_open);
206
  pthread_mutex_destroy(&THR_LOCK_lock);
207
  pthread_mutex_destroy(&THR_LOCK_isam);
208
  pthread_mutex_destroy(&THR_LOCK_myisam);
209
  pthread_mutex_destroy(&THR_LOCK_heap);
210
  pthread_mutex_destroy(&THR_LOCK_net);
211
  pthread_mutex_destroy(&THR_LOCK_time);
212
  pthread_mutex_destroy(&THR_LOCK_charset);
213
  if (all_threads_killed)
214
  {
215
    pthread_mutex_destroy(&THR_LOCK_threads);
216
    pthread_cond_destroy(&THR_COND_threads);
217
  }
218
#if !defined(HAVE_LOCALTIME_R) || !defined(HAVE_GMTIME_R)
219
  pthread_mutex_destroy(&LOCK_localtime_r);
220
#endif
221
#ifndef HAVE_GETHOSTBYNAME_R
222
  pthread_mutex_destroy(&LOCK_gethostbyname_r);
223
#endif
224
}
225
226
static my_thread_id thread_id= 0;
227
228
/*
229
  Allocate thread specific memory for the thread, used by mysys and dbug
230
231
  SYNOPSIS
232
    my_thread_init()
233
234
  NOTES
235
    We can't use mutex_locks here if we are using windows as
236
    we may have compiled the program with SAFE_MUTEX, in which
237
    case the checking of mutex_locks will not work until
238
    the pthread_self thread specific variable is initialized.
239
240
   This function may called multiple times for a thread, for example
241
   if one uses my_init() followed by mysql_server_init().
242
243
  RETURN
244
    0  ok
245
    1  Fatal error; mysys/dbug functions can't be used
246
*/
247
248
my_bool my_thread_init(void)
249
{
250
  struct st_my_thread_var *tmp;
251
  my_bool error=0;
252
253
#ifdef EXTRA_DEBUG_THREADS
254
  fprintf(stderr,"my_thread_init(): thread_id: 0x%lx\n",
255
          (ulong) pthread_self());
256
#endif  
257
258
  if (my_pthread_getspecific(struct st_my_thread_var *,THR_KEY_mysys))
259
  {
260
#ifdef EXTRA_DEBUG_THREADS
261
    fprintf(stderr,"my_thread_init() called more than once in thread 0x%lx\n",
262
            (long) pthread_self());
263
#endif    
264
    goto end;
265
  }
266
  if (!(tmp= (struct st_my_thread_var *) calloc(1, sizeof(*tmp))))
267
  {
268
    error= 1;
269
    goto end;
270
  }
271
  pthread_setspecific(THR_KEY_mysys,tmp);
272
  tmp->pthread_self= pthread_self();
273
  pthread_mutex_init(&tmp->mutex,MY_MUTEX_INIT_FAST);
274
  pthread_cond_init(&tmp->suspend, NULL);
275
  tmp->init= 1;
276
277
  pthread_mutex_lock(&THR_LOCK_threads);
278
  tmp->id= ++thread_id;
279
  ++THR_thread_count;
280
  pthread_mutex_unlock(&THR_LOCK_threads);
281
#ifndef DBUG_OFF
282
  /* Generate unique name for thread */
283
  (void) my_thread_name();
284
#endif
285
286
end:
287
  return error;
288
}
289
290
291
/*
292
  Deallocate memory used by the thread for book-keeping
293
294
  SYNOPSIS
295
    my_thread_end()
296
297
  NOTE
298
    This may be called multiple times for a thread.
299
    This happens for example when one calls 'mysql_server_init()'
300
    mysql_server_end() and then ends with a mysql_end().
301
*/
302
303
void my_thread_end(void)
304
{
305
  struct st_my_thread_var *tmp;
306
  tmp= my_pthread_getspecific(struct st_my_thread_var*,THR_KEY_mysys);
307
308
#ifdef EXTRA_DEBUG_THREADS
309
  fprintf(stderr,"my_thread_end(): tmp: 0x%lx  pthread_self: 0x%lx  thread_id: %ld\n",
310
	  (long) tmp, (long) pthread_self(), tmp ? (long) tmp->id : 0L);
311
#endif  
312
  if (tmp && tmp->init)
313
  {
314
#if !defined(DBUG_OFF)
315
    /* tmp->dbug is allocated inside DBUG library */
316
    if (tmp->dbug)
317
    {
318
      DBUG_POP();
319
      free(tmp->dbug);
320
      tmp->dbug=0;
321
    }
322
#endif
323
#if !defined(__bsdi__) && !defined(__OpenBSD__)
324
 /* bsdi and openbsd 3.5 dumps core here */
325
    pthread_cond_destroy(&tmp->suspend);
326
#endif
327
    pthread_mutex_destroy(&tmp->mutex);
328
#if defined(USE_TLS)
329
    free(tmp);
330
#else
331
    tmp->init= 0;
332
#endif
333
334
    /*
335
      Decrement counter for number of running threads. We are using this
336
      in my_thread_global_end() to wait until all threads have called
337
      my_thread_end and thus freed all memory they have allocated in
338
      my_thread_init() and DBUG_xxxx
339
    */
340
    pthread_mutex_lock(&THR_LOCK_threads);
341
    DBUG_ASSERT(THR_thread_count != 0);
342
    if (--THR_thread_count == 0)
343
      pthread_cond_signal(&THR_COND_threads);
344
   pthread_mutex_unlock(&THR_LOCK_threads);
345
  }
346
  /* The following free has to be done, even if my_thread_var() is 0 */
347
#if defined(USE_TLS)
348
  pthread_setspecific(THR_KEY_mysys,0);
349
#endif
350
}
351
352
struct st_my_thread_var *_my_thread_var(void)
353
{
354
  struct st_my_thread_var *tmp=
355
    my_pthread_getspecific(struct st_my_thread_var*,THR_KEY_mysys);
356
#if defined(USE_TLS)
357
  /* This can only happen in a .DLL */
358
  if (!tmp)
359
  {
360
    my_thread_init();
361
    tmp=my_pthread_getspecific(struct st_my_thread_var*,THR_KEY_mysys);
362
  }
363
#endif
364
  return tmp;
365
}
366
367
368
/****************************************************************************
369
  Get name of current thread.
370
****************************************************************************/
371
372
my_thread_id my_thread_dbug_id()
373
{
374
  return my_thread_var->id;
375
}
376
377
#ifdef DBUG_OFF
378
const char *my_thread_name(void)
379
{
380
  return "no_name";
381
}
382
383
#else
384
385
const char *my_thread_name(void)
386
{
387
  char name_buff[100];
388
  struct st_my_thread_var *tmp=my_thread_var;
389
  if (!tmp->name[0])
390
  {
391
    my_thread_id id= my_thread_dbug_id();
392
    sprintf(name_buff,"T@%lu", (ulong) id);
393
    strmake(tmp->name,name_buff,THREAD_NAME_SIZE);
394
  }
395
  return tmp->name;
396
}
397
#endif /* DBUG_OFF */
398
399
400
static uint get_thread_lib(void)
401
{
402
#ifdef _CS_GNU_LIBPTHREAD_VERSION
403
  char buff[64];
404
    
405
  confstr(_CS_GNU_LIBPTHREAD_VERSION, buff, sizeof(buff));
406
407
  if (!strncasecmp(buff, "NPTL", 4))
408
    return THD_LIB_NPTL;
409
  if (!strncasecmp(buff, "linuxthreads", 12))
410
    return THD_LIB_LT;
411
#endif
412
  return THD_LIB_OTHER;
413
}
414
415
#endif /* THREAD */