File: | thread/rwlock.c |
Warning: | line 405, column 9 Value stored to 'busy' is never read |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | /* |
2 | * The contents of this file are subject to the Mozilla Public License |
3 | * Version 1.1 (the "License"); you may not use this file except in |
4 | * compliance with the License. You may obtain a copy of the License at |
5 | * http://mozilla.org/. |
6 | * |
7 | * Software distributed under the License is distributed on an "AS IS" |
8 | * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See |
9 | * the License for the specific language governing rights and limitations |
10 | * under the License. |
11 | * |
12 | * The Original Code is AOLserver Code and related documentation |
13 | * distributed by AOL. |
14 | * |
15 | * The Initial Developer of the Original Code is America Online, |
16 | * Inc. Portions created by AOL are Copyright (C) 1999 America Online, |
17 | * Inc. All Rights Reserved. |
18 | * |
19 | * Alternatively, the contents of this file may be used under the terms |
20 | * of the GNU General Public License (the "GPL"), in which case the |
21 | * provisions of GPL are applicable instead of those above. If you wish |
22 | * to allow use of your version of this file only under the terms of the |
23 | * GPL and not to allow others to use your version of this file under the |
24 | * License, indicate your decision by deleting the provisions above and |
25 | * replace them with the notice and other provisions required by the GPL. |
26 | * If you do not delete the provisions above, a recipient may use your |
27 | * version of this file under either the License or the GPL. |
28 | */ |
29 | |
30 | /* |
31 | * rwlock.c -- |
32 | * |
33 | * Routines for read/write locks. Read/write locks differ from a mutex |
34 | * in that multiple threads can acquire the read lock until a single |
35 | * thread acquires a write lock. This code is adapted from that in |
36 | * Steven's Unix Network Programming, Volume 3. |
37 | * |
38 | * Note: Read/write locks are not often a good idea. The reason |
39 | * is, like critical sections, the number of actual lock operations |
40 | * is doubled which makes them more expensive to use. Cases where the |
41 | * overhead are justified are then often subject to read locks being |
42 | * held longer than writer threads can wait and/or writer threads holding |
43 | * the lock so long that many reader threads back up. In these cases, |
44 | * specific reference counting techniques (e.g., the management of |
45 | * the Req structures in op.c) normally work better. |
46 | */ |
47 | |
48 | #include "thread.h" |
49 | |
50 | /* |
51 | * This file contains two different implementations of read/write locks: |
52 | * |
53 | * a) a POSIX pthread based implementation (when HAVE_PTHREAD is defined) |
54 | * b) a "hand-written implementation based on a mutex and condition variables, |
55 | * which is defined since ages in NaviServer. |
56 | * |
57 | * Variant (b) is used typically on WINDOWS, unless one integrtion the windows |
58 | * pthread library. |
59 | * |
60 | */ |
61 | |
62 | |
63 | #ifdef HAVE_PTHREAD1 |
64 | |
65 | /* ---------------------------------------------------------------------- |
66 | * POSIX rwlock |
67 | *---------------------------------------------------------------------- |
68 | */ |
69 | #include <pthread.h> |
70 | |
71 | /* |
72 | * Use MUTEX_TIMING to activate/deactivate timing statistics from locks. for |
73 | * RWLOCKS, we measure just the write locks, which also guarantee exclusive |
74 | * access. The name MUTEX_TIMING is kept as it used as well in mutex.c. |
75 | */ |
76 | //#define NS_NO_MUTEX_TIMING 1 |
77 | |
78 | /* |
79 | * The following structure defines a read/write lock including a mutex |
80 | * to protect access to the structure and condition variables for waiting |
81 | * reader and writer threads. |
82 | */ |
83 | |
84 | typedef struct RwLock { |
85 | pthread_rwlock_t rwlock; |
86 | unsigned long nlock; |
87 | unsigned long nrlock; |
88 | unsigned long nwlock; |
89 | unsigned long nbusy; |
90 | struct RwLock *nextPtr; |
91 | uintptr_t id; |
92 | Ns_Time start_time; |
93 | Ns_Time total_waiting_time; |
94 | Ns_Time max_waiting_time; |
95 | Ns_Time total_lock_time; |
96 | NS_RW rw; |
97 | char name[NS_THREAD_NAMESIZE64+1]; |
98 | } RwLock; |
99 | |
100 | static RwLock *GetRwLock(Ns_RWLock *rwPtr) |
101 | NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1))) NS_GNUC_RETURNS_NONNULL; |
102 | |
103 | |
104 | static RwLock *firstRwlockPtr = NULL((void*)0); |
105 | |
106 | /* |
107 | *---------------------------------------------------------------------- |
108 | * |
109 | * Ns_RWLockList -- |
110 | * |
111 | * Append info on each lock to Tcl_DString. |
112 | * |
113 | * Results: |
114 | * None. |
115 | * |
116 | * Side effects: |
117 | * None. |
118 | * |
119 | *---------------------------------------------------------------------- |
120 | */ |
121 | |
122 | void |
123 | Ns_RWLockList(Tcl_DString *dsPtr) |
124 | { |
125 | RwLock *rwlockPtr; |
126 | char buf[200]; |
127 | |
128 | Ns_MasterLock(); |
129 | for (rwlockPtr = firstRwlockPtr; rwlockPtr != NULL((void*)0); rwlockPtr = rwlockPtr->nextPtr) { |
130 | Tcl_DStringStartSublist(dsPtr); |
131 | Tcl_DStringAppendElement(dsPtr, rwlockPtr->name); |
132 | Tcl_DStringAppendElement(dsPtr, ""); /* unused? */ |
133 | #ifndef NS_NO_MUTEX_TIMING |
134 | snprintf(buf, (int)sizeof(buf),__builtin___snprintf_chk (buf, (int)sizeof(buf), 2 - 1, __builtin_object_size (buf, 2 > 1), " %" "l" "u" " %lu %lu " "%" "l" "d" ".%06ld" " " "%" "l" "d" ".%06ld" " " "%" "l" "d" ".%06ld" " %lu %lu" , rwlockPtr->id, rwlockPtr->nlock, rwlockPtr->nbusy , (int64_t)rwlockPtr->total_waiting_time.sec, rwlockPtr-> total_waiting_time.usec, (int64_t)rwlockPtr->max_waiting_time .sec, rwlockPtr->max_waiting_time.usec, (int64_t)rwlockPtr ->total_lock_time.sec, rwlockPtr->total_lock_time.usec, rwlockPtr->nrlock, rwlockPtr->nwlock) |
135 | " %" PRIuPTR " %lu %lu " NS_TIME_FMT " " NS_TIME_FMT " " NS_TIME_FMT__builtin___snprintf_chk (buf, (int)sizeof(buf), 2 - 1, __builtin_object_size (buf, 2 > 1), " %" "l" "u" " %lu %lu " "%" "l" "d" ".%06ld" " " "%" "l" "d" ".%06ld" " " "%" "l" "d" ".%06ld" " %lu %lu" , rwlockPtr->id, rwlockPtr->nlock, rwlockPtr->nbusy , (int64_t)rwlockPtr->total_waiting_time.sec, rwlockPtr-> total_waiting_time.usec, (int64_t)rwlockPtr->max_waiting_time .sec, rwlockPtr->max_waiting_time.usec, (int64_t)rwlockPtr ->total_lock_time.sec, rwlockPtr->total_lock_time.usec, rwlockPtr->nrlock, rwlockPtr->nwlock) |
136 | " %lu %lu" ,__builtin___snprintf_chk (buf, (int)sizeof(buf), 2 - 1, __builtin_object_size (buf, 2 > 1), " %" "l" "u" " %lu %lu " "%" "l" "d" ".%06ld" " " "%" "l" "d" ".%06ld" " " "%" "l" "d" ".%06ld" " %lu %lu" , rwlockPtr->id, rwlockPtr->nlock, rwlockPtr->nbusy , (int64_t)rwlockPtr->total_waiting_time.sec, rwlockPtr-> total_waiting_time.usec, (int64_t)rwlockPtr->max_waiting_time .sec, rwlockPtr->max_waiting_time.usec, (int64_t)rwlockPtr ->total_lock_time.sec, rwlockPtr->total_lock_time.usec, rwlockPtr->nrlock, rwlockPtr->nwlock) |
137 | rwlockPtr->id, rwlockPtr->nlock, rwlockPtr->nbusy,__builtin___snprintf_chk (buf, (int)sizeof(buf), 2 - 1, __builtin_object_size (buf, 2 > 1), " %" "l" "u" " %lu %lu " "%" "l" "d" ".%06ld" " " "%" "l" "d" ".%06ld" " " "%" "l" "d" ".%06ld" " %lu %lu" , rwlockPtr->id, rwlockPtr->nlock, rwlockPtr->nbusy , (int64_t)rwlockPtr->total_waiting_time.sec, rwlockPtr-> total_waiting_time.usec, (int64_t)rwlockPtr->max_waiting_time .sec, rwlockPtr->max_waiting_time.usec, (int64_t)rwlockPtr ->total_lock_time.sec, rwlockPtr->total_lock_time.usec, rwlockPtr->nrlock, rwlockPtr->nwlock) |
138 | (int64_t)rwlockPtr->total_waiting_time.sec, rwlockPtr->total_waiting_time.usec,__builtin___snprintf_chk (buf, (int)sizeof(buf), 2 - 1, __builtin_object_size (buf, 2 > 1), " %" "l" "u" " %lu %lu " "%" "l" "d" ".%06ld" " " "%" "l" "d" ".%06ld" " " "%" "l" "d" ".%06ld" " %lu %lu" , rwlockPtr->id, rwlockPtr->nlock, rwlockPtr->nbusy , (int64_t)rwlockPtr->total_waiting_time.sec, rwlockPtr-> total_waiting_time.usec, (int64_t)rwlockPtr->max_waiting_time .sec, rwlockPtr->max_waiting_time.usec, (int64_t)rwlockPtr ->total_lock_time.sec, rwlockPtr->total_lock_time.usec, rwlockPtr->nrlock, rwlockPtr->nwlock) |
139 | (int64_t)rwlockPtr->max_waiting_time.sec, rwlockPtr->max_waiting_time.usec,__builtin___snprintf_chk (buf, (int)sizeof(buf), 2 - 1, __builtin_object_size (buf, 2 > 1), " %" "l" "u" " %lu %lu " "%" "l" "d" ".%06ld" " " "%" "l" "d" ".%06ld" " " "%" "l" "d" ".%06ld" " %lu %lu" , rwlockPtr->id, rwlockPtr->nlock, rwlockPtr->nbusy , (int64_t)rwlockPtr->total_waiting_time.sec, rwlockPtr-> total_waiting_time.usec, (int64_t)rwlockPtr->max_waiting_time .sec, rwlockPtr->max_waiting_time.usec, (int64_t)rwlockPtr ->total_lock_time.sec, rwlockPtr->total_lock_time.usec, rwlockPtr->nrlock, rwlockPtr->nwlock) |
140 | (int64_t)rwlockPtr->total_lock_time.sec, rwlockPtr->total_lock_time.usec,__builtin___snprintf_chk (buf, (int)sizeof(buf), 2 - 1, __builtin_object_size (buf, 2 > 1), " %" "l" "u" " %lu %lu " "%" "l" "d" ".%06ld" " " "%" "l" "d" ".%06ld" " " "%" "l" "d" ".%06ld" " %lu %lu" , rwlockPtr->id, rwlockPtr->nlock, rwlockPtr->nbusy , (int64_t)rwlockPtr->total_waiting_time.sec, rwlockPtr-> total_waiting_time.usec, (int64_t)rwlockPtr->max_waiting_time .sec, rwlockPtr->max_waiting_time.usec, (int64_t)rwlockPtr ->total_lock_time.sec, rwlockPtr->total_lock_time.usec, rwlockPtr->nrlock, rwlockPtr->nwlock) |
141 | rwlockPtr->nrlock, rwlockPtr->nwlock)__builtin___snprintf_chk (buf, (int)sizeof(buf), 2 - 1, __builtin_object_size (buf, 2 > 1), " %" "l" "u" " %lu %lu " "%" "l" "d" ".%06ld" " " "%" "l" "d" ".%06ld" " " "%" "l" "d" ".%06ld" " %lu %lu" , rwlockPtr->id, rwlockPtr->nlock, rwlockPtr->nbusy , (int64_t)rwlockPtr->total_waiting_time.sec, rwlockPtr-> total_waiting_time.usec, (int64_t)rwlockPtr->max_waiting_time .sec, rwlockPtr->max_waiting_time.usec, (int64_t)rwlockPtr ->total_lock_time.sec, rwlockPtr->total_lock_time.usec, rwlockPtr->nrlock, rwlockPtr->nwlock); |
142 | #else |
143 | snprintf(buf, (int)sizeof(buf),__builtin___snprintf_chk (buf, (int)sizeof(buf), 2 - 1, __builtin_object_size (buf, 2 > 1), " %" "l" "u" " %lu %lu " "%" "l" "d" ".%06ld" " " "%" "l" "d" ".%06ld" " " "%" "l" "d" ".%06ld" " %lu %lu" , rwlockPtr->id, rwlockPtr->nlock, rwlockPtr->nbusy , (int64_t)0, (long)0, (int64_t)0, (long)0, (int64_t)0, (long )0, rwlockPtr->nrlock, rwlockPtr->nwlock) |
144 | " %" PRIuPTR " %lu %lu " NS_TIME_FMT " " NS_TIME_FMT " " NS_TIME_FMT__builtin___snprintf_chk (buf, (int)sizeof(buf), 2 - 1, __builtin_object_size (buf, 2 > 1), " %" "l" "u" " %lu %lu " "%" "l" "d" ".%06ld" " " "%" "l" "d" ".%06ld" " " "%" "l" "d" ".%06ld" " %lu %lu" , rwlockPtr->id, rwlockPtr->nlock, rwlockPtr->nbusy , (int64_t)0, (long)0, (int64_t)0, (long)0, (int64_t)0, (long )0, rwlockPtr->nrlock, rwlockPtr->nwlock) |
145 | " %lu %lu" ,__builtin___snprintf_chk (buf, (int)sizeof(buf), 2 - 1, __builtin_object_size (buf, 2 > 1), " %" "l" "u" " %lu %lu " "%" "l" "d" ".%06ld" " " "%" "l" "d" ".%06ld" " " "%" "l" "d" ".%06ld" " %lu %lu" , rwlockPtr->id, rwlockPtr->nlock, rwlockPtr->nbusy , (int64_t)0, (long)0, (int64_t)0, (long)0, (int64_t)0, (long )0, rwlockPtr->nrlock, rwlockPtr->nwlock) |
146 | rwlockPtr->id, rwlockPtr->nlock, rwlockPtr->nbusy,__builtin___snprintf_chk (buf, (int)sizeof(buf), 2 - 1, __builtin_object_size (buf, 2 > 1), " %" "l" "u" " %lu %lu " "%" "l" "d" ".%06ld" " " "%" "l" "d" ".%06ld" " " "%" "l" "d" ".%06ld" " %lu %lu" , rwlockPtr->id, rwlockPtr->nlock, rwlockPtr->nbusy , (int64_t)0, (long)0, (int64_t)0, (long)0, (int64_t)0, (long )0, rwlockPtr->nrlock, rwlockPtr->nwlock) |
147 | (int64_t)0, (long)0,__builtin___snprintf_chk (buf, (int)sizeof(buf), 2 - 1, __builtin_object_size (buf, 2 > 1), " %" "l" "u" " %lu %lu " "%" "l" "d" ".%06ld" " " "%" "l" "d" ".%06ld" " " "%" "l" "d" ".%06ld" " %lu %lu" , rwlockPtr->id, rwlockPtr->nlock, rwlockPtr->nbusy , (int64_t)0, (long)0, (int64_t)0, (long)0, (int64_t)0, (long )0, rwlockPtr->nrlock, rwlockPtr->nwlock) |
148 | (int64_t)0, (long)0,__builtin___snprintf_chk (buf, (int)sizeof(buf), 2 - 1, __builtin_object_size (buf, 2 > 1), " %" "l" "u" " %lu %lu " "%" "l" "d" ".%06ld" " " "%" "l" "d" ".%06ld" " " "%" "l" "d" ".%06ld" " %lu %lu" , rwlockPtr->id, rwlockPtr->nlock, rwlockPtr->nbusy , (int64_t)0, (long)0, (int64_t)0, (long)0, (int64_t)0, (long )0, rwlockPtr->nrlock, rwlockPtr->nwlock) |
149 | (int64_t)0, (long)0,__builtin___snprintf_chk (buf, (int)sizeof(buf), 2 - 1, __builtin_object_size (buf, 2 > 1), " %" "l" "u" " %lu %lu " "%" "l" "d" ".%06ld" " " "%" "l" "d" ".%06ld" " " "%" "l" "d" ".%06ld" " %lu %lu" , rwlockPtr->id, rwlockPtr->nlock, rwlockPtr->nbusy , (int64_t)0, (long)0, (int64_t)0, (long)0, (int64_t)0, (long )0, rwlockPtr->nrlock, rwlockPtr->nwlock) |
150 | rwlockPtr->nrlock, rwlockPtr->nwlock)__builtin___snprintf_chk (buf, (int)sizeof(buf), 2 - 1, __builtin_object_size (buf, 2 > 1), " %" "l" "u" " %lu %lu " "%" "l" "d" ".%06ld" " " "%" "l" "d" ".%06ld" " " "%" "l" "d" ".%06ld" " %lu %lu" , rwlockPtr->id, rwlockPtr->nlock, rwlockPtr->nbusy , (int64_t)0, (long)0, (int64_t)0, (long)0, (int64_t)0, (long )0, rwlockPtr->nrlock, rwlockPtr->nwlock); |
151 | #endif |
152 | Tcl_DStringAppend(dsPtr, buf, -1); |
153 | Tcl_DStringEndSublist(dsPtr); |
154 | } |
155 | Ns_MasterUnlock(); |
156 | } |
157 | |
158 | |
159 | |
160 | |
161 | |
162 | /* |
163 | *---------------------------------------------------------------------- |
164 | * |
165 | * Ns_RWLockInit -- |
166 | * |
167 | * Initialize a read/write lock. |
168 | * |
169 | * Results: |
170 | * None. |
171 | * |
172 | * Side effects: |
173 | * Lock memory is allocated from the heap and initialized. |
174 | * |
175 | *---------------------------------------------------------------------- |
176 | */ |
177 | |
178 | void |
179 | Ns_RWLockInit(Ns_RWLock *rwPtr) |
180 | { |
181 | RwLock *lockPtr; |
182 | static uintptr_t nextid = 0; |
183 | |
184 | NS_NONNULL_ASSERT(rwPtr != NULL)((void) (0)); |
185 | |
186 | lockPtr = ns_calloc(1u, sizeof(RwLock)); |
187 | |
188 | Ns_MasterLock(); |
189 | lockPtr->nextPtr = firstRwlockPtr; |
190 | firstRwlockPtr = lockPtr; |
191 | lockPtr->id = nextid++; |
192 | lockPtr->name[0] = 'r'; |
193 | lockPtr->name[1] = 'w'; |
194 | (void) ns_uint64toa(&lockPtr->name[2], (uint64_t)lockPtr->id); |
195 | Ns_MasterUnlock(); |
196 | |
197 | lockPtr->rw = NS_READ; |
198 | { |
199 | int err; |
200 | #if (defined(_XOPEN_SOURCE700) && _XOPEN_SOURCE700 >= 500) || (defined(_POSIX_C_SOURCE200809L) && _POSIX_C_SOURCE200809L >= 200809L) |
201 | pthread_rwlockattr_t attr; |
202 | pthread_rwlockattr_setkind_np(&attr, |
203 | PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP); |
204 | err = pthread_rwlock_init(&lockPtr->rwlock, &attr); |
205 | #else |
206 | err = pthread_rwlock_init(&lockPtr->rwlock, NULL((void*)0)); |
207 | #endif |
208 | if (err != 0) { |
209 | NsThreadFatal("Ns_RWLockInit", "pthread_rwlock_init", err); |
210 | } |
211 | } |
212 | *rwPtr = (Ns_RWLock) lockPtr; |
213 | } |
214 | |
215 | /* |
216 | *---------------------------------------------------------------------- |
217 | * |
218 | * Ns_RWLockSetName2 -- |
219 | * |
220 | * Update the string name of a rwlock. Ns_RWLockSetName2 uses |
221 | * Ns_MutexSetName2 to set a name based on the two string components and |
222 | * concatenates these with a colon (":"). |
223 | * |
224 | * Results: |
225 | * None. |
226 | * |
227 | * Side effects: |
228 | * None. |
229 | * |
230 | *---------------------------------------------------------------------- |
231 | */ |
232 | |
233 | void |
234 | Ns_RWLockSetName2(Ns_RWLock *rwPtr, const char *prefix, const char *name) |
235 | { |
236 | RwLock *lockPtr; |
237 | size_t prefixLength, nameLength; |
238 | char *p; |
239 | |
240 | NS_NONNULL_ASSERT(rwPtr != NULL)((void) (0)); |
241 | NS_NONNULL_ASSERT(prefix != NULL)((void) (0)); |
242 | |
243 | prefixLength = strlen(prefix); |
244 | if (prefixLength > NS_THREAD_NAMESIZE64 - 1) { |
245 | prefixLength = NS_THREAD_NAMESIZE64 - 1; |
246 | nameLength = 0u; |
247 | } else if (name != NULL((void*)0)) { |
248 | nameLength = strlen(name); |
249 | if ((nameLength + prefixLength + 1) > NS_THREAD_NAMESIZE64) { |
250 | nameLength = NS_THREAD_NAMESIZE64 - prefixLength - 1; |
251 | } |
252 | } else { |
253 | nameLength = 0u; |
254 | } |
255 | |
256 | lockPtr = GetRwLock(rwPtr); |
257 | |
258 | Ns_MasterLock(); |
259 | p = lockPtr->name; |
260 | memcpy(p, prefix, prefixLength + 1u); |
261 | if (name != NULL((void*)0)) { |
262 | p += prefixLength; |
263 | *p++ = ':'; |
264 | assert(name != NULL)((void) (0)); |
265 | memcpy(p, name, nameLength + 1u); |
266 | } |
267 | Ns_MasterUnlock(); |
268 | } |
269 | |
270 | |
271 | /* |
272 | *---------------------------------------------------------------------- |
273 | * |
274 | * Ns_RWLockDestroy -- |
275 | * |
276 | * Destroy a read/write lock if it was previously initialized. |
277 | * |
278 | * Results: |
279 | * None. |
280 | * |
281 | * Side effects: |
282 | * Read/write lock objects are destroy and the lock memory is |
283 | * returned to the heap. |
284 | * |
285 | *---------------------------------------------------------------------- |
286 | */ |
287 | |
288 | void |
289 | Ns_RWLockDestroy(Ns_RWLock *rwPtr) |
290 | { |
291 | RwLock *lockPtr = (RwLock *) *rwPtr; |
292 | |
293 | if (lockPtr != NULL((void*)0)) { |
294 | RwLock **rwlockPtrPtr; |
295 | int err = pthread_rwlock_destroy(&lockPtr->rwlock); |
296 | |
297 | if (unlikely(err != 0)(__builtin_expect((err != 0), 0))) { |
298 | NsThreadFatal("Ns_RWLockDestroy", "pthread_rwlock_destroy", err); |
299 | } |
300 | /* |
301 | * Remove lock from linked list of rwlocks. |
302 | */ |
303 | Ns_MasterLock(); |
304 | rwlockPtrPtr = &firstRwlockPtr; |
305 | while ((*rwlockPtrPtr) != lockPtr) { |
306 | rwlockPtrPtr = &(*rwlockPtrPtr)->nextPtr; |
307 | } |
308 | *rwlockPtrPtr = lockPtr->nextPtr; |
309 | Ns_MasterUnlock(); |
310 | |
311 | *rwPtr = NULL((void*)0); |
312 | } |
313 | } |
314 | |
315 | |
316 | /* |
317 | *---------------------------------------------------------------------- |
318 | * |
319 | * Ns_RWLockRdLock -- |
320 | * |
321 | * Acquire a read lock. |
322 | * |
323 | * Results: |
324 | * None. |
325 | * |
326 | * Side effects: |
327 | * Thread may wait on a condition variable if the read/write lock |
328 | * currently has a write lock. |
329 | * |
330 | *---------------------------------------------------------------------- |
331 | */ |
332 | |
333 | void |
334 | Ns_RWLockRdLock(Ns_RWLock *rwPtr) |
335 | { |
336 | RwLock *lockPtr; |
337 | int err; |
338 | bool_Bool busy; |
339 | |
340 | NS_NONNULL_ASSERT(rwPtr != NULL)((void) (0)); |
341 | |
342 | lockPtr = GetRwLock(rwPtr); |
343 | |
344 | err = pthread_rwlock_tryrdlock(&lockPtr->rwlock); |
345 | if (unlikely(err == EBUSY)(__builtin_expect((err == 16), 0))) { |
346 | busy = NS_TRUE1; |
347 | } else if (unlikely(err != 0)(__builtin_expect((err != 0), 0))) { |
348 | busy = NS_FALSE0; |
349 | NsThreadFatal("Ns_RWLockRdLock", "pthread_rwlock_tryrdlock", err); |
350 | } else { |
351 | busy = NS_FALSE0; |
352 | } |
353 | |
354 | if (busy) { |
355 | err = pthread_rwlock_rdlock(&lockPtr->rwlock); |
356 | if (err != 0) { |
357 | NsThreadFatal("Ns_RWLockRdLock", "pthread_rwlock_rdlock", err); |
358 | } |
359 | lockPtr->nbusy++; |
360 | } |
361 | lockPtr->nlock++; |
362 | lockPtr->nrlock++; |
363 | } |
364 | |
365 | |
366 | /* |
367 | *---------------------------------------------------------------------- |
368 | * |
369 | * Ns_RWLockWrLock -- |
370 | * |
371 | * Acquire a write lock. |
372 | * |
373 | * Results: |
374 | * None. |
375 | * |
376 | * Side effects: |
377 | * Thread may wait on the write condition if other threads either |
378 | * have the lock read or write locked. |
379 | * |
380 | *---------------------------------------------------------------------- |
381 | */ |
382 | |
383 | void |
384 | Ns_RWLockWrLock(Ns_RWLock *rwPtr) |
385 | { |
386 | RwLock *lockPtr; |
387 | int err; |
388 | bool_Bool busy; |
389 | #ifndef NS_NO_MUTEX_TIMING |
390 | Ns_Time end, diff, startTime; |
391 | #endif |
392 | |
393 | NS_NONNULL_ASSERT(rwPtr != NULL)((void) (0)); |
394 | |
395 | lockPtr = GetRwLock(rwPtr); |
396 | |
397 | #ifndef NS_NO_MUTEX_TIMING |
398 | Ns_GetTime(&startTime); |
399 | #endif |
400 | |
401 | err = pthread_rwlock_trywrlock(&lockPtr->rwlock); |
402 | if (unlikely(err == EBUSY)(__builtin_expect((err == 16), 0))) { |
403 | busy = NS_TRUE1; |
404 | } else if (unlikely(err != 0)(__builtin_expect((err != 0), 0))) { |
405 | busy = NS_FALSE0; |
Value stored to 'busy' is never read | |
406 | NsThreadFatal("Ns_RWLockWrLock", "pthread_rwlock_trywrlock", err); |
407 | } else { |
408 | busy = NS_FALSE0; |
409 | } |
410 | |
411 | if (busy) { |
412 | err = pthread_rwlock_wrlock(&lockPtr->rwlock); |
413 | if (err != 0) { |
414 | NsThreadFatal("Ns_RWLockWrLock", "pthread_rwlock_wrlock", err); |
415 | } |
416 | lockPtr->nbusy ++; |
417 | |
418 | #ifndef NS_NO_MUTEX_TIMING |
419 | /* |
420 | * Measure total and max waiting time for busy rwlock locks. |
421 | */ |
422 | Ns_GetTime(&end); |
423 | Ns_DiffTime(&end, &startTime, &diff); |
424 | Ns_IncrTime(&lockPtr->total_waiting_time, diff.sec, diff.usec); |
425 | #endif |
426 | } |
427 | #ifndef NS_NO_MUTEX_TIMING |
428 | lockPtr->rw = NS_WRITE; |
429 | lockPtr->start_time = startTime; |
430 | #endif |
431 | lockPtr->nlock ++; |
432 | lockPtr->nwlock++; |
433 | } |
434 | |
435 | |
436 | /* |
437 | *---------------------------------------------------------------------- |
438 | * |
439 | * Ns_RWLockUnlock -- |
440 | * |
441 | * Unlock a read/write lock. |
442 | * |
443 | * Results: |
444 | * None. |
445 | * |
446 | * Side effects: |
447 | * Read or write condition may be signaled. |
448 | * |
449 | *---------------------------------------------------------------------- |
450 | */ |
451 | |
452 | void |
453 | Ns_RWLockUnlock(Ns_RWLock *rwPtr) |
454 | { |
455 | RwLock *lockPtr = (RwLock *) *rwPtr; |
456 | int err; |
457 | |
458 | #ifndef NS_NO_MUTEX_TIMING |
459 | /* |
460 | * Measure block times etc only in writer case, which guarantees exclusive |
461 | * access and blocking). |
462 | */ |
463 | if (lockPtr->rw == NS_WRITE) { |
464 | Ns_Time end, diff; |
465 | |
466 | lockPtr->rw = NS_READ; |
467 | Ns_GetTime(&end); |
468 | Ns_DiffTime(&end, &lockPtr->start_time, &diff); |
469 | Ns_IncrTime(&lockPtr->total_lock_time, diff.sec, diff.usec); |
470 | } |
471 | #endif |
472 | |
473 | err = pthread_rwlock_unlock(&lockPtr->rwlock); |
474 | if (err != 0) { |
475 | NsThreadFatal("Ns_RWLockUnlock", "pthread_rwlock_unlock", err); |
476 | } |
477 | } |
478 | |
479 | |
480 | /* |
481 | *---------------------------------------------------------------------- |
482 | * |
483 | * GetRwLock -- |
484 | * |
485 | * Return the read/write lock structure, initializing it if needed. |
486 | * |
487 | * Results: |
488 | * Pointer to lock. |
489 | * |
490 | * Side effects: |
491 | * Lock may be initialized. |
492 | * |
493 | *---------------------------------------------------------------------- |
494 | */ |
495 | |
496 | static RwLock * |
497 | GetRwLock(Ns_RWLock *rwPtr) |
498 | { |
499 | NS_NONNULL_ASSERT(rwPtr != NULL)((void) (0)); |
500 | |
501 | if (*rwPtr == NULL((void*)0)) { |
502 | Ns_MasterLock(); |
503 | if (*rwPtr == NULL((void*)0)) { |
504 | Ns_RWLockInit(rwPtr); |
505 | } |
506 | Ns_MasterUnlock(); |
507 | } |
508 | return (RwLock *) *rwPtr; |
509 | } |
510 | |
511 | #else /* NOT HAVE_PTHREAD */ |
512 | |
513 | /* |
514 | * The following structure defines a read/write lock including a mutex |
515 | * to protect access to the structure and condition variables for waiting |
516 | * reader and writer threads. |
517 | */ |
518 | |
519 | typedef struct RwLock { |
520 | Ns_Mutex mutex; /* Mutex guarding lock structure. */ |
521 | Ns_Cond rcond; /* Condition variable for waiting readers. */ |
522 | Ns_Cond wcond; /* condition variable for waiting writers. */ |
523 | int nreaders; /* Number of readers waiting for lock. */ |
524 | int nwriters; /* Number of writers waiting for lock. */ |
525 | int lockcnt; /* Lock count, > 0 indicates # of shared |
526 | * readers, -1 indicates exclusive writer. */ |
527 | } RwLock; |
528 | |
529 | static RwLock *GetRwLock(Ns_RWLock *rwPtr) |
530 | NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1))) NS_GNUC_RETURNS_NONNULL; |
531 | |
532 | |
533 | /* |
534 | *---------------------------------------------------------------------- |
535 | * |
536 | * Ns_RWLockList -- |
537 | * |
538 | * Append info on each lock to Tcl_DString. Since the rwlock emulation is |
539 | * based on mutexes, the information on these locks is included in |
540 | * Ns_MutexList(). |
541 | * |
542 | * Results: |
543 | * None. |
544 | * |
545 | * Side effects: |
546 | * None. |
547 | * |
548 | *---------------------------------------------------------------------- |
549 | */ |
550 | void |
551 | Ns_RWLockList(Tcl_DString *UNUSED(dsPtr)UNUSED_dsPtr __attribute__((__unused__))) |
552 | { |
553 | } |
554 | |
555 | |
556 | /* |
557 | *---------------------------------------------------------------------- |
558 | * |
559 | * Ns_RWLockInit -- |
560 | * |
561 | * Initialize a read/write lock. |
562 | * |
563 | * Results: |
564 | * None. |
565 | * |
566 | * Side effects: |
567 | * Lock memory is allocated from the heap and initialized. |
568 | * |
569 | *---------------------------------------------------------------------- |
570 | */ |
571 | |
572 | void |
573 | Ns_RWLockInit(Ns_RWLock *rwPtr) |
574 | { |
575 | RwLock *lockPtr; |
576 | static uintptr_t nextid = 0; |
577 | |
578 | NS_NONNULL_ASSERT(rwPtr != NULL)((void) (0)); |
579 | |
580 | lockPtr = ns_calloc(1u, sizeof(RwLock)); |
581 | NsMutexInitNext(&lockPtr->mutex, "rw", &nextid); |
582 | Ns_CondInit(&lockPtr->rcond); |
583 | Ns_CondInit(&lockPtr->wcond); |
584 | lockPtr->nreaders = 0; |
585 | lockPtr->nwriters = 0; |
586 | lockPtr->lockcnt = 0; |
587 | *rwPtr = (Ns_RWLock) lockPtr; |
588 | } |
589 | |
590 | /* |
591 | *---------------------------------------------------------------------- |
592 | * |
593 | * Ns_RWLockSetName2 -- |
594 | * |
595 | * Update the string name of a mutex. Ns_RWLockSetName2 uses |
596 | * Ns_MutexSetName2 to set a name based on the two string components and |
597 | * concatenates these with a colon (":"). |
598 | * |
599 | * Results: |
600 | * None. |
601 | * |
602 | * Side effects: |
603 | * None. |
604 | * |
605 | *---------------------------------------------------------------------- |
606 | */ |
607 | |
608 | void |
609 | Ns_RWLockSetName2(Ns_RWLock *rwPtr, const char *prefix, const char *name) |
610 | { |
611 | RwLock *lockPtr = (RwLock *)*rwPtr; |
612 | |
613 | Ns_MutexSetName2(&lockPtr->mutex, prefix, name); |
614 | |
615 | } |
616 | |
617 | |
618 | |
619 | /* |
620 | *---------------------------------------------------------------------- |
621 | * |
622 | * Ns_RWLockDestroy -- |
623 | * |
624 | * Destroy a read/write lock if it was previously initialized. |
625 | * |
626 | * Results: |
627 | * None. |
628 | * |
629 | * Side effects: |
630 | * Read/write lock objects are destroy and the lock memory is |
631 | * returned to the heap. |
632 | * |
633 | *---------------------------------------------------------------------- |
634 | */ |
635 | |
636 | void |
637 | Ns_RWLockDestroy(Ns_RWLock *rwPtr) |
638 | { |
639 | RwLock *lockPtr = (RwLock *) *rwPtr; |
640 | |
641 | if (lockPtr != NULL((void*)0)) { |
642 | Ns_MutexDestroy(&lockPtr->mutex); |
643 | Ns_CondDestroy(&lockPtr->rcond); |
644 | Ns_CondDestroy(&lockPtr->wcond); |
645 | ns_free(lockPtr); |
646 | *rwPtr = NULL((void*)0); |
647 | } |
648 | } |
649 | |
650 | |
651 | /* |
652 | *---------------------------------------------------------------------- |
653 | * |
654 | * Ns_RWLockRdLock -- |
655 | * |
656 | * Acquire a read lock. |
657 | * |
658 | * Results: |
659 | * None. |
660 | * |
661 | * Side effects: |
662 | * Thread may wait on a condition variable if the read/write lock |
663 | * currently has a write lock. |
664 | * |
665 | *---------------------------------------------------------------------- |
666 | */ |
667 | |
668 | void |
669 | Ns_RWLockRdLock(Ns_RWLock *rwPtr) |
670 | { |
671 | RwLock *lockPtr; |
672 | |
673 | NS_NONNULL_ASSERT(rwPtr != NULL)((void) (0)); |
674 | |
675 | lockPtr = GetRwLock(rwPtr); |
676 | Ns_MutexLock(&lockPtr->mutex); |
677 | |
678 | /* |
679 | * Wait on the read condition while the lock is write-locked or |
680 | * some other thread is waiting for a write lock. |
681 | */ |
682 | |
683 | while (lockPtr->lockcnt < 0 || lockPtr->nwriters > 0) { |
684 | lockPtr->nreaders++; |
685 | Ns_CondWait(&lockPtr->rcond, &lockPtr->mutex); |
686 | lockPtr->nreaders--; |
687 | } |
688 | |
689 | lockPtr->lockcnt++; |
690 | Ns_MutexUnlock(&lockPtr->mutex); |
691 | } |
692 | |
693 | |
694 | /* |
695 | *---------------------------------------------------------------------- |
696 | * |
697 | * Ns_RWLockWrLock -- |
698 | * |
699 | * Acquire a write lock. |
700 | * |
701 | * Results: |
702 | * None. |
703 | * |
704 | * Side effects: |
705 | * Thread may wait on the write condition if other threads either |
706 | * have the lock read or write locked. |
707 | * |
708 | *---------------------------------------------------------------------- |
709 | */ |
710 | |
711 | void |
712 | Ns_RWLockWrLock(Ns_RWLock *rwPtr) |
713 | { |
714 | RwLock *lockPtr; |
715 | |
716 | NS_NONNULL_ASSERT(rwPtr != NULL)((void) (0)); |
717 | |
718 | lockPtr = GetRwLock(rwPtr); |
719 | |
720 | Ns_MutexLock(&lockPtr->mutex); |
721 | while (lockPtr->lockcnt != 0) { |
722 | lockPtr->nwriters++; |
723 | Ns_CondWait(&lockPtr->wcond, &lockPtr->mutex); |
724 | lockPtr->nwriters--; |
725 | } |
726 | lockPtr->lockcnt = -1; |
727 | Ns_MutexUnlock(&lockPtr->mutex); |
728 | } |
729 | |
730 | |
731 | /* |
732 | *---------------------------------------------------------------------- |
733 | * |
734 | * Ns_RWLockUnlock -- |
735 | * |
736 | * Unlock a read/write lock. |
737 | * |
738 | * Results: |
739 | * None. |
740 | * |
741 | * Side effects: |
742 | * Read or write condition may be signaled. |
743 | * |
744 | *---------------------------------------------------------------------- |
745 | */ |
746 | |
747 | void |
748 | Ns_RWLockUnlock(Ns_RWLock *rwPtr) |
749 | { |
750 | RwLock *lockPtr; |
751 | |
752 | NS_NONNULL_ASSERT(rwPtr != NULL)((void) (0)); |
753 | lockPtr = (RwLock *) *rwPtr; |
754 | |
755 | Ns_MutexLock(&lockPtr->mutex); |
756 | if (--lockPtr->lockcnt < 0) { |
757 | lockPtr->lockcnt = 0; |
758 | } |
759 | if (lockPtr->nwriters != 0) { |
760 | Ns_CondSignal(&lockPtr->wcond); |
761 | } else if (lockPtr->nreaders != 0) { |
762 | Ns_CondBroadcast(&lockPtr->rcond); |
763 | } |
764 | Ns_MutexUnlock (&lockPtr->mutex); |
765 | } |
766 | |
767 | |
768 | /* |
769 | *---------------------------------------------------------------------- |
770 | * |
771 | * GetRwLock -- |
772 | * |
773 | * Return the read/write lock structure, initializing it if needed. |
774 | * |
775 | * Results: |
776 | * Pointer to lock. |
777 | * |
778 | * Side effects: |
779 | * Lock may be initialized. |
780 | * |
781 | *---------------------------------------------------------------------- |
782 | */ |
783 | |
784 | static RwLock * |
785 | GetRwLock(Ns_RWLock *rwPtr) |
786 | { |
787 | NS_NONNULL_ASSERT(rwPtr != NULL)((void) (0)); |
788 | |
789 | if (*rwPtr == NULL((void*)0)) { |
790 | Ns_MasterLock(); |
791 | if (*rwPtr == NULL((void*)0)) { |
792 | Ns_RWLockInit(rwPtr); |
793 | } |
794 | Ns_MasterUnlock(); |
795 | } |
796 | return (RwLock *) *rwPtr; |
797 | } |
798 | |
799 | #endif /* HAVE_PTHREAD */ |
800 | /* |
801 | * Local Variables: |
802 | * mode: c |
803 | * c-basic-offset: 4 |
804 | * fill-column: 78 |
805 | * indent-tabs-mode: nil |
806 | * End: |
807 | */ |