Implementing object table. Not complete, but better.
This commit is contained in:
parent
3e0db586c0
commit
d0d30ab719
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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_
|
|
@ -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',
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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_;
|
||||
|
|
Loading…
Reference in New Issue