Merge pull request #363 from DrChat/obj_handle_ref_counting

Track object handle reference counts in the object table.
This commit is contained in:
Ben Vanik
2015-07-22 17:37:54 -07:00
6 changed files with 75 additions and 23 deletions

View File

@@ -22,13 +22,12 @@ ObjectTable::ObjectTable()
: table_capacity_(0), table_(nullptr), last_free_entry_(0) {} : table_capacity_(0), table_(nullptr), last_free_entry_(0) {}
ObjectTable::~ObjectTable() { ObjectTable::~ObjectTable() {
std::lock_guard<xe::mutex> lock(table_mutex_); std::lock_guard<xe::recursive_mutex> lock(table_mutex_);
// Release all objects. // Release all objects.
for (uint32_t n = 0; n < table_capacity_; n++) { for (uint32_t n = 0; n < table_capacity_; n++) {
ObjectTableEntry& entry = table_[n]; ObjectTableEntry& entry = table_[n];
if (entry.object) { if (entry.object) {
entry.object->ReleaseHandle();
entry.object->Release(); entry.object->Release();
} }
} }
@@ -90,7 +89,7 @@ X_STATUS ObjectTable::AddHandle(XObject* object, X_HANDLE* out_handle) {
uint32_t slot = 0; uint32_t slot = 0;
{ {
std::lock_guard<xe::mutex> lock(table_mutex_); std::lock_guard<xe::recursive_mutex> lock(table_mutex_);
// Find a free slot. // Find a free slot.
result = FindFreeSlot(&slot); result = FindFreeSlot(&slot);
@@ -99,9 +98,9 @@ X_STATUS ObjectTable::AddHandle(XObject* object, X_HANDLE* out_handle) {
if (XSUCCEEDED(result)) { if (XSUCCEEDED(result)) {
ObjectTableEntry& entry = table_[slot]; ObjectTableEntry& entry = table_[slot];
entry.object = object; entry.object = object;
entry.handle_ref_count = 1;
// Retain so long as the object is in the table. // Retain so long as the object is in the table.
object->RetainHandle();
object->Retain(); object->Retain();
} }
} }
@@ -128,6 +127,36 @@ X_STATUS ObjectTable::DuplicateHandle(X_HANDLE handle, X_HANDLE* out_handle) {
return result; return result;
} }
X_STATUS ObjectTable::RetainHandle(X_HANDLE handle) {
std::lock_guard<xe::recursive_mutex> lock(table_mutex_);
ObjectTableEntry* entry = LookupTable(handle);
if (!entry) {
return X_STATUS_INVALID_HANDLE;
}
entry->handle_ref_count++;
return X_STATUS_SUCCESS;
}
X_STATUS ObjectTable::ReleaseHandle(X_HANDLE handle) {
std::lock_guard<xe::recursive_mutex> lock(table_mutex_);
ObjectTableEntry* entry = LookupTable(handle);
if (!entry) {
return X_STATUS_INVALID_HANDLE;
}
if (--entry->handle_ref_count == 0) {
// No more references. Remove it from the table.
return RemoveHandle(handle);
}
// FIXME: Return a status code telling the caller it wasn't released
// (but not a failure code)
return X_STATUS_SUCCESS;
}
X_STATUS ObjectTable::RemoveHandle(X_HANDLE handle) { X_STATUS ObjectTable::RemoveHandle(X_HANDLE handle) {
X_STATUS result = X_STATUS_SUCCESS; X_STATUS result = X_STATUS_SUCCESS;
@@ -138,7 +167,7 @@ X_STATUS ObjectTable::RemoveHandle(X_HANDLE handle) {
XObject* object = NULL; XObject* object = NULL;
{ {
std::lock_guard<xe::mutex> lock(table_mutex_); std::lock_guard<xe::recursive_mutex> lock(table_mutex_);
// Lower 2 bits are ignored. // Lower 2 bits are ignored.
uint32_t slot = handle >> 2; uint32_t slot = handle >> 2;
@@ -167,6 +196,23 @@ X_STATUS ObjectTable::RemoveHandle(X_HANDLE handle) {
return result; return result;
} }
ObjectTable::ObjectTableEntry* ObjectTable::LookupTable(X_HANDLE handle) {
handle = TranslateHandle(handle);
if (!handle) {
return nullptr;
}
std::lock_guard<xe::recursive_mutex> lock(table_mutex_);
// Lower 2 bits are ignored.
uint32_t slot = handle >> 2;
if (slot <= table_capacity_) {
return &table_[slot];
}
return nullptr;
}
XObject* ObjectTable::LookupObject(X_HANDLE handle, bool already_locked) { XObject* ObjectTable::LookupObject(X_HANDLE handle, bool already_locked) {
handle = TranslateHandle(handle); handle = TranslateHandle(handle);
if (!handle) { if (!handle) {
@@ -203,7 +249,7 @@ XObject* ObjectTable::LookupObject(X_HANDLE handle, bool already_locked) {
void ObjectTable::GetObjectsByType(XObject::Type type, void ObjectTable::GetObjectsByType(XObject::Type type,
std::vector<object_ref<XObject>>& results) { std::vector<object_ref<XObject>>& results) {
std::lock_guard<xe::mutex> lock(table_mutex_); std::lock_guard<xe::recursive_mutex> lock(table_mutex_);
for (uint32_t slot = 0; slot < table_capacity_; ++slot) { for (uint32_t slot = 0; slot < table_capacity_; ++slot) {
auto& entry = table_[slot]; auto& entry = table_[slot];
if (entry.object) { if (entry.object) {
@@ -229,7 +275,7 @@ X_HANDLE ObjectTable::TranslateHandle(X_HANDLE handle) {
} }
X_STATUS ObjectTable::AddNameMapping(const std::string& name, X_HANDLE handle) { X_STATUS ObjectTable::AddNameMapping(const std::string& name, X_HANDLE handle) {
std::lock_guard<xe::mutex> lock(table_mutex_); std::lock_guard<xe::recursive_mutex> lock(table_mutex_);
if (name_table_.count(name)) { if (name_table_.count(name)) {
return X_STATUS_OBJECT_NAME_COLLISION; return X_STATUS_OBJECT_NAME_COLLISION;
} }
@@ -238,7 +284,7 @@ X_STATUS ObjectTable::AddNameMapping(const std::string& name, X_HANDLE handle) {
} }
void ObjectTable::RemoveNameMapping(const std::string& name) { void ObjectTable::RemoveNameMapping(const std::string& name) {
std::lock_guard<xe::mutex> lock(table_mutex_); std::lock_guard<xe::recursive_mutex> lock(table_mutex_);
auto it = name_table_.find(name); auto it = name_table_.find(name);
if (it != name_table_.end()) { if (it != name_table_.end()) {
name_table_.erase(it); name_table_.erase(it);
@@ -247,7 +293,7 @@ void ObjectTable::RemoveNameMapping(const std::string& name) {
X_STATUS ObjectTable::GetObjectByName(const std::string& name, X_STATUS ObjectTable::GetObjectByName(const std::string& name,
X_HANDLE* out_handle) { X_HANDLE* out_handle) {
std::lock_guard<xe::mutex> lock(table_mutex_); std::lock_guard<xe::recursive_mutex> lock(table_mutex_);
auto it = name_table_.find(name); auto it = name_table_.find(name);
if (it == name_table_.end()) { if (it == name_table_.end()) {
*out_handle = X_INVALID_HANDLE_VALUE; *out_handle = X_INVALID_HANDLE_VALUE;

View File

@@ -28,7 +28,10 @@ class ObjectTable {
X_STATUS AddHandle(XObject* object, X_HANDLE* out_handle); X_STATUS AddHandle(XObject* object, X_HANDLE* out_handle);
X_STATUS DuplicateHandle(X_HANDLE orig, X_HANDLE* out_handle); X_STATUS DuplicateHandle(X_HANDLE orig, X_HANDLE* out_handle);
X_STATUS RetainHandle(X_HANDLE handle);
X_STATUS ReleaseHandle(X_HANDLE handle);
X_STATUS RemoveHandle(X_HANDLE handle); X_STATUS RemoveHandle(X_HANDLE handle);
template <typename T> template <typename T>
object_ref<T> LookupObject(X_HANDLE handle) { object_ref<T> LookupObject(X_HANDLE handle) {
auto object = LookupObject(handle, false); auto object = LookupObject(handle, false);
@@ -48,6 +51,12 @@ class ObjectTable {
} }
private: private:
typedef struct {
int handle_ref_count = 0;
XObject* object = nullptr;
} ObjectTableEntry;
ObjectTableEntry* LookupTable(X_HANDLE handle);
XObject* LookupObject(X_HANDLE handle, bool already_locked); XObject* LookupObject(X_HANDLE handle, bool already_locked);
void GetObjectsByType(XObject::Type type, void GetObjectsByType(XObject::Type type,
std::vector<object_ref<XObject>>& results); std::vector<object_ref<XObject>>& results);
@@ -55,9 +64,7 @@ class ObjectTable {
X_HANDLE TranslateHandle(X_HANDLE handle); X_HANDLE TranslateHandle(X_HANDLE handle);
X_STATUS FindFreeSlot(uint32_t* out_slot); X_STATUS FindFreeSlot(uint32_t* out_slot);
typedef struct { XObject* object; } ObjectTableEntry; xe::recursive_mutex table_mutex_;
xe::mutex table_mutex_;
uint32_t table_capacity_; uint32_t table_capacity_;
ObjectTableEntry* table_; ObjectTableEntry* table_;
uint32_t last_free_entry_; uint32_t last_free_entry_;

View File

@@ -292,6 +292,7 @@ X_STATUS XThread::Create() {
// Always retain when starting - the thread owns itself until exited. // Always retain when starting - the thread owns itself until exited.
Retain(); Retain();
RetainHandle();
xe::threading::Thread::CreationParameters params; xe::threading::Thread::CreationParameters params;
params.stack_size = 16 * 1024 * 1024; // Ignore game, always big! params.stack_size = 16 * 1024 * 1024; // Ignore game, always big!
@@ -350,6 +351,7 @@ X_STATUS XThread::Exit(int exit_code) {
running_ = false; running_ = false;
Release(); Release();
ReleaseHandle();
// NOTE: this does not return! // NOTE: this does not return!
xe::threading::Thread::Exit(exit_code); xe::threading::Thread::Exit(exit_code);
@@ -361,6 +363,7 @@ X_STATUS XThread::Terminate(int exit_code) {
running_ = false; running_ = false;
Release(); Release();
ReleaseHandle();
thread_->Terminate(exit_code); thread_->Terminate(exit_code);
return X_STATUS_SUCCESS; return X_STATUS_SUCCESS;

View File

@@ -190,8 +190,7 @@ dword_result_t NtDuplicateObject(dword_t handle, lpdword_t new_handle_ptr,
DECLARE_XBOXKRNL_EXPORT(NtDuplicateObject, ExportTag::kImplemented); DECLARE_XBOXKRNL_EXPORT(NtDuplicateObject, ExportTag::kImplemented);
dword_result_t NtClose(dword_t handle) { dword_result_t NtClose(dword_t handle) {
// FIXME: This needs to be removed once handle count reaches 0! return kernel_state()->object_table()->ReleaseHandle(handle);
return kernel_state()->object_table()->RemoveHandle(handle);
} }
DECLARE_XBOXKRNL_EXPORT(NtClose, ExportTag::kImplemented); DECLARE_XBOXKRNL_EXPORT(NtClose, ExportTag::kImplemented);

View File

@@ -21,7 +21,6 @@ namespace kernel {
XObject::XObject(KernelState* kernel_state, Type type) XObject::XObject(KernelState* kernel_state, Type type)
: kernel_state_(kernel_state), : kernel_state_(kernel_state),
handle_ref_count_(0),
pointer_ref_count_(1), pointer_ref_count_(1),
type_(type), type_(type),
handle_(X_INVALID_HANDLE_VALUE), handle_(X_INVALID_HANDLE_VALUE),
@@ -34,7 +33,6 @@ XObject::XObject(KernelState* kernel_state, Type type)
} }
XObject::~XObject() { XObject::~XObject() {
assert_zero(handle_ref_count_);
assert_zero(pointer_ref_count_); assert_zero(pointer_ref_count_);
if (allocated_guest_object_) { if (allocated_guest_object_) {
@@ -58,20 +56,20 @@ XObject::Type XObject::type() { return type_; }
X_HANDLE XObject::handle() const { return handle_; } X_HANDLE XObject::handle() const { return handle_; }
void XObject::RetainHandle() { ++handle_ref_count_; } void XObject::RetainHandle() {
kernel_state_->object_table()->RetainHandle(handle_);
}
bool XObject::ReleaseHandle() { bool XObject::ReleaseHandle() {
if (--handle_ref_count_ == 0) { // FIXME: Return true when handle is actually released.
return true; return kernel_state_->object_table()->ReleaseHandle(handle_) ==
} X_STATUS_SUCCESS;
return false;
} }
void XObject::Retain() { ++pointer_ref_count_; } void XObject::Retain() { ++pointer_ref_count_; }
void XObject::Release() { void XObject::Release() {
if (--pointer_ref_count_ == 0) { if (--pointer_ref_count_ == 0) {
assert_true(pointer_ref_count_ >= handle_ref_count_);
delete this; delete this;
} }
} }

View File

@@ -180,7 +180,6 @@ class XObject {
KernelState* kernel_state_; KernelState* kernel_state_;
private: private:
std::atomic<int32_t> handle_ref_count_;
std::atomic<int32_t> pointer_ref_count_; std::atomic<int32_t> pointer_ref_count_;
Type type_; Type type_;