Repository: ipython/ipython

Issue:
%gui osx hangs with ipython 8.13.2 on macOS 13.2.1
This causes ipython to hang.  The process cannot be suspended with ^Z and must be killed from another shell:

```% ipython
Python 3.11.2 (v3.11.2:878ead1ac1, Feb  7 2023, 10:02:41) [Clang 13.0.0 (clang-1300.0.29.30)]
Type 'copyright', 'credits' or 'license' for more information
IPython 8.13.2 -- An enhanced Interactive Python. Type '?' for help.

In [1]: %gui osx
Installed osx event loop hook.

In [2]: 
```

This does not hang:
```% ipython
Python 3.11.2 (v3.11.2:878ead1ac1, Feb  7 2023, 10:02:41) [Clang 13.0.0 (clang-1300.0.29.30)]
Type 'copyright', 'credits' or 'license' for more information
IPython 8.13.2 -- An enhanced Interactive Python. Type '?' for help.

In [1]: import matplotlib

In [2]: matplotlib.get_backend()
Installed osx event loop hook.
Out[2]: 'MacOSX'

In [3]: 2+2
Out[3]: 4
```
If the hung process is killed with ABRT, the crash report shows a deadlock:
```
hread 0 Crashed::  Dispatch queue: com.apple.main-thread
0   libsystem_kernel.dylib        	    0x7ff80a34c5c2 mach_msg2_trap + 10
1   libsystem_kernel.dylib        	    0x7ff80a35a604 mach_msg2_internal + 82
2   libsystem_kernel.dylib        	    0x7ff80a353635 mach_msg_overwrite + 723
3   libsystem_kernel.dylib        	    0x7ff80a34c8a8 mach_msg + 19
4   CoreFoundation                	    0x7ff80a466cbe __CFRunLoopServiceMachPort + 145
5   CoreFoundation                	    0x7ff80a46572a __CFRunLoopRun + 1360
6   CoreFoundation                	    0x7ff80a464b60 CFRunLoopRunSpecific + 560
7   CoreFoundation                	    0x7ff80a4e97aa CFRunLoopRun + 40
8   libffi.dylib                  	    0x7ff8198e3912 ffi_call_unix64 + 82
9   libffi.dylib                  	    0x7ff8198e323a ffi_call_int + 840
[ ... same line repeated 500 times ...]
510 libffi.dylib                  	    0x7ff8198e323a ffi_call_int + 840
511 libffi.dylib                  	    0x7ff8198e323a ffi_call_int + 840

Thread 1:
0   libsystem_kernel.dylib        	    0x7ff80a34f11a __psynch_cvwait + 10
1   libsystem_pthread.dylib       	    0x7ff80a38b7e1 _pthread_cond_wait + 1243
2   Python                        	       0x105a41272 0x1057e3000 + 2482802
3   Python                        	       0x105ac4d9e 0x1057e3000 + 3022238
4   Python                        	       0x105ac4fdc 0x1057e3000 + 3022812
5   Python                        	       0x10589ab07 0x1057e3000 + 752391
6   Python                        	       0x1059b72ef 0x1057e3000 + 1917679
7   Python                        	       0x1059bd4d0 0x1057e3000 + 1942736
8   Python                        	       0x1059ba54a 0x1057e3000 + 1930570
9   Python                        	       0x1059bd4d0 0x1057e3000 + 1942736
10  Python                        	       0x1059ba54a 0x1057e3000 + 1930570
11  Python                        	       0x1059bd4d0 0x1057e3000 + 1942736
12  Python                        	       0x10588d816 0x1057e3000 + 698390
13  Python                        	       0x105ac5b26 0x1057e3000 + 3025702
14  Python                        	       0x105a40ea4 0x1057e3000 + 2481828
15  libsystem_pthread.dylib       	    0x7ff80a38b259 _pthread_start + 125
16  libsystem_pthread.dylib       	    0x7ff80a386c7b thread_start + 15

Thread 2:
0   libsystem_kernel.dylib        	    0x7ff80a34f11a __psynch_cvwait + 10
1   libsystem_pthread.dylib       	    0x7ff80a38b7e1 _pthread_cond_wait + 1243
2   Python                        	       0x105a41272 0x1057e3000 + 2482802
3   Python                        	       0x105ac4d9e 0x1057e3000 + 3022238
4   Python                        	       0x105ac4fdc 0x1057e3000 + 3022812
5   Python                        	       0x10589ab07 0x1057e3000 + 752391
6   Python                        	       0x1059b72ef 0x1057e3000 + 1917679
7   Python                        	       0x1059bd4d0 0x1057e3000 + 1942736
8   Python                        	       0x10588d816 0x1057e3000 + 698390
9   Python                        	       0x1059ba54a 0x1057e3000 + 1930570
10  Python                        	       0x1059bd4d0 0x1057e3000 + 1942736
11  Python                        	       0x10588d816 0x1057e3000 + 698390
12  Python                        	       0x105ac5b26 0x1057e3000 + 3025702
13  Python                        	       0x105a40ea4 0x1057e3000 + 2481828
14  libsystem_pthread.dylib       	    0x7ff80a38b259 _pthread_start + 125
15  libsystem_pthread.dylib       	    0x7ff80a386c7b thread_start + 15



Discussion:
I have the same problem with IPython 8.4.0, 8.6.0, and 8.13.2 running in Python 3.10 and 3.11 (both from the python.org installers) on macOS Monterey 12.6.5.
This problem is fixed by reverting the changes in commit a1be241 (Issue  #9854).  The intent of those changes was to run a nested event loop if the NSApp terminates after closing all windows.  There is a threading.Event which is supposed to indicate whether that has happened.  In fact, that Event gets set frequently, not just when the NSApp is terminating.  The changes in the commit cause the inputhook to call CFRunLoopRun when the Event is set, and that call hangs if there is no NSApp running.
I hope this can get some attention <3 I don't understand enough to contribute
@culler, could you make a pull request with your suggestions to fix this problem? That way others could test it locally.

Resolving patch:
diff --git a/IPython/terminal/pt_inputhooks/osx.py b/IPython/terminal/pt_inputhooks/osx.py
--- a/IPython/terminal/pt_inputhooks/osx.py
+++ b/IPython/terminal/pt_inputhooks/osx.py
@@ -116,11 +116,8 @@ def _wake(NSApp):
     msg(NSApp, n('postEvent:atStart:'), void_p(event), True)
 
 
-_triggered = Event()
-
 def _input_callback(fdref, flags, info):
     """Callback to fire when there's input to be read"""
-    _triggered.set()
     CFFileDescriptorInvalidate(fdref)
     CFRelease(fdref)
     NSApp = _NSApp()
@@ -134,7 +131,6 @@ def _input_callback(fdref, flags, info):
 
 def _stop_on_read(fd):
     """Register callback to stop eventloop when there's data on fd"""
-    _triggered.clear()
     fdref = CFFileDescriptorCreate(None, fd, False, _c_input_callback, None)
     CFFileDescriptorEnableCallBacks(fdref, kCFFileDescriptorReadCallBack)
     source = CFFileDescriptorCreateRunLoopSource(None, fdref, 0)
@@ -149,9 +145,3 @@ def inputhook(context):
     _stop_on_read(context.fileno())
     objc.objc_msgSend.argtypes = [void_p, void_p]
     msg(NSApp, n('run'))
-    if not _triggered.is_set():
-        # app closed without firing callback,
-        # probably due to last window being closed.
-        # Run the loop manually in this case,
-        # since there may be events still to process (#9734)
-        CoreFoundation.CFRunLoopRun()


Test patch: