1 //===-- runtime/lock.h ------------------------------------------*- C++ -*-===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 // Wraps a mutex 10 11 #ifndef FORTRAN_RUNTIME_LOCK_H_ 12 #define FORTRAN_RUNTIME_LOCK_H_ 13 14 #include "terminator.h" 15 #include "tools.h" 16 17 // Avoid <mutex> if possible to avoid introduction of C++ runtime 18 // library dependence. 19 #ifndef _WIN32 20 #define USE_PTHREADS 1 21 #else 22 #undef USE_PTHREADS 23 #endif 24 25 #if USE_PTHREADS 26 #include <pthread.h> 27 #elif defined(_WIN32) 28 #include "flang/Common/windows-include.h" 29 #else 30 #include <mutex> 31 #endif 32 33 namespace Fortran::runtime { 34 35 class Lock { 36 public: 37 #if RT_USE_PSEUDO_LOCK 38 // No lock implementation, e.g. for using together 39 // with RT_USE_PSEUDO_FILE_UNIT. 40 // The users of Lock class may use it under 41 // USE_PTHREADS and otherwise, so it has to provide 42 // all the interfaces. Take()43 RT_API_ATTRS void Take() {} Try()44 RT_API_ATTRS bool Try() { return true; } Drop()45 RT_API_ATTRS void Drop() {} TakeIfNoDeadlock()46 RT_API_ATTRS bool TakeIfNoDeadlock() { return true; } 47 #elif USE_PTHREADS 48 Lock() { pthread_mutex_init(&mutex_, nullptr); } 49 ~Lock() { pthread_mutex_destroy(&mutex_); } 50 void Take() { 51 while (pthread_mutex_lock(&mutex_)) { 52 } 53 holder_ = pthread_self(); 54 isBusy_ = true; 55 } 56 bool TakeIfNoDeadlock() { 57 if (isBusy_) { 58 auto thisThread{pthread_self()}; 59 if (pthread_equal(thisThread, holder_)) { 60 return false; 61 } 62 } 63 Take(); 64 return true; 65 } 66 bool Try() { return pthread_mutex_trylock(&mutex_) == 0; } 67 void Drop() { 68 isBusy_ = false; 69 pthread_mutex_unlock(&mutex_); 70 } 71 #elif defined(_WIN32) 72 Lock() { InitializeCriticalSection(&cs_); } 73 ~Lock() { DeleteCriticalSection(&cs_); } 74 void Take() { EnterCriticalSection(&cs_); } 75 bool Try() { return TryEnterCriticalSection(&cs_); } 76 void Drop() { LeaveCriticalSection(&cs_); } 77 #else 78 void Take() { mutex_.lock(); } 79 bool Try() { return mutex_.try_lock(); } 80 void Drop() { mutex_.unlock(); } 81 #endif 82 CheckLocked(const Terminator & terminator)83 void CheckLocked(const Terminator &terminator) { 84 if (Try()) { 85 Drop(); 86 terminator.Crash("Lock::CheckLocked() failed"); 87 } 88 } 89 90 private: 91 #if RT_USE_PSEUDO_FILE_UNIT 92 // No state. 93 #elif USE_PTHREADS 94 pthread_mutex_t mutex_{}; 95 volatile bool isBusy_{false}; 96 volatile pthread_t holder_; 97 #elif defined(_WIN32) 98 CRITICAL_SECTION cs_; 99 #else 100 std::mutex mutex_; 101 #endif 102 }; 103 104 class CriticalSection { 105 public: CriticalSection(Lock & lock)106 explicit RT_API_ATTRS CriticalSection(Lock &lock) : lock_{lock} { 107 lock_.Take(); 108 } ~CriticalSection()109 RT_API_ATTRS ~CriticalSection() { lock_.Drop(); } 110 111 private: 112 Lock &lock_; 113 }; 114 } // namespace Fortran::runtime 115 116 #endif // FORTRAN_RUNTIME_LOCK_H_ 117