1 #ifndef THR_RWLOCK_INCLUDED
2 #define THR_RWLOCK_INCLUDED
3 
4 /* Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved.
5 
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License, version 2.0,
8    as published by the Free Software Foundation.
9 
10    This program is also distributed with certain software (including
11    but not limited to OpenSSL) that is licensed under separate terms,
12    as designated in a particular file or component or in included license
13    documentation.  The authors of MySQL hereby grant you an additional
14    permission to link the program and your derivative works with the
15    separately licensed software that they have included with MySQL.
16 
17    This program is distributed in the hope that it will be useful,
18    but WITHOUT ANY WARRANTY; without even the implied warranty of
19    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20    GNU General Public License, version 2.0, for more details.
21 
22    You should have received a copy of the GNU General Public License
23    along with this program; if not, write to the Free Software
24    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA */
25 
26 /**
27   MySQL rwlock implementation.
28 
29   There are two "layers":
30   1) native_rw_*()
31        Functions that map directly down to OS primitives.
32        Windows    - SRWLock
33        Other OSes - pthread
34   2) mysql_rw*()
35        Functions that include Performance Schema instrumentation.
36        See include/mysql/psi/mysql_thread.h
37 
38   This file also includes rw_pr_*(), which implements a special
39   version of rwlocks that prefer readers. The P_S version of these
40   are mysql_prlock_*() - see include/mysql/psi/mysql_thread.h
41 */
42 
43 #include "my_global.h"
44 #include "my_thread.h"
45 #include "thr_cond.h"
46 
47 C_MODE_START
48 
49 #ifdef _WIN32
50 typedef struct st_my_rw_lock_t
51 {
52   SRWLOCK srwlock;             /* native reader writer lock */
53   BOOL have_exclusive_srwlock; /* used for unlock */
54 } native_rw_lock_t;
55 #else
56 typedef pthread_rwlock_t native_rw_lock_t;
57 #endif
58 
native_rw_init(native_rw_lock_t * rwp)59 static inline int native_rw_init(native_rw_lock_t *rwp)
60 {
61 #ifdef _WIN32
62   InitializeSRWLock(&rwp->srwlock);
63   rwp->have_exclusive_srwlock = FALSE;
64   return 0;
65 #else
66   /* pthread_rwlockattr_t is not used in MySQL */
67   return pthread_rwlock_init(rwp, NULL);
68 #endif
69 }
70 
native_rw_destroy(native_rw_lock_t * rwp)71 static inline int native_rw_destroy(native_rw_lock_t *rwp)
72 {
73 #ifdef _WIN32
74   return 0; /* no destroy function */
75 #else
76   return pthread_rwlock_destroy(rwp);
77 #endif
78 }
79 
native_rw_rdlock(native_rw_lock_t * rwp)80 static inline int native_rw_rdlock(native_rw_lock_t *rwp)
81 {
82 #ifdef _WIN32
83   AcquireSRWLockShared(&rwp->srwlock);
84   return 0;
85 #else
86   return pthread_rwlock_rdlock(rwp);
87 #endif
88 }
89 
native_rw_tryrdlock(native_rw_lock_t * rwp)90 static inline int native_rw_tryrdlock(native_rw_lock_t *rwp)
91 {
92 #ifdef _WIN32
93   if (!TryAcquireSRWLockShared(&rwp->srwlock))
94     return EBUSY;
95   return 0;
96 #else
97   return pthread_rwlock_tryrdlock(rwp);
98 #endif
99 }
100 
native_rw_wrlock(native_rw_lock_t * rwp)101 static inline int native_rw_wrlock(native_rw_lock_t *rwp)
102 {
103 #ifdef _WIN32
104   AcquireSRWLockExclusive(&rwp->srwlock);
105   rwp->have_exclusive_srwlock= TRUE;
106   return 0;
107 #else
108   return pthread_rwlock_wrlock(rwp);
109 #endif
110 }
111 
native_rw_trywrlock(native_rw_lock_t * rwp)112 static inline int native_rw_trywrlock(native_rw_lock_t *rwp)
113 {
114 #ifdef _WIN32
115   if (!TryAcquireSRWLockExclusive(&rwp->srwlock))
116     return EBUSY;
117   rwp->have_exclusive_srwlock= TRUE;
118   return 0;
119 #else
120   return pthread_rwlock_trywrlock(rwp);
121 #endif
122 }
123 
native_rw_unlock(native_rw_lock_t * rwp)124 static inline int native_rw_unlock(native_rw_lock_t *rwp)
125 {
126 #ifdef _WIN32
127   if (rwp->have_exclusive_srwlock)
128   {
129     rwp->have_exclusive_srwlock= FALSE;
130     ReleaseSRWLockExclusive(&rwp->srwlock);
131   }
132   else
133     ReleaseSRWLockShared(&rwp->srwlock);
134   return 0;
135 #else
136   return pthread_rwlock_unlock(rwp);
137 #endif
138 }
139 
140 
141 /**
142   Portable implementation of special type of read-write locks.
143 
144   These locks have two properties which are unusual for rwlocks:
145   1) They "prefer readers" in the sense that they do not allow
146      situations in which rwlock is rd-locked and there is a
147      pending rd-lock which is blocked (e.g. due to pending
148      request for wr-lock).
149      This is a stronger guarantee than one which is provided for
150      PTHREAD_RWLOCK_PREFER_READER_NP rwlocks in Linux.
151      MDL subsystem deadlock detector relies on this property for
152      its correctness.
153   2) They are optimized for uncontended wr-lock/unlock case.
154      This is scenario in which they are most oftenly used
155      within MDL subsystem. Optimizing for it gives significant
156      performance improvements in some of tests involving many
157      connections.
158 
159   Another important requirement imposed on this type of rwlock
160   by the MDL subsystem is that it should be OK to destroy rwlock
161   object which is in unlocked state even though some threads might
162   have not yet fully left unlock operation for it (of course there
163   is an external guarantee that no thread will try to lock rwlock
164   which is destroyed).
165   Putting it another way the unlock operation should not access
166   rwlock data after changing its state to unlocked.
167 
168   TODO/FIXME: We should consider alleviating this requirement as
169   it blocks us from doing certain performance optimizations.
170 */
171 
172 typedef struct st_rw_pr_lock_t {
173   /**
174     Lock which protects the structure.
175     Also held for the duration of wr-lock.
176   */
177   native_mutex_t lock;
178   /**
179     Condition variable which is used to wake-up
180     writers waiting for readers to go away.
181   */
182   native_cond_t no_active_readers;
183   /** Number of active readers. */
184   uint active_readers;
185   /** Number of writers waiting for readers to go away. */
186   uint writers_waiting_readers;
187   /** Indicates whether there is an active writer. */
188   my_bool active_writer;
189 #ifdef SAFE_MUTEX
190   /** Thread holding wr-lock (for debug purposes only). */
191   my_thread_t writer_thread;
192 #endif
193 } rw_pr_lock_t;
194 
195 extern int rw_pr_init(rw_pr_lock_t *);
196 extern int rw_pr_rdlock(rw_pr_lock_t *);
197 extern int rw_pr_wrlock(rw_pr_lock_t *);
198 extern int rw_pr_unlock(rw_pr_lock_t *);
199 extern int rw_pr_destroy(rw_pr_lock_t *);
200 
201 static inline void
rw_pr_lock_assert_write_owner(const rw_pr_lock_t * rwlock MY_ATTRIBUTE ((unused)))202 rw_pr_lock_assert_write_owner(const rw_pr_lock_t *rwlock MY_ATTRIBUTE((unused)))
203 {
204 #ifdef SAFE_MUTEX
205   DBUG_ASSERT(rwlock->active_writer &&
206               my_thread_equal(my_thread_self(), rwlock->writer_thread));
207 #endif
208 }
209 
210 static inline void
rw_pr_lock_assert_not_write_owner(const rw_pr_lock_t * rwlock MY_ATTRIBUTE ((unused)))211 rw_pr_lock_assert_not_write_owner(const rw_pr_lock_t *rwlock MY_ATTRIBUTE((unused)))
212 {
213 #ifdef SAFE_MUTEX
214   DBUG_ASSERT(!rwlock->active_writer ||
215               !my_thread_equal(my_thread_self(), rwlock->writer_thread));
216 #endif
217 }
218 
219 C_MODE_END
220 
221 #endif /* THR_RWLOCK_INCLUDED */
222