Re: NSImage drawInRect deadlock

2016-08-10 Thread Doug Hill

> On Aug 10, 2016, at 4:49 PM, Graham Cox  wrote:
> 
> 
>> On 9 Aug 2016, at 4:05 AM, Andrew Keller  wrote:
>> 
>> In my app, I’m creating thumbnails of images.  To do this in parallel, I’m 
>> using the global background dispatch queue:
> 
> 
> Just to throw another consideration into the discusion, you don’t say what 
> the thumbnails are being used for.
> 
> Typically you might want to display these to the user to browse a collection 
> of images. If that’s the case here, then it might be much more performant to 
> create them only as needed for display, rather than generate a huge batch of 
> them ahead of time. In other words, when an image thumbnail is required *for 
> display*, kick off a thread to fetch it (or display the one you have cached 
> already). This means that first time the user scrolls through the collection, 
> thumbnails will ‘pop in’ to view as each thread completes, but this effect is 
> usually easily tolerated. With scroll pre-fetching you might not even see it 
> at all.
> 
> This way your threads are naturally limited by the number of images that are 
> shown at a time, or scrolled newly into view.
> 
> —Graham

+1
___

Cocoa-dev mailing list (Cocoa-dev@lists.apple.com)

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:
https://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com

This email sent to arch...@mail-archive.com

Re: NSImage drawInRect deadlock

2016-08-10 Thread Graham Cox

> On 9 Aug 2016, at 4:05 AM, Andrew Keller  wrote:
> 
> In my app, I’m creating thumbnails of images.  To do this in parallel, I’m 
> using the global background dispatch queue:


Just to throw another consideration into the discusion, you don’t say what the 
thumbnails are being used for.

Typically you might want to display these to the user to browse a collection of 
images. If that’s the case here, then it might be much more performant to 
create them only as needed for display, rather than generate a huge batch of 
them ahead of time. In other words, when an image thumbnail is required *for 
display*, kick off a thread to fetch it (or display the one you have cached 
already). This means that first time the user scrolls through the collection, 
thumbnails will ‘pop in’ to view as each thread completes, but this effect is 
usually easily tolerated. With scroll pre-fetching you might not even see it at 
all.

This way your threads are naturally limited by the number of images that are 
shown at a time, or scrolled newly into view.

—Graham



___

Cocoa-dev mailing list (Cocoa-dev@lists.apple.com)

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:
https://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com

This email sent to arch...@mail-archive.com

Re: NSImage drawInRect deadlock

2016-08-10 Thread Andrew Keller
Am 10.08.2016 um 2:48 vorm. schrieb Quincey Morris 
:
> On Aug 9, 2016, at 20:47 , Andrew Keller  > wrote:
>> 
>> 2. When utilizing Mike’s approach to limiting the number of parallel tasks 
>> down to, say, 1-8, I have been completely unable to reproduce the deadlock.
>> 3. When utilizing Mike’s approach to limiting the number of parallel tasks, 
>> Xcode is still saying that threads a being created like crazy — almost one 
>> thread per block submitted to the queue.
> 
> I’m not a big fan of Mike’s proposed solution. If you want to use N-wide 
> parallelism, then use NSOperationQueue, not GCD.
> 
> Blocks dispatched to GCD queues should not contain any internal waits, such 
> as for I/O. Instead, a dispatched block should occupy the CPU continuously, 
> and at the end do one of 3 things:
> 
> 1. Just exit.
> 
> 2. Start an asynchronous action, such as GCD I/O, with a completion handler 
> that’s not scheduled until the action is done.
> 
> 3. Queue another block that represents another processing step in the overall 
> task being performed.
> 
> The point of #3 is that I think it’s also a mistake to queue large numbers of 
> blocks to GCD all at once, for the pragmatic reason that if you accidentally 
> violate the non-internal-waits rule, the size of the thread explosion depends 
> on the amount of combustible material that’s queued. It’s better for *each 
> operation* to queue its successor, and to start the whole thing off by 
> priming the pump with a modest number of blocks.
> 
> The other thing to be very careful of is global locks. If your code (perhaps 
> outside your direct control) hits any global locks that affect multiple 
> threads, *and* if the kind of lock being used is slower to test when locked 
> than when unlocked, then more parallelism can be counterproductive.
> 
> I’ve run into this in cases where adding more operations on more CPUs just 
> adds a disproportionate amount of system overhead, decreasing the throughput 
> of the actual calculation.
> 
> The point of all this is that you may not have enough control of the internal 
> behavior of those NSImage methods to safely use GCD parallelism for a job 
> like this. NSOperationQueue might be a better solution.

Interesting idea.  I’ve modified my scheduler to use existing work to post the 
next work to GCD.  This implementation has no semaphores and no custom dispatch 
queues at all.  Interestingly, I get roughly the same results: no crazy 
swapping to disk, no deadlock, and Xcode is still saying that threads are 
piling up like crazy.  (Note that I’m not letting the concurrent procedure 
count past 8)

That said, this implementation does “feel” better than before from an 
architectural point of view.  I believe I have more along the lines of 
“computationally intensive things I can do to iteratively improve the UI” 
rather than “a long list of work to do”.  Based on what the user is doing, I 
can prioritize certain parts of the UI being rendered before others, and using 
existing work to post the next work in GCD makes a lot of sense because the 
concept of “future work” can change so easily.  I know NSOperationQueue 
supports giving certain tasks priority, but I’d have to actually set all of 
those values — whereas with blocks posting other blocks, I get this behavior 
almost for free because there is always “the next most useful thing to render 
in the UI”.  If some distant future work happens to become irreverent because 
the user moved to another screen, then this approach simply never gets around 
to queueing that work, which is usually good.

Thanks,
 - Andrew

___

Cocoa-dev mailing list (Cocoa-dev@lists.apple.com)

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:
https://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com

This email sent to arch...@mail-archive.com

Re: NSImage drawInRect deadlock

2016-08-10 Thread Quincey Morris
On Aug 9, 2016, at 20:47 , Andrew Keller  wrote:
> 
> 2. When utilizing Mike’s approach to limiting the number of parallel tasks 
> down to, say, 1-8, I have been completely unable to reproduce the deadlock.
> 3. When utilizing Mike’s approach to limiting the number of parallel tasks, 
> Xcode is still saying that threads a being created like crazy — almost one 
> thread per block submitted to the queue.

I’m not a big fan of Mike’s proposed solution. If you want to use N-wide 
parallelism, then use NSOperationQueue, not GCD.

Blocks dispatched to GCD queues should not contain any internal waits, such as 
for I/O. Instead, a dispatched block should occupy the CPU continuously, and at 
the end do one of 3 things:

1. Just exit.

2. Start an asynchronous action, such as GCD I/O, with a completion handler 
that’s not scheduled until the action is done.

3. Queue another block that represents another processing step in the overall 
task being performed.

The point of #3 is that I think it’s also a mistake to queue large numbers of 
blocks to GCD all at once, for the pragmatic reason that if you accidentally 
violate the non-internal-waits rule, the size of the thread explosion depends 
on the amount of combustible material that’s queued. It’s better for *each 
operation* to queue its successor, and to start the whole thing off by priming 
the pump with a modest number of blocks.

The other thing to be very careful of is global locks. If your code (perhaps 
outside your direct control) hits any global locks that affect multiple 
threads, *and* if the kind of lock being used is slower to test when locked 
than when unlocked, then more parallelism can be counterproductive.

I’ve run into this in cases where adding more operations on more CPUs just adds 
a disproportionate amount of system overhead, decreasing the throughput of the 
actual calculation.

The point of all this is that you may not have enough control of the internal 
behavior of those NSImage methods to safely use GCD parallelism for a job like 
this. NSOperationQueue might be a better solution.

___

Cocoa-dev mailing list (Cocoa-dev@lists.apple.com)

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:
https://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com

This email sent to arch...@mail-archive.com

Re: NSImage drawInRect deadlock

2016-08-09 Thread Andrew Keller
Am 09.08.2016 um 7:38 nachm. schrieb Andrew Keller :
> Am 09.08.2016 um 3:59 nachm. schrieb Kyle Sluder :
>> On Tue, Aug 9, 2016, at 07:38 AM, Andrew Keller wrote:
>> 
>>> I was under the impression from the docs that macOS
>>> handles the thread pool “automatically”.
>> 
>> Mike Ash did a good job explaining why this isn’t as “automatic” as one
>> might think:
>> https://www.mikeash.com/pyblog/friday-qa-2009-09-25-gcd-practicum.html 
>> 
> 
> Wow.  That was incredibly eye opening.  I’m seeing the exact same symptoms 
> he’s describing — the deadlock is just the sugar on top.
> 
> I’m going to play with some of his concepts and see what happens.  I wonder 
> if the deadlock can be worked around by using the image scaling approach he 
> uses in his example.

A few interesting observations:

1. Mike’s thumbnail generation process is vulnerable to the same deadlock as my 
code.  In hindsight, this is not surprising since we are both using drawInRect.
2. When utilizing Mike’s approach to limiting the number of parallel tasks down 
to, say, 1-8, I have been completely unable to reproduce the deadlock.
3. When utilizing Mike’s approach to limiting the number of parallel tasks, 
Xcode is still saying that threads a being created like crazy — almost one 
thread per block submitted to the queue.

#2 suggests that this deadlock issue is related to very large (20+?  30+?  40+? 
 100+?) numbers of threads operating at the same time.

Not sure what to do about #3.  So long as it doesn’t negatively impact the app, 
I suppose I could ignore it for now.  (Unless anyone has seen this behavior, 
and perhaps foresees a problem with it.)

Thanks,
 - Andrew


___

Cocoa-dev mailing list (Cocoa-dev@lists.apple.com)

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:
https://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com

This email sent to arch...@mail-archive.com

Re: NSImage drawInRect deadlock

2016-08-09 Thread Andrew Keller
Am 09.08.2016 um 3:59 nachm. schrieb Kyle Sluder :
> On Tue, Aug 9, 2016, at 07:38 AM, Andrew Keller wrote:
> 
>> I was under the impression from the docs that macOS
>> handles the thread pool “automatically”.
> 
> Mike Ash did a good job explaining why this isn’t as “automatic” as one
> might think:
> https://www.mikeash.com/pyblog/friday-qa-2009-09-25-gcd-practicum.html 
> 

Wow.  That was incredibly eye opening.  I’m seeing the exact same symptoms he’s 
describing — the deadlock is just the sugar on top.

I’m going to play with some of his concepts and see what happens.  I wonder if 
the deadlock can be worked around by using the image scaling approach he uses 
in his example.

Thanks,
 - Andrew

___

Cocoa-dev mailing list (Cocoa-dev@lists.apple.com)

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:
https://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com

This email sent to arch...@mail-archive.com

Re: NSImage drawInRect deadlock

2016-08-09 Thread Jens Alfke

>> I was under the impression from the docs that macOS
>> handles the thread pool “automatically”.

GCD’s pool does use a thread per CPU core … but only as long as none of the 
operations run for a long time. If its entire thread pool is busy for “a while” 
and there are more blocks waiting in the queue, GCD will start to create more 
threads to keep up, rather than let the waiting threads starve.

—Jens
___

Cocoa-dev mailing list (Cocoa-dev@lists.apple.com)

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:
https://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com

This email sent to arch...@mail-archive.com

Re: NSImage drawInRect deadlock

2016-08-09 Thread Kyle Sluder
On Tue, Aug 9, 2016, at 07:38 AM, Andrew Keller wrote:
> Am 08.08.2016 um 8:12 nachm. schrieb Kyle Sluder :
> > 
> > On Mon, Aug 8, 2016, at 05:11 PM, Jens Alfke wrote:
> >> 
> >>> On Aug 8, 2016, at 2:54 PM, Aaron Tuller  wrote:
> >>> 
> >>> "The following classes and functions are generally not thread-safe. In 
> >>> most cases, you can use these classes from any thread as long as you use 
> >>> them from only one thread at a time."
> >> 
> >> The images are only being used on one thread at a time. The problem seems
> >> to be that the NSImage _cache_ is shared and (apparently) not
> >> thread-safe.
> > 
> > I wouldn’t jump immediately to thread-unsafety. It’s possible that
> > Andrew is simply exhausting the thread pool.
> > 
> > Andrew, are you doing anything to limit the amount of decode operations
> > you’re putting on the global queue?
> 
> Not presently.  Under normal usage, it hovers around 10-40 threads, but
> I’ve seen it as high as 200.  Honestly, I was hoping to limit it to 3 or
> 6 — just experimentally, one thread by itself can saturate roughly 20-30%
> of my i7 processor.  If 3-6 simultaneous operations will saturate the
> whole processor, I don’t see much value in letting it go much higher than
> that.
> 
> What is the preferred way to limit the number of parallel operations in
> the global queue?  I was under the impression from the docs that macOS
> handles the thread pool “automatically”.

Mike Ash did a good job explaining why this isn’t as “automatic” as one
might think:
https://www.mikeash.com/pyblog/friday-qa-2009-09-25-gcd-practicum.html

--Kyle Sluder

> 
> Thanks,
>  - Andrew
> 

___

Cocoa-dev mailing list (Cocoa-dev@lists.apple.com)

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:
https://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com

This email sent to arch...@mail-archive.com

Re: NSImage drawInRect deadlock

2016-08-09 Thread Damien DeVille
You could change your code to use `NSBlockOperation`. You would then
have an `NSOperationQueue` on which you could set the
`maxConcurrentOperationCount` to whatever number makes sense for you.

Damien

On Tue, Aug 9, 2016, at 05:38 AM, Andrew Keller wrote:
> Am 08.08.2016 um 8:12 nachm. schrieb Kyle Sluder :
> > 
> > On Mon, Aug 8, 2016, at 05:11 PM, Jens Alfke wrote:
> >> 
> >>> On Aug 8, 2016, at 2:54 PM, Aaron Tuller  wrote:
> >>> 
> >>> "The following classes and functions are generally not thread-safe. In 
> >>> most cases, you can use these classes from any thread as long as you use 
> >>> them from only one thread at a time."
> >> 
> >> The images are only being used on one thread at a time. The problem seems
> >> to be that the NSImage _cache_ is shared and (apparently) not
> >> thread-safe.
> > 
> > I wouldn’t jump immediately to thread-unsafety. It’s possible that
> > Andrew is simply exhausting the thread pool.
> > 
> > Andrew, are you doing anything to limit the amount of decode operations
> > you’re putting on the global queue?
> 
> Not presently.  Under normal usage, it hovers around 10-40 threads, but
> I’ve seen it as high as 200.  Honestly, I was hoping to limit it to 3 or
> 6 — just experimentally, one thread by itself can saturate roughly 20-30%
> of my i7 processor.  If 3-6 simultaneous operations will saturate the
> whole processor, I don’t see much value in letting it go much higher than
> that.
> 
> What is the preferred way to limit the number of parallel operations in
> the global queue?  I was under the impression from the docs that macOS
> handles the thread pool “automatically”.
> 
> Thanks,
>  - Andrew
> 
> 
> ___
> 
> Cocoa-dev mailing list (Cocoa-dev@lists.apple.com)
> 
> 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:
> https://lists.apple.com/mailman/options/cocoa-dev/damien%40ddeville.me
> 
> This email sent to dam...@ddeville.me

___

Cocoa-dev mailing list (Cocoa-dev@lists.apple.com)

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:
https://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com

This email sent to arch...@mail-archive.com

Re: NSImage drawInRect deadlock

2016-08-09 Thread Andrew Keller
Am 08.08.2016 um 8:12 nachm. schrieb Kyle Sluder :
> 
> On Mon, Aug 8, 2016, at 05:11 PM, Jens Alfke wrote:
>> 
>>> On Aug 8, 2016, at 2:54 PM, Aaron Tuller  wrote:
>>> 
>>> "The following classes and functions are generally not thread-safe. In most 
>>> cases, you can use these classes from any thread as long as you use them 
>>> from only one thread at a time."
>> 
>> The images are only being used on one thread at a time. The problem seems
>> to be that the NSImage _cache_ is shared and (apparently) not
>> thread-safe.
> 
> I wouldn’t jump immediately to thread-unsafety. It’s possible that
> Andrew is simply exhausting the thread pool.
> 
> Andrew, are you doing anything to limit the amount of decode operations
> you’re putting on the global queue?

Not presently.  Under normal usage, it hovers around 10-40 threads, but I’ve 
seen it as high as 200.  Honestly, I was hoping to limit it to 3 or 6 — just 
experimentally, one thread by itself can saturate roughly 20-30% of my i7 
processor.  If 3-6 simultaneous operations will saturate the whole processor, I 
don’t see much value in letting it go much higher than that.

What is the preferred way to limit the number of parallel operations in the 
global queue?  I was under the impression from the docs that macOS handles the 
thread pool “automatically”.

Thanks,
 - Andrew


___

Cocoa-dev mailing list (Cocoa-dev@lists.apple.com)

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:
https://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com

This email sent to arch...@mail-archive.com

Re: NSImage drawInRect deadlock

2016-08-08 Thread Kyle Sluder
On Mon, Aug 8, 2016, at 05:11 PM, Jens Alfke wrote:
> 
> > On Aug 8, 2016, at 2:54 PM, Aaron Tuller  wrote:
> > 
> > "The following classes and functions are generally not thread-safe. In most 
> > cases, you can use these classes from any thread as long as you use them 
> > from only one thread at a time."
> 
> The images are only being used on one thread at a time. The problem seems
> to be that the NSImage _cache_ is shared and (apparently) not
> thread-safe.

I wouldn’t jump immediately to thread-unsafety. It’s possible that
Andrew is simply exhausting the thread pool.

Andrew, are you doing anything to limit the amount of decode operations
you’re putting on the global queue?

--Kyle

> 
> —Jens

___

Cocoa-dev mailing list (Cocoa-dev@lists.apple.com)

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:
https://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com

This email sent to arch...@mail-archive.com

Re: NSImage drawInRect deadlock

2016-08-08 Thread Jens Alfke

> On Aug 8, 2016, at 2:54 PM, Aaron Tuller  wrote:
> 
> "The following classes and functions are generally not thread-safe. In most 
> cases, you can use these classes from any thread as long as you use them from 
> only one thread at a time."

The images are only being used on one thread at a time. The problem seems to be 
that the NSImage _cache_ is shared and (apparently) not thread-safe.

—Jens
___

Cocoa-dev mailing list (Cocoa-dev@lists.apple.com)

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:
https://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com

This email sent to arch...@mail-archive.com

Re: NSImage drawInRect deadlock

2016-08-08 Thread Aaron Tuller
On Aug 8, 2016, at 1:48 PM, Jens Alfke  wrote:
> On Aug 8, 2016, at 12:46 PM, Aaron Tuller  > wrote:
>> 
>> Search for NSImage here:
>> 
>> https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/Multithreading/ThreadSafetySummary/ThreadSafetySummary.html#//apple_ref/doc/uid/1057i-CH12-126728
>>  
>> 
>> 
> 
> All it says about NSImage is: "One thread can create an NSImage object, draw 
> to the image buffer, and pass it off to the main thread for drawing. The 
> underlying image cache is shared among all threads.”
> 
> So it sounds like the problem isn’t the new NSImage but the existing one 
> being drawn into it. It might help to get the NSImage’s imageRep on the main 
> thread and then pass that to the background thread.
> 
> (I’ve got some similar code in one of my apps that creates NSImages on a 
> parallel dispatch queue and draws into them, but it only draws text, not any 
> pre-existing images. This code works fine even with dozens of tasks at a 
> time.)


Well it also says:

"The following classes and functions are generally not thread-safe. In most 
cases, you can use these classes from any thread as long as you use them from 
only one thread at a time."

and then lists NSImage. That's why a serial queue would probably work...or 
using something else like passing around an image rep as Jens suggests and 
drawing it to a local NSGraphicsContext backed by a NSBitmapImageRep.

-aaron
___

Cocoa-dev mailing list (Cocoa-dev@lists.apple.com)

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:
https://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com

This email sent to arch...@mail-archive.com

Re: NSImage drawInRect deadlock

2016-08-08 Thread Jens Alfke

> On Aug 8, 2016, at 12:46 PM, Aaron Tuller  wrote:
> 
> Search for NSImage here:
> 
> https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/Multithreading/ThreadSafetySummary/ThreadSafetySummary.html#//apple_ref/doc/uid/1057i-CH12-126728
>  
> 
> 

All it says about NSImage is: "One thread can create an NSImage object, draw to 
the image buffer, and pass it off to the main thread for drawing. The 
underlying image cache is shared among all threads.”

So it sounds like the problem isn’t the new NSImage but the existing one being 
drawn into it. It might help to get the NSImage’s imageRep on the main thread 
and then pass that to the background thread.

(I’ve got some similar code in one of my apps that creates NSImages on a 
parallel dispatch queue and draws into them, but it only draws text, not any 
pre-existing images. This code works fine even with dozens of tasks at a time.)

—Jens
___

Cocoa-dev mailing list (Cocoa-dev@lists.apple.com)

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:
https://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com

This email sent to arch...@mail-archive.com

Re: NSImage drawInRect deadlock

2016-08-08 Thread Aaron Tuller
Search for NSImage here:

https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/Multithreading/ThreadSafetySummary/ThreadSafetySummary.html#//apple_ref/doc/uid/1057i-CH12-126728

It sounds like you may need to do this serially, or try setting the cache mode 
to NSImageCacheNever right after you create the new NSImage to see if you can 
avoid hitting the shared cache.

Alternatively, you might want to look into using NSBitmapImageRep or 
CGImageSourceCreateThumbnailAtIndex.

-aaron


> On Aug 8, 2016, at 11:05 AM, Andrew Keller  wrote:
> 
> 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:
> 
> #00x7fff93d2951a in semaphore_wait_trap ()
> #10x7fff8b75ac5b in _os_semaphore_wait ()
> #20x000100074e12 in _dispatch_barrier_sync_f_slow ()
> #30x7fff89315182 in ___lldb_unnamed_function872$$RawCamera ()
> #40x7fff86a98e26 in ImageProviderCopyImageBlockSetCallback ()
> #50x7fff877d5279 in img_blocks_create ()
> #60x7fff87804598 in img_blocks_extent ()
> #70x7fff877c55b0 in img_data_lock ()
> #80x7fff877c25a6 in CGSImageDataLock ()
> #90x7fff8be62a02 in ripc_AcquireImage ()
> #10   0x7fff8be61525 in ripc_DrawImage ()
> #11   0x7fff877c21b0 in CGContextDrawImage ()
> #12   0x7fff8ee9627d in __75-[NSBitmapImageRep 
> _withoutChangingBackingPerformBlockUsingBackingCGImage:]_block_invoke ()
> #13   0x7fff8ee95969 in -[NSBitmapImageRep 
> _withoutChangingBackingPerformBlockUsingBackingCGImage:] ()
> #14   0x7fff8ee958f5 in __53-[NSBitmapImageRep 
> _performBlockUsingBackingCGImage:]_block_invoke ()
> #15   0x7fff8ee95859 in -[NSBitmapImageRep 
> _performBlockUsingBackingCGImage:] ()
> #16   0x7fff8ee957b4 in -[NSBitmapImageRep CGImage] ()
> #17   0x7fff8ee95719 in -[NSBitmapImageRep 
> CGImageForProposedRect:context:hints:] ()
> #18   0x7fff8ee4e6af in __74-[NSImageRep 
> drawInRect:fromRect:operation:fraction:respectFlipped:hints:]_block_invoke ()
> #19   0x7fff8ee4e195 in -[NSImageRep 
> drawInRect:fromRect:operation:fraction:respectFlipped:hints:] ()
> #20   0x7fff8f33e656 in __71-[NSImage 
> drawInRect:fromRect:operation:fraction:respectFlipped:hints:]_block_invoke1012
>  ()
> #21   0x7fff8edf4dc6 in -[NSImage 
> _usingBestRepresentationForRect:context:hints:body:] ()
> #22   0x7fff8ee4d9d7 in -[NSImage 
> drawInRect:fromRect:operation:fraction:respectFlipped:hints:] ()
> #23   0x00018aa5 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:
> 
> #00x7fff93d2951a in semaphore_wait_trap ()
> #10x00010006d2d5 in _dispatch_semaphore_wait_slow ()
> #20x7fff908f4da9 in xpc_connection_send_message_with_reply_sync ()
> #30x7fff90b1fe99 in _LSCopyApplicationInformation ()
> #40x7fff90b29a6c in _LSCopyApplicationInformationItem ()
> #50x7fff8ef8d08d in -[NSApplication _copyPublicPersistentUIInfo] ()
> #60x7fff8ef8b570 in recursivelyEncodeInvalidPersistentState ()
> #70x7fff8ef8a13a in -[NSPersistentUIManager 
> flushAllChangesOptionallyWaitingUntilDone:updatingSnapshots:] ()
> #80x7fff8ef89c6b in -[NSPersistentUIManager 
> flushPersistentStateAndClose:waitingUntilDone:] ()
> #90x7fff8ed5c466 in run_cocoa_block ()
> #10   0x7fff8ef89b8e in __42-[NSPersistentUIManager 
> acquireDirtyState]_block_invoke ()
> #11   0x000100062fc3 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 

NSImage drawInRect deadlock

2016-08-08 Thread Andrew Keller
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  0x7fff93d2951a in semaphore_wait_trap ()
#1  0x7fff8b75ac5b in _os_semaphore_wait ()
#2  0x000100074e12 in _dispatch_barrier_sync_f_slow ()
#3  0x7fff89315182 in ___lldb_unnamed_function872$$RawCamera ()
#4  0x7fff86a98e26 in ImageProviderCopyImageBlockSetCallback ()
#5  0x7fff877d5279 in img_blocks_create ()
#6  0x7fff87804598 in img_blocks_extent ()
#7  0x7fff877c55b0 in img_data_lock ()
#8  0x7fff877c25a6 in CGSImageDataLock ()
#9  0x7fff8be62a02 in ripc_AcquireImage ()
#10 0x7fff8be61525 in ripc_DrawImage ()
#11 0x7fff877c21b0 in CGContextDrawImage ()
#12 0x7fff8ee9627d in __75-[NSBitmapImageRep 
_withoutChangingBackingPerformBlockUsingBackingCGImage:]_block_invoke ()
#13 0x7fff8ee95969 in -[NSBitmapImageRep 
_withoutChangingBackingPerformBlockUsingBackingCGImage:] ()
#14 0x7fff8ee958f5 in __53-[NSBitmapImageRep 
_performBlockUsingBackingCGImage:]_block_invoke ()
#15 0x7fff8ee95859 in -[NSBitmapImageRep 
_performBlockUsingBackingCGImage:] ()
#16 0x7fff8ee957b4 in -[NSBitmapImageRep CGImage] ()
#17 0x7fff8ee95719 in -[NSBitmapImageRep 
CGImageForProposedRect:context:hints:] ()
#18 0x7fff8ee4e6af in __74-[NSImageRep 
drawInRect:fromRect:operation:fraction:respectFlipped:hints:]_block_invoke ()
#19 0x7fff8ee4e195 in -[NSImageRep 
drawInRect:fromRect:operation:fraction:respectFlipped:hints:] ()
#20 0x7fff8f33e656 in __71-[NSImage 
drawInRect:fromRect:operation:fraction:respectFlipped:hints:]_block_invoke1012 
()
#21 0x7fff8edf4dc6 in -[NSImage 
_usingBestRepresentationForRect:context:hints:body:] ()
#22 0x7fff8ee4d9d7 in -[NSImage 
drawInRect:fromRect:operation:fraction:respectFlipped:hints:] ()
#23 0x00018aa5 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  0x7fff93d2951a in semaphore_wait_trap ()
#1  0x00010006d2d5 in _dispatch_semaphore_wait_slow ()
#2  0x7fff908f4da9 in xpc_connection_send_message_with_reply_sync ()
#3  0x7fff90b1fe99 in _LSCopyApplicationInformation ()
#4  0x7fff90b29a6c in _LSCopyApplicationInformationItem ()
#5  0x7fff8ef8d08d in -[NSApplication _copyPublicPersistentUIInfo] ()
#6  0x7fff8ef8b570 in recursivelyEncodeInvalidPersistentState ()
#7  0x7fff8ef8a13a in -[NSPersistentUIManager 
flushAllChangesOptionallyWaitingUntilDone:updatingSnapshots:] ()
#8  0x7fff8ef89c6b in -[NSPersistentUIManager 
flushPersistentStateAndClose:waitingUntilDone:] ()
#9  0x7fff8ed5c466 in run_cocoa_block ()
#10 0x7fff8ef89b8e in __42-[NSPersistentUIManager 
acquireDirtyState]_block_invoke ()
#11 0x000100062fc3 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 (Cocoa-dev@lists.apple.com)

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:
https://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com

This email sent to arch...@mail-archive.com