NSBlockOperation and the start method - iphone

In reviewing my code, I've been seeing that in many places I have been making the assumption that calling [NSBlockOperationInstance start]; will start this operation on the main thread. I don't know why I thought this, but I shouldn't have been so sure any way. I checked the documentation but couldn't find any explicit mention of the thread the block would run on. However, asserting assert([NSThread isMainThread]); in the main body of the block does pass every time using start, so I'm not sure if this is a coincidence. Any one have more solid understanding of how this would work?
I forgot to mention that [op start] is being called on the main thread.

OK, it all depends on where you call start(). While NSBlockOperation will farm out blocks to other threads, start() is synchronous, and will not return until all the blocks that have been given to NSBlockOperation have completed.
While NSBlockOperation will concurrently execute the blocks it is given, NSBlockOperation itself is NOT concurrent (i.e., isConcurrent is false). Thus, according to the documentation, start() will execute in its entirety in the thread of the caller to start().
Since the thread that calls start() will not return until all the blocks have executed, it makes sense to let the calling thread be involved in the thread pool that is executing the concurrent blocks. That is why you will see some blocks executing in the thread that called start().
If you are seeing a block execute in the main thread, then you must have called it from the main thread.
On a related note, if your NSBlockOperation contains a single block, than that block will always execute in the calling thread.
Remember, if you want a NSOperation to be fully concurrent, you must implement the appropriate functionality in a subclass.
Barring that, you can give any NSOperation to a NSOperationQueue, and it will execute concurrently, because the NSOperation is given to a queue, and the thread running the operation calls start().
Personally, I do not see any advantage in using NSBlockOperation over dispatch_async() unless I need to use its features. If you are only executing one block, just call
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ });
If you want to utilize the features of NSBlockOperation, but you do not want to wait for them to complete in the current calling thread, it still makes sense to do this...
// Add lots of concurrent blocks
[op addExecutionBlock:^{ /*whatever*/ }];
// Execute the blocks asynchronously
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[op start];
// Now do what you want after all the concurrent blocks have completed...
// Maybe even tell the UI
dispatch_async(dispatch_get_main_queue(), ^{
// Update the UI now that all my concurrent blocks have finished.
});
});
EDIT
To address your comment to tc's answer...
If you call
op = [NSBlockOperation blockOperationWithBlock:^{assert([NSThread isMainThread])}];
[op start];
from the main thread, then there are some guarantees, and some high probabilities.
First, you are guaranteed that [op start] will run to completion in the calling thread. That's because NSBlockOperation does not override the default behavior of NSOperation that specifies it is NOT a concurrent operation.
Next, you have a very high probability that if the NSBlockOperation only has one block, that it will run in the calling thread. You have almost the same probability that the first block will run in the calling thread.
However, the above "probabilities" are not guarantees (only because the documentation does not say it). I guess, some engineer may find some reason to spin that single block to one of the concurrent queues, and just have the calling thread join on the operation that is executing in another thread... but I highly doubt that.
Anyway, maybe your confusion comes from the fact that the documentation for NSBlockOperation says it executes block concurrently, which it does. However, the operation itself is not concurrent, so the initial operation is synchronous. It will wait for all blocks to execute, and it may (or may not) execute some of them on the calling thread.
While there is no guarantee, I find it highly unlikely that a NSBlockOperation with only one block will do anything other than execute on the calling thread.

The docs specifically say
Blocks added to a block operation are dispatched with default priority to an appropriate work queue. The blocks themselves should not make any assumptions about the configuration of their execution environment.
I suspect that the following will crash:
NSBlockOperation * op = [NSBlockOperation blockOperationWithBlock:^{ sleep(1); }];
[op addExecutionBlock:^{assert([NSThread isMainThread]); }];
[op start];
What's wrong with simply executing the block?

Related

Deadlock with dispatch_sync

{
dispatch_queue_t myQueue = dispatch_queue_create("com.mycompany.myqueue", 0);
dispatch_sync(myQueue, ^{
//Do EXTREME PROCESSING!!!
for (int i = 0; i< 100; i++) {
[NSThread sleepForTimeInterval:.05];
NSLog(#"%i", i);
}
dispatch_sync(dispatch_get_main_queue(), ^{
[self updateLabelWhenBackgroundDone];
});
});
}
I am getting a deadlock here. According to Apple documentation
"dispatch_sync": "Submits a block to a dispatch queue for synchronous
execution. Unlike dispatch_async, this function does not return until
the block has finished. Calling this function and targeting the
current queue results in deadlock.".
However, I do the outer dispatch_sync on myQueue and then I do inner ditpatch_sync on a different queue which is `main_queue.
Can not find out the reason for the deadlock. Any comments/help are appreciated here.
If you dispatch_sync to myQueue like that and the call happens on the main thread, then dispatch_sync will, if possible, execute the block right there and not on a new worker thread like dispatch_async would. You're not guaranteed to get a separate worker thread for your queue.
The block then runs on the main thread until it hits your second dispatch_sync call, which happens to target the main queue. That queue can't be serviced, since there's already a block running on it, and that's where you end up in a deadlock.
If that's your problem, i.e. the first dispatch_sync is indeed coming from the main thread, then you should switch to dispatch_async. You wouldn't want to block the main thread with the long-running "EXTREME PROCESSING" operation.
You are calling dispatch_sync twice. The first time suspends the main thread waiting for your block to complete. The block then suspends the background thread with the second call which tries to push back to the main thread (which will never process the block from its queue because it's suspended). Both threads are now waiting for each other.
At least one of the calls needs to be dispatch_async.
I had similar problems and none of these solutions worked. I asked someone smarter than me.
My problem was I was spawning a dispatching an async worker block, and then displaying a progress window. Calls back into the main thread via
dispatch_sync(dispatch_get_main_queue(), ^{})
failed as did async calls.
The explanation was that the main thread was no longer in 'commons mode' because of the modal window. I replaced my calls to the main thread with this....
CFRunLoopPerformBlock(([[NSRunLoop mainRunLoop] getCFRunLoop]), (__bridge CFStringRef)NSModalPanelRunLoopMode, ^{
//Update UI thread.
});

iOS -- persuade an NSOperation to yield to other threads before starting a long and apparently unbreakable task?

I have a low-priority PDF-drawing NSOperation. I am using the function CGContextDrawPDFPage to draw the page.
As best I can tell, the app will not switch to another thread while the CGContextDrawPDFPage function is executing. I suppose I need to break up my graphics context into several smaller contexts to get around that. But I still have one problem -- how can I persuade the operation to allow other threads to perform their tasks before starting that draw? The way things are set up right now, the app draws the page right away, even though the thread priority is set to zero.
Here is the code that adds the NSOperation to the queue:
MyNSOperationSubclass* op = [[MyNSOperationSubclass alloc]initWithStuff: stuff];
[op setThreadPriority:0.];
[self.drawingQueue addOperation:op];
Here is some code within MyNSOperationSubclass:
-(void) drawStuff {
NSLog(#"drawStuff");
#autoreleasepool {
// some code to setup the graphics context and PDF page here
CGContextDrawPDFPage(context, page);
[self finish]; // finishes the NSOperation
}
}
-(void) main {
NSLog(#"main");
#autoreleasepool {
[self drawStuff];
}
}
-(void) start {
self.isExecuting = YES;
[NSThread detachNewThreadSelector:#selector(main) toTarget:self withObject:nil];
}
what happens is that even though the thread priority is set to zero, the setup code is so quick that the NSOperation gets into the CGContextDrawPDFPage call right as the operation is started. But once that call has started, the thread won't yield to other threads until it has finished. So the drawing operation happens right away, which is the opposite of what I want to do.
EDIT: After taking a closer look, the first answerer is correct that the CGContextDrawPDFPage call does not always prevent a thread from yielding. But even with a non-concurrent operation, I still have the problem that it finishes quickly, rather than waiting around for other, higher-priority stuff to happen.
EDIT 2: Apparently I don't understand how thread priorities work. After changing it to a nonconcurrent operation, the thread priority of the operation thread is 0.5, even though I called [op setThreadPriority: 0] before adding it to the queue.
So, the first thought is that your thread can't avoid "yielding" to other threads. Context switching is pre-emptive here. So you might want to talk about why you think this thread is not yielding. You may have some other bigger issue here with the other work that you are expecting to be done.
Second, when you set the thread priority for this operation, you're not setting the priority of this new thread that you are creating yourself here:
[NSThread detachNewThreadSelector:#selector(main) toTarget:self withObject:nil];
That's a new thread with normal priority.
But stepping back from that a second, why not just make this a non-concurrent operation and let your NSOperationQueue manage the thread for you? The queue will create a new thread for you as needed (and apply your thread priority that you asked for).
Alternatively, unless you still need to support iOS3, you can just use the global low priority GCD queue for this work (or target your own GCD queue to that global queue if you need finer control over order and still want all of this done with low priority).
So, consider letting either NSOperationQueue or GCD manage some of this for you. And think about why you think this thread is interfering with other work. That's probably some bigger issue.
Hope that helps.

Dispatch Queues with Multiple Methods ? iPhone

I am trying to learn more about dispatch queues. If I put three methods in a dispatch queue as in the code below, do they execute one after the other or all at once ?
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(queue, ^ {
[activeModel release];
[mainViewController showSceneList];
[mainViewController removeTidyUpScreen];
});
How would I specify that the next should not run until the previous one is completed ?
Think of a block -- the code you submit to a dispatch queue as you have here -- as an anonymous function. So, the code you have in your block here executes in order just as if you were calling a function that contained the same calls, one method, then the next, and so on.
In your particular example, it looks like you may be doing some operations with the UI on a queue that is not the main queue. You MUST do UI operations on the main queue, because it has access to the UI. You might use dispatch_get_main_queue() instead, to be sure you're getting that queue. If you have something you want to run in the background that will not touch the UI, then using a global queue is fine, and preferred especially if not stalling the UI is important.

Grand Central Dispatch (GCD) vs. performSelector - need a better explanation

I've used both GCD and performSelectorOnMainThread:waitUntilDone in my apps, and tend to think of them as interchangeable--that is, performSelectorOnMainThread:waitUntilDone is an Obj-C wrapper to the GCD C syntax. I've been thinking of these two commands as equivalent:
dispatch_sync(dispatch_get_main_queue(), ^{ [self doit:YES]; });
[self performSelectorOnMainThread:#selector(doit:) withObject:YES waitUntilDone:YES];
Am I incorrect? That is, is there a difference of the performSelector* commands versus the GCD ones? I've read a lot of documentation on them, but have yet to see a definitive answer.
As Jacob points out, while they may appear the same, they are different things. In fact, there's a significant difference in the way that they handle sending actions to the main thread if you're already running on the main thread.
I ran into this recently, where I had a common method that sometimes was run from something on the main thread, sometimes not. In order to protect certain UI updates, I had been using -performSelectorOnMainThread: for them with no problems.
When I switched over to using dispatch_sync on the main queue, the application would deadlock whenever this method was run on the main queue. Reading the documentation on dispatch_sync, we see:
Calling this function and targeting
the current queue results in deadlock.
where for -performSelectorOnMainThread: we see
wait
A Boolean that specifies whether the
current thread blocks until after the
specified selector is performed on the
receiver on the main thread. Specify
YES to block this thread; otherwise,
specify NO to have this method return
immediately.
If the current thread is also the main
thread, and you specify YES for this
parameter, the message is delivered
and processed immediately.
I still prefer the elegance of GCD, the better compile-time checking it provides, and its greater flexibility regarding arguments, etc., so I made this little helper function to prevent deadlocks:
void runOnMainQueueWithoutDeadlocking(void (^block)(void))
{
if ([NSThread isMainThread])
{
block();
}
else
{
dispatch_sync(dispatch_get_main_queue(), block);
}
}
Update: In response to Dave Dribin pointing out the caveats section ondispatch_get_current_queue(), I've changed to using [NSThread isMainThread] in the above code.
I then use
runOnMainQueueWithoutDeadlocking(^{
//Do stuff
});
to perform the actions I need to secure on the main thread, without worrying about what thread the original method was executed on.
performSelectorOnMainThread: does not use GCD to send messages to objects on the main thread.
Here's how the documentation says the method is implemented:
- (void) performSelectorOnMainThread:(SEL) selector withObject:(id) obj waitUntilDone:(BOOL) wait {
[[NSRunLoop mainRunLoop] performSelector:selector target:self withObject:obj order:1 modes: NSRunLoopCommonModes];
}
And on performSelector:target:withObject:order:modes:, the documentation states:
This method sets up a timer to perform the aSelector message on the current thread’s run loop at the start of the next run loop iteration. The timer is configured to run in the modes specified by the modes parameter. When the timer fires, the thread attempts to dequeue the message from the run loop and perform the selector. It succeeds if the run loop is running and in one of the specified modes; otherwise, the timer waits until the run loop is in one of those modes.
GCD's way is suppose to be more efficient and easier to handle and is only available in iOS4 onwards whereas performSelector is supported in the older and newer iOS.

Main thread hangs indefinitely while waiting for NSOperationQueue operations to cancel [Only on Device!]

I have an NSOperationQueue on my main thread running a set of NSOperations (max concurrent set to 1) that I want to be able to cancel at any time. When I press a button I tell the queue to cancel all operations and wait until finished. This should hang the main thread until the operation queue is empty, however it is hanging my main thread indefinitely.
Here's the code I use to stop it:
...
[myQueue cancelAllOperations];
[myQueue waitUntilAllOperationsAreFinished];
return YES; // This line never gets called
Note: I need to use waitUntilAllOperationsAreFinished as further processes require that the queue be empty.
The strange thing is this is only occurring on the device. When running in the simulator it works as expected.
I have watched breakpoints and I can follow the currently running operation until it finishes. It detects [self isCancelled], stops what it's doing and zips through to the end of the main method. I can see that nothing in the operation is causing it to hang, and by cancelling all operations, none of the other operations should start, and the queue should finish. I have checked by adding breakpoints and none of the other operations start.
Why is this happening?
In any of your operations (or in any other thread), are you using -performSelectorOnMainThread:withObject:waitUntilDone:? -waitUntilAllOperationsAreFinished will block whatever thread it's called on until all operations are complete. Odds are, if you're calling this as the application is terminating, that thread would be the main thread. If the main thread is blocked, and one of the operations uses -performSelectorOnMainThread:withObject:waitUntilDone:, your application will freeze because the operation will never complete.
I've had this exact thing happen to me before. Unfortunately, it's pretty difficult to work around.
You should never, ever block the main thread. It handles all your UI updates for one thing, for another as you have noted you managed to create a deadlock.
Instead, try creating a method like:
- (void) notifyOnFinish
{
[myQueue waitUntilAllOperationsAreFinished];
[self performSelectorOnMainThread:(queueEmpty) withObject:nil waitUntilDone:NO];
}
Then where you have your code now, call:
[myQueue cancelAllOperations];
[self performSelectorInBackground:#selector(notifyOnFinish) withObject:nil];
And in a queueEmpty method you just do whatever you want to do when the queue is emptied.
Basically, just create a background thread to block instead of the main thread.
Perhaps the waitUntilAllOperationsArefFinished is causing the block... Maybe the operations all cancel and finish before the call to waitUntilAllOperationsArefFinished and then the queue is sat hanging, waiting for operations that are already finished to finish...?
I don't know this for a fact, but maybe try not calling waitUntilAllOperationsArefFinished.