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