NSImage drawInRect deadlock
NSImage drawInRect deadlock
- Subject: NSImage drawInRect deadlock
- From: Andrew Keller <email@hidden>
- Date: Mon, 08 Aug 2016 14:05:01 -0400
Hi all,
In my app, I’m creating thumbnails of images. To do this in parallel, I’m using the global background dispatch queue:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
@autoreleasepool {
NSSize *thumbnailSize = // calculate thumbnail size
NSImage *thumbnail = [img imageByResizingTo:thumbnailSize];
// Do stuff with result
}
});
I have this in a category extension on NSImage:
- (NSImage *)imageByResizingTo:(NSSize)newSize {
NSImage *newImage = [[NSImage alloc] initWithSize:newSize];
NSSize currentSize = [self size];
NSRect currentRect = NSMakeRect(0, 0, currentSize.width, currentSize.height);
NSRect newRect = NSMakeRect(0, 0, newSize.width, newSize.height);
[newImage lockFocus];
[self drawInRect:newRect fromRect:currentRect operation:NSCompositeCopy fraction:1.0f];
[newImage unlockFocus];
return newImage;
}
However, when enough images are being processed simultaneously, the background threads servicing the queue appear to all lock up with this stack trace:
#0 0x00007fff93d2951a in semaphore_wait_trap ()
#1 0x00007fff8b75ac5b in _os_semaphore_wait ()
#2 0x0000000100074e12 in _dispatch_barrier_sync_f_slow ()
#3 0x00007fff89315182 in ___lldb_unnamed_function872$$RawCamera ()
#4 0x00007fff86a98e26 in ImageProviderCopyImageBlockSetCallback ()
#5 0x00007fff877d5279 in img_blocks_create ()
#6 0x00007fff87804598 in img_blocks_extent ()
#7 0x00007fff877c55b0 in img_data_lock ()
#8 0x00007fff877c25a6 in CGSImageDataLock ()
#9 0x00007fff8be62a02 in ripc_AcquireImage ()
#10 0x00007fff8be61525 in ripc_DrawImage ()
#11 0x00007fff877c21b0 in CGContextDrawImage ()
#12 0x00007fff8ee9627d in __75-[NSBitmapImageRep _withoutChangingBackingPerformBlockUsingBackingCGImage:]_block_invoke ()
#13 0x00007fff8ee95969 in -[NSBitmapImageRep _withoutChangingBackingPerformBlockUsingBackingCGImage:] ()
#14 0x00007fff8ee958f5 in __53-[NSBitmapImageRep _performBlockUsingBackingCGImage:]_block_invoke ()
#15 0x00007fff8ee95859 in -[NSBitmapImageRep _performBlockUsingBackingCGImage:] ()
#16 0x00007fff8ee957b4 in -[NSBitmapImageRep CGImage] ()
#17 0x00007fff8ee95719 in -[NSBitmapImageRep CGImageForProposedRect:context:hints:] ()
#18 0x00007fff8ee4e6af in __74-[NSImageRep drawInRect:fromRect:operation:fraction:respectFlipped:hints:]_block_invoke ()
#19 0x00007fff8ee4e195 in -[NSImageRep drawInRect:fromRect:operation:fraction:respectFlipped:hints:] ()
#20 0x00007fff8f33e656 in __71-[NSImage drawInRect:fromRect:operation:fraction:respectFlipped:hints:]_block_invoke1012 ()
#21 0x00007fff8edf4dc6 in -[NSImage _usingBestRepresentationForRect:context:hints:body:] ()
#22 0x00007fff8ee4d9d7 in -[NSImage drawInRect:fromRect:operation:fraction:respectFlipped:hints:] ()
#23 0x0000000100008aa5 in -[NSImage(AppKitHelpers) imageByResizingTo:] at /Users/kelleran/Documents/Example/Example/AppKitHelpers.m:43
[ snip ]
Then, if I try to do anything in the app such as click a button or quit the app, the main thread locks up too, with this stack trace:
#0 0x00007fff93d2951a in semaphore_wait_trap ()
#1 0x000000010006d2d5 in _dispatch_semaphore_wait_slow ()
#2 0x00007fff908f4da9 in xpc_connection_send_message_with_reply_sync ()
#3 0x00007fff90b1fe99 in _LSCopyApplicationInformation ()
#4 0x00007fff90b29a6c in _LSCopyApplicationInformationItem ()
#5 0x00007fff8ef8d08d in -[NSApplication _copyPublicPersistentUIInfo] ()
#6 0x00007fff8ef8b570 in recursivelyEncodeInvalidPersistentState ()
#7 0x00007fff8ef8a13a in -[NSPersistentUIManager flushAllChangesOptionallyWaitingUntilDone:updatingSnapshots:] ()
#8 0x00007fff8ef89c6b in -[NSPersistentUIManager flushPersistentStateAndClose:waitingUntilDone:] ()
#9 0x00007fff8ed5c466 in run_cocoa_block ()
#10 0x00007fff8ef89b8e in __42-[NSPersistentUIManager acquireDirtyState]_block_invoke ()
#11 0x0000000100062fc3 in _dispatch_client_callout ()
[ snip ]
Experimentally, it seems that I need at least 10 to 20-ish threads actively performing work from the queue simultaneously for the deadlock to occur. However, just because more threads reproduces the issue faster doesn’t mean that fewer threads makes the problem go away entirely — it could just be a matter of probability and luck. To guarantee that nothing deadlocks, must I perform this work serially, or is there something I’m doing wrong?
Thanks,
- Andrew Keller
_______________________________________________
Cocoa-dev mailing list (email@hidden)
Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden