-
Notifications
You must be signed in to change notification settings - Fork 43
Expand file tree
/
Copy pathhandles.cpp
More file actions
289 lines (249 loc) · 6.66 KB
/
Copy pathhandles.cpp
File metadata and controls
289 lines (249 loc) · 6.66 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
#include "handles.h"
#include "types.h"
#include <atomic>
#include <cassert>
#include <cstdint>
namespace {
constexpr uint32_t kHandleAlignShift = 2;
// Max index that still yields HANDLE < 0x8000 with (index + 1) << 2
constexpr uint32_t kCompatMaxIndex = (0x7FFFu >> kHandleAlignShift) - 1;
// Delay reuse of small handles to avoid accidental stale aliasing
constexpr uint32_t kQuarantineLen = 64;
inline uint32_t indexOf(HANDLE h) noexcept {
uint32_t v = static_cast<uint32_t>(h);
if (v == 0 || (v & ((1U << kHandleAlignShift) - 1)) != 0) {
return UINT32_MAX;
}
return (v >> kHandleAlignShift) - 1;
}
inline HANDLE makeHandle(uint32_t index) noexcept {
uint32_t v = (index + 1) << kHandleAlignShift;
return static_cast<HANDLE>(v);
}
inline bool isPseudo(HANDLE h) noexcept { return reinterpret_cast<int32_t>(h) < 0; }
} // namespace
Handles::~Handles() { clear(); }
void Handles::clear() {
for (auto &entry : mSlots) {
if (entry.obj) {
detail::deref(entry.obj);
}
}
mSlots.clear();
mFreeBelow.clear();
mFreeAbove.clear();
nextIndex = 0;
}
HANDLE Handles::alloc(Pin<> obj, uint32_t grantedAccess, uint32_t flags) {
std::unique_lock lk(m);
// Attempt to, in order:
// 1) use a fresh index in the compat range (0..kCompatMaxIndex)
// 2) reuse a recently-freed index in the compat range
// 3) reuse a recently-freed index above the compat range
// 4) use a fresh index above the compat range
uint32_t idx;
if (nextIndex <= kCompatMaxIndex) {
idx = nextIndex++;
if (idx >= mSlots.size()) {
mSlots.emplace_back();
}
} else if (!mFreeBelow.empty()) {
idx = mFreeBelow.back();
mFreeBelow.pop_back();
} else if (!mFreeAbove.empty()) {
idx = mFreeAbove.back();
mFreeAbove.pop_back();
} else {
idx = static_cast<uint32_t>(mSlots.size());
mSlots.emplace_back();
}
// Initialize entry
auto &e = mSlots[idx];
e.obj = obj.release(); // Transfer ownership
e.meta.grantedAccess = grantedAccess;
e.meta.flags = flags;
e.meta.typeCache = e.obj->type;
if (e.meta.generation == 0) {
e.meta.generation = 1;
}
HANDLE h = makeHandle(idx);
e.obj->handleCount.fetch_add(1, std::memory_order_relaxed);
return h;
}
Pin<> Handles::get(HANDLE h, HandleMeta *metaOut) {
if (h == NO_HANDLE || isPseudo(h)) {
return {}; // pseudo-handles have no entries
}
std::shared_lock lk(m);
const auto idx = indexOf(h);
if (idx >= mSlots.size()) {
return {};
}
const auto &e = mSlots[idx];
if (!e.obj) {
return {};
}
if (metaOut) {
*metaOut = e.meta;
}
return Pin<>::acquire(e.obj);
}
bool Handles::release(HANDLE h) {
if (isPseudo(h)) {
return true; // no-op, success
}
std::unique_lock lk(m);
const auto idx = indexOf(h);
if (idx >= mSlots.size()) {
return false;
}
auto &e = mSlots[idx];
if (!e.obj || e.meta.flags & HANDLE_FLAG_PROTECT_FROM_CLOSE) {
return false;
}
ObjectBase *obj = e.obj;
const auto generation = e.meta.generation + 1;
e = {}; // Clear entry
e.meta.generation = generation;
uint32_t handleCount = obj->handleCount.fetch_sub(1, std::memory_order_relaxed) - 1;
if (idx <= kCompatMaxIndex) {
mQuarantine.push_back(idx);
if (mQuarantine.size() > kQuarantineLen) {
mFreeBelow.push_back(mQuarantine.front());
mQuarantine.pop_front();
}
} else {
mFreeAbove.push_back(idx);
}
lk.unlock();
if (handleCount == 0 && mOnHandleZero) {
mOnHandleZero(obj);
}
detail::deref(obj);
return true;
}
bool Handles::setInformation(HANDLE h, uint32_t mask, uint32_t value) {
if (isPseudo(h)) {
return true; // no-op, success
}
std::unique_lock lk(m);
const auto idx = indexOf(h);
if (idx >= mSlots.size()) {
return false;
}
auto &e = mSlots[idx];
if (!e.obj) {
return false;
}
constexpr uint32_t kAllowedFlags = HANDLE_FLAG_INHERIT | HANDLE_FLAG_PROTECT_FROM_CLOSE;
mask &= kAllowedFlags;
e.meta.flags = (e.meta.flags & ~mask) | (value & mask);
return true;
}
bool Handles::getInformation(HANDLE h, uint32_t *outFlags) const {
if (!outFlags) {
return false;
}
if (isPseudo(h)) {
*outFlags = 0;
return true;
}
std::shared_lock lk(m);
const auto idx = indexOf(h);
if (idx >= mSlots.size()) {
return false;
}
const auto &e = mSlots[idx];
if (!e.obj) {
return false;
}
*outFlags = e.meta.flags;
return true;
}
bool Handles::duplicateTo(HANDLE src, Handles &dst, HANDLE &out, uint32_t desiredAccess, bool inherit,
uint32_t options) {
HandleMeta meta{};
Pin<> obj = get(src, &meta);
if (!obj) {
return false;
}
bool closeSource = (options & DUPLICATE_CLOSE_SOURCE) != 0;
if (closeSource && (meta.flags & HANDLE_FLAG_PROTECT_FROM_CLOSE) != 0) {
// Cannot close source if it is protected
return false;
}
uint32_t effAccess = (options & DUPLICATE_SAME_ACCESS) ? meta.grantedAccess : (desiredAccess & meta.grantedAccess);
const uint32_t flags = (inherit ? HANDLE_FLAG_INHERIT : 0);
// Reuse the same handle if duplicating with DUPLICATE_CLOSE_SOURCE within the same table and no changes
if (&dst == this && closeSource && effAccess == meta.grantedAccess && flags == meta.flags) {
out = src;
return true;
}
out = dst.alloc(std::move(obj), effAccess, flags);
if (closeSource) {
release(src);
}
return true;
}
bool Namespace::insert(const std::u16string &name, ObjectBase *obj, bool permanent) {
if (name.empty() || !obj) {
return false;
}
std::unique_lock lk(m);
// Namespace holds a weak ref
const auto [_, inserted] = mTable.try_emplace(name, obj, permanent);
return inserted;
}
void Namespace::remove(ObjectBase *obj) {
std::unique_lock lk(m);
for (auto it = mTable.begin(); it != mTable.end(); ++it) {
if (it->second.obj == obj && !it->second.permanent) {
mTable.erase(it);
break;
}
}
}
Pin<> Namespace::get(const std::u16string &name) {
if (name.empty()) {
return {};
}
std::shared_lock lk(m);
auto it = mTable.find(name);
if (it == mTable.end()) {
return {};
}
assert(it->second.obj);
return Pin<>::acquire(it->second.obj);
}
void WaitableObject::registerWaiter(void *context, DWORD index, WaiterCallback cb) {
if (!cb) {
return;
}
std::lock_guard lk(waitersMutex);
waiters.emplace_back(cb, context, index);
}
void WaitableObject::unregisterWaiter(void *context) {
std::lock_guard lk(waitersMutex);
waiters.erase(
std::remove_if(waiters.begin(), waiters.end(), [context](const Waiter &w) { return w.context == context; }),
waiters.end());
}
void WaitableObject::notifyWaiters(bool abandoned) {
std::vector<Waiter> snapshot;
{
std::lock_guard lk(waitersMutex);
snapshot = waiters;
}
for (const auto &w : snapshot) {
if (w.callback) {
w.callback(w.context, this, w.index, abandoned);
}
}
}
namespace wibo {
Namespace g_namespace;
Handles &handles() {
static Handles table([](ObjectBase *obj) { g_namespace.remove(obj); });
return table;
}
} // namespace wibo