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
24static BOOL have_srwlock= FALSE;
25/* Prototypes and function pointers for windows functions */
26typedef VOID (WINAPI* srw_func) (PSRWLOCK SRWLock);
27typedef BOOLEAN (WINAPI* srw_bool_func) (PSRWLOCK SRWLock);
28
29static srw_func my_InitializeSRWLock;
30static srw_func my_AcquireSRWLockExclusive;
31static srw_func my_ReleaseSRWLockExclusive;
32static srw_func my_AcquireSRWLockShared;
33static srw_func my_ReleaseSRWLockShared;
34
35static srw_bool_func my_TryAcquireSRWLockExclusive;
36static srw_bool_func my_TryAcquireSRWLockShared;
37
38/**
39 Check for presence of Windows slim reader writer lock function.
40 Load function pointers.
41*/
42
43static 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
78static 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
86static int srw_rdlock(my_rw_lock_t *rwp)
87{
88 my_AcquireSRWLockShared(&rwp->srwlock);
89 return 0;
90}
91
92
93static 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
102static 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
110static 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
119static 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*
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
174int 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
210int 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
224int 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
242int 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
264int 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
288int 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
313int 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 DBUG_PRINT("rw_unlock",
321 ("state: %d waiters: %d", rwp->state, rwp->waiters));
322 pthread_mutex_lock(&rwp->lock);
323
324 DBUG_ASSERT(rwp->state != 0);
325
326 if (rwp->state == -1) /* writer releasing */
327 {
328 my_rw_lock_assert_write_owner(rwp);
329 rwp->state= 0; /* mark as available */
330#ifdef SAFE_MUTEX
331 rwp->write_thread= 0;
332#endif
333
334 if ( rwp->waiters ) /* writers queued */
335 pthread_cond_signal( &rwp->writers );
336 else
337 pthread_cond_broadcast( &rwp->readers );
338 }
339 else
340 {
341 if ( --rwp->state == 0 && /* no more readers */
342 rwp->waiters)
343 pthread_cond_signal( &rwp->writers );
344 }
345
346 pthread_mutex_unlock( &rwp->lock );
347 return(0);
348}
349
350#endif /* defined(NEED_MY_RW_LOCK) */
351
352
353int rw_pr_init(rw_pr_lock_t *rwlock)
354{
355 pthread_mutex_init(&rwlock->lock, NULL);
356 pthread_cond_init(&rwlock->no_active_readers, NULL);
357 rwlock->active_readers= 0;
358 rwlock->writers_waiting_readers= 0;
359 rwlock->active_writer= FALSE;
360#ifdef SAFE_MUTEX
361 rwlock->writer_thread= 0;
362#endif
363 return 0;
364}
365
366
367int rw_pr_destroy(rw_pr_lock_t *rwlock)
368{
369 pthread_cond_destroy(&rwlock->no_active_readers);
370 pthread_mutex_destroy(&rwlock->lock);
371 return 0;
372}
373
374
375int rw_pr_rdlock(rw_pr_lock_t *rwlock)
376{
377 pthread_mutex_lock(&rwlock->lock);
378 /*
379 The fact that we were able to acquire 'lock' mutex means
380 that there are no active writers and we can acquire rd-lock.
381 Increment active readers counter to prevent requests for
382 wr-lock from succeeding and unlock mutex.
383 */
384 rwlock->active_readers++;
385 pthread_mutex_unlock(&rwlock->lock);
386 return 0;
387}
388
389
390int rw_pr_wrlock(rw_pr_lock_t *rwlock)
391{
392 pthread_mutex_lock(&rwlock->lock);
393
394 if (rwlock->active_readers != 0)
395 {
396 /* There are active readers. We have to wait until they are gone. */
397 rwlock->writers_waiting_readers++;
398
399 while (rwlock->active_readers != 0)
400 pthread_cond_wait(&rwlock->no_active_readers, &rwlock->lock);
401
402 rwlock->writers_waiting_readers--;
403 }
404
405 /*
406 We own 'lock' mutex so there is no active writers.
407 Also there are no active readers.
408 This means that we can grant wr-lock.
409 Not releasing 'lock' mutex until unlock will block
410 both requests for rd and wr-locks.
411 Set 'active_writer' flag to simplify unlock.
412
413 Thanks to the fact wr-lock/unlock in the absence of
414 contention from readers is essentially mutex lock/unlock
415 with a few simple checks make this rwlock implementation
416 wr-lock optimized.
417 */
418 rwlock->active_writer= TRUE;
419#ifdef SAFE_MUTEX
420 rwlock->writer_thread= pthread_self();
421#endif
422 return 0;
423}
424
425
426int rw_pr_unlock(rw_pr_lock_t *rwlock)
427{
428 if (rwlock->active_writer)
429 {
430 /* We are unlocking wr-lock. */
431#ifdef SAFE_MUTEX
432 rwlock->writer_thread= 0;
433#endif
434 rwlock->active_writer= FALSE;
435 if (rwlock->writers_waiting_readers)
436 {
437 /*
438 Avoid expensive cond signal in case when there is no contention
439 or it is wr-only.
440
441 Note that from view point of performance it would be better to
442 signal on the condition variable after unlocking mutex (as it
443 reduces number of contex switches).
444
445 Unfortunately this would mean that such rwlock can't be safely
446 used by MDL subsystem, which relies on the fact that it is OK
447 to destroy rwlock once it is in unlocked state.
448 */
449 pthread_cond_signal(&rwlock->no_active_readers);
450 }
451 pthread_mutex_unlock(&rwlock->lock);
452 }
453 else
454 {
455 /* We are unlocking rd-lock. */
456 pthread_mutex_lock(&rwlock->lock);
457 rwlock->active_readers--;
458 if (rwlock->active_readers == 0 &&
459 rwlock->writers_waiting_readers)
460 {
461 /*
462 If we are last reader and there are waiting
463 writers wake them up.
464 */
465 pthread_cond_signal(&rwlock->no_active_readers);
466 }
467 pthread_mutex_unlock(&rwlock->lock);
468 }
469 return 0;
470}
471
472
473