1 1.1 christos /* Locking in multithreaded situations. 2 1.1 christos Copyright (C) 2005-2006 Free Software Foundation, Inc. 3 1.1 christos 4 1.1 christos This program is free software; you can redistribute it and/or modify 5 1.1 christos it under the terms of the GNU General Public License as published by 6 1.1 christos the Free Software Foundation; either version 2, or (at your option) 7 1.1 christos any later version. 8 1.1 christos 9 1.1 christos This program is distributed in the hope that it will be useful, 10 1.1 christos but WITHOUT ANY WARRANTY; without even the implied warranty of 11 1.1 christos MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 1.1 christos GNU General Public License for more details. 13 1.1 christos 14 1.1 christos You should have received a copy of the GNU General Public License 15 1.1 christos along with this program; if not, write to the Free Software Foundation, 16 1.1 christos Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ 17 1.1 christos 18 1.1 christos /* Written by Bruno Haible <bruno (at) clisp.org>, 2005. 19 1.1 christos Based on GCC's gthr-posix.h, gthr-posix95.h, gthr-solaris.h, 20 1.1 christos gthr-win32.h. */ 21 1.1 christos 22 1.1 christos #include <config.h> 23 1.1 christos 24 1.1 christos #include "lock.h" 25 1.1 christos 26 1.1 christos /* ========================================================================= */ 27 1.1 christos 28 1.1 christos #if USE_POSIX_THREADS 29 1.1 christos 30 1.1 christos /* Use the POSIX threads library. */ 31 1.1 christos 32 1.1 christos # if PTHREAD_IN_USE_DETECTION_HARD 33 1.1 christos 34 1.1 christos /* The function to be executed by a dummy thread. */ 35 1.1 christos static void * 36 1.1 christos dummy_thread_func (void *arg) 37 1.1 christos { 38 1.1 christos return arg; 39 1.1 christos } 40 1.1 christos 41 1.1 christos int 42 1.1 christos glthread_in_use (void) 43 1.1 christos { 44 1.1 christos static int tested; 45 1.1 christos static int result; /* 1: linked with -lpthread, 0: only with libc */ 46 1.1 christos 47 1.1 christos if (!tested) 48 1.1 christos { 49 1.1 christos pthread_t thread; 50 1.1 christos 51 1.1 christos if (pthread_create (&thread, NULL, dummy_thread_func, NULL) != 0) 52 1.1 christos /* Thread creation failed. */ 53 1.1 christos result = 0; 54 1.1 christos else 55 1.1 christos { 56 1.1 christos /* Thread creation works. */ 57 1.1 christos void *retval; 58 1.1 christos if (pthread_join (thread, &retval) != 0) 59 1.1 christos abort (); 60 1.1 christos result = 1; 61 1.1 christos } 62 1.1 christos tested = 1; 63 1.1 christos } 64 1.1 christos return result; 65 1.1 christos } 66 1.1 christos 67 1.1 christos # endif 68 1.1 christos 69 1.1 christos /* -------------------------- gl_lock_t datatype -------------------------- */ 70 1.1 christos 71 1.1 christos /* ------------------------- gl_rwlock_t datatype ------------------------- */ 72 1.1 christos 73 1.1 christos # if HAVE_PTHREAD_RWLOCK 74 1.1 christos 75 1.1 christos # if !defined PTHREAD_RWLOCK_INITIALIZER 76 1.1 christos 77 1.1 christos void 78 1.1 christos glthread_rwlock_init (gl_rwlock_t *lock) 79 1.1 christos { 80 1.1 christos if (pthread_rwlock_init (&lock->rwlock, NULL) != 0) 81 1.1 christos abort (); 82 1.1 christos lock->initialized = 1; 83 1.1 christos } 84 1.1 christos 85 1.1 christos void 86 1.1 christos glthread_rwlock_rdlock (gl_rwlock_t *lock) 87 1.1 christos { 88 1.1 christos if (!lock->initialized) 89 1.1 christos { 90 1.1 christos if (pthread_mutex_lock (&lock->guard) != 0) 91 1.1 christos abort (); 92 1.1 christos if (!lock->initialized) 93 1.1 christos glthread_rwlock_init (lock); 94 1.1 christos if (pthread_mutex_unlock (&lock->guard) != 0) 95 1.1 christos abort (); 96 1.1 christos } 97 1.1 christos if (pthread_rwlock_rdlock (&lock->rwlock) != 0) 98 1.1 christos abort (); 99 1.1 christos } 100 1.1 christos 101 1.1 christos void 102 1.1 christos glthread_rwlock_wrlock (gl_rwlock_t *lock) 103 1.1 christos { 104 1.1 christos if (!lock->initialized) 105 1.1 christos { 106 1.1 christos if (pthread_mutex_lock (&lock->guard) != 0) 107 1.1 christos abort (); 108 1.1 christos if (!lock->initialized) 109 1.1 christos glthread_rwlock_init (lock); 110 1.1 christos if (pthread_mutex_unlock (&lock->guard) != 0) 111 1.1 christos abort (); 112 1.1 christos } 113 1.1 christos if (pthread_rwlock_wrlock (&lock->rwlock) != 0) 114 1.1 christos abort (); 115 1.1 christos } 116 1.1 christos 117 1.1 christos void 118 1.1 christos glthread_rwlock_unlock (gl_rwlock_t *lock) 119 1.1 christos { 120 1.1 christos if (!lock->initialized) 121 1.1 christos abort (); 122 1.1 christos if (pthread_rwlock_unlock (&lock->rwlock) != 0) 123 1.1 christos abort (); 124 1.1 christos } 125 1.1 christos 126 1.1 christos void 127 1.1 christos glthread_rwlock_destroy (gl_rwlock_t *lock) 128 1.1 christos { 129 1.1 christos if (!lock->initialized) 130 1.1 christos abort (); 131 1.1 christos if (pthread_rwlock_destroy (&lock->rwlock) != 0) 132 1.1 christos abort (); 133 1.1 christos lock->initialized = 0; 134 1.1 christos } 135 1.1 christos 136 1.1 christos # endif 137 1.1 christos 138 1.1 christos # else 139 1.1 christos 140 1.1 christos void 141 1.1 christos glthread_rwlock_init (gl_rwlock_t *lock) 142 1.1 christos { 143 1.1 christos if (pthread_mutex_init (&lock->lock, NULL) != 0) 144 1.1 christos abort (); 145 1.1 christos if (pthread_cond_init (&lock->waiting_readers, NULL) != 0) 146 1.1 christos abort (); 147 1.1 christos if (pthread_cond_init (&lock->waiting_writers, NULL) != 0) 148 1.1 christos abort (); 149 1.1 christos lock->waiting_writers_count = 0; 150 1.1 christos lock->runcount = 0; 151 1.1 christos } 152 1.1 christos 153 1.1 christos void 154 1.1 christos glthread_rwlock_rdlock (gl_rwlock_t *lock) 155 1.1 christos { 156 1.1 christos if (pthread_mutex_lock (&lock->lock) != 0) 157 1.1 christos abort (); 158 1.1 christos /* Test whether only readers are currently running, and whether the runcount 159 1.1 christos field will not overflow. */ 160 1.1 christos /* POSIX says: "It is implementation-defined whether the calling thread 161 1.1 christos acquires the lock when a writer does not hold the lock and there are 162 1.1 christos writers blocked on the lock." Let's say, no: give the writers a higher 163 1.1 christos priority. */ 164 1.1 christos while (!(lock->runcount + 1 > 0 && lock->waiting_writers_count == 0)) 165 1.1 christos { 166 1.1 christos /* This thread has to wait for a while. Enqueue it among the 167 1.1 christos waiting_readers. */ 168 1.1 christos if (pthread_cond_wait (&lock->waiting_readers, &lock->lock) != 0) 169 1.1 christos abort (); 170 1.1 christos } 171 1.1 christos lock->runcount++; 172 1.1 christos if (pthread_mutex_unlock (&lock->lock) != 0) 173 1.1 christos abort (); 174 1.1 christos } 175 1.1 christos 176 1.1 christos void 177 1.1 christos glthread_rwlock_wrlock (gl_rwlock_t *lock) 178 1.1 christos { 179 1.1 christos if (pthread_mutex_lock (&lock->lock) != 0) 180 1.1 christos abort (); 181 1.1 christos /* Test whether no readers or writers are currently running. */ 182 1.1 christos while (!(lock->runcount == 0)) 183 1.1 christos { 184 1.1 christos /* This thread has to wait for a while. Enqueue it among the 185 1.1 christos waiting_writers. */ 186 1.1 christos lock->waiting_writers_count++; 187 1.1 christos if (pthread_cond_wait (&lock->waiting_writers, &lock->lock) != 0) 188 1.1 christos abort (); 189 1.1 christos lock->waiting_writers_count--; 190 1.1 christos } 191 1.1 christos lock->runcount--; /* runcount becomes -1 */ 192 1.1 christos if (pthread_mutex_unlock (&lock->lock) != 0) 193 1.1 christos abort (); 194 1.1 christos } 195 1.1 christos 196 1.1 christos void 197 1.1 christos glthread_rwlock_unlock (gl_rwlock_t *lock) 198 1.1 christos { 199 1.1 christos if (pthread_mutex_lock (&lock->lock) != 0) 200 1.1 christos abort (); 201 1.1 christos if (lock->runcount < 0) 202 1.1 christos { 203 1.1 christos /* Drop a writer lock. */ 204 1.1 christos if (!(lock->runcount == -1)) 205 1.1 christos abort (); 206 1.1 christos lock->runcount = 0; 207 1.1 christos } 208 1.1 christos else 209 1.1 christos { 210 1.1 christos /* Drop a reader lock. */ 211 1.1 christos if (!(lock->runcount > 0)) 212 1.1 christos abort (); 213 1.1 christos lock->runcount--; 214 1.1 christos } 215 1.1 christos if (lock->runcount == 0) 216 1.1 christos { 217 1.1 christos /* POSIX recommends that "write locks shall take precedence over read 218 1.1 christos locks", to avoid "writer starvation". */ 219 1.1 christos if (lock->waiting_writers_count > 0) 220 1.1 christos { 221 1.1 christos /* Wake up one of the waiting writers. */ 222 1.1 christos if (pthread_cond_signal (&lock->waiting_writers) != 0) 223 1.1 christos abort (); 224 1.1 christos } 225 1.1 christos else 226 1.1 christos { 227 1.1 christos /* Wake up all waiting readers. */ 228 1.1 christos if (pthread_cond_broadcast (&lock->waiting_readers) != 0) 229 1.1 christos abort (); 230 1.1 christos } 231 1.1 christos } 232 1.1 christos if (pthread_mutex_unlock (&lock->lock) != 0) 233 1.1 christos abort (); 234 1.1 christos } 235 1.1 christos 236 1.1 christos void 237 1.1 christos glthread_rwlock_destroy (gl_rwlock_t *lock) 238 1.1 christos { 239 1.1 christos if (pthread_mutex_destroy (&lock->lock) != 0) 240 1.1 christos abort (); 241 1.1 christos if (pthread_cond_destroy (&lock->waiting_readers) != 0) 242 1.1 christos abort (); 243 1.1 christos if (pthread_cond_destroy (&lock->waiting_writers) != 0) 244 1.1 christos abort (); 245 1.1 christos } 246 1.1 christos 247 1.1 christos # endif 248 1.1 christos 249 1.1 christos /* --------------------- gl_recursive_lock_t datatype --------------------- */ 250 1.1 christos 251 1.1 christos # if HAVE_PTHREAD_MUTEX_RECURSIVE 252 1.1 christos 253 1.1 christos # if !(defined PTHREAD_RECURSIVE_MUTEX_INITIALIZER || defined PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP) 254 1.1 christos 255 1.1 christos void 256 1.1 christos glthread_recursive_lock_init (gl_recursive_lock_t *lock) 257 1.1 christos { 258 1.1 christos pthread_mutexattr_t attributes; 259 1.1 christos 260 1.1 christos if (pthread_mutexattr_init (&attributes) != 0) 261 1.1 christos abort (); 262 1.1 christos if (pthread_mutexattr_settype (&attributes, PTHREAD_MUTEX_RECURSIVE) != 0) 263 1.1 christos abort (); 264 1.1 christos if (pthread_mutex_init (&lock->recmutex, &attributes) != 0) 265 1.1 christos abort (); 266 1.1 christos if (pthread_mutexattr_destroy (&attributes) != 0) 267 1.1 christos abort (); 268 1.1 christos lock->initialized = 1; 269 1.1 christos } 270 1.1 christos 271 1.1 christos void 272 1.1 christos glthread_recursive_lock_lock (gl_recursive_lock_t *lock) 273 1.1 christos { 274 1.1 christos if (!lock->initialized) 275 1.1 christos { 276 1.1 christos if (pthread_mutex_lock (&lock->guard) != 0) 277 1.1 christos abort (); 278 1.1 christos if (!lock->initialized) 279 1.1 christos glthread_recursive_lock_init (lock); 280 1.1 christos if (pthread_mutex_unlock (&lock->guard) != 0) 281 1.1 christos abort (); 282 1.1 christos } 283 1.1 christos if (pthread_mutex_lock (&lock->recmutex) != 0) 284 1.1 christos abort (); 285 1.1 christos } 286 1.1 christos 287 1.1 christos void 288 1.1 christos glthread_recursive_lock_unlock (gl_recursive_lock_t *lock) 289 1.1 christos { 290 1.1 christos if (!lock->initialized) 291 1.1 christos abort (); 292 1.1 christos if (pthread_mutex_unlock (&lock->recmutex) != 0) 293 1.1 christos abort (); 294 1.1 christos } 295 1.1 christos 296 1.1 christos void 297 1.1 christos glthread_recursive_lock_destroy (gl_recursive_lock_t *lock) 298 1.1 christos { 299 1.1 christos if (!lock->initialized) 300 1.1 christos abort (); 301 1.1 christos if (pthread_mutex_destroy (&lock->recmutex) != 0) 302 1.1 christos abort (); 303 1.1 christos lock->initialized = 0; 304 1.1 christos } 305 1.1 christos 306 1.1 christos # endif 307 1.1 christos 308 1.1 christos # else 309 1.1 christos 310 1.1 christos void 311 1.1 christos glthread_recursive_lock_init (gl_recursive_lock_t *lock) 312 1.1 christos { 313 1.1 christos if (pthread_mutex_init (&lock->mutex, NULL) != 0) 314 1.1 christos abort (); 315 1.1 christos lock->owner = (pthread_t) 0; 316 1.1 christos lock->depth = 0; 317 1.1 christos } 318 1.1 christos 319 1.1 christos void 320 1.1 christos glthread_recursive_lock_lock (gl_recursive_lock_t *lock) 321 1.1 christos { 322 1.1 christos pthread_t self = pthread_self (); 323 1.1 christos if (lock->owner != self) 324 1.1 christos { 325 1.1 christos if (pthread_mutex_lock (&lock->mutex) != 0) 326 1.1 christos abort (); 327 1.1 christos lock->owner = self; 328 1.1 christos } 329 1.1 christos if (++(lock->depth) == 0) /* wraparound? */ 330 1.1 christos abort (); 331 1.1 christos } 332 1.1 christos 333 1.1 christos void 334 1.1 christos glthread_recursive_lock_unlock (gl_recursive_lock_t *lock) 335 1.1 christos { 336 1.1 christos if (lock->owner != pthread_self ()) 337 1.1 christos abort (); 338 1.1 christos if (lock->depth == 0) 339 1.1 christos abort (); 340 1.1 christos if (--(lock->depth) == 0) 341 1.1 christos { 342 1.1 christos lock->owner = (pthread_t) 0; 343 1.1 christos if (pthread_mutex_unlock (&lock->mutex) != 0) 344 1.1 christos abort (); 345 1.1 christos } 346 1.1 christos } 347 1.1 christos 348 1.1 christos void 349 1.1 christos glthread_recursive_lock_destroy (gl_recursive_lock_t *lock) 350 1.1 christos { 351 1.1 christos if (lock->owner != (pthread_t) 0) 352 1.1 christos abort (); 353 1.1 christos if (pthread_mutex_destroy (&lock->mutex) != 0) 354 1.1 christos abort (); 355 1.1 christos } 356 1.1 christos 357 1.1 christos # endif 358 1.1 christos 359 1.1 christos /* -------------------------- gl_once_t datatype -------------------------- */ 360 1.1 christos 361 1.1 christos static const pthread_once_t fresh_once = PTHREAD_ONCE_INIT; 362 1.1 christos 363 1.1 christos int 364 1.1 christos glthread_once_singlethreaded (pthread_once_t *once_control) 365 1.1 christos { 366 1.1 christos /* We don't know whether pthread_once_t is an integer type, a floating-point 367 1.1 christos type, a pointer type, or a structure type. */ 368 1.1 christos char *firstbyte = (char *)once_control; 369 1.1 christos if (*firstbyte == *(const char *)&fresh_once) 370 1.1 christos { 371 1.1 christos /* First time use of once_control. Invert the first byte. */ 372 1.1 christos *firstbyte = ~ *(const char *)&fresh_once; 373 1.1 christos return 1; 374 1.1 christos } 375 1.1 christos else 376 1.1 christos return 0; 377 1.1 christos } 378 1.1 christos 379 1.1 christos #endif 380 1.1 christos 381 1.1 christos /* ========================================================================= */ 382 1.1 christos 383 1.1 christos #if USE_PTH_THREADS 384 1.1 christos 385 1.1 christos /* Use the GNU Pth threads library. */ 386 1.1 christos 387 1.1 christos /* -------------------------- gl_lock_t datatype -------------------------- */ 388 1.1 christos 389 1.1 christos /* ------------------------- gl_rwlock_t datatype ------------------------- */ 390 1.1 christos 391 1.1 christos /* --------------------- gl_recursive_lock_t datatype --------------------- */ 392 1.1 christos 393 1.1 christos /* -------------------------- gl_once_t datatype -------------------------- */ 394 1.1 christos 395 1.1 christos void 396 1.1 christos glthread_once_call (void *arg) 397 1.1 christos { 398 1.1 christos void (**gl_once_temp_addr) (void) = (void (**) (void)) arg; 399 1.1 christos void (*initfunction) (void) = *gl_once_temp_addr; 400 1.1 christos initfunction (); 401 1.1 christos } 402 1.1 christos 403 1.1 christos int 404 1.1 christos glthread_once_singlethreaded (pth_once_t *once_control) 405 1.1 christos { 406 1.1 christos /* We know that pth_once_t is an integer type. */ 407 1.1 christos if (*once_control == PTH_ONCE_INIT) 408 1.1 christos { 409 1.1 christos /* First time use of once_control. Invert the marker. */ 410 1.1 christos *once_control = ~ PTH_ONCE_INIT; 411 1.1 christos return 1; 412 1.1 christos } 413 1.1 christos else 414 1.1 christos return 0; 415 1.1 christos } 416 1.1 christos 417 1.1 christos #endif 418 1.1 christos 419 1.1 christos /* ========================================================================= */ 420 1.1 christos 421 1.1 christos #if USE_SOLARIS_THREADS 422 1.1 christos 423 1.1 christos /* Use the old Solaris threads library. */ 424 1.1 christos 425 1.1 christos /* -------------------------- gl_lock_t datatype -------------------------- */ 426 1.1 christos 427 1.1 christos /* ------------------------- gl_rwlock_t datatype ------------------------- */ 428 1.1 christos 429 1.1 christos /* --------------------- gl_recursive_lock_t datatype --------------------- */ 430 1.1 christos 431 1.1 christos void 432 1.1 christos glthread_recursive_lock_init (gl_recursive_lock_t *lock) 433 1.1 christos { 434 1.1 christos if (mutex_init (&lock->mutex, USYNC_THREAD, NULL) != 0) 435 1.1 christos abort (); 436 1.1 christos lock->owner = (thread_t) 0; 437 1.1 christos lock->depth = 0; 438 1.1 christos } 439 1.1 christos 440 1.1 christos void 441 1.1 christos glthread_recursive_lock_lock (gl_recursive_lock_t *lock) 442 1.1 christos { 443 1.1 christos thread_t self = thr_self (); 444 1.1 christos if (lock->owner != self) 445 1.1 christos { 446 1.1 christos if (mutex_lock (&lock->mutex) != 0) 447 1.1 christos abort (); 448 1.1 christos lock->owner = self; 449 1.1 christos } 450 1.1 christos if (++(lock->depth) == 0) /* wraparound? */ 451 1.1 christos abort (); 452 1.1 christos } 453 1.1 christos 454 1.1 christos void 455 1.1 christos glthread_recursive_lock_unlock (gl_recursive_lock_t *lock) 456 1.1 christos { 457 1.1 christos if (lock->owner != thr_self ()) 458 1.1 christos abort (); 459 1.1 christos if (lock->depth == 0) 460 1.1 christos abort (); 461 1.1 christos if (--(lock->depth) == 0) 462 1.1 christos { 463 1.1 christos lock->owner = (thread_t) 0; 464 1.1 christos if (mutex_unlock (&lock->mutex) != 0) 465 1.1 christos abort (); 466 1.1 christos } 467 1.1 christos } 468 1.1 christos 469 1.1 christos void 470 1.1 christos glthread_recursive_lock_destroy (gl_recursive_lock_t *lock) 471 1.1 christos { 472 1.1 christos if (lock->owner != (thread_t) 0) 473 1.1 christos abort (); 474 1.1 christos if (mutex_destroy (&lock->mutex) != 0) 475 1.1 christos abort (); 476 1.1 christos } 477 1.1 christos 478 1.1 christos /* -------------------------- gl_once_t datatype -------------------------- */ 479 1.1 christos 480 1.1 christos void 481 1.1 christos glthread_once (gl_once_t *once_control, void (*initfunction) (void)) 482 1.1 christos { 483 1.1 christos if (!once_control->inited) 484 1.1 christos { 485 1.1 christos /* Use the mutex to guarantee that if another thread is already calling 486 1.1 christos the initfunction, this thread waits until it's finished. */ 487 1.1 christos if (mutex_lock (&once_control->mutex) != 0) 488 1.1 christos abort (); 489 1.1 christos if (!once_control->inited) 490 1.1 christos { 491 1.1 christos once_control->inited = 1; 492 1.1 christos initfunction (); 493 1.1 christos } 494 1.1 christos if (mutex_unlock (&once_control->mutex) != 0) 495 1.1 christos abort (); 496 1.1 christos } 497 1.1 christos } 498 1.1 christos 499 1.1 christos int 500 1.1 christos glthread_once_singlethreaded (gl_once_t *once_control) 501 1.1 christos { 502 1.1 christos /* We know that gl_once_t contains an integer type. */ 503 1.1 christos if (!once_control->inited) 504 1.1 christos { 505 1.1 christos /* First time use of once_control. Invert the marker. */ 506 1.1 christos once_control->inited = ~ 0; 507 1.1 christos return 1; 508 1.1 christos } 509 1.1 christos else 510 1.1 christos return 0; 511 1.1 christos } 512 1.1 christos 513 1.1 christos #endif 514 1.1 christos 515 1.1 christos /* ========================================================================= */ 516 1.1 christos 517 1.1 christos #if USE_WIN32_THREADS 518 1.1 christos 519 1.1 christos /* -------------------------- gl_lock_t datatype -------------------------- */ 520 1.1 christos 521 1.1 christos void 522 1.1 christos glthread_lock_init (gl_lock_t *lock) 523 1.1 christos { 524 1.1 christos InitializeCriticalSection (&lock->lock); 525 1.1 christos lock->guard.done = 1; 526 1.1 christos } 527 1.1 christos 528 1.1 christos void 529 1.1 christos glthread_lock_lock (gl_lock_t *lock) 530 1.1 christos { 531 1.1 christos if (!lock->guard.done) 532 1.1 christos { 533 1.1 christos if (InterlockedIncrement (&lock->guard.started) == 0) 534 1.1 christos /* This thread is the first one to need this lock. Initialize it. */ 535 1.1 christos glthread_lock_init (lock); 536 1.1 christos else 537 1.1 christos /* Yield the CPU while waiting for another thread to finish 538 1.1 christos initializing this lock. */ 539 1.1 christos while (!lock->guard.done) 540 1.1 christos Sleep (0); 541 1.1 christos } 542 1.1 christos EnterCriticalSection (&lock->lock); 543 1.1 christos } 544 1.1 christos 545 1.1 christos void 546 1.1 christos glthread_lock_unlock (gl_lock_t *lock) 547 1.1 christos { 548 1.1 christos if (!lock->guard.done) 549 1.1 christos abort (); 550 1.1 christos LeaveCriticalSection (&lock->lock); 551 1.1 christos } 552 1.1 christos 553 1.1 christos void 554 1.1 christos glthread_lock_destroy (gl_lock_t *lock) 555 1.1 christos { 556 1.1 christos if (!lock->guard.done) 557 1.1 christos abort (); 558 1.1 christos DeleteCriticalSection (&lock->lock); 559 1.1 christos lock->guard.done = 0; 560 1.1 christos } 561 1.1 christos 562 1.1 christos /* ------------------------- gl_rwlock_t datatype ------------------------- */ 563 1.1 christos 564 1.1 christos static inline void 565 1.1 christos gl_waitqueue_init (gl_waitqueue_t *wq) 566 1.1 christos { 567 1.1 christos wq->array = NULL; 568 1.1 christos wq->count = 0; 569 1.1 christos wq->alloc = 0; 570 1.1 christos wq->offset = 0; 571 1.1 christos } 572 1.1 christos 573 1.1 christos /* Enqueues the current thread, represented by an event, in a wait queue. 574 1.1 christos Returns INVALID_HANDLE_VALUE if an allocation failure occurs. */ 575 1.1 christos static HANDLE 576 1.1 christos gl_waitqueue_add (gl_waitqueue_t *wq) 577 1.1 christos { 578 1.1 christos HANDLE event; 579 1.1 christos unsigned int index; 580 1.1 christos 581 1.1 christos if (wq->count == wq->alloc) 582 1.1 christos { 583 1.1 christos unsigned int new_alloc = 2 * wq->alloc + 1; 584 1.1 christos HANDLE *new_array = 585 1.1 christos (HANDLE *) realloc (wq->array, new_alloc * sizeof (HANDLE)); 586 1.1 christos if (new_array == NULL) 587 1.1 christos /* No more memory. */ 588 1.1 christos return INVALID_HANDLE_VALUE; 589 1.1 christos /* Now is a good opportunity to rotate the array so that its contents 590 1.1 christos starts at offset 0. */ 591 1.1 christos if (wq->offset > 0) 592 1.1 christos { 593 1.1 christos unsigned int old_count = wq->count; 594 1.1 christos unsigned int old_alloc = wq->alloc; 595 1.1 christos unsigned int old_offset = wq->offset; 596 1.1 christos unsigned int i; 597 1.1 christos if (old_offset + old_count > old_alloc) 598 1.1 christos { 599 1.1 christos unsigned int limit = old_offset + old_count - old_alloc; 600 1.1 christos for (i = 0; i < limit; i++) 601 1.1 christos new_array[old_alloc + i] = new_array[i]; 602 1.1 christos } 603 1.1 christos for (i = 0; i < old_count; i++) 604 1.1 christos new_array[i] = new_array[old_offset + i]; 605 1.1 christos wq->offset = 0; 606 1.1 christos } 607 1.1 christos wq->array = new_array; 608 1.1 christos wq->alloc = new_alloc; 609 1.1 christos } 610 1.1 christos event = CreateEvent (NULL, TRUE, FALSE, NULL); 611 1.1 christos if (event == INVALID_HANDLE_VALUE) 612 1.1 christos /* No way to allocate an event. */ 613 1.1 christos return INVALID_HANDLE_VALUE; 614 1.1 christos index = wq->offset + wq->count; 615 1.1 christos if (index >= wq->alloc) 616 1.1 christos index -= wq->alloc; 617 1.1 christos wq->array[index] = event; 618 1.1 christos wq->count++; 619 1.1 christos return event; 620 1.1 christos } 621 1.1 christos 622 1.1 christos /* Notifies the first thread from a wait queue and dequeues it. */ 623 1.1 christos static inline void 624 1.1 christos gl_waitqueue_notify_first (gl_waitqueue_t *wq) 625 1.1 christos { 626 1.1 christos SetEvent (wq->array[wq->offset + 0]); 627 1.1 christos wq->offset++; 628 1.1 christos wq->count--; 629 1.1 christos if (wq->count == 0 || wq->offset == wq->alloc) 630 1.1 christos wq->offset = 0; 631 1.1 christos } 632 1.1 christos 633 1.1 christos /* Notifies all threads from a wait queue and dequeues them all. */ 634 1.1 christos static inline void 635 1.1 christos gl_waitqueue_notify_all (gl_waitqueue_t *wq) 636 1.1 christos { 637 1.1 christos unsigned int i; 638 1.1 christos 639 1.1 christos for (i = 0; i < wq->count; i++) 640 1.1 christos { 641 1.1 christos unsigned int index = wq->offset + i; 642 1.1 christos if (index >= wq->alloc) 643 1.1 christos index -= wq->alloc; 644 1.1 christos SetEvent (wq->array[index]); 645 1.1 christos } 646 1.1 christos wq->count = 0; 647 1.1 christos wq->offset = 0; 648 1.1 christos } 649 1.1 christos 650 1.1 christos void 651 1.1 christos glthread_rwlock_init (gl_rwlock_t *lock) 652 1.1 christos { 653 1.1 christos InitializeCriticalSection (&lock->lock); 654 1.1 christos gl_waitqueue_init (&lock->waiting_readers); 655 1.1 christos gl_waitqueue_init (&lock->waiting_writers); 656 1.1 christos lock->runcount = 0; 657 1.1 christos lock->guard.done = 1; 658 1.1 christos } 659 1.1 christos 660 1.1 christos void 661 1.1 christos glthread_rwlock_rdlock (gl_rwlock_t *lock) 662 1.1 christos { 663 1.1 christos if (!lock->guard.done) 664 1.1 christos { 665 1.1 christos if (InterlockedIncrement (&lock->guard.started) == 0) 666 1.1 christos /* This thread is the first one to need this lock. Initialize it. */ 667 1.1 christos glthread_rwlock_init (lock); 668 1.1 christos else 669 1.1 christos /* Yield the CPU while waiting for another thread to finish 670 1.1 christos initializing this lock. */ 671 1.1 christos while (!lock->guard.done) 672 1.1 christos Sleep (0); 673 1.1 christos } 674 1.1 christos EnterCriticalSection (&lock->lock); 675 1.1 christos /* Test whether only readers are currently running, and whether the runcount 676 1.1 christos field will not overflow. */ 677 1.1 christos if (!(lock->runcount + 1 > 0)) 678 1.1 christos { 679 1.1 christos /* This thread has to wait for a while. Enqueue it among the 680 1.1 christos waiting_readers. */ 681 1.1 christos HANDLE event = gl_waitqueue_add (&lock->waiting_readers); 682 1.1 christos if (event != INVALID_HANDLE_VALUE) 683 1.1 christos { 684 1.1 christos DWORD result; 685 1.1 christos LeaveCriticalSection (&lock->lock); 686 1.1 christos /* Wait until another thread signals this event. */ 687 1.1 christos result = WaitForSingleObject (event, INFINITE); 688 1.1 christos if (result == WAIT_FAILED || result == WAIT_TIMEOUT) 689 1.1 christos abort (); 690 1.1 christos CloseHandle (event); 691 1.1 christos /* The thread which signalled the event already did the bookkeeping: 692 1.1 christos removed us from the waiting_readers, incremented lock->runcount. */ 693 1.1 christos if (!(lock->runcount > 0)) 694 1.1 christos abort (); 695 1.1 christos return; 696 1.1 christos } 697 1.1 christos else 698 1.1 christos { 699 1.1 christos /* Allocation failure. Weird. */ 700 1.1 christos do 701 1.1 christos { 702 1.1 christos LeaveCriticalSection (&lock->lock); 703 1.1 christos Sleep (1); 704 1.1 christos EnterCriticalSection (&lock->lock); 705 1.1 christos } 706 1.1 christos while (!(lock->runcount + 1 > 0)); 707 1.1 christos } 708 1.1 christos } 709 1.1 christos lock->runcount++; 710 1.1 christos LeaveCriticalSection (&lock->lock); 711 1.1 christos } 712 1.1 christos 713 1.1 christos void 714 1.1 christos glthread_rwlock_wrlock (gl_rwlock_t *lock) 715 1.1 christos { 716 1.1 christos if (!lock->guard.done) 717 1.1 christos { 718 1.1 christos if (InterlockedIncrement (&lock->guard.started) == 0) 719 1.1 christos /* This thread is the first one to need this lock. Initialize it. */ 720 1.1 christos glthread_rwlock_init (lock); 721 1.1 christos else 722 1.1 christos /* Yield the CPU while waiting for another thread to finish 723 1.1 christos initializing this lock. */ 724 1.1 christos while (!lock->guard.done) 725 1.1 christos Sleep (0); 726 1.1 christos } 727 1.1 christos EnterCriticalSection (&lock->lock); 728 1.1 christos /* Test whether no readers or writers are currently running. */ 729 1.1 christos if (!(lock->runcount == 0)) 730 1.1 christos { 731 1.1 christos /* This thread has to wait for a while. Enqueue it among the 732 1.1 christos waiting_writers. */ 733 1.1 christos HANDLE event = gl_waitqueue_add (&lock->waiting_writers); 734 1.1 christos if (event != INVALID_HANDLE_VALUE) 735 1.1 christos { 736 1.1 christos DWORD result; 737 1.1 christos LeaveCriticalSection (&lock->lock); 738 1.1 christos /* Wait until another thread signals this event. */ 739 1.1 christos result = WaitForSingleObject (event, INFINITE); 740 1.1 christos if (result == WAIT_FAILED || result == WAIT_TIMEOUT) 741 1.1 christos abort (); 742 1.1 christos CloseHandle (event); 743 1.1 christos /* The thread which signalled the event already did the bookkeeping: 744 1.1 christos removed us from the waiting_writers, set lock->runcount = -1. */ 745 1.1 christos if (!(lock->runcount == -1)) 746 1.1 christos abort (); 747 1.1 christos return; 748 1.1 christos } 749 1.1 christos else 750 1.1 christos { 751 1.1 christos /* Allocation failure. Weird. */ 752 1.1 christos do 753 1.1 christos { 754 1.1 christos LeaveCriticalSection (&lock->lock); 755 1.1 christos Sleep (1); 756 1.1 christos EnterCriticalSection (&lock->lock); 757 1.1 christos } 758 1.1 christos while (!(lock->runcount == 0)); 759 1.1 christos } 760 1.1 christos } 761 1.1 christos lock->runcount--; /* runcount becomes -1 */ 762 1.1 christos LeaveCriticalSection (&lock->lock); 763 1.1 christos } 764 1.1 christos 765 1.1 christos void 766 1.1 christos glthread_rwlock_unlock (gl_rwlock_t *lock) 767 1.1 christos { 768 1.1 christos if (!lock->guard.done) 769 1.1 christos abort (); 770 1.1 christos EnterCriticalSection (&lock->lock); 771 1.1 christos if (lock->runcount < 0) 772 1.1 christos { 773 1.1 christos /* Drop a writer lock. */ 774 1.1 christos if (!(lock->runcount == -1)) 775 1.1 christos abort (); 776 1.1 christos lock->runcount = 0; 777 1.1 christos } 778 1.1 christos else 779 1.1 christos { 780 1.1 christos /* Drop a reader lock. */ 781 1.1 christos if (!(lock->runcount > 0)) 782 1.1 christos abort (); 783 1.1 christos lock->runcount--; 784 1.1 christos } 785 1.1 christos if (lock->runcount == 0) 786 1.1 christos { 787 1.1 christos /* POSIX recommends that "write locks shall take precedence over read 788 1.1 christos locks", to avoid "writer starvation". */ 789 1.1 christos if (lock->waiting_writers.count > 0) 790 1.1 christos { 791 1.1 christos /* Wake up one of the waiting writers. */ 792 1.1 christos lock->runcount--; 793 1.1 christos gl_waitqueue_notify_first (&lock->waiting_writers); 794 1.1 christos } 795 1.1 christos else 796 1.1 christos { 797 1.1 christos /* Wake up all waiting readers. */ 798 1.1 christos lock->runcount += lock->waiting_readers.count; 799 1.1 christos gl_waitqueue_notify_all (&lock->waiting_readers); 800 1.1 christos } 801 1.1 christos } 802 1.1 christos LeaveCriticalSection (&lock->lock); 803 1.1 christos } 804 1.1 christos 805 1.1 christos void 806 1.1 christos glthread_rwlock_destroy (gl_rwlock_t *lock) 807 1.1 christos { 808 1.1 christos if (!lock->guard.done) 809 1.1 christos abort (); 810 1.1 christos if (lock->runcount != 0) 811 1.1 christos abort (); 812 1.1 christos DeleteCriticalSection (&lock->lock); 813 1.1 christos if (lock->waiting_readers.array != NULL) 814 1.1 christos free (lock->waiting_readers.array); 815 1.1 christos if (lock->waiting_writers.array != NULL) 816 1.1 christos free (lock->waiting_writers.array); 817 1.1 christos lock->guard.done = 0; 818 1.1 christos } 819 1.1 christos 820 1.1 christos /* --------------------- gl_recursive_lock_t datatype --------------------- */ 821 1.1 christos 822 1.1 christos void 823 1.1 christos glthread_recursive_lock_init (gl_recursive_lock_t *lock) 824 1.1 christos { 825 1.1 christos lock->owner = 0; 826 1.1 christos lock->depth = 0; 827 1.1 christos InitializeCriticalSection (&lock->lock); 828 1.1 christos lock->guard.done = 1; 829 1.1 christos } 830 1.1 christos 831 1.1 christos void 832 1.1 christos glthread_recursive_lock_lock (gl_recursive_lock_t *lock) 833 1.1 christos { 834 1.1 christos if (!lock->guard.done) 835 1.1 christos { 836 1.1 christos if (InterlockedIncrement (&lock->guard.started) == 0) 837 1.1 christos /* This thread is the first one to need this lock. Initialize it. */ 838 1.1 christos glthread_recursive_lock_init (lock); 839 1.1 christos else 840 1.1 christos /* Yield the CPU while waiting for another thread to finish 841 1.1 christos initializing this lock. */ 842 1.1 christos while (!lock->guard.done) 843 1.1 christos Sleep (0); 844 1.1 christos } 845 1.1 christos { 846 1.1 christos DWORD self = GetCurrentThreadId (); 847 1.1 christos if (lock->owner != self) 848 1.1 christos { 849 1.1 christos EnterCriticalSection (&lock->lock); 850 1.1 christos lock->owner = self; 851 1.1 christos } 852 1.1 christos if (++(lock->depth) == 0) /* wraparound? */ 853 1.1 christos abort (); 854 1.1 christos } 855 1.1 christos } 856 1.1 christos 857 1.1 christos void 858 1.1 christos glthread_recursive_lock_unlock (gl_recursive_lock_t *lock) 859 1.1 christos { 860 1.1 christos if (lock->owner != GetCurrentThreadId ()) 861 1.1 christos abort (); 862 1.1 christos if (lock->depth == 0) 863 1.1 christos abort (); 864 1.1 christos if (--(lock->depth) == 0) 865 1.1 christos { 866 1.1 christos lock->owner = 0; 867 1.1 christos LeaveCriticalSection (&lock->lock); 868 1.1 christos } 869 1.1 christos } 870 1.1 christos 871 1.1 christos void 872 1.1 christos glthread_recursive_lock_destroy (gl_recursive_lock_t *lock) 873 1.1 christos { 874 1.1 christos if (lock->owner != 0) 875 1.1 christos abort (); 876 1.1 christos DeleteCriticalSection (&lock->lock); 877 1.1 christos lock->guard.done = 0; 878 1.1 christos } 879 1.1 christos 880 1.1 christos /* -------------------------- gl_once_t datatype -------------------------- */ 881 1.1 christos 882 1.1 christos void 883 1.1 christos glthread_once (gl_once_t *once_control, void (*initfunction) (void)) 884 1.1 christos { 885 1.1 christos if (once_control->inited <= 0) 886 1.1 christos { 887 1.1 christos if (InterlockedIncrement (&once_control->started) == 0) 888 1.1 christos { 889 1.1 christos /* This thread is the first one to come to this once_control. */ 890 1.1 christos InitializeCriticalSection (&once_control->lock); 891 1.1 christos EnterCriticalSection (&once_control->lock); 892 1.1 christos once_control->inited = 0; 893 1.1 christos initfunction (); 894 1.1 christos once_control->inited = 1; 895 1.1 christos LeaveCriticalSection (&once_control->lock); 896 1.1 christos } 897 1.1 christos else 898 1.1 christos { 899 1.1 christos /* Undo last operation. */ 900 1.1 christos InterlockedDecrement (&once_control->started); 901 1.1 christos /* Some other thread has already started the initialization. 902 1.1 christos Yield the CPU while waiting for the other thread to finish 903 1.1 christos initializing and taking the lock. */ 904 1.1 christos while (once_control->inited < 0) 905 1.1 christos Sleep (0); 906 1.1 christos if (once_control->inited <= 0) 907 1.1 christos { 908 1.1 christos /* Take the lock. This blocks until the other thread has 909 1.1 christos finished calling the initfunction. */ 910 1.1 christos EnterCriticalSection (&once_control->lock); 911 1.1 christos LeaveCriticalSection (&once_control->lock); 912 1.1 christos if (!(once_control->inited > 0)) 913 1.1 christos abort (); 914 1.1 christos } 915 1.1 christos } 916 1.1 christos } 917 1.1 christos } 918 1.1 christos 919 1.1 christos #endif 920 1.1 christos 921 1.1 christos /* ========================================================================= */ 922