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 | |
16 | // Avoid <mutex> if possible to avoid introduction of C++ runtime |
17 | // library dependence. |
18 | #ifndef _WIN32 |
19 | #define USE_PTHREADS 1 |
20 | #else |
21 | #undef USE_PTHREADS |
22 | #endif |
23 | |
24 | #if USE_PTHREADS |
25 | #include <pthread.h> |
26 | #elif defined(_WIN32) |
27 | // Do not define macros for "min" and "max" |
28 | #define NOMINMAX |
29 | #include <windows.h> |
30 | #else |
31 | #include <mutex> |
32 | #endif |
33 | |
34 | namespace Fortran::runtime { |
35 | |
36 | class Lock { |
37 | public: |
38 | #if USE_PTHREADS |
39 | Lock() { pthread_mutex_init(mutex: &mutex_, mutexattr: nullptr); } |
40 | ~Lock() { pthread_mutex_destroy(mutex: &mutex_); } |
41 | void Take() { |
42 | while (pthread_mutex_lock(mutex: &mutex_)) { |
43 | } |
44 | holder_ = pthread_self(); |
45 | isBusy_ = true; |
46 | } |
47 | bool TakeIfNoDeadlock() { |
48 | if (isBusy_) { |
49 | auto thisThread{pthread_self()}; |
50 | if (pthread_equal(thread1: thisThread, thread2: holder_)) { |
51 | return false; |
52 | } |
53 | } |
54 | Take(); |
55 | return true; |
56 | } |
57 | bool Try() { return pthread_mutex_trylock(mutex: &mutex_) == 0; } |
58 | void Drop() { |
59 | isBusy_ = false; |
60 | pthread_mutex_unlock(mutex: &mutex_); |
61 | } |
62 | #elif defined(_WIN32) |
63 | Lock() { InitializeCriticalSection(&cs_); } |
64 | ~Lock() { DeleteCriticalSection(&cs_); } |
65 | void Take() { EnterCriticalSection(&cs_); } |
66 | bool Try() { return TryEnterCriticalSection(&cs_); } |
67 | void Drop() { LeaveCriticalSection(&cs_); } |
68 | #else |
69 | void Take() { mutex_.lock(); } |
70 | bool Try() { return mutex_.try_lock(); } |
71 | void Drop() { mutex_.unlock(); } |
72 | #endif |
73 | |
74 | void CheckLocked(const Terminator &terminator) { |
75 | if (Try()) { |
76 | Drop(); |
77 | terminator.Crash("Lock::CheckLocked() failed" ); |
78 | } |
79 | } |
80 | |
81 | private: |
82 | #if USE_PTHREADS |
83 | pthread_mutex_t mutex_{}; |
84 | volatile bool isBusy_{false}; |
85 | volatile pthread_t holder_; |
86 | #elif defined(_WIN32) |
87 | CRITICAL_SECTION cs_; |
88 | #else |
89 | std::mutex mutex_; |
90 | #endif |
91 | }; |
92 | |
93 | class CriticalSection { |
94 | public: |
95 | explicit CriticalSection(Lock &lock) : lock_{lock} { lock_.Take(); } |
96 | ~CriticalSection() { lock_.Drop(); } |
97 | |
98 | private: |
99 | Lock &lock_; |
100 | }; |
101 | } // namespace Fortran::runtime |
102 | |
103 | #endif // FORTRAN_RUNTIME_LOCK_H_ |
104 | |