1 /* Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved.
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, version 2.0,
5 as published by the Free Software Foundation.
6
7 This program is also distributed with certain software (including
8 but not limited to OpenSSL) that is licensed under separate terms,
9 as designated in a particular file or component or in included license
10 documentation. The authors of MySQL hereby grant you an additional
11 permission to link the program and your derivative works with the
12 separately licensed software that they have included with MySQL.
13
14 Without limiting anything contained in the foregoing, this file,
15 which is part of C Driver for MySQL (Connector/C), is also subject to the
16 Universal FOSS Exception, version 1.0, a copy of which can be found at
17 http://oss.oracle.com/licenses/universal-foss-exception.
18
19 This program is distributed in the hope that it will be useful,
20 but WITHOUT ANY WARRANTY; without even the implied warranty of
21 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 GNU General Public License, version 2.0, for more details.
23
24 You should have received a copy of the GNU General Public License
25 along with this program; if not, write to the Free Software
26 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
27
28 /* This makes a wrapper for mutex handling to make it easier to debug mutex */
29
30 #include <my_global.h>
31 #if defined(TARGET_OS_LINUX) && !defined (__USE_UNIX98)
32 #define __USE_UNIX98 /* To get rw locks under Linux */
33 #endif
34 #if defined(SAFE_MUTEX)
35 #undef SAFE_MUTEX /* Avoid safe_mutex redefinitions */
36 #include "mysys_priv.h"
37 #include "my_static.h"
38 #include <m_string.h>
39
40 #ifndef DO_NOT_REMOVE_THREAD_WRAPPERS
41 /* Remove wrappers */
42 #undef pthread_mutex_t
43 #undef pthread_mutex_init
44 #undef pthread_mutex_lock
45 #undef pthread_mutex_unlock
46 #undef pthread_mutex_destroy
47 #undef pthread_cond_wait
48 #undef pthread_cond_timedwait
49 #ifdef HAVE_NONPOSIX_PTHREAD_MUTEX_INIT
50 #define pthread_mutex_init(a,b) my_pthread_mutex_init((a),(b))
51 #endif
52 #endif /* DO_NOT_REMOVE_THREAD_WRAPPERS */
53
54 /* Not instrumented */
55 static pthread_mutex_t THR_LOCK_mutex;
56 static ulong safe_mutex_count= 0; /* Number of mutexes created */
57 #ifdef SAFE_MUTEX_DETECT_DESTROY
58 static struct st_safe_mutex_info_t *safe_mutex_root= NULL;
59 #endif
60
safe_mutex_global_init(void)61 void safe_mutex_global_init(void)
62 {
63 pthread_mutex_init(&THR_LOCK_mutex,MY_MUTEX_INIT_FAST);
64 }
65
66
safe_mutex_init(safe_mutex_t * mp,const pthread_mutexattr_t * attr MY_ATTRIBUTE ((unused)),const char * file,uint line)67 int safe_mutex_init(safe_mutex_t *mp,
68 const pthread_mutexattr_t *attr MY_ATTRIBUTE((unused)),
69 const char *file,
70 uint line)
71 {
72 memset(mp, 0, sizeof(*mp));
73 pthread_mutex_init(&mp->global,MY_MUTEX_INIT_ERRCHK);
74 pthread_mutex_init(&mp->mutex,attr);
75 /* Mark that mutex is initialized */
76 mp->file= file;
77 mp->line= line;
78
79 #ifdef SAFE_MUTEX_DETECT_DESTROY
80 /*
81 Monitor the freeing of mutexes. This code depends on single thread init
82 and destroy
83 */
84 if ((mp->info= (safe_mutex_info_t *) malloc(sizeof(safe_mutex_info_t))))
85 {
86 struct st_safe_mutex_info_t *info =mp->info;
87
88 info->init_file= file;
89 info->init_line= line;
90 info->prev= NULL;
91 info->next= NULL;
92
93 pthread_mutex_lock(&THR_LOCK_mutex);
94 if ((info->next= safe_mutex_root))
95 safe_mutex_root->prev= info;
96 safe_mutex_root= info;
97 safe_mutex_count++;
98 pthread_mutex_unlock(&THR_LOCK_mutex);
99 }
100 #else
101 pthread_mutex_lock(&THR_LOCK_mutex);
102 safe_mutex_count++;
103 pthread_mutex_unlock(&THR_LOCK_mutex);
104 #endif /* SAFE_MUTEX_DETECT_DESTROY */
105 return 0;
106 }
107
108
safe_mutex_lock(safe_mutex_t * mp,my_bool try_lock,const char * file,uint line)109 int safe_mutex_lock(safe_mutex_t *mp, my_bool try_lock, const char *file, uint line)
110 {
111 int error;
112 if (!mp->file)
113 {
114 fprintf(stderr,
115 "safe_mutex: Trying to lock unitialized mutex at %s, line %d\n",
116 file, line);
117 fflush(stderr);
118 abort();
119 }
120
121 pthread_mutex_lock(&mp->global);
122 if (mp->count > 0)
123 {
124 if (try_lock)
125 {
126 pthread_mutex_unlock(&mp->global);
127 return EBUSY;
128 }
129 else if (pthread_equal(pthread_self(),mp->thread))
130 {
131 fprintf(stderr,
132 "safe_mutex: Trying to lock mutex at %s, line %d, when the"
133 " mutex was already locked at %s, line %d in thread %s\n",
134 file,line,mp->file, mp->line, my_thread_name());
135 fflush(stderr);
136 abort();
137 }
138 }
139 pthread_mutex_unlock(&mp->global);
140
141 /*
142 If we are imitating trylock(), we need to take special
143 precautions.
144
145 - We cannot use pthread_mutex_lock() only since another thread can
146 overtake this thread and take the lock before this thread
147 causing pthread_mutex_trylock() to hang. In this case, we should
148 just return EBUSY. Hence, we use pthread_mutex_trylock() to be
149 able to return immediately.
150
151 - We cannot just use trylock() and continue execution below, since
152 this would generate an error and abort execution if the thread
153 was overtaken and trylock() returned EBUSY . In this case, we
154 instead just return EBUSY, since this is the expected behaviour
155 of trylock().
156 */
157 if (try_lock)
158 {
159 error= pthread_mutex_trylock(&mp->mutex);
160 if (error == EBUSY)
161 return error;
162 }
163 else
164 error= pthread_mutex_lock(&mp->mutex);
165
166 if (error || (error=pthread_mutex_lock(&mp->global)))
167 {
168 fprintf(stderr,"Got error %d when trying to lock mutex at %s, line %d\n",
169 error, file, line);
170 fflush(stderr);
171 abort();
172 }
173 mp->thread= pthread_self();
174 if (mp->count++)
175 {
176 fprintf(stderr,"safe_mutex: Error in thread libray: Got mutex at %s, \
177 line %d more than 1 time\n", file,line);
178 fflush(stderr);
179 abort();
180 }
181 mp->file= file;
182 mp->line=line;
183 pthread_mutex_unlock(&mp->global);
184 return error;
185 }
186
187
safe_mutex_unlock(safe_mutex_t * mp,const char * file,uint line)188 int safe_mutex_unlock(safe_mutex_t *mp,const char *file, uint line)
189 {
190 int error;
191 pthread_mutex_lock(&mp->global);
192 if (mp->count == 0)
193 {
194 fprintf(stderr,"safe_mutex: Trying to unlock mutex that wasn't locked at %s, line %d\n Last used at %s, line: %d\n",
195 file,line,mp->file ? mp->file : "",mp->line);
196 fflush(stderr);
197 abort();
198 }
199 if (!pthread_equal(pthread_self(),mp->thread))
200 {
201 fprintf(stderr,"safe_mutex: Trying to unlock mutex at %s, line %d that was locked by another thread at: %s, line: %d\n",
202 file,line,mp->file,mp->line);
203 fflush(stderr);
204 abort();
205 }
206 mp->thread= 0;
207 mp->count--;
208 #ifdef __WIN__
209 pthread_mutex_unlock(&mp->mutex);
210 error=0;
211 #else
212 error=pthread_mutex_unlock(&mp->mutex);
213 if (error)
214 {
215 fprintf(stderr,"safe_mutex: Got error: %d (%d) when trying to unlock mutex at %s, line %d\n", error, errno, file, line);
216 fflush(stderr);
217 abort();
218 }
219 #endif /* __WIN__ */
220 pthread_mutex_unlock(&mp->global);
221 return error;
222 }
223
224
safe_cond_wait(pthread_cond_t * cond,safe_mutex_t * mp,const char * file,uint line)225 int safe_cond_wait(pthread_cond_t *cond, safe_mutex_t *mp, const char *file,
226 uint line)
227 {
228 int error;
229 pthread_mutex_lock(&mp->global);
230 if (mp->count == 0)
231 {
232 fprintf(stderr,"safe_mutex: Trying to cond_wait on a unlocked mutex at %s, line %d\n",file,line);
233 fflush(stderr);
234 abort();
235 }
236 if (!pthread_equal(pthread_self(),mp->thread))
237 {
238 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",
239 file,line,mp->file,mp->line);
240 fflush(stderr);
241 abort();
242 }
243
244 if (mp->count-- != 1)
245 {
246 fprintf(stderr,"safe_mutex: Count was %d on locked mutex at %s, line %d\n",
247 mp->count+1, file, line);
248 fflush(stderr);
249 abort();
250 }
251 pthread_mutex_unlock(&mp->global);
252 error=pthread_cond_wait(cond,&mp->mutex);
253 pthread_mutex_lock(&mp->global);
254 if (error)
255 {
256 fprintf(stderr,"safe_mutex: Got error: %d (%d) when doing a safe_mutex_wait at %s, line %d\n", error, errno, file, line);
257 fflush(stderr);
258 abort();
259 }
260 mp->thread=pthread_self();
261 if (mp->count++)
262 {
263 fprintf(stderr,
264 "safe_mutex: Count was %d in thread 0x%lx when locking mutex at %s, line %d\n",
265 mp->count-1, my_thread_dbug_id(), file, line);
266 fflush(stderr);
267 abort();
268 }
269 mp->file= file;
270 mp->line=line;
271 pthread_mutex_unlock(&mp->global);
272 return error;
273 }
274
275
safe_cond_timedwait(pthread_cond_t * cond,safe_mutex_t * mp,const struct timespec * abstime,const char * file,uint line)276 int safe_cond_timedwait(pthread_cond_t *cond, safe_mutex_t *mp,
277 const struct timespec *abstime,
278 const char *file, uint line)
279 {
280 int error;
281 pthread_mutex_lock(&mp->global);
282 if (mp->count != 1 || !pthread_equal(pthread_self(),mp->thread))
283 {
284 fprintf(stderr,"safe_mutex: Trying to cond_wait at %s, line %d on a not hold mutex\n",file,line);
285 fflush(stderr);
286 abort();
287 }
288 mp->count--; /* Mutex will be released */
289 pthread_mutex_unlock(&mp->global);
290 error=pthread_cond_timedwait(cond,&mp->mutex,abstime);
291 #ifdef EXTRA_DEBUG
292 if (error && (error != EINTR && error != ETIMEDOUT && error != ETIME))
293 {
294 fprintf(stderr,"safe_mutex: Got error: %d (%d) when doing a safe_mutex_timedwait at %s, line %d\n", error, errno, file, line);
295 }
296 #endif
297 pthread_mutex_lock(&mp->global);
298 mp->thread=pthread_self();
299 if (mp->count++)
300 {
301 fprintf(stderr,
302 "safe_mutex: Count was %d in thread 0x%lx when locking mutex at %s, line %d (error: %d (%d))\n",
303 mp->count-1, my_thread_dbug_id(), file, line, error, error);
304 fflush(stderr);
305 abort();
306 }
307 mp->file= file;
308 mp->line=line;
309 pthread_mutex_unlock(&mp->global);
310 return error;
311 }
312
313
safe_mutex_destroy(safe_mutex_t * mp,const char * file,uint line)314 int safe_mutex_destroy(safe_mutex_t *mp, const char *file, uint line)
315 {
316 int error=0;
317 if (!mp->file)
318 {
319 fprintf(stderr,
320 "safe_mutex: Trying to destroy unitialized mutex at %s, line %d\n",
321 file, line);
322 fflush(stderr);
323 abort();
324 }
325 if (mp->count != 0)
326 {
327 fprintf(stderr,"safe_mutex: Trying to destroy a mutex that was locked at %s, line %d at %s, line %d\n",
328 mp->file,mp->line, file, line);
329 fflush(stderr);
330 abort();
331 }
332 #ifdef __WIN__
333 pthread_mutex_destroy(&mp->global);
334 pthread_mutex_destroy(&mp->mutex);
335 #else
336 if (pthread_mutex_destroy(&mp->global))
337 error=1;
338 if (pthread_mutex_destroy(&mp->mutex))
339 error=1;
340 #endif
341 mp->file= 0; /* Mark destroyed */
342
343 #ifdef SAFE_MUTEX_DETECT_DESTROY
344 if (mp->info)
345 {
346 struct st_safe_mutex_info_t *info= mp->info;
347 pthread_mutex_lock(&THR_LOCK_mutex);
348
349 if (info->prev)
350 info->prev->next = info->next;
351 else
352 safe_mutex_root = info->next;
353 if (info->next)
354 info->next->prev = info->prev;
355 safe_mutex_count--;
356
357 pthread_mutex_unlock(&THR_LOCK_mutex);
358 free(info);
359 mp->info= NULL; /* Get crash if double free */
360 }
361 #else
362 pthread_mutex_lock(&THR_LOCK_mutex);
363 safe_mutex_count--;
364 pthread_mutex_unlock(&THR_LOCK_mutex);
365 #endif /* SAFE_MUTEX_DETECT_DESTROY */
366 return error;
367 }
368
369
370 /*
371 Free global resources and check that all mutex has been destroyed
372
373 SYNOPSIS
374 safe_mutex_end()
375 file Print errors on this file
376
377 NOTES
378 We can't use DBUG_PRINT() here as we have in my_end() disabled
379 DBUG handling before calling this function.
380
381 In MySQL one may get one warning for a mutex created in my_thr_init.c
382 This is ok, as this thread may not yet have been exited.
383 */
384
safe_mutex_end(FILE * file MY_ATTRIBUTE ((unused)))385 void safe_mutex_end(FILE *file MY_ATTRIBUTE((unused)))
386 {
387 if (!safe_mutex_count) /* safetly */
388 pthread_mutex_destroy(&THR_LOCK_mutex);
389 #ifdef SAFE_MUTEX_DETECT_DESTROY
390 if (!file)
391 return;
392
393 if (safe_mutex_count)
394 {
395 fprintf(file, "Warning: Not destroyed mutex: %lu\n", safe_mutex_count);
396 (void) fflush(file);
397 }
398 {
399 struct st_safe_mutex_info_t *ptr;
400 for (ptr= safe_mutex_root ; ptr ; ptr= ptr->next)
401 {
402 fprintf(file, "\tMutex initiated at line %4u in '%s'\n",
403 ptr->init_line, ptr->init_file);
404 (void) fflush(file);
405 }
406 }
407 #endif /* SAFE_MUTEX_DETECT_DESTROY */
408 }
409
410 #endif /* SAFE_MUTEX */
411
412 #if defined(MY_PTHREAD_FASTMUTEX) && !defined(SAFE_MUTEX)
413
414 #include "mysys_priv.h"
415 #include "my_static.h"
416 #include <m_string.h>
417
418 #include <m_ctype.h>
419 #include <hash.h>
420 #include <myisampack.h>
421 #include <mysys_err.h>
422 #include <my_sys.h>
423
424 #undef pthread_mutex_t
425 #undef pthread_mutex_init
426 #undef pthread_mutex_lock
427 #undef pthread_mutex_trylock
428 #undef pthread_mutex_unlock
429 #undef pthread_mutex_destroy
430 #undef pthread_cond_wait
431 #undef pthread_cond_timedwait
432
mutex_delay(ulong delayloops)433 ulong mutex_delay(ulong delayloops)
434 {
435 ulong i;
436 volatile ulong j;
437
438 j = 0;
439
440 for (i = 0; i < delayloops * 50; i++)
441 j += i;
442
443 return(j);
444 }
445
446 #define MY_PTHREAD_FASTMUTEX_SPINS 8
447 #define MY_PTHREAD_FASTMUTEX_DELAY 4
448
449 static int cpu_count= 0;
450
my_pthread_fastmutex_init(my_pthread_fastmutex_t * mp,const pthread_mutexattr_t * attr)451 int my_pthread_fastmutex_init(my_pthread_fastmutex_t *mp,
452 const pthread_mutexattr_t *attr)
453 {
454 if ((cpu_count > 1) && (attr == MY_MUTEX_INIT_FAST))
455 mp->spins= MY_PTHREAD_FASTMUTEX_SPINS;
456 else
457 mp->spins= 0;
458 mp->rng_state= 1;
459 return pthread_mutex_init(&mp->mutex, attr);
460 }
461
462 /**
463 Park-Miller random number generator. A simple linear congruential
464 generator that operates in multiplicative group of integers modulo n.
465
466 x_{k+1} = (x_k g) mod n
467
468 Popular pair of parameters: n = 2^32 − 5 = 4294967291 and g = 279470273.
469 The period of the generator is about 2^31.
470 Largest value that can be returned: 2147483646 (RAND_MAX)
471
472 Reference:
473
474 S. K. Park and K. W. Miller
475 "Random number generators: good ones are hard to find"
476 Commun. ACM, October 1988, Volume 31, No 10, pages 1192-1201.
477 */
478
park_rng(my_pthread_fastmutex_t * mp)479 static double park_rng(my_pthread_fastmutex_t *mp)
480 {
481 mp->rng_state= ((my_ulonglong)mp->rng_state * 279470273U) % 4294967291U;
482 return (mp->rng_state / 2147483647.0);
483 }
484
my_pthread_fastmutex_lock(my_pthread_fastmutex_t * mp)485 int my_pthread_fastmutex_lock(my_pthread_fastmutex_t *mp)
486 {
487 int res;
488 uint i;
489 uint maxdelay= MY_PTHREAD_FASTMUTEX_DELAY;
490
491 for (i= 0; i < mp->spins; i++)
492 {
493 res= pthread_mutex_trylock(&mp->mutex);
494
495 if (res == 0)
496 return 0;
497
498 if (res != EBUSY)
499 return res;
500
501 mutex_delay(maxdelay);
502 maxdelay += park_rng(mp) * MY_PTHREAD_FASTMUTEX_DELAY + 1;
503 }
504 return pthread_mutex_lock(&mp->mutex);
505 }
506
507
fastmutex_global_init(void)508 void fastmutex_global_init(void)
509 {
510 #ifdef _SC_NPROCESSORS_CONF
511 cpu_count= sysconf(_SC_NPROCESSORS_CONF);
512 #endif
513 }
514
515 #endif /* defined(MY_PTHREAD_FASTMUTEX) && !defined(SAFE_MUTEX) */
516