Subject: bug#75275: 30.0.92; `make-thread` bug on macOS 15.2

From: Eli Zaretskii <[email-masked]>
Date: Thu, 02 Jan 2025 09:13:50 +0200

> From: Stefan Kangas <[email-masked]>
> Date: Wed, 1 Jan 2025 22:57:38 -0600
> 
> I have run into a bug with make-thread on macOS 15.2, running on an M2.
> 
> I can reproduce the issue consistently both on emacs-30 and master by
> evaluating this in emacs -Q:
> 
>     (make-thread (lambda () (sleep-for 1)) "bug")
> 
> This leads to Emacs freezing up completely within a fraction of a
> second.  I have time to move point once or maybe twice before it gets
> non-responsive, let's say within a few tenths of a second.

The above works as expected on MS-Windows and on GNU/Linux: after
about 1 sec the new thread exits, and Emacs works normally.
list-threads shows a single main thread running at that time.

> When I kill the process in the lldb window with Ctrl+C, I can get the
> following (this is on emacs-30):
> 
> [...]
> 2025-01-02 05:47:20.778199+0100 emacs[78593:1366649] [General]
> nextEventMatchingMask should only be called from the Main Thread!
> Process 78593 stopped
> * thread #1, queue = 'com.apple.main-thread', stop reason = signal SIGSTOP
>     frame #0: 0x000000018e12dbbc libsystem_kernel.dylib`__psynch_mutexwait + 8
> libsystem_kernel.dylib`:
> ->  0x18e12dbbc <+8>:  b.lo   0x18e12dbdc               ; <+40>
>     0x18e12dbc0 <+12>: pacibsp
>     0x18e12dbc4 <+16>: stp    x29, x30, [sp, #-0x10]!
>     0x18e12dbc8 <+20>: mov    x29, sp
> Target 0: (emacs) stopped.
> (lldb) bt all
> * thread #1, queue = 'com.apple.main-thread', stop reason = signal SIGSTOP
>   * frame #0: 0x000000018e12dbbc libsystem_kernel.dylib`__psynch_mutexwait + 8
>     frame #1: 0x000000018e1693f8
> libsystem_pthread.dylib`_pthread_mutex_firstfit_lock_wait + 84
>     frame #2: 0x000000018e166dbc
> libsystem_pthread.dylib`_pthread_mutex_firstfit_lock_slow + 220
>     frame #3: 0x00000001003bfb94
> emacs`sys_mutex_lock(mutex=0x0000000100b7bd30) at systhread.c:140:15
>     frame #4: 0x00000001003bcef8
> emacs`acquire_global_lock(self=0x0000000100b074d0) at thread.c:160:3
>     frame #5: 0x00000001003bdce0

This is the main thread waiting for the global lock, which is
expected.

>   thread #7, name = 'bug'
>     frame #0: 0x000000018de3a2b0
> dyld`dyld3::MachOLoaded::findClosestSymbol(unsigned long long, char
> const**, unsigned long long*) const + 488
>     frame #1: 0x000000018de1b13c dyld`dyld4::APIs::dladdr(void const*,
> dl_info*) + 236
>     frame #2: 0x000000018e012f00 libsystem_c.dylib`backtrace_symbols + 144
>     frame #3: 0x000000018f4998c0 Foundation`-[_NSCallStackArray
> descriptionWithLocale:indent:] + 144
>     frame #4: 0x000000018f3e8c10 Foundation`_NS_os_log_callback + 276
>     frame #5: 0x000000018debee60
> libsystem_trace.dylib`_os_log_fmt_flatten_NSCF + 64
>     frame #6: 0x000000018dec5830
> libsystem_trace.dylib`_os_log_fmt_flatten_object_impl + 372
>     frame #7: 0x000000018debc9c8
> libsystem_trace.dylib`_os_log_impl_flatten_and_send + 2144
>     frame #8: 0x000000018debc150 libsystem_trace.dylib`_os_log + 168
>     frame #9: 0x000000018debc0a0 libsystem_trace.dylib`_os_log_impl + 28
>     frame #10: 0x000000019209151c AppKit`-[NSApplication reportException:] + 624
>     frame #11: 0x0000000191db0118 AppKit`-[NSApplication run] + 664
>     frame #12: 0x0000000100403fec emacs`-[EmacsApp
> run](self=0x0000000156610fe0, _cmd="run") at nsterm.m:5938:7
>     frame #13: 0x00000001004024b0 emacs`ns_select_1(nfds=0,
> readfds=0x00000001708c26bc, writefds=0x00000001708c263c,
> exceptfds=0x0000000000000000, timeout=0x00000001708c2610,
> sigmask=0x0000000000000000, run_loop_only=NO) at nsterm.m:4954:3
>     frame #14: 0x000000010040202c emacs`ns_select(nfds=0,
> readfds=0x00000001708c26bc, writefds=0x00000001708c263c,
> exceptfds=0x0000000000000000, timeout=0x00000001708c2610,
> sigmask=0x0000000000000000) at nsterm.m:5006:10
>     frame #15: 0x000000010036930c
> emacs`wait_reading_process_output(time_limit=1, nsecs=0, read_kbd=0,
> do_display=false, wait_for_cell=(i = 0x0000000000000000),
> wait_proc=0x0000000000000000, just_wait_proc=0) at process.c:5753:18
>     frame #16: 0x000000010000b400 emacs`Fsleep_for(seconds=(i =
> 0x0000000000000006), milliseconds=(i = 0x0000000000000000)) at
> dispnew.c:6248:2

This is the new Lisp thread started by make-thread, but it is
somewhere in the bowels of macOS.  Is this what you see each time you
kill Emacs after it hangs?

Can someone describe how sleep-for works on macOS, i.e. what is
supposed to happen after ns_select_1 calls -[EmacsApp run], whatever
that is?  It sounds like something in that machinery conflicts with
how Lisp threads are implemented.

>From the backtrace of the new Lisp thread, it looks like it finished
sleeping for 1 sec and then it proceeds to calling [NSApp run] -- is
this correct behavior for a non-main thread?  E.g., does that try to
run the main thread (which is parked inside sys_mutex_lock, waiting
for the global lock to become free)?