1 #ifndef LLVM_LIBC_SRC_SUPPORT_THREAD_LINUX_MUTEX_H
2 #define LLVM_LIBC_SRC_SUPPORT_THREAD_LINUX_MUTEX_H
3 
4 #include "include/sys/syscall.h" // For syscall numbers.
5 #include "src/__support/CPP/atomic.h"
6 #include "src/__support/OSUtil/syscall.h" // For syscall functions.
7 #include "src/__support/threads/mutex.h"
8 
9 #include <linux/futex.h>
10 #include <stdint.h>
11 #include <threads.h>
12 
13 namespace __llvm_libc {
14 
15 struct Mutex {
16   unsigned char timed;
17   unsigned char recursive;
18   unsigned char robust;
19 
20   void *owner;
21   unsigned long long lock_count;
22 
23   using FutexWordType = unsigned int;
24 
25   cpp::Atomic<FutexWordType> futex_word;
26 
27   enum class LockState : FutexWordType {
28     Free,
29     Locked,
30     Waiting,
31   };
32 
33 public:
34   constexpr Mutex(bool istimed, bool isrecursive, bool isrobust)
35       : timed(istimed), recursive(isrecursive), robust(isrobust),
36         owner(nullptr), lock_count(0),
37         futex_word(FutexWordType(LockState::Free)) {}
38 
39   static MutexError init(Mutex *mutex, bool istimed, bool isrecur,
40                          bool isrobust) {
41     mutex->timed = istimed;
42     mutex->recursive = isrecur;
43     mutex->robust = isrobust;
44     mutex->owner = nullptr;
45     mutex->lock_count = 0;
46     mutex->futex_word.set(FutexWordType(LockState::Free));
47     return MutexError::NONE;
48   }
49 
50   static MutexError init(mtx_t *m, bool istimed, bool isrecur, bool isrobust) {
51     auto *mutex = reinterpret_cast<Mutex *>(m);
52     return init(mutex, istimed, isrecur, isrobust);
53   }
54 
55   static MutexError destroy(mtx_t *) { return MutexError::NONE; }
56 
57   MutexError reset();
58 
59   MutexError lock() {
60     bool was_waiting = false;
61     while (true) {
62       FutexWordType mutex_status = FutexWordType(LockState::Free);
63       FutexWordType locked_status = FutexWordType(LockState::Locked);
64 
65       if (futex_word.compare_exchange_strong(
66               mutex_status, FutexWordType(LockState::Locked))) {
67         if (was_waiting)
68           futex_word = FutexWordType(LockState::Waiting);
69         return MutexError::NONE;
70       }
71 
72       switch (LockState(mutex_status)) {
73       case LockState::Waiting:
74         // If other threads are waiting already, then join them. Note that the
75         // futex syscall will block if the futex data is still
76         // `LockState::Waiting` (the 4th argument to the syscall function
77         // below.)
78         __llvm_libc::syscall(SYS_futex, &futex_word.val, FUTEX_WAIT_PRIVATE,
79                              FutexWordType(LockState::Waiting), 0, 0, 0);
80         was_waiting = true;
81         // Once woken up/unblocked, try everything all over.
82         continue;
83       case LockState::Locked:
84         // Mutex has been locked by another thread so set the status to
85         // LockState::Waiting.
86         if (futex_word.compare_exchange_strong(
87                 locked_status, FutexWordType(LockState::Waiting))) {
88           // If we are able to set the futex data to `LockState::Waiting`, then
89           // we will wait for the futex to be woken up. Note again that the
90           // following syscall will block only if the futex data is still
91           // `LockState::Waiting`.
92           __llvm_libc::syscall(SYS_futex, &futex_word, FUTEX_WAIT_PRIVATE,
93                                FutexWordType(LockState::Waiting), 0, 0, 0);
94           was_waiting = true;
95         }
96         continue;
97       case LockState::Free:
98         // If it was LockState::Free, we shouldn't be here at all.
99         [[clang::fallthrough]];
100       default:
101         // Mutex status cannot be anything else. So control should not reach
102         // here at all.
103         return MutexError::BAD_LOCK_STATE;
104       }
105     }
106   }
107 
108   MutexError unlock() {
109     while (true) {
110       FutexWordType mutex_status = FutexWordType(LockState::Waiting);
111       if (futex_word.compare_exchange_strong(mutex_status,
112                                              FutexWordType(LockState::Free))) {
113         // If any thread is waiting to be woken up, then do it.
114         __llvm_libc::syscall(SYS_futex, &futex_word, FUTEX_WAKE_PRIVATE, 1, 0,
115                              0, 0);
116         return MutexError::NONE;
117       }
118 
119       if (mutex_status == FutexWordType(LockState::Locked)) {
120         // If nobody was waiting at this point, just free it.
121         if (futex_word.compare_exchange_strong(mutex_status,
122                                                FutexWordType(LockState::Free)))
123           return MutexError::NONE;
124       } else {
125         // This can happen, for example if some thread tries to unlock an
126         // already free mutex.
127         return MutexError::UNLOCK_WITHOUT_LOCK;
128       }
129     }
130   }
131 
132   MutexError trylock();
133 };
134 
135 } // namespace __llvm_libc
136 
137 #endif // LLVM_LIBC_SRC_SUPPORT_THREAD_LINUX_MUTEX_H
138