Fixed a deadlock caused by the UI thread dropping important messages

This commit is contained in:
Dr. Chat 2016-08-03 18:00:32 -05:00
parent a7e4bc8f49
commit 83f7cc27d1
2 changed files with 39 additions and 34 deletions

View File

@ -14,18 +14,6 @@
namespace xe { namespace xe {
namespace ui { namespace ui {
const DWORD kWmWin32LoopPost = WM_APP + 0x100;
const DWORD kWmWin32LoopQuit = WM_APP + 0x101;
class PostedFn {
public:
explicit PostedFn(std::function<void()> fn) : fn_(std::move(fn)) {}
void Call() { fn_(); }
private:
std::function<void()> fn_;
};
std::unique_ptr<Loop> Loop::Create() { return std::make_unique<Win32Loop>(); } std::unique_ptr<Loop> Loop::Create() { return std::make_unique<Win32Loop>(); }
Win32Loop::Win32Loop() : thread_id_(0) { Win32Loop::Win32Loop() : thread_id_(0) {
@ -64,22 +52,20 @@ Win32Loop::~Win32Loop() {
void Win32Loop::ThreadMain() { void Win32Loop::ThreadMain() {
MSG msg; MSG msg;
while (GetMessage(&msg, nullptr, 0, 0)) { while (!should_exit_) {
TranslateMessage(&msg); DWORD result =
DispatchMessage(&msg); MsgWaitForMultipleObjectsEx(0, nullptr, INFINITE, QS_ALLEVENTS, 0);
switch (msg.message) {
case kWmWin32LoopPost: if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
if (msg.wParam == reinterpret_cast<WPARAM>(this)) { TranslateMessage(&msg);
auto posted_fn = reinterpret_cast<PostedFn*>(msg.lParam); DispatchMessage(&msg);
posted_fn->Call(); }
delete posted_fn;
} // Process queued functions.
break; std::lock_guard<std::mutex> lock(posted_functions_mutex_);
case kWmWin32LoopQuit: for (auto it = posted_functions_.begin(); it != posted_functions_.end();) {
if (msg.wParam == reinterpret_cast<WPARAM>(this)) { (*it).Call();
return; it = posted_functions_.erase(it);
}
break;
} }
} }
@ -93,10 +79,14 @@ bool Win32Loop::is_on_loop_thread() {
void Win32Loop::Post(std::function<void()> fn) { void Win32Loop::Post(std::function<void()> fn) {
assert_true(thread_id_ != 0); assert_true(thread_id_ != 0);
if (!PostThreadMessage( {
thread_id_, kWmWin32LoopPost, reinterpret_cast<WPARAM>(this), std::lock_guard<std::mutex> lock(posted_functions_mutex_);
reinterpret_cast<LPARAM>(new PostedFn(std::move(fn))))) { PostedFn posted_fn(fn);
assert_always("Unable to post message to thread queue"); posted_functions_.push_back(posted_fn);
}
while (!PostThreadMessage(thread_id_, WM_NULL, 0, 0)) {
Sleep(1);
} }
} }
@ -134,8 +124,10 @@ void Win32Loop::PostDelayed(std::function<void()> fn, uint64_t delay_millis) {
void Win32Loop::Quit() { void Win32Loop::Quit() {
assert_true(thread_id_ != 0); assert_true(thread_id_ != 0);
PostThreadMessage(thread_id_, kWmWin32LoopQuit, should_exit_ = true;
reinterpret_cast<WPARAM>(this), 0); while (!PostThreadMessage(thread_id_, WM_NULL, 0, 0)) {
Sleep(1);
}
} }
void Win32Loop::AwaitQuit() { quit_fence_.Wait(); } void Win32Loop::AwaitQuit() { quit_fence_.Wait(); }

View File

@ -42,17 +42,30 @@ class Win32Loop : public Loop {
std::function<void()> fn; std::function<void()> fn;
}; };
class PostedFn {
public:
explicit PostedFn(std::function<void()> fn) : fn_(std::move(fn)) {}
void Call() { fn_(); }
private:
std::function<void()> fn_;
};
void ThreadMain(); void ThreadMain();
static void TimerQueueCallback(void* context, uint8_t); static void TimerQueueCallback(void* context, uint8_t);
std::thread thread_; std::thread thread_;
DWORD thread_id_; DWORD thread_id_;
bool should_exit_ = false;
xe::threading::Fence quit_fence_; xe::threading::Fence quit_fence_;
HANDLE timer_queue_; HANDLE timer_queue_;
std::mutex pending_timers_mutex_; std::mutex pending_timers_mutex_;
std::list<PendingTimer*> pending_timers_; std::list<PendingTimer*> pending_timers_;
std::mutex posted_functions_mutex_;
std::list<PostedFn> posted_functions_;
}; };
} // namespace ui } // namespace ui