aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorStefan Monnier2022-02-22 10:18:43 -0500
committerStefan Monnier2022-02-22 10:18:43 -0500
commit4bd7963e2e244ace94afa59124f2637543d74ba2 (patch)
tree01c8ae1f66cdaa307448fbcd7fcb79081fb87d7f
parent09bd220d865520f2426ae99d97b8b8296058732f (diff)
downloademacs-4bd7963e2e244ace94afa59124f2637543d74ba2.tar.gz
emacs-4bd7963e2e244ace94afa59124f2637543d74ba2.zip
(add-hook, remove-hook): Fix leaks (bug#48666)
* lisp/subr.el (add-hook, remove-hook): Rewrite the hook depth management so we only keep the info relevant to functions present on the hook.
-rw-r--r--lisp/subr.el65
1 files changed, 39 insertions, 26 deletions
diff --git a/lisp/subr.el b/lisp/subr.el
index a78af09c40e..1b9b67b7054 100644
--- a/lisp/subr.el
+++ b/lisp/subr.el
@@ -1913,26 +1913,34 @@ performance impact when running `add-hook' and `remove-hook'."
1913 (setq hook-value (list hook-value))) 1913 (setq hook-value (list hook-value)))
1914 ;; Do the actual addition if necessary 1914 ;; Do the actual addition if necessary
1915 (unless (member function hook-value) 1915 (unless (member function hook-value)
1916 (when (stringp function) ;FIXME: Why? 1916 (let ((depth-sym (get hook 'hook--depth-alist)))
1917 (setq function (purecopy function))) 1917 ;; While the `member' test above has to use `equal' for historical
1918 ;; All those `equal' tests performed between functions can end up being 1918 ;; reasons, `equal' is a performance problem on large/cyclic functions,
1919 ;; costly since those functions may be large recursive and even cyclic 1919 ;; so we index `hook--depth-alist' with `eql'. (bug#46326)
1920 ;; structures, so we index `hook--depth-alist' with `eq'. (bug#46326) 1920 (unless (zerop depth)
1921 (when (or (get hook 'hook--depth-alist) (not (zerop depth))) 1921 (unless depth-sym
1922 ;; Note: The main purpose of the above `when' test is to avoid running 1922 (setq depth-sym (make-symbol "depth-alist"))
1923 ;; this `setf' before `gv' is loaded during bootstrap. 1923 (set depth-sym nil)
1924 (setf (alist-get function (get hook 'hook--depth-alist) 0) depth)) 1924 (setf (get hook 'hook--depth-alist) depth-sym))
1925 (setq hook-value 1925 (if local (make-local-variable depth-sym))
1926 (if (< 0 depth) 1926 (setf (alist-get function
1927 (append hook-value (list function)) 1927 (if local (symbol-value depth-sym)
1928 (cons function hook-value))) 1928 (default-value depth-sym))
1929 (let ((depth-alist (get hook 'hook--depth-alist))) 1929 0)
1930 (when depth-alist 1930 depth))
1931 (setq hook-value 1931 (setq hook-value
1932 (sort (if (< 0 depth) hook-value (copy-sequence hook-value)) 1932 (if (< 0 depth)
1933 (lambda (f1 f2) 1933 (append hook-value (list function))
1934 (< (alist-get f1 depth-alist 0 nil #'eq) 1934 (cons function hook-value)))
1935 (alist-get f2 depth-alist 0 nil #'eq)))))))) 1935 (when depth-sym
1936 (let ((depth-alist (if local (symbol-value depth-sym)
1937 (default-value depth-sym))))
1938 (when depth-alist
1939 (setq hook-value
1940 (sort (if (< 0 depth) hook-value (copy-sequence hook-value))
1941 (lambda (f1 f2)
1942 (< (alist-get f1 depth-alist 0 nil #'eq)
1943 (alist-get f2 depth-alist 0 nil #'eq))))))))))
1936 ;; Set the actual variable 1944 ;; Set the actual variable
1937 (if local 1945 (if local
1938 (progn 1946 (progn
@@ -2005,9 +2013,14 @@ one will be removed."
2005 (when old-fun 2013 (when old-fun
2006 ;; Remove auxiliary depth info to avoid leaks (bug#46414) 2014 ;; Remove auxiliary depth info to avoid leaks (bug#46414)
2007 ;; and to avoid the list growing too long. 2015 ;; and to avoid the list growing too long.
2008 (let* ((depths (get hook 'hook--depth-alist)) 2016 (let* ((depth-sym (get hook 'hook--depth-alist))
2009 (di (assq old-fun depths))) 2017 (depth-alist (if depth-sym (if local (symbol-value depth-sym)
2010 (when di (put hook 'hook--depth-alist (delq di depths))))) 2018 (default-value depth-sym))))
2019 (di (assq old-fun depth-alist)))
2020 (when di
2021 (setf (if local (symbol-value depth-sym)
2022 (default-value depth-sym))
2023 (delq di depth-alist)))))
2011 ;; If the function is on the global hook, we need to shadow it locally 2024 ;; If the function is on the global hook, we need to shadow it locally
2012 ;;(when (and local (member function (default-value hook)) 2025 ;;(when (and local (member function (default-value hook))
2013 ;; (not (member (cons 'not function) hook-value))) 2026 ;; (not (member (cons 'not function) hook-value)))
@@ -2169,7 +2182,7 @@ can do the job."
2169 (not (macroexp-const-p append))) 2182 (not (macroexp-const-p append)))
2170 exp 2183 exp
2171 (let* ((sym (cadr list-var)) 2184 (let* ((sym (cadr list-var))
2172 (append (eval append)) 2185 (append (eval append lexical-binding))
2173 (msg (format-message 2186 (msg (format-message
2174 "`add-to-list' can't use lexical var `%s'; use `push' or `cl-pushnew'" 2187 "`add-to-list' can't use lexical var `%s'; use `push' or `cl-pushnew'"
2175 sym)) 2188 sym))
@@ -2718,7 +2731,7 @@ It can be retrieved with `(process-get PROCESS PROPNAME)'."
2718 2731
2719(defconst read-key-full-map 2732(defconst read-key-full-map
2720 (let ((map (make-sparse-keymap))) 2733 (let ((map (make-sparse-keymap)))
2721 (define-key map [t] 'dummy) 2734 (define-key map [t] #'ignore) ;Dummy binding.
2722 2735
2723 ;; ESC needs to be unbound so that escape sequences in 2736 ;; ESC needs to be unbound so that escape sequences in
2724 ;; `input-decode-map' are still processed by `read-key-sequence'. 2737 ;; `input-decode-map' are still processed by `read-key-sequence'.
@@ -4471,7 +4484,7 @@ is allowed once again. (Immediately, if `inhibit-quit' is nil.)"
4471 ;; Without this, it will not be handled until the next function 4484 ;; Without this, it will not be handled until the next function
4472 ;; call, and that might allow it to exit thru a condition-case 4485 ;; call, and that might allow it to exit thru a condition-case
4473 ;; that intends to handle the quit signal next time. 4486 ;; that intends to handle the quit signal next time.
4474 (eval '(ignore nil))))) 4487 (eval '(ignore nil) t))))
4475 4488
4476(defmacro while-no-input (&rest body) 4489(defmacro while-no-input (&rest body)
4477 "Execute BODY only as long as there's no pending input. 4490 "Execute BODY only as long as there's no pending input.