thr_mutex.c 10.6 KB
Newer Older
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
1
/* Copyright (C) 2000-2003 MySQL AB
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2 3 4

   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

/* This makes a wrapper for mutex handling to make it easier to debug mutex */

18
#include <my_global.h>
19
#if defined(TARGET_OS_LINUX) && !defined (__USE_UNIX98)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
20 21 22 23
#define __USE_UNIX98			/* To get rw locks under Linux */
#endif
#if defined(THREAD) && defined(SAFE_MUTEX)
#undef SAFE_MUTEX			/* Avoid safe_mutex redefinitions */
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
24 25 26
#include "mysys_priv.h"
#include "my_static.h"
#include <m_string.h>
bk@work.mysql.com's avatar
bk@work.mysql.com committed
27 28 29

#ifndef DO_NOT_REMOVE_THREAD_WRAPPERS
/* Remove wrappers */
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
30
#undef pthread_mutex_t
bk@work.mysql.com's avatar
bk@work.mysql.com committed
31 32 33 34 35 36 37 38 39
#undef pthread_mutex_init
#undef pthread_mutex_lock
#undef pthread_mutex_unlock
#undef pthread_mutex_destroy
#undef pthread_cond_wait
#undef pthread_cond_timedwait
#ifdef HAVE_NONPOSIX_PTHREAD_MUTEX_INIT
#define pthread_mutex_init(a,b) my_pthread_mutex_init((a),(b))
#endif
40
#endif /* DO_NOT_REMOVE_THREAD_WRAPPERS */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
41

monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
42 43 44 45 46 47 48 49 50 51 52 53
static pthread_mutex_t THR_LOCK_mutex;
static ulong safe_mutex_count= 0;		/* Number of mutexes created */
#ifdef SAFE_MUTEX_DETECT_DESTROY
static struct st_safe_mutex_info_t *safe_mutex_root= NULL;
#endif

void safe_mutex_global_init(void)
{
  pthread_mutex_init(&THR_LOCK_mutex,MY_MUTEX_INIT_FAST);
}


bk@work.mysql.com's avatar
bk@work.mysql.com committed
54
int safe_mutex_init(safe_mutex_t *mp,
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
55
		    const pthread_mutexattr_t *attr __attribute__((unused)),
56 57
		    const char *file,
		    uint line)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
58 59
{
  bzero((char*) mp,sizeof(*mp));
60
  pthread_mutex_init(&mp->global,MY_MUTEX_INIT_ERRCHK);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
61
  pthread_mutex_init(&mp->mutex,attr);
62 63 64
  /* Mark that mutex is initialized */
  mp->file= file;
  mp->line= line;
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
65 66 67 68 69 70 71 72 73 74

#ifdef SAFE_MUTEX_DETECT_DESTROY
  /*
    Monitor the freeing of mutexes.  This code depends on single thread init
    and destroy
  */
  if ((mp->info= (safe_mutex_info_t *) malloc(sizeof(safe_mutex_info_t))))
  {
    struct st_safe_mutex_info_t *info =mp->info;

75
    info->init_file= file;
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
76 77 78 79 80 81 82 83 84 85 86 87 88 89
    info->init_line= line;
    info->prev= NULL;
    info->next= NULL;

    pthread_mutex_lock(&THR_LOCK_mutex);
    if ((info->next= safe_mutex_root))
      safe_mutex_root->prev= info;
    safe_mutex_root= info;
    safe_mutex_count++;
    pthread_mutex_unlock(&THR_LOCK_mutex);
  }
#else
  thread_safe_increment(safe_mutex_count, &THR_LOCK_mutex);
#endif /* SAFE_MUTEX_DETECT_DESTROY */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
90 91 92
  return 0;
}

monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
93

94
int safe_mutex_lock(safe_mutex_t *mp, my_bool try_lock, const char *file, uint line)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
95 96
{
  int error;
97 98
  if (!mp->file)
  {
99 100 101
    fprintf(stderr,
	    "safe_mutex: Trying to lock unitialized mutex at %s, line %d\n",
	    file, line);
102 103
    fflush(stderr);
    abort();
104
  }
105

bk@work.mysql.com's avatar
bk@work.mysql.com committed
106
  pthread_mutex_lock(&mp->global);
107
  if (mp->count > 0)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
108
  {
109 110 111 112 113 114 115 116 117 118 119 120 121 122
    if (try_lock)
    {
      pthread_mutex_unlock(&mp->global);
      return EBUSY;
    }
    else if (pthread_equal(pthread_self(),mp->thread))
    {
      fprintf(stderr,
              "safe_mutex: Trying to lock mutex at %s, line %d, when the"
              " mutex was already locked at %s, line %d in thread %s\n",
              file,line,mp->file, mp->line, my_thread_name());
      fflush(stderr);
      abort();
    }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
123 124
  }
  pthread_mutex_unlock(&mp->global);
125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150

  /*
    If we are imitating trylock(), we need to take special
    precautions.

    - We cannot use pthread_mutex_lock() only since another thread can
      overtake this thread and take the lock before this thread
      causing pthread_mutex_trylock() to hang. In this case, we should
      just return EBUSY. Hence, we use pthread_mutex_trylock() to be
      able to return immediately.

    - We cannot just use trylock() and continue execution below, since
      this would generate an error and abort execution if the thread
      was overtaken and trylock() returned EBUSY . In this case, we
      instead just return EBUSY, since this is the expected behaviour
      of trylock().
   */
  if (try_lock)
  {
    error= pthread_mutex_trylock(&mp->mutex);
    if (error == EBUSY)
      return error;
  }
  else
    error= pthread_mutex_lock(&mp->mutex);

bk@work.mysql.com's avatar
bk@work.mysql.com committed
151 152 153 154
  if (error || (error=pthread_mutex_lock(&mp->global)))
  {
    fprintf(stderr,"Got error %d when trying to lock mutex at %s, line %d\n",
	    error, file, line);
155
    fflush(stderr);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
156 157
    abort();
  }
158
  mp->thread= pthread_self();
bk@work.mysql.com's avatar
bk@work.mysql.com committed
159 160
  if (mp->count++)
  {
161 162
    fprintf(stderr,"safe_mutex: Error in thread libray: Got mutex at %s, \
line %d more than 1 time\n", file,line);
163
    fflush(stderr);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
164 165
    abort();
  }
166
  mp->file= file;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
167 168 169 170 171 172 173 174 175 176 177 178 179 180
  mp->line=line;
  pthread_mutex_unlock(&mp->global);
  return error;
}


int safe_mutex_unlock(safe_mutex_t *mp,const char *file, uint line)
{
  int error;
  pthread_mutex_lock(&mp->global);
  if (mp->count == 0)
  {
    fprintf(stderr,"safe_mutex: Trying to unlock mutex that wasn't locked at %s, line %d\n            Last used at %s, line: %d\n",
	    file,line,mp->file ? mp->file : "",mp->line);
181
    fflush(stderr);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
182 183 184 185 186 187
    abort();
  }
  if (!pthread_equal(pthread_self(),mp->thread))
  {
    fprintf(stderr,"safe_mutex: Trying to unlock mutex at %s, line %d  that was locked by another thread at: %s, line: %d\n",
	    file,line,mp->file,mp->line);
188
    fflush(stderr);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
189 190
    abort();
  }
191
  mp->thread= 0;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
192
  mp->count--;
193 194 195 196
#ifdef __WIN__
  pthread_mutex_unlock(&mp->mutex);
  error=0;
#else
bk@work.mysql.com's avatar
bk@work.mysql.com committed
197 198 199
  error=pthread_mutex_unlock(&mp->mutex);
  if (error)
  {
200
    fprintf(stderr,"safe_mutex: Got error: %d (%d) when trying to unlock mutex at %s, line %d\n", error, errno, file, line);
201
    fflush(stderr);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
202 203
    abort();
  }
204
#endif /* __WIN__ */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
205 206 207 208 209 210 211 212 213 214 215 216 217
  pthread_mutex_unlock(&mp->global);
  return error;
}


int safe_cond_wait(pthread_cond_t *cond, safe_mutex_t *mp, const char *file,
		   uint line)
{
  int error;
  pthread_mutex_lock(&mp->global);
  if (mp->count == 0)
  {
    fprintf(stderr,"safe_mutex: Trying to cond_wait on a unlocked mutex at %s, line %d\n",file,line);
218
    fflush(stderr);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
219 220 221 222 223 224
    abort();
  }
  if (!pthread_equal(pthread_self(),mp->thread))
  {
    fprintf(stderr,"safe_mutex: Trying to cond_wait on a mutex at %s, line %d  that was locked by another thread at: %s, line: %d\n",
	    file,line,mp->file,mp->line);
225
    fflush(stderr);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
226 227 228 229 230 231 232
    abort();
  }

  if (mp->count-- != 1)
  {
    fprintf(stderr,"safe_mutex:  Count was %d on locked mutex at %s, line %d\n",
	    mp->count+1, file, line);
233
    fflush(stderr);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
234 235 236 237 238 239 240
    abort();
  }
  pthread_mutex_unlock(&mp->global);
  error=pthread_cond_wait(cond,&mp->mutex);
  pthread_mutex_lock(&mp->global);
  if (error)
  {
241
    fprintf(stderr,"safe_mutex: Got error: %d (%d) when doing a safe_mutex_wait at %s, line %d\n", error, errno, file, line);
242
    fflush(stderr);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
243 244
    abort();
  }
245
  mp->thread=pthread_self();
bk@work.mysql.com's avatar
bk@work.mysql.com committed
246 247 248
  if (mp->count++)
  {
    fprintf(stderr,
249
	    "safe_mutex:  Count was %d in thread 0x%lx when locking mutex at %s, line %d\n",
bk@work.mysql.com's avatar
bk@work.mysql.com committed
250
	    mp->count-1, my_thread_id(), file, line);
251
    fflush(stderr);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
252 253
    abort();
  }
254
  mp->file= file;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
255 256 257 258 259 260 261 262 263 264 265 266 267 268 269
  mp->line=line;
  pthread_mutex_unlock(&mp->global);
  return error;
}


int safe_cond_timedwait(pthread_cond_t *cond, safe_mutex_t *mp,
			struct timespec *abstime,
			const char *file, uint line)
{
  int error;
  pthread_mutex_lock(&mp->global);
  if (mp->count != 1 || !pthread_equal(pthread_self(),mp->thread))
  {
    fprintf(stderr,"safe_mutex: Trying to cond_wait at %s, line %d on a not hold mutex\n",file,line);
270
    fflush(stderr);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
271 272 273 274 275 276
    abort();
  }
  mp->count--;					/* Mutex will be released */
  pthread_mutex_unlock(&mp->global);
  error=pthread_cond_timedwait(cond,&mp->mutex,abstime);
#ifdef EXTRA_DEBUG
monty@mysql.com's avatar
monty@mysql.com committed
277
  if (error && (error != EINTR && error != ETIMEDOUT && error != ETIME))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
278
  {
279
    fprintf(stderr,"safe_mutex: Got error: %d (%d) when doing a safe_mutex_timedwait at %s, line %d\n", error, errno, file, line);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
280 281 282
  }
#endif
  pthread_mutex_lock(&mp->global);
283
  mp->thread=pthread_self();
bk@work.mysql.com's avatar
bk@work.mysql.com committed
284 285 286
  if (mp->count++)
  {
    fprintf(stderr,
287
	    "safe_mutex:  Count was %d in thread 0x%lx when locking mutex at %s, line %d (error: %d (%d))\n",
288
	    mp->count-1, my_thread_id(), file, line, error, error);
289
    fflush(stderr);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
290 291
    abort();
  }
292
  mp->file= file;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
293 294 295 296 297
  mp->line=line;
  pthread_mutex_unlock(&mp->global);
  return error;
}

monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
298

bk@work.mysql.com's avatar
bk@work.mysql.com committed
299 300
int safe_mutex_destroy(safe_mutex_t *mp, const char *file, uint line)
{
301
  int error=0;
302 303 304 305 306 307 308 309
  if (!mp->file)
  {
    fprintf(stderr,
	    "safe_mutex: Trying to destroy unitialized mutex at %s, line %d\n",
	    file, line);
    fflush(stderr);
    abort();
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
310 311 312 313
  if (mp->count != 0)
  {
    fprintf(stderr,"safe_mutex: Trying to destroy a mutex that was locked at %s, line %d at %s, line %d\n",
	    mp->file,mp->line, file, line);
314
    fflush(stderr);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
315 316
    abort();
  }
317
#ifdef __WIN__ 
bk@work.mysql.com's avatar
bk@work.mysql.com committed
318
  pthread_mutex_destroy(&mp->global);
319 320
  pthread_mutex_destroy(&mp->mutex);
#else
321 322 323 324
  if (pthread_mutex_destroy(&mp->global))
    error=1;
  if (pthread_mutex_destroy(&mp->mutex))
    error=1;
325
#endif
326
  mp->file= 0;					/* Mark destroyed */
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348

#ifdef SAFE_MUTEX_DETECT_DESTROY
  if (mp->info)
  {
    struct st_safe_mutex_info_t *info= mp->info;
    pthread_mutex_lock(&THR_LOCK_mutex);

    if (info->prev)
      info->prev->next = info->next;
    else
      safe_mutex_root = info->next;
    if (info->next)
      info->next->prev = info->prev;
    safe_mutex_count--;

    pthread_mutex_unlock(&THR_LOCK_mutex);
    free(info);
    mp->info= NULL;				/* Get crash if double free */
  }
#else
  thread_safe_sub(safe_mutex_count, 1, &THR_LOCK_mutex);
#endif /* SAFE_MUTEX_DETECT_DESTROY */
349
  return error;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
350 351
}

monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392

/*
  Free global resources and check that all mutex has been destroyed

  SYNOPSIS
    safe_mutex_end()
    file		Print errors on this file

  NOTES
    We can't use DBUG_PRINT() here as we have in my_end() disabled
    DBUG handling before calling this function.

   In MySQL one may get one warning for a mutex created in my_thr_init.c
   This is ok, as this thread may not yet have been exited.
*/

void safe_mutex_end(FILE *file __attribute__((unused)))
{
  if (!safe_mutex_count)			/* safetly */
    pthread_mutex_destroy(&THR_LOCK_mutex);
#ifdef SAFE_MUTEX_DETECT_DESTROY
  if (!file)
    return;

  if (safe_mutex_count)
  {
    fprintf(file, "Warning: Not destroyed mutex: %lu\n", safe_mutex_count);
    (void) fflush(file);
  }
  {
    struct st_safe_mutex_info_t *ptr;
    for (ptr= safe_mutex_root ; ptr ; ptr= ptr->next)
    {
      fprintf(file, "\tMutex initiated at line %4u in '%s'\n",
	      ptr->init_line, ptr->init_file);
      (void) fflush(file);
    }
  }
#endif /* SAFE_MUTEX_DETECT_DESTROY */
}

393
#endif /* THREAD && SAFE_MUTEX */