#include "handles.h" #include #include 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(reinterpret_cast(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(static_cast(v)); } inline bool isPseudo(HANDLE h) { return reinterpret_cast(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(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