MySQL 5.6.14 Source Code Document
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
thr_rwlock.c
1 /* Copyright (c) 2000, 2011, 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 as published by
5  the Free Software Foundation; version 2 of the License.
6 
7  This program is distributed in the hope that it will be useful,
8  but WITHOUT ANY WARRANTY; without even the implied warranty of
9  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10  GNU General Public License for more details.
11 
12  You should have received a copy of the GNU General Public License
13  along with this program; if not, write to the Free Software
14  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
15 
16 /* Synchronization - readers / writer thread locks */
17 
18 #include "mysys_priv.h"
19 #if defined(NEED_MY_RW_LOCK)
20 #include <errno.h>
21 
22 #ifdef _WIN32
23 
24 static BOOL have_srwlock= FALSE;
25 /* Prototypes and function pointers for windows functions */
26 typedef VOID (WINAPI* srw_func) (PSRWLOCK SRWLock);
27 typedef BOOLEAN (WINAPI* srw_bool_func) (PSRWLOCK SRWLock);
28 
29 static srw_func my_InitializeSRWLock;
30 static srw_func my_AcquireSRWLockExclusive;
31 static srw_func my_ReleaseSRWLockExclusive;
32 static srw_func my_AcquireSRWLockShared;
33 static srw_func my_ReleaseSRWLockShared;
34 
35 static srw_bool_func my_TryAcquireSRWLockExclusive;
36 static srw_bool_func my_TryAcquireSRWLockShared;
37 
43 static void check_srwlock_availability(void)
44 {
45  HMODULE module= GetModuleHandle("kernel32");
46 
47  my_InitializeSRWLock= (srw_func) GetProcAddress(module,
48  "InitializeSRWLock");
49  my_AcquireSRWLockExclusive= (srw_func) GetProcAddress(module,
50  "AcquireSRWLockExclusive");
51  my_AcquireSRWLockShared= (srw_func) GetProcAddress(module,
52  "AcquireSRWLockShared");
53  my_ReleaseSRWLockExclusive= (srw_func) GetProcAddress(module,
54  "ReleaseSRWLockExclusive");
55  my_ReleaseSRWLockShared= (srw_func) GetProcAddress(module,
56  "ReleaseSRWLockShared");
57  my_TryAcquireSRWLockExclusive= (srw_bool_func) GetProcAddress(module,
58  "TryAcquireSRWLockExclusive");
59  my_TryAcquireSRWLockShared= (srw_bool_func) GetProcAddress(module,
60  "TryAcquireSRWLockShared");
61 
62  /*
63  We currently require TryAcquireSRWLockExclusive. This API is missing on
64  Vista, this means SRWLock are only used starting with Win7.
65 
66  If "trylock" usage for rwlocks is eliminated from server codebase (it is used
67  in a single place currently, in query cache), then SRWLock can be enabled on
68  Vista too. In this case condition below needs to be changed to e.g check
69  for my_InitializeSRWLock.
70  */
71 
72  if (my_TryAcquireSRWLockExclusive)
73  have_srwlock= TRUE;
74 
75 }
76 
77 
78 static int srw_init(my_rw_lock_t *rwp)
79 {
80  my_InitializeSRWLock(&rwp->srwlock);
81  rwp->have_exclusive_srwlock = FALSE;
82  return 0;
83 }
84 
85 
86 static int srw_rdlock(my_rw_lock_t *rwp)
87 {
88  my_AcquireSRWLockShared(&rwp->srwlock);
89  return 0;
90 }
91 
92 
93 static int srw_tryrdlock(my_rw_lock_t *rwp)
94 {
95 
96  if (!my_TryAcquireSRWLockShared(&rwp->srwlock))
97  return EBUSY;
98  return 0;
99 }
100 
101 
102 static int srw_wrlock(my_rw_lock_t *rwp)
103 {
104  my_AcquireSRWLockExclusive(&rwp->srwlock);
105  rwp->have_exclusive_srwlock= TRUE;
106  return 0;
107 }
108 
109 
110 static int srw_trywrlock(my_rw_lock_t *rwp)
111 {
112  if (!my_TryAcquireSRWLockExclusive(&rwp->srwlock))
113  return EBUSY;
114  rwp->have_exclusive_srwlock= TRUE;
115  return 0;
116 }
117 
118 
119 static int srw_unlock(my_rw_lock_t *rwp)
120 {
121  if (rwp->have_exclusive_srwlock)
122  {
123  rwp->have_exclusive_srwlock= FALSE;
124  my_ReleaseSRWLockExclusive(&rwp->srwlock);
125  }
126  else
127  {
128  my_ReleaseSRWLockShared(&rwp->srwlock);
129  }
130  return 0;
131 }
132 
133 #endif /*_WIN32 */
134 
135 /*
136  Source base from Sun Microsystems SPILT, simplified for MySQL use
137  -- Joshua Chamas
138  Some cleanup and additional code by Monty
139 */
140 
141 /*
142 * Multithreaded Demo Source
143 *
144 * Copyright (C) 1995 by Sun Microsystems, Inc.
145 * All rights reserved.
146 *
147 * This file is a product of SunSoft, Inc. and is provided for
148 * unrestricted use provided that this legend is included on all
149 * media and as a part of the software program in whole or part.
150 * Users may copy, modify or distribute this file at will.
151 *
152 * THIS FILE IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING
153 * THE WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
154 * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
155 *
156 * This file is provided with no support and without any obligation on the
157 * part of SunSoft, Inc. to assist in its use, correction, modification or
158 * enhancement.
159 *
160 * SUNSOFT AND SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT
161 * TO THE INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY THIS
162 * FILE OR ANY PART THEREOF.
163 *
164 * IN NO EVENT WILL SUNSOFT OR SUN MICROSYSTEMS, INC. BE LIABLE FOR ANY
165 * LOST REVENUE OR PROFITS OR OTHER SPECIAL, INDIRECT AND CONSEQUENTIAL
166 * DAMAGES, EVEN IF THEY HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH
167 * DAMAGES.
168 *
169 * SunSoft, Inc.
170 * 2550 Garcia Avenue
171 * Mountain View, California 94043
172 */
173 
174 int my_rw_init(my_rw_lock_t *rwp)
175 {
176  pthread_condattr_t cond_attr;
177 
178 #ifdef _WIN32
179  /*
180  Once initialization is used here rather than in my_init(), in order to
181  - avoid my_init() pitfalls- (undefined order in which initialization should
182  run)
183  - be potentially useful C++ (static constructors)
184  - just to simplify the API.
185  Also, the overhead is of my_pthread_once is very small.
186  */
187  static my_pthread_once_t once_control= MY_PTHREAD_ONCE_INIT;
188  my_pthread_once(&once_control, check_srwlock_availability);
189 
190  if (have_srwlock)
191  return srw_init(rwp);
192 #endif
193 
194  pthread_mutex_init( &rwp->lock, MY_MUTEX_INIT_FAST);
195  pthread_condattr_init( &cond_attr );
196  pthread_cond_init( &rwp->readers, &cond_attr );
197  pthread_cond_init( &rwp->writers, &cond_attr );
198  pthread_condattr_destroy(&cond_attr);
199 
200  rwp->state = 0;
201  rwp->waiters = 0;
202 #ifdef SAFE_MUTEX
203  rwp->write_thread = 0;
204 #endif
205 
206  return(0);
207 }
208 
209 
210 int my_rw_destroy(my_rw_lock_t *rwp)
211 {
212 #ifdef _WIN32
213  if (have_srwlock)
214  return 0; /* no destroy function */
215 #endif
216  DBUG_ASSERT(rwp->state == 0);
217  pthread_mutex_destroy( &rwp->lock );
218  pthread_cond_destroy( &rwp->readers );
219  pthread_cond_destroy( &rwp->writers );
220  return(0);
221 }
222 
223 
224 int my_rw_rdlock(my_rw_lock_t *rwp)
225 {
226 #ifdef _WIN32
227  if (have_srwlock)
228  return srw_rdlock(rwp);
229 #endif
230 
231  pthread_mutex_lock(&rwp->lock);
232 
233  /* active or queued writers */
234  while (( rwp->state < 0 ) || rwp->waiters)
235  pthread_cond_wait( &rwp->readers, &rwp->lock);
236 
237  rwp->state++;
238  pthread_mutex_unlock(&rwp->lock);
239  return(0);
240 }
241 
242 int my_rw_tryrdlock(my_rw_lock_t *rwp)
243 {
244  int res;
245 
246 #ifdef _WIN32
247  if (have_srwlock)
248  return srw_tryrdlock(rwp);
249 #endif
250 
251  pthread_mutex_lock(&rwp->lock);
252  if ((rwp->state < 0 ) || rwp->waiters)
253  res= EBUSY; /* Can't get lock */
254  else
255  {
256  res=0;
257  rwp->state++;
258  }
259  pthread_mutex_unlock(&rwp->lock);
260  return(res);
261 }
262 
263 
264 int my_rw_wrlock(my_rw_lock_t *rwp)
265 {
266 #ifdef _WIN32
267  if (have_srwlock)
268  return srw_wrlock(rwp);
269 #endif
270 
271  pthread_mutex_lock(&rwp->lock);
272  rwp->waiters++; /* another writer queued */
273 
274  my_rw_lock_assert_not_write_owner(rwp);
275 
276  while (rwp->state)
277  pthread_cond_wait(&rwp->writers, &rwp->lock);
278  rwp->state = -1;
279  rwp->waiters--;
280 #ifdef SAFE_MUTEX
281  rwp->write_thread= pthread_self();
282 #endif
283  pthread_mutex_unlock(&rwp->lock);
284  return(0);
285 }
286 
287 
288 int my_rw_trywrlock(my_rw_lock_t *rwp)
289 {
290  int res;
291 
292 #ifdef _WIN32
293  if (have_srwlock)
294  return srw_trywrlock(rwp);
295 #endif
296 
297  pthread_mutex_lock(&rwp->lock);
298  if (rwp->state)
299  res= EBUSY; /* Can't get lock */
300  else
301  {
302  res=0;
303  rwp->state = -1;
304 #ifdef SAFE_MUTEX
305  rwp->write_thread= pthread_self();
306 #endif
307  }
308  pthread_mutex_unlock(&rwp->lock);
309  return(res);
310 }
311 
312 
313 int my_rw_unlock(my_rw_lock_t *rwp)
314 {
315 #ifdef _WIN32
316  if (have_srwlock)
317  return srw_unlock(rwp);
318 #endif
319 
320  /*
321  The DBUG api uses rw locks to protect global debug settings. Calling into
322  the DBUG api from here can cause a deadlock.
323 
324  DBUG_PRINT("rw_unlock", ("state: %d waiters: %d",
325  rwp->state, rwp->waiters));
326  */
327  pthread_mutex_lock(&rwp->lock);
328 
329  DBUG_ASSERT(rwp->state != 0);
330 
331  if (rwp->state == -1) /* writer releasing */
332  {
333  my_rw_lock_assert_write_owner(rwp);
334  rwp->state= 0; /* mark as available */
335 #ifdef SAFE_MUTEX
336  rwp->write_thread= 0;
337 #endif
338 
339  if ( rwp->waiters ) /* writers queued */
340  pthread_cond_signal( &rwp->writers );
341  else
342  pthread_cond_broadcast( &rwp->readers );
343  }
344  else
345  {
346  if ( --rwp->state == 0 && /* no more readers */
347  rwp->waiters)
348  pthread_cond_signal( &rwp->writers );
349  }
350 
351  pthread_mutex_unlock( &rwp->lock );
352  return(0);
353 }
354 
355 #endif /* defined(NEED_MY_RW_LOCK) */
356 
357 
358 int rw_pr_init(rw_pr_lock_t *rwlock)
359 {
360  pthread_mutex_init(&rwlock->lock, NULL);
361  pthread_cond_init(&rwlock->no_active_readers, NULL);
362  rwlock->active_readers= 0;
363  rwlock->writers_waiting_readers= 0;
364  rwlock->active_writer= FALSE;
365 #ifdef SAFE_MUTEX
366  rwlock->writer_thread= 0;
367 #endif
368  return 0;
369 }
370 
371 
372 int rw_pr_destroy(rw_pr_lock_t *rwlock)
373 {
374  pthread_cond_destroy(&rwlock->no_active_readers);
375  pthread_mutex_destroy(&rwlock->lock);
376  return 0;
377 }
378 
379 
380 int rw_pr_rdlock(rw_pr_lock_t *rwlock)
381 {
382  pthread_mutex_lock(&rwlock->lock);
383  /*
384  The fact that we were able to acquire 'lock' mutex means
385  that there are no active writers and we can acquire rd-lock.
386  Increment active readers counter to prevent requests for
387  wr-lock from succeeding and unlock mutex.
388  */
389  rwlock->active_readers++;
390  pthread_mutex_unlock(&rwlock->lock);
391  return 0;
392 }
393 
394 
395 int rw_pr_wrlock(rw_pr_lock_t *rwlock)
396 {
397  pthread_mutex_lock(&rwlock->lock);
398 
399  if (rwlock->active_readers != 0)
400  {
401  /* There are active readers. We have to wait until they are gone. */
402  rwlock->writers_waiting_readers++;
403 
404  while (rwlock->active_readers != 0)
405  pthread_cond_wait(&rwlock->no_active_readers, &rwlock->lock);
406 
407  rwlock->writers_waiting_readers--;
408  }
409 
410  /*
411  We own 'lock' mutex so there is no active writers.
412  Also there are no active readers.
413  This means that we can grant wr-lock.
414  Not releasing 'lock' mutex until unlock will block
415  both requests for rd and wr-locks.
416  Set 'active_writer' flag to simplify unlock.
417 
418  Thanks to the fact wr-lock/unlock in the absence of
419  contention from readers is essentially mutex lock/unlock
420  with a few simple checks make this rwlock implementation
421  wr-lock optimized.
422  */
423  rwlock->active_writer= TRUE;
424 #ifdef SAFE_MUTEX
425  rwlock->writer_thread= pthread_self();
426 #endif
427  return 0;
428 }
429 
430 
431 int rw_pr_unlock(rw_pr_lock_t *rwlock)
432 {
433  if (rwlock->active_writer)
434  {
435  /* We are unlocking wr-lock. */
436 #ifdef SAFE_MUTEX
437  rwlock->writer_thread= 0;
438 #endif
439  rwlock->active_writer= FALSE;
440  if (rwlock->writers_waiting_readers)
441  {
442  /*
443  Avoid expensive cond signal in case when there is no contention
444  or it is wr-only.
445 
446  Note that from view point of performance it would be better to
447  signal on the condition variable after unlocking mutex (as it
448  reduces number of contex switches).
449 
450  Unfortunately this would mean that such rwlock can't be safely
451  used by MDL subsystem, which relies on the fact that it is OK
452  to destroy rwlock once it is in unlocked state.
453  */
454  pthread_cond_signal(&rwlock->no_active_readers);
455  }
456  pthread_mutex_unlock(&rwlock->lock);
457  }
458  else
459  {
460  /* We are unlocking rd-lock. */
461  pthread_mutex_lock(&rwlock->lock);
462  rwlock->active_readers--;
463  if (rwlock->active_readers == 0 &&
464  rwlock->writers_waiting_readers)
465  {
466  /*
467  If we are last reader and there are waiting
468  writers wake them up.
469  */
470  pthread_cond_signal(&rwlock->no_active_readers);
471  }
472  pthread_mutex_unlock(&rwlock->lock);
473  }
474  return 0;
475 }
476 
477