1 /* $NetBSD: pthread_rwlock.c,v 1.11 2005/01/09 01:57:38 nathanw Exp $ */ 2 3 /*- 4 * Copyright (c) 2002 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. 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.11 2005/01/09 01:57:38 nathanw Exp $"); 41 42 #include <errno.h> 43 44 #include "pthread.h" 45 #include "pthread_int.h" 46 47 static void pthread_rwlock__callback(void *); 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 /* Locking a rwlock is not a cancellation point; don't check */ 121 pthread_spinlock(self, &self->pt_statelock); 122 self->pt_state = PT_STATE_BLOCKED_QUEUE; 123 self->pt_sleepobj = rwlock; 124 self->pt_sleepq = &rwlock->ptr_rblocked; 125 self->pt_sleeplock = &rwlock->ptr_interlock; 126 pthread_spinunlock(self, &self->pt_statelock); 127 pthread__block(self, &rwlock->ptr_interlock); 128 /* interlock is not held when we return */ 129 pthread_spinlock(self, &rwlock->ptr_interlock); 130 } 131 132 rwlock->ptr_nreaders++; 133 pthread_spinunlock(self, &rwlock->ptr_interlock); 134 135 return 0; 136 } 137 138 139 int 140 pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock) 141 { 142 pthread_t self; 143 #ifdef ERRORCHECK 144 if ((rwlock == NULL) || (rwlock->ptr_magic != _PT_RWLOCK_MAGIC)) 145 return EINVAL; 146 #endif 147 self = pthread__self(); 148 149 pthread_spinlock(self, &rwlock->ptr_interlock); 150 /* 151 * Don't get a readlock if there is a writer or if there are waiting 152 * writers; i.e. prefer writers to readers. This strategy is dictated 153 * by SUSv3. 154 */ 155 if ((rwlock->ptr_writer != NULL) || 156 (!PTQ_EMPTY(&rwlock->ptr_wblocked))) { 157 pthread_spinunlock(self, &rwlock->ptr_interlock); 158 return EBUSY; 159 } 160 161 rwlock->ptr_nreaders++; 162 pthread_spinunlock(self, &rwlock->ptr_interlock); 163 164 return 0; 165 } 166 167 168 int 169 pthread_rwlock_wrlock(pthread_rwlock_t *rwlock) 170 { 171 pthread_t self; 172 #ifdef ERRORCHECK 173 if ((rwlock == NULL) || (rwlock->ptr_magic != _PT_RWLOCK_MAGIC)) 174 return EINVAL; 175 #endif 176 self = pthread__self(); 177 178 pthread_spinlock(self, &rwlock->ptr_interlock); 179 #ifdef ERRORCHECK 180 if (rwlock->ptr_writer == self) { 181 pthread_spinunlock(self, &rwlock->ptr_interlock); 182 return EDEADLK; 183 } 184 #endif 185 /* 186 * Prefer writers to readers here; permit writers even if there are 187 * waiting readers. 188 */ 189 while ((rwlock->ptr_nreaders > 0) || (rwlock->ptr_writer != NULL)) { 190 PTQ_INSERT_TAIL(&rwlock->ptr_wblocked, self, pt_sleep); 191 /* Locking a rwlock is not a cancellation point; don't check */ 192 pthread_spinlock(self, &self->pt_statelock); 193 self->pt_state = PT_STATE_BLOCKED_QUEUE; 194 self->pt_sleepobj = rwlock; 195 self->pt_sleepq = &rwlock->ptr_wblocked; 196 self->pt_sleeplock = &rwlock->ptr_interlock; 197 pthread_spinunlock(self, &self->pt_statelock); 198 pthread__block(self, &rwlock->ptr_interlock); 199 /* interlock is not held when we return */ 200 pthread_spinlock(self, &rwlock->ptr_interlock); 201 } 202 203 rwlock->ptr_writer = self; 204 pthread_spinunlock(self, &rwlock->ptr_interlock); 205 206 return 0; 207 } 208 209 210 int 211 pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock) 212 { 213 pthread_t self; 214 #ifdef ERRORCHECK 215 if ((rwlock == NULL) || (rwlock->ptr_magic != _PT_RWLOCK_MAGIC)) 216 return EINVAL; 217 #endif 218 self = pthread__self(); 219 220 pthread_spinlock(self, &rwlock->ptr_interlock); 221 /* 222 * Prefer writers to readers here; permit writers even if there are 223 * waiting readers. 224 */ 225 if ((rwlock->ptr_nreaders > 0) || (rwlock->ptr_writer != NULL)) { 226 pthread_spinunlock(self, &rwlock->ptr_interlock); 227 return EBUSY; 228 } 229 230 rwlock->ptr_writer = self; 231 pthread_spinunlock(self, &rwlock->ptr_interlock); 232 233 return 0; 234 } 235 236 237 struct pthread_rwlock__waitarg { 238 pthread_t ptw_thread; 239 pthread_rwlock_t *ptw_rwlock; 240 struct pthread_queue_t *ptw_queue; 241 }; 242 243 int 244 pthread_rwlock_timedrdlock(pthread_rwlock_t *rwlock, 245 const struct timespec *abs_timeout) 246 { 247 pthread_t self; 248 struct pthread_rwlock__waitarg wait; 249 struct pt_alarm_t alarm; 250 int retval; 251 #ifdef ERRORCHECK 252 if ((rwlock == NULL) || (rwlock->ptr_magic != _PT_RWLOCK_MAGIC)) 253 return EINVAL; 254 if (abs_timeout == NULL) 255 return EINVAL; 256 #endif 257 if ((abs_timeout->tv_nsec >= 1000000000) || 258 (abs_timeout->tv_nsec < 0) || 259 (abs_timeout->tv_sec < 0)) 260 return EINVAL; 261 self = pthread__self(); 262 263 pthread_spinlock(self, &rwlock->ptr_interlock); 264 #ifdef ERRORCHECK 265 if (rwlock->ptr_writer == self) { 266 pthread_spinunlock(self, &rwlock->ptr_interlock); 267 return EDEADLK; 268 } 269 #endif 270 /* 271 * Don't get a readlock if there is a writer or if there are waiting 272 * writers; i.e. prefer writers to readers. This strategy is dictated 273 * by SUSv3. 274 */ 275 retval = 0; 276 while ((retval == 0) && ((rwlock->ptr_writer != NULL) || 277 (!PTQ_EMPTY(&rwlock->ptr_wblocked)))) { 278 wait.ptw_thread = self; 279 wait.ptw_rwlock = rwlock; 280 wait.ptw_queue = &rwlock->ptr_rblocked; 281 pthread__alarm_add(self, &alarm, abs_timeout, 282 pthread_rwlock__callback, &wait); 283 PTQ_INSERT_TAIL(&rwlock->ptr_rblocked, self, pt_sleep); 284 /* Locking a rwlock is not a cancellation point; don't check */ 285 pthread_spinlock(self, &self->pt_statelock); 286 self->pt_state = PT_STATE_BLOCKED_QUEUE; 287 self->pt_sleepobj = rwlock; 288 self->pt_sleepq = &rwlock->ptr_rblocked; 289 self->pt_sleeplock = &rwlock->ptr_interlock; 290 pthread_spinunlock(self, &self->pt_statelock); 291 pthread__block(self, &rwlock->ptr_interlock); 292 /* interlock is not held when we return */ 293 pthread__alarm_del(self, &alarm); 294 if (pthread__alarm_fired(&alarm)) 295 retval = ETIMEDOUT; 296 pthread_spinlock(self, &rwlock->ptr_interlock); 297 } 298 299 /* One last chance to get the lock, in case it was released between 300 the alarm firing and when this thread got rescheduled, or in case 301 a signal handler kept it busy */ 302 if ((rwlock->ptr_writer == NULL) && 303 (PTQ_EMPTY(&rwlock->ptr_wblocked))) { 304 rwlock->ptr_nreaders++; 305 retval = 0; 306 } 307 pthread_spinunlock(self, &rwlock->ptr_interlock); 308 309 return retval; 310 } 311 312 313 int 314 pthread_rwlock_timedwrlock(pthread_rwlock_t *rwlock, 315 const struct timespec *abs_timeout) 316 { 317 struct pthread_rwlock__waitarg wait; 318 struct pt_alarm_t alarm; 319 int retval; 320 pthread_t self; 321 #ifdef ERRORCHECK 322 if ((rwlock == NULL) || (rwlock->ptr_magic != _PT_RWLOCK_MAGIC)) 323 return EINVAL; 324 if (abs_timeout == NULL) 325 return EINVAL; 326 #endif 327 if ((abs_timeout->tv_nsec >= 1000000000) || 328 (abs_timeout->tv_nsec < 0) || 329 (abs_timeout->tv_sec < 0)) 330 return EINVAL; 331 self = pthread__self(); 332 333 pthread_spinlock(self, &rwlock->ptr_interlock); 334 #ifdef ERRORCHECK 335 if (rwlock->ptr_writer == self) { 336 pthread_spinunlock(self, &rwlock->ptr_interlock); 337 return EDEADLK; 338 } 339 #endif 340 /* 341 * Prefer writers to readers here; permit writers even if there are 342 * waiting readers. 343 */ 344 retval = 0; 345 while (retval == 0 && 346 ((rwlock->ptr_nreaders > 0) || (rwlock->ptr_writer != NULL))) { 347 wait.ptw_thread = self; 348 wait.ptw_rwlock = rwlock; 349 wait.ptw_queue = &rwlock->ptr_wblocked; 350 pthread__alarm_add(self, &alarm, abs_timeout, 351 pthread_rwlock__callback, &wait); 352 PTQ_INSERT_TAIL(&rwlock->ptr_wblocked, self, pt_sleep); 353 /* Locking a rwlock is not a cancellation point; don't check */ 354 pthread_spinlock(self, &self->pt_statelock); 355 self->pt_state = PT_STATE_BLOCKED_QUEUE; 356 self->pt_sleepobj = rwlock; 357 self->pt_sleepq = &rwlock->ptr_wblocked; 358 self->pt_sleeplock = &rwlock->ptr_interlock; 359 pthread_spinunlock(self, &self->pt_statelock); 360 pthread__block(self, &rwlock->ptr_interlock); 361 /* interlock is not held when we return */ 362 pthread__alarm_del(self, &alarm); 363 if (pthread__alarm_fired(&alarm)) 364 retval = ETIMEDOUT; 365 pthread_spinlock(self, &rwlock->ptr_interlock); 366 } 367 368 if ((rwlock->ptr_nreaders == 0) && (rwlock->ptr_writer == NULL)) { 369 rwlock->ptr_writer = self; 370 retval = 0; 371 } 372 pthread_spinunlock(self, &rwlock->ptr_interlock); 373 374 return retval; 375 } 376 377 378 static void 379 pthread_rwlock__callback(void *arg) 380 { 381 struct pthread_rwlock__waitarg *a; 382 pthread_t self; 383 384 a = arg; 385 self = pthread__self(); 386 387 pthread_spinlock(self, &a->ptw_rwlock->ptr_interlock); 388 /* 389 * Don't dequeue and schedule the thread if it's already been 390 * queued up by a signal or broadcast (but hasn't yet run as far 391 * as pthread__alarm_del(), or we wouldn't be here, and hence can't 392 * have become blocked on some *other* queue). 393 */ 394 if (a->ptw_thread->pt_state == PT_STATE_BLOCKED_QUEUE) { 395 PTQ_REMOVE(a->ptw_queue, a->ptw_thread, pt_sleep); 396 pthread__sched(self, a->ptw_thread); 397 } 398 pthread_spinunlock(self, &a->ptw_rwlock->ptr_interlock); 399 400 } 401 402 403 int 404 pthread_rwlock_unlock(pthread_rwlock_t *rwlock) 405 { 406 pthread_t self, writer; 407 struct pthread_queue_t blockedq; 408 #ifdef ERRORCHECK 409 if ((rwlock == NULL) || (rwlock->ptr_magic != _PT_RWLOCK_MAGIC)) 410 return EINVAL; 411 #endif 412 writer = NULL; 413 PTQ_INIT(&blockedq); 414 self = pthread__self(); 415 416 pthread_spinlock(self, &rwlock->ptr_interlock); 417 if (rwlock->ptr_writer != NULL) { 418 /* Releasing a write lock. */ 419 #ifdef ERRORCHECK 420 if (rwlock->ptr_writer != self) { 421 pthread_spinunlock(self, &rwlock->ptr_interlock); 422 return EPERM; 423 } 424 #endif 425 rwlock->ptr_writer = NULL; 426 writer = PTQ_FIRST(&rwlock->ptr_wblocked); 427 if (writer != NULL) { 428 PTQ_REMOVE(&rwlock->ptr_wblocked, writer, pt_sleep); 429 } else { 430 blockedq = rwlock->ptr_rblocked; 431 PTQ_INIT(&rwlock->ptr_rblocked); 432 } 433 } else 434 #ifdef ERRORCHECK 435 if (rwlock->ptr_nreaders > 0) 436 #endif 437 { 438 /* Releasing a read lock. */ 439 rwlock->ptr_nreaders--; 440 if (rwlock->ptr_nreaders == 0) { 441 writer = PTQ_FIRST(&rwlock->ptr_wblocked); 442 if (writer != NULL) 443 PTQ_REMOVE(&rwlock->ptr_wblocked, writer, 444 pt_sleep); 445 } 446 #ifdef ERRORCHECK 447 } else { 448 pthread_spinunlock(self, &rwlock->ptr_interlock); 449 return EPERM; 450 #endif 451 } 452 453 if (writer != NULL) 454 pthread__sched(self, writer); 455 else 456 pthread__sched_sleepers(self, &blockedq); 457 458 pthread_spinunlock(self, &rwlock->ptr_interlock); 459 460 return 0; 461 } 462 463 464 int 465 pthread_rwlockattr_init(pthread_rwlockattr_t *attr) 466 { 467 #ifdef ERRORCHECK 468 if (attr == NULL) 469 return EINVAL; 470 #endif 471 attr->ptra_magic = _PT_RWLOCKATTR_MAGIC; 472 473 return 0; 474 } 475 476 477 int 478 pthread_rwlockattr_destroy(pthread_rwlockattr_t *attr) 479 { 480 #ifdef ERRORCHECK 481 if ((attr == NULL) || 482 (attr->ptra_magic != _PT_RWLOCKATTR_MAGIC)) 483 return EINVAL; 484 #endif 485 attr->ptra_magic = _PT_RWLOCKATTR_DEAD; 486 487 return 0; 488 } 489