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 | |
38 | /** |
39 | Check for presence of Windows slim reader writer lock function. |
40 | Load function pointers. |
41 | */ |
42 | |
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 | * |
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 | 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 | |
353 | int 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 | |
367 | int 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 | |
375 | int 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 | |
390 | int 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 | |
426 | int 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 | |