-
Notifications
You must be signed in to change notification settings - Fork 10
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[#39] Integrate with existing (main) Windows GUI thread
This commit adds a new class template called `base::WinThreadAttachment` which can be used to integrate libbase's cross-thread post-tasking with Windows native message queue system. Example usage: ```cpp HWND hwnd = CreateWindowEx(/* ... */); // This must be done after creating a window and before main loop and // outlive the main loop. // `LIBBASE_TASK_MSG_ID` here is some custom integer provided by user // of this library that will not be used as any other message ID in // the app (e.g. `WM_APP+0`). base::WinThreadAttachment<LIBBASE_TASK_MSG_ID> mainThread{hwnd}; // As long as the `mainThread` lives, current thread will have // associated task runner that will post task to Window's message // queue and all such tasks will be executed between other messages // on that queue. // Main loop example MSG msg = {}; while (GetMessage(&msg, NULL, 0, 0) > 0) { TranslateMessage(&msg); DispatchMessage(&msg); } ``` Apart from there, new (Windows-only) example called `win32` has been added to showcase new functionality.
- Loading branch information
Showing
6 changed files
with
225 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,5 @@ | ||
add_subdirectory(simple) | ||
add_subdirectory(simple) | ||
|
||
if (CMAKE_SYSTEM_NAME STREQUAL "Windows") | ||
add_subdirectory(win32) | ||
endif () |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
add_executable(win32 WIN32 "") | ||
|
||
target_compile_options(win32 PRIVATE ${LIBBASE_COMPILE_FLAGS}) | ||
target_link_libraries(win32 PRIVATE libbase) | ||
|
||
target_sources(win32 | ||
PRIVATE | ||
main.cc | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
#ifndef UNICODE | ||
#define UNICODE | ||
#endif | ||
|
||
#include <thread> | ||
|
||
#include "base/threading/thread_pool.h" | ||
#include "base/threading/win/win_thread_attachment.h" | ||
|
||
#include <windows.h> | ||
|
||
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam); | ||
|
||
void SetWindowTitle(HWND hWnd, const wchar_t* title) { | ||
SetWindowText(hWnd, title); | ||
} | ||
|
||
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE, PWSTR, int nCmdShow) { | ||
// Register the window class. | ||
const wchar_t CLASS_NAME[] = L"Window Class"; | ||
WNDCLASS wc = {}; | ||
wc.lpfnWndProc = WindowProc; | ||
wc.hInstance = hInstance; | ||
wc.lpszClassName = CLASS_NAME; | ||
RegisterClass(&wc); | ||
|
||
HWND hwnd = CreateWindowEx( | ||
0, CLASS_NAME, L"Default title", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, | ||
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL); | ||
if (hwnd == NULL) { | ||
return 0; | ||
} | ||
|
||
ShowWindow(hwnd, nCmdShow); | ||
|
||
// Setup libbase attachment to main Window thread's message loop | ||
constexpr unsigned int LIBBASE_TASK_MSG_ID = WM_APP + 0; | ||
base::WinThreadAttachment<LIBBASE_TASK_MSG_ID> mainThread{hwnd}; | ||
|
||
// Some example testing | ||
mainThread.TaskRunner()->PostTask( | ||
FROM_HERE, base::BindOnce(&SetWindowTitle, hwnd, | ||
L"Changed window title from post-task")); | ||
mainThread.TaskRunner()->PostDelayedTask( | ||
FROM_HERE, | ||
base::BindOnce(&SetWindowTitle, hwnd, L"Delayed task executed as well!"), | ||
base::Seconds(3)); | ||
|
||
base::ThreadPool threadPool{4}; | ||
threadPool.Start(); | ||
threadPool.GetTaskRunner()->PostTaskAndReplyWithResult( | ||
FROM_HERE, base::BindOnce([]() -> const wchar_t* { | ||
std::this_thread::sleep_for(std::chrono::seconds(5)); | ||
return L"Greetings from thread pool!"; | ||
}), | ||
base::BindOnce(&SetWindowTitle, hwnd)); | ||
|
||
// Run the message loop. | ||
MSG msg = {}; | ||
while (GetMessage(&msg, NULL, 0, 0) > 0) { | ||
TranslateMessage(&msg); | ||
DispatchMessage(&msg); | ||
} | ||
|
||
return 0; | ||
} | ||
|
||
LRESULT CALLBACK WindowProc(HWND hwnd, | ||
UINT uMsg, | ||
WPARAM wParam, | ||
LPARAM lParam) { | ||
switch (uMsg) { | ||
case WM_DESTROY: | ||
PostQuitMessage(0); | ||
return 0; | ||
|
||
case WM_PAINT: { | ||
PAINTSTRUCT ps; | ||
HDC hdc = BeginPaint(hwnd, &ps); | ||
FillRect(hdc, &ps.rcPaint, (HBRUSH)(COLOR_WINDOW + 1)); | ||
EndPaint(hwnd, &ps); | ||
} | ||
return 0; | ||
} | ||
|
||
return DefWindowProc(hwnd, uMsg, wParam, lParam); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
#pragma once | ||
|
||
#ifdef LIBBASE_IS_WINDOWS | ||
|
||
#include <windows.h> | ||
|
||
#include "base/message_loop/message_pump_impl.h" | ||
|
||
namespace base { | ||
|
||
template <UINT WM_LIBBASE_EXECUTE_TASK> | ||
class MessagePumpWinImpl : public MessagePumpImpl { | ||
public: | ||
MessagePumpWinImpl(size_t executors_count, HWND hwnd) | ||
: MessagePumpImpl(executors_count), hwnd_(hwnd) {} | ||
|
||
// MessagePump | ||
bool QueuePendingTask(PendingTask pending_task) override { | ||
if (MessagePumpImpl::QueuePendingTask(std::move(pending_task))) { | ||
PostMessage(hwnd_, WM_LIBBASE_EXECUTE_TASK, 0, 0); | ||
return true; | ||
} | ||
return false; | ||
} | ||
|
||
private: | ||
HWND hwnd_; | ||
}; | ||
|
||
} // namespace base | ||
|
||
#endif // LIBBASE_IS_WINDOWS |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
#pragma once | ||
|
||
#ifdef LIBBASE_IS_WINDOWS | ||
|
||
#include <windows.h> | ||
|
||
#include "base/message_loop/win/message_pump_win_impl.h" | ||
#include "base/sequenced_task_runner_helpers.h" | ||
#include "base/threading/delayed_task_manager_shared_instance.h" | ||
#include "base/threading/sequenced_task_runner_handle.h" | ||
#include "base/threading/task_runner_impl.h" | ||
|
||
namespace base { | ||
|
||
template <UINT WM_LIBBASE_EXECUTE_TASK> | ||
class WinThreadAttachment { | ||
public: | ||
static LRESULT CALLBACK WindowProc(HWND hWnd, | ||
UINT uMsg, | ||
WPARAM wParam, | ||
LPARAM lParam) { | ||
DCHECK_NE(WinThreadAttachment::current_instance_, nullptr); | ||
|
||
switch (uMsg) { | ||
case WM_LIBBASE_EXECUTE_TASK: { | ||
const MessagePump::ExecutorId executor_id = 0; | ||
if (auto pending_task = | ||
WinThreadAttachment::current_instance_->message_pump_ | ||
->GetNextPendingTask(executor_id)) { | ||
std::move(pending_task.task).Run(); | ||
} | ||
} | ||
return DefWindowProc(hWnd, uMsg, wParam, lParam); | ||
|
||
default: | ||
return WinThreadAttachment::current_instance_->wndProc_(hWnd, uMsg, | ||
wParam, lParam); | ||
} | ||
} | ||
|
||
WinThreadAttachment(HWND hWnd) | ||
: hWnd_(hWnd), | ||
wndProc_((WNDPROC)GetWindowLongPtr(hWnd_, GWLP_WNDPROC)), | ||
sequence_id_(detail::SequenceIdGenerator::GetNextSequenceId()), | ||
message_pump_( | ||
std::make_shared<MessagePumpWinImpl<WM_LIBBASE_EXECUTE_TASK>>( | ||
1, | ||
hWnd_)), | ||
task_runner_(SingleThreadTaskRunnerImpl::Create( | ||
message_pump_, | ||
sequence_id_, | ||
0, | ||
DelayedTaskManagerSharedInstance::GetOrCreateSharedInstance())), | ||
scoped_sequence_id_(sequence_id_), | ||
scoped_task_runner_handle_(task_runner_) { | ||
DCHECK_EQ(WinThreadAttachment::current_instance_, nullptr); | ||
WinThreadAttachment::current_instance_ = this; | ||
|
||
SetWindowLongPtr(hWnd_, GWLP_WNDPROC, | ||
(LONG_PTR)WinThreadAttachment::WindowProc); | ||
} | ||
|
||
~WinThreadAttachment() { | ||
DCHECK_NE(WinThreadAttachment::current_instance_, nullptr); | ||
WinThreadAttachment::current_instance_ = nullptr; | ||
|
||
message_pump_->Stop({}); | ||
SetWindowLongPtr(hWnd_, GWLP_WNDPROC, (LONG_PTR)wndProc_); | ||
} | ||
|
||
std::shared_ptr<SingleThreadTaskRunner> TaskRunner() const { | ||
return task_runner_; | ||
} | ||
|
||
private: | ||
inline static thread_local WinThreadAttachment* current_instance_ = nullptr; | ||
|
||
HWND hWnd_; | ||
WNDPROC wndProc_; | ||
|
||
SequenceId sequence_id_; | ||
std::shared_ptr<MessagePump> message_pump_; | ||
std::shared_ptr<SingleThreadTaskRunnerImpl> task_runner_; | ||
detail::ScopedSequenceIdSetter scoped_sequence_id_; | ||
SequencedTaskRunnerHandle scoped_task_runner_handle_; | ||
}; | ||
|
||
} // namespace base | ||
|
||
#endif // LIBBASE_IS_WINDOWS |