Subject: Re: bug#34765: 26.1; with-temp-buffer should not run buffer-list-update-hook

From: "Basil L. Contovounesios" <[email-masked]>
Date: Mon, 07 Dec 2020 22:16:23 +0000

"Basil L. Contovounesios" <[email-masked]> writes:

> I'll finish up Martin's patch with some docs then.

How's the attached?  The gist of it is that get-buffer-create and
generate-new-buffer gain a new argument for inhibiting buffer hooks,
which is set in with-temp-buffer, with-temp-file, with-output-to-string,
insert-file-contents, Vprin1_to_string_buffer, and code_conversion_save.

Arising questions:

0. Why isn't kill-buffer-hook defined as a DEFVAR_LISP?
   Is it because it has to be defined in init_buffer_once?
   If so, why is that the case?

1. Is it okay for with-temp-buffer, with-temp-file, and
   with-output-to-string to unconditionally inhibit buffer hooks,
   or should this be controlled by e.g. a global variable?

Thanks,

-- 
Basil

>From 6847a28e105094d6e024b1239520d45498ff8653 Mon Sep 17 00:00:00 2001
From: "Basil L. Contovounesios" <[email-masked]>
Date: Tue, 1 Dec 2020 01:12:32 +0000
Subject: [PATCH] Inhibit buffer hooks in temporary buffers

Give get-buffer-create an optional argument to inhibit buffer hooks
in internal or temporary buffers for efficiency (bug#34765).

* .dir-locals.el (c-mode): Enforce existing indent-tabs-mode policy.

* etc/NEWS: Announce new parameter of get-buffer-create and
generate-new-buffer, and that with-temp-buffer and with-temp-file
now inhibit buffer hooks.

* doc/lispref/buffers.texi (Buffer Names): Fix typo.
(Creating Buffers): Document new parameter of get-buffer-create and
generate-new-buffer.
(Buffer List, Killing Buffers): Document when buffer hooks are
inhibited.
(Current Buffer):
* doc/lispref/files.texi (Writing to Files): Document that
with-temp-buffer and with-temp-file inhibit buffer hooks.
* doc/lispref/internals.texi (Buffer Internals): Document
inhibit_buffer_hooks flag.  Remove stale comment.
* doc/misc/gnus-faq.texi (FAQ 5-8):
* lisp/simple.el (shell-command-on-region): Fix indentation.

* lisp/files.el (kill-buffer-hook): Document when hook is inhibited.
(create-file-buffer):
* lisp/gnus/gnus-uu.el (gnus-uu-unshar-article):
* lisp/international/mule.el (load-with-code-conversion):
* lisp/mh-e/mh-xface.el (mh-x-image-url-fetch-image):
* lisp/net/imap.el (imap-open):
* lisp/net/mailcap.el (mailcap-maybe-eval):
* lisp/progmodes/flymake-proc.el
(flymake-proc--read-file-to-temp-buffer)
(flymake-proc--copy-buffer-to-temp-buffer): Simplify.

* lisp/subr.el (generate-new-buffer): Forward new optional argument
to inhibit buffer hooks to get-buffer-create.
(with-temp-buffer, with-temp-file, with-output-to-string): Inhibit
buffer hooks in created buffer.

* src/buffer.c (run_buffer_list_update_hook): New helper function.
(Fget_buffer_create): Use it.  Add optional argument to set
inhibit_buffer_hooks flag instead of comparing the buffer name to
Vcode_conversion_workbuf_name.  All callers changed.
(Fmake_indirect_buffer, Frename_buffer, Fbury_buffer_internal)
(record_buffer): Use run_buffer_list_update_hook.
(Fkill_buffer): Document when buffer hooks are inhibited.  Use
run_buffer_list_update_hook.
(init_buffer_once): Inhibit buffer hooks in Vprin1_to_string_buffer.
(Vkill_buffer_query_functions, Vbuffer_list_update_hook): Document
when hooks are inhibited.
* src/buffer.h (struct buffer): Update inhibit_buffer_hooks
commentary.
* src/coding.h (Vcode_conversion_workbuf_name):
* src/coding.c (Vcode_conversion_workbuf_name): Make static again
since it is no longer needed in src/buffer.c.
(code_conversion_restore, code_conversion_save, syms_of_coding):
Prefer boolean over integer constants.
* src/fileio.c (Finsert_file_contents): Inhibit buffer hooks in
" *code-converting-work*" buffer.
* src/window.c (Fselect_window): Fix grammar.  Mention
window-selection-change-functions alongside buffer-list-update-hook.

* test/src/buffer-tests.el: Fix requires.
(buffer-tests-inhibit-buffer-hooks): New test.
---
 .dir-locals.el                 |  1 +
 doc/lispref/buffers.texi       | 55 ++++++++++++++++------
 doc/lispref/files.texi         |  7 ++-
 doc/lispref/internals.texi     | 11 ++++-
 doc/misc/gnus-faq.texi         |  8 ++--
 etc/NEWS                       | 16 +++++++
 lisp/files.el                  |  6 ++-
 lisp/gnus/gnus-uu.el           |  3 +-
 lisp/international/mule.el     |  9 ++--
 lisp/mh-e/mh-xface.el          |  3 +-
 lisp/net/imap.el               |  3 +-
 lisp/net/mailcap.el            |  3 +-
 lisp/progmodes/flymake-proc.el | 13 +++--
 lisp/simple.el                 |  3 +-
 lisp/subr.el                   | 54 ++++++++++-----------
 src/buffer.c                   | 86 +++++++++++++++++++---------------
 src/buffer.h                   | 10 ++--
 src/callproc.c                 |  5 +-
 src/coding.c                   | 12 ++---
 src/coding.h                   |  3 --
 src/fileio.c                   |  2 +-
 src/minibuf.c                  |  2 +-
 src/print.c                    |  2 +-
 src/process.c                  | 12 ++---
 src/w32fns.c                   |  2 +-
 src/window.c                   |  7 +--
 src/xdisp.c                    |  4 +-
 src/xfns.c                     |  2 +-
 src/xwidget.c                  |  3 +-
 test/src/buffer-tests.el       | 33 +++++++++++--
 30 files changed, 228 insertions(+), 152 deletions(-)

diff --git a/.dir-locals.el b/.dir-locals.el
index 27d50c6069..b313945936 100644
--- a/.dir-locals.el
+++ b/.dir-locals.el
@@ -9,6 +9,7 @@
             (c-noise-macro-names . ("INLINE" "ATTRIBUTE_NO_SANITIZE_UNDEFINED" "UNINIT" "CALLBACK" "ALIGN_STACK"))
             (electric-quote-comment . nil)
             (electric-quote-string . nil)
+            (indent-tabs-mode . t)
 	    (mode . bug-reference-prog)))
  (objc-mode . ((c-file-style . "GNU")
                (electric-quote-comment . nil)
diff --git a/doc/lispref/buffers.texi b/doc/lispref/buffers.texi
index 2860343628..c8c4852e30 100644
--- a/doc/lispref/buffers.texi
+++ b/doc/lispref/buffers.texi
@@ -225,13 +225,18 @@ Current Buffer
 
 @defmac with-temp-buffer body@dots{}
 @anchor{Definition of with-temp-buffer}
-The @code{with-temp-buffer} macro evaluates the @var{body} forms
-with a temporary buffer as the current buffer.  It saves the identity of
+The @code{with-temp-buffer} macro evaluates the @var{body} forms with
+a temporary buffer as the current buffer.  It saves the identity of
 the current buffer, creates a temporary buffer and makes it current,
 evaluates the @var{body} forms, and finally restores the previous
-current buffer while killing the temporary buffer.  By default, undo
-information (@pxref{Undo}) is not recorded in the buffer created by
-this macro (but @var{body} can enable that, if needed).
+current buffer while killing the temporary buffer.
+
+By default, undo information (@pxref{Undo}) is not recorded in the
+buffer created by this macro (but @var{body} can enable that, if
+needed).  The temporary buffer also does not run the hooks
+@code{kill-buffer-hook}, @code{kill-buffer-query-functions}
+(@pxref{Killing Buffers}), and @code{buffer-list-update-hook}
+(@pxref{Buffer List}).
 
 The return value is the value of the last form in @var{body}.  You can
 return the contents of the temporary buffer by using
@@ -345,9 +350,9 @@ Buffer Names
 
 If the optional second argument @var{ignore} is non-@code{nil}, it
 should be a string, a potential buffer name.  It means to consider
-that potential buffer acceptable, if it is tried, even it is the name
-of an existing buffer (which would normally be rejected).  Thus, if
-buffers named @samp{foo}, @samp{foo<2>}, @samp{foo<3>} and
+that potential buffer acceptable, if it is tried, even if it is the
+name of an existing buffer (which would normally be rejected).  Thus,
+if buffers named @samp{foo}, @samp{foo<2>}, @samp{foo<3>} and
 @samp{foo<4>} exist,
 
 @example
@@ -932,13 +937,17 @@ Buffer List
 @defvar buffer-list-update-hook
 This is a normal hook run whenever the buffer list changes.  Functions
 (implicitly) running this hook are @code{get-buffer-create}
-(@pxref{Creating Buffers}), @code{rename-buffer} (@pxref{Buffer Names}),
-@code{kill-buffer} (@pxref{Killing Buffers}), @code{bury-buffer} (see
-above) and @code{select-window} (@pxref{Selecting Windows}).
+(@pxref{Creating Buffers}), @code{rename-buffer} (@pxref{Buffer
+Names}), @code{kill-buffer} (@pxref{Killing Buffers}),
+@code{bury-buffer} (see above), and @code{select-window}
+(@pxref{Selecting Windows}).  This hook is not run for internal or
+temporary buffers created by @code{get-buffer-create} or
+@code{generate-new-buffer} with a non-@code{nil} argument
+@var{inhibit-buffer-hooks}.
 
 Functions run by this hook should avoid calling @code{select-window}
-with a nil @var{norecord} argument or @code{with-temp-buffer} since
-either may lead to infinite recursion.
+with a @code{nil} @var{norecord} argument since this may lead to
+infinite recursion.
 @end defvar
 
 @node Creating Buffers
@@ -951,12 +960,20 @@ Creating Buffers
 with the specified name; @code{generate-new-buffer} always creates a new
 buffer and gives it a unique name.
 
+  Both functions accept an optional argument @var{inhibit-buffer-hooks}.
+If it is non-@code{nil}, the buffer they create does not run the hooks
+@code{kill-buffer-hook}, @code{kill-buffer-query-functions}
+(@pxref{Killing Buffers}), and @code{buffer-list-update-hook}
+(@pxref{Buffer List}).  This avoids slowing down internal or temporary
+buffers that are never presented to users or passed on to other
+applications.
+
   Other functions you can use to create buffers include
 @code{with-output-to-temp-buffer} (@pxref{Temporary Displays}) and
 @code{create-file-buffer} (@pxref{Visiting Files}).  Starting a
 subprocess can also create a buffer (@pxref{Processes}).
 
-@defun get-buffer-create buffer-or-name
+@defun get-buffer-create buffer-or-name &optional inhibit-buffer-hooks
 This function returns a buffer named @var{buffer-or-name}.  The buffer
 returned does not become the current buffer---this function does not
 change which buffer is current.
@@ -980,7 +997,7 @@ Creating Buffers
 buffer initially disables undo information recording (@pxref{Undo}).
 @end defun
 
-@defun generate-new-buffer name
+@defun generate-new-buffer name &optional inhibit-buffer-hooks
 This function returns a newly created, empty buffer, but does not make
 it current.  The name of the buffer is generated by passing @var{name}
 to the function @code{generate-new-buffer-name} (@pxref{Buffer
@@ -1092,6 +1109,10 @@ Killing Buffers
 they are called.  The idea of this feature is that these functions will
 ask for confirmation from the user.  If any of them returns @code{nil},
 @code{kill-buffer} spares the buffer's life.
+
+This hook is not run for internal or temporary buffers created by
+@code{get-buffer-create} or @code{generate-new-buffer} with a
+non-@code{nil} argument @var{inhibit-buffer-hooks}.
 @end defvar
 
 @defvar kill-buffer-hook
@@ -1100,6 +1121,10 @@ Killing Buffers
 The buffer to be killed is current when the hook functions run.
 @xref{Hooks}.  This variable is a permanent local, so its local binding
 is not cleared by changing major modes.
+
+This hook is not run for internal or temporary buffers created by
+@code{get-buffer-create} or @code{generate-new-buffer} with a
+non-@code{nil} argument @var{inhibit-buffer-hooks}.
 @end defvar
 
 @defopt buffer-offer-save
diff --git a/doc/lispref/files.texi b/doc/lispref/files.texi
index d49ac42bb4..6949ca29c6 100644
--- a/doc/lispref/files.texi
+++ b/doc/lispref/files.texi
@@ -701,8 +701,11 @@ Writing to Files
 The current buffer is restored even in case of an abnormal exit via
 @code{throw} or error (@pxref{Nonlocal Exits}).
 
-See also @code{with-temp-buffer} in @ref{Definition of
-with-temp-buffer,, The Current Buffer}.
+Like @code{with-temp-buffer} (@pxref{Definition of with-temp-buffer,,
+Current Buffer}), the temporary buffer used by this macro does not run
+the hooks @code{kill-buffer-hook}, @code{kill-buffer-query-functions}
+(@pxref{Killing Buffers}), and @code{buffer-list-update-hook}
+(@pxref{Buffer List}).
 @end defmac
 
 @node File Locks
diff --git a/doc/lispref/internals.texi b/doc/lispref/internals.texi
index bb25983aa4..3ba59298f1 100644
--- a/doc/lispref/internals.texi
+++ b/doc/lispref/internals.texi
@@ -2375,6 +2375,15 @@ Buffer Internals
 This flag indicates that redisplay optimizations should not be used to
 display this buffer.
 
+@item inhibit_buffer_hooks
+This flag indicates that the buffer should not run the hooks
+@code{kill-buffer-hook}, @code{kill-buffer-query-functions}
+(@pxref{Killing Buffers}), and @code{buffer-list-update-hook}
+(@pxref{Buffer List}).  It is set at buffer creation (@pxref{Creating
+Buffers}), and avoids slowing down internally used temporary buffers,
+such as those created by @code{with-temp-buffer} (@pxref{Definition of
+with-temp-buffer,, Current Buffer}).
+
 @item overlay_center
 This field holds the current overlay center position.  @xref{Managing
 Overlays}.
@@ -2388,8 +2397,6 @@ Buffer Internals
 and @code{overlays_after} is sorted in order of increasing beginning
 position.
 
-@c FIXME? the following are now all Lisp_Object BUFFER_INTERNAL_FIELD (foo).
-
 @item name
 A Lisp string that names the buffer.  It is guaranteed to be unique.
 @xref{Buffer Names}.  This and the following fields have their names
diff --git a/doc/misc/gnus-faq.texi b/doc/misc/gnus-faq.texi
index adb812f572..c30e80ff56 100644
--- a/doc/misc/gnus-faq.texi
+++ b/doc/misc/gnus-faq.texi
@@ -1523,10 +1523,10 @@ FAQ 5-8
 
 @example
 (setq message-default-headers
-        (with-temp-buffer
-          (insert "X-Face: ")
-          (insert-file-contents "~/.xface")
-          (buffer-string)))
+      (with-temp-buffer
+        (insert "X-Face: ")
+        (insert-file-contents "~/.xface")
+        (buffer-string)))
 @end example
 @noindent
 
diff --git a/etc/NEWS b/etc/NEWS
index d796a4cdbc..6e2b92fed2 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -1735,6 +1735,13 @@ modifies the string's text properties; instead, it uses and returns
 a copy of the string.  This helps avoid trouble when strings are
 shared or constants.
 
++++
+** Temporary buffers no longer run certain buffer hooks.
+The macros 'with-temp-buffer' and 'with-temp-file' no longer run the
+hooks 'kill-buffer-hook', 'kill-buffer-query-functions', and
+'buffer-list-update-hook' for the temporary buffers they create.  This
+avoids slowing them down when a lot of these hooks are defined.
+
 ---
 ** The obsolete function 'thread-alive-p' has been removed.
 
@@ -2077,6 +2084,15 @@ and 'play-sound-file'.
 If this variable is non-nil, character syntax is used for printing
 numbers when this makes sense, such as '?A' for 65.
 
++++
+** Buffers can now be created with certain hooks disabled.
+The functions 'get-buffer-create' and 'generate-new-buffer' accept a
+new optional argument 'inhibit-buffer-hooks'.  If non-nil, the new
+buffer does not run the hooks 'kill-buffer-hook',
+'kill-buffer-query-functions', and 'buffer-list-update-hook'.  This
+avoids slowing down internal or temporary buffers that are never
+presented to users or passed on to other applications.
+
 
 * Changes in Emacs 28.1 on Non-Free Operating Systems
 
diff --git a/lisp/files.el b/lisp/files.el
index 2cf77d5d7e..97316f0f1e 100644
--- a/lisp/files.el
+++ b/lisp/files.el
@@ -1850,6 +1850,10 @@ kill-buffer-hook
 The buffer being killed is current while the hook is running.
 See `kill-buffer'.
 
+This hook is not run for internal or temporary buffers created by
+`get-buffer-create' or `generate-new-buffer' with argument
+INHIBIT-BUFFER-HOOKS non-nil.
+
 Note: Be careful with let-binding this hook considering it is
 frequently used for cleanup.")
 
@@ -1951,7 +1955,7 @@ create-file-buffer
   (let ((lastname (file-name-nondirectory filename)))
     (if (string= lastname "")
 	(setq lastname filename))
-    (generate-new-buffer (if (string-match-p "\\` " lastname)
+    (generate-new-buffer (if (string-prefix-p " " lastname)
 			     (concat "|" lastname)
 			   lastname))))
 
diff --git a/lisp/gnus/gnus-uu.el b/lisp/gnus/gnus-uu.el
index 70aeac00d7..00ca430cc5 100644
--- a/lisp/gnus/gnus-uu.el
+++ b/lisp/gnus/gnus-uu.el
@@ -1587,8 +1587,7 @@ gnus-uu-unshar-article
 	  (save-excursion
 	    (switch-to-buffer (current-buffer))
 	    (delete-other-windows)
-	    (let ((buffer (get-buffer-create (generate-new-buffer-name
-					      "*Warning*"))))
+            (let ((buffer (generate-new-buffer "*Warning*")))
 	      (unless
 		  (unwind-protect
 		      (with-current-buffer buffer
diff --git a/lisp/international/mule.el b/lisp/international/mule.el
index 212e7232b4..6571454dff 100644
--- a/lisp/international/mule.el
+++ b/lisp/international/mule.el
@@ -307,12 +307,9 @@ load-with-code-conversion
       (and (null noerror)
 	   (signal 'file-error (list "Cannot open load file" file)))
     ;; Read file with code conversion, and then eval.
-    (let* ((buffer
-            ;; We can't use `generate-new-buffer' because files.el
-            ;; is not yet loaded.
-            (get-buffer-create (generate-new-buffer-name " *load*")))
-	   (load-in-progress t)
-	   (source (save-match-data (string-match "\\.el\\'" fullname))))
+    (let ((buffer (generate-new-buffer " *load*"))
+          (load-in-progress t)
+          (source (string-suffix-p ".el" fullname)))
       (unless nomessage
 	(if source
 	    (message "Loading %s (source)..." file)
diff --git a/lisp/mh-e/mh-xface.el b/lisp/mh-e/mh-xface.el
index 909f1fe95d..65039310e7 100644
--- a/lisp/mh-e/mh-xface.el
+++ b/lisp/mh-e/mh-xface.el
@@ -425,8 +425,7 @@ mh-x-image-url-fetch-image
 be displayed in a buffer and position specified by MARKER. The
 actual display is carried out by the SENTINEL function."
   (if mh-wget-executable
-      (let ((buffer (get-buffer-create (generate-new-buffer-name
-                                        mh-temp-fetch-buffer)))
+      (let ((buffer (generate-new-buffer mh-temp-fetch-buffer))
             (filename (or (mh-funcall-if-exists make-temp-file "mhe-fetch")
                           (expand-file-name (make-temp-name "~/mhe-fetch")))))
         (with-current-buffer buffer
diff --git a/lisp/net/imap.el b/lisp/net/imap.el
index 0394f0efea..50b08d9612 100644
--- a/lisp/net/imap.el
+++ b/lisp/net/imap.el
@@ -1033,8 +1033,7 @@ imap-open
 	    (when (funcall (nth 1 (assq stream imap-stream-alist)) buffer)
 	      ;; Stream changed?
 	      (if (not (eq imap-default-stream stream))
-		  (with-current-buffer (get-buffer-create
-					(generate-new-buffer-name " *temp*"))
+                  (with-current-buffer (generate-new-buffer " *temp*")
 		    (mapc 'make-local-variable imap-local-variables)
 		    (set-buffer-multibyte nil)
 		    (buffer-disable-undo)
diff --git a/lisp/net/mailcap.el b/lisp/net/mailcap.el
index d0f8c1272d..bc99f02fe3 100644
--- a/lisp/net/mailcap.el
+++ b/lisp/net/mailcap.el
@@ -386,8 +386,7 @@ mailcap-maybe-eval
     (when
 	(save-window-excursion
 	  (delete-other-windows)
-	  (let ((buffer (get-buffer-create (generate-new-buffer-name
-					    "*Warning*"))))
+          (let ((buffer (generate-new-buffer "*Warning*")))
 	    (unwind-protect
 		(with-current-buffer buffer
 		  (insert (substitute-command-keys
diff --git a/lisp/progmodes/flymake-proc.el b/lisp/progmodes/flymake-proc.el
index 152dc725c7..e9d5018f30 100644
--- a/lisp/progmodes/flymake-proc.el
+++ b/lisp/progmodes/flymake-proc.el
@@ -429,16 +429,15 @@ flymake-proc--replace-region
 
 (defun flymake-proc--read-file-to-temp-buffer (file-name)
   "Insert contents of FILE-NAME into newly created temp buffer."
-  (let* ((temp-buffer (get-buffer-create (generate-new-buffer-name (concat "flymake:" (file-name-nondirectory file-name))))))
-    (with-current-buffer temp-buffer
-      (insert-file-contents file-name))
-    temp-buffer))
+  (with-current-buffer (generate-new-buffer
+                        (concat "flymake:" (file-name-nondirectory file-name)))
+    (insert-file-contents file-name)
+    (current-buffer)))
 
 (defun flymake-proc--copy-buffer-to-temp-buffer (buffer)
   "Copy contents of BUFFER into newly created temp buffer."
-  (with-current-buffer
-      (get-buffer-create (generate-new-buffer-name
-                          (concat "flymake:" (buffer-name buffer))))
+  (with-current-buffer (generate-new-buffer
+                        (concat "flymake:" (buffer-name buffer)))
     (insert-buffer-substring buffer)
     (current-buffer)))
 
diff --git a/lisp/simple.el b/lisp/simple.el
index 3c4f756230..7781a1e91a 100644
--- a/lisp/simple.el
+++ b/lisp/simple.el
@@ -4301,8 +4301,7 @@ shell-command-on-region
 (defun shell-command-to-string (command)
   "Execute shell command COMMAND and return its output as a string."
   (with-output-to-string
-    (with-current-buffer
-      standard-output
+    (with-current-buffer standard-output
       (shell-command command t))))
 
 (defun process-file (program &optional infile buffer display &rest args)
diff --git a/lisp/subr.el b/lisp/subr.el
index c28807f694..0daceb6869 100644
--- a/lisp/subr.el
+++ b/lisp/subr.el
@@ -3692,10 +3692,11 @@ internal--after-with-selected-window
   (when (window-live-p (nth 1 state))
     (select-window (nth 1 state) 'norecord)))
 
-(defun generate-new-buffer (name)
+(defun generate-new-buffer (name &optional inhibit-buffer-hooks)
   "Create and return a buffer with a name based on NAME.
-Choose the buffer's name using `generate-new-buffer-name'."
-  (get-buffer-create (generate-new-buffer-name name)))
+Choose the buffer's name using `generate-new-buffer-name'.
+See `get-buffer-create' for the meaning of INHIBIT-BUFFER-HOOKS."
+  (get-buffer-create (generate-new-buffer-name name) inhibit-buffer-hooks))
 
 (defmacro with-selected-window (window &rest body)
   "Execute the forms in BODY with WINDOW as the selected window.
@@ -3854,23 +3855,29 @@ with-output-to-temp-buffer
        (prog1 (progn ,@body)
          (internal-temp-output-buffer-show ,buf)))))
 
+(defmacro with-temp-buffer (&rest body)
+  "Create a temporary buffer, and evaluate BODY there like `progn'.
+The buffer does not run the hooks `kill-buffer-hook',
+`kill-buffer-query-functions', and `buffer-list-update-hook'.
+See also `with-temp-file' and `with-output-to-string'."
+  (declare (indent 0) (debug t))
+  (let ((temp-buffer (make-symbol "temp-buffer")))
+    `(let ((,temp-buffer (generate-new-buffer " *temp*" t)))
+       ;; `kill-buffer' can change current-buffer in some odd cases.
+       (with-current-buffer ,temp-buffer
+         (unwind-protect
+             (progn ,@body)
+           (and (buffer-name ,temp-buffer)
+                (kill-buffer ,temp-buffer)))))))
+
 (defmacro with-temp-file (file &rest body)
   "Create a new buffer, evaluate BODY there, and write the buffer to FILE.
 The value returned is the value of the last form in BODY.
-See also `with-temp-buffer'."
+The buffer is created using `with-temp-buffer', which see."
   (declare (indent 1) (debug t))
-  (let ((temp-file (make-symbol "temp-file"))
-	(temp-buffer (make-symbol "temp-buffer")))
-    `(let ((,temp-file ,file)
-	   (,temp-buffer (generate-new-buffer " *temp file*")))
-       (unwind-protect
-	   (prog1
-	       (with-current-buffer ,temp-buffer
-		 ,@body)
-	     (with-current-buffer ,temp-buffer
-	       (write-region nil nil ,temp-file nil 0)))
-	 (and (buffer-name ,temp-buffer)
-	      (kill-buffer ,temp-buffer))))))
+  `(with-temp-buffer
+     (prog1 (progn ,@body)
+       (write-region nil nil ,file nil 0))))
 
 (defmacro with-temp-message (message &rest body)
   "Display MESSAGE temporarily if non-nil while BODY is evaluated.
@@ -3895,19 +3902,6 @@ with-temp-message
 		  (message "%s" ,current-message)
 		(message nil)))))))
 
-(defmacro with-temp-buffer (&rest body)
-  "Create a temporary buffer, and evaluate BODY there like `progn'.
-See also `with-temp-file' and `with-output-to-string'."
-  (declare (indent 0) (debug t))
-  (let ((temp-buffer (make-symbol "temp-buffer")))
-    `(let ((,temp-buffer (generate-new-buffer " *temp*")))
-       ;; `kill-buffer' can change current-buffer in some odd cases.
-       (with-current-buffer ,temp-buffer
-         (unwind-protect
-	     (progn ,@body)
-           (and (buffer-name ,temp-buffer)
-                (kill-buffer ,temp-buffer)))))))
-
 (defmacro with-silent-modifications (&rest body)
   "Execute BODY, pretending it does not modify the buffer.
 This macro is typically used around modifications of
@@ -3935,7 +3929,7 @@ with-silent-modifications
 (defmacro with-output-to-string (&rest body)
   "Execute BODY, return the text it sent to `standard-output', as a string."
   (declare (indent 0) (debug t))
-  `(let ((standard-output (generate-new-buffer " *string-output*")))
+  `(let ((standard-output (generate-new-buffer " *string-output*" t)))
      (unwind-protect
 	 (progn
 	   (let ((standard-output standard-output))
diff --git a/src/buffer.c b/src/buffer.c
index 4215acbf1d..926fbc8f09 100644
--- a/src/buffer.c
+++ b/src/buffer.c
@@ -37,7 +37,6 @@ Copyright (C) 1985-1989, 1993-1995, 1997-2020 Free Software Foundation,
 #include "window.h"
 #include "commands.h"
 #include "character.h"
-#include "coding.h"
 #include "buffer.h"
 #include "region-cache.h"
 #include "indent.h"
@@ -514,16 +513,31 @@ get_truename_buffer (register Lisp_Object filename)
   return Qnil;
 }
 
-DEFUN ("get-buffer-create", Fget_buffer_create, Sget_buffer_create, 1, 1, 0,
+/* Run 'buffer-list-update-hook' if Vrun_hooks is non-nil, and BUF is
+   either NULL or does not have buffer hooks inhibited.  */
+
+static void
+run_buffer_list_update_hook (struct buffer *buf) {
+  if (! (NILP (Vrun_hooks) || (buf && buf->inhibit_buffer_hooks)))
+    call1 (Vrun_hooks, Qbuffer_list_update_hook);
+}
+
+DEFUN ("get-buffer-create", Fget_buffer_create, Sget_buffer_create, 1, 2, 0,
        doc: /* Return the buffer specified by BUFFER-OR-NAME, creating a new one if needed.
 If BUFFER-OR-NAME is a string and a live buffer with that name exists,
 return that buffer.  If no such buffer exists, create a new buffer with
-that name and return it.  If BUFFER-OR-NAME starts with a space, the new
-buffer does not keep undo information.
+that name and return it.
+
+If BUFFER-OR-NAME starts with a space, the new buffer does not keep undo
+information.  If optional argument INHIBIT-BUFFER-HOOKS is non-nil, the
+new buffer does not run the hooks `kill-buffer-hook',
+`kill-buffer-query-functions', and `buffer-list-update-hook'.  This
+avoids slowing down internal or temporary buffers that are never
+presented to users or passed on to other applications.
 
 If BUFFER-OR-NAME is a buffer instead of a string, return it as given,
 even if it is dead.  The return value is never nil.  */)
-  (register Lisp_Object buffer_or_name)
+  (register Lisp_Object buffer_or_name, Lisp_Object inhibit_buffer_hooks)
 {
   register Lisp_Object buffer, name;
   register struct buffer *b;
@@ -598,11 +612,7 @@ DEFUN ("get-buffer-create", Fget_buffer_create, Sget_buffer_create, 1, 1, 0,
   set_string_intervals (name, NULL);
   bset_name (b, name);
 
-  b->inhibit_buffer_hooks
-    = (STRINGP (Vcode_conversion_workbuf_name)
-       && strncmp (SSDATA (name), SSDATA (Vcode_conversion_workbuf_name),
-		   SBYTES (Vcode_conversion_workbuf_name)) == 0);
-
+  b->inhibit_buffer_hooks = !NILP (inhibit_buffer_hooks);
   bset_undo_list (b, SREF (name, 0) != ' ' ? Qnil : Qt);
 
   reset_buffer (b);
@@ -614,9 +624,8 @@ DEFUN ("get-buffer-create", Fget_buffer_create, Sget_buffer_create, 1, 1, 0,
   /* Put this in the alist of all live buffers.  */
   XSETBUFFER (buffer, b);
   Vbuffer_alist = nconc2 (Vbuffer_alist, list1 (Fcons (name, buffer)));
-  /* And run buffer-list-update-hook.  */
-  if (!NILP (Vrun_hooks) && !b->inhibit_buffer_hooks)
-    call1 (Vrun_hooks, Qbuffer_list_update_hook);
+
+  run_buffer_list_update_hook (b);
 
   return buffer;
 }
@@ -890,9 +899,7 @@ DEFUN ("make-indirect-buffer", Fmake_indirect_buffer, Smake_indirect_buffer,
       set_buffer_internal_1 (old_b);
     }
 
-  /* Run buffer-list-update-hook.  */
-  if (!NILP (Vrun_hooks))
-    call1 (Vrun_hooks, Qbuffer_list_update_hook);
+  run_buffer_list_update_hook (NULL);
 
   return buf;
 }
@@ -1536,9 +1543,7 @@ DEFUN ("rename-buffer", Frename_buffer, Srename_buffer, 1, 2,
       && !NILP (BVAR (current_buffer, auto_save_file_name)))
     call0 (intern ("rename-auto-save-file"));
 
-  /* Run buffer-list-update-hook.  */
-  if (!NILP (Vrun_hooks) && !current_buffer->inhibit_buffer_hooks)
-    call1 (Vrun_hooks, Qbuffer_list_update_hook);
+  run_buffer_list_update_hook (current_buffer);
 
   /* Refetch since that last call may have done GC.  */
   return BVAR (current_buffer, name);
@@ -1612,7 +1617,7 @@ DEFUN ("other-buffer", Fother_buffer, Sother_buffer, 0, 3, 0,
       buf = Fget_buffer (scratch);
       if (NILP (buf))
 	{
-	  buf = Fget_buffer_create (scratch);
+	  buf = Fget_buffer_create (scratch, Qnil);
 	  Fset_buffer_major_mode (buf);
 	}
       return buf;
@@ -1636,7 +1641,7 @@ other_buffer_safely (Lisp_Object buffer)
   buf = Fget_buffer (scratch);
   if (NILP (buf))
     {
-      buf = Fget_buffer_create (scratch);
+      buf = Fget_buffer_create (scratch, Qnil);
       Fset_buffer_major_mode (buf);
     }
 
@@ -1713,7 +1718,9 @@ DEFUN ("kill-buffer", Fkill_buffer, Skill_buffer, 0, 1, "bKill buffer: ",
 the buffer is not killed.  The hook `kill-buffer-hook' is run before the
 buffer is actually killed.  The buffer being killed will be current
 while the hook is running.  Functions called by any of these hooks are
-supposed to not change the current buffer.
+supposed to not change the current buffer.  Neither hook is run for
+internal or temporary buffers created by `get-buffer-create' or
+`generate-new-buffer' with argument INHIBIT-BUFFER-HOOKS non-nil.
 
 Any processes that have this buffer as the `process-buffer' are killed
 with SIGHUP.  This function calls `replace-buffer-in-windows' for
@@ -1973,9 +1980,7 @@ DEFUN ("kill-buffer", Fkill_buffer, Skill_buffer, 0, 1, "bKill buffer: ",
   bset_width_table (b, Qnil);
   unblock_input ();
 
-  /* Run buffer-list-update-hook.  */
-  if (!NILP (Vrun_hooks) && !b->inhibit_buffer_hooks)
-    call1 (Vrun_hooks, Qbuffer_list_update_hook);
+  run_buffer_list_update_hook (b);
 
   return Qt;
 }
@@ -2015,9 +2020,7 @@ record_buffer (Lisp_Object buffer)
   fset_buffer_list (f, Fcons (buffer, Fdelq (buffer, f->buffer_list)));
   fset_buried_buffer_list (f, Fdelq (buffer, f->buried_buffer_list));
 
-  /* Run buffer-list-update-hook.  */
-  if (!NILP (Vrun_hooks) && !XBUFFER (buffer)->inhibit_buffer_hooks)
-    call1 (Vrun_hooks, Qbuffer_list_update_hook);
+  run_buffer_list_update_hook (XBUFFER (buffer));
 }

@@ -2054,9 +2057,7 @@ DEFUN ("bury-buffer-internal", Fbury_buffer_internal, Sbury_buffer_internal,
   fset_buried_buffer_list
     (f, Fcons (buffer, Fdelq (buffer, f->buried_buffer_list)));
 
-  /* Run buffer-list-update-hook.  */
-  if (!NILP (Vrun_hooks) && !XBUFFER (buffer)->inhibit_buffer_hooks)
-    call1 (Vrun_hooks, Qbuffer_list_update_hook);
+  run_buffer_list_update_hook (XBUFFER (buffer));
 
   return Qnil;
 }
@@ -5349,10 +5350,11 @@ init_buffer_once (void)
   Fput (Qkill_buffer_hook, Qpermanent_local, Qt);
 
   /* Super-magic invisible buffer.  */
-  Vprin1_to_string_buffer = Fget_buffer_create (build_pure_c_string (" prin1"));
+  Vprin1_to_string_buffer =
+    Fget_buffer_create (build_pure_c_string (" prin1"), Qt);
   Vbuffer_alist = Qnil;
 
-  Fset_buffer (Fget_buffer_create (build_pure_c_string ("*scratch*")));
+  Fset_buffer (Fget_buffer_create (build_pure_c_string ("*scratch*"), Qnil));
 
   inhibit_modification_hooks = 0;
 }
@@ -5397,7 +5399,7 @@ init_buffer (void)
 #endif /* USE_MMAP_FOR_BUFFERS */
 
   AUTO_STRING (scratch, "*scratch*");
-  Fset_buffer (Fget_buffer_create (scratch));
+  Fset_buffer (Fget_buffer_create (scratch, Qnil));
   if (NILP (BVAR (&buffer_defaults, enable_multibyte_characters)))
     Fset_buffer_multibyte (Qnil);
 
@@ -6300,9 +6302,14 @@ from (abs POSITION).  If POSITION is positive, point was at the front
   DEFVAR_LISP ("kill-buffer-query-functions", Vkill_buffer_query_functions,
 	       doc: /* List of functions called with no args to query before killing a buffer.
 The buffer being killed will be current while the functions are running.
+See `kill-buffer'.
 
 If any of them returns nil, the buffer is not killed.  Functions run by
-this hook are supposed to not change the current buffer.  */);
+this hook are supposed to not change the current buffer.
+
+This hook is not run for internal or temporary buffers created by
+`get-buffer-create' or `generate-new-buffer' with argument
+INHIBIT-BUFFER-HOOKS non-nil.  */);
   Vkill_buffer_query_functions = Qnil;
 
   DEFVAR_LISP ("change-major-mode-hook", Vchange_major_mode_hook,
@@ -6315,9 +6322,12 @@ from (abs POSITION).  If POSITION is positive, point was at the front
 	       doc: /* Hook run when the buffer list changes.
 Functions (implicitly) running this hook are `get-buffer-create',
 `make-indirect-buffer', `rename-buffer', `kill-buffer', `bury-buffer'
-and `select-window'.  Functions run by this hook should avoid calling
-`select-window' with a nil NORECORD argument or `with-temp-buffer'
-since either may lead to infinite recursion.  */);
+and `select-window'.  This hook is not run for internal or temporary
+buffers created by `get-buffer-create' or `generate-new-buffer' with
+argument INHIBIT-BUFFER-HOOKS non-nil.
+
+Functions run by this hook should avoid calling `select-window' with a
+nil NORECORD argument since it may lead to infinite recursion.  */);
   Vbuffer_list_update_hook = Qnil;
   DEFSYM (Qbuffer_list_update_hook, "buffer-list-update-hook");
 
diff --git a/src/buffer.h b/src/buffer.h
index fe549c5dac..b8c5162be4 100644
--- a/src/buffer.h
+++ b/src/buffer.h
@@ -663,11 +663,11 @@ #define BVAR(buf, field) ((buf)->field ## _)
   /* Non-zero whenever the narrowing is changed in this buffer.  */
   bool_bf clip_changed : 1;
 
-  /* Non-zero for internally used temporary buffers that don't need to
-     run hooks kill-buffer-hook, buffer-list-update-hook, and
-     kill-buffer-query-functions.  This is used in coding.c to avoid
-     slowing down en/decoding when there are a lot of these hooks
-     defined.  */
+  /* Non-zero for internal or temporary buffers that don't need to
+     run hooks kill-buffer-hook, kill-buffer-query-functions, and
+     buffer-list-update-hook.  This is used in coding.c to avoid
+     slowing down en/decoding when a lot of these hooks are
+     defined, as well as by with-temp-buffer, for example.  */
   bool_bf inhibit_buffer_hooks : 1;
 
   /* List of overlays that end at or before the current center,
diff --git a/src/callproc.c b/src/callproc.c
index e3346e2eab..4bca1e5ebd 100644
--- a/src/callproc.c
+++ b/src/callproc.c
@@ -405,9 +405,8 @@ call_process (ptrdiff_t nargs, Lisp_Object *args, int filefd,
 
       if (! (NILP (buffer) || EQ (buffer, Qt) || FIXNUMP (buffer)))
 	{
-	  Lisp_Object spec_buffer;
-	  spec_buffer = buffer;
-	  buffer = Fget_buffer_create (buffer);
+	  Lisp_Object spec_buffer = buffer;
+	  buffer = Fget_buffer_create (buffer, Qnil);
 	  /* Mention the buffer name for a better error message.  */
 	  if (NILP (buffer))
 	    CHECK_BUFFER (spec_buffer);
diff --git a/src/coding.c b/src/coding.c
index 2142e7fa51..1afa4aa474 100644
--- a/src/coding.c
+++ b/src/coding.c
@@ -7821,7 +7821,7 @@ encode_coding (struct coding_system *coding)
 
 /* A string that serves as name of the reusable work buffer, and as base
    name of temporary work buffers used for code-conversion operations.  */
-Lisp_Object Vcode_conversion_workbuf_name;
+static Lisp_Object Vcode_conversion_workbuf_name;
 
 /* The reusable working buffer, created once and never killed.  */
 static Lisp_Object Vcode_conversion_reused_workbuf;
@@ -7839,7 +7839,7 @@ code_conversion_restore (Lisp_Object arg)
   if (! NILP (workbuf))
     {
       if (EQ (workbuf, Vcode_conversion_reused_workbuf))
-	reused_workbuf_in_use = 0;
+	reused_workbuf_in_use = false;
       else
 	Fkill_buffer (workbuf);
     }
@@ -7857,13 +7857,13 @@ code_conversion_save (bool with_work_buf, bool multibyte)
 	{
 	  Lisp_Object name
 	    = Fgenerate_new_buffer_name (Vcode_conversion_workbuf_name, Qnil);
-	  workbuf = Fget_buffer_create (name);
+	  workbuf = Fget_buffer_create (name, Qt);
 	}
       else
 	{
 	  if (NILP (Fbuffer_live_p (Vcode_conversion_reused_workbuf)))
 	    Vcode_conversion_reused_workbuf
-	      = Fget_buffer_create (Vcode_conversion_workbuf_name);
+	      = Fget_buffer_create (Vcode_conversion_workbuf_name, Qt);
 	  workbuf = Vcode_conversion_reused_workbuf;
 	}
     }
@@ -7881,7 +7881,7 @@ code_conversion_save (bool with_work_buf, bool multibyte)
       bset_undo_list (current_buffer, Qt);
       bset_enable_multibyte_characters (current_buffer, multibyte ? Qt : Qnil);
       if (EQ (workbuf, Vcode_conversion_reused_workbuf))
-	reused_workbuf_in_use = 1;
+	reused_workbuf_in_use = true;
       set_buffer_internal (current);
     }
 
@@ -11639,7 +11639,7 @@ syms_of_coding (void)
   staticpro (&Vcode_conversion_workbuf_name);
   Vcode_conversion_workbuf_name = build_pure_c_string (" *code-conversion-work*");
 
-  reused_workbuf_in_use = 0;
+  reused_workbuf_in_use = false;
   PDUMPER_REMEMBER_SCALAR (reused_workbuf_in_use);
 
   DEFSYM (Qcharset, "charset");
diff --git a/src/coding.h b/src/coding.h
index 4973cf89eb..9ad1e954f8 100644
--- a/src/coding.h
+++ b/src/coding.h
@@ -97,9 +97,6 @@ #define EMACS_CODING_H
 
 extern Lisp_Object Vcoding_system_hash_table;
 
-/* Name (or base name) of work buffer for code conversion.  */
-extern Lisp_Object Vcode_conversion_workbuf_name;
-
 /* Enumeration of index to an attribute vector of a coding system.  */
 
 enum coding_attr_index
diff --git a/src/fileio.c b/src/fileio.c
index 283813ff89..c8d2e923a0 100644
--- a/src/fileio.c
+++ b/src/fileio.c
@@ -4003,7 +4003,7 @@ because (1) it preserves some marker positions and (2) it puts less data
 
 		  record_unwind_current_buffer ();
 
-		  workbuf = Fget_buffer_create (name);
+		  workbuf = Fget_buffer_create (name, Qt);
 		  buf = XBUFFER (workbuf);
 
 		  delete_all_overlays (buf);
diff --git a/src/minibuf.c b/src/minibuf.c
index fc3fd92a88..1940564a80 100644
--- a/src/minibuf.c
+++ b/src/minibuf.c
@@ -809,7 +809,7 @@ get_minibuffer (EMACS_INT depth)
       static char const name_fmt[] = " *Minibuf-%"pI"d*";
       char name[sizeof name_fmt + INT_STRLEN_BOUND (EMACS_INT)];
       AUTO_STRING_WITH_LEN (lname, name, sprintf (name, name_fmt, depth));
-      buf = Fget_buffer_create (lname);
+      buf = Fget_buffer_create (lname, Qnil);
 
       /* Although the buffer's name starts with a space, undo should be
 	 enabled in it.  */
diff --git a/src/print.c b/src/print.c
index 008bf5e639..ec271d914c 100644
--- a/src/print.c
+++ b/src/print.c
@@ -562,7 +562,7 @@ temp_output_buffer_setup (const char *bufname)
 
   record_unwind_current_buffer ();
 
-  Fset_buffer (Fget_buffer_create (build_string (bufname)));
+  Fset_buffer (Fget_buffer_create (build_string (bufname), Qnil));
 
   Fkill_all_local_variables ();
   delete_all_overlays (current_buffer);
diff --git a/src/process.c b/src/process.c
index bf64ead24e..6dc7d4b647 100644
--- a/src/process.c
+++ b/src/process.c
@@ -1731,7 +1731,7 @@ DEFUN ("make-process", Fmake_process, Smake_process, 0, MANY, 0,
 
   buffer = Fplist_get (contact, QCbuffer);
   if (!NILP (buffer))
-    buffer = Fget_buffer_create (buffer);
+    buffer = Fget_buffer_create (buffer, Qnil);
 
   /* Make sure that the child will be able to chdir to the current
      buffer's current directory, or its unhandled equivalent.  We
@@ -1768,7 +1768,7 @@ DEFUN ("make-process", Fmake_process, Smake_process, 0, MANY, 0,
 			  QCname,
 			  concat2 (name, build_string (" stderr")),
 			  QCbuffer,
-			  Fget_buffer_create (xstderr),
+			  Fget_buffer_create (xstderr, Qnil),
 			  QCnoquery,
 			  query_on_exit ? Qnil : Qt);
     }
@@ -2443,7 +2443,7 @@ DEFUN ("make-pipe-process", Fmake_pipe_process, Smake_pipe_process,
   buffer = Fplist_get (contact, QCbuffer);
   if (NILP (buffer))
     buffer = name;
-  buffer = Fget_buffer_create (buffer);
+  buffer = Fget_buffer_create (buffer, Qnil);
   pset_buffer (p, buffer);
 
   pset_childp (p, contact);
@@ -3173,7 +3173,7 @@ DEFUN ("make-serial-process", Fmake_serial_process, Smake_serial_process,
   buffer = Fplist_get (contact, QCbuffer);
   if (NILP (buffer))
     buffer = name;
-  buffer = Fget_buffer_create (buffer);
+  buffer = Fget_buffer_create (buffer, Qnil);
   pset_buffer (p, buffer);
 
   pset_childp (p, contact);
@@ -4188,7 +4188,7 @@ DEFUN ("make-network-process", Fmake_network_process, Smake_network_process,
  open_socket:
 
   if (!NILP (buffer))
-    buffer = Fget_buffer_create (buffer);
+    buffer = Fget_buffer_create (buffer, Qnil);
 
   /* Unwind bind_polling_period.  */
   unbind_to (count, Qnil);
@@ -4961,7 +4961,7 @@ server_accept_connection (Lisp_Object server, int channel)
       if (!NILP (buffer))
 	{
 	  args[1] = buffer;
-	  buffer = Fget_buffer_create (Fformat (nargs, args));
+	  buffer = Fget_buffer_create (Fformat (nargs, args), Qnil);
 	}
     }
 
diff --git a/src/w32fns.c b/src/w32fns.c
index a840f0e122..36bee0676b 100644
--- a/src/w32fns.c
+++ b/src/w32fns.c
@@ -7372,7 +7372,7 @@ DEFUN ("x-show-tip", Fx_show_tip, Sx_show_tip, 1, 6, 0,
 
   tip_f = XFRAME (tip_frame);
   window = FRAME_ROOT_WINDOW (tip_f);
-  tip_buf = Fget_buffer_create (tip);
+  tip_buf = Fget_buffer_create (tip, Qnil);
   /* We will mark the tip window a "pseudo-window" below, and such
      windows cannot have display margins.  */
   bset_left_margin_cols (XBUFFER (tip_buf), make_fixnum (0));
diff --git a/src/window.c b/src/window.c
index 6cd3122b43..4dd11eb857 100644
--- a/src/window.c
+++ b/src/window.c
@@ -617,11 +617,12 @@ DEFUN ("select-window", Fselect_window, Sselect_window, 1, 2, 0,
 Run `buffer-list-update-hook' unless NORECORD is non-nil.  Note that
 applications and internal routines often select a window temporarily for
 various purposes; mostly, to simplify coding.  As a rule, such
-selections should be not recorded and therefore will not pollute
+selections should not be recorded and therefore will not pollute
 `buffer-list-update-hook'.  Selections that "really count" are those
 causing a visible change in the next redisplay of WINDOW's frame and
-should be always recorded.  So if you think of running a function each
-time a window gets selected put it on `buffer-list-update-hook'.
+should always be recorded.  So if you think of running a function each
+time a window gets selected, put it on `buffer-list-update-hook' or
+`window-selection-change-functions'.
 
 Also note that the main editor command loop sets the current buffer to
 the buffer of the selected window before each command.  */)
diff --git a/src/xdisp.c b/src/xdisp.c
index ed1d4761b9..22d33f4a1f 100644
--- a/src/xdisp.c
+++ b/src/xdisp.c
@@ -10853,7 +10853,7 @@ message_dolog (const char *m, ptrdiff_t nbytes, bool nlflag, bool multibyte)
       /* Ensure the Messages buffer exists, and switch to it.
          If we created it, set the major-mode.  */
       bool newbuffer = NILP (Fget_buffer (Vmessages_buffer_name));
-      Fset_buffer (Fget_buffer_create (Vmessages_buffer_name));
+      Fset_buffer (Fget_buffer_create (Vmessages_buffer_name, Qnil));
       if (newbuffer
 	  && !NILP (Ffboundp (intern ("messages-buffer-mode"))))
 	call0 (intern ("messages-buffer-mode"));
@@ -11339,7 +11339,7 @@ ensure_echo_area_buffers (void)
 	static char const name_fmt[] = " *Echo Area %d*";
 	char name[sizeof name_fmt + INT_STRLEN_BOUND (int)];
 	AUTO_STRING_WITH_LEN (lname, name, sprintf (name, name_fmt, i));
-	echo_buffer[i] = Fget_buffer_create (lname);
+	echo_buffer[i] = Fget_buffer_create (lname, Qnil);
 	bset_truncate_lines (XBUFFER (echo_buffer[i]), Qnil);
 	/* to force word wrap in echo area -
 	   it was decided to postpone this*/
diff --git a/src/xfns.c b/src/xfns.c
index 46e4bd73a6..abe293e903 100644
--- a/src/xfns.c
+++ b/src/xfns.c
@@ -7041,7 +7041,7 @@ DEFUN ("x-show-tip", Fx_show_tip, Sx_show_tip, 1, 6, 0,
 
   tip_f = XFRAME (tip_frame);
   window = FRAME_ROOT_WINDOW (tip_f);
-  tip_buf = Fget_buffer_create (tip);
+  tip_buf = Fget_buffer_create (tip, Qnil);
   /* We will mark the tip window a "pseudo-window" below, and such
      windows cannot have display margins.  */
   bset_left_margin_cols (XBUFFER (tip_buf), make_fixnum (0));
diff --git a/src/xwidget.c b/src/xwidget.c
index e078a28a35..accde65b52 100644
--- a/src/xwidget.c
+++ b/src/xwidget.c
@@ -100,7 +100,8 @@ DEFUN ("make-xwidget",
   Lisp_Object val;
   xw->type = type;
   xw->title = title;
-  xw->buffer = NILP (buffer) ? Fcurrent_buffer () : Fget_buffer_create (buffer);
+  xw->buffer = (NILP (buffer) ? Fcurrent_buffer ()
+		: Fget_buffer_create (buffer, Qnil));
   xw->height = XFIXNAT (height);
   xw->width = XFIXNAT (width);
   xw->kill_without_query = false;
diff --git a/test/src/buffer-tests.el b/test/src/buffer-tests.el
index 0db66f9751..dd8927457a 100644
--- a/test/src/buffer-tests.el
+++ b/test/src/buffer-tests.el
@@ -19,9 +19,7 @@
 
 ;;; Code:
 
-(require 'ert)
-(require 'seq)
-(eval-when-compile (require 'cl-lib))
+(require 'cl-lib)
 
 (ert-deftest overlay-modification-hooks-message-other-buf ()
   "Test for bug#21824.
@@ -1334,4 +1332,33 @@ buffer-tests-buffer-local-variables-undo
   (with-temp-buffer
     (should (assq 'buffer-undo-list (buffer-local-variables)))))
 
+(ert-deftest buffer-tests-inhibit-buffer-hooks ()
+  "Test `get-buffer-create' argument INHIBIT-BUFFER-HOOKS."
+  (let* (run-bluh (bluh (lambda () (setq run-bluh t))))
+    (unwind-protect
+        (let* ( run-kbh  (kbh  (lambda () (setq run-kbh  t)))
+                run-kbqf (kbqf (lambda () (setq run-kbqf t))) )
+
+          ;; Inhibited.
+          (add-hook 'buffer-list-update-hook bluh)
+          (with-current-buffer (generate-new-buffer " foo" t)
+            (add-hook 'kill-buffer-hook kbh nil t)
+            (add-hook 'kill-buffer-query-functions kbqf nil t)
+            (kill-buffer))
+          (with-temp-buffer)
+          (with-output-to-string)
+          (should-not run-bluh)
+          (should-not run-kbh)
+          (should-not run-kbqf)
+
+          ;; Not inhibited.
+          (with-current-buffer (generate-new-buffer " foo")
+            (should run-bluh)
+            (add-hook 'kill-buffer-hook kbh nil t)
+            (add-hook 'kill-buffer-query-functions kbqf nil t)
+            (kill-buffer))
+          (should run-kbh)
+          (should run-kbqf))
+      (remove-hook 'buffer-list-update-hook bluh))))
+
 ;;; buffer-tests.el ends here
-- 
2.29.2