Subject: bug#63620: 30.0.50; [Feature Request] run hooks on sleep/wake

From: Andrew Cohen <[email-masked]>
Date: Fri, 07 Feb 2025 22:34:01 +0800

>>>>> "EZ" == Eli Zaretskii <[email-masked]> writes:

    >> Date: Fri, 7 Feb 2025 19:43:32 +0800 (GMT+08:00) From: Andrew
    >> Cohen <[email-masked]> Cc: [email-masked], Stefan Monnier
    >> <[email-masked]>, Eli Zaretskii <[email-masked]>,
    >> [email-masked]
    >> 
    >> Having thought a bit more I am convinced that starting the block
    >> before the sleep notification is the right thing

    EZ> This sounds strange to me.  Are you absolutely sure?  Did you
    EZ> try this on some system?  

Yes, I am quite sure, and I have tried it extensively on my system. The
documentation is explicit, and now that I understand things better I see
why. 

    EZ> Why would an application need to proactively block sleep mode
    EZ> before the system announces its intention to sleep?  

The block does nothing until the system tries to sleep, so it makes no
difference if it is set hours before the attempt to sleep, or
milliseconds before; the only relevant thing is that the block is in
place /before/ the system starts the sleep process. Setting the block
early just /guarantees/ that the block is in place at the time sleep is
called, and otherwise has no effect.  If you try to install AFTER
getting the signal, it may be too late. From the documentation (FYI
"watching" means registering a monitor for the sleep signal so that some
action can be taken when the signal is received):

   "Note that watching PrepareForShutdown(true)/PrepareForSleep(true)
   without taking a delay lock is racy and should not be done, as any
   code that an application might want to execute on this signal might
   not actually finish before the suspend/shutdown cycle is executed.

   Again: if you watch PrepareForShutdown(true)/PrepareForSleep(true),
   then you really should have taken a delay lock first."

    EZ> Won't doing that prevent sleep, including one due to system
    EZ> idleness, altogether?

No it won't. If the sleep request is initiated through logind (which on
most systems, /all/ sleep requests are) then the application that has
set the block is monitoring the PrepareForSleep signal and will do its
work and then release the block. Even if some system generates a sleep
request NOT through logind then it won't be blocked---the blocks only
apply to requests from logind. Again from the documentation
   
   "Note that this will only be sent out for suspend/resume cycles done
   via logind, i.e. generally only for high-level user-induced suspend
   cycles, and not automatic, low-level kernel induced ones which might
   exist on certain devices with more aggressive power management."

    EZ> We don't need to block sleep, we only need to delay it for a
    EZ> short while.

Sorry, I was using shorthand. When I say "block", I mean "block with
delay": if the block is not removed within 5 seconds of the sleep
request (or some other configurable time) the sleep proceeds anyway.

    >> Can I get any feedback about the file descriptor closure issue?
    >> From what I can tell without exposing it in lisp the way I did I
    >> won't be able to use blocking anyway.

    EZ> Emacs has no way of manipulating handles or file descriptors.
    EZ> If this is required in this case, we must expose it to Lisp in
    EZ> the form of some primitive.

Right, I suspected this. In my local version I have done exactly that:
exposed it to lisp in the form of some primitive (I just created a lisp
function `emacs-close-fd' that calls `emacs_close')

    EZ> But since there could be only one such descriptor at any given
    EZ> time, why would any Lisp program need to know its value?  It
    EZ> sounds like we need to record the descriptor internally, and
    EZ> then close it in C, no?  Maybe some D-Bus related primitive
    EZ> could do that?

Right. The issue is that for the D-Bus case I am doing everything using
the lisp dbus API and not touching the C source. When I use the API to
install the block (by calling `dbus-call-method') all I get returned is
an integer, the file descriptor. The D-Bus API specifies that I am to
close the descriptor to release the block. But all I have in lisp is
this integer, which doesn't do me much good unless I expose the
primitive as above.

I looked through the D-Bus code and don't see any primitive that would
do this. I also asked Michael, who wasn't aware of anything either.  So
as far as I can tell either we have to expose a primitive to close file
descriptors, as I have done (or something similar), or try something
more drastic in dbusbind.c that changes the way methods that return file
descriptors work (I know less than nothing about how this works, so I
can't say anything intelligent about it. But I would guess that
something major would have to be done, so that instead of the dbus
library opening the descriptor and returning it, the standard dbus
routines would have to be bypassed so that some other kind of emacs
object would be used instead).

Best,
Andy
-- 
Andrew Cohen