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 <<a href=3D"mailto:[email protected]">[email protected]</a>> 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> > Alan and St=C3=A9phane, any comments or suggestions?<br> <br> This looks good to me. Any "better" alternative would probably in= volve<br> getting rid of this appdefined event nonsense completely, which isn't<b= r> easy.<br> <br> > > From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00= 2001<br> > > From: Anton Dergunov <<a href=3D"mailto:adergunov.login@gmail.= com" target=3D"_blank">[email protected]</a>><br> > > Subject: [PATCH] Fix a permanent hang on macOS when no window is = main<br> > > <br> > > 'ns_send_appdefined' posted the event that ends [NSApp ru= n] with<br> > > windowNumber: [[NSApp mainWindow] windowNumber].=C2=A0 While the = frame is<br> > > miniaturized, or while key/main status is being handed over, [NSA= pp<br> > > mainWindow] is nil and the window number is 0; AppKit discards su= ch an<br> > > event, so [NSApp run] never returns and Emacs never gets back to = its<br> > > command loop.<br> > > <br> > > The hang is permanent rather than intermittent because nothing re= tries:<br> > > 'send_appdefined' has already been cleared by this same c= all, so every<br> > > later EV_TRAILER re-post is suppressed, and 'timed_entry'= has just been<br> > > invalidated.=C2=A0 Unlike 'ns_select_1', 'ns_read_soc= ket_1' never arms a<br> > > 'timed_entry', so no timer can wake the loop either.<br> > > <br> > > Note that [NSApp keyWindow] is nil while miniaturized too, so the= scan<br> > > over [NSApp windows] is the fallback that actually applies.<br> > > <br> > > * src/nsterm.m (ns_send_appdefined): Address the app-defined even= t to a<br> > > window that exists.=C2=A0 (Bug#72496)<br> > > ---<br> > > --- a/src/nsterm.m<br> > > +++ b/src/nsterm.m<br> > > @@ -4686,6 +4686,7 @@<br> > >=C2=A0 =C2=A0 if (send_appdefined)<br> > >=C2=A0 =C2=A0 =C2=A0 {<br> > >=C2=A0 =C2=A0 =C2=A0 =C2=A0 NSEvent *nxev;<br> > > +=C2=A0 =C2=A0 =C2=A0 NSWindow *dest;<br> > >=C2=A0 <br> > >=C2=A0 =C2=A0 =C2=A0 =C2=A0 /* We only need one NX_APPDEFINED even= t to stop NXApp from running.=C2=A0 */<br> > >=C2=A0 =C2=A0 =C2=A0 =C2=A0 send_appdefined =3D NO;<br> > > @@ -4697,12 +4698,29 @@<br> > >=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 [timed_entry release];<b= r> > >=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 timed_entry =3D nil;<br> > >=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 }<br> > > +<br> > > +=C2=A0 =C2=A0 =C2=A0 /* Address the event to a window that actua= lly exists.=C2=A0 With no main<br> > > +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0window -- miniaturized, or mid= handover of key/main status -- the<br> > > +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0window number would be 0 and A= ppKit would silently discard the<br> > > +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0event.=C2=A0 That is fatal her= e: send_appdefined has just been cleared<br> > > +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0and timed_entry invalidated, s= o nothing would ever end [NSApp run]<br> > > +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0again, and Emacs would hang fo= rever with its UI unresponsive.=C2=A0 */<br> > > +=C2=A0 =C2=A0 =C2=A0 dest =3D [NSApp mainWindow];<br> > > +=C2=A0 =C2=A0 =C2=A0 if (dest =3D=3D nil)<br> > > +=C2=A0 =C2=A0 =C2=A0 =C2=A0 dest =3D [NSApp keyWindow];<br> > > +=C2=A0 =C2=A0 =C2=A0 if (dest =3D=3D nil)<br> > > +=C2=A0 =C2=A0 =C2=A0 =C2=A0 for (NSWindow *cand in [NSApp window= s])<br> > > +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if ([cand windowNumber] > = 0)<br> > > +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 {<br> > > +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 dest =3D cand;<= br> > > +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 break;<br> > > +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 }<br> > >=C2=A0 <br> > >=C2=A0 =C2=A0 =C2=A0 =C2=A0 nxev =3D [NSEvent otherEventWithType: = NSEventTypeApplicationDefined<br> > >=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> > >=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> > >=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> > > -=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> > > +=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> > >=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> > >=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> > >=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--