[Kernel] Thread safety fixes for xfile, xmutant, and xtimer objects

This commit is contained in:
Herman S.
2026-02-24 16:28:39 +09:00
parent 0efd3a9610
commit 341d3b66a8
6 changed files with 49 additions and 14 deletions

View File

@@ -38,6 +38,7 @@ XFile::~XFile() {
X_STATUS XFile::QueryDirectory(X_FILE_DIRECTORY_INFORMATION* out_info, X_STATUS XFile::QueryDirectory(X_FILE_DIRECTORY_INFORMATION* out_info,
size_t length, const std::string_view file_name, size_t length, const std::string_view file_name,
bool restart) { bool restart) {
std::lock_guard<std::mutex> lock(file_lock_);
assert_not_null(out_info); assert_not_null(out_info);
vfs::Entry* entry = nullptr; vfs::Entry* entry = nullptr;
@@ -91,6 +92,15 @@ X_STATUS XFile::QueryDirectory(X_FILE_DIRECTORY_INFORMATION* out_info,
X_STATUS XFile::Read(uint32_t buffer_guest_address, uint32_t buffer_length, X_STATUS XFile::Read(uint32_t buffer_guest_address, uint32_t buffer_length,
uint64_t byte_offset, uint32_t* out_bytes_read, uint64_t byte_offset, uint32_t* out_bytes_read,
uint32_t apc_context, bool notify_completion) { uint32_t apc_context, bool notify_completion) {
std::lock_guard<std::mutex> lock(file_lock_);
return ReadInternal(buffer_guest_address, buffer_length, byte_offset,
out_bytes_read, apc_context, notify_completion);
}
X_STATUS XFile::ReadInternal(uint32_t buffer_guest_address,
uint32_t buffer_length, uint64_t byte_offset,
uint32_t* out_bytes_read, uint32_t apc_context,
bool notify_completion) {
if (byte_offset == uint64_t(-1)) { if (byte_offset == uint64_t(-1)) {
// Read from current position. // Read from current position.
byte_offset = position_; byte_offset = position_;
@@ -184,6 +194,7 @@ X_STATUS XFile::Read(uint32_t buffer_guest_address, uint32_t buffer_length,
X_STATUS XFile::ReadScatter(uint32_t segments_guest_address, uint32_t length, X_STATUS XFile::ReadScatter(uint32_t segments_guest_address, uint32_t length,
uint64_t byte_offset, uint32_t* out_bytes_read, uint64_t byte_offset, uint32_t* out_bytes_read,
uint32_t apc_context) { uint32_t apc_context) {
std::lock_guard<std::mutex> lock(file_lock_);
X_STATUS result = X_STATUS_SUCCESS; X_STATUS result = X_STATUS_SUCCESS;
// segments points to an array of buffer pointers of type // segments points to an array of buffer pointers of type
@@ -206,12 +217,13 @@ X_STATUS XFile::ReadScatter(uint32_t segments_guest_address, uint32_t length,
} }
uint32_t bytes_read = 0; uint32_t bytes_read = 0;
result = Read(read_buffer, read_length, result =
byte_offset ? ((byte_offset != -1 && byte_offset != -2) ReadInternal(read_buffer, read_length,
? byte_offset + read_total byte_offset ? ((byte_offset != -1 && byte_offset != -2)
: byte_offset) ? byte_offset + read_total
: -1, : byte_offset)
&bytes_read, apc_context, false); : -1,
&bytes_read, apc_context, false);
if (result != X_STATUS_SUCCESS) { if (result != X_STATUS_SUCCESS) {
break; break;
@@ -240,6 +252,7 @@ X_STATUS XFile::ReadScatter(uint32_t segments_guest_address, uint32_t length,
X_STATUS XFile::Write(uint32_t buffer_guest_address, uint32_t buffer_length, X_STATUS XFile::Write(uint32_t buffer_guest_address, uint32_t buffer_length,
uint64_t byte_offset, uint32_t* out_bytes_written, uint64_t byte_offset, uint32_t* out_bytes_written,
uint32_t apc_context) { uint32_t apc_context) {
std::lock_guard<std::mutex> lock(file_lock_);
if (byte_offset == uint64_t(-1)) { if (byte_offset == uint64_t(-1)) {
// Write from current position. // Write from current position.
byte_offset = position_; byte_offset = position_;
@@ -269,7 +282,10 @@ X_STATUS XFile::Write(uint32_t buffer_guest_address, uint32_t buffer_length,
return result; return result;
} }
X_STATUS XFile::SetLength(size_t length) { return file_->SetLength(length); } X_STATUS XFile::SetLength(size_t length) {
std::lock_guard<std::mutex> lock(file_lock_);
return file_->SetLength(length);
}
X_STATUS XFile::Rename(const std::filesystem::path file_path) { X_STATUS XFile::Rename(const std::filesystem::path file_path) {
entry()->Rename(file_path); entry()->Rename(file_path);
return X_STATUS_SUCCESS; return X_STATUS_SUCCESS;

View File

@@ -10,6 +10,7 @@
#ifndef XENIA_KERNEL_XFILE_H_ #ifndef XENIA_KERNEL_XFILE_H_
#define XENIA_KERNEL_XFILE_H_ #define XENIA_KERNEL_XFILE_H_
#include <mutex>
#include <string> #include <string>
#include "xenia/kernel/xiocompletion.h" #include "xenia/kernel/xiocompletion.h"
@@ -185,9 +186,14 @@ class XFile : public XObject {
private: private:
XFile(); XFile();
X_STATUS ReadInternal(uint32_t buffer_guest_address, uint32_t buffer_length,
uint64_t byte_offset, uint32_t* out_bytes_read,
uint32_t apc_context, bool notify_completion);
vfs::File* file_ = nullptr; vfs::File* file_ = nullptr;
std::unique_ptr<threading::Event> async_event_ = nullptr; std::unique_ptr<threading::Event> async_event_ = nullptr;
std::mutex file_lock_;
std::mutex completion_port_lock_; std::mutex completion_port_lock_;
std::vector<std::pair<uint32_t, object_ref<XIOCompletion>>> completion_ports_; std::vector<std::pair<uint32_t, object_ref<XIOCompletion>>> completion_ports_;

View File

@@ -59,7 +59,8 @@ bool XMutant::Save(ByteStream* stream) {
return false; return false;
} }
uint32_t owning_thread_handle = owning_thread_ ? owning_thread_->handle() : 0; XThread* owner = owning_thread_.load();
uint32_t owning_thread_handle = owner ? owner->handle() : 0;
stream->Write<uint32_t>(owning_thread_handle); stream->Write<uint32_t>(owning_thread_handle);
XELOGD("XMutant {:08X} (owner: {:08X})", handle(), owning_thread_handle); XELOGD("XMutant {:08X} (owner: {:08X})", handle(), owning_thread_handle);
@@ -83,7 +84,8 @@ object_ref<XMutant> XMutant::Restore(KernelState* kernel_state,
mutant->owning_thread_ = kernel_state->object_table() mutant->owning_thread_ = kernel_state->object_table()
->LookupObject<XThread>(owning_thread_handle) ->LookupObject<XThread>(owning_thread_handle)
.get(); .get();
mutant->owning_thread_->AcquireMutantOnStartup(retain_object(mutant)); mutant->owning_thread_.load()->AcquireMutantOnStartup(
retain_object(mutant));
} }
return object_ref<XMutant>(mutant); return object_ref<XMutant>(mutant);

View File

@@ -10,6 +10,8 @@
#ifndef XENIA_KERNEL_XMUTANT_H_ #ifndef XENIA_KERNEL_XMUTANT_H_
#define XENIA_KERNEL_XMUTANT_H_ #define XENIA_KERNEL_XMUTANT_H_
#include <atomic>
#include "xenia/base/threading.h" #include "xenia/base/threading.h"
#include "xenia/kernel/xobject.h" #include "xenia/kernel/xobject.h"
#include "xenia/xbox.h" #include "xenia/xbox.h"
@@ -42,7 +44,7 @@ class XMutant : public XObject {
XMutant(); XMutant();
std::unique_ptr<xe::threading::Mutant> mutant_; std::unique_ptr<xe::threading::Mutant> mutant_;
XThread* owning_thread_ = nullptr; std::atomic<XThread*> owning_thread_{nullptr};
}; };
} // namespace kernel } // namespace kernel

View File

@@ -45,6 +45,8 @@ X_STATUS XTimer::SetTimer(int64_t due_time, uint32_t period_ms,
return X_STATUS_TIMER_RESUME_IGNORED; return X_STATUS_TIMER_RESUME_IGNORED;
} }
std::lock_guard<std::mutex> lock(timer_lock_);
period_ms = Clock::ScaleGuestDurationMillis(period_ms); period_ms = Clock::ScaleGuestDurationMillis(period_ms);
WinSystemClock::time_point due_tp; WinSystemClock::time_point due_tp;
if (due_time < 0) { if (due_time < 0) {
@@ -63,9 +65,13 @@ X_STATUS XTimer::SetTimer(int64_t due_time, uint32_t period_ms,
callback_routine_arg_ = routine_arg; callback_routine_arg_ = routine_arg;
// This callback will only be issued when the timer is fired. // This callback will only be issued when the timer is fired.
// Capture values by value to avoid racing with a future SetTimer() call.
std::function<void()> callback = nullptr; std::function<void()> callback = nullptr;
if (callback_routine_) { if (callback_routine_) {
callback = [this]() { auto cb_thread = callback_thread_;
auto cb_routine = callback_routine_;
auto cb_routine_arg = callback_routine_arg_;
callback = [cb_thread, cb_routine, cb_routine_arg]() {
// Queue APC to call back routine with (arg, low, high). // Queue APC to call back routine with (arg, low, high).
// It'll be executed on the thread that requested the timer. // It'll be executed on the thread that requested the timer.
uint64_t time = xe::Clock::QueryGuestSystemTime(); uint64_t time = xe::Clock::QueryGuestSystemTime();
@@ -73,9 +79,8 @@ X_STATUS XTimer::SetTimer(int64_t due_time, uint32_t period_ms,
uint32_t time_high = static_cast<uint32_t>(time >> 32); uint32_t time_high = static_cast<uint32_t>(time >> 32);
XELOGI( XELOGI(
"XTimer enqueuing timer callback to {:08X}({:08X}, {:08X}, {:08X})", "XTimer enqueuing timer callback to {:08X}({:08X}, {:08X}, {:08X})",
callback_routine_, callback_routine_arg_, time_low, time_high); cb_routine, cb_routine_arg, time_low, time_high);
callback_thread_->EnqueueApc(callback_routine_, callback_routine_arg_, cb_thread->EnqueueApc(cb_routine, cb_routine_arg, time_low, time_high);
time_low, time_high);
}; };
} }
@@ -91,6 +96,7 @@ X_STATUS XTimer::SetTimer(int64_t due_time, uint32_t period_ms,
} }
X_STATUS XTimer::Cancel() { X_STATUS XTimer::Cancel() {
std::lock_guard<std::mutex> lock(timer_lock_);
return timer_->Cancel() ? X_STATUS_SUCCESS : X_STATUS_UNSUCCESSFUL; return timer_->Cancel() ? X_STATUS_SUCCESS : X_STATUS_UNSUCCESSFUL;
} }

View File

@@ -10,6 +10,8 @@
#ifndef XENIA_KERNEL_XTIMER_H_ #ifndef XENIA_KERNEL_XTIMER_H_
#define XENIA_KERNEL_XTIMER_H_ #define XENIA_KERNEL_XTIMER_H_
#include <mutex>
#include "xenia/base/threading.h" #include "xenia/base/threading.h"
#include "xenia/kernel/xobject.h" #include "xenia/kernel/xobject.h"
#include "xenia/xbox.h" #include "xenia/xbox.h"
@@ -37,6 +39,7 @@ class XTimer : public XObject {
private: private:
std::unique_ptr<xe::threading::Timer> timer_; std::unique_ptr<xe::threading::Timer> timer_;
std::mutex timer_lock_;
XThread* callback_thread_ = nullptr; XThread* callback_thread_ = nullptr;
uint32_t callback_routine_ = 0; uint32_t callback_routine_ = 0;