Skip to content

Exception thrown if app starts while no displays connected on macOS #18895

@kirb

Description

@kirb

Describe the bug

The way CVDisplayLink is used inside PlatformRenderTimer.mm can cause the app to crash on startup, because it expects a display to immediately be available. This is not always the case - our app can update and restart itself automatically, which can happen during Power Nap, when the laptop lid is closed and no external displays are attached.

System.InvalidOperationException: Avalonia.Native was not able to start the RenderTimer. Native error code is: -6661
  ?, in void AvaloniaNativeRenderTimer.add_Tick(Action<TimeSpan> value)
  ?, in void ThreadProxyRenderTimer.add_Tick(Action<TimeSpan> value)
  ?, in void RenderLoop.Add(IRenderLoopTask i)
  ?, in new ServerCompositor(IRenderLoop renderLoop, IPlatformGraphics platformGraphics, CompositionOptions options, BatchStreamObjectPool<object> batchObjectPool, BatchStreamMemoryPool batchMemoryPool)
  ?, in new Compositor(IRenderLoop loop, IPlatformGraphics gpu, bool useUiThreadForSynchronousCommits, ICompositorScheduler scheduler, bool reclaimBuffersImmediately, Dispatcher dispatcher, CompositionOptions options) x 3
  ?, in void AvaloniaNativePlatform.DoInitialize(AvaloniaNativePlatformOptions options)
  ?, in AvaloniaNativePlatform AvaloniaNativePlatform.Initialize(IntPtr factory, AvaloniaNativePlatformOptions options) x 2
  ?, in AppBuilder AvaloniaNativePlatformExtensions.UseAvaloniaNative(AppBuilder builder)+() => { }
  ?, in void AppBuilder.SetupUnsafe()
  ?, in void AppBuilder.Setup()
  ?, in AppBuilder AppBuilder.SetupWithLifetime(IApplicationLifetime lifetime)
  ?, in int ClassicDesktopStyleApplicationLifetimeExtensions.StartWithClassicDesktopLifetime(AppBuilder builder, string[] args, Action<IClassicDesktopStyleApplicationLifetime> lifetimeBuilder)
  ?, in void Program.Main(string[] args)

This seems related to, or possibly the same as, #14927.

To Reproduce

Repro steps are a bit annoying given the nature of the edge case. This might be sufficient:

  1. Build any Avalonia app. The absolute barebones Avalonia .NET App template will work.
  2. Create the following shell script:
while true; do
	./MyAvaloniaApp & pid=$!
	sleep 10
	kill $pid
done
  1. Run the script.
  2. Close the lid and disconnect all cables (displays/power) from the laptop, but keep it in Wi-Fi range.
  3. Wait some time for Power Nap to happen.

Expected behavior

The app consistently launches 100% of the time. No exceptions are logged in the terminal window.

Avalonia version

11.3.0

OS

macOS

Additional context

We worked around the crash with the following, which we call in Program.Main() just before BuildAvaloniaApp():

[SupportedOSPlatform("macos")]
public static partial class CVDisplayLink
{
	private const string CoreVideoLibrary = "/System/Library/Frameworks/CoreVideo.framework/CoreVideo";

	[LibraryImport(CoreVideoLibrary)]
	private static partial int CVDisplayLinkCreateWithActiveCGDisplays(out IntPtr displayLinkOut);

	[LibraryImport(CoreVideoLibrary)]
	private static partial void CVDisplayLinkRelease(IntPtr displayLink);

	/// <summary>
	/// Loop until a display link is available. If no displays are connected, this will wait until one does connect.
	/// </summary>
	public static async Task AwaitDisplayLink()
	{
		IntPtr displayLink;
		while (CVDisplayLinkCreateWithActiveCGDisplays(out displayLink) != 0) {
			await Task.Delay(1000);
		}

		CVDisplayLinkRelease(displayLink);
	}
}

Metadata

Metadata

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions