bug#72496: 31.0.50 macOS: freezes without beach ball

Alan Third <[email protected]> Mon, 3 Aug 2026 16:21:57 +0100
Newsgroups gmane.emacs.bugs
Message-ID <[email protected]>
On Mon, Aug 03, 2026 at 02:29:37PM +0300, Eli Zaretskii wrote:
> Alan and Stéphane, 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 2001
> > 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 an
> > event, so [NSApp run] never returns and Emacs never gets back to its
> > command loop.
> > 
> > The hang is permanent rather than intermittent because nothing retries:
> > 'send_appdefined' has already been cleared by this same call, so every
> > later EV_TRAILER re-post is suppressed, and 'timed_entry' has just been
> > 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 scan
> > 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 = NO;
> > @@ -4697,12 +4698,29 @@
> >            [timed_entry release];
> >            timed_entry = nil;
> >          }
> > +
> > +      /* Address the event to a window that actually exists.  With no 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 = [NSApp mainWindow];
> > +      if (dest == nil)
> > +        dest = [NSApp keyWindow];
> > +      if (dest == nil)
> > +        for (NSWindow *cand in [NSApp windows])
> > +          if ([cand windowNumber] > 0)
> > +            {
> > +              dest = cand;
> > +              break;
> > +            }
> >  
> >        nxev = [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

-- 
Alan Third