How to not crash your app on quit when using concurrency - objective-c

I'm using NSOperationQueue and a subclass of NSOperation for a part in my app that is generating a lot of data and therefore is very calculation-heavy.
When the app is closed by the user processingQueue.cancelAllOperations() is called. Also in my NSOperation subclass I overwrote cancel() to let it forward a cancel request to the class that does the actual heavy lifting ...
override func cancel() {
AppDelegate.instance.cancelDataGeneration()
super.cancel()
}
But this is still not enough. When I close the app while the data generation is ongoing it will crash in Xcode.
What can I do to prevent the crashing (which might result in data loss)? Is it OK to let the app wait for closing until all concurrent operations are canceled and how is this done (if it's even possible)? Or what other methods are generally used to address this issue?
UPDATE:
After more investigation I found that cancel() on my NSOperation subclass is never called, even after calling processingQueue.cancelAllOperations() in applicationShouldTerminate. So I added a method to manually call cancel on it:
func cancelDataGeneration() {
if let op = AppDelegate.instance._operation {
op.cancel();
}
}
And I call this from inside applicationShouldTerminate (since applicationShouldTerminate is called earlier than applicationWillTerminate. Interestingly, since my AppDelegate is a Singleton I have to use AppDelegate.instance._operation. If I only check for _operation it results in being nil when called from applicationShouldTerminate. Would be interesting to know why this is the case.
In any case, canceling now works properly: When the app is quit, it will cancel the data generation class and exits without crashing ... mostly anyway. But I still wonder why my NSOperation subclass' cancel() isn't called when I use processingQueue.cancelAllOperations()!

From Apple's documentation.
Canceling the operations does not automatically remove them from the queue or stop those that are currently executing. For operations that are queued and waiting execution, the queue must still attempt to execute the operation before recognizing that it is canceled and moving it to the finished state.
I would block the App's mainthread until the NSOperationQueue finishes all of its work.
I would call [NSOperationQueue cancelAllOperations] first.
Then in the 'Application will terminate' method I will call
[NSOperationQueue waitUntilAllOperationsAreFinished]. This will make sure the currently executing block(all the other queued tasks will be cancelled) will complete before the application quits.
Now if you are not comfortable with the main thread blocking until the currently executing block finishes, then you need to check for a flag(or an NSApplicationDelegate could be set on that class) which signals if the application is still active in order to continue. If application is to be terminated, then fall out of the block voluntarily, this is the cleanest way to do it.
Something roughly like the below.
void ^(aBlock)(void) = ^void(void)
{
for(NSUInteger i = 0;i < 1000; i++)
{
// heavy processing code. Taking several iterations each second
// At the start of each iteration, check for a flag, to see if to quit
if(_applicationIsShuttingDown) break;
// perform block operation
}
};
and your class is an NSApplicationDelegate and implements
-applicationWillTerminate:(NSNotification *)aNotification
{
_applicationIsShuttingDown = YES;
}

Related

ObjectiveC - displaying window through distributed objects

I try to display window in other process using Distributed objects.
Process A invoke remotely through Distributed Object method from process B which display dialog. Something wrong happens if I try to wait for results.
The method looks like that:
-(BOOL)showWindow //method invoked through distributed objects
{
dispatch_semaphore_t sem = dispatch_semaphore_create(0);
[object showDialog:^(BOOL result){ //this methods creates and display window
NSLog(#"Block called");
dispatch_semaphor_signal(sem);
}];
dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
return YES;
}
The function showWindow never ends. If I comment dispatch_semaphore_wait "Block called" is displayed and window is show.
I checked different variant synchronization, I tried to run this code using dispatch_sync or async but nothing helped.
I will be gratefull for help.
Kon
The problem is that DO requires that the run loop runs in order to process communication between the two sides. Basically, the code you showed sends a request to the remote side, the one which hosts the real object that object is a proxy for. The request carries a reference to your local block object. The remote side effectively gets a proxy for that.
Some time later, the remote side invokes its block proxy. That causes a request to be sent back to this process and thread. However, this thread can't receive that request because it is blocked in dispatch_semaphore_wait(). It is not attending to any communication channels.
So, both sides deadlock. The local side waits forever for a semaphore that will never be signaled and the remote side waits forever for its request to run the block to complete.
Thought of another way, the semaphore is not signaled by the remote process. You've asked for it to be signaled by this same thread which is waiting on it. It's just that, if it were possible, this thread would do so in response to an event sent by another process. Having a thread be responsible for signaling the semaphore that it's waiting on is a pretty clear, immediate self-deadlock.
Can you change -showWindow to not be synchronous? Why is it using a semaphore and waiting? Why can't it just fire off the request and then return back to the calling code, which should not assume the dialog has completed and instead return all the way back to the main event loop? Whatever work should happen "next" should be put into the completion block and will just be invoked asynchronously when the dialog completes.
If you really, really need this method to be synchronous, then you will have to use the run loop to wait instead of a dispatch semaphore. Something like:
-(BOOL)showWindow //method invoked through distributed objects
{
__block BOOL done = NO;
[object showDialog:^(BOOL result){ //this methods creates and display window
NSLog(#"Block called");
done = YES;
}];
NSString* mode = #"com.yourcompany.yourapp.privatemode";
NSConnection* conn = [(NSDistantObject*)object connectionForProxy];
[conn addRequestMode:mode];
while (!done)
[[NSRunLoop currentRunLoop] runMode:mode beforeDate:[NSDate distantFuture]];
[conn removeRequestMode:mode];
return YES;
}
Probably, you should actually add the private mode to the connection when it's first created and leave it there for its lifetime. You don't want to use any of the predefined modes for this because then unexpected things may fire while you're waiting and your code becomes unexpectedly re-entrant, etc.

dispatch_async doesn't fire until returned parent thread returns?

My Question is - Does dispatch_async fire immediately? I'm seeing that the creating thread has to return before dispatch_async fires.
My issue is that I am trying to make a async call to a 3rd party library appear to be synchronous. I know, I know... I should allow these async operations run as intended but I'm trying to do this way down in a complex multi-threaded operation and I really don't have a choice.
here's what starts the async call
-(void)connect
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0) , ^{
[device connect];
});
while(!connectCompleted)
{
NSLog(#"Sleeping..");
[NSThread sleepForTimeInterval:1.0];
}
}
and here's the delegate function that gets called AFTER the [device connect] successfully connects:
- (void)didConnect:(BXPrinterController *)controller
printer:(BXPrinter *)printer
{
connectCompleted = YES;
NSLog(#"didConnect");
}
with that dispatch_async wrapped around [device connect] the delegate never gets fired;
Does dispatch_async fire immediately
Of course not! That is what async means! The whole basis of the concept "asynchronous" (which is what async stands for) is that this is code that runs at some future unspecified time. Meanwhile the code that calls dispatch_async continues on to its end without waiting.
If for whatever reason you must make a call appear synchronous, I'd recommend that you first implement it synchronously (because you'll end up doing that anyway if people had enough of delays), and then you write a helper method that is synchronous:
Step 1: Create a dispatch_semaphore_t.
Step 2: Start the asynchronous code, with every code path ending by sending a signal to the dispatch_semaphore_t.
Step 3: Send a wait to the dispatch_semaphore_t. It wakes up, with no CPU time spent on it at all, just when the asynchronous code finishes.

Protecting critical code from being called again

I need to protect a critical area of my code, which is multi-threaded. I want to prevent it from being called multiple times before the other thread is finished. This is what I am working with:
- (void) filterAllEventsIntoDictionary{
// start critical area
if (self.sortedKeys.count != 0) {
[self.sortedKeys removeAllObjects];
}
dispatch_async(self.filterMainQueue, ^{
[self internal_filterAllEventsIntoDictionary];
dispatch_sync(dispatch_get_main_queue(), ^{
[self.tableView reloadData];
});
});
}
Since the internal_filterAllEventsIntoDictionary method also accesses self.sortedKeys, if this code is called twice, it crashes because of removeAllObjects at the start.
I still need to call the internal... method in another thread since I don't want to block the UI. So what's the best way to block on the start of this method while the dispatch_async call is still not finished?
While I am far from being a concurrency expert, it sounds to me like you need a lock on your sortedKeys object. If you used a traditional lock, though, you'd end up blocking the main thread.
The recommended replacement for locks in the world of Grand Central Dispatch is to put critical sections of code on a serial queue. See "Eliminating Lock-Based Code" in the Concurrency Programming Guide.
If you put the [self.sortedKeys removeAllObjects]; call onto the same queue that the block with the internal... call is scheduled on, you guarantee that it won't happen until after that block completes:
// start critical area
dispatch_async(self.filterMainQueue, ^{
if (self.sortedKeys.count != 0) {
[self.sortedKeys removeAllObjects];
}
});
This assumes that filterMainQueue is serial. Using dispatch_async for the critical section ensures that the main thread will not be blocked. Also note the warning in "Dispatch Queues and Thread Safety":
Do not call the dispatch_sync function from a task that is executing on the same queue that you pass to your function call. Doing so will deadlock the queue.
Although this will only be an issue if the internal... method does something that causes this method to be called again.

dispatch_sync on main queue hangs in unit test

I was having some trouble unit testing some grand central dispatch code with the built in Xcode unit testing framework, SenTestingKit. I managed to boil my problem done to this. I have a unit test that builds a block and tries to execute it on the main thread. However, the block is never actually executed, so the test hangs because it's a synchronous dispatch.
- (void)testSample {
dispatch_sync(dispatch_get_main_queue(), ^(void) {
NSLog(#"on main thread!");
});
STFail(#"FAIL!");
}
What is it about the testing environment that causes this to hang?
dispatch_sync runs a block on a given queue and waits for it to complete. In this case, the queue is the main dispatch queue. The main queue runs all its operations on the main thread, in FIFO (first-in-first-out) order. That means that whenever you call dispatch_sync, your new block will be put at the end of the line, and won't run until everything else before it in the queue is done.
The problem here is that the block you just enqueued is at the end of the line waiting to run on the main thread—while the testSample method is currently running on the main thread. The block at the end of the queue can't get access to the main thread until the current method (itself) finishes using the main thread. However dispatch_sync means Submits a block object for execution on a dispatch queue and waits until that block completes.
The problem in your code is that no matter whether you use dispatch_sync or dispatch_async , STFail() will always be called, causing your test to fail.
More importantly, as BJ Homer's explained, if you need to run something synchronously in the main queue, you must make sure you are not in the main queue or a dead-lock will happen. If you are in the main queue you can simply run the block as a regular function.
Hope this helps:
- (void)testSample {
__block BOOL didRunBlock = NO;
void (^yourBlock)(void) = ^(void) {
NSLog(#"on main queue!");
// Probably you want to do more checks here...
didRunBlock = YES;
};
// 2012/12/05 Note: dispatch_get_current_queue() function has been
// deprecated starting in iOS6 and OSX10.8. Docs clearly state they
// should be used only for debugging/testing. Luckily this is our case :)
dispatch_queue_t currentQueue = dispatch_get_current_queue();
dispatch_queue_t mainQueue = dispatch_get_main_queue();
if (currentQueue == mainQueue) {
blockInTheMainThread();
} else {
dispatch_sync(mainQueue, yourBlock);
}
STAssertEquals(YES, didRunBlock, #"FAIL!");
}
If you are on the main queue and synchronously wait for the main queue to be available you will indeed wait a long time. You should test to make sure you are not already on the main thread.
Will you ever get out of house if you must wait for yourself to get out house first? You guessed right! No! :]
Basically if:
You are on FooQueue. (doesn't have to be main_queue)
You call the method using sync ie in a serial way and want to execute on FooQueue.
It will never happen for same reason that you will never get out of house!
It won't ever get dispatched because it's waiting for itself to get off the queue!
To follow up, since
dispatch_get_current_queue()
is now deprecated, you can use
[NSThread isMainThread]
to see if you are on the main thread.
So, using the other answer above, you could do:
- (void)testSample
{
BOOL __block didRunBlock = NO;
void (^yourBlock)(void) = ^(void) {
NSLog(#"on main queue!");
didRunBlock = YES;
};
if ([NSThread isMainThread])
yourBlock();
else
dispatch_sync(dispatch_get_main_queue(), yourBlock);
STAssertEquals(YES, didRunBlock, #"FAIL!");
}

How to update UI in a task completion block?

In my application, I let a progress indicator starts animation before I send a HTTP request.
The completion handler is defined in a block. After I get the response data, I hide the progress indicator from inside the block. My question is, as I know, UI updates must be performed in the main thread. How can I make sure it?
If I define a method in the window controller which updates UI, and let the block calls the method instead of updating UI directly, is it a solution?
Also, if your app targets iOS >= 4 you can use Grand Central Dispatch:
dispatch_async(dispatch_get_main_queue(), ^{
// This block will be executed asynchronously on the main thread.
});
This is useful when your custom logic cannot easily be expressed with the single selector and object arguments that the performSelect… methods take.
To execute a block synchronously, use dispatch_sync() – but make sure you’re not currently executing on the main queue or GCD will deadlock.
__block NSInteger alertResult; // The __block modifier makes alertResult writable
// from a referencing block.
void (^ getResponse)() = ^{
NSAlert *alert = …;
alertResult = [NSAlert runModal];
};
if ([NSThread isMainThread]) {
// We're currently executing on the main thread.
// We can execute the block directly.
getResponse();
} else {
dispatch_sync(dispatch_get_main_queue(), getResponse);
}
// Check the user response.
if (alertResult == …) {
…
}
You probably misunderstood something. Using blocks doesn't mean that your code is running in a background thread. There are many plugins that work asynchronously (in another thread) and use blocks.
There are a few options to solve your problem.
You can check if your code is running in the main thread my using [NSThread isMainThread]. That helps you to make sure that you're not in the background.
You can also perform actions in the main or background by using performSelectorInMainThread:SEL or performSelectorInBackground:SEL.
The app immediately crashes when you're trying to call the UI from a bakcground thread so it's quite easy to find a bug.