Canceling previous call of dispatch_async - objective-c

I'm implementing a tracking mechanism of window A following the position of window B. Window B sending events of its position and window B reacts to those events by calling to setWindowProperties:
void setWindowProperties(bool topMost, bool visible,
CGWindowID parentWindow, CGWindowID aboveWindow,
NSRect windowFrame, NSRect viewFrame, bool isAbove)
{
dispatch_async(dispatch_get_main_queue(), ^{
setWindowPropertiesImpl(topMost, visible, parentWindow, aboveWindow, windowFrame, viewFrame, isAbove);
});
}
But, because of too much events sent by window B I'm getting a "snake tracing" effect. I want to get over it by reacting only to the last position event, meaning, canceling all previous call to :
dispatch_async(dispatch_get_main_queue(), ^{
setWindowPropertiesImpl(topMost, visible, parentWindow, aboveWindow, windowFrame, viewFrame, isAbove);
});
And as a result, leaving in the queue only the last position event - the only one that matters.
My question: Is there a way to cancel all previous calls for of dispatch_async?

Yes, dispatch tasks are now cancelable, but when there are events that are coming in more quickly than the main queue can process them, it's sometimes useful to use a dispatch source. Specifically a DISPATCH_SOURCE_TYPE_DATA_ADD data source.
// create source (and save this reference somewhere so it doesn't get released on you)
dispatch_source_t source = dispatch_source_create(DISPATCH_SOURCE_TYPE_DATA_ADD, 0, 0, dispatch_get_main_queue());
// specify what you want the event handler to do
dispatch_source_set_event_handler(source, ^{
// whatever you want to do
});
// start the dispatch source
dispatch_resume(source);
Then, when you want to trigger this, rather than doing dispatch_async, you would:
dispatch_source_merge_data(source, 1);
Clearly, this means that the event handler has to pull the data from the other window rather than pushing it, but hopefully this illustrates the basic idea.
For more information see WWDC 2012 video Asynchronous Design Patterns with Blocks, GCD, and XPC. Specifically, see design pattern 8, "Update State Asynchronously" in the latter part of the video.

You cannot cancel an operation enqueued on a dispatch queue.
GCD queues have no way of cancelling a block once it scheduled. The architecture is very much "fire and forget".
Instead of GCD you could use NSOperationQueue that can also async executes, then you can cancel.

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.

GCD timer never fires

I have a bare single-view iOS app with the following in the -viewDidLoad of the view:
dispatch_queue_t q_default;
q_default = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, q_default); //run event handler on the default global queue
dispatch_time_t now = dispatch_walltime(DISPATCH_TIME_NOW, 0);
dispatch_source_set_timer(timer, now, 30ull*NSEC_PER_SEC, 5000ull);
dispatch_source_set_event_handler(timer, ^{
printf("test\n");
});
dispatch_resume(timer);
This is taken directly from the docs (except for a simplified printf() argument). The block is never executed--can someone tell me why??
Additional Information
I was trying this in a larger app to no avail. I then backed out to the barebones app, tried with ARC both on and off, tried this code in -appDidFinishLaunching..., all with no luck. I can surround this code with NSLogs, both of which are printed. I've checked timer--it is not nil.
So, the problem was that I'd lost my reference to timer when the surrounding scope was destroyed. Changing timer to an ivar instead of an automatic variable fixed things...
Per the documentation of dispatch_source_create:
Dispatch sources are created in a suspended state. After creating the
source and setting any desired attributes (for example, the handler or
the context), your application must call dispatch_resume to begin
event delivery.
So your timer never fires because it's suspended. To end its suspension you need to call dispatch_resume.

UI does not update when main thread is blocked in Cocoa app

I am using a NSProgressIndicator in my main thread to update on progress as I run through my entire method. Now when I end up calling an object from a different class file, and wait for that object to return to a value to my main thread, I notice that the NSProgressIndicator will disappear. I understand that this is because the main thread is blocked until I get the return value from the other object.
So my questions is what is the recommended way for updating UI in the main thread without blocking it and having other objects run in the background and return values to the main thread as needed. I know how to use blocks but blockoperations are not allowed to return values.
What I need is something that helps this pseudo code:
-(IBAction) main {
//Update progress indicator UI to show progress
//perform an call to another object from another class.
// wait till i get its return value.
//Update progress indicator UI to show progress
// Use this return value to do something.
//Update progress indicator UI to show progress
}
When the call to the other object is made, I notice that the determinate NSProgressIndicator I have completely disappears since the main thread is blocked. Thanks.
Your above code is not the correct approach. Since main never returns, the progress indicator will never update. You must return quickly on the main thread.
Instead, what you want to do is set up a background block that at various points updates the progress indicator on the main thread. So, for instance:
- (IBAction)start:(id)sender {
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(queue, ^{
dispatch_async(dispatch_get_main_queue(), ^{[self.progress setProgress:0];});
// Doing some stuff
dispatch_async(dispatch_get_main_queue(), ^{[self.progress setProgress:.25];});
// Doing more stuff
dispatch_async(dispatch_get_main_queue(), ^{[self.progress setProgress:.75];});
});
}
(Yes, this causes the queue to retain self, but that's ok here because self is not retaining the queue.)
You can achieve what you are looking for with GCD (Grand Central Dispatch).
Here is an example to get you started:
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0ul);
dispatch_async(queue, ^{
// Perform async operation
dispatch_sync(dispatch_get_main_queue(), ^{
// Update UI
});
});
It sounds like your operation should be run in a separate thread which can be done several ways but is probably most easily achieved using NSOperationQueue and either custom NSOperation classes (it's easier than it sounds to set these up) or use of the NSInvokeOperation class.
Then you can send messages back to your class in the main thread using the NSNotificationCenter or set up as an observer using Key-Value Observing (KVO).
Bottom line, you have a variety of choices and to make the best one should have an understanding of the underlying technologies. I'd start with Apple's Threaded Programming Guide personally, then read it a second time to be sure you extracted all the goodness before building out your solution.

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.

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.