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