AvaloniaUI/Avalonia · error · ObjectDisposedException
CompositionImportedGpuSemaphore
Error message
CompositionImportedGpuSemaphore
What it means
Thrown as ObjectDisposedException (nameof CompositionImportedGpuSemaphore) by the Semaphore property when the imported GPU semaphore is null — after Dispose() nulls it or if Import() failed. As with the GPU image, the IsUsable flag is the intended pre-check (semaphore non-null and matching render context).
Source
Thrown at src/Avalonia.Base/Rendering/Composition/CompositionInterop.cs:148
_image?.Dispose();
_image = null!;
}
}
class CompositionImportedGpuSemaphore : CompositionGpuImportedObjectBase, ICompositionImportedGpuSemaphore
{
private readonly IPlatformHandle _handle;
private IPlatformRenderInterfaceImportedSemaphore? _semaphore;
public CompositionImportedGpuSemaphore(IPlatformHandle handle,
Compositor compositor, IPlatformRenderInterfaceContext context,
IExternalObjectsRenderInterfaceContextFeature feature) : base(compositor, context, feature, handle)
{
_handle = handle;
}
public IPlatformRenderInterfaceImportedSemaphore Semaphore =>
_semaphore ?? throw new ObjectDisposedException(nameof(CompositionImportedGpuSemaphore));
public bool IsUsable => _semaphore != null && Compositor.Server.RenderInterface.Value == Context;
protected override void Import()
{
_semaphore = Feature.ImportSemaphore(_handle);
}
public override void Dispose()
{
_semaphore?.Dispose();
_semaphore = null;
}
}
View on GitHub (pinned to 11c5427268)
Solutions
- Guard with IsUsable before reading Semaphore; re-import when false.
- Drop references after Dispose() and stop submitting the semaphore.
- Recreate the imported semaphore when the render interface context changes (catch context-lost during import).
Example fix
// before var sem = importedGpuSemaphore.Semaphore; // throws post-dispose // after if (!importedGpuSemaphore.IsUsable) importedGpuSemaphore = ReimportSemaphore(compositor, handle); var sem = importedGpuSemaphore.Semaphore;
Defensive patterns
Strategy: validation
Validate before calling
if (!importedGpuSemaphore.IsUsable) importedGpuSemaphore = ReimportSemaphore(compositor, handle); var sem = importedGpuSemaphore.Semaphore;
Type guard
static bool IsUsable(CompositionImportedGpuSemaphore s) => s.IsUsable;
Try / catch
try { use(importedGpuSemaphore.Semaphore); }
catch (ObjectDisposedException) { importedGpuSemaphore = ReimportSemaphore(compositor, handle); use(importedGpuSemaphore.Semaphore); } Prevention
- Check IsUsable before Semaphore and re-import when false.
- Recreate imported semaphores on render-context change.
- Do not submit a semaphore after its owning object is disposed.
When it happens
Trigger: Accessing importedGpuSemaphore.Semaphore after Dispose(), or after a failed ImportSemaphore. Reached when a cross-process/cross-device sync primitive has been torn down but still read.
Common situations: Device loss invalidating imported semaphores; using a semaphore after the composition object that owns it was disposed; a render loop reading a semaphore that a parallel dispose nulled.
Related errors
- CompositionImportedGpuImage
- Unable to access .NET memory
- Device with the corresponding LUID not found
- Not supported format
- Visual was invalidated during the render pass
AI-assisted analysis of AvaloniaUI/Avalonia@11c5427268 (2026-08-13).
Data as JSON: /api/errors/03aa804dd0b9bcbc.
Report an issue: GitHub.