Skip to content

Commit

Permalink
Improve message loop (#161)
Browse files Browse the repository at this point in the history
This patch improves a message loop.

* Implement `OnScheduleMessagePumpWork()`
* Add the `MessageLoopWorker` class to manage execution of `CefDoMessageLoopWork()`
  * `MessageLoopWorker` works on a special message window
* Remove `Sazabi::PumpMessage()` because it is no longer needed
* Move process to read leftover messages to `MessageLoopWorker`  from `CSazabi`

`MessageLoopWorker` does:

* Manage the timer so that `DoMessageLoopWork()` executed at at least 30fps.
* For `WM_SCHEDULE_CEF_WORK` message:
  * If `delayMs` is less than or equal to 0, execute `DoMessageLoopWork()` immediately. The timer does not reset.
  * If `delayMs` is greater than 0, set the timer for the specified time. Existing timers will be reset.
* For time timeout message:
  * Reset the timer and execute `DoMessageLoopWork()`.

This patch resolves a problem that the redirection is slow in the some pages.
That is caused by a lack of the implementation of `OnScheduleMessagePumpWork()`.
  • Loading branch information
HashidaTKS authored Mar 19, 2024
1 parent 21eb978 commit f6c0a70
Show file tree
Hide file tree
Showing 9 changed files with 252 additions and 84 deletions.
179 changes: 179 additions & 0 deletions MessageLoopWorker.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
// This class is based on CEF's main_message_loop_external_pump_win.cc.
// https://github.com/chromiumembedded/cef/blob/master/tests/shared/browser/main_message_loop_external_pump_win.cc
// main_message_loop_external_pump_win.cc is governed by a BSD-style license.
// https://github.com/chromiumembedded/cef/blob/master/LICENSE.txt

#include "stdafx.h"
#include "MessageLoopWorker.h"

MessageLoopWorker::MessageLoopWorker(HINSTANCE hInstance)
{
m_bTimerPending_ = false;
m_bIsActive_ = false;
m_bReentrancyDetected_ = false;
m_hWnd_ = NULL;
m_hInstance_ = hInstance;
m_bRunning_ = false;
}

MessageLoopWorker::~MessageLoopWorker()
{
Quit();
}

void MessageLoopWorker::Run()
{
InitWindow();
OnScheduleWork(0);
m_bRunning_ = true;
}

void MessageLoopWorker::Quit()
{
if (!m_bRunning_)
{
return;
}
KillTimer();
::SetWindowLongPtr(m_hWnd_, GWLP_USERDATA, NULL);
DestroyWindow(m_hWnd_);
m_hWnd_ = NULL;
//Process rest CEF messages.
CefDoMessageLoopWork();
m_bRunning_ = false;
}

bool MessageLoopWorker::PostScheduleMessage(int64_t delayMs)
{
if (!m_hWnd_)
{
return FALSE;
}
return PostMessage(m_hWnd_, WM_SCHEDULE_CEF_WORK, NULL, static_cast<LPARAM>(delayMs));
}

void MessageLoopWorker::OnScheduleWork(int64_t delayMs)
{
if (delayMs == m_nTimerDelayPlaceholder && m_bTimerPending_)
{
// Don't set the maximum timer requested from DoWork() if a timer event is
// currently pending.
return;
}

if (delayMs <= 0)
{
DoWork();
}
else
{
// If | delayMs | is > 0 then the call should be scheduled to happen after the specified delay
// and any currently pending scheduled call should be cancelled.
KillTimer();

if (delayMs > m_nMaxTimerDelay)
{
delayMs = m_nMaxTimerDelay;
}
SetTimer(delayMs);
}
}

void MessageLoopWorker::OnTimerTimeout()
{
KillTimer();
DoWork();
}

void MessageLoopWorker::SetTimer(int64_t delayMs)
{
m_bTimerPending_ = true;
::SetTimer(m_hWnd_, m_nTimerID, static_cast<UINT>(delayMs), nullptr);
}

void MessageLoopWorker::KillTimer()
{
if (m_bTimerPending_)
{
::KillTimer(m_hWnd_, m_nTimerID);
m_bTimerPending_ = false;
}
}

void MessageLoopWorker::DoWork()
{
if (m_bIsActive_)
{
// When CefDoMessageLoopWork() is called there may be various callbacks
// (such as paint and IPC messages) that result in additional calls to this
// method. If re-entrancy is detected we must repost a request again to the
// owner thread to ensure that the discarded call is executed in the future.
m_bReentrancyDetected_ = true;
}
else
{
m_bReentrancyDetected_ = false;

m_bIsActive_ = true;
CefDoMessageLoopWork();
m_bIsActive_ = false;

// Note: |m_bReentrancy_detected_| may have changed due to re-entrant calls to
// this method.
}

if (m_bReentrancyDetected_)
{
// Execute the remaining work as soon as possible.
PostScheduleMessage(0);
}
else if (!m_bTimerPending_)
{
PostScheduleMessage(m_nTimerDelayPlaceholder);
}
}

void MessageLoopWorker::InitWindow()
{
const wchar_t* className = _T("MessageLoopWindow");
HINSTANCE hInstance = m_hInstance_;
WNDCLASSEX wcex = {};
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.lpfnWndProc = MessageLoopWorker::WindowProcesser;
wcex.hInstance = hInstance;
wcex.lpszClassName = className;
RegisterClassEx(&wcex);

m_hWnd_ = CreateWindow(className, nullptr, WS_OVERLAPPEDWINDOW, 0, 0, 0, 0, HWND_MESSAGE, nullptr, hInstance, nullptr);
::SetWindowLongPtr(m_hWnd_, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(this));
}

// static
LRESULT CALLBACK MessageLoopWorker::WindowProcesser(HWND hwnd,
UINT msg,
WPARAM wparam,
LPARAM lparam)
{
MessageLoopWorker* messageLoopWorker = reinterpret_cast<MessageLoopWorker*>(GetWindowLongPtr(hwnd, GWLP_USERDATA));
if (!messageLoopWorker)
{
return DefWindowProc(hwnd, msg, wparam, lparam);
}
switch (msg)
{
case WM_TIMER:
// Timer timed out.
if (wparam == m_nTimerID)
{
messageLoopWorker->OnTimerTimeout();
return 0;
}
break;
case WM_SCHEDULE_CEF_WORK:
// OnScheduleMessagePumpWork() request.
const int64_t delayMs = static_cast<int64_t>(lparam);
messageLoopWorker->OnScheduleWork(delayMs);
return 0;
}
return DefWindowProc(hwnd, msg, wparam, lparam);
}
37 changes: 37 additions & 0 deletions MessageLoopWorker.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
#pragma once
#include "sbcommon.h"

class MessageLoopWorker
{
public:
MessageLoopWorker(HINSTANCE hInstance);
~MessageLoopWorker();
bool PostScheduleMessage(int64_t delayMs);
void Run();
void Quit();

private:
static const UINT_PTR m_nTimerID = 1;
static const int32_t m_nTimerDelayPlaceholder = INT_MAX;
// The maximum number of milliseconds we're willing to wait between calls to
// DoWork().
static const int64_t m_nMaxTimerDelay = 1000 / 30; // 30fps

HWND m_hWnd_;
bool m_bTimerPending_;
bool m_bIsActive_;
bool m_bReentrancyDetected_;
HINSTANCE m_hInstance_;
bool m_bRunning_;

void SetTimer(int64_t delayMs);
void KillTimer();
void OnScheduleWork(int64_t delayMs);
void OnTimerTimeout();
void DoWork();
void InitWindow();
static LRESULT CALLBACK WindowProcesser(HWND hwnd,
UINT msg,
WPARAM wparam,
LPARAM lparam);
};
87 changes: 10 additions & 77 deletions Sazabi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ CSazabi::CSazabi()
m_bFirstInstance = FALSE;
m_bEnforceDeleteCache = FALSE;
m_bMultiThreadedMessageLoop = FALSE;
m_pMessageLoopWorker = NULL;
}
CSazabi::~CSazabi()
{
Expand Down Expand Up @@ -2190,76 +2191,6 @@ HWND CSazabi::GetTopWindowHandle(const DWORD TargetID)
return NULL;
}

BOOL CSazabi::PumpMessage()
{
MSG msg = {0};
TCHAR classname[32] = {0};
__try
{
if (m_bToBeShutdown)
{
UnInitializeCef();
return FALSE;
}

//SZB
if (m_bCEFInitialized)
{
if (!m_bMultiThreadedMessageLoop)
{
//Windows 10 (2004)以降に搭載されているMS-IMEでアドレスバーでEnterキーが効かない問題が発生
//IMEを「以前のバージョンのMicrosoft IMEを使う」にすると問題なくなるが
//KEYボード系のイベント発生時にEditコントロールなどでは、CefDoMessageLoopWorkをCallしなければ問題ないことが
//判明したため、フィルタリングする。multi_threaded_message_loopを有効にすれば問題ないことも判明したが
//キーボード、マウス系のイベントがBroViewやBroFrameに飛んでこない弊害あり
if (::PeekMessage(&msg, NULL, WM_KEYFIRST, WM_KEYLAST, PM_NOREMOVE))
{
if (msg.hwnd)
{
::GetClassName(msg.hwnd, classname, 31);
TRACE(_T("PumpMessage[0x%08x] %s (0x%x)\n"), msg.hwnd, classname, msg.message);
if (_tcscmp(classname, WC_EDIT) == 0 ||
_tcscmp(classname, WC_COMBOBOXEX) == 0 ||
_tcscmp(classname, WC_COMBOBOX) == 0 ||
_tcscmp(classname, WC_BUTTON) == 0 ||
_tcscmp(classname, WC_STATIC) == 0 ||
_tcscmp(classname, _T("#32770")) == 0 ||
_tcscmp(classname, REBARCLASSNAME) == 0 ||
_tcscmp(classname, WC_TABCONTROL) == 0 ||
_tcscmp(classname, WC_TREEVIEW) == 0 ||
_tcscmp(classname, WC_LISTVIEW) == 0 ||
_tcscmp(classname, WC_LISTBOX) == 0
//|| _tcscmp(classname, WC_HEADER) == 0
//|| _tcscmp(classname, TOOLBARCLASSNAME) == 0
//|| _tcscmp(classname, TOOLTIPS_CLASS) == 0
//|| _tcscmp(classname, STATUSCLASSNAME) == 0
//|| _tcscmp(classname, TRACKBAR_CLASS) == 0
//|| _tcscmp(classname, UPDOWN_CLASS) == 0
//|| _tcscmp(classname, PROGRESS_CLASS) == 0
//|| _tcscmp(classname, HOTKEY_CLASS) == 0
//|| _tcscmp(classname, ANIMATE_CLASS) == 0
//|| _tcscmp(classname, MONTHCAL_CLASS) == 0
//|| _tcscmp(classname, DATETIMEPICK_CLASS) == 0
//|| _tcscmp(classname, WC_IPADDRESS) == 0
//|| _tcscmp(classname, WC_PAGESCROLLER) == 0
//|| _tcscmp(classname, WC_NATIVEFONTCTL) == 0
//|| _tcscmp(classname, WC_SCROLLBAR) == 0
)
{
return CWinApp::PumpMessage();
}
}
}
CefDoMessageLoopWork();
}
}
return CWinApp::PumpMessage();
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
return FALSE;
}
}
///////////////////////////////////////////////////////////////////////////////////
BOOL CSazabi::SafeTerminateProcess(HANDLE hProcess, INT_PTR uExitCode)
{
Expand Down Expand Up @@ -4258,6 +4189,11 @@ void CSazabi::InitializeCef()
}

m_bCEFInitialized = CefInitialize(mainargs, settings, m_cefApp.get(), sandbox_info);
if (!m_bMultiThreadedMessageLoop)
{
m_pMessageLoopWorker = new MessageLoopWorker(m_hInstance);
m_pMessageLoopWorker->Run();
}
}

/*
Expand Down Expand Up @@ -4305,14 +4241,11 @@ void CSazabi::UnInitializeCef()
{
m_bCEFInitialized = FALSE;
m_cefApp = nullptr;
if (!m_bMultiThreadedMessageLoop)
if (m_pMessageLoopWorker)
{
for (int i = 0; i < 10; i++)
{
CefDoMessageLoopWork();
// Sleep to allow the CEF proc to do work.
Sleep(50);
}
m_pMessageLoopWorker->Quit();
delete m_pMessageLoopWorker;
m_pMessageLoopWorker = NULL;
}
// shutdown CEF
CefShutdown();
Expand Down
3 changes: 2 additions & 1 deletion Sazabi.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include "DlgSetting.h"
#include "DlgDebugWnd.h"
#include "include\cef_parser.h"
#include "MessageLoopWorker.h"
/////////////////////////////////////////////////////////////////////////////
// CSazabi:
//
Expand Down Expand Up @@ -60,13 +61,13 @@ class CSazabi : public CWinApp
//func
CSazabi();
virtual ~CSazabi();
BOOL PumpMessage();
CStringW m_strAppIDw;

//SZB
BOOL m_bCEFInitialized;
BOOL m_bToBeShutdown;
BOOL m_bMultiThreadedMessageLoop;
MessageLoopWorker* m_pMessageLoopWorker;

CefRefPtr<ClientApp> m_cefApp;

Expand Down
2 changes: 2 additions & 0 deletions Sazabi.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,7 @@
<ClCompile Include="MainFrm.cpp" />
<ClCompile Include="MyComboBoxEx.cpp" />
<ClCompile Include="Sazabi.cpp" />
<ClCompile Include="MessageLoopWorker.cpp" />
<ClCompile Include="sbcommon.cpp" />
<ClCompile Include="StdAfx.cpp">
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='R64_CSG|Win32'">Create</PrecompiledHeader>
Expand Down Expand Up @@ -234,6 +235,7 @@
<ClInclude Include="minhook\trampoline.h" />
<ClInclude Include="MyComboBoxEx.h" />
<ClInclude Include="Resource.h" />
<ClInclude Include="MessageLoopWorker.h" />
<ClInclude Include="sbcommon.h" />
<ClInclude Include="Sazabi.h" />
<ClInclude Include="StdAfx.h" />
Expand Down
6 changes: 6 additions & 0 deletions Sazabi.vcxproj.filters
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,9 @@
<ClCompile Include="DlgCertification.cpp">
<Filter>DLG</Filter>
</ClCompile>
<ClCompile Include="MessageLoopWorker.cpp">
<Filter>SB</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="Resource.h" />
Expand Down Expand Up @@ -150,6 +153,9 @@
<ClInclude Include="DlgCertification.h">
<Filter>DLG</Filter>
</ClInclude>
<ClInclude Include="MessageLoopWorker.h">
<Filter>SB</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<CustomBuild Include="res\Sazabi.ico">
Expand Down
Loading

0 comments on commit f6c0a70

Please sign in to comment.