mirror of
https://github.com/decompals/wibo.git
synced 2025-10-16 15:15:10 +00:00
239 lines
5.5 KiB
C++
239 lines
5.5 KiB
C++
#include "handles.h"
|
|
#include <cassert>
|
|
#include <cstdint>
|
|
|
|
namespace {
|
|
|
|
constexpr uint32_t kHandleAlignShift = 2;
|
|
// Max index that still yields HANDLE < 0x10000 with (index + 1) << 2
|
|
constexpr uint32_t kCompatMaxIndex = (0xFFFFu >> kHandleAlignShift) - 1;
|
|
// Delay reuse of small handles to avoid accidental stale aliasing
|
|
constexpr uint32_t kQuarantineLen = 64;
|
|
|
|
inline uint32_t indexOf(HANDLE h) {
|
|
uint32_t v = static_cast<uint32_t>(reinterpret_cast<uintptr_t>(h));
|
|
if (v == 0 || (v & ((1U << kHandleAlignShift) - 1)) != 0) {
|
|
return UINT32_MAX;
|
|
}
|
|
return (v >> kHandleAlignShift) - 1;
|
|
}
|
|
|
|
inline HANDLE makeHandle(uint32_t index) {
|
|
uint32_t v = (index + 1) << kHandleAlignShift;
|
|
return reinterpret_cast<HANDLE>(static_cast<uintptr_t>(v));
|
|
}
|
|
|
|
inline bool isPseudo(HANDLE h) { return reinterpret_cast<int32_t>(h) < 0; }
|
|
|
|
} // namespace
|
|
|
|
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_acq_rel);
|
|
return h;
|
|
}
|
|
|
|
Pin<> Handles::get(HANDLE h, HandleMeta *metaOut) {
|
|
if (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_acq_rel) - 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);
|
|
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);
|
|
}
|
|
|
|
namespace wibo {
|
|
|
|
Namespace g_namespace;
|
|
Handles &handles() {
|
|
static Handles table([](ObjectBase *obj) { g_namespace.remove(obj); });
|
|
return table;
|
|
}
|
|
|
|
} // namespace wibo
|