Closed oscarfv closed 4 years ago
The problem can be easily reproduced by run "org-babel-tangle-file
" to tangle some elisp code.
During doing ob-tangle, it seems that it will create a lot of temporary buffers for elisp code
blocks, and these buffers will be killed finally.
However the timer function "aggressive-indent--indent-if-changed
" can't handle this case
correctly.
The root cause is that the variable "aggressive-indent--idle-timer
" is buffer local, it means
the timer handle will be lost after the buffer is killed, and "aggressive-indent
" can't cancel
this timer anymore.
I made a bugfixing on https://github.com/leuven65/aggressive-indent-mode/commit/f611f3114b55e92a03e978cd0721b21e58523f48
The fixing is simple:
(defun aggressive-indent--on-buffer-kill ()
"Cancel the timer before buffer is killed"
(when (timerp aggressive-indent--idle-timer)
(cancel-timer aggressive-indent--idle-timer)
(setq aggressive-indent--idle-timer nil)))
(add-hook 'kill-buffer-hook #'aggressive-indent--on-buffer-kill nil t)
Bump? I noticed the same issue (thousands of dangling timers) and can confirm that #135 fixes it for me.
FYI it doesn't actually fix the bug that oscarfv reported (if the timer fires when a different buffer is current, then the timer function never reindents / removes itself). However, at least in my everyday use, the current-buffer mismatch only causes one long-lived timer (attached to the org src emacs lisp fontification buffer) which isn't that annoying. I've opened a PR for that fix.
This is what I was using since I reported the bug (still not rebased against the most recent upstream changes):
diff --git a/singles/aggressive-indent.el b/singles/aggressive-indent.el
index e4b29bb..a130c89 100644
--- a/singles/aggressive-indent.el
+++ b/singles/aggressive-indent.el
@@ -422,6 +422,16 @@ typing, try tweaking this number."
(defvar-local aggressive-indent--idle-timer nil
"Idle timer used for indentation")
+(defun aggressive-indent--cancel-timer ()
+ (if (timerp aggressive-indent--idle-timer)
+ (progn
+ (cancel-timer aggressive-indent--idle-timer)
+ (setq aggressive-indent--idle-timer nil))
+ ;; For an unknown reason, sometimes timers are not canceled and
+ ;; accumulate on the list of active timers, slowing down Emacs.
+ ;; This ensures that any timer we created is canceled:
+ (cancel-function-timers 'aggressive-indent--indent-if-changed)))
+
;; Ripped from Emacs 27.0 subr.el.
;; See Github Issue#111 and Emacs bug#31692.
(defmacro aggressive-indent--while-no-input (&rest body)
@@ -462,21 +472,19 @@ If BODY finishes, `while-no-input' returns whatever value BODY produced."
(defun aggressive-indent--indent-if-changed ()
"Indent any region that changed in the last command loop."
(if (not (buffer-live-p (current-buffer)))
- (cancel-timer aggressive-indent--idle-timer)
+ (aggressive-indent--cancel-timer)
(when (and aggressive-indent-mode aggressive-indent--changed-list)
(save-excursion
(save-selected-window
(aggressive-indent--while-no-input
(aggressive-indent--proccess-changed-list-and-indent))))
- (when (timerp aggressive-indent--idle-timer)
- (cancel-timer aggressive-indent--idle-timer)))))
+ (aggressive-indent--cancel-timer))))
(defun aggressive-indent--keep-track-of-changes (l r &rest _)
"Store the limits (L and R) of each change in the buffer."
(when aggressive-indent-mode
(push (list l r) aggressive-indent--changed-list)
- (when (timerp aggressive-indent--idle-timer)
- (cancel-timer aggressive-indent--idle-timer))
+ (aggressive-indent--cancel-timer)
(setq aggressive-indent--idle-timer
(run-with-idle-timer aggressive-indent-sit-for-time t #'aggressive-indent--indent-if-changed))))
@@ -511,8 +519,7 @@ If BODY finishes, `while-no-input' returns whatever value BODY produced."
(add-hook 'after-revert-hook #'aggressive-indent--clear-change-list nil 'local)
(add-hook 'before-save-hook #'aggressive-indent--proccess-changed-list-and-indent nil 'local))
;; Clean the hooks
- (when (timerp aggressive-indent--idle-timer)
- (cancel-timer aggressive-indent--idle-timer))
+ (aggressive-indent--cancel-timer)
(remove-hook 'after-change-functions #'aggressive-indent--keep-track-of-changes 'local)
(remove-hook 'after-revert-hook #'aggressive-indent--clear-change-list 'local)
(remove-hook 'before-save-hook #'aggressive-indent--proccess-changed-list-and-indent 'local)
Sorry for the long silence. I've merged both PRs. Let's hope that fixes the problem for good. Let me know how it works for you.
On a long-lived (24 days) Emacs session I was experiencing severe sluggishness and upon investigation M-x list-timers showed hundreds of timers created by a-g-m. Since this is an old problem that supposedly was fixed several times, now it is my turn :-)
a-g-m assumes that the current-buffer remains the same on the cycle change -> timer expiration -> indentation -> timer cancelation. The key point here is that we are using an idle timer stored on a buffer local variable. That means that no matter the delay we assign to the timer, it only will fire after Emacs is idle, and then
(current-buffer)
might have changed.For instance:
But how this happens on real practice? Do some automated buffer change process and save and kill the buffer without visiting it. That's my case, simplified: say that I want to change
foo
tobar
on all source files; for that I use some text-searching tool, such asgrep
, and thenwgrep.el
that makes thegrep
buffer editable and allows toM-x replace-string foo bar
and thenwgrep
applies the changes to the real files; finally I kill the buffers without visiting them. Now I have a dangling timer for each file that was modified.The solution involves not assuming that the current buffer remains the same along the a-g-m actuation cycle mentioned above. We can store
(current-buffer)
in the list of changes, make the list non-local (and the timer too) and then switch to the corresponding buffer to apply each change.I plan to do some experimentation with this approach as time allows but if there are comments (or patches! :-) about this in the meantime that suggests a better approach, the better.