dispatch_semaphore_signal never being executed because of the wait - ios

I am trying to take two pictures and want them to run in order. The method for capturing pictures is done using a method that runs asynchronously. I currently use semaphores to try to synchronize this, but it stops the program from running. I believe this is because the completionHandler is run on the same thread, and since the thread is locked, it can't execute to free the semaphore.
Here is my current code:
camera_sema = dispatch_semaphore_create(0);
[photoOutput captureStillImageAsynchronouslyFromConnection:videoConnection completionHandler:^(CMSampleBufferRef imageSampleBuffer, NSError *error)
{
[flash turnOffFlash];//turning off flash for second picture
//perform needed work
dispatch_semaphore_signal(camera_sema);
}];
dispatch_semaphore_wait(camera_sema, DISPATCH_TIME_FOREVER);
 Is there a way I can use dispatch_async for this case or a way to run the completionHandler on another background thread so it is able to execute?
UPDATE 1:
This works but not sure if its the best way to do it.
-(void*)method: (int) iteration {
[photoOutput captureStillImageAsynchronouslyFromConnection:videoConnection completionHandler:^(CMSampleBufferRef imageSampleBuffer, NSError *error)
{
[flash turnOffFlash];//turning off flash for second picture
//perform needed work
//base case
[method: iteration-1];
}];
}
So basically I call the method recursively to ensure that it only repeats once the callback has been reached. It will essentially run an async task synchronously. Unfortunately, for the final design, I need to be able to run it infinitely. Doing this recursively will fail. Please let me know if there is better way to do this.

Related

AWS SDK - Implementing a network queue for CloudFront downloads

I'm currently working on an iOS project that utilises the AWS SDK to download large media files to the device. I am using CloudFront to distribute the content and the downloads are working well, however I am having problems implementing a network queue for these operations. No matter what I try, all the files want to download at once.
I am using the AWSContent downloadWithDownloadType: method to initiate and monitor progress on the actual downloads.
I have tried using an NSOperationQueue and setting setMaxConcurrentOperationCount, and all the code blocks execute at once. :(
I have a feeling it might be configurable with AWSServiceConfiguration in the AppDelegate, but the documentation is extremely vague on what variables you can pass into that object... http://docs.aws.amazon.com/AWSiOSSDK/latest/Classes/AWSServiceConfiguration.html
Has anyone had any experience with this?
TIA
Your problem is most likely that you misunderstand an approach of asynchronous operations.
I have tried using an NSOperationQueue and setting
setMaxConcurrentOperationCount, and all the code blocks execute at
once. :(
It's difficult to say what's definitely wrong without seeing an actual code, however most likely it's tied to the following steps:
You create NSOperationQueue
You set maxConcurrentOperationsCount to 2 for example
You add 4 blocks to it with AWSContent downloadWithDownloadType:
You expect no more 2 downloads to be run simultaneously
What do you probably do wrong
The key is inside point 3. What exactly the block does? My guess is that it completes before actual download completes. So if you have something like:
NSOperationQueue *queue = [NSOperationQueue new];
queue.maxConcurrentOperationsCount = 2;
for (AWSContent *content in contentArray) { // Assume you already do have this array
[queue addOperationWithBlock:^() {
[content downloadWithDownloadType:AWSContentDownloadTypeIfNotCached
pinOnCompletion:YES
progressBlock:nil
completionHandler:^(AWSContent *content, NSData *data, NSError *error) {
// do some stuff here on completion
}];
}];
}
Your block exits before your download is finished, allowing next blocks to run on queue and starting further downloads.
What to try
You should simply add some synchronization mechanism to your block to let operation complete only on completion block. Say:
NSOperationQueue *queue = [NSOperationQueue new];
queue.maxConcurrentOperationsCount = 2;
for (AWSContent *content in contentArray) { // Assume you already do have this array
[queue addOperationWithBlock:^() {
dispatch_semaphore_t dsema = dispatch_semaphore_create(0);
[content downloadWithDownloadType:AWSContentDownloadTypeIfNotCached
pinOnCompletion:YES
progressBlock:nil
completionHandler:^(AWSContent *content, NSData *data, NSError *error) {
// do some stuff here on completion
// ...
dispatch_semaphore_signal(dsema); // it's important to call this function in both error and success cases of download to free the block from queue
}];
dispatch_semaphore_wait(dsema, DISPATCH_TIME_FOREVER); // or another dispatch_time if you want your custom timeout instead of AWS
}];
}
Effectively your answer is https://stackoverflow.com/a/4326754/2392973
You just schedule plenty of such blocks to your operation queue.
More reading
https://developer.apple.com/reference/dispatch

Is a __block variable assignment thread-safe to read immediately after the block?

__block NSHTTPURLResponse *httpResponse;
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
NSURLSessionDataTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
if (!error)
httpResponse = (NSHTTPURLResponse *)response;
}
dispatch_semaphore_signal(semaphore);
}];
[task resume];
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
Is it safe to read httpResponse after this? The semaphore waits for the block to compete execution. If there was no error, will the assignment be seen immediately or do I have to synchronise or create a memory barrier outside the block?
Does waiting on the semaphore implicitly perform some synchronisation which makes the __block variable safe to read immediately. If this was done with Thread.join() in Java instead of a semaphore, it would be safe since it guarantees a happens-before relationship with the assignment in the "block".
The short answer is yes.
The semaphore lock essentially forces the thread that is currently operating to stop execution until it receives enough unlock signals to proceed.
The variable you have defined is modified on some other thread before the semaphore is allowed to continue executing, so your assignment should have safely occurred.
Strictly speaking, this code will block on the executing thread (probably the main thread) until the semaphore lock is signalled. So - short answer, yes it should work, but it isn't best practice because it blocks the main thread.
Longer answer:
Yes, the semaphore will make sure the __block captured storage isn't accessed until it has been filled in. However, the calling thread will be blocked by the wait until the block has completed. This isn't ideal - normal UI tasks like making sure Activity Indicators spin won't happen.
Best practice would be to have the block signal the main object (potentially using a dispatch_async call to the main queue) once it has completed, and only accessing it after that. This is especially true given that if your session task fails (e.g. from network connectivity), then the calling thread will potentially block until the completion handler is called with a timeout error. This will appear to a user like the app has frozen, and they can't really do anything about it but kill the app.
For more information on working with blocks, see:
https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/ProgrammingWithObjectiveC/WorkingwithBlocks/WorkingwithBlocks.html
For best practice with Data Session tasks in particular:
http://www.raywenderlich.com/51127/nsurlsession-tutorial
It seems dispatch_semaphore_wait is also a memory barrier, so the value can be safely read.

GTLServiceYouTube executeQuery callback not called

GTLServiceYouTube executeQuery callback not called when I execute it in background thread.Video wont get uploaded. It works fine when I execute it in main thread.I get callbacks and video is also uploaded. I can solve this by forcing executeQuery to run in main thread but that affects the UI performance. How can I solve this issue?
My code goes like this ,
self.uploadFileTicket = [service executeQuery:query
completionHandler:^(GTLServiceTicket *ticket,
GTLYouTubeVideo *uploadedVideo,
NSError *error) {
// code to handle
}
I found a solution! Here whats happening is, when I run the code in background thread, before the callback comes the thread is detached. Hence we don't get the callback. When I run it in main thread, main Thread remains alive throughout. Hence we do get callback. Hence this problem can be solved by making current thread to wait using following code.
[[NSRunLoop currentRunLoop] runUntilDate:stopDate];

Executing synchronous queries to Google Cloud Endpoints on iOS

I guess this is really a feature request to Google, but I'm curious if anyone knows a work around. I'd like to execute a synchronous query request to a GAE Endpoints api.
In Android executing a request is synchronous. Then you put it into an AsyncTask to make it work in the background.
In iOS executing a request is asynchronous. You simply pass in a callback block.
I'm converting an Android app into an iOS app and it'd be really nice if they used the same mechanism. For example there are times when I WANT a synchronous query. It just makes my code easier and I know to put it on a background thread.
So my question is this... is there any way (hacky or not) to block until the iOS query completes?
You can wait on the call to finish with code with a timeout using code similar to this. Obviously you wouldn't want to do this on a UI thread but this would ensure your completion handlers run in serial.
NSLog(#"Before API Call");
GTLServiceTicket *apiCall = [apiService executeQuery:query completionHandler:^(GTLServiceTicket *ticket,
GTLHelloworldHelloGreeting *object,
NSError *error) {
NSLog(#"Starting completion handler");
NSArray *greetings = [NSArray arrayWithObjects: object, nil];
greetingsRetrievedFromAPI = greetings;
[self performSegueWithIdentifier: #"DisplayGreetings" sender: self];
NSLog(#"Ending completion handler");
}];
[apiService waitForTicket:apiCall timeout:100000 fetchedObject:nil error:nil];
NSLog(#"After completion handler");

better understand of NSURLConnection with NSOperationQueue

I make a queue
NSOperationQueue *operationQueue = [[NSOperationQueue alloc] init];
I send the queue to my async request
[NSURLConnection sendAsynchronousRequest:req queue:operationQueue completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
dispatch_async(dispatch_get_main_queue(), ^{
// ... do stuff here
});
}];
I cancel my operations prematurely
[operationQueue cancelAllOperations];
However, I can see my async's "completion" code still running. How come this scenario doesn't work like I expected?
Isn't it your responsibility to check isCancelled during your operation's task in case it gets canceled halfway though?
NSOperationQueue won't just kill tasks, it will set them as cancelled and let them finish themselves. This lets you clean up any resources you might have allocated and tidy up before you exit.
Tasks that haven't started yet won't start.
cancelAllOperations goes through the items in the queue and calls cancel on each one. If you look at the documentation for completionBlock:
The completion block you provide is executed when the value returned by the isFinished method changes to YES. Thus, this block is executed by the operation object after the operation’s primary task is finished or cancelled.
can be seen here
edit:
another snippet from the documentation for setCompletionBlock:
A finished operation may finish either because it was cancelled or because it successfully completed its task. You should take that fact into account when writing your block code. Similarly, you should not make any assumptions about the successful completion of dependent operations, which may themselves have been cancelled.

Resources