diff --git a/src/xenia/kernel/modules/xboxkrnl/kernel_state.cc b/src/xenia/kernel/modules/xboxkrnl/kernel_state.cc index f5e03cb6f..c4196152e 100644 --- a/src/xenia/kernel/modules/xboxkrnl/kernel_state.cc +++ b/src/xenia/kernel/modules/xboxkrnl/kernel_state.cc @@ -26,14 +26,12 @@ namespace { KernelState::KernelState(Runtime* runtime) : runtime_(runtime), - executable_module_(NULL), - next_handle_(0) { + executable_module_(NULL) { memory_ = runtime->memory(); processor_ = runtime->processor(); filesystem_ = runtime->filesystem(); - objects_mutex_ = xe_mutex_alloc(0); - XEASSERTNOTNULL(objects_mutex_); + object_table_ = new ObjectTable(); } KernelState::~KernelState() { @@ -43,27 +41,7 @@ KernelState::~KernelState() { } // Delete all objects. - // We first copy the list to another list so that the deletion of the objects - // doesn't mess up iteration. - std::vector all_objects; - xe_mutex_lock(objects_mutex_); - for (std::tr1::unordered_map::iterator it = - objects_.begin(); it != objects_.end(); ++it) { - all_objects.push_back(it->second); - } - objects_.clear(); - modules_.clear(); - threads_.clear(); - xe_mutex_unlock(objects_mutex_); - for (std::vector::iterator it = all_objects.begin(); - it != all_objects.end(); ++it) { - // Perhaps call a special ForceRelease method or something? - XObject* obj = *it; - delete obj; - } - - xe_mutex_free(objects_mutex_); - objects_mutex_ = NULL; + delete object_table_; filesystem_.reset(); processor_.reset(); @@ -86,75 +64,31 @@ fs::FileSystem* KernelState::filesystem() { return filesystem_.get(); } -// TODO(benvanik): invesitgate better handle storage/structure. -// A much better way of doing handles, if performance becomes an issue, would -// be to try to make the pointers 32bit. Then we could round-trip them through -// PPC code without needing to keep a map. -// To achieve this we could try doing allocs in the 32-bit address space via -// the OS alloc calls, or maybe create a section with a reserved size at load -// time (65k handles * 4 is more than enough?). -// We could then use a free list of handle IDs and allocate/release lock-free. - -XObject* KernelState::GetObject(X_HANDLE handle) { - xe_mutex_lock(objects_mutex_); - std::tr1::unordered_map::iterator it = - objects_.find(handle); - XObject* value = it != objects_.end() ? it->second : NULL; - if (value) { - value->Retain(); - } - xe_mutex_unlock(objects_mutex_); - return value; -} - -X_HANDLE KernelState::InsertObject(XObject* obj) { - xe_mutex_lock(objects_mutex_); - X_HANDLE handle = 0x00001000 + (++next_handle_); - objects_.insert(std::pair(handle, obj)); - switch (obj->type()) { - case XObject::kTypeModule: - modules_.insert(std::pair( - handle, static_cast(obj))); - break; - case XObject::kTypeThread: - threads_.insert(std::pair( - handle, static_cast(obj))); - break; - } - xe_mutex_unlock(objects_mutex_); - return handle; -} - -void KernelState::RemoveObject(XObject* obj) { - xe_mutex_lock(objects_mutex_); - objects_.erase(obj->handle()); - xe_mutex_unlock(objects_mutex_); +ObjectTable* KernelState::object_table() const { + return object_table_; } XModule* KernelState::GetModule(const char* name) { - XModule* found = NULL; - xe_mutex_lock(objects_mutex_); - for (std::tr1::unordered_map::iterator it = - modules_.begin(); it != modules_.end(); ++it) { - if (xestrcmpa(name, it->second->name()) == 0) { - found = it->second; - found->Retain(); - } - } - xe_mutex_unlock(objects_mutex_); - return found; -} - -XModule* KernelState::GetExecutableModule() { - if (executable_module_) { - executable_module_->Retain(); - return executable_module_; - } + // TODO(benvanik): implement lookup. Most games seem to look for xam.xex/etc. + XEASSERTALWAYS(); return NULL; } +XModule* KernelState::GetExecutableModule() { + if (!executable_module_) { + return NULL; + } + + executable_module_->Retain(); + return executable_module_; +} + void KernelState::SetExecutableModule(XModule* module) { - if (executable_module_ && executable_module_ != module) { + if (module == executable_module_) { + return; + } + + if (executable_module_) { executable_module_->Release(); } executable_module_ = module; diff --git a/src/xenia/kernel/modules/xboxkrnl/kernel_state.h b/src/xenia/kernel/modules/xboxkrnl/kernel_state.h index 0ba2a9152..448fa07b6 100644 --- a/src/xenia/kernel/modules/xboxkrnl/kernel_state.h +++ b/src/xenia/kernel/modules/xboxkrnl/kernel_state.h @@ -17,6 +17,7 @@ #include #include #include +#include namespace xe { @@ -24,9 +25,7 @@ namespace kernel { namespace xboxkrnl { -class XObject; class XModule; -class XThread; class KernelState { @@ -39,7 +38,7 @@ public: cpu::Processor* processor(); fs::FileSystem* filesystem(); - XObject* GetObject(X_HANDLE handle); + ObjectTable* object_table() const; XModule* GetModule(const char* name); XModule* GetExecutableModule(); @@ -54,13 +53,9 @@ private: shared_ptr processor_; shared_ptr filesystem_; - XModule* executable_module_; + ObjectTable* object_table_; - xe_mutex_t* objects_mutex_; - X_HANDLE next_handle_; - std::tr1::unordered_map objects_; - std::tr1::unordered_map modules_; - std::tr1::unordered_map threads_; + XModule* executable_module_; friend class XObject; }; diff --git a/src/xenia/kernel/modules/xboxkrnl/object_table.cc b/src/xenia/kernel/modules/xboxkrnl/object_table.cc new file mode 100644 index 000000000..0f3eaa13e --- /dev/null +++ b/src/xenia/kernel/modules/xboxkrnl/object_table.cc @@ -0,0 +1,178 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2013 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +#include + +#include + + +using namespace xe; +using namespace xe::kernel; +using namespace xe::kernel::xboxkrnl; + + +ObjectTable::ObjectTable() : + table_capacity_(0), + table_(NULL), + last_free_entry_(0) { + table_mutex_ = xe_mutex_alloc(0); + XEASSERTNOTNULL(table_mutex_); +} + +ObjectTable::~ObjectTable() { + xe_mutex_lock(table_mutex_); + + // Release all objects. + for (uint32_t n = 0; n < table_capacity_; n++) { + ObjectTableEntry& entry = table_[n]; + if (entry.object) { + entry.object->ReleaseHandle(); + } + } + + table_capacity_ = 0; + last_free_entry_ = 0; + xe_free(table_); + table_ = NULL; + + xe_mutex_unlock(table_mutex_); + + xe_mutex_free(table_mutex_); + table_mutex_ = NULL; +} + +X_STATUS ObjectTable::FindFreeSlot(uint32_t* out_slot) { + // Find a free slot. + uint32_t slot = last_free_entry_; + uint32_t scan_count = 0; + while (scan_count < table_capacity_) { + ObjectTableEntry& entry = table_[slot]; + if (!entry.object) { + *out_slot = slot; + return X_STATUS_SUCCESS; + } + scan_count++; + slot = (slot + 1) % table_capacity_; + if (slot == 0) { + // Never allow 0 handles. + scan_count++; + slot++; + } + } + + // Table out of slots, expand. + uint32_t new_table_capacity = MAX(16 * 1024, table_capacity_ * 2); + ObjectTableEntry* new_table = (ObjectTableEntry*)xe_recalloc( + table_, + table_capacity_ * sizeof(ObjectTableEntry), + new_table_capacity * sizeof(ObjectTableEntry)); + if (!new_table) { + return X_STATUS_NO_MEMORY; + } + last_free_entry_ = table_capacity_; + table_capacity_ = new_table_capacity; + table_ = new_table; + + // Never allow 0 handles. + slot = ++last_free_entry_; + *out_slot = slot; + + return X_STATUS_SUCCESS; +} + +X_STATUS ObjectTable::AddHandle(XObject* object, X_HANDLE* out_handle) { + XEASSERTNOTNULL(out_handle); + + X_STATUS result = X_STATUS_SUCCESS; + + xe_mutex_lock(table_mutex_); + + // Find a free slot. + uint32_t slot = 0; + result = FindFreeSlot(&slot); + + // Stash. + if (XSUCCEEDED(result)) { + ObjectTableEntry& entry = table_[slot]; + entry.object = object; + + // Retain so long as the object is in the table. + object->RetainHandle(); + object->Retain(); + } + + xe_mutex_unlock(table_mutex_); + + if (XSUCCEEDED(result)) { + *out_handle = slot << 2; + } + + return result; +} + +X_STATUS ObjectTable::RemoveHandle(X_HANDLE handle) { + X_STATUS result = X_STATUS_SUCCESS; + + xe_mutex_lock(table_mutex_); + + // Lower 2 bits are ignored. + uint32_t slot = handle >> 2; + + // Verify slot. + XObject* object = NULL; + if (slot > table_capacity_) { + result = X_STATUS_INVALID_HANDLE; + } else { + ObjectTableEntry& entry = table_[slot]; + if (entry.object) { + object = entry.object; + + // Release the object handle now that it is out of the table. + object->ReleaseHandle(); + object->Release(); + } else { + result = X_STATUS_INVALID_HANDLE; + } + } + + xe_mutex_unlock(table_mutex_); + + return result; +} + +X_STATUS ObjectTable::GetObject(X_HANDLE handle, XObject** out_object) { + XEASSERTNOTNULL(out_object); + + X_STATUS result = X_STATUS_SUCCESS; + + xe_mutex_lock(table_mutex_); + + // Lower 2 bits are ignored. + uint32_t slot = handle >> 2; + + // Verify slot. + XObject* object = NULL; + if (slot > table_capacity_) { + result = X_STATUS_INVALID_HANDLE; + } else { + ObjectTableEntry& entry = table_[slot]; + if (entry.object) { + object = entry.object; + } else { + result = X_STATUS_INVALID_HANDLE; + } + } + + // Retain the object pointer. + object->Retain(); + + xe_mutex_unlock(table_mutex_); + + return result; +} diff --git a/src/xenia/kernel/modules/xboxkrnl/object_table.h b/src/xenia/kernel/modules/xboxkrnl/object_table.h new file mode 100644 index 000000000..e34e52631 --- /dev/null +++ b/src/xenia/kernel/modules/xboxkrnl/object_table.h @@ -0,0 +1,55 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2013 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +#ifndef XENIA_KERNEL_MODULES_XBOXKRNL_OBJECT_TABLE_H_ +#define XENIA_KERNEL_MODULES_XBOXKRNL_OBJECT_TABLE_H_ + +#include +#include + +#include + + +namespace xe { +namespace kernel { +namespace xboxkrnl { + + +class XObject; + + +class ObjectTable { +public: + ObjectTable(); + ~ObjectTable(); + + X_STATUS AddHandle(XObject* object, X_HANDLE* out_handle); + X_STATUS RemoveHandle(X_HANDLE handle); + X_STATUS GetObject(X_HANDLE handle, XObject** out_object); + +private: + X_STATUS FindFreeSlot(uint32_t* out_slot); + + typedef struct { + XObject* object; + } ObjectTableEntry; + + xe_mutex_t* table_mutex_; + uint32_t table_capacity_; + ObjectTableEntry* table_; + uint32_t last_free_entry_; +}; + + +} // namespace xboxkrnl +} // namespace kernel +} // namespace xe + + +#endif // XENIA_KERNEL_MODULES_XBOXKRNL_OBJECT_TABLE_H_ diff --git a/src/xenia/kernel/modules/xboxkrnl/sources.gypi b/src/xenia/kernel/modules/xboxkrnl/sources.gypi index 41702affc..c22754edf 100644 --- a/src/xenia/kernel/modules/xboxkrnl/sources.gypi +++ b/src/xenia/kernel/modules/xboxkrnl/sources.gypi @@ -5,6 +5,8 @@ 'kernel_state.h', 'module.cc', 'module.h', + 'object_table.cc', + 'object_table.h', 'xboxkrnl_debug.cc', 'xboxkrnl_debug.h', 'xboxkrnl_hal.cc', diff --git a/src/xenia/kernel/modules/xboxkrnl/xobject.cc b/src/xenia/kernel/modules/xboxkrnl/xobject.cc index 0e2d34f57..3707d806f 100644 --- a/src/xenia/kernel/modules/xboxkrnl/xobject.cc +++ b/src/xenia/kernel/modules/xboxkrnl/xobject.cc @@ -17,18 +17,15 @@ using namespace xe::kernel::xboxkrnl; XObject::XObject(KernelState* kernel_state, Type type) : kernel_state_(kernel_state), - ref_count_(1), + handle_ref_count_(0), + pointer_ref_count_(0), type_(type), handle_(X_INVALID_HANDLE_VALUE) { - handle_ = kernel_state->InsertObject(this); + kernel_state->object_table()->AddHandle(this, &handle_); } XObject::~XObject() { - XEASSERTZERO(ref_count_); - - if (handle_ != X_INVALID_HANDLE_VALUE) { - // Remove from state table. - kernel_state_->RemoveObject(this); - } + XEASSERTZERO(handle_ref_count_); + XEASSERTZERO(pointer_ref_count_); } Runtime* XObject::runtime() { @@ -47,16 +44,28 @@ XObject::Type XObject::type() { return type_; } -X_HANDLE XObject::handle() { +X_HANDLE XObject::handle() const { return handle_; } +void XObject::RetainHandle() { + xe_atomic_inc_32(&handle_ref_count_); +} + +bool XObject::ReleaseHandle() { + if (!xe_atomic_dec_32(&handle_ref_count_)) { + return true; + } + return false; +} + void XObject::Retain() { - xe_atomic_inc_32(&ref_count_); + xe_atomic_inc_32(&pointer_ref_count_); } void XObject::Release() { - if (!xe_atomic_dec_32(&ref_count_)) { + if (!xe_atomic_dec_32(&pointer_ref_count_)) { + XEASSERT(pointer_ref_count_ >= handle_ref_count_); delete this; } } diff --git a/src/xenia/kernel/modules/xboxkrnl/xobject.h b/src/xenia/kernel/modules/xboxkrnl/xobject.h index 0e14d948f..a91e48141 100644 --- a/src/xenia/kernel/modules/xboxkrnl/xobject.h +++ b/src/xenia/kernel/modules/xboxkrnl/xobject.h @@ -40,8 +40,10 @@ public: KernelState* kernel_state(); Type type(); - X_HANDLE handle(); + X_HANDLE handle() const; + void RetainHandle(); + bool ReleaseHandle(); void Retain(); void Release(); @@ -52,7 +54,8 @@ protected: private: KernelState* kernel_state_; - volatile int32_t ref_count_; + volatile int32_t handle_ref_count_; + volatile int32_t pointer_ref_count_; Type type_; X_HANDLE handle_;