Subject: Re: bug#51993: 29.0.50; [PATCH explanation] Killing emacsclient terminal with `server-stop-automatically' doesn't prompt to save files

From: Jim Porter <[email-masked]>
Date: Thu, 1 Dec 2022 17:42:18 -0800

On 12/1/2022 9:29 AM, Eli Zaretskii wrote:
> I'm okay with installing the original changes on master, if you indeed
> believe the new code is much cleaner (but then please explain why you think
> so, because I don't think I see that just by looking at the diffs).  But for
> the release branch, I'm not comfortable with making such serious changes in
> a part of server.el that is already way too complicated, what with all the
> fancy shutdown options we strive to support.  There be dragons, and I have
> no intention to release Emacs 29 with buggy server-client editing.  So for
> the release branch, please prepare a safer version of the change, which only
> changes the code which is the immediate cause of the incorrect behavior.

Attached is a patch series that explains in more detail how I arrived at 
the previous version of my patch. This is basically a reconstruction of 
the steps I took when writing it originally. I'll describe my overall 
plan and then address the specific comments you had after that. (This 
might be overly-verbose, but I wanted to put as much detail in as I 
could in the hopes of addressing all your concerns.)

Prior to my patch, 'server-stop-automatically--handle-delete-frame' 
(henceforth 'SSA--handle-delete-frame') can get called in two 
situations: when someone calls 'delete-frame' (it's a hook on 
'delete-frame-functions') or when someone calls 
'save-buffers-kill-emacs' ('server-save-buffers-kill-emacs' delegates to 
it when configured to). To help make the logic easier to follow, I split 
it into two: one for handling 'delete-frame', and one for handling 
'save-buffers-kill-terminal'. See patches 0001 and 0002.

>>  (defun server-stop-automatically--handle-delete-frame (frame)
>>    "Handle deletion of FRAME when `server-stop-automatically' is used."
>> -  (when server-stop-automatically
>> -    (if (if (and (processp (frame-parameter frame 'client))
>> -		 (eq this-command 'save-buffers-kill-terminal))
>> -	    (progn
>> -	      (dolist (f (frame-list))
>> -		(when (and (eq (frame-parameter frame 'client)
>> -			       (frame-parameter f 'client))
>> -			   (not (eq frame f)))
>> -		  (set-frame-parameter f 'client nil)
>> -		  (let ((server-stop-automatically nil))
>> -		    (delete-frame f))))
>> -	      (if (cddr (frame-list))
>> -		  (let ((server-stop-automatically nil))
>> -		    (delete-frame frame)
>> -		    nil)
>> -		t))
>> +  (when (and server-stop-automatically
>>               (null (cddr (frame-list))))
>>      (let ((server-stop-automatically nil))
>>        (save-buffers-kill-emacs)
>> -	  (delete-frame frame)))))
>> +      (delete-frame frame))))
> 
> And here you completely rewrote a function.

In patch 0002, you can see (most of) this change that you mentioned: 
since I made one function for each case as described above, the second 
'if' statement's conditional is always false, so I just got rid of the 
conditional and the then-clause, leaving only the else-clause: (null 
(cddr (frame-list))). I also simplified this function a bit in the last 
patch, 0007.

Now for the rest of the patch series.

The original bug is that the behavior of 
'server-save-buffers-kill-terminal' when there are multiple clients 
should be the same regardless of the SSA setting (it wasn't). So next, I 
made 'SSA--handle-kill-terminal' have the same basic structure as 
'server-save-buffers-kill-terminal' (patch 0003). That makes it easier 
to see the differences between the two.

Patch 0005 is where the real fix is: it makes sure that when we *don't* 
want to kill Emacs, 'SSA--handle-kill-terminal' does the same thing as 
'server-save-buffers-kill-terminal'.

>> @@ -1801,31 +1809,20 @@ server-save-buffers-kill-terminal
>>                          ;; ARG is non-nil), since we're not killing
>>                          ;; Emacs (unlike `save-buffers-kill-emacs').
>>  		        (and arg t)))
>> -	       (server-delete-client proc)))
>> -	    (t (error "Invalid client frame"))))))
>> +	         (server-delete-client proc))
>> +             ;; If `server-stop-automatically' is set, there are no
>> +             ;; other client processes, and no other client frames
>> +             ;; (e.g. `nowait' frames), kill Emacs.
>> +             (save-buffers-kill-emacs arg)))
>> +	  (t (error "Invalid client frame")))))
> 
> But this one is problematic: it adds save-buffers-kill-emacs which wasn't in
> the original code, and I don't understand why.  The bug wasn't about this,
> was it?

In patch 0005, you can start to see this block of code take shape: 
because we want to handle the "don't kill Emacs" case in 
'SSA--handle-kill-terminal', we add the 'save-some-buffers' + 
'server-delete-client' code there, resulting in something that looks 
similar to the above hunk.

Then, in patch 0006, I just merge 'server-save-buffers-kill-terminal' 
and 'SSA--handle-kill-terminal', since most of the code is shared at 
this point. Finally, patch 0007 is just a bit of cleanup; with all of 
these applied, server.el should be identical to my previous patch.

Hopefully this explains things reasonably well, and doesn't go into too 
much (or too little) detail. If there are any other bits that you have 
concerns about, just let me know.

From 74c724a46b16782d6827357dd64efbafe8aeb92f Mon Sep 17 00:00:00 2001
From: Jim Porter <[email-masked]>
Date: Thu, 1 Dec 2022 11:33:18 -0800
Subject: [PATCH 1/7] Duplicate
 'server-stop-automatically--handle-delete-frame'

* lisp/server.el (server-stop-automatically--handle-delete-frame):
Copy this...
(server-stop-automatically--handle-kill-terminal) ... to this.
(server-save-buffers-kill-terminal): Use it.
---
 lisp/server.el | 25 ++++++++++++++++++++++++-
 1 file changed, 24 insertions(+), 1 deletion(-)

diff --git a/lisp/server.el b/lisp/server.el
index 1b027f88ce..0a59c8496a 100644
--- a/lisp/server.el
+++ b/lisp/server.el
@@ -1781,7 +1781,7 @@ server-save-buffers-kill-terminal
 If emacsclient was started with a list of filenames to edit, then
 only these files will be asked to be saved."
   (if server-stop-automatically
-      (server-stop-automatically--handle-delete-frame (selected-frame))
+      (server-stop-automatically--handle-kill-terminal (selected-frame))
     (let ((proc (frame-parameter nil 'client)))
       (cond ((eq proc 'nowait)
 	     ;; Nowait frames have no client buffer list.
@@ -1827,6 +1827,29 @@ server-stop-automatically--handle-delete-frame
 	  (save-buffers-kill-emacs)
 	  (delete-frame frame)))))
 
+(defun server-stop-automatically--handle-kill-terminal (frame)
+  "Handle deletion of FRAME when `server-stop-automatically' is used."
+  (when server-stop-automatically
+    (if (if (and (processp (frame-parameter frame 'client))
+		 (eq this-command 'save-buffers-kill-terminal))
+	    (progn
+	      (dolist (f (frame-list))
+		(when (and (eq (frame-parameter frame 'client)
+			       (frame-parameter f 'client))
+			   (not (eq frame f)))
+		  (set-frame-parameter f 'client nil)
+		  (let ((server-stop-automatically nil))
+		    (delete-frame f))))
+	      (if (cddr (frame-list))
+		  (let ((server-stop-automatically nil))
+		    (delete-frame frame)
+		    nil)
+		t))
+	  (null (cddr (frame-list))))
+	(let ((server-stop-automatically nil))
+	  (save-buffers-kill-emacs)
+	  (delete-frame frame)))))
+
 (defun server-stop-automatically--maybe-kill-emacs ()
   "Handle closing of Emacs daemon when `server-stop-automatically' is used."
   (unless (cdr (frame-list))
-- 
2.25.1

From be0b4556b9cb1ab882d7e6ce5e85dd20cfdc9d96 Mon Sep 17 00:00:00 2001
From: Jim Porter <[email-masked]>
Date: Thu, 1 Dec 2022 11:35:00 -0800
Subject: [PATCH 2/7] Simplify server-stop-automatically handlers

* lisp/server.el (server-stop-automatically--handle-delete-frame):
'this-command' is never 'save-buffers-kill-terminal' here; remove
the whole conditional.
(server-stop-automatically--handle-kill-terminal): 'this-command' is
always 'save-buffers-kill-terminal', so remove that test.
---
 lisp/server.el | 20 ++------------------
 1 file changed, 2 insertions(+), 18 deletions(-)

diff --git a/lisp/server.el b/lisp/server.el
index 0a59c8496a..64332442d3 100644
--- a/lisp/server.el
+++ b/lisp/server.el
@@ -1807,22 +1807,7 @@ server-save-buffers-kill-terminal
 (defun server-stop-automatically--handle-delete-frame (frame)
   "Handle deletion of FRAME when `server-stop-automatically' is used."
   (when server-stop-automatically
-    (if (if (and (processp (frame-parameter frame 'client))
-		 (eq this-command 'save-buffers-kill-terminal))
-	    (progn
-	      (dolist (f (frame-list))
-		(when (and (eq (frame-parameter frame 'client)
-			       (frame-parameter f 'client))
-			   (not (eq frame f)))
-		  (set-frame-parameter f 'client nil)
-		  (let ((server-stop-automatically nil))
-		    (delete-frame f))))
-	      (if (cddr (frame-list))
-		  (let ((server-stop-automatically nil))
-		    (delete-frame frame)
-		    nil)
-		t))
-	  (null (cddr (frame-list))))
+    (if (null (cddr (frame-list)))
 	(let ((server-stop-automatically nil))
 	  (save-buffers-kill-emacs)
 	  (delete-frame frame)))))
@@ -1830,8 +1815,7 @@ server-stop-automatically--handle-delete-frame
 (defun server-stop-automatically--handle-kill-terminal (frame)
   "Handle deletion of FRAME when `server-stop-automatically' is used."
   (when server-stop-automatically
-    (if (if (and (processp (frame-parameter frame 'client))
-		 (eq this-command 'save-buffers-kill-terminal))
+    (if (if (processp (frame-parameter frame 'client))
 	    (progn
 	      (dolist (f (frame-list))
 		(when (and (eq (frame-parameter frame 'client)
-- 
2.25.1

From db0cffb849c0a0be50db7ad4a3a0d48ff343854b Mon Sep 17 00:00:00 2001
From: Jim Porter <[email-masked]>
Date: Thu, 1 Dec 2022 11:42:48 -0800
Subject: [PATCH 3/7] Restructure
 'server-stop-automatically--handle-kill-terminal'

This changes the function to have the same structure as
'server-save-buffers-kill-terminal', but otherwise works exactly the
same as before.  This just takes the nested 'if' statement and brings
it to the top level as 'cond' (note that the 'client' frame parameter
can only be a process or the symbol 'nowait' here, since otherwise
'server-save-buffers-kill-terminal' wouldn't have been called).

* lisp/server.el (server-stop-automatically--handle-kill-terminal):
Restructure.
---
 lisp/server.el | 41 +++++++++++++++++++++++------------------
 1 file changed, 23 insertions(+), 18 deletions(-)

diff --git a/lisp/server.el b/lisp/server.el
index 64332442d3..7191387959 100644
--- a/lisp/server.el
+++ b/lisp/server.el
@@ -1815,24 +1815,29 @@ server-stop-automatically--handle-delete-frame
 (defun server-stop-automatically--handle-kill-terminal (frame)
   "Handle deletion of FRAME when `server-stop-automatically' is used."
   (when server-stop-automatically
-    (if (if (processp (frame-parameter frame 'client))
-	    (progn
-	      (dolist (f (frame-list))
-		(when (and (eq (frame-parameter frame 'client)
-			       (frame-parameter f 'client))
-			   (not (eq frame f)))
-		  (set-frame-parameter f 'client nil)
-		  (let ((server-stop-automatically nil))
-		    (delete-frame f))))
-	      (if (cddr (frame-list))
-		  (let ((server-stop-automatically nil))
-		    (delete-frame frame)
-		    nil)
-		t))
-	  (null (cddr (frame-list))))
-	(let ((server-stop-automatically nil))
-	  (save-buffers-kill-emacs)
-	  (delete-frame frame)))))
+    (let ((proc (frame-parameter frame 'client)))
+      (cond ((eq proc 'nowait)
+             (if (null (cddr (frame-list)))
+                 (let ((server-stop-automatically nil))
+	           (save-buffers-kill-emacs)
+	           (delete-frame frame))))
+            ((processp proc)
+             (if (progn
+	           (dolist (f (frame-list))
+		     (when (and (eq (frame-parameter frame 'client)
+			            (frame-parameter f 'client))
+			        (not (eq frame f)))
+		       (set-frame-parameter f 'client nil)
+		       (let ((server-stop-automatically nil))
+		         (delete-frame f))))
+	           (if (cddr (frame-list))
+		       (let ((server-stop-automatically nil))
+		         (delete-frame frame)
+		         nil)
+		     t))
+	         (let ((server-stop-automatically nil))
+	           (save-buffers-kill-emacs)
+	           (delete-frame frame))))))))
 
 (defun server-stop-automatically--maybe-kill-emacs ()
   "Handle closing of Emacs daemon when `server-stop-automatically' is used."
-- 
2.25.1

From 694aad432aee73acd2335f5ea5d0f47d0c8379e2 Mon Sep 17 00:00:00 2001
From: Jim Porter <[email-masked]>
Date: Thu, 1 Dec 2022 11:48:11 -0800
Subject: [PATCH 4/7] Remove unnecessary 'delete-frame' calls after
 'save-buffers-kill-emacs'

No need to delete a frame if we're killing Emacs entirely anyway.

* lisp/server.el (server-stop-automatically--handle-kill-terminal):
Remove a couple 'delete-frame' calls.
---
 lisp/server.el | 8 ++------
 1 file changed, 2 insertions(+), 6 deletions(-)

diff --git a/lisp/server.el b/lisp/server.el
index 7191387959..399bf694fd 100644
--- a/lisp/server.el
+++ b/lisp/server.el
@@ -1818,9 +1818,7 @@ server-stop-automatically--handle-kill-terminal
     (let ((proc (frame-parameter frame 'client)))
       (cond ((eq proc 'nowait)
              (if (null (cddr (frame-list)))
-                 (let ((server-stop-automatically nil))
-	           (save-buffers-kill-emacs)
-	           (delete-frame frame))))
+	         (save-buffers-kill-emacs)))
             ((processp proc)
              (if (progn
 	           (dolist (f (frame-list))
@@ -1835,9 +1833,7 @@ server-stop-automatically--handle-kill-terminal
 		         (delete-frame frame)
 		         nil)
 		     t))
-	         (let ((server-stop-automatically nil))
-	           (save-buffers-kill-emacs)
-	           (delete-frame frame))))))))
+	         (save-buffers-kill-emacs)))))))
 
 (defun server-stop-automatically--maybe-kill-emacs ()
   "Handle closing of Emacs daemon when `server-stop-automatically' is used."
-- 
2.25.1

From 3730a0c55ed416ec007397be312c3223ebe078ad Mon Sep 17 00:00:00 2001
From: Jim Porter <[email-masked]>
Date: Thu, 1 Dec 2022 12:05:40 -0800
Subject: [PATCH 5/7] This is the commit that actually fixes bug#51993

There are two parts to this:

1. For 'nowait' frames, call 'save-some-buffers' and 'delete-frame'
   when there are other non-daemon frames.

2. For client frames (where the client is a process), don't try to
   delete all the client's frames directly.  Previously, this code
   would delete all *other* frames associated with the client, and
   then either a) delete the current frame if there are still more
   (non-daemon) frames remaining, or b) kill Emacs otherwise.  Now,
   check to see what we should do first: if there are no other clients
   (or frames not owned by the current client), then we should kill
   Emacs; otherwise, only delete this client (after saving).

* lisp/server.el (server-stop-automatically--handle-kill-terminal):
Handle the case when we *don't* kill Emacs, as described above.
---
 lisp/server.el | 41 ++++++++++++++++++++++++++---------------
 1 file changed, 26 insertions(+), 15 deletions(-)

diff --git a/lisp/server.el b/lisp/server.el
index 399bf694fd..9b91fbbbdc 100644
--- a/lisp/server.el
+++ b/lisp/server.el
@@ -1817,23 +1817,34 @@ server-stop-automatically--handle-kill-terminal
   (when server-stop-automatically
     (let ((proc (frame-parameter frame 'client)))
       (cond ((eq proc 'nowait)
-             (if (null (cddr (frame-list)))
+             (if (cddr (frame-list))
+                 ;; If there are any other non-daemon frames, don't
+                 ;; kill Emacs, but *do* save some buffers, as in
+                 ;; `server-save-buffers-kill-terminal'.
+                 (progn
+                   (save-some-buffers)
+	           (delete-frame))
+                 ;; If we're the last non-daemon frame standing, kill
+                 ;; Emacs.
 	         (save-buffers-kill-emacs)))
             ((processp proc)
-             (if (progn
-	           (dolist (f (frame-list))
-		     (when (and (eq (frame-parameter frame 'client)
-			            (frame-parameter f 'client))
-			        (not (eq frame f)))
-		       (set-frame-parameter f 'client nil)
-		       (let ((server-stop-automatically nil))
-		         (delete-frame f))))
-	           (if (cddr (frame-list))
-		       (let ((server-stop-automatically nil))
-		         (delete-frame frame)
-		         nil)
-		     t))
-	         (save-buffers-kill-emacs)))))))
+             (if ;; Keep the server alive if...
+                 (or
+                  ;; a) there are any other clients, or...
+                  (length> server-clients 1)
+                  ;; b) there are any frames not owned by this client.
+                  (seq-some
+                   (lambda (frame)
+                     (when-let ((p (frame-parameter frame 'client)))
+                       (not (eq proc p))))
+                   (frame-list)))
+                 (let ((buffers (process-get proc 'buffers)))
+                   (save-some-buffers
+                    nil (if buffers
+                            ;; Only files from emacsclient file list.
+		            (lambda () (memq (current-buffer) buffers))))
+                   (server-delete-client))
+               (save-buffers-kill-emacs)))))))
 
 (defun server-stop-automatically--maybe-kill-emacs ()
   "Handle closing of Emacs daemon when `server-stop-automatically' is used."
-- 
2.25.1

From fe6ff62de3f18c2d8f9c0a0032fff39615380299 Mon Sep 17 00:00:00 2001
From: Jim Porter <[email-masked]>
Date: Thu, 1 Dec 2022 12:19:44 -0800
Subject: [PATCH 6/7] Merge kill-terminal implementations

Now that 'server-stop-automatically--handle-kill-terminal' and
'server-save-buffers-kill-terminal' are almost the same, merge them
into a single function.

* lisp/server.el (server-stop-automatically--handle-kill-terminal):
Merge this...
(server-save-buffers-kill-terminal): ... into this.
---
 lisp/server.el | 92 +++++++++++++++++++-------------------------------
 1 file changed, 35 insertions(+), 57 deletions(-)

diff --git a/lisp/server.el b/lisp/server.el
index 9b91fbbbdc..6eeaa932a7 100644
--- a/lisp/server.el
+++ b/lisp/server.el
@@ -1780,29 +1780,41 @@ server-save-buffers-kill-terminal
 
 If emacsclient was started with a list of filenames to edit, then
 only these files will be asked to be saved."
-  (if server-stop-automatically
-      (server-stop-automatically--handle-kill-terminal (selected-frame))
-    (let ((proc (frame-parameter nil 'client)))
-      (cond ((eq proc 'nowait)
-	     ;; Nowait frames have no client buffer list.
-	     (if (cdr (frame-list))
-	         (progn (save-some-buffers arg)
-		        (delete-frame))
-	       ;; If we're the last frame standing, kill Emacs.
-	       (save-buffers-kill-emacs arg)))
-	    ((processp proc)
-	     (let ((buffers (process-get proc 'buffers)))
-	       (save-some-buffers
-	        arg (if buffers
-                        ;; Only files from emacsclient file list.
-		        (lambda () (memq (current-buffer) buffers))
-                      ;; No emacsclient file list: don't override
-                      ;; `save-some-buffers-default-predicate' (unless
-                      ;; ARG is non-nil), since we're not killing
-                      ;; Emacs (unlike `save-buffers-kill-emacs').
-		      (and arg t)))
-	       (server-delete-client proc)))
-	    (t (error "Invalid client frame"))))))
+  (let ((proc (frame-parameter nil 'client)))
+    (cond ((eq proc 'nowait)
+	   ;; Nowait frames have no client buffer list.
+	   (if (length> (frame-list) (if server-stop-automatically 2 1))
+               ;; If there are any other frames, only delete this one.
+               ;; When `server-stop-automatically' is set, don't count
+               ;; the daemon frame.
+	       (progn (save-some-buffers arg)
+		      (delete-frame))
+	     ;; If we're the last frame standing, kill Emacs.
+	     (save-buffers-kill-emacs arg)))
+	  ((processp proc)
+           (if (or (not server-stop-automatically)
+                   (length> server-clients 1)
+                   (seq-some
+                    (lambda (frame)
+                      (when-let ((p (frame-parameter frame 'client)))
+                        (not (eq proc p))))
+                    (frame-list)))
+	       (let ((buffers (process-get proc 'buffers)))
+	         (save-some-buffers
+	          arg (if buffers
+                          ;; Only files from emacsclient file list.
+		          (lambda () (memq (current-buffer) buffers))
+                        ;; No emacsclient file list: don't override
+                        ;; `save-some-buffers-default-predicate' (unless
+                        ;; ARG is non-nil), since we're not killing
+                        ;; Emacs (unlike `save-buffers-kill-emacs').
+		        (and arg t)))
+	         (server-delete-client proc))
+             ;; If `server-stop-automatically' is set, there are no
+             ;; other client processes, and no other client frames
+             ;; (e.g. `nowait' frames), kill Emacs.
+             (save-buffers-kill-emacs arg)))
+	  (t (error "Invalid client frame")))))
 
 (defun server-stop-automatically--handle-delete-frame (frame)
   "Handle deletion of FRAME when `server-stop-automatically' is used."
@@ -1812,40 +1824,6 @@ server-stop-automatically--handle-delete-frame
 	  (save-buffers-kill-emacs)
 	  (delete-frame frame)))))
 
-(defun server-stop-automatically--handle-kill-terminal (frame)
-  "Handle deletion of FRAME when `server-stop-automatically' is used."
-  (when server-stop-automatically
-    (let ((proc (frame-parameter frame 'client)))
-      (cond ((eq proc 'nowait)
-             (if (cddr (frame-list))
-                 ;; If there are any other non-daemon frames, don't
-                 ;; kill Emacs, but *do* save some buffers, as in
-                 ;; `server-save-buffers-kill-terminal'.
-                 (progn
-                   (save-some-buffers)
-	           (delete-frame))
-                 ;; If we're the last non-daemon frame standing, kill
-                 ;; Emacs.
-	         (save-buffers-kill-emacs)))
-            ((processp proc)
-             (if ;; Keep the server alive if...
-                 (or
-                  ;; a) there are any other clients, or...
-                  (length> server-clients 1)
-                  ;; b) there are any frames not owned by this client.
-                  (seq-some
-                   (lambda (frame)
-                     (when-let ((p (frame-parameter frame 'client)))
-                       (not (eq proc p))))
-                   (frame-list)))
-                 (let ((buffers (process-get proc 'buffers)))
-                   (save-some-buffers
-                    nil (if buffers
-                            ;; Only files from emacsclient file list.
-		            (lambda () (memq (current-buffer) buffers))))
-                   (server-delete-client))
-               (save-buffers-kill-emacs)))))))
-
 (defun server-stop-automatically--maybe-kill-emacs ()
   "Handle closing of Emacs daemon when `server-stop-automatically' is used."
   (unless (cdr (frame-list))
-- 
2.25.1

From a79b4f34faf2c0bd30f8d66f87e4d84832477371 Mon Sep 17 00:00:00 2001
From: Jim Porter <[email-masked]>
Date: Thu, 1 Dec 2022 12:23:17 -0800
Subject: [PATCH 7/7] Simplify 'server-stop-automatically--handle-delete-frame'

* lisp/server.el (server-stop-automatically--handle-delete-frame):
Combine the 'when' and 'if' into a single conditional.
---
 lisp/server.el | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/lisp/server.el b/lisp/server.el
index 6eeaa932a7..e88cffa8fb 100644
--- a/lisp/server.el
+++ b/lisp/server.el
@@ -1818,11 +1818,11 @@ server-save-buffers-kill-terminal
 
 (defun server-stop-automatically--handle-delete-frame (frame)
   "Handle deletion of FRAME when `server-stop-automatically' is used."
-  (when server-stop-automatically
-    (if (null (cddr (frame-list)))
-	(let ((server-stop-automatically nil))
-	  (save-buffers-kill-emacs)
-	  (delete-frame frame)))))
+  (when (and server-stop-automatically
+             (null (cddr (frame-list))))
+    (let ((server-stop-automatically nil))
+      (save-buffers-kill-emacs)
+      (delete-frame frame))))
 
 (defun server-stop-automatically--maybe-kill-emacs ()
   "Handle closing of Emacs daemon when `server-stop-automatically' is used."
-- 
2.25.1