Hi everyone,

I noticed my emacs started to lag after a few days and it turned out there were over 2000 `org-indent-initialize-agent' timers in `timer-idle-list'.
That caused a delay of .45 secs on every .2s idle pause.

The patch attached cancels the timer if the `org-indent-agentized-buffers' list is empty and there is a timer. The code used to take an empty list as meaning there was no agent running, but the list empties as soon as a buffer finishes initializing or turns the mode off, while the agent only cancels itself on its next run. A buffer enabling the mode in between starts a second timer and overwrites `org-indent-agent-timer'; the old timer later cancels the new one through that variable and keeps running, unreferenced.


Reproducing:

#+begin_src emacs-lisp
(require 'org-indent)
 (dotimes (_ 3)
   (with-current-buffer (generate-new-buffer "x")
     (insert "* H\nText\n")
     (org-mode)
     (org-indent-mode 1)
     (org-indent-initialize-agent)))
 (seq-count (lambda (tm)
              (eq (timer--function tm)
                  #'org-indent-initialize-agent))
            timer-idle-list)
 ;; => 3, expected 1
#+end_src

The patch could be better by moving the duplicated push above the `if', with the test becoming whether the list was empty before it:

#+begin_src emacs-lisp
(push (current-buffer) org-indent-agentized-buffers)
 (unless (cdr org-indent-agentized-buffers)
   (when (timerp org-indent-agent-timer)
     (cancel-timer org-indent-agent-timer))
   (setq org-indent-agent-timer
         (run-with-idle-timer
          0.2 t #'org-indent-initialize-agent)))
#+end_src

That reads cleaner, but makes the diff a restructure rather than an addition, so I kept the patch minimal.

Org main f46d8a23a, GNU Emacs 32.0.50.


Marcel

>From 2b12b6fec412d72e7f5f2901d8785cce1ae6b092 Mon Sep 17 00:00:00 2001
From: Marcel van der Boom <[email protected]>
Date: Mon, 2 Nov 2026 15:37:01 +0100
Subject: [PATCH] org-indent-mode: Do not orphan a still scheduled agent timer

* lisp/org-indent.el (org-indent-mode): Cancel a live
`org-indent-agent-timer' before starting a new one.

An empty `org-indent-agentized-buffers' does not mean the agent is
gone: it only cancels itself on its next run.  A buffer enabling
`org-indent-mode' in between started a second timer and overwrote the
variable.  The old timer then cancelled the new one through that
variable and kept running, unreferenced.  Opening many Org buffers
programmatically accumulated 2109 such timers over six days, costing
about 0.45 s of every idle pause and making typing lag.
---
 lisp/org-indent.el | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/lisp/org-indent.el b/lisp/org-indent.el
index b6284da15..66708bcfc 100644
--- a/lisp/org-indent.el
+++ b/lisp/org-indent.el
@@ -204,6 +204,9 @@ define-minor-mode org-indent-mode
     (if org-indent-agentized-buffers
 	(push (current-buffer) org-indent-agentized-buffers)
       (push (current-buffer) org-indent-agentized-buffers)
+      ;; an empty list is not equal to agent gone!
+      (when (timerp org-indent-agent-timer)
+        (cancel-timer org-indent-agent-timer))
       (setq org-indent-agent-timer
 	    (run-with-idle-timer 0.2 t #'org-indent-initialize-agent))))
    (t
-- 
2.55.0

Reply via email to