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
33namespace Fortran::runtime {
34
35class Lock {
36public:
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
90private:
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
104class CriticalSection {
105public:
106 explicit RT_API_ATTRS CriticalSection(Lock &lock) : lock_{lock} {
107 lock_.Take();
108 }
109 RT_API_ATTRS ~CriticalSection() { lock_.Drop(); }
110
111private:
112 Lock &lock_;
113};
114} // namespace Fortran::runtime
115
116#endif // FORTRAN_RUNTIME_LOCK_H_
117

source code of flang/runtime/lock.h