Malabarba / aggressive-indent-mode

Emacs minor mode that keeps your code always indented. More reliable than electric-indent-mode.
850 stars 36 forks source link

aggressive-indent-mode enabled lot of timers #112

Closed stardiviner closed 5 years ago

stardiviner commented 6 years ago

When I found Emacs suddenly have high CPU usage even I did nothing. Then I try to check out Emacs idle timers with [M-x list-timers]. I toke an screenshot:

aggressive-indent lot of timers

Here is the result:

          -0.64     0.00 auto-revert-buffers
          10.17     0.00 erc-server-send-ping
          15.14     5.00 savehist-autosave
         204.97    10.00 github-notifications
        2020.43        - password-cache-remove
        2020.65    60.00 url-cookie-write-file
        2642.45        - #f(compiled-function () #<bytecode 0x336f071> [#<hash-table equal 13/65 0x1307fdd> (js2-mode :v-adjust -0.05 :height 1.0) remhash])
        2949.34        - #f(compiled-function () #<bytecode 0x339b869> [#<hash-table equal 13/65 0x1307fdd> (magit-status-mode :v-adjust -0.05 :height 1.0) remhash])
        3966.71        - #f(compiled-function () #<bytecode 0x3109225> [#<hash-table equal 13/65 0x1307fdd> (text-mode :v-adjust -0.05 :height 1.0) remhash])
        3966.72        - #f(compiled-function () #<bytecode 0x3261485> [#<hash-table equal 13/65 0x1307fdd> (magit-diff-mode :v-adjust -0.05 :height 1.0) remhash])
        4141.56        - #f(compiled-function () #<bytecode 0x3210ec9> [#<hash-table equal 13/65 0x1307fdd> (ag-mode :v-adjust -0.05 :height 1.0) remhash])
        4663.76        - #f(compiled-function () #<bytecode 0x305fc0d> [#<hash-table equal 13/65 0x1307fdd> (org-agenda-mode :v-adjust -0.05 :height 1.0) remhash])
        5141.02        - #f(compiled-function () #<bytecode 0x2a30941> [#<hash-table equal 13/65 0x1307fdd> (messages-buffer-mode :v-adjust -0.05 :height 1.0) remhash])
        5783.21        - #f(compiled-function () #<bytecode 0x3a9b44d> [#<hash-table equal 13/65 0x1307fdd> (markdown-mode :v-adjust -0.05 :height 1.0) remhash])
        5831.10        - #f(compiled-function () #<bytecode 0x33f99cd> [#<hash-table equal 13/65 0x1307fdd> (emacs-lisp-mode :v-adjust -0.05 :height 1.0) remhash])
        6036.17        - password-cache-remove
        6048.07        - password-cache-remove
        6813.20        - #f(compiled-function () #<bytecode 0x36cdcad> [#<hash-table equal 13/65 0x1307fdd> (fundamental-mode :v-adjust -0.05 :height 1.0) remhash])
        7170.64        - #f(compiled-function () #<bytecode 0x31e7649> [#<hash-table equal 13/65 0x1307fdd> (erc-mode :v-adjust -0.05 :height 1.0) remhash])
        7186.84        - #f(compiled-function () #<bytecode 0x46bc0c1> [#<hash-table equal 13/65 0x1307fdd> (org-mode :v-adjust -0.05 :height 1.0) remhash])
        7186.85        - #f(compiled-function () #<bytecode 0x3bae89d> [#<hash-table equal 13/65 0x1307fdd> (help-mode :v-adjust -0.05 :height 1.0) remhash])
       24592.65  1440.00 circadian-enable-theme
       70192.65  1440.00 circadian-enable-theme
   *       0.10        t aggressive-indent--indent-if-changed
   *       0.10        t aggressive-indent--indent-if-changed
   *       0.10        t aggressive-indent--indent-if-changed
   *       0.10        t aggressive-indent--indent-if-changed
   *       0.10        t aggressive-indent--indent-if-changed
   *       0.10        t aggressive-indent--indent-if-changed
   *       0.10        t aggressive-indent--indent-if-changed
   *       0.10        t aggressive-indent--indent-if-changed
   *       0.10        t aggressive-indent--indent-if-changed
   *       0.10        t aggressive-indent--indent-if-changed
   *       0.10        t aggressive-indent--indent-if-changed
   *       0.10        t aggressive-indent--indent-if-changed
   *       0.10        t aggressive-indent--indent-if-changed
   *       0.10        t aggressive-indent--indent-if-changed
   *       0.10        t aggressive-indent--indent-if-changed
   *       0.10        t aggressive-indent--indent-if-changed
   *       0.40        t which-key--update
   *       0.50        t jit-lock-context-fontify
   *       1.00   repeat ess--idle-timer-function
   *       1.00        t symbol-overlay-maybe-put-temp
   *       2.50        t tern-reparse-on-idle
   *      30.00        t js-gc

I found aggressive-indent-mode created so many timers. Why? Is it normal? Here is my config:

(use-package aggressive-indent
  :ensure t
  :defer t
  :init
  (add-hook 'prog-mode-hook #'aggressive-indent-mode)
  :config
  (setq aggressive-indent-sit-for-time 0.1)

  ;; The variable `aggressive-indent-dont-indent-if' lets you customize when you
  ;; **don't** want indentation to happen.  For instance, if you think it's
  ;; annoying that lines jump around in `c++-mode' because you haven't typed the
  ;; `;' yet, you could add the following clause:
  (add-to-list 'aggressive-indent-dont-indent-if
               '(and (derived-mode-p 'c++-mode)
                     (null (string-match "\\([;{}]\\|\\b\\(if\\|for\\|while\\)\\b\\)"
                                         (thing-at-point 'line)))))

  (add-to-list 'aggressive-indent-excluded-modes 'python-mode)
  (add-to-list 'aggressive-indent-excluded-modes 'haskell-mode)
  (add-to-list 'aggressive-indent-excluded-modes 'lua-mode)
  (add-to-list 'aggressive-indent-excluded-modes 'makefile-mode)
  (add-to-list 'aggressive-indent-excluded-modes 'coq-mode)
  (add-to-list 'aggressive-indent-excluded-modes 'snippet-mode)
  )
Malabarba commented 6 years ago

It's supposed to define a single timer which get's repeatedly run a few times per second. Are you sure you're seeing many different timers? Or could it be a single timer running many times?

Also, it's worth mentioning that aggressive-indent doesn't seem to be the cause for your slow-downs according to your profiling (though maybe you already knew that).

stardiviner commented 6 years ago

Yes, about the profiling result, I already know aggressive-indent is not the cause.

From the screenshot, I can't figure out it is different timers or single timer running many times. It cloud be many timers on many different buffers.

kqr commented 5 years ago

I don't know if this is related but I'll put it here since I have so very little to add:

After having run my Emacs continuously for a couple of weeks, opening somewhere around 10–50 files each day, and continuously cleaning up my open buffers, I noticed interaction became really slow. I looked into the CPU profiler report, and cancel-timer-internal consumed nearly 75% of the time.

I don't actually know whether this is aggressive-indent leaking timers, because my way of trying to see where the timers came from was to insert (error) into cancel-timer-internal and look at the stack trace. The first error came right away, and belonged to aggressive-indent-mode. Then Emacs locked up over all the errors it had to handle, so I had to restart it and the problem went away.

This is very unreliable information, but in the unlikely case it helps someone, there it is.

Malabarba commented 5 years ago

I'm not 100% sure whats going on here. Aggressive-indent does create a separate idle timer for each buffer, but this timer is canceled as soon as its function finishes running. So they shouldn't stick around for more than a second.

Maybe it's a consequence of the buffer being deleted, then the idle timer keeps trying to run, but never actually finishes, so it never gets canceled.

I suppose using just a single global timer might help. I've pushed a commit doing that now, so we can test on melpa unstable. Let me know if it works for y'all.

stardiviner commented 5 years ago

Maybe it's a consequence of the buffer being deleted, then the idle timer keeps trying to run, but never actually finishes, so it never gets canceled.

I think this is very possible. I will try out the latest and report here.

Seems MELPA not fetched the updated aggressive-indent yet. check it out later.

kommen commented 5 years ago

Using the latest aggressive-indent-mode I can report I don't seem to have experienced any regressions from that change.

joaotavora commented 5 years ago

There is a regression from that change as evidenced in #121.

joaotavora commented 5 years ago

Maybe it's a consequence of the buffer being deleted, then the idle timer keeps trying to run, but never actually finishes, so it never gets canceled.

If you think that's the case, why don't you do this instead?

diff --git a/aggressive-indent.el b/aggressive-indent.el
index da21bfe..ea8e9c1 100644
--- a/aggressive-indent.el
+++ b/aggressive-indent.el
@@ -417,6 +417,8 @@ typing, try tweaking this number."

 (defun aggressive-indent--indent-if-changed ()
   "Indent any region that changed in the last command loop."
+  (unless (buffer-live-p (current-buffer))
+    (cancel-timer aggressive-indent--idle-timer)
   (when (and aggressive-indent-mode aggressive-indent--changed-list)
     (save-excursion
       (save-selected-window
Malabarba commented 5 years ago

Alright, I'm trying an alternative version of that. Feedback is appreciated, specially with regards to whether it breaks this issue again.

yuhan0 commented 5 years ago

I'm on the latest version but this issue still keeps cropping up now and again, usually I notice it when lag starts increasing a lot, opening list-timers and finding that there are several hundred aggressive-indent--indent-if-changed entries.

Here's a quick hack I came up with to automate that process and monitor when it happens, I have a hunch that it's related to evil-mc but can't reliably reproduce the context to trigger it.

(defun cancel-aggressive-indent-timers ()
  (interactive)
  (let ((count 0))
    (dolist (timer timer-idle-list)
      (when (eq 'aggressive-indent--indent-if-changed (aref timer 5))
        (incf count)
        (cancel-timer timer)))
    (when (> count 0)
      (message "Cancelled %s aggressive-indent timers" count)))
  (run-with-timer 60 nil 'cancel-aggressive-indent-timers))
kqr commented 5 years ago

@yuhan0 Are you sure you meant

(run-with-timer 60 nil 'cancel-aggressive-indent-timers)

and not

(run-with-timer 60 60 'cancel-aggressive-indent-timers)

(I experienced this issue again, now, and started wondering why running cancel-aggressive-indent-timers manually canceled 3000 timers – and I had never seen it running automatically!)