Skip to content

Commit 8aa1846

Browse files
committed
[Kernel] Thread safety fixes for xfile, xmutant, and xtimer objects
1 parent cf0d65e commit 8aa1846

File tree

5 files changed

+45
-12
lines changed

5 files changed

+45
-12
lines changed

src/xenia/kernel/xfile.cc

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ XFile::~XFile() {
3838
X_STATUS XFile::QueryDirectory(X_FILE_DIRECTORY_INFORMATION* out_info,
3939
size_t length, const std::string_view file_name,
4040
bool restart) {
41+
std::lock_guard<std::mutex> lock(file_lock_);
4142
assert_not_null(out_info);
4243

4344
vfs::Entry* entry = nullptr;
@@ -91,6 +92,15 @@ X_STATUS XFile::QueryDirectory(X_FILE_DIRECTORY_INFORMATION* out_info,
9192
X_STATUS XFile::Read(uint32_t buffer_guest_address, uint32_t buffer_length,
9293
uint64_t byte_offset, uint32_t* out_bytes_read,
9394
uint32_t apc_context, bool notify_completion) {
95+
std::lock_guard<std::mutex> lock(file_lock_);
96+
return ReadInternal(buffer_guest_address, buffer_length, byte_offset,
97+
out_bytes_read, apc_context, notify_completion);
98+
}
99+
100+
X_STATUS XFile::ReadInternal(uint32_t buffer_guest_address,
101+
uint32_t buffer_length, uint64_t byte_offset,
102+
uint32_t* out_bytes_read, uint32_t apc_context,
103+
bool notify_completion) {
94104
if (byte_offset == uint64_t(-1)) {
95105
// Read from current position.
96106
byte_offset = position_;
@@ -184,6 +194,7 @@ X_STATUS XFile::Read(uint32_t buffer_guest_address, uint32_t buffer_length,
184194
X_STATUS XFile::ReadScatter(uint32_t segments_guest_address, uint32_t length,
185195
uint64_t byte_offset, uint32_t* out_bytes_read,
186196
uint32_t apc_context) {
197+
std::lock_guard<std::mutex> lock(file_lock_);
187198
X_STATUS result = X_STATUS_SUCCESS;
188199

189200
// 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,
206217
}
207218

208219
uint32_t bytes_read = 0;
209-
result = Read(read_buffer, read_length,
210-
byte_offset ? ((byte_offset != -1 && byte_offset != -2)
211-
? byte_offset + read_total
212-
: byte_offset)
213-
: -1,
214-
&bytes_read, apc_context, false);
220+
result =
221+
ReadInternal(read_buffer, read_length,
222+
byte_offset ? ((byte_offset != -1 && byte_offset != -2)
223+
? byte_offset + read_total
224+
: byte_offset)
225+
: -1,
226+
&bytes_read, apc_context, false);
215227

216228
if (result != X_STATUS_SUCCESS) {
217229
break;
@@ -240,6 +252,7 @@ X_STATUS XFile::ReadScatter(uint32_t segments_guest_address, uint32_t length,
240252
X_STATUS XFile::Write(uint32_t buffer_guest_address, uint32_t buffer_length,
241253
uint64_t byte_offset, uint32_t* out_bytes_written,
242254
uint32_t apc_context) {
255+
std::lock_guard<std::mutex> lock(file_lock_);
243256
if (byte_offset == uint64_t(-1)) {
244257
// Write from current position.
245258
byte_offset = position_;
@@ -269,7 +282,10 @@ X_STATUS XFile::Write(uint32_t buffer_guest_address, uint32_t buffer_length,
269282
return result;
270283
}
271284

272-
X_STATUS XFile::SetLength(size_t length) { return file_->SetLength(length); }
285+
X_STATUS XFile::SetLength(size_t length) {
286+
std::lock_guard<std::mutex> lock(file_lock_);
287+
return file_->SetLength(length);
288+
}
273289
X_STATUS XFile::Rename(const std::filesystem::path file_path) {
274290
entry()->Rename(file_path);
275291
return X_STATUS_SUCCESS;

src/xenia/kernel/xfile.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#ifndef XENIA_KERNEL_XFILE_H_
1111
#define XENIA_KERNEL_XFILE_H_
1212

13+
#include <mutex>
1314
#include <string>
1415

1516
#include "xenia/kernel/xiocompletion.h"
@@ -185,9 +186,14 @@ class XFile : public XObject {
185186
private:
186187
XFile();
187188

189+
X_STATUS ReadInternal(uint32_t buffer_guest_address, uint32_t buffer_length,
190+
uint64_t byte_offset, uint32_t* out_bytes_read,
191+
uint32_t apc_context, bool notify_completion);
192+
188193
vfs::File* file_ = nullptr;
189194
std::unique_ptr<threading::Event> async_event_ = nullptr;
190195

196+
std::mutex file_lock_;
191197
std::mutex completion_port_lock_;
192198
std::vector<std::pair<uint32_t, object_ref<XIOCompletion>>> completion_ports_;
193199

src/xenia/kernel/xmutant.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
#ifndef XENIA_KERNEL_XMUTANT_H_
1111
#define XENIA_KERNEL_XMUTANT_H_
1212

13+
#include <atomic>
14+
1315
#include "xenia/base/threading.h"
1416
#include "xenia/kernel/xobject.h"
1517
#include "xenia/xbox.h"
@@ -42,7 +44,7 @@ class XMutant : public XObject {
4244
XMutant();
4345

4446
std::unique_ptr<xe::threading::Mutant> mutant_;
45-
XThread* owning_thread_ = nullptr;
47+
std::atomic<XThread*> owning_thread_{nullptr};
4648
};
4749

4850
} // namespace kernel

src/xenia/kernel/xtimer.cc

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@ X_STATUS XTimer::SetTimer(int64_t due_time, uint32_t period_ms,
4545
return X_STATUS_TIMER_RESUME_IGNORED;
4646
}
4747

48+
std::lock_guard<std::mutex> lock(timer_lock_);
49+
4850
period_ms = Clock::ScaleGuestDurationMillis(period_ms);
4951
WinSystemClock::time_point due_tp;
5052
if (due_time < 0) {
@@ -63,19 +65,22 @@ X_STATUS XTimer::SetTimer(int64_t due_time, uint32_t period_ms,
6365
callback_routine_arg_ = routine_arg;
6466

6567
// This callback will only be issued when the timer is fired.
68+
// Capture values by value to avoid racing with a future SetTimer() call.
6669
std::function<void()> callback = nullptr;
6770
if (callback_routine_) {
68-
callback = [this]() {
71+
auto cb_thread = callback_thread_;
72+
auto cb_routine = callback_routine_;
73+
auto cb_routine_arg = callback_routine_arg_;
74+
callback = [cb_thread, cb_routine, cb_routine_arg]() {
6975
// Queue APC to call back routine with (arg, low, high).
7076
// It'll be executed on the thread that requested the timer.
7177
uint64_t time = xe::Clock::QueryGuestSystemTime();
7278
uint32_t time_low = static_cast<uint32_t>(time);
7379
uint32_t time_high = static_cast<uint32_t>(time >> 32);
7480
XELOGI(
7581
"XTimer enqueuing timer callback to {:08X}({:08X}, {:08X}, {:08X})",
76-
callback_routine_, callback_routine_arg_, time_low, time_high);
77-
callback_thread_->EnqueueApc(callback_routine_, callback_routine_arg_,
78-
time_low, time_high);
82+
cb_routine, cb_routine_arg, time_low, time_high);
83+
cb_thread->EnqueueApc(cb_routine, cb_routine_arg, time_low, time_high);
7984
};
8085
}
8186

@@ -91,6 +96,7 @@ X_STATUS XTimer::SetTimer(int64_t due_time, uint32_t period_ms,
9196
}
9297

9398
X_STATUS XTimer::Cancel() {
99+
std::lock_guard<std::mutex> lock(timer_lock_);
94100
return timer_->Cancel() ? X_STATUS_SUCCESS : X_STATUS_UNSUCCESSFUL;
95101
}
96102

src/xenia/kernel/xtimer.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
#ifndef XENIA_KERNEL_XTIMER_H_
1111
#define XENIA_KERNEL_XTIMER_H_
1212

13+
#include <mutex>
14+
1315
#include "xenia/base/threading.h"
1416
#include "xenia/kernel/xobject.h"
1517
#include "xenia/xbox.h"
@@ -37,6 +39,7 @@ class XTimer : public XObject {
3739

3840
private:
3941
std::unique_ptr<xe::threading::Timer> timer_;
42+
std::mutex timer_lock_;
4043

4144
XThread* callback_thread_ = nullptr;
4245
uint32_t callback_routine_ = 0;

0 commit comments

Comments
 (0)