#pragma once #include "common.h" #include #include #include #include #include #include #include #include #include #include enum class ObjectType : uint16_t { File, Directory, Mapping, Process, Token, Mutex, Event, Semaphore, Thread, Heap, RegistryKey, }; struct ObjectBase { const ObjectType type; std::atomic pointerCount{0}; std::atomic handleCount{0}; explicit ObjectBase(ObjectType t) : type(t) {} virtual ~ObjectBase() = default; [[nodiscard]] virtual bool isWaitable() const { return false; } }; namespace detail { inline void ref(ObjectBase *o) { o->pointerCount.fetch_add(1, std::memory_order_acq_rel); } inline void deref(ObjectBase *o) { if (o->pointerCount.fetch_sub(1, std::memory_order_acq_rel) == 1) { delete o; } } } // namespace detail struct WaitableObject : ObjectBase { std::atomic signaled{false}; std::mutex m; std::condition_variable_any cv; using ObjectBase::ObjectBase; [[nodiscard]] bool isWaitable() const override { return true; } }; template struct Pin { static_assert(std::is_base_of_v || std::is_same_v, "Pin: T must be ObjectBase or derive from it"); T *obj = nullptr; Pin() = default; enum class Tag { Acquire, Adopt }; template ::value>> explicit Pin(U *p, Tag t) : obj(static_cast(p)) { if (obj && t == Tag::Acquire) { detail::ref(obj); } } Pin(const Pin &) = delete; Pin(Pin &&other) noexcept : obj(std::exchange(other.obj, nullptr)) {} template ::value>> Pin &operator=(Pin &&other) noexcept { reset(); obj = std::exchange(other.obj, nullptr); return *this; } template ::value>> Pin(Pin &&other) noexcept : obj(std::exchange(other.obj, nullptr)) {} // NOLINT(google-explicit-constructor) Pin &operator=(Pin &&other) noexcept { if (this != &other) { reset(); obj = std::exchange(other.obj, nullptr); } return *this; } ~Pin() { reset(); } static Pin acquire(T *o) { return Pin{o, Tag::Acquire}; } static Pin adopt(T *o) { return Pin{o, Tag::Adopt}; } [[nodiscard]] T *release() { return std::exchange(obj, nullptr); } void reset() { if (obj) { detail::deref(obj); obj = nullptr; } } T *operator->() const { assert(obj); return obj; } T &operator*() const { assert(obj); return *obj; } [[nodiscard]] T *get() const { return obj; } [[nodiscard]] Pin clone() const { return Pin::acquire(obj); } explicit operator bool() const { return obj != nullptr; } template Pin downcast() && { static_assert(std::is_base_of_v, "U must derive from ObjectBase"); if constexpr (std::is_same_v) { return std::move(*this); } if (obj && obj->type == U::kType) { auto *u = static_cast(obj); obj = nullptr; return Pin::adopt(u); } return Pin{}; } }; template Pin make_pin(Args &&...args) noexcept(std::is_nothrow_constructible_v) { T *p = new T(std::forward(args)...); return Pin::acquire(p); } constexpr DWORD HANDLE_FLAG_INHERIT = 0x1; constexpr DWORD HANDLE_FLAG_PROTECT_FROM_CLOSE = 0x2; constexpr DWORD DUPLICATE_CLOSE_SOURCE = 0x1; constexpr DWORD DUPLICATE_SAME_ACCESS = 0x2; struct HandleMeta { uint32_t grantedAccess; uint32_t flags; ObjectType typeCache; uint16_t generation; }; // We have to stay under a HANDLE value of 0xFFFF for legacy applications, // and handles values are aligned to 4. constexpr DWORD MAX_HANDLES = 0x4000; class Handles { public: using OnHandleZeroFn = void (*)(ObjectBase *); explicit Handles(OnHandleZeroFn cb) : mOnHandleZero(cb) {} HANDLE alloc(Pin<> obj, uint32_t grantedAccess, uint32_t flags); bool release(HANDLE h); Pin<> get(HANDLE h, HandleMeta *metaOut = nullptr); template Pin getAs(HANDLE h, HandleMeta *metaOut = nullptr) { static_assert(std::is_base_of_v, "T must derive from ObjectBase"); HandleMeta metaOutLocal{}; if (!metaOut) { metaOut = &metaOutLocal; } auto obj = get(h, metaOut); if (!obj) { return {}; } if constexpr (std::is_same_v) { return std::move(obj); } else if (metaOut->typeCache != T::kType || obj->type != T::kType) { return {}; } else { return Pin::adopt(static_cast(obj.release())); } } bool setInformation(HANDLE h, uint32_t mask, uint32_t value); bool getInformation(HANDLE h, uint32_t *outFlags) const; bool duplicateTo(HANDLE src, Handles &dst, HANDLE &out, uint32_t desiredAccess, bool inherit, uint32_t options); private: struct Entry { ObjectBase *obj; HandleMeta meta; }; mutable std::shared_mutex m; std::vector mSlots; OnHandleZeroFn mOnHandleZero = nullptr; std::vector mFreeBelow; std::vector mFreeAbove; std::deque mQuarantine; uint32_t nextIndex = 0; }; class Namespace { public: bool insert(const std::u16string &name, ObjectBase *obj, bool permanent = false); void remove(ObjectBase *obj); Pin<> get(const std::u16string &name); template Pin getAs(const std::u16string &name) { if (auto pin = get(name)) { return std::move(pin).downcast(); } return {}; } template , typename T = std::remove_pointer_t>, std::enable_if_t>::value, int> = 0> std::pair, bool> getOrCreate(const std::u16string &name, F &&make) { if (name.empty()) { // No name: create unconditionally T *raw = std::invoke(std::forward(make)); return {Pin::acquire(raw), true}; } if (auto existing = get(name)) { // Return even if downcast fails (don't use getAs) return {std::move(existing).downcast(), false}; } T *raw = std::invoke(std::forward(make)); Pin newObj = Pin::acquire(raw); if (!newObj) { return {Pin{}, false}; } if (!insert(name, newObj.get())) { // Race: someone else inserted it first return {getAs(name), false}; } return {std::move(newObj), true}; } private: struct Entry { ObjectBase *obj; bool permanent; Entry(ObjectBase *o, bool p) : obj(o), permanent(p) {} }; mutable std::shared_mutex m; std::unordered_map mTable; }; namespace wibo { extern Namespace g_namespace; extern Handles &handles(); } // namespace wibo