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