reports icon indicating copy to clipboard operation
reports copied to clipboard

FB9857161: NSAlert crashes app if called from a Task

Open sindresorhus opened this issue 3 years ago • 6 comments

  • Date: 2022-01-24
  • Resolution: Open
  • Area: AppKit
  • OS: macOS 12.1
  • Type: Application Crash

Description

NSAlert crashes when called from a Task, even if you use @MainActor. This is a pretty serious issue as a lot of code uses NSAlert.

I have attached a sample project that reproduces the issue.

Task { @MainActor in
	let alert = NSAlert()
	alert.messageText = "X"
	alert.runModal()
}

Stack trace:

Thread 1 Queue : com.apple.main-thread (serial)
#0	0x00000001c7c37a18 in libunwind::UnwindCursor<libunwind::LocalAddressSpace, libunwind::Registers_arm64>::step() ()
#1	0x00000001bd0b762c in objc_addExceptionHandler ()
#2	0x00000001c00e5e80 in -[NSApplication _commonBeginModalSessionForWindow:relativeToWindow:modalDelegate:didEndSelector:contextInfo:] ()
#3	0x00000001c00e5a50 in __35-[NSApplication runModalForWindow:]_block_invoke_2 ()
#4	0x00000001c00e5a08 in __35-[NSApplication runModalForWindow:]_block_invoke ()
#5	0x00000001c00e5198 in _NSTryRunModal ()
#6	0x00000001c00e5048 in -[NSApplication runModalForWindow:] ()
#7	0x00000001c024e218 in __19-[NSAlert runModal]_block_invoke_2 ()
#8	0x00000001c024e15c in __19-[NSAlert runModal]_block_invoke ()
#9	0x00000001c00e5198 in _NSTryRunModal ()
#10	0x00000001c016f694 in -[NSAlert runModal] ()
#11	0x0000000104b69ddc in closure #1 in ViewController.viewDidLoad()

Files

Sample project.zip

sindresorhus avatar Jan 24 '22 12:01 sindresorhus

Hacky workaround:

extension NSAlert {
	/**
	Workaround to allow using `NSAlert` in a `Task`.

	[FB9857161](https://github.com/feedback-assistant/reports/issues/288)
	*/
	@MainActor
	@discardableResult
	func run() async -> NSApplication.ModalResponse {
		await withCheckedContinuation { continuation in
			DispatchQueue.main.async { [self] in
				continuation.resume(returning: runModal())
			}
		}
	}
}

sindresorhus avatar Jan 24 '22 13:01 sindresorhus

+1. (Encountered the issue when refactoring to swift-concurrency)

Neither MainActor.run { alert.runModal() }, Task { await alert.runModal() } and Task { @MainActor alert.runModal()} works for me. The same crash and stack trace.

Thanks for your workaround. But it still seems tricky for me.

I can't call it in MainActor.run{ } since there is no concurrency context(can't use await in it). And I can't call it plain Task context like Task { ... await alert.runModal } because NSAlert() related method are all marked with @MainActor. So I need the following.

Task { 
  let alert = await NSAlert()
  await alert.messageText = "X"
  await alert.run()
}

And the final answer is using the above code you provide which is

Task { @MainActor in
  let alert = NSAlert()
  alert.messageText = "X"
  await alert.run()
}

But personally I prefer MainActor.run {} over Task { @MainActor in ... }

Hoping Apple could fix problem asap😳.

截屏2022-02-15 13 18 32

Kyle-Ye avatar Feb 15 '22 05:02 Kyle-Ye

By the way, in addition to NSAlert, NSOpenPanel and other NSView containing runModal methods will also crash for the same reason

The same approach could be applied 👇

extension NSOpenPanel {
    /**
    Workaround to allow using `NSOpenPanel` in a `Task`.

    [FB9857161](https://github.com/feedback-assistant/reports/issues/288)
    */
    @MainActor
    @discardableResult
    func run() async -> NSApplication.ModalResponse {
        await withCheckedContinuation { continuation in
            DispatchQueue.main.async { [self] in
                continuation.resume(returning: runModal())
            }
        }
    }
}

Kyle-Ye avatar Feb 15 '22 05:02 Kyle-Ye

For NSOpenPanel, you can just use await panel.begin() (the auto-transformed completion handler method).

sindresorhus avatar Feb 15 '22 06:02 sindresorhus

Also, don't forget to duplicate my report, to make it more likely Apple will see it and listen.

sindresorhus avatar Feb 15 '22 06:02 sindresorhus

is seems to be fixed when run in macOS 13 (beta5). Still crashes when run on macOS 12

krzyzanowskim avatar Aug 10 '22 19:08 krzyzanowskim

Fixed in macOS 13.3.1

sindresorhus avatar Apr 25 '23 14:04 sindresorhus