emacs-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: graceful shutdown of non-interactive Elisp program


From: Paul Pogonyshev
Subject: Re: graceful shutdown of non-interactive Elisp program
Date: Sun, 14 Jun 2020 18:47:26 +0200

> If you want to raise an exception from a
> kill-emacs-hook (assuming that you want the unwind-protect forms to do
> something), you can do that from kill-emacs-hook, no?

No, I tried. There is a "shield" at C level that prevents me from passing an
exception from a `kill-emacs-hook' to the "main" code:

$ emacs --batch \
        --eval "(add-hook 'kill-emacs-hook (lambda () (signal 'unwind-the-stack nil)))" \
        --eval "(unwind-protect (while t) (message \"GOING DOWN\"))"

^CError in kill-emacs-hook ((closure (t) nil (signal 'unwind-the-stack nil))): (unwind-the-stack)

As you see, the exception doesn't succeed in making `unwind-protect'
clause to run.

> And if you had a say in handling SIGINT, what would you do in the
> handler that you cannot do in kill-emacs-hook?

It's not the point of doing something special, it's rather the point of
doing _normal_ cleanup that I expected to do otherwise. Let's say my
program does 20 different, sometimes nested and sometimes not
allocations of external resources that I need to free/close somehow.
If `unwind-protect' was guaranteed to run (even in response to C-c)
I could just do

   (unwind-protect
       (do-something-with-resource-N)
     (free) (resource-N) (code))

twenty times. However, as it stands it looks that I have to do

   (let ((free-resource-N (lambda () (free) (resource-N) (code))))
     (add-hook 'kill-emacs-hook free-resource-N)
     (unwind-protect
         (do-something-with-resource-N)
       (funcall free-resource-N))
     (remove-hook 'kill-emacs-hook free-resource-N))

every time.

Additionally, this won't work if I don't control the code that
allocates/frees the resource, i.e. if it happens to be inside some
external package.

> Also, you mention batch mode, but is it relevant?

No, not really. It's just that in batch mode I was expecting to be in full
control of Emacs "command loop" because I don't share Emacs with any
other code (or at most that code can be seen as a "library" for my
program). In interactive mode it's basically the same.

Paul


On Sun, 14 Jun 2020 at 17:01, Eli Zaretskii <eliz@gnu.org> wrote:
> From: Paul Pogonyshev <pogonyshev@gmail.com>
> Date: Sat, 13 Jun 2020 23:42:51 +0200
>
> I'm trying to make a Elisp program that is run in a non-interactive mode, i.e. essentially as `emacs --batch
> --load myfile.el'. A normal way to shutdown terminal programs is with C-c, which is expected to be "graceful"
> shutdown, e.g. the program still has a chance to save files etc.
>
> However, with Elisp I'm not sure how to achieve that except for constantly modifying `kill-emacs-hook', which
> would be a nightmare from coding perspective.
>
> Naively I would expect this print "GOING DOWN" when aborted with C-c:
>
>     $ emacs --batch --eval "(unwind-protect (while t) (message \"GOING DOWN\"))"
>
> For example, Python's handler of SIGINT raises an exception within the program, which unwinds the stack
> as usual and, unless caught, cause program termination after cleaning up as expected (e.g. running all
> `finally' clauses and closing all `with' context managers). However, in Elisp, as I understand, there is no way
> to have a say in handling SIGINT other than adding a function to `kill-emacs-hook'.

I don't think I follow.  If you want to raise an exception from a
kill-emacs-hook (assuming that you want the unwind-protect forms to do
something), you can do that from kill-emacs-hook, no?

And if you had a say in handling SIGINT, what would you do in the
handler that you cannot do in kill-emacs-hook?

Also, you mention batch mode, but is it relevant?  That is, are you
saying that Emacs behaves differently in an interactive session when
it gets a fatal signal?

I'm confused.

reply via email to

[Prev in Thread] Current Thread [Next in Thread]