1 /* $NetBSD: pthread_rwlock.c,v 1.22 2007/11/13 15:57:13 ad Exp $ */ 2 3 /*- 4 * Copyright (c) 2002, 2006, 2007 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Nathan J. Williams and Andrew Doran. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 3. All advertising materials mentioning features or use of this software 19 * must display the following acknowledgement: 20 * This product includes software developed by the NetBSD 21 * Foundation, Inc. and its contributors. 22 * 4. Neither the name of The NetBSD Foundation nor the names of its 23 * contributors may be used to endorse or promote products derived 24 * from this software without specific prior written permission. 25 * 26 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 27 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 28 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 29 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 30 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 31 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 32 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 33 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 34 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 36 * POSSIBILITY OF SUCH DAMAGE. 37 */ 38 39 #include <sys/cdefs.h> 40 __RCSID("$NetBSD: pthread_rwlock.c,v 1.22 2007/11/13 15:57:13 ad Exp $"); 41 42 #include <errno.h> 43 44 #include "pthread.h" 45 #include "pthread_int.h" 46 47 #ifndef PTHREAD__HAVE_ATOMIC 48 49 __strong_alias(__libc_rwlock_init,pthread_rwlock_init) 50 __strong_alias(__libc_rwlock_rdlock,pthread_rwlock_rdlock) 51 __strong_alias(__libc_rwlock_wrlock,pthread_rwlock_wrlock) 52 __strong_alias(__libc_rwlock_tryrdlock,pthread_rwlock_tryrdlock) 53 __strong_alias(__libc_rwlock_trywrlock,pthread_rwlock_trywrlock) 54 __strong_alias(__libc_rwlock_unlock,pthread_rwlock_unlock) 55 __strong_alias(__libc_rwlock_destroy,pthread_rwlock_destroy) 56 57 int 58 pthread_rwlock_init(pthread_rwlock_t *rwlock, 59 const pthread_rwlockattr_t *attr) 60 { 61 #ifdef ERRORCHECK 62 if ((rwlock == NULL) || 63 (attr && (attr->ptra_magic != _PT_RWLOCKATTR_MAGIC))) 64 return EINVAL; 65 #endif 66 rwlock->ptr_magic = _PT_RWLOCK_MAGIC; 67 pthread_lockinit(&rwlock->ptr_interlock); 68 PTQ_INIT(&rwlock->ptr_rblocked); 69 PTQ_INIT(&rwlock->ptr_wblocked); 70 rwlock->ptr_nreaders = 0; 71 rwlock->ptr_writer = NULL; 72 73 return 0; 74 } 75 76 77 int 78 pthread_rwlock_destroy(pthread_rwlock_t *rwlock) 79 { 80 #ifdef ERRORCHECK 81 if ((rwlock == NULL) || 82 (rwlock->ptr_magic != _PT_RWLOCK_MAGIC) || 83 (!PTQ_EMPTY(&rwlock->ptr_rblocked)) || 84 (!PTQ_EMPTY(&rwlock->ptr_wblocked)) || 85 (rwlock->ptr_nreaders != 0) || 86 (rwlock->ptr_writer != NULL)) 87 return EINVAL; 88 #endif 89 rwlock->ptr_magic = _PT_RWLOCK_DEAD; 90 91 return 0; 92 } 93 94 95 int 96 pthread_rwlock_rdlock(pthread_rwlock_t *rwlock) 97 { 98 pthread_t self; 99 #ifdef ERRORCHECK 100 if ((rwlock == NULL) || (rwlock->ptr_magic != _PT_RWLOCK_MAGIC)) 101 return EINVAL; 102 #endif 103 self = pthread__self(); 104 105 pthread__spinlock(self, &rwlock->ptr_interlock); 106 #ifdef ERRORCHECK 107 if (rwlock->ptr_writer == self) { 108 pthread__spinunlock(self, &rwlock->ptr_interlock); 109 return EDEADLK; 110 } 111 #endif 112 /* 113 * Don't get a readlock if there is a writer or if there are waiting 114 * writers; i.e. prefer writers to readers. This strategy is dictated 115 * by SUSv3. 116 */ 117 while ((rwlock->ptr_writer != NULL) || 118 (!PTQ_EMPTY(&rwlock->ptr_wblocked))) { 119 PTQ_INSERT_TAIL(&rwlock->ptr_rblocked, self, pt_sleep); 120 self->pt_sleeponq = 1; 121 self->pt_sleepobj = &rwlock->ptr_rblocked; 122 pthread__spinunlock(self, &rwlock->ptr_interlock); 123 (void)pthread__park(self, &rwlock->ptr_interlock, 124 &rwlock->ptr_rblocked, NULL, 0, &rwlock->ptr_rblocked); 125 pthread__spinlock(self, &rwlock->ptr_interlock); 126 } 127 128 rwlock->ptr_nreaders++; 129 pthread__spinunlock(self, &rwlock->ptr_interlock); 130 131 return 0; 132 } 133 134 135 int 136 pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock) 137 { 138 pthread_t self; 139 140 #ifdef ERRORCHECK 141 if ((rwlock == NULL) || (rwlock->ptr_magic != _PT_RWLOCK_MAGIC)) 142 return EINVAL; 143 #endif 144 145 self = pthread__self(); 146 pthread__spinlock(self, &rwlock->ptr_interlock); 147 /* 148 * Don't get a readlock if there is a writer or if there are waiting 149 * writers; i.e. prefer writers to readers. This strategy is dictated 150 * by SUSv3. 151 */ 152 if ((rwlock->ptr_writer != NULL) || 153 (!PTQ_EMPTY(&rwlock->ptr_wblocked))) { 154 pthread__spinunlock(self, &rwlock->ptr_interlock); 155 return EBUSY; 156 } 157 158 rwlock->ptr_nreaders++; 159 pthread__spinunlock(self, &rwlock->ptr_interlock); 160 161 return 0; 162 } 163 164 165 int 166 pthread_rwlock_wrlock(pthread_rwlock_t *rwlock) 167 { 168 pthread_t self; 169 extern int pthread__started; 170 171 #ifdef ERRORCHECK 172 if ((rwlock == NULL) || (rwlock->ptr_magic != _PT_RWLOCK_MAGIC)) 173 return EINVAL; 174 #endif 175 self = pthread__self(); 176 177 pthread__spinlock(self, &rwlock->ptr_interlock); 178 #ifdef ERRORCHECK 179 if (rwlock->ptr_writer == self) { 180 pthread__spinunlock(self, &rwlock->ptr_interlock); 181 return EDEADLK; 182 } 183 #endif 184 /* 185 * Prefer writers to readers here; permit writers even if there are 186 * waiting readers. 187 */ 188 while ((rwlock->ptr_nreaders > 0) || (rwlock->ptr_writer != NULL)) { 189 #ifdef ERRORCHECK 190 if (pthread__started == 0) { 191 pthread__spinunlock(self, &rwlock->ptr_interlock); 192 return EDEADLK; 193 } 194 #endif 195 PTQ_INSERT_TAIL(&rwlock->ptr_wblocked, self, pt_sleep); 196 self->pt_sleeponq = 1; 197 self->pt_sleepobj = &rwlock->ptr_wblocked; 198 pthread__spinunlock(self, &rwlock->ptr_interlock); 199 (void)pthread__park(self, &rwlock->ptr_interlock, 200 &rwlock->ptr_wblocked, NULL, 0, &rwlock->ptr_wblocked); 201 pthread__spinlock(self, &rwlock->ptr_interlock); 202 } 203 204 rwlock->ptr_writer = self; 205 pthread__spinunlock(self, &rwlock->ptr_interlock); 206 207 return 0; 208 } 209 210 211 int 212 pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock) 213 { 214 pthread_t self; 215 #ifdef ERRORCHECK 216 if ((rwlock == NULL) || (rwlock->ptr_magic != _PT_RWLOCK_MAGIC)) 217 return EINVAL; 218 #endif 219 self = pthread__self(); 220 221 pthread__spinlock(self, &rwlock->ptr_interlock); 222 /* 223 * Prefer writers to readers here; permit writers even if there are 224 * waiting readers. 225 */ 226 if ((rwlock->ptr_nreaders > 0) || (rwlock->ptr_writer != NULL)) { 227 pthread__spinunlock(self, &rwlock->ptr_interlock); 228 return EBUSY; 229 } 230 231 rwlock->ptr_writer = self; 232 pthread__spinunlock(self, &rwlock->ptr_interlock); 233 234 return 0; 235 } 236 237 238 int 239 pthread_rwlock_timedrdlock(pthread_rwlock_t *rwlock, 240 const struct timespec *abs_timeout) 241 { 242 pthread_t self; 243 int retval; 244 245 #ifdef ERRORCHECK 246 if ((rwlock == NULL) || (rwlock->ptr_magic != _PT_RWLOCK_MAGIC)) 247 return EINVAL; 248 if (abs_timeout == NULL) 249 return EINVAL; 250 #endif 251 if ((abs_timeout->tv_nsec >= 1000000000) || 252 (abs_timeout->tv_nsec < 0) || 253 (abs_timeout->tv_sec < 0)) 254 return EINVAL; 255 256 self = pthread__self(); 257 pthread__spinlock(self, &rwlock->ptr_interlock); 258 #ifdef ERRORCHECK 259 if (rwlock->ptr_writer == self) { 260 pthread__spinunlock(self, &rwlock->ptr_interlock); 261 return EDEADLK; 262 } 263 #endif 264 /* 265 * Don't get a readlock if there is a writer or if there are waiting 266 * writers; i.e. prefer writers to readers. This strategy is dictated 267 * by SUSv3. 268 */ 269 retval = 0; 270 while ((retval == 0) && ((rwlock->ptr_writer != NULL) || 271 (!PTQ_EMPTY(&rwlock->ptr_wblocked)))) { 272 PTQ_INSERT_TAIL(&rwlock->ptr_rblocked, self, pt_sleep); 273 self->pt_sleeponq = 1; 274 self->pt_sleepobj = &rwlock->ptr_rblocked; 275 pthread__spinunlock(self, &rwlock->ptr_interlock); 276 retval = pthread__park(self, &rwlock->ptr_interlock, 277 &rwlock->ptr_rblocked, abs_timeout, 0, 278 &rwlock->ptr_rblocked); 279 pthread__spinlock(self, &rwlock->ptr_interlock); 280 } 281 282 /* One last chance to get the lock, in case it was released between 283 the alarm firing and when this thread got rescheduled, or in case 284 a signal handler kept it busy */ 285 if ((rwlock->ptr_writer == NULL) && 286 (PTQ_EMPTY(&rwlock->ptr_wblocked))) { 287 rwlock->ptr_nreaders++; 288 retval = 0; 289 } 290 pthread__spinunlock(self, &rwlock->ptr_interlock); 291 292 return retval; 293 } 294 295 296 int 297 pthread_rwlock_timedwrlock(pthread_rwlock_t *rwlock, 298 const struct timespec *abs_timeout) 299 { 300 pthread_t self; 301 int retval; 302 extern int pthread__started; 303 304 #ifdef ERRORCHECK 305 if ((rwlock == NULL) || (rwlock->ptr_magic != _PT_RWLOCK_MAGIC)) 306 return EINVAL; 307 if (abs_timeout == NULL) 308 return EINVAL; 309 #endif 310 if ((abs_timeout->tv_nsec >= 1000000000) || 311 (abs_timeout->tv_nsec < 0) || 312 (abs_timeout->tv_sec < 0)) 313 return EINVAL; 314 315 self = pthread__self(); 316 pthread__spinlock(self, &rwlock->ptr_interlock); 317 #ifdef ERRORCHECK 318 if (rwlock->ptr_writer == self) { 319 pthread__spinunlock(self, &rwlock->ptr_interlock); 320 return EDEADLK; 321 } 322 #endif 323 /* 324 * Prefer writers to readers here; permit writers even if there are 325 * waiting readers. 326 */ 327 retval = 0; 328 while (retval == 0 && 329 ((rwlock->ptr_nreaders > 0) || (rwlock->ptr_writer != NULL))) { 330 #ifdef ERRORCHECK 331 if (pthread__started == 0) { 332 pthread__spinunlock(self, &rwlock->ptr_interlock); 333 return EDEADLK; 334 } 335 #endif 336 PTQ_INSERT_TAIL(&rwlock->ptr_wblocked, self, pt_sleep); 337 self->pt_sleeponq = 1; 338 self->pt_sleepobj = &rwlock->ptr_wblocked; 339 pthread__spinunlock(self, &rwlock->ptr_interlock); 340 retval = pthread__park(self, &rwlock->ptr_interlock, 341 &rwlock->ptr_wblocked, abs_timeout, 0, 342 &rwlock->ptr_wblocked); 343 pthread__spinlock(self, &rwlock->ptr_interlock); 344 } 345 346 if ((rwlock->ptr_nreaders == 0) && (rwlock->ptr_writer == NULL)) { 347 rwlock->ptr_writer = self; 348 retval = 0; 349 } 350 pthread__spinunlock(self, &rwlock->ptr_interlock); 351 352 return retval; 353 } 354 355 356 int 357 pthread_rwlock_unlock(pthread_rwlock_t *rwlock) 358 { 359 pthread_t self, writer; 360 #ifdef ERRORCHECK 361 if ((rwlock == NULL) || (rwlock->ptr_magic != _PT_RWLOCK_MAGIC)) 362 return EINVAL; 363 #endif 364 writer = NULL; 365 self = pthread__self(); 366 367 pthread__spinlock(self, &rwlock->ptr_interlock); 368 if (rwlock->ptr_writer != NULL) { 369 /* Releasing a write lock. */ 370 #ifdef ERRORCHECK 371 if (rwlock->ptr_writer != self) { 372 pthread__spinunlock(self, &rwlock->ptr_interlock); 373 return EPERM; 374 } 375 #endif 376 rwlock->ptr_writer = NULL; 377 writer = PTQ_FIRST(&rwlock->ptr_wblocked); 378 if (writer != NULL) { 379 PTQ_REMOVE(&rwlock->ptr_wblocked, writer, pt_sleep); 380 } 381 } else 382 #ifdef ERRORCHECK 383 if (rwlock->ptr_nreaders > 0) 384 #endif 385 { 386 /* Releasing a read lock. */ 387 rwlock->ptr_nreaders--; 388 if (rwlock->ptr_nreaders == 0) { 389 writer = PTQ_FIRST(&rwlock->ptr_wblocked); 390 if (writer != NULL) 391 PTQ_REMOVE(&rwlock->ptr_wblocked, writer, 392 pt_sleep); 393 } 394 #ifdef ERRORCHECK 395 } else { 396 pthread__spinunlock(self, &rwlock->ptr_interlock); 397 return EPERM; 398 #endif 399 } 400 401 if (writer != NULL) 402 pthread__unpark(self, &rwlock->ptr_interlock, 403 &rwlock->ptr_wblocked, writer); 404 else 405 pthread__unpark_all(self, &rwlock->ptr_interlock, 406 &rwlock->ptr_rblocked); 407 408 return 0; 409 } 410 411 412 int 413 pthread_rwlockattr_init(pthread_rwlockattr_t *attr) 414 { 415 #ifdef ERRORCHECK 416 if (attr == NULL) 417 return EINVAL; 418 #endif 419 attr->ptra_magic = _PT_RWLOCKATTR_MAGIC; 420 421 return 0; 422 } 423 424 425 int 426 pthread_rwlockattr_destroy(pthread_rwlockattr_t *attr) 427 { 428 #ifdef ERRORCHECK 429 if ((attr == NULL) || 430 (attr->ptra_magic != _PT_RWLOCKATTR_MAGIC)) 431 return EINVAL; 432 #endif 433 attr->ptra_magic = _PT_RWLOCKATTR_DEAD; 434 435 return 0; 436 } 437 438 #endif /* !PTHREAD__HAVE_ATOMIC */ 439