[Kernel] Rewrite of: ObjectTable
This commit is contained in:
parent
580b1f4345
commit
e2c2f522ad
|
@ -710,7 +710,7 @@ void KernelState::UnloadUserModule(const object_ref<UserModule>& module,
|
||||||
return e->path() == module->path();
|
return e->path() == module->path();
|
||||||
}) == user_modules_.end());
|
}) == user_modules_.end());
|
||||||
|
|
||||||
object_table()->ReleaseHandleInLock(module->handle());
|
object_table()->ReleaseHandle(module->handle());
|
||||||
}
|
}
|
||||||
|
|
||||||
void KernelState::TerminateTitle() {
|
void KernelState::TerminateTitle() {
|
||||||
|
|
|
@ -28,90 +28,77 @@ ObjectTable::~ObjectTable() { Reset(); }
|
||||||
void ObjectTable::Reset() {
|
void ObjectTable::Reset() {
|
||||||
auto global_lock = global_critical_region_.Acquire();
|
auto global_lock = global_critical_region_.Acquire();
|
||||||
|
|
||||||
// Release all objects.
|
host_object_table_.Reset();
|
||||||
for (uint32_t n = 0; n < table_capacity_; n++) {
|
for (auto& [_, table] : guest_object_table_) {
|
||||||
ObjectTableEntry& entry = table_[n];
|
table.Reset();
|
||||||
if (entry.object) {
|
|
||||||
entry.object->Release();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
for (uint32_t n = 0; n < host_table_capacity_; n++) {
|
|
||||||
ObjectTableEntry& entry = host_table_[n];
|
|
||||||
if (entry.object) {
|
|
||||||
entry.object->Release();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
table_capacity_ = 0;
|
|
||||||
host_table_capacity_ = 0;
|
|
||||||
last_free_entry_ = 0;
|
|
||||||
last_free_host_entry_ = 0;
|
|
||||||
free(table_);
|
|
||||||
table_ = nullptr;
|
|
||||||
free(host_table_);
|
|
||||||
host_table_ = nullptr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
X_STATUS ObjectTable::FindFreeSlot(uint32_t* out_slot, bool host) {
|
uint32_t ObjectTable::GetFirstFreeSlot(
|
||||||
// Find a free slot.
|
ObjectTable::ObjectTableInfo* const table) {
|
||||||
uint32_t slot = host ? last_free_host_entry_ : last_free_entry_;
|
auto global_lock = global_critical_region_.Acquire();
|
||||||
uint32_t capacity = host ? host_table_capacity_ : table_capacity_;
|
// Check if
|
||||||
uint32_t scan_count = 0;
|
if (!table->freed_table_slots_.empty()) {
|
||||||
while (scan_count < capacity) {
|
uint32_t slot = table->freed_table_slots_.front();
|
||||||
ObjectTableEntry& entry = host ? host_table_[slot] : table_[slot];
|
table->freed_table_slots_.erase(table->freed_table_slots_.begin());
|
||||||
if (!entry.object) {
|
return slot;
|
||||||
*out_slot = slot;
|
}
|
||||||
return X_STATUS_SUCCESS;
|
// Check if latest used slot is free again.
|
||||||
}
|
ObjectTableEntry& entry = table->table_[table->previous_free_slot_];
|
||||||
scan_count++;
|
if (!entry.object) {
|
||||||
slot = (slot + 1) % capacity;
|
return table->previous_free_slot_;
|
||||||
if (slot == 0 && host) {
|
|
||||||
// Never allow 0 handles.
|
|
||||||
scan_count++;
|
|
||||||
slot++;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Table out of slots, expand.
|
if (++table->previous_free_slot_ >= table->table_.size()) {
|
||||||
uint32_t new_table_capacity = std::max(16 * 1024u, capacity * 2);
|
table->table_.reserve(table->table_.size() * 2);
|
||||||
if (!Resize(new_table_capacity, host)) {
|
|
||||||
return X_STATUS_NO_MEMORY;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Never allow 0 handles on host.
|
return table->previous_free_slot_;
|
||||||
slot = host ? ++last_free_host_entry_ : last_free_entry_++;
|
}
|
||||||
*out_slot = slot;
|
|
||||||
|
|
||||||
|
X_STATUS ObjectTable::FindFreeSlot(const XObject* const object,
|
||||||
|
uint32_t* out_slot) {
|
||||||
|
auto object_table = GetTableForObject(object);
|
||||||
|
|
||||||
|
*out_slot = GetFirstFreeSlot(object_table);
|
||||||
return X_STATUS_SUCCESS;
|
return X_STATUS_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ObjectTable::Resize(uint32_t new_capacity, bool host) {
|
ObjectTable::ObjectTableInfo* const ObjectTable::GetTableForObject(
|
||||||
uint32_t capacity = host ? host_table_capacity_ : table_capacity_;
|
const XObject* const obj) {
|
||||||
uint32_t new_size = new_capacity * sizeof(ObjectTableEntry);
|
if (obj->is_host_object()) {
|
||||||
uint32_t old_size = capacity * sizeof(ObjectTableEntry);
|
return &host_object_table_;
|
||||||
auto new_table = reinterpret_cast<ObjectTableEntry*>(
|
|
||||||
realloc(host ? host_table_ : table_, new_size));
|
|
||||||
if (!new_table) {
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Zero out new entries.
|
if (obj->type() == XObject::Type::Thread) {
|
||||||
if (new_size > old_size) {
|
// Switcharoo for title/system thread!
|
||||||
std::memset(reinterpret_cast<uint8_t*>(new_table) + old_size, 0,
|
return &guest_object_table_[kGuestHandleTitleThreadBase];
|
||||||
new_size - old_size);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (host) {
|
return &guest_object_table_[kGuestHandleBase];
|
||||||
last_free_host_entry_ = capacity;
|
}
|
||||||
host_table_capacity_ = new_capacity;
|
|
||||||
host_table_ = new_table;
|
ObjectTable::ObjectTableInfo* const ObjectTable::GetTableForObject(
|
||||||
} else {
|
const X_HANDLE handle) {
|
||||||
last_free_entry_ = capacity;
|
if (!handle) {
|
||||||
table_capacity_ = new_capacity;
|
return nullptr;
|
||||||
table_ = new_table;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
if (handle & 0x00FF0000) {
|
||||||
|
return &guest_object_table_[kGuestHandleBase];
|
||||||
|
}
|
||||||
|
|
||||||
|
const uint32_t handle_mask = handle & 0xFF000000;
|
||||||
|
|
||||||
|
if (handle_mask == kGuestHandleTitleThreadBase) {
|
||||||
|
return &guest_object_table_[kGuestHandleTitleThreadBase];
|
||||||
|
}
|
||||||
|
if (handle_mask == kGuestHandleSystemThreadBase) {
|
||||||
|
return &guest_object_table_[kGuestHandleSystemThreadBase];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only host check remains
|
||||||
|
return &host_object_table_;
|
||||||
}
|
}
|
||||||
|
|
||||||
X_STATUS ObjectTable::AddHandle(XObject* object, X_HANDLE* out_handle) {
|
X_STATUS ObjectTable::AddHandle(XObject* object, X_HANDLE* out_handle) {
|
||||||
|
@ -119,39 +106,24 @@ X_STATUS ObjectTable::AddHandle(XObject* object, X_HANDLE* out_handle) {
|
||||||
|
|
||||||
uint32_t handle = 0;
|
uint32_t handle = 0;
|
||||||
{
|
{
|
||||||
auto global_lock = global_critical_region_.Acquire();
|
auto table = GetTableForObject(object);
|
||||||
|
uint32_t slot = GetFirstFreeSlot(table);
|
||||||
|
|
||||||
// Find a free slot.
|
ObjectTableEntry& entry = table->table_[slot];
|
||||||
uint32_t slot = 0;
|
|
||||||
bool host_object = object->is_host_object();
|
|
||||||
result = FindFreeSlot(&slot, host_object);
|
|
||||||
|
|
||||||
// Stash.
|
entry.object = object;
|
||||||
if (XSUCCEEDED(result)) {
|
entry.handle_ref_count = 1;
|
||||||
ObjectTableEntry& entry = host_object ? host_table_[slot] : table_[slot];
|
handle = table->GetSlotHandle(slot);
|
||||||
entry.object = object;
|
object->handles().push_back(handle);
|
||||||
entry.handle_ref_count = 1;
|
|
||||||
handle = slot << 2;
|
|
||||||
if (!host_object) {
|
|
||||||
if (object->type() != XObject::Type::Socket) {
|
|
||||||
handle += XObject::kHandleBase;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
handle += XObject::kHandleHostBase;
|
|
||||||
}
|
|
||||||
object->handles().push_back(handle);
|
|
||||||
|
|
||||||
// Retain so long as the object is in the table.
|
// Retain so long as the object is in the table.
|
||||||
object->Retain();
|
object->Retain();
|
||||||
|
|
||||||
XELOGI("Added handle:{:08X} for {}", handle, typeid(*object).name());
|
XELOGI("Added handle:{:08X} for {}", handle, typeid(*object).name());
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (XSUCCEEDED(result)) {
|
if (out_handle) {
|
||||||
if (out_handle) {
|
*out_handle = handle;
|
||||||
*out_handle = handle;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
@ -161,40 +133,59 @@ X_STATUS ObjectTable::DuplicateHandle(X_HANDLE handle, X_HANDLE* out_handle) {
|
||||||
X_STATUS result = X_STATUS_SUCCESS;
|
X_STATUS result = X_STATUS_SUCCESS;
|
||||||
handle = TranslateHandle(handle);
|
handle = TranslateHandle(handle);
|
||||||
|
|
||||||
|
// For whatever reason all duplicates are going into base mask even threads.
|
||||||
|
auto table = &guest_object_table_[kGuestHandleBase];
|
||||||
|
uint32_t slot = GetFirstFreeSlot(table);
|
||||||
|
|
||||||
XObject* object = LookupObject(handle, false);
|
XObject* object = LookupObject(handle, false);
|
||||||
|
|
||||||
if (object) {
|
if (object) {
|
||||||
result = AddHandle(object, out_handle);
|
ObjectTableEntry& entry = table->table_[slot];
|
||||||
object->Release(); // Release the ref that LookupObject took
|
entry.object = object;
|
||||||
|
entry.handle_ref_count = 1;
|
||||||
|
*out_handle = table->GetSlotHandle(slot);
|
||||||
|
object->handles().push_back(*out_handle);
|
||||||
|
|
||||||
|
// Retain so long as the object is in the table.
|
||||||
|
object->Retain();
|
||||||
|
|
||||||
|
XELOGI("Duplicated handle:{:08X} to {:08X} for {}", handle, *out_handle,
|
||||||
|
typeid(*object).name());
|
||||||
} else {
|
} else {
|
||||||
result = X_STATUS_INVALID_HANDLE;
|
result = X_STATUS_INVALID_HANDLE;
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
X_STATUS ObjectTable::RetainHandle(X_HANDLE handle) {
|
X_STATUS ObjectTable::RetainHandle(X_HANDLE handle) {
|
||||||
auto global_lock = global_critical_region_.Acquire();
|
handle = TranslateHandle(handle);
|
||||||
|
if (!handle) {
|
||||||
|
return X_STATUS_INVALID_HANDLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
ObjectTableEntry* entry = LookupTable(handle);
|
||||||
|
|
||||||
ObjectTableEntry* entry = LookupTableInLock(handle);
|
|
||||||
if (!entry) {
|
if (!entry) {
|
||||||
return X_STATUS_INVALID_HANDLE;
|
return X_STATUS_INVALID_HANDLE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::lock_guard<xe::xe_fast_mutex> lock(entry->access_mutex);
|
||||||
entry->handle_ref_count++;
|
entry->handle_ref_count++;
|
||||||
return X_STATUS_SUCCESS;
|
return X_STATUS_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
X_STATUS ObjectTable::ReleaseHandle(X_HANDLE handle) {
|
X_STATUS ObjectTable::ReleaseHandle(X_HANDLE handle) {
|
||||||
auto global_lock = global_critical_region_.Acquire();
|
handle = TranslateHandle(handle);
|
||||||
|
if (!handle) {
|
||||||
|
return X_STATUS_INVALID_HANDLE;
|
||||||
|
}
|
||||||
|
|
||||||
return ReleaseHandleInLock(handle);
|
ObjectTableEntry* entry = LookupTable(handle);
|
||||||
}
|
|
||||||
X_STATUS ObjectTable::ReleaseHandleInLock(X_HANDLE handle) {
|
|
||||||
ObjectTableEntry* entry = LookupTableInLock(handle);
|
|
||||||
if (!entry) {
|
if (!entry) {
|
||||||
return X_STATUS_INVALID_HANDLE;
|
return X_STATUS_INVALID_HANDLE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::lock_guard<xe::xe_fast_mutex> lock(entry->access_mutex);
|
||||||
if (--entry->handle_ref_count == 0) {
|
if (--entry->handle_ref_count == 0) {
|
||||||
// No more references. Remove it from the table.
|
// No more references. Remove it from the table.
|
||||||
return RemoveHandle(handle);
|
return RemoveHandle(handle);
|
||||||
|
@ -204,6 +195,7 @@ X_STATUS ObjectTable::ReleaseHandleInLock(X_HANDLE handle) {
|
||||||
// (but not a failure code)
|
// (but not a failure code)
|
||||||
return X_STATUS_SUCCESS;
|
return X_STATUS_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
X_STATUS ObjectTable::RemoveHandle(X_HANDLE handle) {
|
X_STATUS ObjectTable::RemoveHandle(X_HANDLE handle) {
|
||||||
X_STATUS result = X_STATUS_SUCCESS;
|
X_STATUS result = X_STATUS_SUCCESS;
|
||||||
|
|
||||||
|
@ -211,9 +203,10 @@ X_STATUS ObjectTable::RemoveHandle(X_HANDLE handle) {
|
||||||
if (!handle) {
|
if (!handle) {
|
||||||
return X_STATUS_INVALID_HANDLE;
|
return X_STATUS_INVALID_HANDLE;
|
||||||
}
|
}
|
||||||
auto global_lock = global_critical_region_.Acquire();
|
|
||||||
|
|
||||||
ObjectTableEntry* entry = LookupTableInLock(handle);
|
ObjectTableEntry* entry = LookupTable(handle);
|
||||||
|
std::lock_guard<xe::xe_fast_mutex> lock(entry->access_mutex);
|
||||||
|
|
||||||
if (!entry) {
|
if (!entry) {
|
||||||
return X_STATUS_INVALID_HANDLE;
|
return X_STATUS_INVALID_HANDLE;
|
||||||
}
|
}
|
||||||
|
@ -231,6 +224,13 @@ X_STATUS ObjectTable::RemoveHandle(X_HANDLE handle) {
|
||||||
object->handles().erase(handle_entry);
|
object->handles().erase(handle_entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto table = GetTableForObject(handle);
|
||||||
|
|
||||||
|
{
|
||||||
|
auto global_lock = global_critical_region_.Acquire();
|
||||||
|
table->freed_table_slots_.push_back(table->GetHandleSlot(handle));
|
||||||
|
}
|
||||||
|
|
||||||
XELOGI("Removed handle:{:08X} for {}", handle, typeid(*object).name());
|
XELOGI("Removed handle:{:08X} for {}", handle, typeid(*object).name());
|
||||||
|
|
||||||
// Remove object name from mapping to prevent naming collision.
|
// Remove object name from mapping to prevent naming collision.
|
||||||
|
@ -244,65 +244,19 @@ X_STATUS ObjectTable::RemoveHandle(X_HANDLE handle) {
|
||||||
return X_STATUS_SUCCESS;
|
return X_STATUS_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<object_ref<XObject>> ObjectTable::GetAllObjects() {
|
|
||||||
auto lock = global_critical_region_.Acquire();
|
|
||||||
std::vector<object_ref<XObject>> results;
|
|
||||||
|
|
||||||
for (uint32_t slot = 0; slot < host_table_capacity_; slot++) {
|
|
||||||
auto& entry = host_table_[slot];
|
|
||||||
if (entry.object && std::find(results.begin(), results.end(),
|
|
||||||
entry.object) == results.end()) {
|
|
||||||
entry.object->Retain();
|
|
||||||
results.push_back(object_ref<XObject>(entry.object));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (uint32_t slot = 0; slot < table_capacity_; slot++) {
|
|
||||||
auto& entry = table_[slot];
|
|
||||||
if (entry.object && std::find(results.begin(), results.end(),
|
|
||||||
entry.object) == results.end()) {
|
|
||||||
entry.object->Retain();
|
|
||||||
results.push_back(object_ref<XObject>(entry.object));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return results;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ObjectTable::PurgeAllObjects() {
|
|
||||||
auto lock = global_critical_region_.Acquire();
|
|
||||||
for (uint32_t slot = 0; slot < table_capacity_; slot++) {
|
|
||||||
auto& entry = table_[slot];
|
|
||||||
if (entry.object) {
|
|
||||||
entry.handle_ref_count = 0;
|
|
||||||
entry.object->Release();
|
|
||||||
|
|
||||||
entry.object = nullptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ObjectTable::ObjectTableEntry* ObjectTable::LookupTable(X_HANDLE handle) {
|
ObjectTable::ObjectTableEntry* ObjectTable::LookupTable(X_HANDLE handle) {
|
||||||
auto global_lock = global_critical_region_.Acquire();
|
auto table = GetTableForObject(handle);
|
||||||
return LookupTableInLock(handle);
|
if (!table) {
|
||||||
}
|
|
||||||
|
|
||||||
ObjectTable::ObjectTableEntry* ObjectTable::LookupTableInLock(X_HANDLE handle) {
|
|
||||||
handle = TranslateHandle(handle);
|
|
||||||
if (!handle) {
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
auto* entry = &table->table_[table->GetHandleSlot(handle)];
|
||||||
|
|
||||||
const bool is_host_object = XObject::is_handle_host_object(handle);
|
std::lock_guard<xe::xe_fast_mutex> lock(entry->access_mutex);
|
||||||
uint32_t slot = GetHandleSlot(handle, is_host_object);
|
|
||||||
if (is_host_object) {
|
if (!entry->object) {
|
||||||
if (slot <= host_table_capacity_) {
|
return nullptr;
|
||||||
return &host_table_[slot];
|
|
||||||
}
|
|
||||||
} else if (slot <= table_capacity_) {
|
|
||||||
return &table_[slot];
|
|
||||||
}
|
}
|
||||||
|
return entry;
|
||||||
return nullptr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generic lookup
|
// Generic lookup
|
||||||
|
@ -321,64 +275,80 @@ XObject* ObjectTable::LookupObject(X_HANDLE handle, bool already_locked) {
|
||||||
}
|
}
|
||||||
|
|
||||||
XObject* object = nullptr;
|
XObject* object = nullptr;
|
||||||
if (!already_locked) {
|
auto entry = LookupTable(handle);
|
||||||
global_critical_region_.mutex().lock();
|
if (!entry) {
|
||||||
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
const bool is_host_object = XObject::is_handle_host_object(handle);
|
std::lock_guard<xe::xe_fast_mutex> lock(entry->access_mutex);
|
||||||
uint32_t slot = GetHandleSlot(handle, is_host_object);
|
|
||||||
|
|
||||||
// Verify slot.
|
|
||||||
if (is_host_object) {
|
|
||||||
if (slot < host_table_capacity_) {
|
|
||||||
ObjectTableEntry& entry = host_table_[slot];
|
|
||||||
if (entry.object) {
|
|
||||||
object = entry.object;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (slot < table_capacity_) {
|
|
||||||
ObjectTableEntry& entry = table_[slot];
|
|
||||||
if (entry.object) {
|
|
||||||
object = entry.object;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Retain the object pointer.
|
// Retain the object pointer.
|
||||||
if (object) {
|
if (entry->object) {
|
||||||
object->Retain();
|
entry->object->Retain();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!already_locked) {
|
return entry->object;
|
||||||
global_critical_region_.mutex().unlock();
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return object;
|
std::vector<object_ref<XObject>> ObjectTable::GetAllObjects() {
|
||||||
|
auto lock = global_critical_region_.Acquire();
|
||||||
|
std::vector<object_ref<XObject>> results;
|
||||||
|
|
||||||
|
for (auto& [_, table] : guest_object_table_) {
|
||||||
|
for (uint32_t i = 0; i < table.previous_free_slot_ + 1; i++) {
|
||||||
|
auto& object = table.table_.at(i).object;
|
||||||
|
|
||||||
|
if (!object) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
object->Retain();
|
||||||
|
results.push_back(object_ref<XObject>(object));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ObjectTable::PurgeAllObjects() {
|
||||||
|
auto lock = global_critical_region_.Acquire();
|
||||||
|
|
||||||
|
for (auto& [_, table] : guest_object_table_) {
|
||||||
|
for (auto& [_, entry] : table.table_) {
|
||||||
|
if (!entry.object) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
entry.handle_ref_count = 0;
|
||||||
|
entry.object->Release();
|
||||||
|
entry.object = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ObjectTable::GetObjectsByType(XObject::Type type,
|
void ObjectTable::GetObjectsByType(XObject::Type type,
|
||||||
std::vector<object_ref<XObject>>* results) {
|
std::vector<object_ref<XObject>>* results) {
|
||||||
auto global_lock = global_critical_region_.Acquire();
|
auto global_lock = global_critical_region_.Acquire();
|
||||||
for (uint32_t slot = 0; slot < host_table_capacity_; ++slot) {
|
|
||||||
auto& entry = host_table_[slot];
|
if (type == XObject::Type::Thread) {
|
||||||
if (entry.object) {
|
for (auto& [_, entry] :
|
||||||
if (entry.object->type() == type) {
|
guest_object_table_[kGuestHandleTitleThreadBase].table_) {
|
||||||
|
if (entry.object) {
|
||||||
entry.object->Retain();
|
entry.object->Retain();
|
||||||
results->push_back(object_ref<XObject>(entry.object));
|
results->push_back(object_ref<XObject>(entry.object));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
for (uint32_t slot = 0; slot < table_capacity_; ++slot) {
|
|
||||||
auto& entry = table_[slot];
|
for (auto& [_, entry] : guest_object_table_[kGuestHandleBase].table_) {
|
||||||
if (entry.object) {
|
if (entry.object && entry.object->type() == type) {
|
||||||
if (entry.object->type() == type) {
|
entry.object->Retain();
|
||||||
entry.object->Retain();
|
results->push_back(object_ref<XObject>(entry.object));
|
||||||
results->push_back(object_ref<XObject>(entry.object));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
X_HANDLE ObjectTable::TranslateHandle(X_HANDLE handle) {
|
X_HANDLE ObjectTable::TranslateHandle(X_HANDLE handle) const {
|
||||||
// chrispy: reordered these by likelihood, most likely case is that handle is
|
// chrispy: reordered these by likelihood, most likely case is that handle is
|
||||||
// not a special handle
|
// not a special handle
|
||||||
XE_LIKELY_IF(handle < 0xFFFFFFFE) { return handle; }
|
XE_LIKELY_IF(handle < 0xFFFFFFFE) { return handle; }
|
||||||
|
@ -390,22 +360,23 @@ X_HANDLE ObjectTable::TranslateHandle(X_HANDLE handle) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Name mapping is available only for guest objects!
|
||||||
X_STATUS ObjectTable::AddNameMapping(const std::string_view name,
|
X_STATUS ObjectTable::AddNameMapping(const std::string_view name,
|
||||||
X_HANDLE handle) {
|
X_HANDLE handle) {
|
||||||
auto global_lock = global_critical_region_.Acquire();
|
auto global_lock = global_critical_region_.Acquire();
|
||||||
if (name_table_.count(string_key_case(name))) {
|
if (guest_name_table_.count(string_key_case(name))) {
|
||||||
return X_STATUS_OBJECT_NAME_COLLISION;
|
return X_STATUS_OBJECT_NAME_COLLISION;
|
||||||
}
|
}
|
||||||
name_table_.insert({string_key_case::create(name), handle});
|
guest_name_table_.insert({string_key_case::create(name), handle});
|
||||||
return X_STATUS_SUCCESS;
|
return X_STATUS_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ObjectTable::RemoveNameMapping(const std::string_view name) {
|
void ObjectTable::RemoveNameMapping(const std::string_view name) {
|
||||||
// Names are case-insensitive.
|
// Names are case-insensitive.
|
||||||
auto global_lock = global_critical_region_.Acquire();
|
auto global_lock = global_critical_region_.Acquire();
|
||||||
auto it = name_table_.find(string_key_case(name));
|
auto it = guest_name_table_.find(string_key_case(name));
|
||||||
if (it != name_table_.end()) {
|
if (it != guest_name_table_.end()) {
|
||||||
name_table_.erase(it);
|
guest_name_table_.erase(it);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -413,8 +384,8 @@ X_STATUS ObjectTable::GetObjectByName(const std::string_view name,
|
||||||
X_HANDLE* out_handle) {
|
X_HANDLE* out_handle) {
|
||||||
// Names are case-insensitive.
|
// Names are case-insensitive.
|
||||||
auto global_lock = global_critical_region_.Acquire();
|
auto global_lock = global_critical_region_.Acquire();
|
||||||
auto it = name_table_.find(string_key_case(name));
|
auto it = guest_name_table_.find(string_key_case(name));
|
||||||
if (it == name_table_.end()) {
|
if (it == guest_name_table_.end()) {
|
||||||
*out_handle = X_INVALID_HANDLE_VALUE;
|
*out_handle = X_INVALID_HANDLE_VALUE;
|
||||||
return X_STATUS_OBJECT_NAME_NOT_FOUND;
|
return X_STATUS_OBJECT_NAME_NOT_FOUND;
|
||||||
}
|
}
|
||||||
|
@ -431,6 +402,7 @@ X_STATUS ObjectTable::GetObjectByName(const std::string_view name,
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ObjectTable::Save(ByteStream* stream) {
|
bool ObjectTable::Save(ByteStream* stream) {
|
||||||
|
/*
|
||||||
stream->Write<uint32_t>(host_table_capacity_);
|
stream->Write<uint32_t>(host_table_capacity_);
|
||||||
for (uint32_t i = 0; i < host_table_capacity_; i++) {
|
for (uint32_t i = 0; i < host_table_capacity_; i++) {
|
||||||
auto& entry = host_table_[i];
|
auto& entry = host_table_[i];
|
||||||
|
@ -442,11 +414,12 @@ bool ObjectTable::Save(ByteStream* stream) {
|
||||||
auto& entry = table_[i];
|
auto& entry = table_[i];
|
||||||
stream->Write<int32_t>(entry.handle_ref_count);
|
stream->Write<int32_t>(entry.handle_ref_count);
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ObjectTable::Restore(ByteStream* stream) {
|
bool ObjectTable::Restore(ByteStream* stream) {
|
||||||
|
/*
|
||||||
Resize(stream->Read<uint32_t>(), true);
|
Resize(stream->Read<uint32_t>(), true);
|
||||||
for (uint32_t i = 0; i < host_table_capacity_; i++) {
|
for (uint32_t i = 0; i < host_table_capacity_; i++) {
|
||||||
auto& entry = host_table_[i];
|
auto& entry = host_table_[i];
|
||||||
|
@ -460,11 +433,12 @@ bool ObjectTable::Restore(ByteStream* stream) {
|
||||||
// entry.object = nullptr;
|
// entry.object = nullptr;
|
||||||
entry.handle_ref_count = stream->Read<int32_t>();
|
entry.handle_ref_count = stream->Read<int32_t>();
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
X_STATUS ObjectTable::RestoreHandle(X_HANDLE handle, XObject* object) {
|
X_STATUS ObjectTable::RestoreHandle(X_HANDLE handle, XObject* object) {
|
||||||
|
/*
|
||||||
const bool is_host_object = XObject::is_handle_host_object(handle);
|
const bool is_host_object = XObject::is_handle_host_object(handle);
|
||||||
uint32_t slot = GetHandleSlot(handle, is_host_object);
|
uint32_t slot = GetHandleSlot(handle, is_host_object);
|
||||||
uint32_t capacity = is_host_object ? host_table_capacity_ : table_capacity_;
|
uint32_t capacity = is_host_object ? host_table_capacity_ : table_capacity_;
|
||||||
|
@ -475,7 +449,7 @@ X_STATUS ObjectTable::RestoreHandle(X_HANDLE handle, XObject* object) {
|
||||||
entry.object = object;
|
entry.object = object;
|
||||||
object->Retain();
|
object->Retain();
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
return X_STATUS_SUCCESS;
|
return X_STATUS_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -38,7 +38,6 @@ class ObjectTable {
|
||||||
X_STATUS DuplicateHandle(X_HANDLE orig, X_HANDLE* out_handle);
|
X_STATUS DuplicateHandle(X_HANDLE orig, X_HANDLE* out_handle);
|
||||||
X_STATUS RetainHandle(X_HANDLE handle);
|
X_STATUS RetainHandle(X_HANDLE handle);
|
||||||
X_STATUS ReleaseHandle(X_HANDLE handle);
|
X_STATUS ReleaseHandle(X_HANDLE handle);
|
||||||
X_STATUS ReleaseHandleInLock(X_HANDLE handle);
|
|
||||||
X_STATUS RemoveHandle(X_HANDLE handle);
|
X_STATUS RemoveHandle(X_HANDLE handle);
|
||||||
|
|
||||||
bool Save(ByteStream* stream);
|
bool Save(ByteStream* stream);
|
||||||
|
@ -82,31 +81,91 @@ class ObjectTable {
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct ObjectTableEntry {
|
struct ObjectTableEntry {
|
||||||
|
xe::xe_fast_mutex access_mutex = {};
|
||||||
int handle_ref_count = 0;
|
int handle_ref_count = 0;
|
||||||
XObject* object = nullptr;
|
XObject* object = nullptr;
|
||||||
};
|
};
|
||||||
ObjectTableEntry* LookupTableInLock(X_HANDLE handle);
|
|
||||||
|
struct ObjectTableInfo {
|
||||||
|
uint32_t table_base_handle_offset_ = 0;
|
||||||
|
uint32_t previous_free_slot_ = 0;
|
||||||
|
std::unordered_map<uint32_t, ObjectTableEntry> table_ = {};
|
||||||
|
std::vector<uint32_t> freed_table_slots_ = {};
|
||||||
|
|
||||||
|
// Ctor for host objects
|
||||||
|
ObjectTableInfo() {
|
||||||
|
previous_free_slot_ = 1;
|
||||||
|
|
||||||
|
freed_table_slots_.reserve(128);
|
||||||
|
table_.reserve(1024);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Ctor for guest objects
|
||||||
|
ObjectTableInfo(uint32_t base_handle_offset) {
|
||||||
|
table_base_handle_offset_ = base_handle_offset;
|
||||||
|
|
||||||
|
freed_table_slots_.reserve(128);
|
||||||
|
table_.reserve(1024);
|
||||||
|
};
|
||||||
|
|
||||||
|
X_HANDLE GetSlotHandle(uint32_t slot) {
|
||||||
|
return (slot << 2) + table_base_handle_offset_;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t GetHandleSlot(X_HANDLE handle) {
|
||||||
|
return (handle - table_base_handle_offset_) >> 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Reset() {
|
||||||
|
for (auto& [_, entry] : table_) {
|
||||||
|
if (entry.object) {
|
||||||
|
entry.object->Release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
previous_free_slot_ = 1;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
ObjectTableEntry* LookupTable(X_HANDLE handle);
|
ObjectTableEntry* LookupTable(X_HANDLE handle);
|
||||||
XObject* LookupObject(X_HANDLE handle, bool already_locked);
|
XObject* LookupObject(X_HANDLE handle, bool already_locked);
|
||||||
void GetObjectsByType(XObject::Type type,
|
void GetObjectsByType(XObject::Type type,
|
||||||
std::vector<object_ref<XObject>>* results);
|
std::vector<object_ref<XObject>>* results);
|
||||||
|
|
||||||
X_HANDLE TranslateHandle(X_HANDLE handle);
|
X_HANDLE TranslateHandle(X_HANDLE handle) const;
|
||||||
static constexpr uint32_t GetHandleSlot(X_HANDLE handle, bool host) {
|
|
||||||
handle &= host ? ~XObject::kHandleHostBase : ~XObject::kHandleBase;
|
X_STATUS FindFreeSlot(const XObject* const object, uint32_t* out_slot);
|
||||||
return handle >> 2;
|
|
||||||
}
|
ObjectTableInfo* const GetTableForObject(const XObject* const obj);
|
||||||
X_STATUS FindFreeSlot(uint32_t* out_slot, bool host);
|
ObjectTableInfo* const GetTableForObject(const X_HANDLE handle);
|
||||||
bool Resize(uint32_t new_capacity, bool host);
|
uint32_t GetFirstFreeSlot(ObjectTableInfo* const table);
|
||||||
|
|
||||||
xe::global_critical_region global_critical_region_;
|
xe::global_critical_region global_critical_region_;
|
||||||
uint32_t table_capacity_ = 0;
|
|
||||||
uint32_t host_table_capacity_ = 0;
|
ObjectTableInfo host_object_table_;
|
||||||
ObjectTableEntry* table_ = nullptr;
|
|
||||||
ObjectTableEntry* host_table_ = nullptr;
|
// Because of a ambiguity between host and guest socket handles there is a
|
||||||
uint32_t last_free_entry_ = 0;
|
// trick to move socket handles a bit.
|
||||||
uint32_t last_free_host_entry_ = 0;
|
static constexpr uint32_t kGuestSocketHandleBase = 0x00100000;
|
||||||
std::unordered_map<string_key_case, X_HANDLE> name_table_;
|
static constexpr uint32_t kGuestHandleBase = 0x00100000; // 0xF8000000;
|
||||||
|
static constexpr uint32_t kGuestHandleTitleThreadBase =
|
||||||
|
0xF8000000; // 0xF9000000;
|
||||||
|
static constexpr uint32_t kGuestHandleSystemThreadBase = 0xFB000000;
|
||||||
|
// There are multiple tables for guest objects.
|
||||||
|
// There are at least 4 known tables.
|
||||||
|
// 0x00100000 offset table - Used for sockets
|
||||||
|
// 0xF8000000 table - Used as a base table for XObject
|
||||||
|
// 0xF9000000 table - Used by title threads
|
||||||
|
// 0xFB000000 table - Used by system threads
|
||||||
|
std::map<const uint32_t, ObjectTableInfo> guest_object_table_ = {
|
||||||
|
//{kGuestSocketHandleBase, ObjectTableInfo(kGuestSocketHandleBase)},
|
||||||
|
{kGuestHandleBase, ObjectTableInfo(kGuestHandleBase)},
|
||||||
|
{kGuestHandleTitleThreadBase,
|
||||||
|
ObjectTableInfo(kGuestHandleTitleThreadBase)},
|
||||||
|
{kGuestHandleSystemThreadBase,
|
||||||
|
ObjectTableInfo(kGuestHandleSystemThreadBase)}};
|
||||||
|
|
||||||
|
std::unordered_map<string_key_case, X_HANDLE> guest_name_table_;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Generic lookup
|
// Generic lookup
|
||||||
|
|
|
@ -197,9 +197,6 @@ class XObject {
|
||||||
static object_ref<XObject> Restore(KernelState* kernel_state, Type type,
|
static object_ref<XObject> Restore(KernelState* kernel_state, Type type,
|
||||||
ByteStream* stream);
|
ByteStream* stream);
|
||||||
|
|
||||||
static constexpr bool is_handle_host_object(X_HANDLE handle) {
|
|
||||||
return handle > XObject::kHandleHostBase && handle < XObject::kHandleBase;
|
|
||||||
};
|
|
||||||
// Reference()
|
// Reference()
|
||||||
// Dereference()
|
// Dereference()
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue