** Description changed:

  [ Impact ]
  
  Corporate users of the Evolution mail client are running into the
  program getting 'stuck' when performing refreshes on many folders at
  once.
  
  This occurs because `glib2.0`'s `GTask` is prone to livelocking,
  becoming unable to grow the thread pool when it is exhausted. Evolution
  can quickly occupy every thread in the pool on refresh operations,
  triggering the issue.
  
  A fix has been committed upstream that provisions an extra thread in the
  pool, enabling an existing underlying watchdog to always be able to grow
  the pool when needed.
  
  [ Test plan ]
  
  A vibe-coded reproducer program (`gtask-pool-latch-repro.c`) is attached
  to this bug report. The test plan involves compiling and running it to
  observe its output.
  
  # Download dependencies
  0. Get a fresh Noble machine
  1. sudo apt install libglib2.0-dev build-essential curl
  2. curl https://launchpadlibrarian.net/875318806/gtask-pool-latch-repro.c 
--remote-name
  
  # Build and run reproducer
  3. gcc -O0 -g -o gtask-pool-latch-repro gtask-pool-latch-repro.c $(pkg-config 
--cflags --libs gio-2.0)
  4. ./gtask-pool-latch-repro
  
  # Reproducer output
  5a. Unpatched glib2.0 reproducer output (fail)
  ```
  ubuntu@noble-vm:~/glibrepro$ ./gtask-pool-latch-repro
  glib 2.80.0 (compiled against 2.80.0) mode=DEADLOCK
  16 tasks running (pool grew via the watchdog)
    ... inner sub-task still queued after 5s (gtask.c promises pool growth 
within ~1s)
    ... inner sub-task still queued after 10s (gtask.c promises pool growth 
within ~1s)
    ... inner sub-task still queued after 15s (gtask.c promises pool growth 
within ~1s)
    ... inner sub-task still queued after 20s (gtask.c promises pool growth 
within ~1s)
    ... inner sub-task still queued after 25s (gtask.c promises pool growth 
within ~1s)
    ... inner sub-task still queued after 30s (gtask.c promises pool growth 
within ~1s)
  DEADLOCK: inner sub-task never started after 30s.
  The GTask pool watchdog is disarmed (it only arms when a task
  starts, never when one is queued) - the queued sub-task can only
  run if one of the 16 blocked tasks completes, and the task they
  are all blocked on is the queued one.
  ubuntu@noble-vm:~/glibrepro$
  ```
  
  5b. Patched glib2.0 reproducer output (success)
  ```
  ubuntu@noble-vm:~/glibrepro$ ./gtask-pool-latch-repro
  glib 2.80.0 (compiled against 2.80.0) mode=DEADLOCK
  16 tasks running (pool grew via the watchdog)
  inner sub-task RAN (~1000 ms after push): pool recovered [bug NOT reproduced 
this run]
  ubuntu@noble-vm:~/glibrepro$
  ```
  
  [ Where problems could occur ]
  
  This patch modifies the cleanup path of a GTask. Regression risks
  include introducing new memory leaks or other memory errors such as use-
- after-free vulnerabilities.
+ after-free vulnerabilities. In other words, resources that were being
+ cleaned up correctly would now fail to be cleaned up correctly in the
+ case of a regression.
  
- However, the patch has been merged upstream for over 9 months and no
- regressions have since been reported.
+ The patch has been merged upstream for over 9 months. No regressions
+ have since been reported upstream or in newer releases of Ubuntu.
  
  [ Other info ]
  
  Upstream commit: https://gitlab.gnome.org/GNOME/glib/-/commit/7e30869
  Upstream bug report: https://gitlab.gnome.org/GNOME/glib/-/work_items/3840
  
  Thank you Benjamin Herrenschmidt for your work on this bug report!
  
  [ Original bug ]
  
  Please consider backporting
  https://gitlab.gnome.org/GNOME/glib/-/commit/7e30869 to Ubuntu 24.04
  glib2
  
  I finally tracked down why evolution (with EWS backend in my case) ends
  up with a bunch of deadlocked jobs making no forward progress when it
  tries to refresh too many folders at once.
  
  It boils down to evo exhausing glib2's GTask pool, and the underlying
  GTask watchdog for growing the pool not working properly. The above
  upstream commit fixes the latter.
  
  Here's a more detailed description from my artifical buddy:
  
  Evolution's mail sync jobs can occupy every thread in GIO's shared GTask
  pool while blocked on a common connection lock; the lock holder then
  performs a synchronous TLS handshake (glib-networking's
  g_tls_connection_base_handshake()), whose handshake worker is itself
  queued as a GTask on that same exhausted pool — and because gtask.c's
  pool-growing watchdog is left disarmed at that point (glib 2.80's
  g_task_thread_cleanup() shrinks the pool's max threads on every
  completion and nothing re-arms the watchdog when a task is merely
  queued), the handshake task never runs, deadlocking the account
  permanently; fixed upstream by commit 7e30869 ("gtask: fix
  g_task_run_in_thread", issue #3840), which keeps one thread of headroom,
  but that fix is absent from the glib-2-80 series shipped in noble.
  
  ProblemType: Bug
  DistroRelease: Ubuntu 24.04
  Package: libglib2.0-0t64 2.80.0-6ubuntu3.8
  ProcVersionSignature: Ubuntu 7.0.0-28.28~24.04.1-generic 7.0.12
  Uname: Linux 7.0.0-28-generic x86_64
  ApportVersion: 2.28.3-0ubuntu0.1
  Architecture: amd64
  CasperMD5CheckResult: unknown
  CurrentDesktop: ubuntu:GNOME
  Date: Mon Aug 31 14:44:13 2026
  ProcEnviron:
   LANG=en_US.UTF-8
   PATH=(custom, no user)
   SHELL=/bin/bash
   TERM=xterm-256color
   XDG_RUNTIME_DIR=<set>
  RebootRequiredPkgs: Error: path contained symlinks.
  SourcePackage: glib2.0
  UpgradeStatus: No upgrade log present (probably fresh install)

-- 
You received this bug notification because you are a member of Ubuntu
Desktop Bugs, which is subscribed to glib2.0 in Ubuntu.
https://bugs.launchpad.net/bugs/2165869

Title:
  glib2 GTask bug causes livelocks in Evolution (and others)

To manage notifications about this bug go to:
https://bugs.launchpad.net/glib/+bug/2165869/+subscriptions


-- 
desktop-bugs mailing list
[email protected]
https://lists.ubuntu.com/mailman/listinfo/desktop-bugs

Reply via email to