1 /* 2 * Copyright (c) 2012-2014, Bruno Levy 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are met: 7 * 8 * * Redistributions of source code must retain the above copyright notice, 9 * this list of conditions and the following disclaimer. 10 * * Redistributions in binary form must reproduce the above copyright notice, 11 * this list of conditions and the following disclaimer in the documentation 12 * and/or other materials provided with the distribution. 13 * * Neither the name of the ALICE Project-Team nor the names of its 14 * contributors may be used to endorse or promote products derived from this 15 * software without specific prior written permission. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 21 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 * POSSIBILITY OF SUCH DAMAGE. 28 * 29 * If you modify this software, you should include a notice giving the 30 * name of the person performing the modification, the date of modification, 31 * and the reason for such modification. 32 * 33 * Contact: Bruno Levy 34 * 35 * Bruno.Levy@inria.fr 36 * http://www.loria.fr/~levy 37 * 38 * ALICE Project 39 * LORIA, INRIA Lorraine, 40 * Campus Scientifique, BP 239 41 * 54506 VANDOEUVRE LES NANCY CEDEX 42 * FRANCE 43 * 44 */ 45 46 #ifndef GEOGRAM_BASIC_THREAD_SYNC 47 #define GEOGRAM_BASIC_THREAD_SYNC 48 49 #include <geogram/basic/common.h> 50 #include <geogram/basic/atomics.h> 51 #include <geogram/basic/numeric.h> 52 #include <geogram/basic/assert.h> 53 #include <geogram/basic/argused.h> 54 #include <vector> 55 56 #ifdef GEO_OS_APPLE 57 # define GEO_USE_DEFAULT_SPINLOCK_ARRAY 58 # include <AvailabilityMacros.h> 59 # if defined(MAC_OS_X_VERSION_10_12) && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_12 60 # include <os/lock.h> 61 # endif 62 #endif 63 64 #ifdef geo_debug_assert 65 #define geo_thread_sync_assert(x) geo_debug_assert(x) 66 #else 67 #define geo_thread_sync_assert(x) 68 #endif 69 70 /** 71 * \file geogram/basic/thread_sync.h 72 * \brief Functions and classes for process manipulation 73 */ 74 75 namespace GEO { 76 77 namespace Process { 78 79 #if defined(GEO_OS_RASPBERRY) 80 81 /** A lightweight synchronization structure. */ 82 typedef arm32_mutex_t spinlock; 83 84 /** The initialization value of a spin lock. */ 85 # define GEOGRAM_SPINLOCK_INIT 0 86 /** 87 * \brief Loops until \p x is available then reserves it. 88 * \param[in] x a spinlock that should be available. 89 */ acquire_spinlock(spinlock & x)90 inline void acquire_spinlock(spinlock& x) { 91 lock_mutex_arm32(&x); 92 } 93 94 /** 95 * \brief Makes \p x available to other threads. 96 * \param[in] x a spinlock that should be reserved. 97 */ release_spinlock(spinlock & x)98 inline void release_spinlock(spinlock& x) { 99 unlock_mutex_arm32(&x); 100 } 101 102 #elif defined(GEO_OS_ANDROID) 103 104 /** A lightweight synchronization structure. */ 105 typedef android_mutex_t spinlock; 106 107 /** The initialization value of a spin lock. */ 108 # define GEOGRAM_SPINLOCK_INIT 0 109 /** 110 * \brief Loops until \p x is available then reserves it. 111 * \param[in] x a spinlock that should be available. 112 */ 113 inline void acquire_spinlock(spinlock& x) { 114 lock_mutex_android(&x); 115 } 116 117 /** 118 * \brief Makes \p x available to other threads. 119 * \param[in] x a spinlock that should be reserved. 120 */ 121 inline void release_spinlock(spinlock& x) { 122 unlock_mutex_android(&x); 123 } 124 125 #elif defined(GEO_OS_LINUX) || defined(GEO_OS_DRAGONFLY) || defined(GEO_COMPILER_MINGW) 126 127 /** A lightweight synchronization structure. */ 128 typedef unsigned char spinlock; 129 130 /** The initialization value of a spin lock. */ 131 # define GEOGRAM_SPINLOCK_INIT 0 132 /** 133 * \brief Loops until \p x is available then reserve it. 134 * \param[in] x a spinlock that should be available. 135 */ 136 inline void acquire_spinlock(volatile spinlock& x) { 137 while(__sync_lock_test_and_set(&x, 1) == 1) { 138 // Intel recommends to have a PAUSE asm instruction 139 // in the spinlock loop. 140 geo_pause(); 141 } 142 } 143 144 /** 145 * \brief Makes \p x available to other threads. 146 * \param[in] x a spinlock that should be reserved. 147 */ 148 inline void release_spinlock(volatile spinlock& x) { 149 // Note: Since on intel processor, memory writes 150 // (of data types <= bus size) are atomic, we could 151 // simply write 'x=0' instead, but this would 152 // lack the 'memory barrier with release semantics' 153 // required to avoid compiler and/or processor 154 // reordering (important for relaxed memory 155 // models such as Itanium processors). 156 __sync_lock_release(&x); 157 } 158 159 #elif defined(GEO_OS_APPLE) 160 161 #if defined(MAC_OS_X_VERSION_10_12) && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_12 162 /** A lightweight synchronization structure. */ 163 typedef os_unfair_lock spinlock; 164 165 /** The initialization value of a spin lock. */ 166 # define GEOGRAM_SPINLOCK_INIT OS_UNFAIR_LOCK_INIT 167 //inline void init_spinlock(spinlock & s) { s = OS_UNFAIR_LOCK_INIT; } 168 //inline bool try_acquire_spinlock (spinlock & s) { return os_unfair_lock_trylock(&s); } 169 inline void acquire_spinlock (spinlock & s) { os_unfair_lock_lock(&s); } 170 inline void release_spinlock (spinlock & s) { os_unfair_lock_unlock(&s); } 171 #else 172 /** A lightweight synchronization structure. */ 173 typedef OSSpinLock spinlock; 174 175 /** The initialization value of a spin lock. */ 176 # define GEOGRAM_SPINLOCK_INIT OS_SPINLOCK_INIT 177 inline void acquire_spinlock(volatile spinlock& x) { 178 OSSpinLockLock(&x); 179 } 180 181 inline void release_spinlock(volatile spinlock& x) { 182 OSSpinLockUnlock(&x); 183 } 184 #endif // __MAC_10_12 185 186 #elif defined(GEO_OS_WINDOWS) && !defined(GEO_COMPILER_MINGW) 187 188 /** A lightweight synchronization structure. */ 189 typedef short spinlock; 190 191 /** The initialization value of a spin lock. */ 192 # define GEOGRAM_SPINLOCK_INIT 0 193 inline void acquire_spinlock(volatile spinlock& x) { 194 while(_InterlockedCompareExchange16(&x, 1, 0) == 1) { 195 // Intel recommends to have a PAUSE asm instruction 196 // in the spinlock loop. Under MSVC/Windows, 197 // YieldProcessor() is a macro that calls the 198 // (undocumented) _mm_pause() intrinsic function 199 // that generates a PAUSE opcode. 200 YieldProcessor(); 201 } 202 // We do not need _ReadBarrier() here since 203 // _InterlockedCompareExchange16 204 // "acts as a full barrier in VC2005" according to the doc 205 } 206 207 inline void release_spinlock(volatile spinlock& x) { 208 _WriteBarrier(); // prevents compiler reordering 209 x = 0; 210 } 211 212 #endif 213 214 #if defined(GEO_USE_DEFAULT_SPINLOCK_ARRAY) 215 216 // TODO: implement memory-efficient version for 217 // MacOSX MacOSX does have atomic bit 218 // manipulation routines (OSAtomicTestAndSet()), 219 // and also has OSAtomicAnd32OrigBarrier() and 220 // OSAtomicOr32OrigBarrier() functions that can 221 // be used instead (Orig for 'return previous value' 222 // and Barrier for ('include a memory barrier'). 223 // Android needs additional routines in atomics.h/atomics.cpp 224 225 /** 226 * \brief An array of light-weight synchronisation 227 * primitives (spinlocks). 228 * 229 * \details This is the reference implementation, that uses 230 * a std::vector of spinlock. There are more efficient 231 * implementations for Linux and Windows. 232 * 233 * \see acquire_spinlock(), release_spinlock() 234 */ 235 class SpinLockArray { 236 public: 237 /** 238 * \brief Constructs a new SpinLockArray of size 0. 239 */ SpinLockArray()240 SpinLockArray() { 241 } 242 243 /** 244 * \brief Constructs a new SpinLockArray of size \p size_in. 245 * \param[in] size_in number of spinlocks in the array. 246 */ SpinLockArray(index_t size_in)247 SpinLockArray(index_t size_in) { 248 resize(size_in); 249 } 250 251 /** 252 * \brief Resizes a SpinLockArray. 253 * \details All the spinlocks are reset to 0. 254 * \param[in] size_in The desired new size. 255 */ resize(index_t size_in)256 void resize(index_t size_in) { 257 spinlocks_.assign(size_in, GEOGRAM_SPINLOCK_INIT); 258 } 259 260 /** 261 * \brief Resets size to 0 and clears all the memory. 262 */ clear()263 void clear() { 264 spinlocks_.clear(); 265 } 266 267 /** 268 * \brief Gets the number of spinlocks in this array. 269 */ size()270 index_t size() const { 271 return index_t(spinlocks_.size()); 272 } 273 274 /** 275 * \brief Acquires a spinlock at a given index 276 * \details Loops until spinlock at index \p i is available then 277 * reserve it. 278 * \param[in] i index of the spinlock 279 */ acquire_spinlock(index_t i)280 void acquire_spinlock(index_t i) { 281 geo_thread_sync_assert(i < size()); 282 GEO::Process::acquire_spinlock(spinlocks_[i]); 283 } 284 285 /** 286 * \brief Releases a spinlock at a given index 287 * \details Makes spinlock at index \p i available to other threads. 288 * \param[in] i index of the spinlock 289 */ release_spinlock(index_t i)290 void release_spinlock(index_t i) { 291 geo_thread_sync_assert(i < size()); 292 GEO::Process::release_spinlock(spinlocks_[i]); 293 } 294 295 private: 296 std::vector<spinlock> spinlocks_; 297 }; 298 299 #elif defined(GEO_OS_RASPBERRY) 300 301 /** 302 * \brief An array of light-weight synchronisation 303 * primitives (spinlocks). 304 * 305 * \details In this implementation, storage is optimized so that 306 * a single bit per spinlock is used. 307 * 308 */ 309 class SpinLockArray { 310 public: 311 /** 312 * \brief Internal representation of SpinLockArray elements. 313 * \details Each word_t represents 32 spinlocks. 314 */ 315 typedef Numeric::uint32 word_t; 316 317 /** 318 * \brief Constructs a new SpinLockArray of size 0. 319 */ SpinLockArray()320 SpinLockArray() : size_(0) { 321 } 322 323 /** 324 * \brief Constructs a new SpinLockArray of size \p size_in. 325 * \param[in] size_in number of spinlocks in the array. 326 */ SpinLockArray(index_t size_in)327 SpinLockArray(index_t size_in) : size_(0) { 328 resize(size_in); 329 } 330 331 /** 332 * \brief Resizes a SpinLockArray. 333 * \details All the spinlocks are reset to 0. 334 * \param[in] size_in The desired new size. 335 */ resize(index_t size_in)336 void resize(index_t size_in) { 337 if(size_ != size_in) { 338 size_ = size_in; 339 index_t nb_words = (size_ >> 5) + 1; 340 spinlocks_.assign(nb_words, 0); 341 } 342 } 343 344 /** 345 * \brief Gets the number of spinlocks in this array. 346 */ size()347 index_t size() const { 348 return size_; 349 } 350 351 /** 352 * \brief Resets size to 0 and clears all the memory. 353 */ clear()354 void clear() { 355 spinlocks_.clear(); 356 } 357 358 /** 359 * \brief Acquires a spinlock at a given index 360 * \details Loops until spinlock at index \p i is available then 361 * reserve it. 362 * \param[in] i index of the spinlock 363 */ acquire_spinlock(index_t i)364 void acquire_spinlock(index_t i) { 365 geo_thread_sync_assert(i < size()); 366 index_t w = i >> 5; 367 word_t b = word_t(i & 31); 368 // Loop while previously stored value has its bit set. 369 while((atomic_bitset_arm32(&spinlocks_[w], b)) != 0) { 370 // If somebody else has the lock, sleep. 371 // It is important to sleep here, else atomic_bitset_xxx() 372 // keeps acquiring the exclusive monitor (even for testing) 373 // and this slows down everything. 374 wait_for_event_arm32(); 375 } 376 memory_barrier_arm32(); 377 } 378 379 /** 380 * \brief Releases a spinlock at a given index 381 * \details Makes spinlock at index \p i available to other threads. 382 * \param[in] i index of the spinlock 383 */ release_spinlock(index_t i)384 void release_spinlock(index_t i) { 385 geo_thread_sync_assert(i < size()); 386 memory_barrier_android(); 387 index_t w = i >> 5; 388 word_t b = word_t(i & 31); 389 atomic_bitreset_arm32(&spinlocks_[w], b); 390 // Now wake up the other threads that started 391 // sleeping if they did not manage to acquire 392 // the lock. 393 send_event_arm32(); 394 } 395 396 private: 397 std::vector<word_t> spinlocks_; 398 index_t size_; 399 }; 400 401 #elif defined(GEO_OS_ANDROID) 402 403 /** 404 * \brief An array of light-weight synchronisation 405 * primitives (spinlocks). 406 * 407 * \details In this implementation, storage is optimized so that 408 * a single bit per spinlock is used. 409 * 410 */ 411 class SpinLockArray { 412 public: 413 /** 414 * \brief Internal representation of SpinLockArray elements. 415 * \details Each word_t represents 32 spinlocks. 416 */ 417 typedef Numeric::uint32 word_t; 418 419 /** 420 * \brief Constructs a new SpinLockArray of size 0. 421 */ SpinLockArray()422 SpinLockArray() : size_(0) { 423 } 424 425 /** 426 * \brief Constructs a new SpinLockArray of size \p size_in. 427 * \param[in] size_in number of spinlocks in the array. 428 */ SpinLockArray(index_t size_in)429 SpinLockArray(index_t size_in) : size_(0) { 430 resize(size_in); 431 } 432 433 /** 434 * \brief Resizes a SpinLockArray. 435 * \details All the spinlocks are reset to 0. 436 * \param[in] size_in The desired new size. 437 */ resize(index_t size_in)438 void resize(index_t size_in) { 439 if(size_ != size_in) { 440 size_ = size_in; 441 index_t nb_words = (size_ >> 5) + 1; 442 spinlocks_.assign(nb_words, 0); 443 } 444 } 445 446 /** 447 * \brief Gets the number of spinlocks in this array. 448 */ size()449 index_t size() const { 450 return size_; 451 } 452 453 /** 454 * \brief Resets size to 0 and clears all the memory. 455 */ clear()456 void clear() { 457 spinlocks_.clear(); 458 } 459 460 /** 461 * \brief Acquires a spinlock at a given index 462 * \details Loops until spinlock at index \p i is available then 463 * reserve it. 464 * \param[in] i index of the spinlock 465 */ acquire_spinlock(index_t i)466 void acquire_spinlock(index_t i) { 467 geo_thread_sync_assert(i < size()); 468 index_t w = i >> 5; 469 word_t b = word_t(i & 31); 470 // Loop while previously stored value has its bit set. 471 while((atomic_bitset_android(&spinlocks_[w], b)) != 0) { 472 // If somebody else has the lock, sleep. 473 // It is important to sleep here, else atomic_bitset_xxx() 474 // keeps acquiring the exclusive monitor (even for testing) 475 // and this slows down everything. 476 wait_for_event_android(); 477 } 478 memory_barrier_android(); 479 } 480 481 /** 482 * \brief Releases a spinlock at a given index 483 * \details Makes spinlock at index \p i available to other threads. 484 * \param[in] i index of the spinlock 485 */ release_spinlock(index_t i)486 void release_spinlock(index_t i) { 487 geo_thread_sync_assert(i < size()); 488 memory_barrier_android(); 489 index_t w = i >> 5; 490 word_t b = word_t(i & 31); 491 atomic_bitreset_android(&spinlocks_[w], b); 492 // Now wake up the other threads that started 493 // sleeping if they did not manage to acquire 494 // the lock. 495 send_event_android(); 496 } 497 498 private: 499 std::vector<word_t> spinlocks_; 500 index_t size_; 501 }; 502 503 #elif defined(GEO_OS_LINUX) || defined(GEO_OS_DRAGONFLY) 504 505 /** 506 * \brief An array of light-weight synchronisation 507 * primitives (spinlocks). 508 * 509 * \details In this implementation, storage is optimized so that 510 * a single bit per spinlock is used. 511 * 512 * \see acquire_spinlock(), release_spinlock() 513 */ 514 class SpinLockArray { 515 public: 516 /** 517 * \brief Internal representation of SpinLockArray elements. 518 * \details Each word_t represents 32 spinlocks. 519 */ 520 typedef Numeric::uint32 word_t; 521 522 /** 523 * \brief Constructs a new SpinLockArray of size 0. 524 */ SpinLockArray()525 SpinLockArray() : size_(0) { 526 } 527 528 /** 529 * \brief Constructs a new SpinLockArray of size \p size_in. 530 * \param[in] size_in number of spinlocks in the array. 531 */ SpinLockArray(index_t size_in)532 SpinLockArray(index_t size_in) : size_(0) { 533 resize(size_in); 534 } 535 536 /** 537 * \brief Resizes a SpinLockArray. 538 * \details All the spinlocks are reset to 0. 539 * \param[in] size_in The desired new size. 540 */ resize(index_t size_in)541 void resize(index_t size_in) { 542 if(size_ != size_in) { 543 size_ = size_in; 544 index_t nb_words = (size_ >> 5) + 1; 545 spinlocks_.assign(nb_words, 0); 546 } 547 } 548 549 /** 550 * \brief Gets the number of spinlocks in this array. 551 */ size()552 index_t size() const { 553 return size_; 554 } 555 556 /** 557 * \brief Resets size to 0 and clears all the memory. 558 */ clear()559 void clear() { 560 spinlocks_.clear(); 561 } 562 563 /** 564 * \brief Acquires a spinlock at a given index 565 * \details Loops until spinlock at index \p i is available then 566 * reserve it. 567 * \param[in] i index of the spinlock 568 */ acquire_spinlock(index_t i)569 void acquire_spinlock(index_t i) { 570 geo_thread_sync_assert(i < size()); 571 index_t w = i >> 5; 572 index_t b = i & 31; 573 while(atomic_bittestandset_x86(&spinlocks_[w], Numeric::uint32(b))) { 574 // Intel recommends to have a PAUSE asm instruction 575 // in the spinlock loop. It is generated using the 576 // following intrinsic function of GCC. 577 geo_pause(); 578 } 579 } 580 581 /** 582 * \brief Releases a spinlock at a given index 583 * \details Makes spinlock at index \p i available to other threads. 584 * \param[in] i index of the spinlock 585 */ release_spinlock(index_t i)586 void release_spinlock(index_t i) { 587 geo_thread_sync_assert(i < size()); 588 index_t w = i >> 5; 589 index_t b = i & 31; 590 // Note: we need here to use a synchronized bit reset 591 // since &= is not atomic. 592 atomic_bittestandreset_x86(&spinlocks_[w], Numeric::uint32(b)); 593 } 594 595 private: 596 std::vector<word_t> spinlocks_; 597 index_t size_; 598 }; 599 600 #elif defined(GEO_OS_WINDOWS) 601 /** 602 * \brief An array of light-weight synchronisation 603 * primitives (spinlocks). 604 * 605 * \details In this implementation, storage is optimized so that 606 * a single bit per spinlock is used. 607 * 608 * \see acquire_spinlock(), release_spinlock() 609 */ 610 class SpinLockArray { 611 public: 612 /** 613 * \brief Internal representation of SpinLockArray elements. 614 * \details Each word_t represents 32 spinlocks. 615 * \internal 616 * LONG is 32 bits under MSVC 617 * and is what interlockedbittestand(re)set uses. 618 */ 619 typedef LONG word_t; 620 621 /** 622 * \brief Constructs a new SpinLockArray of size 0. 623 */ SpinLockArray()624 SpinLockArray() : size_(0) { 625 } 626 627 /** 628 * \brief Constructs a new SpinLockArray of size \p size_in. 629 * \param[in] size_in number of spinlocks in the array. 630 */ SpinLockArray(index_t size_in)631 SpinLockArray(index_t size_in) : size_(0) { 632 resize(size_in); 633 } 634 635 /** 636 * \brief Resizes a SpinLockArray. 637 * \details All the spinlocks are reset to 0. 638 * \param[in] size_in The desired new size. 639 */ resize(index_t size_in)640 void resize(index_t size_in) { 641 if(size_ != size_in) { 642 size_ = size_in; 643 index_t nb_words = (size_ >> 5) + 1; 644 spinlocks_.assign(nb_words, 0); 645 } 646 } 647 648 /** 649 * \brief Gets the number of spinlocks in this array. 650 */ size()651 index_t size() const { 652 return size_; 653 } 654 655 /** 656 * \brief Resets size to 0 and clears all the memory. 657 */ clear()658 void clear() { 659 spinlocks_.clear(); 660 } 661 662 /** 663 * \brief Acquires a spinlock at a given index 664 * \details Loops until spinlock at index \p i is available then 665 * reserve it. 666 * \param[in] i index of the spinlock 667 */ acquire_spinlock(index_t i)668 void acquire_spinlock(index_t i) { 669 geo_thread_sync_assert(i < size()); 670 index_t w = i >> 5; 671 index_t b = i & 31; 672 while(_interlockedbittestandset((long *)(&spinlocks_[w]), long(b))) { 673 // Intel recommends to have a PAUSE asm instruction 674 // in the spinlock loop. Under MSVC/Windows, 675 // YieldProcessor() is a macro that calls the 676 // (undocumented) _mm_pause() intrinsic function 677 // that generates a PAUSE opcode. 678 YieldProcessor(); 679 } 680 // We do not need here _ReadBarrier() since 681 // _interlockedbittestandset 682 // "acts as a full barrier in VC2005" according to the doc 683 } 684 685 /** 686 * \brief Releases a spinlock at a given index 687 * \details Makes spinlock at index \p i available to other threads. 688 * \param[in] i index of the spinlock 689 */ release_spinlock(index_t i)690 void release_spinlock(index_t i) { 691 geo_thread_sync_assert(i < size()); 692 index_t w = i >> 5; 693 index_t b = i & 31; 694 // Note1: we need here to use a synchronized bit reset 695 // since |= is not atomic. 696 // Note2: We do not need here _WriteBarrier() since 697 // _interlockedbittestandreset 698 // "acts as a full barrier in VC2005" according to the doc 699 _interlockedbittestandreset((long*)(&spinlocks_[w]), long(b)); 700 } 701 702 private: 703 std::vector<word_t> spinlocks_; 704 index_t size_; 705 }; 706 707 #else 708 709 #error Found no implementation of SpinLockArray 710 711 #endif 712 713 714 } 715 716 #ifdef GEO_OS_WINDOWS 717 718 // Emulation of pthread mutexes using Windows API 719 720 typedef CRITICAL_SECTION pthread_mutex_t; 721 typedef unsigned int pthread_mutexattr_t; 722 pthread_mutex_lock(pthread_mutex_t * m)723 inline int pthread_mutex_lock(pthread_mutex_t *m) { 724 EnterCriticalSection(m); 725 return 0; 726 } 727 pthread_mutex_unlock(pthread_mutex_t * m)728 inline int pthread_mutex_unlock(pthread_mutex_t *m) { 729 LeaveCriticalSection(m); 730 return 0; 731 } 732 pthread_mutex_trylock(pthread_mutex_t * m)733 inline int pthread_mutex_trylock(pthread_mutex_t *m) { 734 return TryEnterCriticalSection(m) ? 0 : EBUSY; 735 } 736 pthread_mutex_init(pthread_mutex_t * m,pthread_mutexattr_t * a)737 inline int pthread_mutex_init(pthread_mutex_t *m, pthread_mutexattr_t *a) { 738 geo_argused(a); 739 InitializeCriticalSection(m); 740 return 0; 741 } 742 pthread_mutex_destroy(pthread_mutex_t * m)743 inline int pthread_mutex_destroy(pthread_mutex_t *m) { 744 DeleteCriticalSection(m); 745 return 0; 746 } 747 748 749 // Emulation of pthread condition variables using Windows API 750 751 typedef CONDITION_VARIABLE pthread_cond_t; 752 typedef unsigned int pthread_condattr_t; 753 pthread_cond_init(pthread_cond_t * c,pthread_condattr_t * a)754 inline int pthread_cond_init(pthread_cond_t *c, pthread_condattr_t *a) { 755 geo_argused(a); 756 InitializeConditionVariable(c); 757 return 0; 758 } 759 pthread_cond_destroy(pthread_cond_t * c)760 inline int pthread_cond_destroy(pthread_cond_t *c) { 761 geo_argused(c); 762 return 0; 763 } 764 pthread_cond_broadcast(pthread_cond_t * c)765 inline int pthread_cond_broadcast(pthread_cond_t *c) { 766 WakeAllConditionVariable(c); 767 return 0; 768 } 769 pthread_cond_wait(pthread_cond_t * c,pthread_mutex_t * m)770 inline int pthread_cond_wait(pthread_cond_t *c, pthread_mutex_t *m) { 771 SleepConditionVariableCS(c, m, INFINITE); 772 return 0; 773 } 774 775 #endif 776 777 } 778 779 #endif 780 781