1 /*
2    Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved.
3 
4    This program is free software; you can redistribute it and/or modify
5    it under the terms of the GNU General Public License, version 2.0,
6    as published by the Free Software Foundation.
7 
8    This program is also distributed with certain software (including
9    but not limited to OpenSSL) that is licensed under separate terms,
10    as designated in a particular file or component or in included license
11    documentation.  The authors of MySQL hereby grant you an additional
12    permission to link the program and your derivative works with the
13    separately licensed software that they have included with MySQL.
14 
15    This program is distributed in the hope that it will be useful,
16    but WITHOUT ANY WARRANTY; without even the implied warranty of
17    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18    GNU General Public License, version 2.0, for more details.
19 
20    You should have received a copy of the GNU General Public License
21    along with this program; if not, write to the Free Software
22    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
23 */
24 
25 
26 #include <ndb_global.h>
27 
28 #include <NdbMutex.h>
29 #include <NdbMem.h>
30 
31 #ifdef NDB_MUTEX_STAT
32 static FILE * statout = 0;
33 #endif
34 
NdbMutex_Create()35 NdbMutex* NdbMutex_Create()
36 {
37   return NdbMutex_CreateWithName(0);
38 }
39 
NdbMutex_CreateWithName(const char * name)40 NdbMutex* NdbMutex_CreateWithName(const char * name)
41 {
42   NdbMutex* pNdbMutex;
43   int result;
44 
45   pNdbMutex = (NdbMutex*)NdbMem_Allocate(sizeof(NdbMutex));
46 
47   if (pNdbMutex == NULL)
48     return NULL;
49 
50   result = NdbMutex_InitWithName(pNdbMutex, name);
51   if (result == 0)
52   {
53     return pNdbMutex;
54   }
55   NdbMem_Free(pNdbMutex);
56   return 0;
57 }
58 
NdbMutex_Init(NdbMutex * pNdbMutex)59 int NdbMutex_Init(NdbMutex* pNdbMutex)
60 {
61   return NdbMutex_InitWithName(pNdbMutex, 0);
62 }
63 
NdbMutex_InitWithName(NdbMutex * pNdbMutex,const char * name)64 int NdbMutex_InitWithName(NdbMutex* pNdbMutex, const char * name)
65 {
66   int result;
67   pthread_mutex_t * p;
68   DBUG_ENTER("NdbMutex_Init");
69 
70 #ifdef NDB_MUTEX_STAT
71   bzero(pNdbMutex, sizeof(NdbMutex));
72   pNdbMutex->min_lock_wait_time_ns = ~(Uint64)0;
73   pNdbMutex->min_hold_time_ns = ~(Uint64)0;
74   p = &pNdbMutex->mutex;
75   if (name == 0)
76   {
77     snprintf(pNdbMutex->name, sizeof(pNdbMutex->name), "%p",
78              pNdbMutex);
79   }
80   else
81   {
82     snprintf(pNdbMutex->name, sizeof(pNdbMutex->name), "%p:%s",
83              pNdbMutex, name);
84   }
85   if (getenv("NDB_MUTEX_STAT") != 0)
86   {
87     statout = stdout;
88   }
89 #else
90   p = pNdbMutex;
91   (void)name;
92 #endif
93 
94 #if defined(VM_TRACE) && \
95   defined(HAVE_PTHREAD_MUTEXATTR_INIT) && \
96   defined(HAVE_PTHREAD_MUTEXATTR_SETTYPE)
97 
98   {
99     pthread_mutexattr_t t;
100     pthread_mutexattr_init(&t);
101     pthread_mutexattr_settype(&t, PTHREAD_MUTEX_ERRORCHECK);
102     result = pthread_mutex_init(p, &t);
103     assert(result == 0);
104     pthread_mutexattr_destroy(&t);
105   }
106 #else
107   result = pthread_mutex_init(p, 0);
108 #endif
109   DBUG_RETURN(result);
110 }
111 
NdbMutex_Destroy(NdbMutex * p_mutex)112 int NdbMutex_Destroy(NdbMutex* p_mutex)
113 {
114   int result;
115 
116   if (p_mutex == NULL)
117     return -1;
118 
119 #ifdef NDB_MUTEX_STAT
120   result = pthread_mutex_destroy(&p_mutex->mutex);
121 #else
122   result = pthread_mutex_destroy(p_mutex);
123 #endif
124 
125   NdbMem_Free(p_mutex);
126 
127   return result;
128 }
129 
130 #ifdef NDB_MUTEX_STAT
131 static
132 inline
133 Uint64
now()134 now()
135 {
136   struct timespec ts;
137   clock_gettime(CLOCK_MONOTONIC, &ts);
138   return ts.tv_sec * 1000000000ULL + ts.tv_nsec;
139 }
140 
141 static
142 void
dumpstat(NdbMutex * p)143 dumpstat(NdbMutex* p)
144 {
145   if (statout != 0)
146   {
147     fprintf(statout,
148             "%s : "
149             " lock [ cnt: %u con: %u wait: [ min: %llu avg: %llu max: %llu ] ]"
150             " trylock [ ok: %u nok: %u ]"
151             " hold: [ min: %llu avg: %llu max: %llu ]\n",
152             p->name,
153             p->cnt_lock,
154             p->cnt_lock_contention,
155             p->min_lock_wait_time_ns,
156             p->cnt_lock_contention ?
157             p->sum_lock_wait_time_ns / p->cnt_lock_contention : 0,
158             p->max_lock_wait_time_ns,
159             p->cnt_trylock_ok,
160             p->cnt_trylock_nok,
161             p->min_hold_time_ns,
162             (p->cnt_lock + p->cnt_trylock_ok) ?
163             p->sum_hold_time_ns / (p->cnt_lock + p->cnt_trylock_ok) : 0,
164             p->max_hold_time_ns);
165   }
166   p->cnt_lock = 0;
167   p->cnt_lock_contention = 0;
168   p->cnt_trylock_ok = 0;
169   p->cnt_trylock_nok = 0;
170   p->min_lock_wait_time_ns = ~(Uint64)0;
171   p->sum_lock_wait_time_ns = 0;
172   p->max_lock_wait_time_ns = 0;
173   p->min_hold_time_ns = ~(Uint64)0;
174   p->sum_hold_time_ns = 0;
175   p->max_hold_time_ns = 0;
176 }
177 
178 #endif
179 
NdbMutex_Lock(NdbMutex * p_mutex)180 int NdbMutex_Lock(NdbMutex* p_mutex)
181 {
182   int result;
183 
184   if (p_mutex == NULL)
185     return -1;
186 
187 #ifdef NDB_MUTEX_STAT
188   {
189     Uint64 stop;
190     if ((result = pthread_mutex_trylock(&p_mutex->mutex)) == 0)
191     {
192       stop = now();
193     }
194     else
195     {
196       Uint64 start = now();
197       assert(result == EBUSY);
198       result = pthread_mutex_lock(&p_mutex->mutex);
199       stop = now();
200       p_mutex->cnt_lock_contention++;
201       Uint64 t = (stop - start);
202       p_mutex->sum_lock_wait_time_ns += t;
203       if (t < p_mutex->min_lock_wait_time_ns)
204         p_mutex->min_lock_wait_time_ns = t;
205       if (t > p_mutex->max_lock_wait_time_ns)
206         p_mutex->max_lock_wait_time_ns = t;
207     }
208     p_mutex->cnt_lock++;
209     p_mutex->lock_start_time_ns = stop;
210   }
211 #else
212   result = pthread_mutex_lock(p_mutex);
213 #endif
214   assert(result == 0);
215 
216   return result;
217 }
218 
219 
NdbMutex_Unlock(NdbMutex * p_mutex)220 int NdbMutex_Unlock(NdbMutex* p_mutex)
221 {
222   int result;
223 
224   if (p_mutex == NULL)
225     return -1;
226 
227 #ifdef NDB_MUTEX_STAT
228   {
229     Uint64 stop = now() - p_mutex->lock_start_time_ns;
230     p_mutex->sum_hold_time_ns += stop;
231     if (stop < p_mutex->min_hold_time_ns)
232       p_mutex->min_hold_time_ns = stop;
233     if (stop > p_mutex->max_hold_time_ns)
234       p_mutex->max_hold_time_ns = stop;
235     result = pthread_mutex_unlock(&p_mutex->mutex);
236     if (((p_mutex->sum_hold_time_ns + p_mutex->sum_lock_wait_time_ns)
237          >= 3*1000000000ULL) ||
238         p_mutex->cnt_lock >= 16384 ||
239         p_mutex->cnt_trylock_ok >= 16384)
240     {
241       dumpstat(p_mutex);
242     }
243   }
244 #else
245   result = pthread_mutex_unlock(p_mutex);
246 #endif
247   assert(result == 0);
248 
249   return result;
250 }
251 
252 
NdbMutex_Trylock(NdbMutex * p_mutex)253 int NdbMutex_Trylock(NdbMutex* p_mutex)
254 {
255   int result;
256 
257   if (p_mutex == NULL)
258     return -1;
259 
260 #ifdef NDB_MUTEX_STAT
261   result = pthread_mutex_trylock(&p_mutex->mutex);
262   if (result == 0)
263   {
264     p_mutex->cnt_trylock_ok++;
265     p_mutex->lock_start_time_ns = now();
266   }
267   else
268   {
269     __sync_fetch_and_add(&p_mutex->cnt_trylock_nok, 1);
270   }
271 #else
272   result = pthread_mutex_trylock(p_mutex);
273 #endif
274   assert(result == 0 || result == EBUSY);
275 
276   return result;
277 }
278 
279