Implementing object table. Not complete, but better.

This commit is contained in:
Ben Vanik 2013-09-22 18:36:06 -07:00
parent 3e0db586c0
commit d0d30ab719
7 changed files with 285 additions and 109 deletions

View File

@ -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<XObject*> all_objects;
xe_mutex_lock(objects_mutex_);
for (std::tr1::unordered_map<X_HANDLE, XObject*>::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<XObject*>::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<X_HANDLE, XObject*>::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<X_HANDLE, XObject*>(handle, obj));
switch (obj->type()) {
case XObject::kTypeModule:
modules_.insert(std::pair<X_HANDLE, XModule*>(
handle, static_cast<XModule*>(obj)));
break;
case XObject::kTypeThread:
threads_.insert(std::pair<X_HANDLE, XThread*>(
handle, static_cast<XThread*>(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<X_HANDLE, XModule*>::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;

View File

@ -17,6 +17,7 @@
#include <xenia/kernel/kernel_module.h>
#include <xenia/kernel/xbox.h>
#include <xenia/kernel/fs/filesystem.h>
#include <xenia/kernel/modules/xboxkrnl/object_table.h>
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<cpu::Processor> processor_;
shared_ptr<fs::FileSystem> filesystem_;
XModule* executable_module_;
ObjectTable* object_table_;
xe_mutex_t* objects_mutex_;
X_HANDLE next_handle_;
std::tr1::unordered_map<X_HANDLE, XObject*> objects_;
std::tr1::unordered_map<X_HANDLE, XModule*> modules_;
std::tr1::unordered_map<X_HANDLE, XThread*> threads_;
XModule* executable_module_;
friend class XObject;
};

View File

@ -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 <xenia/kernel/modules/xboxkrnl/object_table.h>
#include <xenia/kernel/modules/xboxkrnl/xobject.h>
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;
}

View File

@ -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 <xenia/common.h>
#include <xenia/core.h>
#include <xenia/kernel/xbox.h>
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_

View File

@ -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',

View File

@ -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;
}
}

View File

@ -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_;