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