{"record":{"id":"4c8f2b4dbc02b3cc","repo":"AvaloniaUI/Avalonia","slug":"iosurfacelock-failed","errorCode":null,"errorMessage":"IOSurfaceLock failed","messagePattern":"IOSurfaceLock failed","errorType":"exception","errorClass":"Exception","httpStatus":null,"severity":"error","filePath":"samples/GpuInterop/VulkanDemo/VulkanImage.cs","lineNumber":350,"sourceCode":"        {\n            Api.DestroyImageView(_device, _imageView, null);\n            Api.DestroyImage(_device, InternalHandle, null);\n            Api.FreeMemory(_device, _imageMemory, null);\n\n            _imageView = default;\n            InternalHandle = default;\n            _imageMemory = default;\n        }\n\n        public void SaveTexture(string path)\n        {\n            if (_vk.GrContext == null)\n            {\n                if (_hasIOSurface)\n                {\n                    var surf = ExportIOSurface();\n                    if (NativeMethods.IOSurfaceLock(surf, 0, IntPtr.Zero) != 0)\n                        throw new Exception(\"IOSurfaceLock failed\");\n                    var w = (int)NativeMethods.IOSurfaceGetWidth(surf);\n                    var h = (int)NativeMethods.IOSurfaceGetHeight(surf);\n                    var sstride = NativeMethods.IOSurfaceGetBytesPerRow(surf);\n\n                    var pSurface = NativeMethods.IOSurfaceGetBaseAddress(surf);\n                    using var b = new Avalonia.Media.Imaging.Bitmap(PixelFormat.Bgra8888,\n                        AlphaFormat.Premul, pSurface, new PixelSize(w, h),\n                        new Vector(96, 96), (int)sstride);\n                    b.Save(path, PngBitmapEncoderOptions.Default);\n\n                    NativeMethods.IOSurfaceUnlock(surf, 0, IntPtr.Zero);\n                    return;\n                }\n                else\n                    throw new NotSupportedException(\"Need skia to dump textures, sorry\");\n            }\n\n            _vk.GrContext.ResetContext();","sourceCodeStart":332,"sourceCodeEnd":368,"githubUrl":"https://github.com/AvaloniaUI/Avalonia/blob/11c542726898ae954a1ef668c65ec79ec92ab17d/samples/GpuInterop/VulkanDemo/VulkanImage.cs#L332-L368","documentation":"On macOS SaveTexture locks the exported IOSurface via IOSurfaceLock to read its pixels; if the return is non-zero it throws a generic Exception. A non-zero kIOReturn code means the kernel rejected the lock (busy, invalid, or already locked).","triggerScenarios":"IOSurfaceLock(surf, 0, NULL) returning non-zero — surface already locked, in use by the GPU (not yet flushed), or surf is an invalid/zero handle from a failed export. Common right after presenting or while Metal still holds it.","commonSituations":"Dumping a texture that is still GPU-owned without flushing/waiting; double-locking; exporting on a thread without IOSurface allocation entitlement; calling SaveTexture in a tight loop without unlocking.","solutions":["Ensure the IOSurface is not GPU-resident at dump time — insert a wait/fence or flush the GrContext before locking.","Always pair IOSurfaceLock with IOSurfaceUnlock (the sample does) and avoid nesting locks.","Decode the non-zero return (kIOReturnBusy etc.) instead of throwing blindly to pinpoint the cause.","Confirm ExportIOSurface() returned a valid (non-zero) handle before locking."],"exampleFix":"// before\nif (NativeMethods.IOSurfaceLock(surf, 0, IntPtr.Zero) != 0)\n    throw new Exception(\"IOSurfaceLock failed\");\n\n// after (capture the error code and surface state)\nvar kr = NativeMethods.IOSurfaceLock(surf, 0, IntPtr.Zero);\nif (kr != 0)\n    throw new InvalidOperationException($\"IOSurfaceLock failed (kern_return_t=0x{kr:X}: {new System.ComponentModel.Win32Exception(kr).Message}).\");","handlingStrategy":"retry","validationCode":"// ensure GPU work is flushed before locking\n_vk.GrContext?.Flush();\n_vk.GrContext?.Submit();","typeGuard":"static bool IsLockable(IntPtr surf) => surf != IntPtr.Zero;","tryCatchPattern":"for (int i = 0; i < 3; i++)\n{\n    if (NativeMethods.IOSurfaceLock(surf, 0, IntPtr.Zero) == 0) { /* read */ break; }\n    Thread.Sleep(5); // surface may be GPU-busy\n}","preventionTips":["Pair every IOSurfaceLock with IOSurfaceUnlock.","Flush the GPU context before locking to avoid kIOReturnBusy.","Decode the return code; do not treat all non-zero as identical."],"tags":["macos","iosurface","texture","dump","sample"],"backgroundTag":null,"analyzedSha":"11c542726898ae954a1ef668c65ec79ec92ab17d","analyzedAt":"2026-08-13T11:57:40.261Z","schemaVersion":2},"datasetVersion":"2026-08-13T14:17:21.547Z"}