/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * This file incorporates work covered by the following license notice: * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed * with this work for additional information regarding copyright * ownership. The ASF licenses this file to you under the Apache * License, Version 2.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.apache.org/licenses/LICENSE-2.0 .
*/
/** * Start a new timer if we need to for nMS duration. * * if this is longer than the existing duration we're * waiting for, do nothing - unless bForce - which means * to reset the minimum period; used by the scheduled itself.
*/ void Scheduler::ImplStartTimer(sal_uInt64 nMS, bool bForce, sal_uInt64 nTime)
{
ImplSVData* pSVData = ImplGetSVData();
ImplSchedulerContext &rSchedCtx = pSVData->maSchedCtx; if ( !rSchedCtx.mbActive ) return;
// Only if smaller timeout, to avoid skipping. // Force instant wakeup on 0ms, if the previous period was not 0ms if (bForce || nProposedTimeout < nCurTimeout || (!nMS && rSchedCtx.mnTimerPeriod))
{
SAL_INFO( "vcl.schedule", " Starting scheduler system timer (" << nMS << "ms)" );
rSchedCtx.mnTimerStart = nTime;
rSchedCtx.mnTimerPeriod = nMS;
rSchedCtx.mpSalTimer->Start( nMS );
}
}
Scheduler::IdlesLockGuard::IdlesLockGuard()
{
ImplSVData* pSVData = ImplGetSVData();
ImplSchedulerContext& rSchedCtx = pSVData->maSchedCtx;
osl_atomic_increment(&rSchedCtx.mnIdlesLockCount); if (!Application::IsMainThread())
{ // Make sure that main thread has reached the main message loop, so no idles are executing. // It is important to ensure this, because e.g. ProcessEventsToIdle could be executed in a // nested event loop, while an active processed idle in the main thread is waiting for some // condition to proceed. Only main thread returning to Application::Execute guarantees that // the flag really took effect.
pSVData->m_inExecuteCondtion.reset(); // Put an empty event to the application's queue, to make sure that it loops through the // code that sets the condition, even when there's no other events in the queue
Application::PostUserEvent({});
SolarMutexReleaser releaser;
pSVData->m_inExecuteCondtion.wait();
}
}
int Scheduler::GetMostUrgentTaskPriority()
{ // Similar to Scheduler::CallbackTaskScheduling(), figure out the most urgent priority, but // don't actually invoke any task. int nMostUrgentPriority = -1;
ImplSVData* pSVData = ImplGetSVData();
ImplSchedulerContext& rSchedCtx = pSVData->maSchedCtx; if (!rSchedCtx.mbActive || rSchedCtx.mnTimerPeriod == InfiniteTimeoutMs)
{ return nMostUrgentPriority;
}
#if !(defined EMSCRIPTEN && ENABLE_QT6 && HAVE_EMSCRIPTEN_JSPI && !HAVE_EMSCRIPTEN_PROXY_TO_PTHREAD) //TODO: While the special Emscripten Qt6 JSPI/non-PROXY_TO_PTHREAD mode doesn't lock the // SolarMutex in QtTimer::timeoutActivated, but only down below when calling pTask->Invoke(), // that looks too brittle in general, so treat that special mode specially here.
DBG_TESTSOLARMUTEX(); #endif
sal_uInt64 nTime = tools::Time::GetSystemTicks(); // Allow for decimals, so subtract in the compare (needed at least on iOS) if ( nTime < rSchedCtx.mnTimerStart + rSchedCtx.mnTimerPeriod -1)
{ int nSleep = rSchedCtx.mnTimerStart + rSchedCtx.mnTimerPeriod - nTime;
UpdateSystemTimer(rSchedCtx, nSleep, true, nTime); return;
}
for (; nTaskPriority < PRIO_COUNT; ++nTaskPriority)
{ // Related: tdf#152703 Eliminate potential blocking during live resize // Only higher priority tasks need to be fired to redraw the window // so skip firing potentially long-running tasks, such as the Writer // idle layout timer, when a window is in live resize if ( nTaskPriority == static_cast<int>(TaskPriority::LOWEST) && ( ImplGetSVData()->mpWinData->mbIsLiveResize || ImplGetSVData()->mpWinData->mbIsWaitingForNativeEvent ) ) continue;
// always push the stack, as we don't traverse the whole list to push later
DropSchedulerData(rSchedCtx, pPrevMostUrgent, pMostUrgent, nMostUrgentPriority);
pMostUrgent->mpNext = rSchedCtx.mpSchedulerStack;
rSchedCtx.mpSchedulerStack = pMostUrgent;
rSchedCtx.mpSchedulerStackTop = pMostUrgent;
// high priority idle handlers have smaller numerical values for mePriority bool bIsLowerPriorityIdle = pMostUrgent->mePriority >= TaskPriority::HIGH_IDLE;
#ifdef MACOSX // tdf#165277 On macOS, only delay priorities lower than POST_PAINT // macOS bugs tdf#157312 and tdf#163945 were fixed by firing the // Skia flush task with TaskPriority::POST_PAINT. // The problem is that this method often executes within an // NSTimer and NSTimers are always fired while LibreOffice is in // -[NSApp nextEventMatchingMask:untilDate:inMode:dequeue:]. // Since fetching the next native event doesn't handle pending // events until *after* all of the pending NSTimers have fired, // calling SalInstance::AnyInput() will almost always return true // due to the pending events that will be handled immediately // after all of the pending NSTimers have fired. // The result is that the Skia flush task is frequently delayed // and, in cases like tdf#165277, a user's attempts to get // LibreOffice to paint the window through key and mouse events // leads to an endless delaying of the Skia flush task. // After experimenting with both Skia/Metal and Skia/Raster, // tdf#165277 requires the Skia flush task to run immediately // before the TaskPriority::POST_PAINT tasks. After that, all // TaskPriority::POST_PAINT tasks must run so the Skia flush // task now uses the TaskPriority::SKIA_FLUSH priority on macOS. // One positive side effect of this change is that live resizing // on macOS is now much smoother. Even with Skia disabled (which // does not paint using a task but does use tasks to handle live // resizing), the content resizes much more quickly when a user // rapidly changes window's size. if (bIsLowerPriorityIdle && pMostUrgent->mePriority <= TaskPriority::POST_PAINT)
bIsLowerPriorityIdle = false; #endif
// invoke the task
Unlock();
// Delay invoking tasks with idle priorities as long as there are user input or repaint events // in the OS event queue. This will often effectively compress such events and repaint only // once at the end, improving performance in cases such as repeated zooming with a complex document. bool bDelayInvoking
= bIsLowerPriorityIdle
&& (rSchedCtx.mnIdlesLockCount > 0
|| Application::AnyInput(VclInputFlags::MOUSE | VclInputFlags::KEYBOARD | VclInputFlags::PAINT));
/* * Current policy is that scheduler tasks aren't allowed to throw an exception. * Because otherwise the exception is caught somewhere totally unrelated. * TODO Ideally we could capture a proper backtrace and feed this into breakpad, * which is do-able, but requires writing some assembly. * See also SalUserEventList::DispatchUserEvents
*/ try
{ if (bDelayInvoking)
SAL_INFO( "vcl.schedule", tools::Time::GetSystemTicks()
<< " idle priority task " << pTask->GetDebugName()
<< " delayed, system events pending" ); else
{ // prepare Scheduler object for deletion after handling
pTask->SetDeletionFlags(); #ifdefined EMSCRIPTEN && ENABLE_QT6 && HAVE_EMSCRIPTEN_JSPI && !HAVE_EMSCRIPTEN_PROXY_TO_PTHREAD if (pTask->DecideTransferredExecution())
{ auto & data = comphelper::emscriptenthreading::getData();
(void) emscripten_proxy_promise(
data.proxyingQueue.queue, data.eventHandlerThread,
[](void * p) { autoconst pTask = static_cast<Task *>(p);
SolarMutexGuard g;
pTask->Invoke();
},
pTask);
} else
{
SolarMutexGuard g;
pTask->Invoke();
} #else
pTask->Invoke(); #endif
}
} catch (css::uno::Exception&)
{
TOOLS_WARN_EXCEPTION("vcl.schedule", "Uncaught");
std::abort();
} catch (std::exception& e)
{
SAL_WARN("vcl.schedule", "Uncaught " << typeid(e).name() << " " << e.what());
std::abort();
} catch (...)
{
SAL_WARN("vcl.schedule", "Uncaught exception during Task::Invoke()!");
std::abort();
}
Lock();
SchedulerGuard aSchedulerGuard; if ( !rSchedCtx.mbActive ) return;
// is the task scheduled in the correct priority queue? // if not we have to get a new data object, as we don't want to traverse // the whole list to move the data to the correct list, as the task list // is just single linked. // Task priority doesn't change that often AFAIK, or we might need to // start caching ImplSchedulerData objects. if (mpSchedulerData && mpSchedulerData->mePriority != mePriority)
{
mpSchedulerData->mpTask = nullptr;
mpSchedulerData = nullptr;
}
mbActive = true;
if ( !mpSchedulerData )
{ // insert Task
ImplSchedulerData* pSchedulerData = new ImplSchedulerData;
pSchedulerData->mpTask = this;
pSchedulerData->mbInScheduler = false; // mePriority is set in AppendSchedulerData
mpSchedulerData = pSchedulerData;
void Task::SetPriority(TaskPriority ePriority)
{ // you don't actually need to call Stop() before but Start() after, but we // can't check that and don't know when Start() should be called.
SAL_WARN_IF(mpSchedulerData && mbActive, "vcl.schedule", "Stop the task before changing the priority, as it will just " "change after the task was scheduled with the old prio!");
mePriority = ePriority;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.