bug#72496: 31.0.50 macOS: freezes without beach ball

Stéphane Marks <[email protected]> Mon, 3 Aug 2026 17:25:00 +0200
Newsgroups gmane.emacs.bugs
Message-ID <CAN+1Hbo+ZnU1HW1vPbmweeBV5oXNtvZSYKJj1khn-9doLCx7Sg@mail.gmail.com>
--000000000000eaa5ab0658262425
Content-Type: text/plain; charset="UTF-8"
Content-Transfer-Encoding: quoted-printable

On Mon, Aug 3, 2026 at 11:21=E2=80=AFAM Alan Third <[email protected]> wrote:

> On Mon, Aug 03, 2026 at 02:29:37PM +0300, Eli Zaretskii wrote:
> > Alan and St=C3=A9phane, any comments or suggestions?
>
> This looks good to me. Any "better" alternative would probably involve
> getting rid of this appdefined event nonsense completely, which isn't
> easy.
>
> > > From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 200=
1
> > > From: Anton Dergunov <[email protected]>
> > > Subject: [PATCH] Fix a permanent hang on macOS when no window is main
> > >
> > > 'ns_send_appdefined' posted the event that ends [NSApp run] with
> > > windowNumber: [[NSApp mainWindow] windowNumber].  While the frame is
> > > miniaturized, or while key/main status is being handed over, [NSApp
> > > mainWindow] is nil and the window number is 0; AppKit discards such a=
n
> > > event, so [NSApp run] never returns and Emacs never gets back to its
> > > command loop.
> > >
> > > The hang is permanent rather than intermittent because nothing retrie=
s:
> > > 'send_appdefined' has already been cleared by this same call, so ever=
y
> > > later EV_TRAILER re-post is suppressed, and 'timed_entry' has just be=
en
> > > invalidated.  Unlike 'ns_select_1', 'ns_read_socket_1' never arms a
> > > 'timed_entry', so no timer can wake the loop either.
> > >
> > > Note that [NSApp keyWindow] is nil while miniaturized too, so the sca=
n
> > > over [NSApp windows] is the fallback that actually applies.
> > >
> > > * src/nsterm.m (ns_send_appdefined): Address the app-defined event to=
 a
> > > window that exists.  (Bug#72496)
> > > ---
> > > --- a/src/nsterm.m
> > > +++ b/src/nsterm.m
> > > @@ -4686,6 +4686,7 @@
> > >    if (send_appdefined)
> > >      {
> > >        NSEvent *nxev;
> > > +      NSWindow *dest;
> > >
> > >        /* We only need one NX_APPDEFINED event to stop NXApp from
> running.  */
> > >        send_appdefined =3D NO;
> > > @@ -4697,12 +4698,29 @@
> > >            [timed_entry release];
> > >            timed_entry =3D nil;
> > >          }
> > > +
> > > +      /* Address the event to a window that actually exists.  With n=
o
> main
> > > +         window -- miniaturized, or mid handover of key/main status
> -- the
> > > +         window number would be 0 and AppKit would silently discard
> the
> > > +         event.  That is fatal here: send_appdefined has just been
> cleared
> > > +         and timed_entry invalidated, so nothing would ever end
> [NSApp run]
> > > +         again, and Emacs would hang forever with its UI
> unresponsive.  */
> > > +      dest =3D [NSApp mainWindow];
> > > +      if (dest =3D=3D nil)
> > > +        dest =3D [NSApp keyWindow];
> > > +      if (dest =3D=3D nil)
> > > +        for (NSWindow *cand in [NSApp windows])
> > > +          if ([cand windowNumber] > 0)
> > > +            {
> > > +              dest =3D cand;
> > > +              break;
> > > +            }
> > >
> > >        nxev =3D [NSEvent otherEventWithType:
> NSEventTypeApplicationDefined
> > >                                  location: NSMakePoint (0, 0)
> > >                             modifierFlags: 0
> > >                                 timestamp: 0
> > > -                            windowNumber: [[NSApp mainWindow]
> windowNumber]
> > > +                            windowNumber: [dest windowNumber]
> > >                                   context: [NSApp context]
> > >                                   subtype: 0
> > >                                     data1: value
>

The patch itself can use some stylistic improvements.  That aside, perhaps
we should create a global invisible canary window and avoid guesswork and
any potential to drop messages should there be no windows available at the
time of this call.

--000000000000eaa5ab0658262425
Content-Type: text/html; charset="UTF-8"
Content-Transfer-Encoding: quoted-printable

<div dir=3D"ltr"><div dir=3D"ltr"><div class=3D"gmail_default" style=3D"fon=
t-family:monospace"><span style=3D"font-family:Arial,Helvetica,sans-serif;b=
ackground-color:transparent">On Mon, Aug 3, 2026 at 11:21=E2=80=AFAM Alan T=
hird &lt;<a href=3D"mailto:[email protected]">[email protected]</a>&gt; wrote:<=
/span></div></div><div class=3D"gmail_quote gmail_quote_container"><blockqu=
ote class=3D"gmail_quote" style=3D"margin:0px 0px 0px 0.8ex;border-left:1px=
 solid rgb(204,204,204);padding-left:1ex">On Mon, Aug 03, 2026 at 02:29:37P=
M +0300, Eli Zaretskii wrote:<br>
&gt; Alan and St=C3=A9phane, any comments or suggestions?<br>
<br>
This looks good to me. Any &quot;better&quot; alternative would probably in=
volve<br>
getting rid of this appdefined event nonsense completely, which isn&#39;t<b=
r>
easy.<br>
<br>
&gt; &gt; From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00=
 2001<br>
&gt; &gt; From: Anton Dergunov &lt;<a href=3D"mailto:adergunov.login@gmail.=
com" target=3D"_blank">[email protected]</a>&gt;<br>
&gt; &gt; Subject: [PATCH] Fix a permanent hang on macOS when no window is =
main<br>
&gt; &gt; <br>
&gt; &gt; &#39;ns_send_appdefined&#39; posted the event that ends [NSApp ru=
n] with<br>
&gt; &gt; windowNumber: [[NSApp mainWindow] windowNumber].=C2=A0 While the =
frame is<br>
&gt; &gt; miniaturized, or while key/main status is being handed over, [NSA=
pp<br>
&gt; &gt; mainWindow] is nil and the window number is 0; AppKit discards su=
ch an<br>
&gt; &gt; event, so [NSApp run] never returns and Emacs never gets back to =
its<br>
&gt; &gt; command loop.<br>
&gt; &gt; <br>
&gt; &gt; The hang is permanent rather than intermittent because nothing re=
tries:<br>
&gt; &gt; &#39;send_appdefined&#39; has already been cleared by this same c=
all, so every<br>
&gt; &gt; later EV_TRAILER re-post is suppressed, and &#39;timed_entry&#39;=
 has just been<br>
&gt; &gt; invalidated.=C2=A0 Unlike &#39;ns_select_1&#39;, &#39;ns_read_soc=
ket_1&#39; never arms a<br>
&gt; &gt; &#39;timed_entry&#39;, so no timer can wake the loop either.<br>
&gt; &gt; <br>
&gt; &gt; Note that [NSApp keyWindow] is nil while miniaturized too, so the=
 scan<br>
&gt; &gt; over [NSApp windows] is the fallback that actually applies.<br>
&gt; &gt; <br>
&gt; &gt; * src/nsterm.m (ns_send_appdefined): Address the app-defined even=
t to a<br>
&gt; &gt; window that exists.=C2=A0 (Bug#72496)<br>
&gt; &gt; ---<br>
&gt; &gt; --- a/src/nsterm.m<br>
&gt; &gt; +++ b/src/nsterm.m<br>
&gt; &gt; @@ -4686,6 +4686,7 @@<br>
&gt; &gt;=C2=A0 =C2=A0 if (send_appdefined)<br>
&gt; &gt;=C2=A0 =C2=A0 =C2=A0 {<br>
&gt; &gt;=C2=A0 =C2=A0 =C2=A0 =C2=A0 NSEvent *nxev;<br>
&gt; &gt; +=C2=A0 =C2=A0 =C2=A0 NSWindow *dest;<br>
&gt; &gt;=C2=A0 <br>
&gt; &gt;=C2=A0 =C2=A0 =C2=A0 =C2=A0 /* We only need one NX_APPDEFINED even=
t to stop NXApp from running.=C2=A0 */<br>
&gt; &gt;=C2=A0 =C2=A0 =C2=A0 =C2=A0 send_appdefined =3D NO;<br>
&gt; &gt; @@ -4697,12 +4698,29 @@<br>
&gt; &gt;=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 [timed_entry release];<b=
r>
&gt; &gt;=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 timed_entry =3D nil;<br>
&gt; &gt;=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 }<br>
&gt; &gt; +<br>
&gt; &gt; +=C2=A0 =C2=A0 =C2=A0 /* Address the event to a window that actua=
lly exists.=C2=A0 With no main<br>
&gt; &gt; +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0window -- miniaturized, or mid=
 handover of key/main status -- the<br>
&gt; &gt; +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0window number would be 0 and A=
ppKit would silently discard the<br>
&gt; &gt; +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0event.=C2=A0 That is fatal her=
e: send_appdefined has just been cleared<br>
&gt; &gt; +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0and timed_entry invalidated, s=
o nothing would ever end [NSApp run]<br>
&gt; &gt; +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0again, and Emacs would hang fo=
rever with its UI unresponsive.=C2=A0 */<br>
&gt; &gt; +=C2=A0 =C2=A0 =C2=A0 dest =3D [NSApp mainWindow];<br>
&gt; &gt; +=C2=A0 =C2=A0 =C2=A0 if (dest =3D=3D nil)<br>
&gt; &gt; +=C2=A0 =C2=A0 =C2=A0 =C2=A0 dest =3D [NSApp keyWindow];<br>
&gt; &gt; +=C2=A0 =C2=A0 =C2=A0 if (dest =3D=3D nil)<br>
&gt; &gt; +=C2=A0 =C2=A0 =C2=A0 =C2=A0 for (NSWindow *cand in [NSApp window=
s])<br>
&gt; &gt; +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if ([cand windowNumber] &gt; =
0)<br>
&gt; &gt; +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 {<br>
&gt; &gt; +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 dest =3D cand;<=
br>
&gt; &gt; +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 break;<br>
&gt; &gt; +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 }<br>
&gt; &gt;=C2=A0 <br>
&gt; &gt;=C2=A0 =C2=A0 =C2=A0 =C2=A0 nxev =3D [NSEvent otherEventWithType: =
NSEventTypeApplicationDefined<br>
&gt; &gt;=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=
=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 location: NSMakePoint =
(0, 0)<br>
&gt; &gt;=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=
=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0modifierFlags: 0<br>
&gt; &gt;=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=
=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0timestamp: 0<br>
&gt; &gt; -=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 windowNumber: [[NSApp mainWindow] window=
Number]<br>
&gt; &gt; +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 windowNumber: [dest windowNumber]<br>
&gt; &gt;=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=
=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0context: [NSApp =
context]<br>
&gt; &gt;=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=
=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0subtype: 0<br>
&gt; &gt;=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=
=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0data1: va=
lue<br></blockquote><div><br></div><div class=3D"gmail_default" style=3D"fo=
nt-family:monospace">The patch itself can use some stylistic improvements.=
=C2=A0 That aside, perhaps we should create a global invisible canary windo=
w and avoid guesswork and any potential to drop messages should there be no=
 windows available at the time of this call.</div></div></div>

--000000000000eaa5ab0658262425--