Moving threads to XHostThread and making shutdown not crash.

This commit is contained in:
Ben Vanik 2015-05-19 22:20:49 -07:00
parent 7a82ad839a
commit f88bf33b4f
22 changed files with 175 additions and 138 deletions

View File

@ -14,8 +14,8 @@
#include "xenia/base/math.h"
#include "xenia/cpu/processor.h"
#include "xenia/cpu/thread_state.h"
#include "xenia/kernel/objects/xthread.h"
#include "xenia/emulator.h"
#include "xenia/kernel/objects/xthread.h"
#include "xenia/profiling.h"
// As with normal Microsoft, there are like twelve different ways to access
@ -48,7 +48,6 @@ namespace xe {
namespace apu {
using namespace xe::cpu;
using namespace xe::kernel;
// Size of a hardware XMA context.
const uint32_t kXmaContextSize = 64;
@ -56,7 +55,7 @@ const uint32_t kXmaContextSize = 64;
const uint32_t kXmaContextCount = 320;
AudioSystem::AudioSystem(Emulator* emulator)
: emulator_(emulator), memory_(emulator->memory()), running_(false) {
: emulator_(emulator), memory_(emulator->memory()), worker_running_(false) {
memset(clients_, 0, sizeof(clients_));
for (size_t i = 0; i < maximum_client_count_; ++i) {
unused_clients_.push(i);
@ -91,22 +90,18 @@ X_STATUS AudioSystem::Setup() {
}
registers_.next_context = 1;
// Setup our worker thread
std::function<int()> thread_fn = [this]() {
this->ThreadStart();
worker_running_ = true;
worker_thread_ = new kernel::XHostThread(emulator()->kernel_state(),
128 * 1024, 0, [this]() {
this->WorkerThreadMain();
return 0;
};
running_ = true;
thread_ = std::make_unique<XHostThread>(emulator()->kernel_state(),
128 * 1024, 0, thread_fn);
thread_->Create();
});
worker_thread_->Create();
return X_STATUS_SUCCESS;
}
void AudioSystem::ThreadStart() {
void AudioSystem::WorkerThreadMain() {
xe::threading::set_name("Audio Worker");
// Initialize driver and ringbuffer.
@ -115,7 +110,7 @@ void AudioSystem::ThreadStart() {
auto processor = emulator_->processor();
// Main run loop.
while (running_) {
while (worker_running_) {
auto result =
WaitForMultipleObjectsEx(DWORD(xe::countof(client_wait_handles_)),
client_wait_handles_, FALSE, INFINITE, FALSE);
@ -135,8 +130,8 @@ void AudioSystem::ThreadStart() {
lock_.unlock();
if (client_callback) {
uint64_t args[] = {client_callback_arg};
processor->Execute(thread_->thread_state(), client_callback, args,
xe::countof(args));
processor->Execute(worker_thread_->thread_state(), client_callback,
args, xe::countof(args));
}
pumped++;
index++;
@ -145,7 +140,7 @@ void AudioSystem::ThreadStart() {
WAIT_OBJECT_0);
}
if (!running_) {
if (!worker_running_) {
break;
}
@ -154,7 +149,7 @@ void AudioSystem::ThreadStart() {
Sleep(500);
}
}
running_ = false;
worker_running_ = false;
// TODO(benvanik): call module API to kill?
}
@ -162,9 +157,10 @@ void AudioSystem::ThreadStart() {
void AudioSystem::Initialize() {}
void AudioSystem::Shutdown() {
running_ = false;
ResetEvent(client_wait_handles_[maximum_client_count_]);
thread_->Wait(0, 0, 0, NULL);
worker_running_ = false;
SetEvent(client_wait_handles_[maximum_client_count_]);
worker_thread_->Wait(0, 0, 0, nullptr);
worker_thread_->Release();
memory()->SystemHeapFree(registers_.xma_context_array_ptr);
}

View File

@ -13,15 +13,17 @@
#include <atomic>
#include <mutex>
#include <queue>
#include <thread>
#include "xenia/emulator.h"
#include "xenia/xbox.h"
namespace xe {
namespace kernel {
class XHostThread;
} // namespace kernel
} // namespace xe
namespace kernel { class XHostThread; }
namespace xe {
namespace apu {
class AudioDriver;
@ -59,7 +61,7 @@ class AudioSystem {
virtual void Initialize();
private:
void ThreadStart();
void WorkerThreadMain();
static uint64_t MMIOReadRegisterThunk(AudioSystem* as, uint32_t addr) {
return as->ReadRegister(addr);
@ -76,8 +78,8 @@ class AudioSystem {
Memory* memory_;
cpu::Processor* processor_;
std::unique_ptr<kernel::XHostThread> thread_;
std::atomic<bool> running_;
std::atomic<bool> worker_running_;
kernel::XHostThread* worker_thread_;
std::mutex lock_;

View File

@ -28,7 +28,6 @@ using namespace xe::apu;
using namespace xe::cpu;
using namespace xe::gpu;
using namespace xe::hid;
using namespace xe::kernel;
using namespace xe::kernel::fs;
using namespace xe::ui;
@ -38,17 +37,14 @@ Emulator::Emulator(const std::wstring& command_line)
Emulator::~Emulator() {
// Note that we delete things in the reverse order they were initialized.
xam_.reset();
xboxkrnl_.reset();
kernel_state_.reset();
file_system_.reset();
input_system_.reset();
// Give the systems time to shutdown before we delete them.
graphics_system_->Shutdown();
audio_system_->Shutdown();
kernel_state_.reset();
file_system_.reset();
input_system_.reset();
graphics_system_.reset();
audio_system_.reset();
@ -95,7 +91,7 @@ X_STATUS Emulator::Setup() {
}
// Initialize the GPU.
graphics_system_ = std::move(xe::gpu::Create());
graphics_system_ = std::move(xe::gpu::Create(this));
if (!graphics_system_) {
return X_STATUS_NOT_IMPLEMENTED;
}
@ -106,15 +102,6 @@ X_STATUS Emulator::Setup() {
return X_STATUS_NOT_IMPLEMENTED;
}
// Setup the core components.
if (!processor_->Setup()) {
return result;
}
result = graphics_system_->Setup(processor_.get(), main_window_->loop(),
main_window_.get());
if (result) {
return result;
}
result = input_system_->Setup();
if (result) {
return result;
@ -124,7 +111,17 @@ X_STATUS Emulator::Setup() {
file_system_ = std::make_unique<FileSystem>();
// Shared kernel state.
kernel_state_ = std::make_unique<KernelState>(this);
kernel_state_ = std::make_unique<kernel::KernelState>(this);
// Setup the core components.
if (!processor_->Setup()) {
return result;
}
result = graphics_system_->Setup(processor_.get(), main_window_->loop(),
main_window_.get());
if (result) {
return result;
}
result = audio_system_->Setup();
if (result) {
@ -132,8 +129,8 @@ X_STATUS Emulator::Setup() {
}
// HLE kernel modules.
xboxkrnl_ = std::make_unique<XboxkrnlModule>(this, kernel_state_.get());
xam_ = std::make_unique<XamModule>(this, kernel_state_.get());
kernel_state_->LoadKernelModule<kernel::XboxkrnlModule>();
kernel_state_->LoadKernelModule<kernel::XamModule>();
return result;
}
@ -192,7 +189,15 @@ X_STATUS Emulator::LaunchSTFSTitle(const std::wstring& path) {
X_STATUS Emulator::CompleteLaunch(const std::wstring& path,
const std::string& module_path) {
return xboxkrnl_->LaunchModule(module_path.c_str());
auto xboxkrnl = static_cast<kernel::XboxkrnlModule*>(
kernel_state_->GetModule("xboxkrnl.exe"));
int result = xboxkrnl->LaunchModule(module_path.c_str());
xboxkrnl->Release();
if (result == 0) {
return X_STATUS_SUCCESS;
} else {
return X_STATUS_UNSUCCESSFUL;
}
}
} // namespace xe

View File

@ -32,10 +32,6 @@ class GraphicsSystem;
namespace hid {
class InputSystem;
} // namespace hid
namespace kernel {
class XamModule;
class XboxkrnlModule;
} // namespace kernel
namespace ui {
class MainWindow;
} // namespace ui
@ -68,9 +64,6 @@ class Emulator {
kernel::KernelState* kernel_state() const { return kernel_state_.get(); }
kernel::XboxkrnlModule* xboxkrnl() const { return xboxkrnl_.get(); }
kernel::XamModule* xam() const { return xam_.get(); }
X_STATUS Setup();
// TODO(benvanik): raw binary.
@ -97,8 +90,6 @@ class Emulator {
std::unique_ptr<kernel::fs::FileSystem> file_system_;
std::unique_ptr<kernel::KernelState> kernel_state_;
std::unique_ptr<kernel::XamModule> xam_;
std::unique_ptr<kernel::XboxkrnlModule> xboxkrnl_;
};
} // namespace xe

View File

@ -19,6 +19,8 @@
#include "xenia/gpu/sampler_info.h"
#include "xenia/gpu/texture_info.h"
#include "xenia/gpu/xenos.h"
#include "xenia/emulator.h"
#include "xenia/kernel/objects/xthread.h"
#include "xenia/profiling.h"
#include "third_party/xxhash/xxhash.h"
@ -99,12 +101,12 @@ bool CommandProcessor::Initialize(std::unique_ptr<GLContext> context) {
context_ = std::move(context);
worker_running_ = true;
worker_thread_ = std::thread([this]() {
xe::threading::set_name("GL4 Worker");
xe::Profiler::ThreadEnter("GL4 Worker");
WorkerMain();
xe::Profiler::ThreadExit();
});
worker_thread_ = new kernel::XHostThread(
graphics_system_->emulator()->kernel_state(), 128 * 1024, 0, [this]() {
WorkerThreadMain();
return 0;
});
worker_thread_->Create();
return true;
}
@ -114,7 +116,8 @@ void CommandProcessor::Shutdown() {
worker_running_ = false;
SetEvent(write_ptr_index_event_);
worker_thread_.join();
worker_thread_->Wait(0, 0, 0, nullptr);
worker_thread_->Release();
all_pipelines_.clear();
all_shaders_.clear();
@ -160,14 +163,16 @@ void CommandProcessor::EndTracing() {
void CommandProcessor::CallInThread(std::function<void()> fn) {
if (pending_fns_.empty() &&
worker_thread_.get_id() == std::this_thread::get_id()) {
worker_thread_ == kernel::XThread::GetCurrentThread()) {
fn();
} else {
pending_fns_.push(std::move(fn));
}
}
void CommandProcessor::WorkerMain() {
void CommandProcessor::WorkerThreadMain() {
xe::threading::set_name("GL4 Worker");
context_->MakeCurrent();
if (!SetupGL()) {
XEFATAL("Unable to setup command processor GL state");

View File

@ -14,7 +14,6 @@
#include <functional>
#include <memory>
#include <queue>
#include <thread>
#include <unordered_map>
#include <vector>
@ -28,6 +27,12 @@
#include "xenia/gpu/xenos.h"
#include "xenia/memory.h"
namespace xe {
namespace kernel {
class XHostThread;
} // namespace kernel
} // namespace xe
namespace xe {
namespace gpu {
namespace gl4 {
@ -134,7 +139,7 @@ class CommandProcessor {
} handles;
};
void WorkerMain();
void WorkerThreadMain();
bool SetupGL();
void ShutdownGL();
GLuint CreateGeometryProgram(const std::string& source);
@ -226,8 +231,9 @@ class CommandProcessor {
TraceState trace_state_;
std::wstring trace_frame_path_;
std::thread worker_thread_;
std::atomic<bool> worker_running_;
kernel::XHostThread* worker_thread_;
std::unique_ptr<GLContext> context_;
SwapHandler swap_handler_;
std::queue<std::function<void()>> pending_fns_;

View File

@ -47,9 +47,9 @@ void InitializeIfNeeded() {
void CleanupOnShutdown() {}
std::unique_ptr<GraphicsSystem> Create() {
std::unique_ptr<GraphicsSystem> Create(Emulator* emulator) {
InitializeIfNeeded();
return std::make_unique<GL4GraphicsSystem>();
return std::make_unique<GL4GraphicsSystem>(emulator);
}
} // namespace gl4

View File

@ -18,7 +18,7 @@ namespace xe {
namespace gpu {
namespace gl4 {
std::unique_ptr<GraphicsSystem> Create();
std::unique_ptr<GraphicsSystem> Create(Emulator* emulator);
} // namespace gl4
} // namespace gpu

View File

@ -23,8 +23,8 @@ namespace gl4 {
extern "C" GLEWContext* glewGetContext();
GL4GraphicsSystem::GL4GraphicsSystem()
: GraphicsSystem(), timer_queue_(nullptr), vsync_timer_(nullptr) {}
GL4GraphicsSystem::GL4GraphicsSystem(Emulator* emulator)
: GraphicsSystem(emulator), timer_queue_(nullptr), vsync_timer_(nullptr) {}
GL4GraphicsSystem::~GL4GraphicsSystem() = default;

View File

@ -23,7 +23,7 @@ namespace gl4 {
class GL4GraphicsSystem : public GraphicsSystem {
public:
GL4GraphicsSystem();
GL4GraphicsSystem(Emulator* emulator);
~GL4GraphicsSystem() override;
X_STATUS Setup(cpu::Processor* processor, ui::PlatformLoop* target_loop,

View File

@ -27,14 +27,14 @@ DEFINE_bool(vsync, true, "Enable VSYNC.");
namespace xe {
namespace gpu {
std::unique_ptr<GraphicsSystem> Create() {
std::unique_ptr<GraphicsSystem> Create(Emulator* emulator) {
if (FLAGS_gpu.compare("gl4") == 0) {
return xe::gpu::gl4::Create();
return xe::gpu::gl4::Create(emulator);
} else {
// Create best available.
std::unique_ptr<GraphicsSystem> best;
best = xe::gpu::gl4::Create();
best = xe::gpu::gl4::Create(emulator);
if (best) {
return best;
}

View File

@ -21,9 +21,7 @@ class Emulator;
namespace xe {
namespace gpu {
std::unique_ptr<GraphicsSystem> Create();
std::unique_ptr<GraphicsSystem> CreateGL4();
std::unique_ptr<GraphicsSystem> Create(Emulator* emulator);
} // namespace gpu
} // namespace xe

View File

@ -17,8 +17,9 @@
namespace xe {
namespace gpu {
GraphicsSystem::GraphicsSystem()
: memory_(nullptr),
GraphicsSystem::GraphicsSystem(Emulator* emulator)
: emulator_(emulator),
memory_(nullptr),
processor_(nullptr),
target_loop_(nullptr),
target_window_(nullptr),

View File

@ -25,6 +25,7 @@ class GraphicsSystem {
public:
virtual ~GraphicsSystem();
Emulator* emulator() const { return emulator_; }
Memory* memory() const { return memory_; }
cpu::Processor* processor() const { return processor_; }
@ -54,8 +55,9 @@ class GraphicsSystem {
virtual void ClearCaches() {}
protected:
GraphicsSystem();
GraphicsSystem(Emulator* emulator);
Emulator* emulator_;
Memory* memory_;
cpu::Processor* processor_;
ui::PlatformLoop* target_loop_;

View File

@ -70,6 +70,15 @@ KernelState::KernelState(Emulator* emulator)
KernelState::~KernelState() {
SetExecutableModule(nullptr);
for (auto user_module : user_modules_) {
user_module->Release();
}
user_modules_.clear();
for (auto kernel_module : kernel_modules_) {
kernel_module->Release();
}
kernel_modules_.clear();
// Delete all objects.
delete object_table_;
@ -80,10 +89,6 @@ KernelState::~KernelState() {
assert_true(shared_kernel_state_ == this);
shared_kernel_state_ = nullptr;
for (XUserModule* mod : user_modules_) {
mod->Release();
}
}
KernelState* KernelState::shared() { return shared_kernel_state_; }
@ -99,14 +104,15 @@ void KernelState::UnregisterModule(XModule* module) {}
bool KernelState::IsKernelModule(const char* name) {
if (!name) {
// executing module isn't a kernel module
// Executing module isn't a kernel module.
return false;
} else if (strcasecmp(name, "xam.xex") == 0) {
return true;
} else if (strcasecmp(name, "xboxkrnl.exe") == 0) {
return true;
}
std::lock_guard<std::recursive_mutex> lock(object_mutex_);
for (auto kernel_module : kernel_modules_) {
if (kernel_module->Matches(name)) {
return true;
}
}
return false;
}
@ -115,32 +121,24 @@ XModule* KernelState::GetModule(const char* name) {
// NULL name = self.
// TODO(benvanik): lookup module from caller address.
return GetExecutableModule();
} else if (strcasecmp(name, "xam.xex") == 0) {
auto module = emulator_->xam();
module->Retain();
return module;
} else if (strcasecmp(name, "xboxkrnl.exe") == 0) {
auto module = emulator_->xboxkrnl();
module->Retain();
return module;
} else if (strcasecmp(name, "kernel32.dll") == 0) {
// Some games request this, for some reason. wtf.
return nullptr;
} else {
std::lock_guard<std::recursive_mutex> lock(object_mutex_);
for (XUserModule* module : user_modules_) {
if ((strcasecmp(xe::find_name_from_path(module->path()).c_str(), name) ==
0) ||
(strcasecmp(module->name().c_str(), name) == 0) ||
(strcasecmp(module->path().c_str(), name) == 0)) {
module->Retain();
return module;
}
}
return nullptr;
}
std::lock_guard<std::recursive_mutex> lock(object_mutex_);
for (auto kernel_module : kernel_modules_) {
if (kernel_module->Matches(name)) {
kernel_module->Retain();
return kernel_module;
}
}
for (auto user_module : user_modules_) {
if (user_module->Matches(name)) {
user_module->Retain();
return user_module;
}
}
return nullptr;
}
XUserModule* KernelState::GetExecutableModule() {
@ -166,6 +164,11 @@ void KernelState::SetExecutableModule(XUserModule* module) {
}
}
void KernelState::LoadKernelModule(XKernelModule* kernel_module) {
std::lock_guard<std::recursive_mutex> lock(object_mutex_);
kernel_modules_.push_back(kernel_module);
}
XUserModule* KernelState::LoadUserModule(const char* raw_name) {
// Some games try to load relative to launch module, others specify full path.
std::string name = xe::find_name_from_path(raw_name);

View File

@ -37,6 +37,7 @@ namespace xe {
namespace kernel {
class Dispatcher;
class XKernelModule;
class XModule;
class XNotifyListener;
class XThread;
@ -74,7 +75,13 @@ class KernelState {
XModule* GetModule(const char* name);
XUserModule* GetExecutableModule();
void SetExecutableModule(XUserModule* module);
XUserModule* LoadUserModule(const char *name);
template <typename T>
XKernelModule* LoadKernelModule() {
auto kernel_module = std::make_unique<T>(emulator_, this);
LoadKernelModule(kernel_module.get());
return kernel_module.release();
}
XUserModule* LoadUserModule(const char* name);
void RegisterThread(XThread* thread);
void UnregisterThread(XThread* thread);
@ -94,6 +101,8 @@ class KernelState {
uint32_t extended_error, uint32_t length);
private:
void LoadKernelModule(XKernelModule* kernel_module);
Emulator* emulator_;
Memory* memory_;
cpu::Processor* processor_;
@ -113,7 +122,7 @@ class KernelState {
uint32_t process_type_;
XUserModule* executable_module_;
std::vector<XKernelModule*> kernel_modules_;
std::vector<XUserModule*> user_modules_;
friend class XObject;

View File

@ -9,6 +9,8 @@
#include "xenia/kernel/objects/xmodule.h"
#include "xenia/base/string.h"
namespace xe {
namespace kernel {
@ -31,6 +33,19 @@ XModule::XModule(KernelState* kernel_state, const std::string& path)
XModule::~XModule() { kernel_state_->UnregisterModule(this); }
bool XModule::Matches(const std::string& name) const {
if (strcasecmp(xe::find_name_from_path(path_).c_str(), name.c_str()) == 0) {
return true;
}
if (strcasecmp(name_.c_str(), name.c_str()) == 0) {
return true;
}
if (strcasecmp(path_.c_str(), name.c_str()) == 0) {
return true;
}
return false;
}
void XModule::OnLoad() { kernel_state_->RegisterModule(this); }
X_STATUS XModule::GetSection(const char* name, uint32_t* out_section_data,

View File

@ -25,6 +25,7 @@ class XModule : public XObject {
const std::string& path() const { return path_; }
const std::string& name() const { return name_; }
bool Matches(const std::string& name) const;
virtual uint32_t GetProcAddressByOrdinal(uint16_t ordinal) = 0;
virtual uint32_t GetProcAddressByName(const char* name) = 0;

View File

@ -156,7 +156,7 @@ X_STATUS XThread::Create() {
scratch_address_ = memory()->SystemHeapAlloc(scratch_size_);
// Allocate TLS block.
uint32_t tls_size = 32; // Default 32 (is this OK?)
uint32_t tls_size = 32; // Default 32 (is this OK?)
if (module && module->xex_header()) {
const xe_xex2_header_t* header = module->xex_header();
tls_size = header->tls_info.slot_count * header->tls_info.data_size;
@ -194,15 +194,15 @@ X_STATUS XThread::Create() {
thread_state_->stack_base());
uint8_t* pcr = memory()->TranslateVirtual(pcr_address_);
std::memset(pcr, 0x0, 0x2D8 + 0xAB0); // Zero the PCR
std::memset(pcr, 0x0, 0x2D8 + 0xAB0); // Zero the PCR
xe::store_and_swap<uint32_t>(pcr + 0x000, tls_address_);
xe::store_and_swap<uint32_t>(pcr + 0x030, pcr_address_);
xe::store_and_swap<uint32_t>(pcr + 0x070, thread_state_->stack_address() +
thread_state_->stack_size());
xe::store_and_swap<uint32_t>(pcr + 0x074, thread_state_->stack_address());
xe::store_and_swap<uint32_t>(pcr + 0x100, thread_state_address_);
xe::store_and_swap<uint8_t> (pcr + 0x10C, 1); // Current CPU(?)
xe::store_and_swap<uint32_t>(pcr + 0x150, 0); // DPC active bool?
xe::store_and_swap<uint8_t>(pcr + 0x10C, 1); // Current CPU(?)
xe::store_and_swap<uint32_t>(pcr + 0x150, 0); // DPC active bool?
// Setup the thread state block (last error/etc).
uint8_t* p = memory()->TranslateVirtual(thread_state_address_);
@ -622,15 +622,14 @@ X_STATUS XThread::Delay(uint32_t processor_mode, uint32_t alertable,
void* XThread::GetWaitHandle() { return event_->GetWaitHandle(); }
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),
host_fn_(host_fn) {
}
uint32_t creation_flags, std::function<int()> host_fn)
: XThread(kernel_state, stack_size, 0, 0, 0, creation_flags),
host_fn_(host_fn) {}
void XHostThread::Execute() {
XELOGKERNEL("XThread::Execute thid %d (handle=%.8X, '%s', native=%.8X, <host>)",
thread_id_, handle(), name_.c_str(),
xe::threading::current_thread_id());
XELOGKERNEL(
"XThread::Execute thid %d (handle=%.8X, '%s', native=%.8X, <host>)",
thread_id_, handle(), name_.c_str(), xe::threading::current_thread_id());
// Let the kernel know we are starting.
kernel_state()->OnThreadExecute(this);

View File

@ -354,7 +354,7 @@ void XUserModule::Dump() {
if (kernel_state_->IsKernelModule(library->name)) {
KernelExport* kernel_export =
export_resolver->GetExportByOrdinal(library->name, info->ordinal);
export_resolver->GetExportByOrdinal(library->name, info->ordinal);
if (kernel_export) {
known_count++;
if (kernel_export->is_implemented) {
@ -371,7 +371,7 @@ void XUserModule::Dump() {
XModule* module = kernel_state_->GetModule(library->name);
if (module) {
uint32_t export_addr =
module->GetProcAddressByOrdinal(info->ordinal);
module->GetProcAddressByOrdinal(info->ordinal);
if (export_addr) {
impl_count++;
known_count++;
@ -400,10 +400,10 @@ void XUserModule::Dump() {
const char* name = "UNKNOWN";
bool implemented = false;
KernelExport* kernel_export;
KernelExport* kernel_export = nullptr;
if (kernel_state_->IsKernelModule(library->name)) {
kernel_export =
export_resolver->GetExportByOrdinal(library->name, info->ordinal);
export_resolver->GetExportByOrdinal(library->name, info->ordinal);
if (kernel_export) {
name = kernel_export->name;
implemented = kernel_export->is_implemented;

View File

@ -17,6 +17,7 @@ Win32Control::Win32Control(uint32_t flags) : Control(flags), hwnd_(nullptr) {}
Win32Control::~Win32Control() {
if (hwnd_) {
SetWindowLongPtr(hwnd_, GWLP_USERDATA, 0);
CloseWindow(hwnd_);
hwnd_ = nullptr;
}

View File

@ -29,7 +29,7 @@ class PostedFn {
Win32Loop::Win32Loop() : thread_id_(0) {
xe::threading::Fence init_fence;
thread_ = std::thread([&]() {
thread_ = std::thread([&init_fence, this]() {
xe::threading::set_name("Win32 Loop");
thread_id_ = GetCurrentThreadId();
@ -46,7 +46,10 @@ Win32Loop::Win32Loop() : thread_id_(0) {
init_fence.Wait();
}
Win32Loop::~Win32Loop() = default;
Win32Loop::~Win32Loop() {
Quit();
thread_.join();
}
void Win32Loop::ThreadMain() {
MSG msg;