The Pedigree Project 0.1
advisory-lock-state.cc
1/* Copyright (c) 2026, Pedigree Developers. */
2#include "advisory-lock-state.h"
3#include "pedigree/kernel/LockGuard.h"
4#include "pedigree/kernel/process/ConditionVariable.h"
5#include "pedigree/kernel/process/Mutex.h"
6#include "pedigree/kernel/process/TerminationDeferral.h"
7#include "pedigree/kernel/syscallError.h"
8#include "pedigree/kernel/utilities/assert.h"
9
10using namespace PosixAdvisory;
11
12namespace {
13uint64_t nextOwnerId = 0;
14}
15
16AdvisoryOwner::AdvisoryOwner(Kind kind)
17 : m_Id(__atomic_add_fetch(&nextOwnerId, uint64_t(1), __ATOMIC_RELAXED)), m_Kind(kind) {
18 assert(m_Id);
19}
20
22 public:
23 int apply(AdvisoryOwner& owner, Grant request, bool wait, PosixAdvisoryAdmission admission,
24 void* context) {
25 TerminationDeferral lifetime;
26 if (!open(owner, request)) {
27 SYSCALL_ERROR(BadFileDescriptor);
28 return -1;
29 }
30 Error::PosixError result = Error::NoError;
31 size_t slot = MaximumWaiters;
32 {
33 LockGuard<Mutex> guard(m_Lock);
34 bool converted = false;
35 for (;;) {
36 if (__atomic_load_n(&owner.m_State, __ATOMIC_ACQUIRE) == AdvisoryOwner::Closed) {
37 result = Error::BadFileDescriptor;
38 break;
39 }
40 if (admission && !admission(context)) {
41 // Close already owns its release event. This request has installed
42 // nothing, so removing grants here could erase a later valid lock.
43 result = Error::BadFileDescriptor;
44 break;
45 }
46 if (!converted && request.name == Namespace::Flock && request.type != Type::Unlock) {
47 for (size_t i = 0; i < m_Table.count(); ++i) {
48 const Grant& existing = m_Table.at(i);
49 if (existing.owner == request.owner && existing.inode == request.inode &&
50 existing.name == Namespace::Flock && existing.type != request.type) {
51 Grant unlock = request;
52 unlock.type = Type::Unlock;
53 const PosixAdvisory::Result released = m_Table.apply(unlock);
54 assert(released == PosixAdvisory::Result::Success);
55 m_Changed.broadcast();
56 break;
57 }
58 }
59 converted = true;
60 }
61 const PosixAdvisory::Result applied = m_Table.apply(request);
62 if (applied == PosixAdvisory::Result::Success) {
63 m_Changed.broadcast();
64 break;
65 }
66 if (applied == PosixAdvisory::Result::Full) {
67 result = Error::NoLocksAvailable;
68 break;
69 }
70 if (!wait) {
71 result = Error::NoMoreProcesses;
72 break;
73 }
74 if (m_Table.wouldDeadlock(request, m_Waiters, MaximumWaiters)) {
75 result = Error::Deadlock;
76 break;
77 }
78 if (slot == MaximumWaiters) {
79 for (size_t i = 0; i < MaximumWaiters; ++i) {
80 if (!m_Waiters[i]) {
81 slot = i;
82 m_Waiters[i] = &request;
83 break;
84 }
85 }
86 if (slot == MaximumWaiters) {
87 result = Error::NoLocksAvailable;
88 break;
89 }
90 }
91 ConditionVariable::Error error = ConditionVariable::NoError;
92 if (!m_Changed.wait(m_Lock, error)) {
94 result = Error::Interrupted;
95 break;
96 }
97 }
98 // Closing an owner can retire its slot before this stack resumes.
99 if (slot != MaximumWaiters && m_Waiters[slot] == &request)
100 m_Waiters[slot] = nullptr;
101 }
102 if (result != Error::NoError) {
103 syscallError(result);
104 return -1;
105 }
106 return 0;
107 }
108
109 int query(AdvisoryOwner& owner, Grant request, Grant& conflict, bool& found) {
110 TerminationDeferral lifetime;
111 if (!open(owner, request)) {
112 SYSCALL_ERROR(BadFileDescriptor);
113 return -1;
114 }
115 LockGuard<Mutex> guard(m_Lock);
116 if (__atomic_load_n(&owner.m_State, __ATOMIC_ACQUIRE) == AdvisoryOwner::Closed) {
117 SYSCALL_ERROR(BadFileDescriptor);
118 return -1;
119 }
120 found = m_Table.query(request, conflict);
121 return 0;
122 }
123
124 void descriptorClosed(AdvisoryOwner& owner, uintptr_t inode) {
125 if (!inode || __atomic_load_n(&owner.m_State, __ATOMIC_ACQUIRE) != AdvisoryOwner::Open)
126 return;
127 TerminationDeferral lifetime;
128 LockGuard<Mutex> guard(m_Lock);
129 m_Table.removeOwner(owner.m_Id, inode);
130 // A closed original fd must also wake a request that had no grant yet.
131 m_Changed.broadcast();
132 }
133
134 void ownerClosed(AdvisoryOwner& owner) {
135 TerminationDeferral lifetime;
136 const uint8_t previous =
137 __atomic_exchange_n(&owner.m_State, AdvisoryOwner::Closed, __ATOMIC_ACQ_REL);
138 if (previous != AdvisoryOwner::Open)
139 return;
140 LockGuard<Mutex> guard(m_Lock);
141 m_Table.removeOwner(owner.m_Id);
142 for (auto& waiter : m_Waiters) {
143 if (waiter && waiter->owner == owner.m_Id)
144 waiter = nullptr;
145 }
146 m_Changed.broadcast();
147 }
148
149 private:
150 bool open(AdvisoryOwner& owner, Grant& request) {
151 uint8_t expected = AdvisoryOwner::Unused;
152 __atomic_compare_exchange_n(&owner.m_State, &expected, AdvisoryOwner::Open, false,
153 __ATOMIC_ACQ_REL, __ATOMIC_ACQUIRE);
154 if (expected == AdvisoryOwner::Closed)
155 return false;
156 request.owner = owner.m_Id;
157 request.kind = owner.m_Kind;
158 if (owner.m_Kind == AdvisoryOwner::Kind::OpenDescription)
159 request.pid = -1;
160 return true;
161 }
162
163 Mutex m_Lock;
164 ConditionVariable m_Changed;
165 Grant m_First[MaximumGrants];
166 Grant m_Second[MaximumGrants];
167 Table m_Table{m_First, m_Second, MaximumGrants};
168 const Grant* m_Waiters[MaximumWaiters] = {};
169};
170
171namespace {
173}
174
175void posix_advisory_descriptor_closed(AdvisoryOwner& owner, uintptr_t inodeIdentity) {
176 locks.descriptorClosed(owner, inodeIdentity);
177}
178
179void posix_advisory_owner_closed(AdvisoryOwner& owner) {
180 locks.ownerClosed(owner);
181}
182
183int posix_advisory_apply(AdvisoryOwner& owner, Grant request, bool wait,
184 PosixAdvisoryAdmission admission, void* context) {
185 return locks.apply(owner, request, wait, admission, context);
186}
187
188int posix_advisory_query(AdvisoryOwner& owner, Grant request, Grant& conflict, bool& found) {
189 return locks.query(owner, request, conflict, found);
190}
MUST_USE_RESULT bool wait(Mutex &mutex, Time::Timestamp &timeout, Error &error, WaitQueue::StackDiscardCleanup onStackDiscard=nullptr, void *stackDiscardContext=nullptr)
static bool mutexAcquired(Error error)
Definition Mutex.h:56