diff --git a/src/xenia/base/spinlock.h b/src/xenia/base/spinlock.h new file mode 100644 index 000000000..c857a42db --- /dev/null +++ b/src/xenia/base/spinlock.h @@ -0,0 +1,30 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2024 Xenia Canary. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +#ifndef XENIA_BASE_SPINLOCK_H_ +#define XENIA_BASE_SPINLOCK_H_ + +#include + +namespace xe { +class spinlock { + public: + void lock() { + while (locked.test_and_set(std::memory_order_acquire)) { + ; + } + } + void unlock() { locked.clear(std::memory_order_release); } + + private: + std::atomic_flag locked = ATOMIC_FLAG_INIT; +}; + +} // namespace xe +#endif \ No newline at end of file diff --git a/src/xenia/kernel/kernel_state.cc b/src/xenia/kernel/kernel_state.cc index aef639dda..3cc037e62 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..6f621e741 100644 --- a/src/xenia/kernel/util/object_table.cc +++ b/src/xenia/kernel/util/object_table.cc @@ -26,132 +26,104 @@ ObjectTable::ObjectTable() {} ObjectTable::~ObjectTable() { Reset(); } void ObjectTable::Reset() { - auto global_lock = global_critical_region_.Acquire(); + std::lock_guard lock(spinlock_); - // 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) { + // 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) { X_STATUS result = X_STATUS_SUCCESS; uint32_t handle = 0; - { - auto global_lock = global_critical_region_.Acquire(); - // Find a free slot. - uint32_t slot = 0; - bool host_object = object->is_host_object(); - result = FindFreeSlot(&slot, host_object); + std::lock_guard lock(spinlock_); - // 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); + auto table = GetTableForObject(object); + uint32_t slot = GetFirstFreeSlot(table); - // Retain so long as the object is in the table. - object->Retain(); + ObjectTableEntry& entry = table->table_[slot]; - XELOGI("Added handle:{:08X} for {}", handle, typeid(*object).name()); - } - } + entry.object = object; + entry.handle_ref_count = 1; + handle = table->GetSlotHandle(slot); + object->handles().push_back(handle); - if (XSUCCEEDED(result)) { - if (out_handle) { - *out_handle = handle; - } + // Retain so long as the object is in the table. + object->Retain(); + + XELOGI("Added handle:{:08X} for {}", handle, typeid(*object).name()); + + if (out_handle) { + *out_handle = handle; } return result; @@ -161,21 +133,40 @@ X_STATUS ObjectTable::DuplicateHandle(X_HANDLE handle, X_HANDLE* out_handle) { X_STATUS result = X_STATUS_SUCCESS; handle = TranslateHandle(handle); + std::lock_guard lock(spinlock_); + // 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 = LookupTableInLock(handle); + ObjectTableEntry* entry = LookupTable(handle); + + std::lock_guard lock(spinlock_); if (!entry) { return X_STATUS_INVALID_HANDLE; } @@ -185,17 +176,19 @@ X_STATUS ObjectTable::RetainHandle(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); -} -X_STATUS ObjectTable::ReleaseHandleInLock(X_HANDLE handle) { - ObjectTableEntry* entry = LookupTableInLock(handle); + std::lock_guard lock(spinlock_); + ObjectTableEntry* entry = LookupTable(handle); if (!entry) { return X_STATUS_INVALID_HANDLE; } if (--entry->handle_ref_count == 0) { + lock.~lock_guard(); // No more references. Remove it from the table. return RemoveHandle(handle); } @@ -204,6 +197,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 +205,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); + std::lock_guard lock(spinlock_); + ObjectTableEntry* entry = LookupTable(handle); + if (!entry) { return X_STATUS_INVALID_HANDLE; } @@ -231,6 +226,10 @@ X_STATUS ObjectTable::RemoveHandle(X_HANDLE handle) { object->handles().erase(handle_entry); } + auto table = GetTableForObject(handle); + const uint32_t slot = table->GetHandleSlot(handle); + table->freed_table_slots_.push_back(slot); + XELOGI("Removed handle:{:08X} for {}", handle, typeid(*object).name()); // Remove object name from mapping to prevent naming collision. @@ -240,69 +239,21 @@ X_STATUS ObjectTable::RemoveHandle(X_HANDLE handle) { // Release now that the object has been removed from the table. object->Release(); } - 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; } - 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]; - } + auto* entry = &table->table_[table->GetHandleSlot(handle)]; - return nullptr; + if (!entry->object) { + return nullptr; + } + return entry; } // Generic lookup @@ -321,64 +272,78 @@ XObject* ObjectTable::LookupObject(X_HANDLE handle, bool already_locked) { } XObject* object = nullptr; - if (!already_locked) { - global_critical_region_.mutex().lock(); - } - - 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; - } + auto entry = LookupTable(handle); + if (!entry) { + return nullptr; } // 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() { + std::vector> results; + + std::lock_guard lock(spinlock_); + 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() { + std::lock_guard lock(spinlock_); + + 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) { + std::lock_guard lock(spinlock_); + + 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,31 +355,31 @@ 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))) { + std::lock_guard lock(spinlock_); + 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); } } 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()) { + std::lock_guard lock(spinlock_); + 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 +396,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 +408,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 +427,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 +443,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..995403df0 100644 --- a/src/xenia/kernel/util/object_table.h +++ b/src/xenia/kernel/util/object_table.h @@ -15,6 +15,7 @@ #include #include "xenia/base/mutex.h" +#include "xenia/base/spinlock.h" #include "xenia/base/string_key.h" #include "xenia/kernel/xobject.h" #include "xenia/xbox.h" @@ -38,7 +39,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); @@ -59,6 +59,7 @@ class ObjectTable { X_STATUS AddNameMapping(const std::string_view name, X_HANDLE handle); void RemoveNameMapping(const std::string_view name); + X_STATUS GetObjectByName(const std::string_view name, X_HANDLE* out_handle); template std::vector> GetObjectsByType(XObject::Type type) { @@ -85,28 +86,77 @@ class ObjectTable { 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(255); + table_.reserve(4095); + }; + + // Ctor for guest objects + ObjectTableInfo(uint32_t base_handle_offset) { + table_base_handle_offset_ = base_handle_offset; + + freed_table_slots_.reserve(255); + table_.reserve(4095); + }; + + 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; - 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_; + 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::spinlock spinlock_; + + ObjectTableInfo host_object_table_; + + static constexpr uint32_t kGuestHandleBase = 0x00100000; + static constexpr uint32_t kGuestHandleTitleThreadBase = 0xF8000000; + static constexpr uint32_t kGuestHandleSystemThreadBase = 0xFB000000; + + std::map guest_object_table_ = { + {kGuestHandleBase, ObjectTableInfo(kGuestHandleBase)}, + {kGuestHandleTitleThreadBase, + ObjectTableInfo(kGuestHandleTitleThreadBase)}, + {kGuestHandleSystemThreadBase, + ObjectTableInfo(kGuestHandleSystemThreadBase)}}; + + std::unordered_map guest_name_table_; }; // Generic lookup diff --git a/src/xenia/kernel/xobject.cc b/src/xenia/kernel/xobject.cc index 96495d782..01708537f 100644 --- a/src/xenia/kernel/xobject.cc +++ b/src/xenia/kernel/xobject.cc @@ -95,12 +95,9 @@ X_STATUS XObject::Delete() { if (kernel_state_ == nullptr) { // Fake return value for api-scanner return X_STATUS_SUCCESS; - } else { - if (!name_.empty()) { - kernel_state_->object_table()->RemoveNameMapping(name_); - } - return kernel_state_->object_table()->RemoveHandle(handles_[0]); } + + return kernel_state_->object_table()->RemoveHandle(handles_[0]); } bool XObject::SaveObject(ByteStream* stream) { diff --git a/src/xenia/kernel/xobject.h b/src/xenia/kernel/xobject.h index 0717c2bdc..55cd70916 100644 --- a/src/xenia/kernel/xobject.h +++ b/src/xenia/kernel/xobject.h @@ -199,9 +199,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()