From e2c2f522ad43152f0cec9060672de954c490391c Mon Sep 17 00:00:00 2001 From: Gliniak Date: Sat, 28 Dec 2024 10:21:27 +0100 Subject: [PATCH] [Kernel] Rewrite of: ObjectTable --- src/xenia/kernel/kernel_state.cc | 2 +- src/xenia/kernel/util/object_table.cc | 388 ++++++++++++-------------- src/xenia/kernel/util/object_table.h | 91 ++++-- src/xenia/kernel/xobject.h | 3 - 4 files changed, 257 insertions(+), 227 deletions(-) diff --git a/src/xenia/kernel/kernel_state.cc b/src/xenia/kernel/kernel_state.cc index 8b9bbe34f..7290c1666 100644 --- a/src/xenia/kernel/kernel_state.cc +++ b/src/xenia/kernel/kernel_state.cc @@ -710,7 +710,7 @@ void KernelState::UnloadUserModule(const object_ref& module, return e->path() == module->path(); }) == user_modules_.end()); - object_table()->ReleaseHandleInLock(module->handle()); + object_table()->ReleaseHandle(module->handle()); } void KernelState::TerminateTitle() { diff --git a/src/xenia/kernel/util/object_table.cc b/src/xenia/kernel/util/object_table.cc index 4568c72d1..7ce8742c4 100644 --- a/src/xenia/kernel/util/object_table.cc +++ b/src/xenia/kernel/util/object_table.cc @@ -28,90 +28,77 @@ ObjectTable::~ObjectTable() { Reset(); } void ObjectTable::Reset() { auto global_lock = global_critical_region_.Acquire(); - // Release all objects. - for (uint32_t n = 0; n < table_capacity_; n++) { - ObjectTableEntry& entry = table_[n]; - if (entry.object) { - entry.object->Release(); - } + host_object_table_.Reset(); + for (auto& [_, table] : guest_object_table_) { + table.Reset(); } - 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) { - // Find a free slot. - uint32_t slot = host ? last_free_host_entry_ : last_free_entry_; - uint32_t capacity = host ? host_table_capacity_ : table_capacity_; - uint32_t scan_count = 0; - while (scan_count < capacity) { - ObjectTableEntry& entry = host ? host_table_[slot] : table_[slot]; - if (!entry.object) { - *out_slot = slot; - return X_STATUS_SUCCESS; - } - scan_count++; - slot = (slot + 1) % capacity; - if (slot == 0 && host) { - // Never allow 0 handles. - scan_count++; - slot++; - } +uint32_t ObjectTable::GetFirstFreeSlot( + ObjectTable::ObjectTableInfo* const table) { + auto global_lock = global_critical_region_.Acquire(); + // Check if + if (!table->freed_table_slots_.empty()) { + uint32_t slot = table->freed_table_slots_.front(); + table->freed_table_slots_.erase(table->freed_table_slots_.begin()); + return slot; + } + // Check if latest used slot is free again. + ObjectTableEntry& entry = table->table_[table->previous_free_slot_]; + if (!entry.object) { + return table->previous_free_slot_; } - // Table out of slots, expand. - uint32_t new_table_capacity = std::max(16 * 1024u, capacity * 2); - if (!Resize(new_table_capacity, host)) { - return X_STATUS_NO_MEMORY; + if (++table->previous_free_slot_ >= table->table_.size()) { + table->table_.reserve(table->table_.size() * 2); } - // Never allow 0 handles on host. - slot = host ? ++last_free_host_entry_ : last_free_entry_++; - *out_slot = slot; + return table->previous_free_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; } -bool ObjectTable::Resize(uint32_t new_capacity, bool host) { - uint32_t capacity = host ? host_table_capacity_ : table_capacity_; - uint32_t new_size = new_capacity * sizeof(ObjectTableEntry); - uint32_t old_size = capacity * sizeof(ObjectTableEntry); - auto new_table = reinterpret_cast( - realloc(host ? host_table_ : table_, new_size)); - if (!new_table) { - return false; +ObjectTable::ObjectTableInfo* const ObjectTable::GetTableForObject( + const XObject* const obj) { + if (obj->is_host_object()) { + return &host_object_table_; } - // Zero out new entries. - if (new_size > old_size) { - std::memset(reinterpret_cast(new_table) + old_size, 0, - new_size - old_size); + if (obj->type() == XObject::Type::Thread) { + // Switcharoo for title/system thread! + return &guest_object_table_[kGuestHandleTitleThreadBase]; } - if (host) { - last_free_host_entry_ = capacity; - host_table_capacity_ = new_capacity; - host_table_ = new_table; - } else { - last_free_entry_ = capacity; - table_capacity_ = new_capacity; - table_ = new_table; + return &guest_object_table_[kGuestHandleBase]; +} + +ObjectTable::ObjectTableInfo* const ObjectTable::GetTableForObject( + const X_HANDLE handle) { + if (!handle) { + return nullptr; } - 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) { @@ -119,39 +106,24 @@ X_STATUS ObjectTable::AddHandle(XObject* object, X_HANDLE* out_handle) { uint32_t handle = 0; { - auto global_lock = global_critical_region_.Acquire(); + auto table = GetTableForObject(object); + uint32_t slot = GetFirstFreeSlot(table); - // Find a free slot. - uint32_t slot = 0; - bool host_object = object->is_host_object(); - result = FindFreeSlot(&slot, host_object); + ObjectTableEntry& entry = table->table_[slot]; - // Stash. - if (XSUCCEEDED(result)) { - ObjectTableEntry& entry = host_object ? host_table_[slot] : table_[slot]; - entry.object = object; - 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); + entry.object = object; + entry.handle_ref_count = 1; + handle = table->GetSlotHandle(slot); + object->handles().push_back(handle); - // Retain so long as the object is in the table. - object->Retain(); + // Retain so long as the object is in the table. + 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) { - *out_handle = handle; - } + if (out_handle) { + *out_handle = handle; } return result; @@ -161,40 +133,59 @@ X_STATUS ObjectTable::DuplicateHandle(X_HANDLE handle, X_HANDLE* out_handle) { X_STATUS result = X_STATUS_SUCCESS; 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); + if (object) { - result = AddHandle(object, out_handle); - object->Release(); // Release the ref that LookupObject took + ObjectTableEntry& entry = table->table_[slot]; + 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 { result = X_STATUS_INVALID_HANDLE; } - return result; } 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) { return X_STATUS_INVALID_HANDLE; } + std::lock_guard lock(entry->access_mutex); entry->handle_ref_count++; return X_STATUS_SUCCESS; } 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); -} -X_STATUS ObjectTable::ReleaseHandleInLock(X_HANDLE handle) { - ObjectTableEntry* entry = LookupTableInLock(handle); + ObjectTableEntry* entry = LookupTable(handle); if (!entry) { return X_STATUS_INVALID_HANDLE; } + std::lock_guard lock(entry->access_mutex); if (--entry->handle_ref_count == 0) { // No more references. Remove it from the table. return RemoveHandle(handle); @@ -204,6 +195,7 @@ X_STATUS ObjectTable::ReleaseHandleInLock(X_HANDLE handle) { // (but not a failure code) return X_STATUS_SUCCESS; } + X_STATUS ObjectTable::RemoveHandle(X_HANDLE handle) { X_STATUS result = X_STATUS_SUCCESS; @@ -211,9 +203,10 @@ X_STATUS ObjectTable::RemoveHandle(X_HANDLE handle) { if (!handle) { return X_STATUS_INVALID_HANDLE; } - auto global_lock = global_critical_region_.Acquire(); - ObjectTableEntry* entry = LookupTableInLock(handle); + ObjectTableEntry* entry = LookupTable(handle); + std::lock_guard lock(entry->access_mutex); + if (!entry) { return X_STATUS_INVALID_HANDLE; } @@ -231,6 +224,13 @@ X_STATUS ObjectTable::RemoveHandle(X_HANDLE handle) { 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()); // Remove object name from mapping to prevent naming collision. @@ -244,65 +244,19 @@ X_STATUS ObjectTable::RemoveHandle(X_HANDLE handle) { return X_STATUS_SUCCESS; } -std::vector> ObjectTable::GetAllObjects() { - auto lock = global_critical_region_.Acquire(); - std::vector> 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(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(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) { - auto global_lock = global_critical_region_.Acquire(); - return LookupTableInLock(handle); -} - -ObjectTable::ObjectTableEntry* ObjectTable::LookupTableInLock(X_HANDLE handle) { - handle = TranslateHandle(handle); - if (!handle) { + auto table = GetTableForObject(handle); + if (!table) { return nullptr; } + auto* entry = &table->table_[table->GetHandleSlot(handle)]; - const bool is_host_object = XObject::is_handle_host_object(handle); - uint32_t slot = GetHandleSlot(handle, is_host_object); - if (is_host_object) { - if (slot <= host_table_capacity_) { - return &host_table_[slot]; - } - } else if (slot <= table_capacity_) { - return &table_[slot]; + std::lock_guard lock(entry->access_mutex); + + if (!entry->object) { + return nullptr; } - - return nullptr; + return entry; } // Generic lookup @@ -321,64 +275,80 @@ XObject* ObjectTable::LookupObject(X_HANDLE handle, bool already_locked) { } XObject* object = nullptr; - if (!already_locked) { - global_critical_region_.mutex().lock(); + auto entry = LookupTable(handle); + if (!entry) { + return nullptr; } - const bool is_host_object = XObject::is_handle_host_object(handle); - 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; - } - } + std::lock_guard lock(entry->access_mutex); // Retain the object pointer. - if (object) { - object->Retain(); + if (entry->object) { + entry->object->Retain(); } - if (!already_locked) { - global_critical_region_.mutex().unlock(); - } + return entry->object; +} - return object; +std::vector> ObjectTable::GetAllObjects() { + auto lock = global_critical_region_.Acquire(); + std::vector> 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(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, std::vector>* results) { auto global_lock = global_critical_region_.Acquire(); - for (uint32_t slot = 0; slot < host_table_capacity_; ++slot) { - auto& entry = host_table_[slot]; - if (entry.object) { - if (entry.object->type() == type) { + + if (type == XObject::Type::Thread) { + for (auto& [_, entry] : + guest_object_table_[kGuestHandleTitleThreadBase].table_) { + if (entry.object) { entry.object->Retain(); results->push_back(object_ref(entry.object)); } } + return; } - for (uint32_t slot = 0; slot < table_capacity_; ++slot) { - auto& entry = table_[slot]; - if (entry.object) { - if (entry.object->type() == type) { - entry.object->Retain(); - results->push_back(object_ref(entry.object)); - } + + for (auto& [_, entry] : guest_object_table_[kGuestHandleBase].table_) { + if (entry.object && entry.object->type() == type) { + entry.object->Retain(); + results->push_back(object_ref(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 // not a special 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_HANDLE handle) { 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; } - name_table_.insert({string_key_case::create(name), handle}); + guest_name_table_.insert({string_key_case::create(name), handle}); return X_STATUS_SUCCESS; } void ObjectTable::RemoveNameMapping(const std::string_view name) { // Names are case-insensitive. auto global_lock = global_critical_region_.Acquire(); - auto it = name_table_.find(string_key_case(name)); - if (it != name_table_.end()) { - name_table_.erase(it); + auto it = guest_name_table_.find(string_key_case(name)); + if (it != guest_name_table_.end()) { + guest_name_table_.erase(it); } } @@ -413,8 +384,8 @@ X_STATUS ObjectTable::GetObjectByName(const std::string_view name, X_HANDLE* out_handle) { // Names are case-insensitive. auto global_lock = global_critical_region_.Acquire(); - auto it = name_table_.find(string_key_case(name)); - if (it == name_table_.end()) { + auto it = guest_name_table_.find(string_key_case(name)); + if (it == guest_name_table_.end()) { *out_handle = X_INVALID_HANDLE_VALUE; 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) { + /* stream->Write(host_table_capacity_); for (uint32_t i = 0; i < host_table_capacity_; i++) { auto& entry = host_table_[i]; @@ -442,11 +414,12 @@ bool ObjectTable::Save(ByteStream* stream) { auto& entry = table_[i]; stream->Write(entry.handle_ref_count); } - + */ return true; } bool ObjectTable::Restore(ByteStream* stream) { + /* Resize(stream->Read(), true); for (uint32_t i = 0; i < host_table_capacity_; i++) { auto& entry = host_table_[i]; @@ -460,11 +433,12 @@ bool ObjectTable::Restore(ByteStream* stream) { // entry.object = nullptr; entry.handle_ref_count = stream->Read(); } - + */ return true; } X_STATUS ObjectTable::RestoreHandle(X_HANDLE handle, XObject* object) { + /* const bool is_host_object = XObject::is_handle_host_object(handle); uint32_t slot = GetHandleSlot(handle, is_host_object); 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; object->Retain(); } - + */ return X_STATUS_SUCCESS; } diff --git a/src/xenia/kernel/util/object_table.h b/src/xenia/kernel/util/object_table.h index c055210a4..7f3ac22d2 100644 --- a/src/xenia/kernel/util/object_table.h +++ b/src/xenia/kernel/util/object_table.h @@ -38,7 +38,6 @@ class ObjectTable { X_STATUS DuplicateHandle(X_HANDLE orig, X_HANDLE* out_handle); X_STATUS RetainHandle(X_HANDLE handle); X_STATUS ReleaseHandle(X_HANDLE handle); - X_STATUS ReleaseHandleInLock(X_HANDLE handle); X_STATUS RemoveHandle(X_HANDLE handle); bool Save(ByteStream* stream); @@ -82,31 +81,91 @@ class ObjectTable { private: struct ObjectTableEntry { + xe::xe_fast_mutex access_mutex = {}; int handle_ref_count = 0; 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 table_ = {}; + std::vector 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); XObject* LookupObject(X_HANDLE handle, bool already_locked); void GetObjectsByType(XObject::Type type, std::vector>* results); - X_HANDLE TranslateHandle(X_HANDLE handle); - static constexpr uint32_t GetHandleSlot(X_HANDLE handle, bool host) { - handle &= host ? ~XObject::kHandleHostBase : ~XObject::kHandleBase; - return handle >> 2; - } - X_STATUS FindFreeSlot(uint32_t* out_slot, bool host); - bool Resize(uint32_t new_capacity, bool host); + X_HANDLE TranslateHandle(X_HANDLE handle) const; + + X_STATUS FindFreeSlot(const XObject* const object, uint32_t* out_slot); + + ObjectTableInfo* const GetTableForObject(const XObject* const obj); + ObjectTableInfo* const GetTableForObject(const X_HANDLE handle); + uint32_t GetFirstFreeSlot(ObjectTableInfo* const table); xe::global_critical_region global_critical_region_; - uint32_t table_capacity_ = 0; - uint32_t host_table_capacity_ = 0; - ObjectTableEntry* table_ = nullptr; - ObjectTableEntry* host_table_ = nullptr; - uint32_t last_free_entry_ = 0; - uint32_t last_free_host_entry_ = 0; - std::unordered_map name_table_; + + ObjectTableInfo host_object_table_; + + // Because of a ambiguity between host and guest socket handles there is a + // trick to move socket handles a bit. + static constexpr uint32_t kGuestSocketHandleBase = 0x00100000; + 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 guest_object_table_ = { + //{kGuestSocketHandleBase, ObjectTableInfo(kGuestSocketHandleBase)}, + {kGuestHandleBase, ObjectTableInfo(kGuestHandleBase)}, + {kGuestHandleTitleThreadBase, + ObjectTableInfo(kGuestHandleTitleThreadBase)}, + {kGuestHandleSystemThreadBase, + ObjectTableInfo(kGuestHandleSystemThreadBase)}}; + + std::unordered_map guest_name_table_; }; // Generic lookup diff --git a/src/xenia/kernel/xobject.h b/src/xenia/kernel/xobject.h index 293182a96..a25917358 100644 --- a/src/xenia/kernel/xobject.h +++ b/src/xenia/kernel/xobject.h @@ -197,9 +197,6 @@ class XObject { static object_ref Restore(KernelState* kernel_state, Type type, ByteStream* stream); - static constexpr bool is_handle_host_object(X_HANDLE handle) { - return handle > XObject::kHandleHostBase && handle < XObject::kHandleBase; - }; // Reference() // Dereference()