office-gobmx/include/vcl/scheduler.hxx
Mike Kaganski 3e0a2239e9 Introduce a guard to delay processing of idles
In a following scenario, there could be a crash:

1. Platform: a Windows system with MS Word installed.
2. LibreOffice is run in a listener mode;
3. A Java program opens a Writer document in a visible mode, with an
   embedded Word OLE object;
4. It adds some text; then resizes the OLE object; then removes the
   OLE object.

Word OLE objects have OLEMISC_RECOMPOSEONRESIZE flag [1]; this means,
that every re-layout of the document with this object must ask the
OLE server to re-layout the object content. So, the request thread
changes the document text, which triggers idle re-layout or redraw;
the idles start executing immediately in the idle main thread, with
solar mutex locked; then the request thread starts the OLE object
removal operation. The ongoing relayout in main thread would at some
stage need to execute a call to the OLE object, which temporarily
releases the solar mutex (this makes impossible using solar mutex to
synchronize the order of operations in this scenario). Other mutexes
guarding OLE object (in OleEmbeddedObject, and in OleComponent) are
also released for the duration of the call. Thus, the removal that
happens in the request thread proceeds, and the node containing the
OLE object is destroyed, while the main thread (processing exactly
this node) is waiting for the OLE server response, then for mutexes,
to proceed. After that, the main thread would attempt to access the
destroyed node object.

This change introduces a scheduler guard (a RAII object), that sets
a flag to not process idle events during the lifetime of the guard.
In its constructor, it also makes sure, that current pending idle
events are finished. This would make sure that guarded code started
from other threads would not race with idles potentially accessing
the model that is currently in transient state.

[1] https://learn.microsoft.com/en-us/windows/win32/api/oleidl/ne-oleidl-olemisc

Change-Id: I2ef0601ccd8b5872588a88493d1f43e39022dbed
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/164753
Tested-by: Mike Kaganski <mike.kaganski@collabora.com>
Reviewed-by: Mike Kaganski <mike.kaganski@collabora.com>
2024-03-13 13:47:53 +01:00

95 lines
3.3 KiB
C++

/* -*- 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 .
*/
#ifndef INCLUDED_VCL_SCHEDULER_HXX
#define INCLUDED_VCL_SCHEDULER_HXX
#include <vcl/dllapi.h>
struct ImplSchedulerContext;
class VCL_DLLPUBLIC Scheduler final
{
friend class SchedulerGuard;
friend class Task;
Scheduler() = delete;
static inline void UpdateSystemTimer( ImplSchedulerContext &rSchedCtx,
sal_uInt64 nMinPeriod,
bool bForce, sal_uInt64 nTime );
static void ImplStartTimer ( sal_uInt64 nMS, bool bForce, sal_uInt64 nTime );
static void Lock();
static void Unlock();
public:
static constexpr sal_uInt64 ImmediateTimeoutMs = 0;
static constexpr sal_uInt64 InfiniteTimeoutMs = SAL_MAX_UINT64;
static void ImplDeInitScheduler();
/**
* System timer callback function, which processes one LO task
*
* Will restart the system timer, so it will process further tasks at the right time.
**/
static void CallbackTaskScheduling();
/**
* Process all events until none is pending
*
* This can busy-lock, if some task or system event always generates new
* events when being processed. Most time it's called in unit tests to
* process all pending events. Internally it just calls
* Application::Reschedule( true ) until it fails.
*
* @see Application::Reschedule
*/
static void ProcessEventsToIdle();
/**
* Wakes up the scheduler
*
* This doesn't handle any events! It just ensures the Scheduler is run as
* soon as possible by forcing the Scheduler timer to fire.
*
* Can be used for complex UpdateMinPeriod function, where the task is
* actually active but not ready and we want to skip the Task::Start()
* queue append for faster reaction.
*/
static void Wakeup();
/// Control the deterministic mode. In this mode, two subsequent runs of
/// LibreOffice fire about the same amount idles.
static void SetDeterministicMode(bool bDeterministic);
/// Return the current state of deterministic mode.
static bool GetDeterministicMode();
// Makes sure that idles are not processed, until the guard is destroyed
struct VCL_DLLPUBLIC IdlesLockGuard final
{
IdlesLockGuard();
~IdlesLockGuard();
};
};
#endif // INCLUDED_VCL_SCHEDULER_HXX
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */