mirror of
https://github.com/decompals/wibo.git
synced 2025-10-16 23:25:11 +00:00
New handle system headers
This commit is contained in:
parent
a9581423a8
commit
072b3e1da9
221
handles.cpp
221
handles.cpp
@ -1,29 +1,208 @@
|
||||
#include "common.h"
|
||||
#include "handles.h"
|
||||
#include "common.h"
|
||||
#include <utility>
|
||||
|
||||
namespace handles {
|
||||
static Data datas[MAX_HANDLES];
|
||||
namespace {
|
||||
|
||||
Data dataFromHandle(void *handle, bool pop) {
|
||||
uintptr_t index = (uintptr_t)handle;
|
||||
if (index > 0 && index < MAX_HANDLES) {
|
||||
Data ret = datas[index];
|
||||
if (pop)
|
||||
datas[index] = Data{};
|
||||
return ret;
|
||||
}
|
||||
return Data{};
|
||||
constexpr uint32_t kIndexBits = 17;
|
||||
constexpr uint32_t kIndexMask = (1u << kIndexBits) - 1; // 0x1FFFF
|
||||
constexpr uint32_t kGenerationMask = (1u << 15) - 1; // 0x7FFF
|
||||
constexpr unsigned kGenerationShift = kIndexBits; // 17
|
||||
|
||||
inline uint32_t indexOf(Handle h) { return h & kIndexMask; }
|
||||
inline uint32_t generationOf(Handle h) { return (h >> kGenerationShift) & kGenerationMask; }
|
||||
inline Handle makeHandle(uint32_t index, uint32_t gen) { return (gen << kGenerationShift) | index; }
|
||||
inline bool isPseudo(Handle h) { return static_cast<int32_t>(h) < 0; }
|
||||
|
||||
} // namespace
|
||||
|
||||
Handle HandleTable::create(ObjectHeader *obj, uint32_t grantedAccess, uint32_t flags) {
|
||||
std::unique_lock lk(mu_);
|
||||
|
||||
uint32_t idx;
|
||||
if (!freeList_.empty()) {
|
||||
idx = freeList_.back();
|
||||
freeList_.pop_back();
|
||||
} else {
|
||||
idx = static_cast<uint32_t>(slots_.size());
|
||||
slots_.push_back(HandleEntry{});
|
||||
}
|
||||
|
||||
void *allocDataHandle(Data data) {
|
||||
for (size_t i = 1; i < MAX_HANDLES; i++) {
|
||||
if (datas[i].type == TYPE_UNUSED) {
|
||||
datas[i] = data;
|
||||
return (void*)i;
|
||||
}
|
||||
}
|
||||
printf("Out of handles\n");
|
||||
assert(0);
|
||||
auto &e = slots_[idx];
|
||||
|
||||
// Initialize generation if needed
|
||||
if (e.generation == 0) {
|
||||
e.generation = 1;
|
||||
}
|
||||
const uint16_t gen = e.generation;
|
||||
|
||||
// Table owns one pointer ref for this entry
|
||||
detail::ref(obj);
|
||||
|
||||
// Initialize entry
|
||||
e.obj = obj;
|
||||
e.grantedAccess = grantedAccess;
|
||||
e.flags = flags;
|
||||
e.typeCache = obj->type;
|
||||
|
||||
const Handle h = makeHandle(idx, gen);
|
||||
obj->handleCount.fetch_add(1, std::memory_order_acq_rel);
|
||||
return h;
|
||||
}
|
||||
|
||||
bool HandleTable::get(Handle h, HandleEntry &out, Pin<ObjectHeader> &pinOut) {
|
||||
if (isPseudo(h)) {
|
||||
return false; // pseudo-handles have no entries
|
||||
}
|
||||
|
||||
std::shared_lock lk(mu_);
|
||||
const auto idx = indexOf(h);
|
||||
if (idx >= slots_.size()) {
|
||||
return false;
|
||||
}
|
||||
const auto &e = slots_[idx];
|
||||
if (e.generation != generationOf(h) || !e.obj) {
|
||||
return false;
|
||||
}
|
||||
|
||||
detail::ref(e.obj); // pin under the lock
|
||||
pinOut = Pin<ObjectHeader>::adopt(e.obj); // dtor will deref
|
||||
out = e;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool HandleTable::close(Handle h) {
|
||||
if (isPseudo(h)) {
|
||||
return true; // no-op, success
|
||||
}
|
||||
|
||||
std::unique_lock lk(mu_);
|
||||
const auto idx = indexOf(h);
|
||||
if (idx >= slots_.size()) {
|
||||
return false;
|
||||
}
|
||||
auto &e = slots_[idx];
|
||||
if (e.generation != generationOf(h) || !e.obj || e.flags & HANDLE_FLAG_PROTECT_FROM_CLOSE) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ObjectHeader *obj = e.obj;
|
||||
e.obj = nullptr; // tombstone
|
||||
auto newHandleCnt = obj->handleCount.fetch_sub(1, std::memory_order_acq_rel) - 1;
|
||||
|
||||
// bump generation & recycle while still holding the lock
|
||||
e.generation = static_cast<uint16_t>((e.generation + 1) & kGenerationMask);
|
||||
freeList_.push_back(idx);
|
||||
lk.unlock();
|
||||
|
||||
// if (newHandleCnt == 0) {
|
||||
// namespaceOnHandleCountZero(obj);
|
||||
// }
|
||||
detail::deref(obj);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool HandleTable::setInformation(Handle h, uint32_t mask, uint32_t value) {
|
||||
if (isPseudo(h)) {
|
||||
return true; // no-op, success
|
||||
}
|
||||
|
||||
std::unique_lock lk(mu_);
|
||||
const auto idx = indexOf(h);
|
||||
if (idx >= slots_.size()) {
|
||||
return false;
|
||||
}
|
||||
auto &e = slots_[idx];
|
||||
if (e.generation != generationOf(h) || !e.obj) {
|
||||
return false;
|
||||
}
|
||||
|
||||
constexpr uint32_t kAllowedFlags = HANDLE_FLAG_INHERIT | HANDLE_FLAG_PROTECT_FROM_CLOSE;
|
||||
mask &= kAllowedFlags;
|
||||
|
||||
e.flags = (e.flags & ~mask) | (value & mask);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool HandleTable::getInformation(Handle h, uint32_t *outFlags) const {
|
||||
if (!outFlags) {
|
||||
return false;
|
||||
}
|
||||
if (isPseudo(h)) {
|
||||
*outFlags = 0;
|
||||
return true;
|
||||
}
|
||||
std::shared_lock lk(mu_);
|
||||
const auto idx = indexOf(h);
|
||||
if (idx >= slots_.size()) {
|
||||
return false;
|
||||
}
|
||||
const auto &e = slots_[idx];
|
||||
if (e.generation != generationOf(h) || !e.obj) {
|
||||
return false;
|
||||
}
|
||||
*outFlags = e.flags;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool HandleTable::duplicateTo(Handle src, HandleTable &dst, Handle *out, uint32_t desiredAccess, bool inherit,
|
||||
uint32_t options) {
|
||||
if (!out)
|
||||
return false;
|
||||
|
||||
// Pseudo-handles: resolve to a Borrow of the live object
|
||||
// if (isPseudo(src)) {
|
||||
// Pin pin = resolvePseudoBorrow(src);
|
||||
// if (!pin)
|
||||
// return false;
|
||||
// const uint32_t granted = desiredAccess; // or compute from type; pseudo has full rights to self
|
||||
// *out = dst.create(pin.obj, granted, inherit ? HANDLE_FLAG_INHERIT : 0);
|
||||
// return true;
|
||||
// }
|
||||
|
||||
HandleEntry e{};
|
||||
Pin<ObjectHeader> pin;
|
||||
if (!get(src, e, pin))
|
||||
return false;
|
||||
|
||||
bool closeSource = (options & DUPLICATE_CLOSE_SOURCE) != 0;
|
||||
if (closeSource && (e.flags & HANDLE_FLAG_PROTECT_FROM_CLOSE) != 0) {
|
||||
// Cannot close source if it is protected
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t effAccess = (options & DUPLICATE_SAME_ACCESS) ? e.grantedAccess : (desiredAccess & e.grantedAccess);
|
||||
const uint32_t flags = (inherit ? HANDLE_FLAG_INHERIT : 0);
|
||||
*out = dst.create(pin.obj, effAccess, flags);
|
||||
|
||||
if (closeSource) {
|
||||
close(src);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
namespace handles {
|
||||
static Data datas[MAX_HANDLES];
|
||||
|
||||
Data dataFromHandle(void *handle, bool pop) {
|
||||
uintptr_t index = (uintptr_t)handle;
|
||||
if (index > 0 && index < MAX_HANDLES) {
|
||||
Data ret = datas[index];
|
||||
if (pop)
|
||||
datas[index] = Data{};
|
||||
return ret;
|
||||
}
|
||||
return Data{};
|
||||
}
|
||||
|
||||
void *allocDataHandle(Data data) {
|
||||
for (size_t i = 1; i < MAX_HANDLES; i++) {
|
||||
if (datas[i].type == TYPE_UNUSED) {
|
||||
datas[i] = data;
|
||||
return (void *)i;
|
||||
}
|
||||
}
|
||||
printf("Out of handles\n");
|
||||
assert(0);
|
||||
}
|
||||
} // namespace handles
|
||||
|
196
handles.h
196
handles.h
@ -1,30 +1,182 @@
|
||||
#pragma once
|
||||
|
||||
#include "common.h"
|
||||
#include <atomic>
|
||||
#include <condition_variable>
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <shared_mutex>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
enum class ObjectType : uint16_t {
|
||||
File,
|
||||
Mapped,
|
||||
Process,
|
||||
Token,
|
||||
Mutex,
|
||||
Event,
|
||||
Semaphore,
|
||||
Thread,
|
||||
Heap,
|
||||
RegistryKey,
|
||||
};
|
||||
|
||||
struct ObjectHeader {
|
||||
const ObjectType type;
|
||||
std::atomic<uint32_t> pointerCount{1};
|
||||
std::atomic<uint32_t> handleCount{0};
|
||||
|
||||
explicit ObjectHeader(ObjectType t) : type(t) {}
|
||||
virtual ~ObjectHeader() = default;
|
||||
|
||||
[[nodiscard]] virtual bool isWaitable() const { return false; }
|
||||
virtual void onDestroy() {}
|
||||
};
|
||||
|
||||
namespace detail {
|
||||
|
||||
inline void ref(ObjectHeader *o) { o->pointerCount.fetch_add(1, std::memory_order_acq_rel); }
|
||||
inline void deref(ObjectHeader *o) {
|
||||
if (o->pointerCount.fetch_sub(1, std::memory_order_acq_rel) == 1) {
|
||||
o->onDestroy();
|
||||
delete o;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
struct WaitableObject : ObjectHeader {
|
||||
std::atomic<bool> signaled{false};
|
||||
std::mutex m;
|
||||
std::condition_variable_any cv;
|
||||
|
||||
using ObjectHeader::ObjectHeader;
|
||||
[[nodiscard]] bool isWaitable() const override { return true; }
|
||||
};
|
||||
|
||||
template <class T> struct Pin {
|
||||
enum class Tag { Acquire, Adopt };
|
||||
|
||||
T *obj = nullptr;
|
||||
|
||||
Pin() = default;
|
||||
Pin(T *o, Tag t) : obj(o) {
|
||||
if (obj && t == Tag::Acquire) {
|
||||
detail::ref(obj);
|
||||
}
|
||||
}
|
||||
|
||||
static Pin acquire(ObjectHeader *o) { return Pin{o, Tag::Acquire}; }
|
||||
static Pin adopt(ObjectHeader *o) { return Pin{o, Tag::Adopt}; }
|
||||
|
||||
Pin(const Pin &) = delete;
|
||||
Pin &operator=(const Pin &) = delete;
|
||||
|
||||
Pin(Pin &&other) noexcept : obj(std::exchange(other.obj, nullptr)) {}
|
||||
Pin &operator=(Pin &&other) noexcept {
|
||||
if (this != &other) {
|
||||
reset();
|
||||
obj = std::exchange(other.obj, nullptr);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
~Pin() { reset(); }
|
||||
|
||||
ObjectHeader *release() { return std::exchange(obj, nullptr); }
|
||||
void reset() {
|
||||
if (obj) {
|
||||
detail::deref(obj);
|
||||
obj = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
T *operator->() const { return obj; }
|
||||
T &operator*() const { return *obj; }
|
||||
explicit operator bool() const { return obj != nullptr; }
|
||||
|
||||
template <class U> Pin<U> downcast() && {
|
||||
if (obj && obj->type == U::kType) {
|
||||
auto *u = static_cast<U *>(obj);
|
||||
obj = nullptr;
|
||||
return Pin<U>::adopt(u);
|
||||
}
|
||||
return Pin<U>{};
|
||||
}
|
||||
};
|
||||
|
||||
using Handle = uint32_t;
|
||||
|
||||
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 HandleEntry {
|
||||
struct ObjectHeader *obj; // intrusive ref (pointerCount++)
|
||||
uint32_t grantedAccess; // effective access mask for this handle
|
||||
uint32_t flags; // inherit/protect/etc
|
||||
ObjectType typeCache; // cached ObjectType for fast getAs
|
||||
uint16_t generation; // must match handle’s generation
|
||||
};
|
||||
|
||||
class HandleTable {
|
||||
public:
|
||||
Handle create(ObjectHeader *obj, uint32_t grantedAccess, uint32_t flags);
|
||||
bool close(Handle h);
|
||||
bool get(Handle h, HandleEntry &out, Pin<ObjectHeader> &pinOut);
|
||||
template <typename T> Pin<T> getAs(Handle h) {
|
||||
static_assert(std::is_base_of_v<ObjectHeader, T>, "T must derive from ObjectHeader");
|
||||
HandleEntry meta{};
|
||||
Pin<ObjectHeader> pin;
|
||||
if (!get(h, meta, pin)) {
|
||||
return {};
|
||||
}
|
||||
if constexpr (std::is_same_v<T, ObjectHeader>) {
|
||||
return std::move(pin);
|
||||
} else if (meta.typeCache != T::kType || pin->type != T::kType) {
|
||||
return {};
|
||||
} else {
|
||||
// Cast directly to T* and transfer ownership to Pin<T>
|
||||
return Pin<T>::adopt(static_cast<T *>(pin.release()));
|
||||
}
|
||||
}
|
||||
bool setInformation(Handle h, uint32_t mask, uint32_t value);
|
||||
bool getInformation(Handle h, uint32_t *outFlags) const;
|
||||
bool duplicateTo(Handle src, HandleTable &dst, Handle *out, uint32_t desiredAccess, bool inherit, uint32_t options);
|
||||
|
||||
private:
|
||||
std::vector<HandleEntry> slots_;
|
||||
std::vector<uint32_t> freeList_;
|
||||
mutable std::shared_mutex mu_;
|
||||
};
|
||||
|
||||
namespace handles {
|
||||
constexpr size_t MAX_HANDLES = 0x10000;
|
||||
|
||||
enum Type {
|
||||
TYPE_UNUSED,
|
||||
TYPE_FILE,
|
||||
TYPE_MAPPED,
|
||||
TYPE_PROCESS,
|
||||
TYPE_TOKEN,
|
||||
TYPE_MUTEX,
|
||||
TYPE_EVENT,
|
||||
TYPE_SEMAPHORE,
|
||||
TYPE_THREAD,
|
||||
TYPE_HEAP,
|
||||
TYPE_REGISTRY_KEY
|
||||
};
|
||||
constexpr size_t MAX_HANDLES = 0x10000;
|
||||
|
||||
struct Data {
|
||||
Type type = TYPE_UNUSED;
|
||||
void *ptr;
|
||||
size_t size;
|
||||
};
|
||||
enum Type {
|
||||
TYPE_UNUSED,
|
||||
TYPE_FILE,
|
||||
TYPE_MAPPED,
|
||||
TYPE_PROCESS,
|
||||
TYPE_TOKEN,
|
||||
TYPE_MUTEX,
|
||||
TYPE_EVENT,
|
||||
TYPE_SEMAPHORE,
|
||||
TYPE_THREAD,
|
||||
TYPE_HEAP,
|
||||
TYPE_REGISTRY_KEY
|
||||
};
|
||||
|
||||
Data dataFromHandle(void *handle, bool pop);
|
||||
void *allocDataHandle(Data data);
|
||||
}
|
||||
struct Data {
|
||||
Type type = TYPE_UNUSED;
|
||||
void *ptr;
|
||||
size_t size;
|
||||
};
|
||||
|
||||
Data dataFromHandle(void *handle, bool pop);
|
||||
void *allocDataHandle(Data data);
|
||||
} // namespace handles
|
||||
|
Loading…
x
Reference in New Issue
Block a user