Switching over kernel objects to the platform-agnostic APIs.
Possibly some regressions here.
This commit is contained in:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user