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 return MutexError::BAD_LOCK_STATE; 100 } 101 } 102 } 103 104 MutexError unlock() { 105 while (true) { 106 FutexWordType mutex_status = FutexWordType(LockState::Waiting); 107 if (futex_word.compare_exchange_strong(mutex_status, 108 FutexWordType(LockState::Free))) { 109 // If any thread is waiting to be woken up, then do it. 110 __llvm_libc::syscall(SYS_futex, &futex_word, FUTEX_WAKE_PRIVATE, 1, 0, 111 0, 0); 112 return MutexError::NONE; 113 } 114 115 if (mutex_status == FutexWordType(LockState::Locked)) { 116 // If nobody was waiting at this point, just free it. 117 if (futex_word.compare_exchange_strong(mutex_status, 118 FutexWordType(LockState::Free))) 119 return MutexError::NONE; 120 } else { 121 // This can happen, for example if some thread tries to unlock an 122 // already free mutex. 123 return MutexError::UNLOCK_WITHOUT_LOCK; 124 } 125 } 126 } 127 128 MutexError trylock(); 129 }; 130 131 } // namespace __llvm_libc 132 133 #endif // LLVM_LIBC_SRC_SUPPORT_THREAD_LINUX_MUTEX_H 134