| Ticket UUID: | 33b3af1ceac681ad0f7756f9495de42bbdadb925 | |||
| Title: | macOS 27 beta: modal dialog opened synchronously from a menu command becomes unresponsive | |||
| Type: | Bug | Version: | 8.6.17 and 9 | |
| Submitter: | bdubzkc | Created on: | 2026-07-21 13:04:34 | |
| Subsystem: | 11. Aqua Menus | Assigned To: | marc_culler | |
| Priority: | 5 Medium | Severity: | Critical | |
| Status: | Closed | Last Modified: | 2026-10-16 22:19:42 | |
| Resolution: | Fixed | Closed By: | chrstphrchvz | |
| Closed on: | 2026-10-16 22:19:42 | |||
| Description: |
On macOS 27 beta, opening a modal dialog synchronously from a Tk menu command causes the dialog and application to become unresponsive. The dialog appears, but it does not accept mouse clicks or keyboard input. The application must be force-quit. Deferring the identical dialog with Environment
Steps to reproduce
Actual resultWhen Wish must be force-quit. Expected result
WorkaroundDeferring the dialog until the next idle iteration works correctly:
Version informationThe reproduction works correctly on macOS 25 and macOS 26. Both the direct and deferred paths remain responsive on those releases. The failure has been reproduced on macOS 27 beta build | |||
| User Comments: |
chrstphrchvz added on 2026-10-16 22:19:42:
Can [1bc92655] please be backported to 8.6/9.0? Thanks marc_culler (claiming to be Marc Culler) added on 2026-10-16 13:27:48: I think this is sufficient, so I will close the ticket. chrstphrchvz added on 2026-10-13 03:45:47: There are also the options to document the behavior (e.g. "If the main interpreter is deleted, tkAboutDialog will not be called"); or, match the existing fallback behavior in -[TKApplication orderFromStandardAboutPanel:], which is to show the standard about dialog (the following works, but might not be ideal):
chrstphrchvz added on 2026-10-13 02:48:10: I think even developers could find that error message confusing, whether or not their program uses eval/bgerror/etc. Several Tkinter users had encountered [310c74ec], for example, and it seemed not uncommon for such issues to prompt them to believe (or be told) that their code was responsible for the error, but then not figure out exactly why. marc_culler (claiming to be Marc Culler) added on 2026-10-11 16:16:16: I think I would lean towards emitting a message. In a real app, it is hard for me to imagine how a user would be in the situation where an "About" dialog is being requested from a deleted interpreter. A user might open the dialog to check the version of the software or on the first run to find out whatever information is available. But that is not likely to involve a deleted interpreter. So the message would be aimed at developers, and they deserve to get a message. I don't know what is wrong with "attempt to call eval in deleted interpreter" chrstphrchvz added on 2026-10-11 04:51:02: Thanks. Preserving marc_culler (claiming to be Marc Culler) added on 2026-10-11 03:53:35: Thank you. Of course you may. I did not see any problem when running that script, but I added the preserve call anyway. I released _eventInterp in the idle task itself. I assume that is what you had in mind. I just pushed that change to the tip of main. chrstphrchvz added on 2026-10-11 00:36:24: Reopening this if I may. The current fix passes _eventInterp to Tcl_DoWhenIdle() without preserving it, which leaves open the possibility of a use-after-free if _eventInterp is deleted before showAboutIdleTask() is called. The following tclsh script triggers the problem, at least after [8e32cb7e] and [1ab7a5f2] are fixed first:
interp create i
interp eval i package require Tk
interp eval i proc tkAboutDialog x {{puts unimplemented}}
interp eval i ::tk::mac::standardAboutPanel
interp delete i
update idletasks
marc_culler (claiming to be Marc Culler) added on 2026-10-05 15:50:45: I have merged bug-33b3af1cea into 9.1, 9.0 and 8.6. I am closing this ticket now. marc_culler (claiming to be Marc Culler) added on 2026-10-05 02:54:49: Here is my current best guess. People claim that the menubar display has been completely changed in macOS 27 and now uses a private undocumented framework named MenuBarClientCore. Evidently that framework changed the behavior of the runloop which is started when a menu is opened. In macOS 26 a runloop is started in NSEventTrackingRunLoopMode which terminates when a menuitem is selected. The action for the menuitem is then called in Tk's default state, which has a nil runloop mode. My experiments indicate that in macOS 27 a runloop is first started in NSDefaultRunLoopMode which does unknown things before starting a runloop in NSEventTrackingRunLoopMode. When that runloop ends the action for the selected item gets run before the parent default runloop stops. Apparently the default runloop does not stop until the menuitem action returns. But Tk does not attempt to process any events from the AppKit event queue unless the runloop mode is nil (and possibly it would not be allowed to do that if it did attempt to). Therefore, if user input, such as pressing the OK button in a dialog window, must be processed before the command returns this means that the command can never return and the default runloop will never stop and Tk will never be able to process a user event again. The idle task workaround allows the menuitem action to return immediately, which allows the outer default runloop to end and Tk is then able to return to processing user events. I can see that when either the NSMenuDidBeginTrackingNotification or the NSMenuDidEndTrackingNotification is received by Tk the current runloop mode is NSDefaultRunLoopMode in macOS 27 but is nil in macOS 26. Also, the Tcl function TclpWaitForEvent, which starts a runloop to process one event from any event source, tracks the runloop nesting level. I can see that in macOS 26 the nesting level never exceeds 2, but in macOS 27 it reaches 3. This data is at least consistent with my guess of what is happening. Given all this, I think the idle task workaround is the most reasonable way to deal with this. (We are lucky that you noticed that it works!) This has been implemented in the bug-33b3af1cea branch and it was verified that it allowed the Python IDLE.app (which launches dialogs from menu item callbacks) to work again. My plan is to merge the bugfix branch into 9.0 and 9.1, and perhaps also into 8.6, although I would prefer that people not use 8.6 on macOS 27 and later. marc_culler (claiming to be Marc Culler) added on 2026-09-30 20:16:16: Yes, I think it is a blocker for 9.0.5. I have pushed a bugfix branch bug-33b3af1cea which uses the workaround suggested here. All menu actions are run as idle tasks instead of being called directtly. I am not comfortable with this workaround because I cannot explain why it works. However, as far as I can tell, it does work. I suspect the answer to why it works may be buried in the Tcl notifier for macosx. If that is correct, changing the Tcl notifier might be a better fix. jan.nijtmans added on 2026-09-30 15:57:19: This looks like a blocker for Tk 9.0.5. +1 marc_culler (claiming to be Marc Culler) added on 2026-09-29 16:08:05: This is a copy of the comment I just uploaded to the Python github site about this issue: Issue 1580530 I have at least identified what changed between macOS 26 and 27. Of course, with Apple, we are dealing with a black box. So I can only report symptoms, not underlying causes. The background is:
The behavior which changed between macOS 26 and 27 is:
Tk receives the notifications NSMenuDidBeginTrackingNotification and NSMenuDidEndTrackingNotification. So the information above is found by printing the runloop mode in the handlers for those events. Since Tk does not process any events when the runloop mode is not nil, the consequence of the runloop mode being set to NSDefaultRunLoopMode when a menu closes is that Tk will hang until the runloop mode returns to nil. It is not clear how to force the runloop mode to return to nil. But a nil runloop mode is the indicator that the Apple runloop is idle (as it should be if CFRunLoopRun has never been called), and the fact that it sometimes gets set to nil when Tk enters its idle state seems encouraging. marc_culler (claiming to be Marc Culler) added on 2026-07-22 01:47:18: Correction! When I said "thread" with respect to menus and file dialogs I was not correct. I should have said "Runloop Mode". All of the events are processed by the main thread, but menus and file dialog events are handled in different runloop modes. For menus, the runloop mode is called NSEventTrackingRunLoopMode and for file dialogs it is called NSModalPanelRunLoopMode. So this is probably caused by an Apple bug which broke the transition from NSEventTrackingRunLoopMode to NSModalPanelRunLoopMode when that transition occurs by calling NSOpenPanel or NSSavePanel from a menu action. If the bug persists after the OS release Tk can probably work around it the same way - by calling NSOpenPanel and NSSavePanel from an idle task. We have to wait and see if they fix it. marc_culler (claiming to be Marc Culler) added on 2026-07-21 23:28:37: I am glad there is a simple workaround, but I would like to know what the cause of the hang is, and what changed in macOS 27 to cause this. Some things I know are: * Opening a menu launches a separate thread which handles *all* mouse and key events belonging to the Tk application. Tk does not receive any of these events while a menu is open. * In order to make Tk timer tasks get processed while a menu is open (for example, to keep the pendulum swinging in the pendulum demo while a menu is open) Tk launches its own separate thread when a menu is opened. The Tk thread processes Timer tasks only. * Opening a file dialog also causes Apple to create a separate thread which manages all mouse and key events targeting the dialog while hiding all of those events from Tk. One thing to keep in mind is that this may be an Apple bug which may get fixed before macOS 27 is released. However, it may also be an intentional change. The fact that it can be worked around by just delaying the launch of the file dialog suggests a race condition has been created which can somehow prevent Apple from launching the thread to manage the file dialog. It is hard to imagine that Apple would intentionally create a race condition like that. So I am leaning towards the idea that this is an Apple bug. The race condition theory may be wrong, however. I believe that when a menu item is selected which runs a Tk command, the Tk command may actually get run in the thread which controls the menu. So the issue may be whether a file dialog thread can be launched by a thread which is not the main thread. Maybe that used to be possible but is not any longer, due to some change in macOS 27. This alternate theory is consistent with this report, because the workaround changes the menu action from "launch file dialog" to "add command to launch file dialog to the idle queue". The idle command would then be run by the main thread instead of by the menu management thread, so the file dialog thread would be launched by the main thread with the proposed workaround. | |||
Attachments:
- menufreezereproduction.tcl [download] added by bdubzkc on 2026-07-21 13:05:02. [details]
