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