https://bugs.documentfoundation.org/show_bug.cgi?id=173208

            Bug ID: 173208
           Summary: LO hangs at GUI startup when RecoveryInfo/Crashed=true
                    triggers SynchronousDispatch while a pyuno extension
                    OnStartApp handler calls Desktop.getCurrentComponent()
                    (Solar Mutex deadlock)
           Product: LibreOffice
           Version: 26.2.5.2 release
          Hardware: x86-64 (AMD64)
                OS: Linux (All)
            Status: UNCONFIRMED
          Severity: normal
          Priority: medium
         Component: framework
          Assignee: [email protected]
          Reporter: [email protected]

LibreOffice hangs indefinitely at GUI startup — no window ever appears, both
oosplash
and soffice.bin remain in kernel wait states — when both of the following are
true:

  (a) RecoveryInfo/Crashed=true is present in the user's
      ~/.config/libreoffice/4/user/registrymodifications.xcu (typical after any
previous
      crash, including headless --convert-to crashes such as bug 173205, or a
SIGKILL
      on a running GUI instance), AND

  (b) A pyuno extension whose OnStartApp handler eventually calls
      Desktop.getCurrentComponent() on a background thread is installed. Nelson
MCP 0.12.1
      is a concrete reproducer that hits this reliably; the OnStartApp path is
documented
      in its upstream tracker.

Neither condition alone is enough. Removing Crashed=true (setting it to false)
makes the
next launch succeed within ~1 s. Removing the extension also breaks the cycle.

Steps to Reproduce:

1. Install a pyuno extension whose OnStartApp handler calls
Desktop.getCurrentComponent()
   on a background thread. Nelson MCP 0.12.1 (nelson-0.12.1.oxt) is one such
extension.

2. Cause any LibreOffice crash that leaves RecoveryInfo/Crashed=true in the
user profile.
   Reliable path: run

       soffice --headless --norestore --convert-to ods --outdir out
<bug-173205-file.ods>

   and let it SIGABRT (see bug 173205). Alternatively, kill a running GUI
soffice.bin
   with SIGKILL.

3. Start LO GUI:

       soffice --writer any-file.doc

   or double-click any document in the file manager.

Actual Results:

oosplash and soffice.bin are spawned. Both processes remain in kernel wait
states
forever. No window ever appears.

    oosplash          -> wchan = anon_pipe_read     (waiting on splash-pipe
signal)
    soffice.bin main  -> wchan = futex_do_wait      (inside
Application::Execute)
    soffice.bin py×2  -> wchan = futex_do_wait      (blocked at
SolarMutex::doAcquire)

The pair remains hung until killed by the user.

Expected Results:

The Writer/Calc window opens normally, or the Recovery dialog appears if
Recovery has
work to do.

Additional Information:

Live backtrace of the hung soffice.bin process captured with

    sudo gdb -batch -ex 'set pagination off' -ex 'thread apply all bt 30' -p
<PID>

after the pair had been hung for 18 minutes. Full 12-thread backtrace is
attached
(08-sudo-gdb-full-bt.txt). The relevant frames are as follows.

Main thread (Thread 1) — holds the Solar Mutex, waits on a framework mutex:

    #0  __lll_lock_wait                              libc.so.6
    #1  pthread_mutex_lock@@GLIBC_2.2.5              libc.so.6
    #2  ???                                          libmergedlo.so + 0x3539e3
    #3  ???                                          libmergedlo.so + 0x2d3668
    ...
    #12 comphelper::SynchronousDispatch::dispatch()  libmergedlo.so
    #13 ???                                          libmergedlo.so (document
open pipeline)
    ...
    #18 SalUserEventList::DispatchUserEvents(bool)   libmergedlo.so
    #19 QtInstance::ImplYield(bool, bool)            libvclplug_qt6lo.so
    #20 QtInstance::DoYield(bool, bool)              libvclplug_qt6lo.so
    #21 Application::Yield()                         libmergedlo.so
    #22 Application::Execute()                       libmergedlo.so
    #23 ImplSVMain()                                 libmergedlo.so
    #24 soffice_main()                               libmergedlo.so

Two Python worker threads (Thread 6 and Thread 7, spawned by the extension's
OnStartApp
bootstrap) — blocked at SolarMutex::doAcquire:

    #0  __lll_lock_wait                              libc.so.6
    #1  pthread_mutex_lock@@GLIBC_2.2.5              libc.so.6
    #2  osl_acquireMutex                             libuno_sal.so.3
    #3  comphelper::SolarMutex::doAcquire            libmergedlo.so   ← blocked
    #4  ???                                          libmergedlo.so + 0x3251f53
    #5  framework::Desktop::getActiveFrame()         libmergedlo.so
    #6  framework::Desktop::getCurrentFrame()        libmergedlo.so
    #7  framework::Desktop::getCurrentComponent()    libmergedlo.so
    #8-11 ???                                        libgcc3_uno.so,
libreflectionlo.so
    #12 ???                                          libinvocationlo.so
    #13 ???                                          libpyuno.so
    #14 _PyObject_MakeTpCall                         libpython3.13.so
    #15 _PyEval_EvalFrameDefault                     libpython3.13.so
    ...

Cycle:
  * Main thread is in comphelper::SynchronousDispatch::dispatch(), reached from
    Application::Execute -> Yield -> DispatchUserEvents. This is the
document-open path
    that Recovery drives at startup when RecoveryInfo/Crashed=true.
  * Main thread holds the Solar Mutex (unsurprising during GUI startup) and
waits on
    an internal framework mutex whose release requires a UNO callback to
complete.
  * The UNO callback is coming from the extension's pyuno background thread;
that
    thread is blocked at SolarMutex::doAcquire, which is held by the main
thread.

Falsification: reproduced on an isolated -env:UserInstallation profile without
any
pyuno extension installed. Six combinations of user profile state were tested
(Crashed x zombie RecoveryList/replace with empty TempURL x backup lock x
0-byte
backup .ods_0.ods). The Recovery dialog appeared within ~5 s in every
combination.
No hang. The extension's OnStartApp Desktop-API call is essential; the LO
Recovery
flow alone is not sufficient.

Suggested fixes (either one breaks the cycle):

  (1) Recovery-triggered SynchronousDispatch in Application::Execute should not
hold
      framework locks that must be released via UNO callbacks. This would let
      extension OnStartApp handlers acquire the Solar Mutex concurrently.

  (2) Extension OnStartApp handlers should be invoked after
Application::Execute has
      yielded to idle at least once, so that startup framework locks are
released
      before any pyuno-driven UNO calls run.

Related defect in the same profile-write path (may be a separate ticket at the
triager's preference): headless soffice run with

    soffice -env:UserInstallation=file:///path/to/agent-profile --headless \
            --convert-to <fmt> <file>

writes RecoveryInfo/Crashed=true and RecoveryList entries to the user's regular
profile (~/.config/libreoffice/4/user/registrymodifications.xcu) instead of the
agent
profile passed via -env:UserInstallation, when the process is killed by a
signal
(SIGABRT or SIGKILL). This violates the documented isolation of
-env:UserInstallation
and is the source of the Crashed=true state that later triggers the deadlock
above.

Environment:

  LibreOffice 26.2.5.2 (openSUSE Tumbleweed distro build; upstream TDF 26.2.5)
  VCL plugin: Qt6
  Desktop: KDE Plasma 6, Wayland session
  Python: 3.13
  OS: openSUSE Tumbleweed, Linux 7.1.8-1-default

Reproducing extension: Nelson MCP 0.12.1 (nelson-0.12.1.oxt). Upstream Nelson
issue
tracking the extension-side fix:
https://github.com/quazardous/nelson-mcp/issues/37

-- 
You are receiving this mail because:
You are the assignee for the bug.

Reply via email to