CFReadStreamRead blocking forever under iOS 7 - ios

I'm seeing an issue wherein CFReadStreamRead, as part of a streamed file upload, never returns.
This seems to happen only on iOS7 — and far more often when debugging against a physical device than in the simulator — or at least, it's far more evident there.
We have an HTTP (or HTTPS, the problem occurs either way, with a locally-hosted or remote server) POST of a file, via straight-line, blocking (non-event-driven) CFNetwork calls. It's a necessity of the C code calling this handler; there's no provision for callbacks.
That's well and good, the network calls are happening in background threads and/or via async dispatch.
The network code in question boils down to (removing error handling for brevity):
CFReadStreamRef upload = CFReadStreamCreateWithFile(
kCFAllocatorDefault, upload_file_url);
CFRelease(upload_file_url);
CFReadStreamOpen(upload);
CFReadStreamRef myReadStream = CFReadStreamCreateForStreamedHTTPRequest(
kCFAllocatorDefault, myRequest, upload);
CFReadStreamOpen(myReadStream);
CFIndex numBytesRead = CFReadStreamRead(myReadStream, buf, sizeof(buf));
// etc.
On its own, this code wants to hang immediately under iOS7. If I add a loop with some calls to usleep before it (checking CFReadStreamHasBytesAvailable along the way), it will almost always succeed. Every few hundred tries, it will still fail, never returning. Again, the main thread is unaffected.
I'd hoped the GM would clear up this behavior, but it's still present.
Adding a runloop/callback method to watch for bytes-available events has no effect - when the call hangs, no events are seen, either.
Any suggestions as to why this is happening, or how it can be prevented? Anyone else seeing different CFReadStream behavior under iOS 7?

I've try such nasty workaround and it works for me, problem is that I'm requesting delta values from server, so if something goes wrong I'm just fetching new delta value with, in general case it will not work (in logs I see that timeout kicks in sometimes). At least this prevents form permanent thread blocking and gives a chance to handle somehow this problem:
NSInteger readStreamReadWorkaround(CFReadStreamRef readStrem, UInt8 *buffer, CFIndex bufferLength) {
static dispatch_once_t onceToken;
static BOOL isProblematicOs = YES;
dispatch_once(&onceToken, ^{
isProblematicOs = [[UIDevice currentDevice].systemName compare: #"7.0" options: NSNumericSearch]!=NSOrderedAscending;
});
NSInteger readBytesCount = -2;
if (isProblematicOs) {
CFStreamStatus sStatus = CFReadStreamGetStatus(readStrem);
NSDate *date = [NSDate date];
while (YES) {
if(CFReadStreamHasBytesAvailable(readStrem)) {
readBytesCount = CFReadStreamRead(readStrem, buffer, bufferLength);
break;
}
sStatus = CFReadStreamGetStatus(readStrem);
if (sStatus!=kCFStreamStatusOpen && sStatus !=kCFStreamStatusAtEnd
|| [date timeIntervalSinceNow]<-15.0) {
break;
}
usleep(50000);
}
} else {
readBytesCount = CFReadStreamRead(readStrem, buffer, sizeof(buffer));
}
return readBytesCount;
}
I don't like this solution but so far I don't see an alternative.

Related

How to calculate payload size and timeout length for network connectivity test in iOS?

My app offers the option to download 3430 high resolution images from our server, each image of size 50k - 600k bytes.
The original approach was to just download all of them - but we realized that gave a lot of NSURLErrorTimedOut errors and crashed our program. We've now implemented it such that we download all of the images, but in batches of 100 images at a time.
- (void)batchDownloadImagesFromServer:(BOOL)downloadHiResImages
{
[UIApplication sharedApplication].idleTimerDisabled = YES;
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES];
[self generateImageURLList:YES];
[leafletImageLoaderQueue removeAllObjects];
numberOfThumbnailLeft = [uncachedThumbnailArray count];
numberOfHiResImageLeft = [uncachedHiResImageArray count];
NSLog(#"DEBUG: In batchDownloadImagesFromServer numberOfThumbnailLeft %ul , numberOfHiResImageLeft %ul ", numberOfThumbnailLeft, numberOfHiResImageLeft);
numberOfImagesToDownload = numberOfThumbnailLeft;
if (downloadHiResImages)
{
numberOfImagesToDownload += numberOfHiResImageLeft;
}
if (numberTotalToDownload < 0) {
numberTotalToDownload = numberOfHiResImageLeft;
}
int midBatchCt = 0;
// start where we stopped
NSArray *subArray;
NSRange batchRange;
batchRange.location = 0;//uncachedHiResIndex;
NSInteger uncachedNumber = [uncachedHiResImageArray count];
NSLog(#"uncachedHiResIndex and numberTotalToDownload: %d %d", uncachedHiResIndex, numberTotalToDownload);
if (uncachedHiResIndex >= numberTotalToDownload || batchRange.location >= uncachedNumber) {
// we have reached the end of the uncached hires images
NSLog(#" END of download total to download=%ld , uncachedNumber=%ld, # not downloaded is %ld", (long)numberTotalToDownload, uncachedNumber, (long)numberFailedToDownload);
return;
}
if (batchRange.location+100 > uncachedNumber) {
NSInteger imagesUntilEnd = uncachedNumber -1;
batchRange.length = imagesUntilEnd;
NSLog(#"this is uncached number: %d this is uncachedhiresindex:%d and this images until end:%d ", uncachedNumber, uncachedHiResIndex, imagesUntilEnd);
} else {
batchRange.length = 100;
}
NSLog(#" NEW RANGE is from %lul to %lul ", (unsigned long)batchRange.location, batchRange.length);
subArray = [uncachedHiResImageArray subarrayWithRange:batchRange];
if (downloadHiResImages)
{
for ( LeafletURL* aLeafletURL in subArray )
{
LeafletImageLoader* hiResImageLoader = [[LeafletImageLoader alloc] initWithDelegate:self];
[leafletImageLoaderQueue addObject:hiResImageLoader]; // do this before making connection!! //
[hiResImageLoader loadImage:aLeafletURL isThumbnail:NO isBatchDownload:YES];
//// Adding object to array already retains it, so it's safe to release it here. ////
[hiResImageLoader release];
midBatchCt++;
uncachedHiResIndex++;
if (midBatchCt == 10) {
NSLog(#" Waiting for queued images to download...");
NSLog(#" REMOVED from QUEUE %lul , UncachedIndex %lul", numberRemovedFromQueue, uncachedHiResIndex);
break;
}
}
}
if ( [leafletImageLoaderQueue count] == 0 && numberRemovedFromQueue == numberTotalToDownload) {
if([delegate respondsToSelector:#selector(requestDidBatchDownloadImages:)])
{
[delegate requestDidBatchDownloadImages:self];
}
}
}
This has resolved most of our issues. However, we would like to test for network connectivity before even beginning batch downloading. I found a low level ping library that gives accurate round-trip timing results. Using the demo code from GBPing as a reference, I wrote my own code to ping our server before we call batchDownloadImagesFromServer.
- (IBAction)preloadAll:(id)sender
{
self.ping = [GBPing new];
self.ping.host = kDefaultDataServer;
self.ping.delegate = self;
self.ping.timeout = 1;
self.ping.pingPeriod = 0.9;
// setup the ping object (this resolves addresses etc)
[self.ping setupWithBlock:^(BOOL success, NSError *error) {
if (success) {
// start pinging
[self.ping startPinging];
// stop it after 5 seconds
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(#"stop it");
[self.ping stop];
self.ping = nil;
});
} else {
UIAlertController * alert = [UIAlertController alertControllerWithTitle:#"Internet Connection"
message:#"Not strong enough internet connection"
preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction* OKButton = [UIAlertAction
actionWithTitle:#"Ok"
style:UIAlertActionStyleCancel
handler:^(UIAlertAction * action) {
[downloadManager batchDownloadImagesFromServer:YES];
}];
[alert addAction:OKButton];
[self presentViewController:alert animated:NO completion:nil];
}
}];
}
I am completely new to networking. How do I determine the payload size and timeout length for my test considering the batch size and the size of the images?
Timeout length is per request. It is just the time the networking code will wait for a reply before it gives up. This shouldn't be too short, but for most system API, timeout length is something around a minute or more, which is probably too long.
Also, note that you will still get time out errors if connectivity is bad, so whatever caused your crashes needs to be fixed. You have to be able to recover from time-outs.
You're not giving much information about your crash (what kind of crash is it? What backtrace do you get?), but I can see three obvious things that may be happening:
You did your downloading in a tight loop without an #autoreleasepool {} block inside it. This means that all your downloaded file data accumulated in RAM and blew your app's memory limit. Be sure to put autorelease pools in long-running loops.
You were doing these downloads on the main thread. The main thread is for the UI and short actions. If your main thread code does anything that takes longer than a few seconds, UIApplication will not get to process touch events from the user (and other occurrences) and the operating system will shoot it down as being unresponsive. Offload longer operations onto a dispatch queue (or use some other way to move the actions off the main thread, e.g. by using asynchronous API).
You are flooding your server with requests, and some DDoS-protection inside it decides to just ignore requests from you for a few minutes as a form of self-protection. Many servers have limits on how many requests they will accept from a client within a given period of time, or how many open connections a client may have at the same time.
I think you would be much better served by showing the code that performs the actual download. You should not need to get accurate ping timing information to download a bunch of image files.
Assuming one or more of the above possibilities are true, I'd suggest you implement your download code like this:
Create a list of all file URLs that need to be downloaded.
Write your code so that it downloads these URLs sequentially. I.e. do not let it start downloading a file until the previous one has finished (or failed and you decided to skip it for now).
Use NSURLSession's support for downloading an individual file to a folder, don't use the code to get an NSData and save the file yourself. That way, your application doesn't need to be running while the download finishes.
Ensure that you can tell whether a file has already been downloaded or not, in case your download gets interrupted, or the phone is restarted in mid-download. You can e.g. do this by comparing their names (if they are unique enough), or saving a note to a plist that lets you match a downloaded file to the URL where it came from, or whatever constitutes an identifying characteristic in your case.
At startup, check whether all files are there. If not, put the missing ones in above download list and download them sequentially, as in #2.
Before you start downloading anything (and that includes downloading the next file after the previous download has finished or failed), do a reachability check using the Reachability API from Apple's SystemConfiguration.framework. That will tell you whether the user has a connection at all, and whether you're on WiFi or cellular (in general, you do not want to download a large number of files via cellular, most cellular connections are metered).
If your images are stored on separate servers, or they are comparatively small and there is more overhead setting up the connection than actually downloading the data, you could modify the code to download several images at once, but usually if you're downloading more than 4 images from a server at the same time, you'll likely not see a performance benefit, as every additional image will just reduce the amount of bandwidth available for the others.

iOS - How to measure thread wakeups?

I have an app that's crashing due to too many "thread wakeups". For example:
45004 wakeups over the last 220 seconds (205 wakeups per
second average), exceeding limit of 150 wakeups per second over 300 seconds
This is difficult to debug because I know of no direct way to measure thread wakeups. The closest I've found is an Instruments template called System Trace that will show you number of blocked thread events. Presumably, this is closely related since a blocked thread means that that thread will sleep and then wake up when it becomes unblocked.
The weird thing about this is that the number of blocked threads is in the 10,000's range per second when the app is running normally and doesn't crash. My assumption is that a blocked, sleeping thread only counts towards your "wakeups" limit in certain circumstances - e.g. I would expect that a thread that is locked due to a mutex lock counts, whereas the OS simply transitioning to other threads in normal operation doesn't.
It would be amazing to me if Instruments had a Thread Wakeups template. The only documentation I can find is here - https://developer.apple.com/library/content/technotes/tn2151/_index.html:
The exception subtype WAKEUPS indicates that threads in the process are being woken up too many times per second, which forces the CPU to wake up very often and consumes battery life.
Typically, this is caused by thread-to-thread communication (generally using peformSelector:onThread: or dispatch_async) that is unwittingly happening far more often than it should be. Because the sort of communication that triggers this exception is happening so frequently, there will usually be multiple background threads with very similar Backtraces - indicating where the communication is originating.
Here’s some Objective-C code based on Ivan’s answer you can copy + paste somewhere into your project (e.g. your applicationDidFinishLaunching: method) to log the number of wakeups per second (works on Mac and iOS):
#include <mach/task.h>
#include <mach/mach.h>
...
__block NSUInteger lastWakeups = 0;
[NSTimer scheduledTimerWithTimeInterval:1.0 repeats:YES block:^(NSTimer * _Nonnull timer) {
struct task_power_info info = {0};
mach_msg_type_number_t count = TASK_POWER_INFO_COUNT;
kern_return_t ret = task_info(current_task(), TASK_POWER_INFO, (task_info_t)&info, &count);
if (ret == KERN_SUCCESS) {
NSUInteger wakeups = info.task_interrupt_wakeups + info.task_timer_wakeups_bin_1 + info.task_timer_wakeups_bin_2;
NSLog(#"WAKEUPS: %lu per second", (unsigned long)(wakeups - lastWakeups));
lastWakeups = wakeups;
} else {
NSLog(#"Error: unable to get CPU wakeups (%d)", ret);
}
}];
Please, take a look here https://developer.apple.com/forums/thread/124180 There is a description of a code of getting wakeup count in your app, not only in the instrument. May help you:
#include <mach/task.h>
#include <mach/mach.h>
BOOL GetSystemWakeup(NSInteger *interrupt_wakeup, NSInteger *timer_wakeup) {
struct task_power_info info = {0};
mach_msg_type_number_t count = TASK_POWER_INFO_COUNT;
kern_return_t ret = task_info(current_task(), TASK_POWER_INFO, (task_info_t)&info, &count);
if (ret == KERN_SUCCESS) {
if (interrupt_wakeup) {
*interrupt_wakeup = info.task_interrupt_wakeups;
}
if (timer_wakeup) {
*timer_wakeup = info.task_timer_wakeups_bin_1 + info.task_timer_wakeups_bin_2;
}
return true;
}
else {
if (interrupt_wakeup) {
*interrupt_wakeup = 0;
}
if (timer_wakeup) {
*timer_wakeup = 0;
}
return false;
}
}
Also there you can find some reasons why wakeups occur too much times.
The "Energy Efficiency Guide for Mac Apps" at the end mentions a command-line utility called timerfires that can be used to see what is causing wakeups.
However, the utility seems to be outdated on macOS 12 Monterey, as I was getting errors like the following when I first tried to run it:
probe description fbt::thread_dispatch:entry does not match any probes
I had to copy the utility and edit it to remove all the DTrace methods that are no longer available to get the tool working.
Once that was done, the tool will show each timer invocation, which is very helpful to track down timers in order to reduce wakeups.

Function cannot run concurrently

I would like to make a function that only run once and cancel if is still running.
I tried it using a simple lock boolean on start/end, but sometimes it's "overlapping".
There's a better and secure way to do that?
#property (assign) BOOL lock;
- (void)myFuntion
{
if (self.lock) {
NSLog(#"(Canceled) Syncing is already running...");
return;
}
self.lock = YES;
// My Code
self.lock = NO;
}
The NSLock class should be able to help you here. I have not tried this example directly, but something like:
NSLock *myFunctionLock=[NSLock new]; // this should be a class data member/property/etc.
- (void)myFuntion
{
if (![myFunctionLock tryLock])
return; /* already running */
// My Synchronized Code
[myFunctionLock unlock];
}
We are all assuming that you are talking about concurrent programming, where you are running the same code on different threads. If that's not what you mean then you would need to explain what you DO mean, since code that runs on the same thread can only execute a function once at any particular moment.
Take a look at NSLock's tryLock function. The first caller to assert the lock gets back a TRUE, and can proceed to access the critical resource. Other callers get back FALSE and should not access the critical resource, but won't block.

Synchronize two clients temporally over Multipeer Framework

I've been working at this problem for a few days and none of my solutions have been adequate. I'm lacking the theoretical knowledge to make this happen, I think, and would love some advice (does not have to be iOS specific--I can translate C, pseudocode, whatever, into what I need).
Basically, I have two iPhones. Either one can trigger a repeating action when the user presses a button. It then needs to notify the other iPhone (via the MultiPeer framework) to trigger the same action...but they both need to start at the same instant and stay in step. I really need to get 1/100sec accuracy, which I think is achievable on this platform.
As a semi-rough gauge of how well in synch I am, I use AudioServices to play a "tick" sound on each device...you can very easily tell by ear how well in synch they are (ideally you would not be able to discern multiple sound sources).
Of course, I have to account for the MultiPeer latency somehow...and it's highly variable, anywhere from .1 sec to .8 sec in my testing.
Having found that the system clock is totally unreliable for my purposes, I found an iOS implementation of NTP and am using that. So I'm reasonably confident that the two phones have an accurate common reference for time (though I haven't figured out a way to test this assumption short of continuously displaying NTP time on both devices, which I do, and it seems nicely in synch to my eye).
What I was trying before was sending the "start time" with the P2P message, then (on the recipient end) subtracting that latency from a 1.5sec constant, and performing the action after that delay. On the sender end, I would simply wait for that constant to elapse and then perform the action. This didn't work at all. I was way off.
My next attempt was to wait, on both ends, for a whole second divisible by three, Since latency always seems to be <1sec, I thought this would work. I use the "delay" method to simply block the thread. It's a cudgel, I know, but I just want to get the timing working period before I worry about a more elegant solution. So, my "sender" (the device where the button is pressed) does this:
-(void)startActionAsSender
{
[self notifyPeerToStartAction];
[self delay];
[self startAction];
}
And the recipient does this, in response to a delegate call:
-(void)peerDidStartAction
{
[self delay];
[self startAction];
}
My "delay" method looks like this:
-(void)delay
{
NSDate *NTPTimeNow = [[NetworkClock sharedInstance] networkTime];
NSCalendar *calendar = [NSCalendar currentCalendar];
NSDateComponents *components = [calendar components:NSSecondCalendarUnit
fromDate:NTPTimeNow];
NSInteger seconds = [components second];
// If this method gets called on a second divisible by three, wait a second...
if (seconds % 3 == 0) {
sleep(1);
}
// Spinlock
while (![self secondsDivideByThree]) {}
}
-(BOOL)secondsDivideByThree
{
NSDate *NTPTime = [[NetworkClock sharedInstance] networkTime];
NSCalendar *calendar = [NSCalendar currentCalendar];
NSInteger seconds = [[calendar components:NSSecondCalendarUnit fromDate:NTPTime]
second];
return (seconds % 3 == 0);
}
This is old, so I hope you were able to get something working. I faced a very similar problem. In my case, I found that the inconsistency was almost entirely due to timer coalescing, which causes timers to be wrong by up to 10% on iOS devices in order to save battery usage.
For reference, here's a solution that I've been using in my own app. First, I use a simple custom protocol that's essentially a rudimentary NTP equivalent to synchronize a monotonically increasing clock between the two devices over the local network. I call this synchronized time "DTime" in the code below. With this code I'm able to tell all peers "perform action X at time Y", and it happens in sync.
+ (DTimeVal)getCurrentDTime
{
DTimeVal baseTime = mach_absolute_time();
// Convert from ticks to nanoseconds:
static mach_timebase_info_data_t s_timebase_info;
if (s_timebase_info.denom == 0) {
mach_timebase_info(&s_timebase_info);
}
DTimeVal timeNanoSeconds = (baseTime * s_timebase_info.numer) / s_timebase_info.denom;
return timeNanoSeconds + localDTimeOffset;
}
+ (void)atExactDTime:(DTimeVal)val runBlock:(dispatch_block_t)block
{
// Use the most accurate timing possible to trigger an event at the specified DTime.
// This is much more accurate than dispatch_after(...), which has a 10% "leeway" by default.
// However, this method will use battery faster as it avoids most timer coalescing.
// Use as little as necessary.
dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, DISPATCH_TIMER_STRICT, dispatch_get_main_queue());
dispatch_source_set_event_handler(timer, ^{
dispatch_source_cancel(timer); // one shot timer
while (val - [self getCurrentDTime] > 1000) {
// It is at least 1 microsecond too early...
[NSThread sleepForTimeInterval:0.000001]; // Change this to zero for even better accuracy
}
block();
});
// Now, we employ a dirty trick:
// Since even with DISPATCH_TIMER_STRICT there can be about 1ms of inaccuracy, we set the timer to
// fire 1.3ms too early, then we use an until(time) { sleep(); } loop to delay until the exact time
// that we wanted. This takes us from an accuracy of ~1ms to an accuracy of ~0.01ms, i.e. two orders
// of magnitude improvement. However, of course the downside is that this will block the main thread
// for 1.3ms.
dispatch_time_t at_time = dispatch_time(DISPATCH_TIME_NOW, val - [self getCurrentDTime] - 1300000);
dispatch_source_set_timer(timer, at_time, DISPATCH_TIME_FOREVER /*one shot*/, 0 /* minimal leeway */);
dispatch_resume(timer);
}

Error using semaphore token to run code after a group of asynchronous web service calls

I am in a situation where I need to call multiple web service requests at a time to call to my server to delete messages on the server and I am having a difficult time trying to figure out the best way to trigger some methods to refresh my data at the completion of these group of web service calls.
From what I have researched using a semaphore counter should work for what I am wanting to do, but I am running into a problem where upon calling dispatch_release() on my semaphore token my application crashes with this -
libdispatch.dylib`_dispatch_semaphore_dispose$VARIANT$mp:
0x3c03ed70: push {r7, lr}
0x3c03ed72: ldr r1, [r0, #40]
0x3c03ed74: mov r7, sp
0x3c03ed76: ldr r2, [r0, #36]
0x3c03ed78: cmp r1, r2
0x3c03ed7a: bge 0x3c03ed7e ; _dispatch_semaphore_dispose$VARIANT$mp + 14
0x3c03ed7c: trap
Everything i've found on this problem points to the semaphore token being referenced by something but I can't see what would have a reference to the token.
- (void)deleteMultipleThreadsForMessages:(NSArray *)messages withCompletion:(void(^)(BOOL allThreadsDeleted))completion
{
NSDictionary *userDictionary = [[MSMKeychainAccess sharedKeychain] returnUserDictionary];
long messagesCount = [messages count] - 1;
dispatch_semaphore_t semaphore = dispatch_semaphore_create(messagesCount);
BOOL (^isLastRequest)(void) = ^BOOL (void) {
long result = dispatch_semaphore_wait(semaphore, DISPATCH_TIME_NOW);
if (0 == result) {
return false;
}
dispatch_release(semaphore);
return true;
};
for (MSMMessageDataModel *message in messages) {
NSDictionary *dictionary = #{#"license": userDictionary[#"license"],
#"messageID" : message.msgID};
NSLog(#"Called this many times! %#", message.msgID);
[[MSMClient sharedClient] soapPostsWithCompletion:^(NSDictionary *response, NSError *error) {
if (error) {
isLastRequest();
completion(NO);
} else {
if (isLastRequest()) {
completion(YES);
}
}
} andRequest:[[[MSMRequestsController alloc] init] createGetMessagesSOAPCallAndSendRequest:#"DeleteThread"
withParameters:dictionary]];
}
}
EDIT
Thanks for the great answers. As Dustin said I was attempting to use dispatch_semaphore for something that it should not be used for. I accepted his answer because it was simple to implement and didn't need any re-structure of what i'm doing currently to send my web services. I now have some good reading material though about dispatch_groups in general though!
Thanks for all your help!
I'm not sure this is the most efficient way to solve your problem, but just looking through your code, one problem you might be running into is that you're creating the semaphore with a count and trying to release it when that count is less than the initial count. In GCD, always create a semaphore with 0 and then signal the semaphore to the correct number of "resources" you need to count. The reason is because semaphores can't be destroyed/released if their count is less than the initial count. It kinda makes sense if you think of it as a resource counter. Having a semaphore number less than the initial count means that you have a worker still using one of the resources.
You can see this code here http://opensource.apple.com/source/libdispatch/libdispatch-187.7/src/semaphore.c. The code that will throw the exception in _dispatch_semaphore_dispose is:
if (dsema->dsema_value < dsema->dsema_orig) {
DISPATCH_CLIENT_CRASH(
"Semaphore/group object deallocated while in use");
}
This is exactly what dispatch_group is designed to address. You dispatch several blocks to a group, and when they have all completed, another block will be executed (or you can wait on them if you need a synchronous behavior).
First see Waiting on Groups of Queued Tasks in the Concurrency Programming Guide, and see the dispatch_group functions in the GCD reference. To see them in action, see the JuliaCell example from Chapter 13 of iOS:PTL. Cocoa Samurai also has some examples.
Even if you can't actually dispatch the blocks to a group (it may not work with how MSMClient operates), you can still use dispatch groups manually by calling dispatch_group_enter() and dispatch_group_leave() to get the same behavior you're trying to get from the semaphore.
As a side note, BOOL is not the same as bool. BOOL return YES and NO, which are different than true and false (which I assume means you're compiling this as ObjC++, which always makes me shudder, but that's a different issue). Mixing them can matter because they can be (and sometimes are) different sizes. I've had to crashes due to that personally.
This is an incorrect usage for dispatch_semaphore -- it isn't meant to do what you are attempting. What you need is a counting variable that is thread safe. You can get this using __sync_sub_and_fetch.
- (void)deleteMultipleThreadsForMessages:(NSArray *)messages withCompletion:(void(^)(BOOL allThreadsDeleted))completion
{
NSDictionary *userDictionary = [[MSMKeychainAccess sharedKeychain] returnUserDictionary];
__block long messagesCount = [messages count];
for (MSMMessageDataModel *message in messages) {
NSDictionary *dictionary = #{#"license": userDictionary[#"license"],
#"messageID" : message.msgID};
NSLog(#"Called this many times! %#", message.msgID);
[[MSMClient sharedClient] soapPostsWithCompletion:^(NSDictionary *response, NSError *error) {
long which = __sync_sub_and_fetch(&messageCount, 1);
if(which == 0)
completion(error == nil);
} andRequest:[[[MSMRequestsController alloc] init] createGetMessagesSOAPCallAndSendRequest:#"DeleteThread"
withParameters:dictionary]];
}
}
__sync_sub_and_fetch tells the CPU that you want it to take latest version of 'messageCount' from all threads (and cores), subtract 1, and give you the result.

Resources