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. |
43 | RT_API_ATTRS void Take() {} |
44 | RT_API_ATTRS bool Try() { return true; } |
45 | RT_API_ATTRS void Drop() {} |
46 | RT_API_ATTRS bool TakeIfNoDeadlock() { return true; } |
47 | #elif USE_PTHREADS |
48 | Lock() { pthread_mutex_init(mutex: &mutex_, mutexattr: nullptr); } |
49 | ~Lock() { pthread_mutex_destroy(mutex: &mutex_); } |
50 | void Take() { |
51 | while (pthread_mutex_lock(mutex: &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(thread1: thisThread, thread2: holder_)) { |
60 | return false; |
61 | } |
62 | } |
63 | Take(); |
64 | return true; |
65 | } |
66 | bool Try() { return pthread_mutex_trylock(mutex: &mutex_) == 0; } |
67 | void Drop() { |
68 | isBusy_ = false; |
69 | pthread_mutex_unlock(mutex: &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 | |
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: |
106 | explicit RT_API_ATTRS CriticalSection(Lock &lock) : lock_{lock} { |
107 | lock_.Take(); |
108 | } |
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 | |