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_THREADS_LINUX_MUTEX_H
10#define LLVM_LIBC_SRC___SUPPORT_THREADS_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
21namespace LIBC_NAMESPACE {
22
23struct 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
39public:
40 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
45 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
56 static MutexError destroy(Mutex *) { return MutexError::NONE; }
57
58 MutexError reset();
59
60 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 expected&: mutex_status, desired: 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 LIBC_NAMESPACE::syscall_impl<long>(
80 number: FUTEX_SYSCALL_ID, ts: &futex_word.val, FUTEX_WAIT_PRIVATE,
81 ts: FutexWordType(LockState::Waiting), ts: 0, ts: 0, ts: 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 expected&: locked_status, desired: 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 LIBC_NAMESPACE::syscall_impl<long>(
95 number: FUTEX_SYSCALL_ID, ts: &futex_word, FUTEX_WAIT_PRIVATE,
96 ts: FutexWordType(LockState::Waiting), ts: 0, ts: 0, ts: 0);
97 was_waiting = true;
98 }
99 continue;
100 case LockState::Free:
101 // If it was LockState::Free, we shouldn't be here at all.
102 return MutexError::BAD_LOCK_STATE;
103 }
104 }
105 }
106
107 MutexError unlock() {
108 while (true) {
109 FutexWordType mutex_status = FutexWordType(LockState::Waiting);
110 if (futex_word.compare_exchange_strong(expected&: mutex_status,
111 desired: FutexWordType(LockState::Free))) {
112 // If any thread is waiting to be woken up, then do it.
113 LIBC_NAMESPACE::syscall_impl<long>(number: FUTEX_SYSCALL_ID, ts: &futex_word,
114 FUTEX_WAKE_PRIVATE, ts: 1, ts: 0, ts: 0, ts: 0);
115 return MutexError::NONE;
116 }
117
118 if (mutex_status == FutexWordType(LockState::Locked)) {
119 // If nobody was waiting at this point, just free it.
120 if (futex_word.compare_exchange_strong(expected&: mutex_status,
121 desired: FutexWordType(LockState::Free)))
122 return MutexError::NONE;
123 } else {
124 // This can happen, for example if some thread tries to unlock an
125 // already free mutex.
126 return MutexError::UNLOCK_WITHOUT_LOCK;
127 }
128 }
129 }
130
131 MutexError trylock();
132};
133
134} // namespace LIBC_NAMESPACE
135
136#endif // LLVM_LIBC_SRC___SUPPORT_THREADS_LINUX_MUTEX_H
137

source code of libc/src/__support/threads/linux/mutex.h