idletask: Wait with a condition_variable when not on the main thread

Judging by the comment that was in waitUntilIdleDispatched, I think the
intention of this helper class is to wait until the main loop is idle
and that any event processing has actually completed. However, if this
is called outside of the main thread then that doesn’t work if the event
processing temporarily releases the solar mutex. In that case, the
thread that is waiting for the IdleTask flag would wake up and set the
flag itself even though the main thread hasn’t actually finished
executing its event handling code yet.

To fix that this patch makes it instead wait with a
std::condition_variable instead of Yield when not running on the main
thread so that we can be sure that the main thread will invoke the idle
task instead of the waiting thread.

Change-Id: Ib5c6efe31dfb5e1a5039b43702e23bf0104cd403
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/194180
Tested-by: Jenkins
Reviewed-by: Caolán McNamara <caolan.mcnamara@collabora.com>
This commit is contained in:
Neil Roberts
2025-11-19 20:18:33 +01:00
committed by Caolán McNamara
parent 2a62fe6a6b
commit f7d0ddb247
2 changed files with 22 additions and 3 deletions
+7
View File
@@ -12,6 +12,8 @@
#include <sal/config.h>
#include <vcl/dllapi.h>
#include <vcl/idle.hxx>
#include <condition_variable>
#include <mutex>
//IdleTask class to add a low priority Idle task
class VCL_DLLPUBLIC IdleTask
@@ -27,6 +29,11 @@ public:
private:
DECL_LINK(FlipFlag, Timer*, void);
// Mutex and condition variable used to signal flag
std::mutex mFlagMutex;
std::condition_variable mFlagCv;
bool flag;
Idle maIdle{ "testtool IdleTask" };
};
+15 -3
View File
@@ -32,19 +32,31 @@ bool IdleTask::GetFlag() const
//Callback function of IdleTask Class
IMPL_LINK(IdleTask, FlipFlag, Timer*, , void)
{
std::lock_guard aGuard(mFlagMutex);
//setting the flag to make sure that low priority idle task has been dispatched
flag = true;
mFlagCv.notify_all();
}
void IdleTask::waitUntilIdleDispatched()
{
//creating instance of IdleTask Class
IdleTask idleTask;
while (!idleTask.GetFlag())
if (Application::IsMainThread())
{
//dispatching all the events via VCL main-loop
SolarMutexGuard aGuard;
Application::Yield();
while (!idleTask.GetFlag())
Application::Yield();
}
else
{
// We don’t want to wait with Yield because that can cause this thread to execute the idle
// task. If the main thread is processing an event and temporarily releases the solar mutex
// then that would cause this function to finish before the event processing in the main
// thread actually finishes.
std::unique_lock aLock(idleTask.mFlagMutex);
idleTask.mFlagCv.wait(aLock, [&idleTask] { return idleTask.GetFlag(); });
}
}