thr_mutex.c 9.68 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 5 6 7 8

   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
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.

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

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

19
#include <my_global.h>
20
#if defined(TARGET_OS_LINUX) && !defined (__USE_UNIX98)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
21 22 23 24
#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
25 26 27
#include "mysys_priv.h"
#include "my_static.h"
#include <m_string.h>
bk@work.mysql.com's avatar
bk@work.mysql.com committed
28 29 30

#ifndef DO_NOT_REMOVE_THREAD_WRAPPERS
/* Remove wrappers */
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
31
#undef pthread_mutex_t
bk@work.mysql.com's avatar
bk@work.mysql.com committed
32 33 34 35 36 37 38 39 40
#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
41
#endif /* DO_NOT_REMOVE_THREAD_WRAPPERS */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
42

monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
43 44 45 46 47 48 49 50 51 52 53 54
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
55
int safe_mutex_init(safe_mutex_t *mp,
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
56
		    const pthread_mutexattr_t *attr __attribute__((unused)),
57 58
		    const char *file,
		    uint line)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
59 60
{
  bzero((char*) mp,sizeof(*mp));
61
  pthread_mutex_init(&mp->global,MY_MUTEX_INIT_ERRCHK);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
62
  pthread_mutex_init(&mp->mutex,attr);
63 64 65
  /* Mark that mutex is initialized */
  mp->file= file;
  mp->line= line;
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
66 67 68 69 70 71 72 73 74 75

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

76
    info->init_file= file;
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
77 78 79 80 81 82 83 84 85 86 87 88 89 90
    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
91 92 93
  return 0;
}

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

bk@work.mysql.com's avatar
bk@work.mysql.com committed
95 96 97
int safe_mutex_lock(safe_mutex_t *mp,const char *file, uint line)
{
  int error;
98 99
  if (!mp->file)
  {
100 101 102
    fprintf(stderr,
	    "safe_mutex: Trying to lock unitialized mutex at %s, line %d\n",
	    file, line);
103 104
    fflush(stderr);
    abort();
105
  }
106
    
bk@work.mysql.com's avatar
bk@work.mysql.com committed
107 108 109
  pthread_mutex_lock(&mp->global);
  if (mp->count > 0 && pthread_equal(pthread_self(),mp->thread))
  {
110 111
    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());
112
    fflush(stderr);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
113 114 115 116 117 118 119 120
    abort();
  }
  pthread_mutex_unlock(&mp->global);
  error=pthread_mutex_lock(&mp->mutex);
  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);
121
    fflush(stderr);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
122 123 124 125
    abort();
  }
  if (mp->count++)
  {
126 127
    fprintf(stderr,"safe_mutex: Error in thread libray: Got mutex at %s, \
line %d more than 1 time\n", file,line);
128
    fflush(stderr);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
129 130 131
    abort();
  }
  mp->thread=pthread_self();
132
  mp->file= file;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
133 134 135 136 137 138 139 140 141 142 143 144 145 146
  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);
147
    fflush(stderr);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
148 149 150 151 152 153
    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);
154
    fflush(stderr);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
155 156 157
    abort();
  }
  mp->count--;
158 159 160 161
#ifdef __WIN__
  pthread_mutex_unlock(&mp->mutex);
  error=0;
#else
bk@work.mysql.com's avatar
bk@work.mysql.com committed
162 163 164
  error=pthread_mutex_unlock(&mp->mutex);
  if (error)
  {
165
    fprintf(stderr,"safe_mutex: Got error: %d (%d) when trying to unlock mutex at %s, line %d\n", error, errno, file, line);
166
    fflush(stderr);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
167 168
    abort();
  }
169
#endif /* __WIN__ */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
170 171 172 173 174 175 176 177 178 179 180 181 182
  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);
183
    fflush(stderr);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
184 185 186 187 188 189
    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);
190
    fflush(stderr);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
191 192 193 194 195 196 197
    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);
198
    fflush(stderr);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
199 200 201 202 203 204 205
    abort();
  }
  pthread_mutex_unlock(&mp->global);
  error=pthread_cond_wait(cond,&mp->mutex);
  pthread_mutex_lock(&mp->global);
  if (error)
  {
206
    fprintf(stderr,"safe_mutex: Got error: %d (%d) when doing a safe_mutex_wait at %s, line %d\n", error, errno, file, line);
207
    fflush(stderr);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
208 209 210 211 212
    abort();
  }
  if (mp->count++)
  {
    fprintf(stderr,
213
	    "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
214
	    mp->count-1, my_thread_id(), file, line);
215
    fflush(stderr);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
216 217 218
    abort();
  }
  mp->thread=pthread_self();
219
  mp->file= file;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
220 221 222 223 224 225 226 227 228 229 230 231 232 233 234
  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);
235
    fflush(stderr);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
236 237 238 239 240 241 242 243
    abort();
  }
  mp->count--;					/* Mutex will be released */
  pthread_mutex_unlock(&mp->global);
  error=pthread_cond_timedwait(cond,&mp->mutex,abstime);
#ifdef EXTRA_DEBUG
  if (error && (error != EINTR && error != ETIMEDOUT))
  {
244
    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
245 246 247 248 249 250
  }
#endif
  pthread_mutex_lock(&mp->global);
  if (mp->count++)
  {
    fprintf(stderr,
251
	    "safe_mutex:  Count was %d in thread 0x%lx when locking mutex at %s, line %d (error: %d (%d))\n",
252
	    mp->count-1, my_thread_id(), file, line, error, error);
253
    fflush(stderr);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
254 255 256
    abort();
  }
  mp->thread=pthread_self();
257
  mp->file= file;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
258 259 260 261 262
  mp->line=line;
  pthread_mutex_unlock(&mp->global);
  return error;
}

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

bk@work.mysql.com's avatar
bk@work.mysql.com committed
264 265
int safe_mutex_destroy(safe_mutex_t *mp, const char *file, uint line)
{
266
  int error=0;
267 268 269 270 271 272 273 274
  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
275 276 277 278
  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);
279
    fflush(stderr);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
280 281
    abort();
  }
282
#ifdef __WIN__ 
bk@work.mysql.com's avatar
bk@work.mysql.com committed
283
  pthread_mutex_destroy(&mp->global);
284 285
  pthread_mutex_destroy(&mp->mutex);
#else
286 287 288 289
  if (pthread_mutex_destroy(&mp->global))
    error=1;
  if (pthread_mutex_destroy(&mp->mutex))
    error=1;
290
#endif
291
  mp->file= 0;					/* Mark destroyed */
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313

#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 */
314
  return error;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
315 316
}

monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357

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

358
#endif /* THREAD && SAFE_MUTEX */