aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMattias EngdegÄrd2019-05-09 09:40:46 +0200
committerMattias EngdegÄrd2019-05-20 11:26:51 +0200
commite9f9827eb01a382bead6c180f6703322167e6f89 (patch)
tree497784bbe60ffcd6985ea785481a5d382862e509
parent2bf0b3bf6183c1da9b5dee789c7aa3241ca93441 (diff)
downloademacs-e9f9827eb01a382bead6c180f6703322167e6f89.tar.gz
emacs-e9f9827eb01a382bead6c180f6703322167e6f89.zip
Avoid polling in global-auto-revert-mode (bug#35418)
Make `auto-revert-avoid-polling' have effect in global-auto-revert-mode. Buffers actually handled by that mode are marked with a non-nil value of `auto-revert--global-mode'. When global-auto-revert-mode is entered, eligible buffers are marked in that way, and hooks are set up to mark new buffers and take care of buffers whose file names change. This way the existing poll-avoidance logic can be used, since the entire set of buffers in auto-revert is known. A new hook, `after-set-visited-file-name-hook', was added to handle the case when the file name of a tracked buffer changes. * lisp/autorevert.el (auto-revert-avoid-polling): Amend doc string. (auto-revert--global-mode): New buffer-local variable. (global-auto-revert-mode): Mark existing buffers and set up hooks when mode is entered; do the opposite when exited. (auto-revert--global-add-current-buffer) (auto-revert--global-adopt-current-buffer) (auto-revert--set-visited-file-name-advice): New functions. (auto-revert--polled-buffers, auto-revert--need-polling-p) (auto-revert-notify-handler) (auto-revert-active-p): Modify logic to cover global-auto-revert-mode. * lisp/files.el (after-set-visited-file-name-hook): New hook. (set-visited-file-name-hook): Call new hook. * test/lisp/autorevert-tests.el (top): Use lexical-binding. (auto-revert-test--write-file, auto-revert-test--buffer-string) (auto-revert-test--wait-for, auto-revert-test--wait-for-buffer-text) (auto-revert-test05-global-notify): New test. * doc/lispref/hooks.texi (Standard Hooks): Mention new hook (in a comment, since it's unclear whether it should actually be documented here) * etc/NEWS (Changes in Specialized Modes and Packages): Update entry.
-rw-r--r--doc/lispref/hooks.texi1
-rw-r--r--etc/NEWS3
-rw-r--r--lisp/autorevert.el124
-rw-r--r--lisp/files.el6
-rw-r--r--test/lisp/autorevert-tests.el107
5 files changed, 210 insertions, 31 deletions
diff --git a/doc/lispref/hooks.texi b/doc/lispref/hooks.texi
index 71992464e09..f775aa4d4b0 100644
--- a/doc/lispref/hooks.texi
+++ b/doc/lispref/hooks.texi
@@ -251,6 +251,7 @@ I thought did not need to be mentioned here:
251 251
252Lisp: 252Lisp:
253after-load-functions 253after-load-functions
254after-set-visited-file-name-hook
254auto-coding-functions 255auto-coding-functions
255choose-completion-string-functions 256choose-completion-string-functions
256completing-read-function 257completing-read-function
diff --git a/etc/NEWS b/etc/NEWS
index d70cda179e0..9ca98c370e6 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -1443,8 +1443,7 @@ When set to a non-nil value, buffers in Auto Revert mode are no longer
1443polled for changes periodically. This reduces the power consumption 1443polled for changes periodically. This reduces the power consumption
1444of an idle Emacs, but may fail on some network file systems; set 1444of an idle Emacs, but may fail on some network file systems; set
1445'auto-revert-notify-exclude-dir-regexp' to match files where 1445'auto-revert-notify-exclude-dir-regexp' to match files where
1446notification is not supported. The new variable currently has no 1446notification is not supported. The default value is nil.
1447effect in 'global-auto-revert-mode'. The default value is nil.
1448 1447
1449*** New variable 'buffer-auto-revert-by-notification' 1448*** New variable 'buffer-auto-revert-by-notification'
1450A major mode can declare that notification on the buffer's default 1449A major mode can declare that notification on the buffer's default
diff --git a/lisp/autorevert.el b/lisp/autorevert.el
index 197a2bf1578..2de855b303e 100644
--- a/lisp/autorevert.el
+++ b/lisp/autorevert.el
@@ -312,10 +312,7 @@ when those files are modified from another computer.
312 312
313When nil, buffers in Auto-Revert Mode will always be polled for 313When nil, buffers in Auto-Revert Mode will always be polled for
314changes to their files on disk every `auto-revert-interval' 314changes to their files on disk every `auto-revert-interval'
315seconds, in addition to using notification for those files. 315seconds, in addition to using notification for those files."
316
317In Global Auto-Revert Mode, polling is always done regardless of
318the value of this variable."
319 :group 'auto-revert 316 :group 'auto-revert
320 :type 'boolean 317 :type 'boolean
321 :set (lambda (variable value) 318 :set (lambda (variable value)
@@ -335,6 +332,9 @@ buffers to this list.
335The timer function `auto-revert-buffers' is responsible for purging 332The timer function `auto-revert-buffers' is responsible for purging
336the list of old buffers.") 333the list of old buffers.")
337 334
335(defvar-local auto-revert--global-mode nil
336 "Non-nil if buffer is handled by Global Auto-Revert mode.")
337
338(defvar auto-revert-remaining-buffers () 338(defvar auto-revert-remaining-buffers ()
339 "Buffers not checked when user input stopped execution.") 339 "Buffers not checked when user input stopped execution.")
340 340
@@ -501,34 +501,107 @@ specifies in the mode line."
501 :global t :group 'auto-revert :lighter global-auto-revert-mode-text 501 :global t :group 'auto-revert :lighter global-auto-revert-mode-text
502 (auto-revert-set-timer) 502 (auto-revert-set-timer)
503 (if global-auto-revert-mode 503 (if global-auto-revert-mode
504 (auto-revert-buffers) 504 ;; Turn global-auto-revert-mode ON.
505 (progn
506 (dolist (buf (buffer-list))
507 (with-current-buffer buf
508 (auto-revert--global-add-current-buffer)))
509 ;; Make sure future buffers are added as well.
510 (add-hook 'find-file-hook #'auto-revert--global-adopt-current-buffer)
511 (add-hook 'after-set-visited-file-name-hook
512 #'auto-revert--global-set-visited-file-name)
513 ;; To track non-file buffers, we need to listen in to buffer
514 ;; creation in general. Listening to major-mode changes is
515 ;; suitable, since we then know whether it's a mode that is tracked.
516 (when global-auto-revert-non-file-buffers
517 (add-hook 'after-change-major-mode-hook
518 #'auto-revert--global-adopt-current-buffer))
519 (auto-revert-buffers))
520 ;; Turn global-auto-revert-mode OFF.
521 (remove-hook 'after-change-major-mode-hook
522 #'auto-revert--global-adopt-current-buffer)
523 (remove-hook 'after-set-visited-file-name-hook
524 #'auto-revert--global-set-visited-file-name)
525 (remove-hook 'find-file-hook #'auto-revert--global-adopt-current-buffer)
505 (dolist (buf (buffer-list)) 526 (dolist (buf (buffer-list))
506 (with-current-buffer buf 527 (with-current-buffer buf
507 (when (and auto-revert-notify-watch-descriptor 528 (when auto-revert--global-mode
508 (not (memq buf auto-revert-buffer-list))) 529 (setq auto-revert--global-mode nil)
509 (auto-revert-notify-rm-watch)))))) 530 (when (and auto-revert-notify-watch-descriptor
531 (not (or auto-revert-mode auto-revert-tail-mode)))
532 (auto-revert-notify-rm-watch)))))))
533
534(defun auto-revert--global-add-current-buffer ()
535 "Set current buffer to be tracked by Global Auto-Revert if appropriate."
536 (when (and (not auto-revert--global-mode)
537 (or buffer-file-name
538 (and global-auto-revert-non-file-buffers
539 (not (string-prefix-p " " (buffer-name)))
540 ;; Any non-file buffer must have a custom
541 ;; `buffer-stale-function' to be tracked, since
542 ;; we wouldn't know when to revert it otherwise.
543 (not (eq buffer-stale-function
544 #'buffer-stale--default-function))))
545 (not (memq 'major-mode global-auto-revert-ignore-modes))
546 (not global-auto-revert-ignore-buffer))
547 (setq auto-revert--global-mode t)))
548
549(defun auto-revert--global-adopt-current-buffer ()
550 "Consider tracking current buffer in a running Global Auto-Revert mode."
551 (auto-revert--global-add-current-buffer)
552 (auto-revert-set-timer))
553
554(defun auto-revert--global-set-visited-file-name ()
555 "Update Global Auto-Revert management of the current buffer.
556Called after `set-visited-file-name'."
557 ;; Remove any existing notifier first so that we don't track the
558 ;; wrong file in case the file name was changed.
559 (when auto-revert-notify-watch-descriptor
560 (auto-revert-notify-rm-watch))
561 (auto-revert--global-adopt-current-buffer))
510 562
511(defun auto-revert--polled-buffers () 563(defun auto-revert--polled-buffers ()
512 "List of buffers that need to be polled." 564 "List of buffers that need to be polled."
513 (cond (global-auto-revert-mode (buffer-list)) 565 (cond (global-auto-revert-mode
566 (mapcan (lambda (buffer)
567 (and (not (and auto-revert-avoid-polling
568 (buffer-local-value
569 'auto-revert-notify-watch-descriptor
570 buffer)))
571 (or (buffer-local-value
572 'auto-revert--global-mode buffer)
573 (buffer-local-value 'auto-revert-mode buffer)
574 (buffer-local-value 'auto-revert-tail-mode buffer))
575 (list buffer)))
576 (buffer-list)))
514 (auto-revert-avoid-polling 577 (auto-revert-avoid-polling
515 (mapcan (lambda (buffer) 578 (mapcan (lambda (buffer)
516 (and (not (buffer-local-value 579 (and (not (buffer-local-value
517 'auto-revert-notify-watch-descriptor buffer)) 580 'auto-revert-notify-watch-descriptor buffer))
518 (list buffer))) 581 (list buffer)))
519 auto-revert-buffer-list)) 582 auto-revert-buffer-list))
520 (t auto-revert-buffer-list))) 583 (t auto-revert-buffer-list)))
521 584
522;; Same as above in a boolean context, but cheaper. 585;; Same as above in a boolean context, but cheaper.
523(defun auto-revert--need-polling-p () 586(defun auto-revert--need-polling-p ()
524 "Whether periodic polling is required." 587 "Whether periodic polling is required."
525 (or global-auto-revert-mode 588 (cond (global-auto-revert-mode
526 (if auto-revert-avoid-polling 589 (or (not auto-revert-avoid-polling)
527 (not (cl-every (lambda (buffer) 590 (cl-some
528 (buffer-local-value 591 (lambda (buffer)
529 'auto-revert-notify-watch-descriptor buffer)) 592 (and (not (buffer-local-value
530 auto-revert-buffer-list)) 593 'auto-revert-notify-watch-descriptor buffer))
531 auto-revert-buffer-list))) 594 (or (buffer-local-value 'auto-revert--global-mode buffer)
595 (buffer-local-value 'auto-revert-mode buffer)
596 (buffer-local-value 'auto-revert-tail-mode buffer))))
597 (buffer-list))))
598 (auto-revert-avoid-polling
599 (not (cl-every
600 (lambda (buffer)
601 (buffer-local-value
602 'auto-revert-notify-watch-descriptor buffer))
603 auto-revert-buffer-list)))
604 (t auto-revert-buffer-list)))
532 605
533(defun auto-revert-set-timer () 606(defun auto-revert-set-timer ()
534 "Restart or cancel the timer used by Auto-Revert Mode. 607 "Restart or cancel the timer used by Auto-Revert Mode.
@@ -652,9 +725,8 @@ system.")
652 (null buffer-file-name)) 725 (null buffer-file-name))
653 (auto-revert-notify-rm-watch) 726 (auto-revert-notify-rm-watch)
654 ;; Restart the timer if it wasn't running. 727 ;; Restart the timer if it wasn't running.
655 (when (and (memq buffer auto-revert-buffer-list) 728 (unless auto-revert-timer)
656 (not auto-revert-timer)) 729 (auto-revert-set-timer))))
657 (auto-revert-set-timer)))))
658 730
659 ;; Loop over all buffers, in order to find the intended one. 731 ;; Loop over all buffers, in order to find the intended one.
660 (cl-dolist (buffer buffers) 732 (cl-dolist (buffer buffers)
@@ -697,12 +769,10 @@ If the buffer needs to be reverted, do it now."
697 (auto-revert-handler))))) 769 (auto-revert-handler)))))
698 770
699(defun auto-revert-active-p () 771(defun auto-revert-active-p ()
700 "Check if auto-revert is active (in current buffer or globally)." 772 "Check if auto-revert is active in current buffer."
701 (or auto-revert-mode 773 (or auto-revert-mode
702 auto-revert-tail-mode 774 auto-revert-tail-mode
703 (and global-auto-revert-mode 775 auto-revert--global-mode))
704 (not global-auto-revert-ignore-buffer)
705 (not (memq major-mode global-auto-revert-ignore-modes)))))
706 776
707(defun auto-revert-handler () 777(defun auto-revert-handler ()
708 "Revert current buffer, if appropriate. 778 "Revert current buffer, if appropriate.
diff --git a/lisp/files.el b/lisp/files.el
index 21fa6143e3d..989d1cb4658 100644
--- a/lisp/files.el
+++ b/lisp/files.el
@@ -4268,6 +4268,9 @@ However, the mode will not be changed if
4268 :type 'boolean 4268 :type 'boolean
4269 :group 'editing-basics) 4269 :group 'editing-basics)
4270 4270
4271(defvar after-set-visited-file-name-hook nil
4272 "Normal hook run just after setting visited file name of current buffer.")
4273
4271(defun set-visited-file-name (filename &optional no-query along-with-file) 4274(defun set-visited-file-name (filename &optional no-query along-with-file)
4272 "Change name of file visited in current buffer to FILENAME. 4275 "Change name of file visited in current buffer to FILENAME.
4273This also renames the buffer to correspond to the new file. 4276This also renames the buffer to correspond to the new file.
@@ -4388,7 +4391,8 @@ the old visited file has been renamed to the new name FILENAME."
4388 (set-auto-mode t) 4391 (set-auto-mode t)
4389 (or (eq old major-mode) 4392 (or (eq old major-mode)
4390 (hack-local-variables)))) 4393 (hack-local-variables))))
4391 (error nil)))) 4394 (error nil))
4395 (run-hooks 'after-set-visited-file-name-hook)))
4392 4396
4393(defun write-file (filename &optional confirm) 4397(defun write-file (filename &optional confirm)
4394 "Write current buffer into file FILENAME. 4398 "Write current buffer into file FILENAME.
diff --git a/test/lisp/autorevert-tests.el b/test/lisp/autorevert-tests.el
index 040dbb45a91..5024a2daf0a 100644
--- a/test/lisp/autorevert-tests.el
+++ b/test/lisp/autorevert-tests.el
@@ -1,4 +1,4 @@
1;;; auto-revert-tests.el --- Tests of auto-revert 1;;; auto-revert-tests.el --- Tests of auto-revert -*- lexical-binding: t -*-
2 2
3;; Copyright (C) 2015-2019 Free Software Foundation, Inc. 3;; Copyright (C) 2015-2019 Free Software Foundation, Inc.
4 4
@@ -436,6 +436,111 @@ This expects `auto-revert--messages' to be bound by
436(auto-revert--deftest-remote auto-revert-test04-auto-revert-mode-dired 436(auto-revert--deftest-remote auto-revert-test04-auto-revert-mode-dired
437 "Check remote autorevert for dired.") 437 "Check remote autorevert for dired.")
438 438
439(defun auto-revert-test--write-file (string file)
440 "Write STRING to FILE."
441 (write-region string nil file nil 'no-message))
442
443(defun auto-revert-test--buffer-string (buffer)
444 "Contents of BUFFER as a string."
445 (with-current-buffer buffer
446 (buffer-string)))
447
448(defun auto-revert-test--wait-for (pred max-wait)
449 "Wait until PRED is true, or MAX-WAIT seconds elapsed."
450 (let ((ct (current-time)))
451 (while (and (< (float-time (time-subtract (current-time) ct)) max-wait)
452 (not (funcall pred)))
453 (read-event nil nil 0.1))))
454
455(defun auto-revert-test--wait-for-buffer-text (buffer string max-wait)
456 "Wait until BUFFER has the contents STRING, or MAX-WAIT seconds elapsed."
457 (auto-revert-test--wait-for
458 (lambda () (string-equal (auto-revert-test--buffer-string buffer) string))
459 max-wait))
460
461(ert-deftest auto-revert-test05-global-notify ()
462 "Test `global-auto-revert-mode' without polling."
463 :tags '(:expensive-test)
464 (skip-unless (or file-notify--library
465 (file-remote-p temporary-file-directory)))
466 (let* ((auto-revert-use-notify t)
467 (auto-revert-avoid-polling t)
468 (was-in-global-auto-revert-mode global-auto-revert-mode)
469 (file-1 (make-temp-file "global-auto-revert-test-1"))
470 (file-2 (make-temp-file "global-auto-revert-test-2"))
471 (file-3 (make-temp-file "global-auto-revert-test-3"))
472 (file-2b (concat file-2 "-b"))
473 buf-1 buf-2 buf-3)
474 (unwind-protect
475 (progn
476 (setq buf-1 (find-file-noselect file-1))
477 (setq buf-2 (find-file-noselect file-2))
478 (auto-revert-test--write-file "1-a" file-1)
479 (should (equal (auto-revert-test--buffer-string buf-1) ""))
480
481 (global-auto-revert-mode 1) ; Turn it on.
482
483 (should (buffer-local-value
484 'auto-revert-notify-watch-descriptor buf-1))
485 (should (buffer-local-value
486 'auto-revert-notify-watch-descriptor buf-2))
487
488 ;; buf-1 should have been reverted immediately when the mode
489 ;; was enabled.
490 (should (equal (auto-revert-test--buffer-string buf-1) "1-a"))
491
492 ;; Alter a file.
493 (auto-revert-test--write-file "2-a" file-2)
494 ;; Allow for some time to handle notification events.
495 (auto-revert-test--wait-for-buffer-text buf-2 "2-a" 1)
496 (should (equal (auto-revert-test--buffer-string buf-2) "2-a"))
497
498 ;; Visit a file, and modify it on disk.
499 (setq buf-3 (find-file-noselect file-3))
500 ;; Newly opened buffers won't be use notification until the
501 ;; first poll cycle; wait for it.
502 (auto-revert-test--wait-for
503 (lambda () (buffer-local-value
504 'auto-revert-notify-watch-descriptor buf-3))
505 (+ auto-revert-interval 1))
506 (should (buffer-local-value
507 'auto-revert-notify-watch-descriptor buf-3))
508 (auto-revert-test--write-file "3-a" file-3)
509 (auto-revert-test--wait-for-buffer-text buf-3 "3-a" 1)
510 (should (equal (auto-revert-test--buffer-string buf-3) "3-a"))
511
512 ;; Delete a visited file, and re-create it with new contents.
513 (delete-file file-1)
514 (sleep-for 0.5)
515 (should (equal (auto-revert-test--buffer-string buf-1) "1-a"))
516 (auto-revert-test--write-file "1-b" file-1)
517 (auto-revert-test--wait-for-buffer-text buf-1 "1-b"
518 (+ auto-revert-interval 1))
519 (should (buffer-local-value
520 'auto-revert-notify-watch-descriptor buf-1))
521
522 ;; Write a buffer to a new file, then modify the new file on disk.
523 (with-current-buffer buf-2
524 (write-file file-2b))
525 (should (equal (auto-revert-test--buffer-string buf-2) "2-a"))
526 (auto-revert-test--write-file "2-b" file-2b)
527 (auto-revert-test--wait-for-buffer-text buf-2 "2-b"
528 (+ auto-revert-interval 1))
529 (should (buffer-local-value
530 'auto-revert-notify-watch-descriptor buf-2)))
531
532 ;; Clean up.
533 (unless was-in-global-auto-revert-mode
534 (global-auto-revert-mode 0)) ; Turn it off.
535 (dolist (buf (list buf-1 buf-2 buf-3))
536 (ignore-errors (kill-buffer buf)))
537 (dolist (file (list file-1 file-2 file-2b file-3))
538 (ignore-errors (delete-file file)))
539 )))
540
541(auto-revert--deftest-remote auto-revert-test04-auto-revert-mode-dired
542 "Test `global-auto-revert-mode' without polling for remote buffers.")
543
439(defun auto-revert-test-all (&optional interactive) 544(defun auto-revert-test-all (&optional interactive)
440 "Run all tests for \\[auto-revert]." 545 "Run all tests for \\[auto-revert]."
441 (interactive "p") 546 (interactive "p")