Switching over kernel objects to the platform-agnostic APIs.
Possibly some regressions here.
This commit is contained in:
parent
bd058feb39
commit
345fe60da0
|
@ -45,7 +45,8 @@ class Fence {
|
|||
std::atomic<bool> signaled_;
|
||||
};
|
||||
|
||||
// TODO(benvanik): processor info API.
|
||||
// Returns the total number of logical processors in the host system.
|
||||
uint32_t logical_processor_count();
|
||||
|
||||
// Gets a stable thread-specific ID, but may not be. Use for informative
|
||||
// purposes only.
|
||||
|
@ -69,6 +70,21 @@ void Sleep(std::chrono::duration<Rep, Period> duration) {
|
|||
Sleep(std::chrono::duration_cast<std::chrono::microseconds>(duration));
|
||||
}
|
||||
|
||||
enum class SleepResult {
|
||||
kSuccess,
|
||||
kAlerted,
|
||||
};
|
||||
// Sleeps the current thread for at least as long as the given duration.
|
||||
// The thread is put in an alertable state and may wake to dispatch user
|
||||
// callbacks. If this happens the sleep returns early with
|
||||
// SleepResult::kAlerted.
|
||||
SleepResult AlertableSleep(std::chrono::microseconds duration);
|
||||
template <typename Rep, typename Period>
|
||||
SleepResult AlertableSleep(std::chrono::duration<Rep, Period> duration) {
|
||||
return AlertableSleep(
|
||||
std::chrono::duration_cast<std::chrono::microseconds>(duration));
|
||||
}
|
||||
|
||||
// Results for a WaitHandle operation.
|
||||
enum class WaitResult {
|
||||
// The state of the specified object is signaled.
|
||||
|
@ -236,6 +252,8 @@ class Mutant : public WaitHandle {
|
|||
virtual bool Release() = 0;
|
||||
};
|
||||
|
||||
// Models a Win32-like timer object.
|
||||
// https://msdn.microsoft.com/en-us/library/windows/desktop/ms687012(v=vs.85).aspx
|
||||
class Timer : public WaitHandle {
|
||||
public:
|
||||
// Creates a timer whose state remains signaled until SetOnce() or
|
||||
|
@ -279,6 +297,83 @@ class Timer : public WaitHandle {
|
|||
virtual bool Cancel() = 0;
|
||||
};
|
||||
|
||||
struct ThreadPriority {
|
||||
static const int32_t kLowest = -2;
|
||||
static const int32_t kBelowNormal = -1;
|
||||
static const int32_t kNormal = 0;
|
||||
static const int32_t kAboveNormal = 1;
|
||||
static const int32_t kHighest = 2;
|
||||
};
|
||||
|
||||
// Models a Win32-like thread object.
|
||||
// https://msdn.microsoft.com/en-us/library/windows/desktop/ms682453(v=vs.85).aspx
|
||||
class Thread : public WaitHandle {
|
||||
public:
|
||||
struct CreationParameters {
|
||||
size_t stack_size = 4 * 1024 * 1024;
|
||||
bool create_suspended = false;
|
||||
int32_t initial_priority = 0;
|
||||
};
|
||||
|
||||
// Creates a thread with the given parameters and calls the start routine from
|
||||
// within that thread.
|
||||
static std::unique_ptr<Thread> Create(CreationParameters params,
|
||||
std::function<void()> start_routine);
|
||||
|
||||
// Returns the current name of the thread, if previously specified.
|
||||
std::string name() const { return name_; }
|
||||
|
||||
// Sets the name of the thread, used in debugging and logging.
|
||||
virtual void set_name(std::string name) { name_ = std::move(name); }
|
||||
|
||||
// Returns the current priority value for the thread.
|
||||
virtual int32_t priority() = 0;
|
||||
|
||||
// Sets the priority value for the thread. This value, together with the
|
||||
// priority class of the thread's process, determines the thread's base
|
||||
// priority level. ThreadPriority contains useful constants.
|
||||
virtual void set_priority(int32_t new_priority) = 0;
|
||||
|
||||
// Returns the current processor affinity mask for the thread.
|
||||
virtual uint64_t affinity_mask() = 0;
|
||||
|
||||
// Sets a processor affinity mask for the thread.
|
||||
// A thread affinity mask is a bit vector in which each bit represents a
|
||||
// logical processor that a thread is allowed to run on. A thread affinity
|
||||
// mask must be a subset of the process affinity mask for the containing
|
||||
// process of a thread.
|
||||
virtual void set_affinity_mask(uint64_t new_affinity_mask) = 0;
|
||||
|
||||
// Adds a user-mode asynchronous procedure call request to the thread queue.
|
||||
// When a user-mode APC is queued, the thread is not directed to call the APC
|
||||
// function unless it is in an alertable state. After the thread is in an
|
||||
// alertable state, the thread handles all pending APCs in first in, first out
|
||||
// (FIFO) order, and the wait operation returns WaitResult::kUserCallback.
|
||||
virtual void QueueUserCallback(std::function<void()> callback) = 0;
|
||||
|
||||
// Decrements a thread's suspend count. When the suspend count is decremented
|
||||
// to zero, the execution of the thread is resumed.
|
||||
virtual bool Resume(uint32_t* out_new_suspend_count = nullptr) = 0;
|
||||
|
||||
// Suspends the specified thread.
|
||||
virtual bool Suspend(uint32_t* out_previous_suspend_count = nullptr) = 0;
|
||||
|
||||
// Ends the calling thread.
|
||||
// No destructors are called, and this function does not return.
|
||||
// The state of the thread object becomes signaled, releasing any other
|
||||
// threads that had been waiting for the thread to terminate.
|
||||
virtual void Exit(int exit_code) = 0;
|
||||
|
||||
// Terminates the thread.
|
||||
// No destructors are called, and this function does not return.
|
||||
// The state of the thread object becomes signaled, releasing any other
|
||||
// threads that had been waiting for the thread to terminate.
|
||||
virtual void Terminate(int exit_code) = 0;
|
||||
|
||||
protected:
|
||||
std::string name_;
|
||||
};
|
||||
|
||||
} // namespace threading
|
||||
} // namespace xe
|
||||
|
||||
|
|
|
@ -9,11 +9,23 @@
|
|||
|
||||
#include "xenia/base/threading.h"
|
||||
|
||||
#include "xenia/base/assert.h"
|
||||
#include "xenia/base/logging.h"
|
||||
#include "xenia/base/platform_win.h"
|
||||
|
||||
namespace xe {
|
||||
namespace threading {
|
||||
|
||||
uint32_t logical_processor_count() {
|
||||
static uint32_t value = 0;
|
||||
if (!value) {
|
||||
SYSTEM_INFO system_info;
|
||||
GetSystemInfo(&system_info);
|
||||
value = system_info.dwNumberOfProcessors;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
uint32_t current_thread_id() {
|
||||
return static_cast<uint32_t>(GetCurrentThreadId());
|
||||
}
|
||||
|
@ -67,6 +79,14 @@ void Sleep(std::chrono::microseconds duration) {
|
|||
}
|
||||
}
|
||||
|
||||
SleepResult AlertableSleep(std::chrono::microseconds duration) {
|
||||
if (SleepEx(static_cast<DWORD>(duration.count() / 1000), TRUE) ==
|
||||
WAIT_IO_COMPLETION) {
|
||||
return SleepResult::kAlerted;
|
||||
}
|
||||
return SleepResult::kSuccess;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
class Win32Handle : public T {
|
||||
public:
|
||||
|
@ -268,5 +288,116 @@ std::unique_ptr<Timer> Timer::CreateSynchronizationTimer() {
|
|||
return std::make_unique<Win32Timer>(CreateWaitableTimer(NULL, FALSE, NULL));
|
||||
}
|
||||
|
||||
class Win32Thread : public Win32Handle<Thread> {
|
||||
public:
|
||||
Win32Thread(HANDLE handle) : Win32Handle(handle) {}
|
||||
~Win32Thread() = default;
|
||||
|
||||
void set_name(std::string name) override {
|
||||
AssertCallingThread();
|
||||
xe::threading::set_name(name);
|
||||
Thread::set_name(name);
|
||||
}
|
||||
|
||||
int32_t priority() override { return GetThreadPriority(handle_); }
|
||||
|
||||
void set_priority(int32_t new_priority) override {
|
||||
SetThreadPriority(handle_, new_priority);
|
||||
}
|
||||
|
||||
uint64_t affinity_mask() override {
|
||||
uint64_t value = 0;
|
||||
SetThreadAffinityMask(handle_, reinterpret_cast<DWORD_PTR>(&value));
|
||||
return value;
|
||||
}
|
||||
|
||||
void set_affinity_mask(uint64_t new_affinity_mask) override {
|
||||
SetThreadAffinityMask(handle_, new_affinity_mask);
|
||||
}
|
||||
|
||||
struct ApcData {
|
||||
std::function<void()> callback;
|
||||
};
|
||||
static void NTAPI DispatchApc(ULONG_PTR parameter) {
|
||||
auto apc_data = reinterpret_cast<ApcData*>(parameter);
|
||||
apc_data->callback();
|
||||
delete apc_data;
|
||||
}
|
||||
|
||||
void QueueUserCallback(std::function<void()> callback) override {
|
||||
auto apc_data = new ApcData({std::move(callback)});
|
||||
QueueUserAPC(DispatchApc, handle_, reinterpret_cast<ULONG_PTR>(apc_data));
|
||||
}
|
||||
|
||||
bool Resume(uint32_t* out_new_suspend_count = nullptr) override {
|
||||
if (out_new_suspend_count) {
|
||||
*out_new_suspend_count = 0;
|
||||
}
|
||||
DWORD result = ResumeThread(handle_);
|
||||
if (result == UINT_MAX) {
|
||||
return false;
|
||||
}
|
||||
if (out_new_suspend_count) {
|
||||
*out_new_suspend_count = result;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Suspend(uint32_t* out_previous_suspend_count = nullptr) override {
|
||||
if (out_previous_suspend_count) {
|
||||
*out_previous_suspend_count = 0;
|
||||
}
|
||||
DWORD result = SuspendThread(handle_);
|
||||
if (result == UINT_MAX) {
|
||||
return false;
|
||||
}
|
||||
if (out_previous_suspend_count) {
|
||||
*out_previous_suspend_count = result;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void Exit(int exit_code) override {
|
||||
AssertCallingThread();
|
||||
ExitThread(exit_code);
|
||||
}
|
||||
|
||||
void Terminate(int exit_code) override {
|
||||
TerminateThread(handle_, exit_code);
|
||||
}
|
||||
|
||||
private:
|
||||
void AssertCallingThread() {
|
||||
assert_true(GetCurrentThreadId() == GetThreadId(handle_));
|
||||
}
|
||||
};
|
||||
|
||||
struct ThreadStartData {
|
||||
std::function<void()> start_routine;
|
||||
};
|
||||
DWORD WINAPI ThreadStartRoutine(LPVOID parameter) {
|
||||
auto start_data = reinterpret_cast<ThreadStartData*>(parameter);
|
||||
start_data->start_routine();
|
||||
delete start_data;
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::unique_ptr<Thread> Thread::Create(CreationParameters params,
|
||||
std::function<void()> start_routine) {
|
||||
auto start_data = new ThreadStartData({std::move(start_routine)});
|
||||
HANDLE handle =
|
||||
CreateThread(NULL, params.stack_size, ThreadStartRoutine, start_data,
|
||||
params.create_suspended ? CREATE_SUSPENDED : 0, NULL);
|
||||
if (!handle) {
|
||||
// TODO(benvanik): pass back?
|
||||
auto last_error = GetLastError();
|
||||
XELOGE("Unable to CreateThread: %d", last_error);
|
||||
delete start_data;
|
||||
return nullptr;
|
||||
}
|
||||
GetThreadId(handle);
|
||||
return std::make_unique<Win32Thread>(handle);
|
||||
}
|
||||
|
||||
} // namespace threading
|
||||
} // namespace xe
|
||||
|
|
|
@ -13,23 +13,22 @@
|
|||
namespace xe {
|
||||
namespace kernel {
|
||||
|
||||
XEvent::XEvent(KernelState* kernel_state)
|
||||
: XObject(kernel_state, kTypeEvent), native_handle_(NULL) {}
|
||||
XEvent::XEvent(KernelState* kernel_state) : XObject(kernel_state, kTypeEvent) {}
|
||||
|
||||
XEvent::~XEvent() {
|
||||
if (native_handle_) {
|
||||
CloseHandle(native_handle_);
|
||||
XEvent::~XEvent() = default;
|
||||
|
||||
void XEvent::Initialize(bool manual_reset, bool initial_state) {
|
||||
assert_false(event_);
|
||||
|
||||
if (manual_reset) {
|
||||
event_ = xe::threading::Event::CreateManualResetEvent(initial_state);
|
||||
} else {
|
||||
event_ = xe::threading::Event::CreateAutoResetEvent(initial_state);
|
||||
}
|
||||
}
|
||||
|
||||
void XEvent::Initialize(bool manual_reset, bool initial_state) {
|
||||
assert_null(native_handle_);
|
||||
|
||||
native_handle_ = CreateEvent(NULL, manual_reset, initial_state, NULL);
|
||||
}
|
||||
|
||||
void XEvent::InitializeNative(void* native_ptr, X_DISPATCH_HEADER& header) {
|
||||
assert_null(native_handle_);
|
||||
assert_false(event_);
|
||||
|
||||
bool manual_reset;
|
||||
switch (header.type) {
|
||||
|
@ -45,21 +44,25 @@ void XEvent::InitializeNative(void* native_ptr, X_DISPATCH_HEADER& header) {
|
|||
}
|
||||
|
||||
bool initial_state = header.signal_state ? true : false;
|
||||
|
||||
native_handle_ = CreateEvent(NULL, manual_reset, initial_state, NULL);
|
||||
Initialize(manual_reset, initial_state);
|
||||
}
|
||||
|
||||
int32_t XEvent::Set(uint32_t priority_increment, bool wait) {
|
||||
return SetEvent(native_handle_) ? 1 : 0;
|
||||
event_->Set();
|
||||
return 1;
|
||||
}
|
||||
|
||||
int32_t XEvent::Pulse(uint32_t priority_increment, bool wait) {
|
||||
return PulseEvent(native_handle_) ? 1 : 0;
|
||||
event_->Pulse();
|
||||
return 1;
|
||||
}
|
||||
|
||||
int32_t XEvent::Reset() { return ResetEvent(native_handle_) ? 1 : 0; }
|
||||
int32_t XEvent::Reset() {
|
||||
event_->Reset();
|
||||
return 1;
|
||||
}
|
||||
|
||||
void XEvent::Clear() { ResetEvent(native_handle_); }
|
||||
void XEvent::Clear() { event_->Reset(); }
|
||||
|
||||
} // namespace kernel
|
||||
} // namespace xe
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
#ifndef XENIA_KERNEL_XBOXKRNL_XEVENT_H_
|
||||
#define XENIA_KERNEL_XBOXKRNL_XEVENT_H_
|
||||
|
||||
#include "xenia/base/platform_win.h"
|
||||
#include "xenia/base/threading.h"
|
||||
#include "xenia/kernel/xobject.h"
|
||||
#include "xenia/xbox.h"
|
||||
|
||||
|
@ -20,7 +20,7 @@ namespace kernel {
|
|||
class XEvent : public XObject {
|
||||
public:
|
||||
XEvent(KernelState* kernel_state);
|
||||
virtual ~XEvent();
|
||||
~XEvent() override;
|
||||
|
||||
void Initialize(bool manual_reset, bool initial_state);
|
||||
void InitializeNative(void* native_ptr, X_DISPATCH_HEADER& header);
|
||||
|
@ -30,10 +30,10 @@ class XEvent : public XObject {
|
|||
int32_t Reset();
|
||||
void Clear();
|
||||
|
||||
virtual void* GetWaitHandle() { return native_handle_; }
|
||||
xe::threading::WaitHandle* GetWaitHandle() override { return event_.get(); }
|
||||
|
||||
private:
|
||||
HANDLE native_handle_;
|
||||
std::unique_ptr<xe::threading::Event> event_;
|
||||
};
|
||||
|
||||
} // namespace kernel
|
||||
|
|
|
@ -19,9 +19,7 @@ namespace kernel {
|
|||
XFile::XFile(KernelState* kernel_state, uint32_t file_access, vfs::Entry* entry)
|
||||
: XObject(kernel_state, kTypeFile),
|
||||
entry_(entry),
|
||||
file_access_(file_access),
|
||||
position_(0),
|
||||
find_index_(0) {
|
||||
file_access_(file_access) {
|
||||
async_event_ = new XEvent(kernel_state);
|
||||
async_event_->Initialize(false, false);
|
||||
}
|
||||
|
@ -32,8 +30,6 @@ XFile::~XFile() {
|
|||
async_event_->Delete();
|
||||
}
|
||||
|
||||
void* XFile::GetWaitHandle() { return async_event_->GetWaitHandle(); }
|
||||
|
||||
X_STATUS XFile::QueryDirectory(X_FILE_DIRECTORY_INFORMATION* out_info,
|
||||
size_t length, const char* file_name,
|
||||
bool restart) {
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#define XENIA_KERNEL_XBOXKRNL_XFILE_H_
|
||||
|
||||
#include "xenia/base/filesystem.h"
|
||||
#include "xenia/kernel/objects/xevent.h"
|
||||
#include "xenia/kernel/xobject.h"
|
||||
#include "xenia/vfs/device.h"
|
||||
#include "xenia/vfs/entry.h"
|
||||
|
@ -21,7 +22,6 @@ namespace xe {
|
|||
namespace kernel {
|
||||
|
||||
class XAsyncRequest;
|
||||
class XEvent;
|
||||
|
||||
// https://msdn.microsoft.com/en-us/library/windows/hardware/ff545822.aspx
|
||||
struct X_FILE_NETWORK_OPEN_INFORMATION {
|
||||
|
@ -100,7 +100,9 @@ class XFile : public XObject {
|
|||
X_STATUS Write(const void* buffer, size_t buffer_length, size_t byte_offset,
|
||||
size_t* out_bytes_written);
|
||||
|
||||
virtual void* GetWaitHandle();
|
||||
xe::threading::WaitHandle* GetWaitHandle() override {
|
||||
return async_event_->GetWaitHandle();
|
||||
}
|
||||
|
||||
protected:
|
||||
XFile(KernelState* kernel_state, uint32_t file_access, vfs::Entry* entry);
|
||||
|
@ -112,16 +114,16 @@ class XFile : public XObject {
|
|||
}
|
||||
|
||||
private:
|
||||
vfs::Entry* entry_;
|
||||
uint32_t file_access_;
|
||||
XEvent* async_event_;
|
||||
vfs::Entry* entry_ = nullptr;
|
||||
uint32_t file_access_ = 0;
|
||||
XEvent* async_event_ = nullptr;
|
||||
|
||||
// TODO(benvanik): create flags, open state, etc.
|
||||
|
||||
size_t position_;
|
||||
size_t position_ = 0;
|
||||
|
||||
xe::filesystem::WildcardEngine find_engine_;
|
||||
size_t find_index_;
|
||||
size_t find_index_ = 0;
|
||||
};
|
||||
|
||||
} // namespace kernel
|
||||
|
|
|
@ -13,22 +13,18 @@ namespace xe {
|
|||
namespace kernel {
|
||||
|
||||
XMutant::XMutant(KernelState* kernel_state)
|
||||
: XObject(kernel_state, kTypeMutant), native_handle_(NULL) {}
|
||||
: XObject(kernel_state, kTypeMutant) {}
|
||||
|
||||
XMutant::~XMutant() {
|
||||
if (native_handle_) {
|
||||
CloseHandle(native_handle_);
|
||||
}
|
||||
}
|
||||
XMutant::~XMutant() = default;
|
||||
|
||||
void XMutant::Initialize(bool initial_owner) {
|
||||
assert_null(native_handle_);
|
||||
assert_false(mutant_);
|
||||
|
||||
native_handle_ = CreateMutex(NULL, initial_owner ? TRUE : FALSE, NULL);
|
||||
mutant_ = xe::threading::Mutant::Create(initial_owner);
|
||||
}
|
||||
|
||||
void XMutant::InitializeNative(void* native_ptr, X_DISPATCH_HEADER& header) {
|
||||
assert_null(native_handle_);
|
||||
assert_false(mutant_);
|
||||
|
||||
// Haven't seen this yet, but it's possible.
|
||||
assert_always();
|
||||
|
@ -38,8 +34,7 @@ X_STATUS XMutant::ReleaseMutant(uint32_t priority_increment, bool abandon,
|
|||
bool wait) {
|
||||
// TODO(benvanik): abandoning.
|
||||
assert_false(abandon);
|
||||
BOOL result = ReleaseMutex(native_handle_);
|
||||
if (result) {
|
||||
if (mutant_->Release()) {
|
||||
return X_STATUS_SUCCESS;
|
||||
} else {
|
||||
return X_STATUS_MUTANT_NOT_OWNED;
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
#ifndef XENIA_KERNEL_XBOXKRNL_XMUTANT_H_
|
||||
#define XENIA_KERNEL_XBOXKRNL_XMUTANT_H_
|
||||
|
||||
#include "xenia/base/platform_win.h"
|
||||
#include "xenia/base/threading.h"
|
||||
#include "xenia/kernel/xobject.h"
|
||||
#include "xenia/xbox.h"
|
||||
|
||||
|
@ -20,17 +20,17 @@ namespace kernel {
|
|||
class XMutant : public XObject {
|
||||
public:
|
||||
XMutant(KernelState* kernel_state);
|
||||
virtual ~XMutant();
|
||||
~XMutant() override;
|
||||
|
||||
void Initialize(bool initial_owner);
|
||||
void InitializeNative(void* native_ptr, X_DISPATCH_HEADER& header);
|
||||
|
||||
X_STATUS ReleaseMutant(uint32_t priority_increment, bool abandon, bool wait);
|
||||
|
||||
virtual void* GetWaitHandle() { return native_handle_; }
|
||||
xe::threading::WaitHandle* GetWaitHandle() override { return mutant_.get(); }
|
||||
|
||||
private:
|
||||
HANDLE native_handle_;
|
||||
std::unique_ptr<xe::threading::Mutant> mutant_;
|
||||
};
|
||||
|
||||
} // namespace kernel
|
||||
|
|
|
@ -9,29 +9,22 @@
|
|||
|
||||
#include "xenia/kernel/objects/xnotify_listener.h"
|
||||
|
||||
#include "xenia/base/platform_win.h"
|
||||
#include "xenia/kernel/kernel_state.h"
|
||||
|
||||
namespace xe {
|
||||
namespace kernel {
|
||||
|
||||
XNotifyListener::XNotifyListener(KernelState* kernel_state)
|
||||
: XObject(kernel_state, kTypeNotifyListener),
|
||||
wait_handle_(NULL),
|
||||
mask_(0),
|
||||
notification_count_(0) {}
|
||||
: XObject(kernel_state, kTypeNotifyListener) {}
|
||||
|
||||
XNotifyListener::~XNotifyListener() {
|
||||
kernel_state_->UnregisterNotifyListener(this);
|
||||
if (wait_handle_) {
|
||||
CloseHandle(wait_handle_);
|
||||
}
|
||||
}
|
||||
|
||||
void XNotifyListener::Initialize(uint64_t mask) {
|
||||
assert_null(wait_handle_);
|
||||
assert_false(wait_handle_);
|
||||
|
||||
wait_handle_ = CreateEvent(NULL, TRUE, FALSE, NULL);
|
||||
wait_handle_ = xe::threading::Event::CreateManualResetEvent(false);
|
||||
mask_ = mask;
|
||||
|
||||
kernel_state_->RegisterNotifyListener(this);
|
||||
|
@ -52,7 +45,7 @@ void XNotifyListener::EnqueueNotification(XNotificationID id, uint32_t data) {
|
|||
notification_count_++;
|
||||
notifications_.insert({id, data});
|
||||
}
|
||||
SetEvent(wait_handle_);
|
||||
wait_handle_->Set();
|
||||
}
|
||||
|
||||
bool XNotifyListener::DequeueNotification(XNotificationID* out_id,
|
||||
|
@ -67,7 +60,7 @@ bool XNotifyListener::DequeueNotification(XNotificationID* out_id,
|
|||
notifications_.erase(it);
|
||||
notification_count_--;
|
||||
if (!notification_count_) {
|
||||
ResetEvent(wait_handle_);
|
||||
wait_handle_->Reset();
|
||||
}
|
||||
}
|
||||
return dequeued;
|
||||
|
@ -85,7 +78,7 @@ bool XNotifyListener::DequeueNotification(XNotificationID id,
|
|||
notifications_.erase(it);
|
||||
notification_count_--;
|
||||
if (!notification_count_) {
|
||||
ResetEvent(wait_handle_);
|
||||
wait_handle_->Reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,10 +10,11 @@
|
|||
#ifndef XENIA_KERNEL_XBOXKRNL_XNOTIFY_LISTENER_H_
|
||||
#define XENIA_KERNEL_XBOXKRNL_XNOTIFY_LISTENER_H_
|
||||
|
||||
#include <mutex>
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "xenia/base/mutex.h"
|
||||
#include "xenia/base/threading.h"
|
||||
#include "xenia/kernel/xobject.h"
|
||||
#include "xenia/xbox.h"
|
||||
|
||||
|
@ -25,7 +26,7 @@ namespace kernel {
|
|||
class XNotifyListener : public XObject {
|
||||
public:
|
||||
XNotifyListener(KernelState* kernel_state);
|
||||
virtual ~XNotifyListener();
|
||||
~XNotifyListener() override;
|
||||
|
||||
uint64_t mask() const { return mask_; }
|
||||
|
||||
|
@ -35,14 +36,14 @@ class XNotifyListener : public XObject {
|
|||
bool DequeueNotification(XNotificationID* out_id, uint32_t* out_data);
|
||||
bool DequeueNotification(XNotificationID id, uint32_t* out_data);
|
||||
|
||||
virtual void* GetWaitHandle() { return wait_handle_; }
|
||||
xe::threading::WaitHandle* GetWaitHandle() { return wait_handle_.get(); }
|
||||
|
||||
private:
|
||||
HANDLE wait_handle_;
|
||||
std::unique_ptr<xe::threading::Event> wait_handle_;
|
||||
xe::mutex lock_;
|
||||
std::unordered_map<XNotificationID, uint32_t> notifications_;
|
||||
size_t notification_count_;
|
||||
uint64_t mask_;
|
||||
size_t notification_count_ = 0;
|
||||
uint64_t mask_ = 0;
|
||||
};
|
||||
|
||||
} // namespace kernel
|
||||
|
|
|
@ -13,32 +13,28 @@ namespace xe {
|
|||
namespace kernel {
|
||||
|
||||
XSemaphore::XSemaphore(KernelState* kernel_state)
|
||||
: XObject(kernel_state, kTypeSemaphore), native_handle_(NULL) {}
|
||||
: XObject(kernel_state, kTypeSemaphore) {}
|
||||
|
||||
XSemaphore::~XSemaphore() {
|
||||
if (native_handle_) {
|
||||
CloseHandle(native_handle_);
|
||||
}
|
||||
}
|
||||
XSemaphore::~XSemaphore() = default;
|
||||
|
||||
void XSemaphore::Initialize(int32_t initial_count, int32_t maximum_count) {
|
||||
assert_null(native_handle_);
|
||||
assert_false(semaphore_);
|
||||
|
||||
CreateNative(sizeof(X_SEMAPHORE));
|
||||
|
||||
native_handle_ = CreateSemaphore(NULL, initial_count, maximum_count, NULL);
|
||||
semaphore_ = xe::threading::Semaphore::Create(initial_count, maximum_count);
|
||||
}
|
||||
|
||||
void XSemaphore::InitializeNative(void* native_ptr, X_DISPATCH_HEADER& header) {
|
||||
assert_null(native_handle_);
|
||||
assert_false(semaphore_);
|
||||
|
||||
// NOT IMPLEMENTED
|
||||
// We expect Initialize to be called shortly.
|
||||
}
|
||||
|
||||
int32_t XSemaphore::ReleaseSemaphore(int32_t release_count) {
|
||||
LONG previous_count = 0;
|
||||
::ReleaseSemaphore(native_handle_, release_count, &previous_count);
|
||||
int32_t previous_count = 0;
|
||||
semaphore_->Release(release_count, &previous_count);
|
||||
return previous_count;
|
||||
}
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
#ifndef XENIA_KERNEL_XBOXKRNL_XSEMAPHORE_H_
|
||||
#define XENIA_KERNEL_XBOXKRNL_XSEMAPHORE_H_
|
||||
|
||||
#include "xenia/base/platform_win.h"
|
||||
#include "xenia/base/threading.h"
|
||||
#include "xenia/kernel/xobject.h"
|
||||
#include "xenia/xbox.h"
|
||||
|
||||
|
@ -25,17 +25,19 @@ struct X_SEMAPHORE {
|
|||
class XSemaphore : public XObject {
|
||||
public:
|
||||
XSemaphore(KernelState* kernel_state);
|
||||
virtual ~XSemaphore();
|
||||
~XSemaphore() override;
|
||||
|
||||
void Initialize(int32_t initial_count, int32_t maximum_count);
|
||||
void InitializeNative(void* native_ptr, X_DISPATCH_HEADER& header);
|
||||
|
||||
int32_t ReleaseSemaphore(int32_t release_count);
|
||||
|
||||
virtual void* GetWaitHandle() { return native_handle_; }
|
||||
xe::threading::WaitHandle* GetWaitHandle() override {
|
||||
return semaphore_.get();
|
||||
}
|
||||
|
||||
private:
|
||||
HANDLE native_handle_;
|
||||
std::unique_ptr<xe::threading::Semaphore> semaphore_;
|
||||
};
|
||||
|
||||
} // namespace kernel
|
||||
|
|
|
@ -45,7 +45,6 @@ XThread::XThread(KernelState* kernel_state, uint32_t stack_size,
|
|||
bool guest_thread)
|
||||
: XObject(kernel_state, kTypeThread),
|
||||
thread_id_(++next_xthread_id),
|
||||
thread_handle_(0),
|
||||
pcr_address_(0),
|
||||
thread_state_address_(0),
|
||||
thread_state_(0),
|
||||
|
@ -84,7 +83,7 @@ XThread::~XThread() {
|
|||
|
||||
delete apc_list_;
|
||||
|
||||
PlatformDestroy();
|
||||
thread_.reset();
|
||||
|
||||
if (thread_state_) {
|
||||
delete thread_state_;
|
||||
|
@ -93,7 +92,7 @@ XThread::~XThread() {
|
|||
kernel_state()->memory()->SystemHeapFree(tls_address_);
|
||||
kernel_state()->memory()->SystemHeapFree(pcr_address_);
|
||||
|
||||
if (thread_handle_) {
|
||||
if (thread_) {
|
||||
// TODO(benvanik): platform kill
|
||||
XELOGE("Thread disposed without exiting");
|
||||
}
|
||||
|
@ -130,7 +129,11 @@ void XThread::set_last_error(uint32_t error_code) {
|
|||
|
||||
void XThread::set_name(const std::string& name) {
|
||||
name_ = name;
|
||||
xe::threading::set_name(thread_handle_, name);
|
||||
if (thread_) {
|
||||
// May be getting set before the thread is created.
|
||||
// One the thread is ready it will handle it.
|
||||
thread_->set_name(name);
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t GetFakeCpuNumber(uint8_t proc_mask) {
|
||||
|
@ -318,21 +321,45 @@ X_STATUS XThread::Create() {
|
|||
xe::store_and_swap<uint32_t>(p + 0x16C, creation_params_.creation_flags);
|
||||
xe::store_and_swap<uint32_t>(p + 0x17C, 1);
|
||||
|
||||
X_STATUS return_code = PlatformCreate();
|
||||
if (XFAILED(return_code)) {
|
||||
XELOGW("Unable to create platform thread (%.8X)", return_code);
|
||||
return return_code;
|
||||
}
|
||||
// Always retain when starting - the thread owns itself until exited.
|
||||
Retain();
|
||||
|
||||
// uint32_t proc_mask = creation_params_.creation_flags >> 24;
|
||||
if (proc_mask) {
|
||||
SetAffinity(proc_mask);
|
||||
xe::threading::Thread::CreationParameters params;
|
||||
params.stack_size = 16 * 1024 * 1024; // Ignore game, always big!
|
||||
params.create_suspended = (creation_params_.creation_flags & 0x1) == 0x1;
|
||||
thread_ = xe::threading::Thread::Create(params, [this]() {
|
||||
// Set name immediately, if we have one.
|
||||
thread_->set_name(name());
|
||||
|
||||
// Profiler needs to know about the thread.
|
||||
xe::Profiler::ThreadEnter(name().c_str());
|
||||
|
||||
// Execute user code.
|
||||
current_thread_tls = this;
|
||||
Execute();
|
||||
current_thread_tls = nullptr;
|
||||
|
||||
xe::Profiler::ThreadExit();
|
||||
|
||||
// Release the self-reference to the thread.
|
||||
Release();
|
||||
});
|
||||
if (!thread_) {
|
||||
// TODO(benvanik): translate error?
|
||||
XELOGE("CreateThread failed");
|
||||
return X_STATUS_NO_MEMORY;
|
||||
}
|
||||
thread_->set_affinity_mask(proc_mask);
|
||||
|
||||
if (creation_params_.creation_flags & 0x60) {
|
||||
thread_->set_priority(creation_params_.creation_flags & 0x20 ? 1 : 0);
|
||||
}
|
||||
|
||||
return X_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
X_STATUS XThread::Exit(int exit_code) {
|
||||
// This may only be called on the thread itself.
|
||||
assert_true(XThread::GetCurrentThread() == this);
|
||||
|
||||
// TODO(benvanik): set exit code in thread state block
|
||||
|
@ -348,10 +375,9 @@ X_STATUS XThread::Exit(int exit_code) {
|
|||
|
||||
running_ = false;
|
||||
Release();
|
||||
X_STATUS return_code = PlatformExit(exit_code);
|
||||
if (XFAILED(return_code)) {
|
||||
return return_code;
|
||||
}
|
||||
|
||||
// NOTE: this does not return!
|
||||
thread_->Exit(exit_code);
|
||||
return X_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
|
@ -360,135 +386,11 @@ X_STATUS XThread::Terminate(int exit_code) {
|
|||
|
||||
running_ = false;
|
||||
Release();
|
||||
X_STATUS status = PlatformTerminate(exit_code);
|
||||
if (XFAILED(status)) {
|
||||
return status;
|
||||
}
|
||||
|
||||
thread_->Terminate(exit_code);
|
||||
return X_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
#if XE_PLATFORM_WIN32
|
||||
|
||||
static uint32_t __stdcall XThreadStartCallbackWin32(void* param) {
|
||||
auto thread = reinterpret_cast<XThread*>(param);
|
||||
thread->set_name(thread->name());
|
||||
xe::Profiler::ThreadEnter(thread->name().c_str());
|
||||
current_thread_tls = thread;
|
||||
thread->Execute();
|
||||
current_thread_tls = nullptr;
|
||||
xe::Profiler::ThreadExit();
|
||||
thread->Release();
|
||||
return 0;
|
||||
}
|
||||
|
||||
X_STATUS XThread::PlatformCreate() {
|
||||
Retain();
|
||||
bool suspended = creation_params_.creation_flags & 0x1;
|
||||
const size_t kStackSize = 16 * 1024 * 1024; // let's do the stupid thing
|
||||
thread_handle_ = CreateThread(
|
||||
NULL, kStackSize, (LPTHREAD_START_ROUTINE)XThreadStartCallbackWin32, this,
|
||||
suspended ? CREATE_SUSPENDED : 0, NULL);
|
||||
if (!thread_handle_) {
|
||||
uint32_t last_error = GetLastError();
|
||||
// TODO(benvanik): translate?
|
||||
XELOGE("CreateThread failed with %d", last_error);
|
||||
return last_error;
|
||||
}
|
||||
|
||||
if (creation_params_.creation_flags & 0x60) {
|
||||
SetThreadPriority(thread_handle_,
|
||||
creation_params_.creation_flags & 0x20 ? 1 : 0);
|
||||
}
|
||||
|
||||
return X_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
void XThread::PlatformDestroy() {
|
||||
CloseHandle(reinterpret_cast<HANDLE>(thread_handle_));
|
||||
thread_handle_ = NULL;
|
||||
}
|
||||
|
||||
X_STATUS XThread::PlatformExit(int exit_code) {
|
||||
// NOTE: does not return.
|
||||
ExitThread(exit_code);
|
||||
return X_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
X_STATUS XThread::PlatformTerminate(int exit_code) {
|
||||
if (!TerminateThread(thread_handle_, exit_code)) {
|
||||
return X_STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
|
||||
return X_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static void* XThreadStartCallbackPthreads(void* param) {
|
||||
auto thread = object_ref<XThread>(reinterpret_cast<XThread*>(param));
|
||||
xe::Profiler::ThreadEnter(thread->name().c_str());
|
||||
current_thread_tls = thread.get();
|
||||
thread->Execute();
|
||||
current_thread_tls = nullptr;
|
||||
xe::Profiler::ThreadExit();
|
||||
return 0;
|
||||
}
|
||||
|
||||
X_STATUS XThread::PlatformCreate() {
|
||||
pthread_attr_t attr;
|
||||
|
||||
pthread_attr_init(&attr);
|
||||
// TODO(benvanik): this shouldn't be necessary
|
||||
// pthread_attr_setstacksize(&attr, creation_params_.stack_size);
|
||||
|
||||
int result_code;
|
||||
if (creation_params_.creation_flags & 0x1) {
|
||||
#if XE_PLATFORM_MAC
|
||||
result_code = pthread_create_suspended_np(
|
||||
reinterpret_cast<pthread_t*>(&thread_handle_), &attr,
|
||||
&XThreadStartCallbackPthreads, this);
|
||||
#else
|
||||
// TODO(benvanik): pthread_create_suspended_np on linux
|
||||
assert_always();
|
||||
#endif // OSX
|
||||
} else {
|
||||
result_code = pthread_create(reinterpret_cast<pthread_t*>(&thread_handle_),
|
||||
&attr, &XThreadStartCallbackPthreads, this);
|
||||
}
|
||||
|
||||
pthread_attr_destroy(&attr);
|
||||
|
||||
switch (result_code) {
|
||||
case 0:
|
||||
// Succeeded!
|
||||
return X_STATUS_SUCCESS;
|
||||
default:
|
||||
case EAGAIN:
|
||||
return X_STATUS_NO_MEMORY;
|
||||
case EINVAL:
|
||||
case EPERM:
|
||||
return X_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
}
|
||||
|
||||
void XThread::PlatformDestroy() {
|
||||
// No-op?
|
||||
}
|
||||
|
||||
X_STATUS XThread::PlatformExit(int exit_code) {
|
||||
// NOTE: does not return.
|
||||
pthread_exit(reinterpret_cast<void*>(exit_code));
|
||||
return X_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
X_STATUS XThread::PlatformTerminate(int exit_code) {
|
||||
// TODO!
|
||||
assert_always();
|
||||
}
|
||||
|
||||
#endif // WIN32
|
||||
|
||||
void XThread::Execute() {
|
||||
XELOGKERNEL("XThread::Execute thid %d (handle=%.8X, '%s', native=%.8X)",
|
||||
thread_id_, handle(), name_.c_str(),
|
||||
|
@ -540,7 +442,7 @@ uint32_t XThread::RaiseIrql(uint32_t new_irql) {
|
|||
|
||||
void XThread::LowerIrql(uint32_t new_irql) { irql_ = new_irql; }
|
||||
|
||||
void XThread::CheckApcs() { DeliverAPCs(this); }
|
||||
void XThread::CheckApcs() { DeliverAPCs(); }
|
||||
|
||||
void XThread::LockApc() { apc_lock_.lock(); }
|
||||
|
||||
|
@ -548,8 +450,7 @@ void XThread::UnlockApc(bool queue_delivery) {
|
|||
bool needs_apc = apc_list_->HasPending();
|
||||
apc_lock_.unlock();
|
||||
if (needs_apc && queue_delivery) {
|
||||
QueueUserAPC(reinterpret_cast<PAPCFUNC>(DeliverAPCs), thread_handle_,
|
||||
reinterpret_cast<ULONG_PTR>(this));
|
||||
thread_->QueueUserCallback([this]() { DeliverAPCs(); });
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -577,21 +478,16 @@ void XThread::EnqueueApc(uint32_t normal_routine, uint32_t normal_context,
|
|||
UnlockApc(true);
|
||||
}
|
||||
|
||||
void XThread::DeliverAPCs(void* data) {
|
||||
XThread* thread = reinterpret_cast<XThread*>(data);
|
||||
assert_true(XThread::GetCurrentThread() == thread);
|
||||
|
||||
void XThread::DeliverAPCs() {
|
||||
// http://www.drdobbs.com/inside-nts-asynchronous-procedure-call/184416590?pgno=1
|
||||
// http://www.drdobbs.com/inside-nts-asynchronous-procedure-call/184416590?pgno=7
|
||||
auto memory = thread->memory();
|
||||
auto processor = thread->kernel_state()->processor();
|
||||
auto apc_list = thread->apc_list();
|
||||
thread->LockApc();
|
||||
while (apc_list->HasPending()) {
|
||||
auto processor = kernel_state()->processor();
|
||||
LockApc();
|
||||
while (apc_list_->HasPending()) {
|
||||
// Get APC entry (offset for LIST_ENTRY offset) and cache what we need.
|
||||
// Calling the routine may delete the memory/overwrite it.
|
||||
uint32_t apc_ptr = apc_list->Shift() - 8;
|
||||
auto apc = reinterpret_cast<XAPC*>(memory->TranslateVirtual(apc_ptr));
|
||||
uint32_t apc_ptr = apc_list_->Shift() - 8;
|
||||
auto apc = reinterpret_cast<XAPC*>(memory()->TranslateVirtual(apc_ptr));
|
||||
bool needs_freeing = apc->kernel_routine == XAPC::kDummyKernelRoutine;
|
||||
|
||||
XELOGD("Delivering APC to %.8X", uint32_t(apc->normal_routine));
|
||||
|
@ -603,7 +499,7 @@ void XThread::DeliverAPCs(void* data) {
|
|||
// The routine can modify all of its arguments before passing it on.
|
||||
// Since we need to give guest accessible pointers over, we copy things
|
||||
// into and out of scratch.
|
||||
uint8_t* scratch_ptr = memory->TranslateVirtual(thread->scratch_address_);
|
||||
uint8_t* scratch_ptr = memory()->TranslateVirtual(scratch_address_);
|
||||
xe::store_and_swap<uint32_t>(scratch_ptr + 0, apc->normal_routine);
|
||||
xe::store_and_swap<uint32_t>(scratch_ptr + 4, apc->normal_context);
|
||||
xe::store_and_swap<uint32_t>(scratch_ptr + 8, apc->arg1);
|
||||
|
@ -612,11 +508,11 @@ void XThread::DeliverAPCs(void* data) {
|
|||
// kernel_routine(apc_address, &normal_routine, &normal_context,
|
||||
// &system_arg1, &system_arg2)
|
||||
uint64_t kernel_args[] = {
|
||||
apc_ptr, thread->scratch_address_ + 0, thread->scratch_address_ + 4,
|
||||
thread->scratch_address_ + 8, thread->scratch_address_ + 12,
|
||||
apc_ptr, scratch_address_ + 0, scratch_address_ + 4,
|
||||
scratch_address_ + 8, scratch_address_ + 12,
|
||||
};
|
||||
processor->Execute(thread->thread_state(), apc->kernel_routine,
|
||||
kernel_args, xe::countof(kernel_args));
|
||||
processor->Execute(thread_state_, apc->kernel_routine, kernel_args,
|
||||
xe::countof(kernel_args));
|
||||
}
|
||||
uint32_t normal_routine = xe::load_and_swap<uint32_t>(scratch_ptr + 0);
|
||||
uint32_t normal_context = xe::load_and_swap<uint32_t>(scratch_ptr + 4);
|
||||
|
@ -626,22 +522,22 @@ void XThread::DeliverAPCs(void* data) {
|
|||
// Call the normal routine. Note that it may have been killed by the kernel
|
||||
// routine.
|
||||
if (normal_routine) {
|
||||
thread->UnlockApc(false);
|
||||
UnlockApc(false);
|
||||
// normal_routine(normal_context, system_arg1, system_arg2)
|
||||
uint64_t normal_args[] = {normal_context, arg1, arg2};
|
||||
processor->Execute(thread->thread_state(), normal_routine, normal_args,
|
||||
processor->Execute(thread_state_, normal_routine, normal_args,
|
||||
xe::countof(normal_args));
|
||||
thread->LockApc();
|
||||
LockApc();
|
||||
}
|
||||
|
||||
XELOGD("Completed delivery of APC to %.8X", uint32_t(apc->normal_routine));
|
||||
|
||||
// If special, free it.
|
||||
if (needs_freeing) {
|
||||
memory->SystemHeapFree(apc_ptr);
|
||||
memory()->SystemHeapFree(apc_ptr);
|
||||
}
|
||||
}
|
||||
thread->UnlockApc(true);
|
||||
UnlockApc(true);
|
||||
}
|
||||
|
||||
void XThread::RundownAPCs() {
|
||||
|
@ -675,24 +571,24 @@ void XThread::RundownAPCs() {
|
|||
UnlockApc(true);
|
||||
}
|
||||
|
||||
int32_t XThread::QueryPriority() { return GetThreadPriority(thread_handle_); }
|
||||
int32_t XThread::QueryPriority() { return thread_->priority(); }
|
||||
|
||||
void XThread::SetPriority(int32_t increment) {
|
||||
priority_ = increment;
|
||||
int target_priority = 0;
|
||||
int32_t target_priority = 0;
|
||||
if (increment > 0x22) {
|
||||
target_priority = THREAD_PRIORITY_HIGHEST;
|
||||
target_priority = xe::threading::ThreadPriority::kHighest;
|
||||
} else if (increment > 0x11) {
|
||||
target_priority = THREAD_PRIORITY_ABOVE_NORMAL;
|
||||
target_priority = xe::threading::ThreadPriority::kAboveNormal;
|
||||
} else if (increment < -0x22) {
|
||||
target_priority = THREAD_PRIORITY_IDLE;
|
||||
target_priority = xe::threading::ThreadPriority::kLowest;
|
||||
} else if (increment < -0x11) {
|
||||
target_priority = THREAD_PRIORITY_LOWEST;
|
||||
target_priority = xe::threading::ThreadPriority::kBelowNormal;
|
||||
} else {
|
||||
target_priority = THREAD_PRIORITY_NORMAL;
|
||||
target_priority = xe::threading::ThreadPriority::kNormal;
|
||||
}
|
||||
if (!FLAGS_ignore_thread_priorities) {
|
||||
SetThreadPriority(thread_handle_, target_priority);
|
||||
thread_->set_priority(target_priority);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -707,15 +603,13 @@ void XThread::SetAffinity(uint32_t affinity) {
|
|||
// 5 - core 2, thread 1 - user
|
||||
// TODO(benvanik): implement better thread distribution.
|
||||
// NOTE: these are logical processors, not physical processors or cores.
|
||||
SYSTEM_INFO system_info;
|
||||
GetSystemInfo(&system_info);
|
||||
if (system_info.dwNumberOfProcessors < 6) {
|
||||
if (xe::threading::logical_processor_count() < 6) {
|
||||
XELOGW("Too few processors - scheduling will be wonky");
|
||||
}
|
||||
SetActiveCpu(GetFakeCpuNumber(affinity));
|
||||
affinity_ = affinity;
|
||||
if (!FLAGS_ignore_thread_affinities) {
|
||||
SetThreadAffinityMask(reinterpret_cast<HANDLE>(thread_handle_), affinity);
|
||||
thread_->set_affinity_mask(affinity);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -730,11 +624,7 @@ void XThread::SetActiveCpu(uint32_t cpu_index) {
|
|||
}
|
||||
|
||||
X_STATUS XThread::Resume(uint32_t* out_suspend_count) {
|
||||
DWORD result = ResumeThread(thread_handle_);
|
||||
if (result >= 0) {
|
||||
if (out_suspend_count) {
|
||||
*out_suspend_count = result;
|
||||
}
|
||||
if (thread_->Resume(out_suspend_count)) {
|
||||
return X_STATUS_SUCCESS;
|
||||
} else {
|
||||
return X_STATUS_UNSUCCESSFUL;
|
||||
|
@ -742,11 +632,7 @@ X_STATUS XThread::Resume(uint32_t* out_suspend_count) {
|
|||
}
|
||||
|
||||
X_STATUS XThread::Suspend(uint32_t* out_suspend_count) {
|
||||
DWORD result = SuspendThread(thread_handle_);
|
||||
if (result >= 0) {
|
||||
if (out_suspend_count) {
|
||||
*out_suspend_count = result;
|
||||
}
|
||||
if (thread_->Suspend(out_suspend_count)) {
|
||||
return X_STATUS_SUCCESS;
|
||||
} else {
|
||||
return X_STATUS_UNSUCCESSFUL;
|
||||
|
@ -756,7 +642,7 @@ X_STATUS XThread::Suspend(uint32_t* out_suspend_count) {
|
|||
X_STATUS XThread::Delay(uint32_t processor_mode, uint32_t alertable,
|
||||
uint64_t interval) {
|
||||
int64_t timeout_ticks = interval;
|
||||
DWORD timeout_ms;
|
||||
uint32_t timeout_ms;
|
||||
if (timeout_ticks > 0) {
|
||||
// Absolute time, based on January 1, 1601.
|
||||
// TODO(benvanik): convert time to relative time.
|
||||
|
@ -764,24 +650,27 @@ X_STATUS XThread::Delay(uint32_t processor_mode, uint32_t alertable,
|
|||
timeout_ms = 0;
|
||||
} else if (timeout_ticks < 0) {
|
||||
// Relative time.
|
||||
timeout_ms = (DWORD)(-timeout_ticks / 10000); // Ticks -> MS
|
||||
timeout_ms = uint32_t(-timeout_ticks / 10000); // Ticks -> MS
|
||||
} else {
|
||||
timeout_ms = 0;
|
||||
}
|
||||
timeout_ms = Clock::ScaleGuestDurationMillis(timeout_ms);
|
||||
DWORD result = SleepEx(timeout_ms, alertable ? TRUE : FALSE);
|
||||
switch (result) {
|
||||
case 0:
|
||||
return X_STATUS_SUCCESS;
|
||||
case WAIT_IO_COMPLETION:
|
||||
return X_STATUS_USER_APC;
|
||||
default:
|
||||
return X_STATUS_ALERTED;
|
||||
if (alertable) {
|
||||
auto result =
|
||||
xe::threading::AlertableSleep(std::chrono::milliseconds(timeout_ms));
|
||||
switch (result) {
|
||||
default:
|
||||
case xe::threading::SleepResult::kSuccess:
|
||||
return X_STATUS_SUCCESS;
|
||||
case xe::threading::SleepResult::kAlerted:
|
||||
return X_STATUS_USER_APC;
|
||||
}
|
||||
} else {
|
||||
xe::threading::Sleep(std::chrono::milliseconds(timeout_ms));
|
||||
return X_STATUS_SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
void* XThread::GetWaitHandle() { return thread_handle_; }
|
||||
|
||||
XHostThread::XHostThread(KernelState* kernel_state, uint32_t stack_size,
|
||||
uint32_t creation_flags, std::function<int()> host_fn)
|
||||
: XThread(kernel_state, stack_size, 0, 0, 0, creation_flags, false),
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include <mutex>
|
||||
#include <string>
|
||||
|
||||
#include "xenia/base/threading.h"
|
||||
#include "xenia/cpu/thread_state.h"
|
||||
#include "xenia/kernel/xobject.h"
|
||||
#include "xenia/xbox.h"
|
||||
|
@ -133,15 +134,10 @@ class XThread : public XObject {
|
|||
X_STATUS Delay(uint32_t processor_mode, uint32_t alertable,
|
||||
uint64_t interval);
|
||||
|
||||
virtual void* GetWaitHandle();
|
||||
xe::threading::WaitHandle* GetWaitHandle() override { return thread_.get(); }
|
||||
|
||||
protected:
|
||||
X_STATUS PlatformCreate();
|
||||
void PlatformDestroy();
|
||||
X_STATUS PlatformExit(int exit_code);
|
||||
X_STATUS PlatformTerminate(int exit_code);
|
||||
|
||||
static void DeliverAPCs(void* data);
|
||||
void DeliverAPCs();
|
||||
void RundownAPCs();
|
||||
|
||||
struct {
|
||||
|
@ -153,7 +149,7 @@ class XThread : public XObject {
|
|||
} creation_params_;
|
||||
|
||||
uint32_t thread_id_;
|
||||
void* thread_handle_;
|
||||
std::unique_ptr<xe::threading::Thread> thread_;
|
||||
uint32_t scratch_address_;
|
||||
uint32_t scratch_size_;
|
||||
uint32_t tls_address_;
|
||||
|
|
|
@ -16,32 +16,25 @@
|
|||
namespace xe {
|
||||
namespace kernel {
|
||||
|
||||
XTimer::XTimer(KernelState* kernel_state)
|
||||
: XObject(kernel_state, kTypeTimer), native_handle_(NULL) {}
|
||||
XTimer::XTimer(KernelState* kernel_state) : XObject(kernel_state, kTypeTimer) {}
|
||||
|
||||
XTimer::~XTimer() {
|
||||
if (native_handle_) {
|
||||
CloseHandle(native_handle_);
|
||||
}
|
||||
}
|
||||
XTimer::~XTimer() = default;
|
||||
|
||||
void XTimer::Initialize(uint32_t timer_type) {
|
||||
assert_null(native_handle_);
|
||||
assert_false(timer_);
|
||||
|
||||
bool manual_reset = false;
|
||||
switch (timer_type) {
|
||||
case 0: // NotificationTimer
|
||||
manual_reset = true;
|
||||
timer_ = xe::threading::Timer::CreateManualResetTimer();
|
||||
break;
|
||||
case 1: // SynchronizationTimer
|
||||
manual_reset = false;
|
||||
timer_ = xe::threading::Timer::CreateSynchronizationTimer();
|
||||
break;
|
||||
default:
|
||||
assert_always();
|
||||
break;
|
||||
}
|
||||
|
||||
native_handle_ = CreateWaitableTimer(NULL, manual_reset, NULL);
|
||||
}
|
||||
|
||||
X_STATUS XTimer::SetTimer(int64_t due_time, uint32_t period_ms,
|
||||
|
@ -54,34 +47,29 @@ X_STATUS XTimer::SetTimer(int64_t due_time, uint32_t period_ms,
|
|||
// Stash routine for callback.
|
||||
current_routine_ = routine;
|
||||
current_routine_arg_ = routine_arg;
|
||||
if (current_routine_) {
|
||||
// Queue APC to call back routine with (arg, low, high).
|
||||
// TODO(benvanik): APC dispatch.
|
||||
XELOGE("Timer needs APC!");
|
||||
assert_zero(current_routine_);
|
||||
}
|
||||
|
||||
due_time = Clock::ScaleGuestDurationFileTime(due_time);
|
||||
period_ms = Clock::ScaleGuestDurationMillis(period_ms);
|
||||
|
||||
LARGE_INTEGER due_time_li;
|
||||
due_time_li.QuadPart = due_time;
|
||||
BOOL result =
|
||||
SetWaitableTimer(native_handle_, &due_time_li, period_ms,
|
||||
routine ? (PTIMERAPCROUTINE)CompletionRoutine : NULL,
|
||||
this, resume ? TRUE : FALSE);
|
||||
bool result;
|
||||
if (!period_ms) {
|
||||
result = timer_->SetOnce(std::chrono::nanoseconds(due_time * 100));
|
||||
} else {
|
||||
result = timer_->SetRepeating(std::chrono::nanoseconds(due_time * 100),
|
||||
std::chrono::milliseconds(period_ms));
|
||||
}
|
||||
|
||||
return result ? X_STATUS_SUCCESS : X_STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
|
||||
void XTimer::CompletionRoutine(XTimer* timer, DWORD timer_low,
|
||||
DWORD timer_high) {
|
||||
assert_true(timer->current_routine_);
|
||||
|
||||
// Queue APC to call back routine with (arg, low, high).
|
||||
// TODO(benvanik): APC dispatch.
|
||||
XELOGE("Timer needs APC!");
|
||||
|
||||
DebugBreak();
|
||||
}
|
||||
|
||||
X_STATUS XTimer::Cancel() {
|
||||
return CancelWaitableTimer(native_handle_) == 0 ? X_STATUS_SUCCESS
|
||||
: X_STATUS_UNSUCCESSFUL;
|
||||
return timer_->Cancel() ? X_STATUS_SUCCESS : X_STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
|
||||
} // namespace kernel
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
#ifndef XENIA_KERNEL_XBOXKRNL_XTIMER_H_
|
||||
#define XENIA_KERNEL_XBOXKRNL_XTIMER_H_
|
||||
|
||||
#include "xenia/base/platform_win.h"
|
||||
#include "xenia/base/threading.h"
|
||||
#include "xenia/kernel/xobject.h"
|
||||
#include "xenia/xbox.h"
|
||||
|
||||
|
@ -20,7 +20,7 @@ namespace kernel {
|
|||
class XTimer : public XObject {
|
||||
public:
|
||||
XTimer(KernelState* kernel_state);
|
||||
virtual ~XTimer();
|
||||
~XTimer() override;
|
||||
|
||||
void Initialize(uint32_t timer_type);
|
||||
|
||||
|
@ -29,16 +29,13 @@ class XTimer : public XObject {
|
|||
uint32_t routine_arg, bool resume);
|
||||
X_STATUS Cancel();
|
||||
|
||||
virtual void* GetWaitHandle() { return native_handle_; }
|
||||
xe::threading::WaitHandle* GetWaitHandle() override { return timer_.get(); }
|
||||
|
||||
private:
|
||||
HANDLE native_handle_;
|
||||
std::unique_ptr<xe::threading::Timer> timer_;
|
||||
|
||||
uint32_t current_routine_;
|
||||
uint32_t current_routine_arg_;
|
||||
|
||||
static void CompletionRoutine(XTimer* timer, DWORD timer_low,
|
||||
DWORD timer_high);
|
||||
};
|
||||
|
||||
} // namespace kernel
|
||||
|
|
|
@ -117,28 +117,30 @@ uint32_t XObject::TimeoutTicksToMs(int64_t timeout_ticks) {
|
|||
|
||||
X_STATUS XObject::Wait(uint32_t wait_reason, uint32_t processor_mode,
|
||||
uint32_t alertable, uint64_t* opt_timeout) {
|
||||
void* wait_handle = GetWaitHandle();
|
||||
auto wait_handle = GetWaitHandle();
|
||||
if (!wait_handle) {
|
||||
// Object doesn't support waiting.
|
||||
return X_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
DWORD timeout_ms = opt_timeout ? TimeoutTicksToMs(*opt_timeout) : INFINITE;
|
||||
timeout_ms = Clock::ScaleGuestDurationMillis(timeout_ms);
|
||||
auto timeout_ms =
|
||||
opt_timeout ? std::chrono::milliseconds(Clock::ScaleGuestDurationMillis(
|
||||
TimeoutTicksToMs(*opt_timeout)))
|
||||
: std::chrono::milliseconds::max();
|
||||
|
||||
DWORD result = WaitForSingleObjectEx(wait_handle, timeout_ms, alertable);
|
||||
auto result = wait_handle->Wait(alertable ? true : false, timeout_ms);
|
||||
switch (result) {
|
||||
case WAIT_OBJECT_0:
|
||||
case xe::threading::WaitResult::kSuccess:
|
||||
return X_STATUS_SUCCESS;
|
||||
case WAIT_IO_COMPLETION:
|
||||
case xe::threading::WaitResult::kUserCallback:
|
||||
// Or X_STATUS_ALERTED?
|
||||
return X_STATUS_USER_APC;
|
||||
case WAIT_TIMEOUT:
|
||||
case xe::threading::WaitResult::kTimeout:
|
||||
YieldProcessor();
|
||||
return X_STATUS_TIMEOUT;
|
||||
default:
|
||||
case WAIT_FAILED:
|
||||
case WAIT_ABANDONED:
|
||||
case xe::threading::WaitResult::kAbandoned:
|
||||
case xe::threading::WaitResult::kFailed:
|
||||
return X_STATUS_ABANDONED_WAIT_0;
|
||||
}
|
||||
}
|
||||
|
@ -146,33 +148,81 @@ X_STATUS XObject::Wait(uint32_t wait_reason, uint32_t processor_mode,
|
|||
X_STATUS XObject::SignalAndWait(XObject* signal_object, XObject* wait_object,
|
||||
uint32_t wait_reason, uint32_t processor_mode,
|
||||
uint32_t alertable, uint64_t* opt_timeout) {
|
||||
DWORD timeout_ms = opt_timeout ? TimeoutTicksToMs(*opt_timeout) : INFINITE;
|
||||
timeout_ms = Clock::ScaleGuestDurationMillis(timeout_ms);
|
||||
auto timeout_ms =
|
||||
opt_timeout ? std::chrono::milliseconds(Clock::ScaleGuestDurationMillis(
|
||||
TimeoutTicksToMs(*opt_timeout)))
|
||||
: std::chrono::milliseconds::max();
|
||||
|
||||
DWORD result = SignalObjectAndWait(signal_object->GetWaitHandle(),
|
||||
wait_object->GetWaitHandle(), timeout_ms,
|
||||
alertable ? TRUE : FALSE);
|
||||
|
||||
return result;
|
||||
auto result = xe::threading::WaitHandle::SignalAndWait(
|
||||
signal_object->GetWaitHandle(), wait_object->GetWaitHandle(),
|
||||
alertable ? true : false, timeout_ms);
|
||||
switch (result) {
|
||||
case xe::threading::WaitResult::kSuccess:
|
||||
return X_STATUS_SUCCESS;
|
||||
case xe::threading::WaitResult::kUserCallback:
|
||||
// Or X_STATUS_ALERTED?
|
||||
return X_STATUS_USER_APC;
|
||||
case xe::threading::WaitResult::kTimeout:
|
||||
YieldProcessor();
|
||||
return X_STATUS_TIMEOUT;
|
||||
default:
|
||||
case xe::threading::WaitResult::kAbandoned:
|
||||
case xe::threading::WaitResult::kFailed:
|
||||
return X_STATUS_ABANDONED_WAIT_0;
|
||||
}
|
||||
}
|
||||
|
||||
X_STATUS XObject::WaitMultiple(uint32_t count, XObject** objects,
|
||||
uint32_t wait_type, uint32_t wait_reason,
|
||||
uint32_t processor_mode, uint32_t alertable,
|
||||
uint64_t* opt_timeout) {
|
||||
HANDLE* wait_handles = (HANDLE*)alloca(sizeof(HANDLE) * count);
|
||||
for (uint32_t n = 0; n < count; n++) {
|
||||
wait_handles[n] = objects[n]->GetWaitHandle();
|
||||
assert_not_null(wait_handles[n]);
|
||||
std::vector<xe::threading::WaitHandle*> wait_handles(count);
|
||||
for (size_t i = 0; i < count; ++i) {
|
||||
wait_handles[i] = objects[i]->GetWaitHandle();
|
||||
assert_not_null(wait_handles[i]);
|
||||
}
|
||||
|
||||
DWORD timeout_ms = opt_timeout ? TimeoutTicksToMs(*opt_timeout) : INFINITE;
|
||||
timeout_ms = Clock::ScaleGuestDurationMillis(timeout_ms);
|
||||
auto timeout_ms =
|
||||
opt_timeout ? std::chrono::milliseconds(Clock::ScaleGuestDurationMillis(
|
||||
TimeoutTicksToMs(*opt_timeout)))
|
||||
: std::chrono::milliseconds::max();
|
||||
|
||||
DWORD result = WaitForMultipleObjectsEx(
|
||||
count, wait_handles, wait_type ? FALSE : TRUE, timeout_ms, alertable);
|
||||
|
||||
return result;
|
||||
if (wait_type) {
|
||||
auto result = xe::threading::WaitHandle::WaitAny(
|
||||
std::move(wait_handles), alertable ? true : false, timeout_ms);
|
||||
switch (result.first) {
|
||||
case xe::threading::WaitResult::kSuccess:
|
||||
return X_STATUS(result.second);
|
||||
case xe::threading::WaitResult::kUserCallback:
|
||||
// Or X_STATUS_ALERTED?
|
||||
return X_STATUS_USER_APC;
|
||||
case xe::threading::WaitResult::kTimeout:
|
||||
YieldProcessor();
|
||||
return X_STATUS_TIMEOUT;
|
||||
default:
|
||||
case xe::threading::WaitResult::kAbandoned:
|
||||
return X_STATUS(X_STATUS_ABANDONED_WAIT_0 + result.second);
|
||||
case xe::threading::WaitResult::kFailed:
|
||||
return X_STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
} else {
|
||||
auto result = xe::threading::WaitHandle::WaitAll(
|
||||
std::move(wait_handles), alertable ? true : false, timeout_ms);
|
||||
switch (result) {
|
||||
case xe::threading::WaitResult::kSuccess:
|
||||
return X_STATUS_SUCCESS;
|
||||
case xe::threading::WaitResult::kUserCallback:
|
||||
// Or X_STATUS_ALERTED?
|
||||
return X_STATUS_USER_APC;
|
||||
case xe::threading::WaitResult::kTimeout:
|
||||
YieldProcessor();
|
||||
return X_STATUS_TIMEOUT;
|
||||
default:
|
||||
case xe::threading::WaitResult::kAbandoned:
|
||||
case xe::threading::WaitResult::kFailed:
|
||||
return X_STATUS_ABANDONED_WAIT_0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t* XObject::CreateNative(uint32_t size) {
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
|
||||
#include <atomic>
|
||||
|
||||
#include "xenia/base/threading.h"
|
||||
#include "xenia/xbox.h"
|
||||
|
||||
namespace xe {
|
||||
|
@ -148,7 +149,7 @@ class XObject {
|
|||
GetNativeObject(kernel_state, native_ptr, as_type).release()));
|
||||
}
|
||||
|
||||
virtual void* GetWaitHandle() { return 0; }
|
||||
virtual xe::threading::WaitHandle* GetWaitHandle() { return nullptr; }
|
||||
|
||||
protected:
|
||||
// Creates the kernel object for guest code to use. Typically not needed.
|
||||
|
|
Loading…
Reference in New Issue