I need to perform periodic updates of the data I persist with Core Data. I get such data from asynchronous calls to REST services. To firstly retrieve all the data, I create a full core data stack in a private queue and then I do this:
- (void)updateDataFromServices
{
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES];
self.dataUpdatePrivateContext = [MyCoreDataStackHelper getPrivateContext];
if (self.dataUpdatePrivateContext != nil) {
[self.dataUpdatePrivateContext performBlockAndWait: ^{
// Asynchronous operations
[self callService1];
[self callService2];
[self callService3];
}];
}
}
In the callback of each service that is called, I check that the rest of services have already finished too, and if all have finished I call a method (manageInfoUpdate) to handle updates between the data I have in my main context (in main thread) and the data I now have in the private context (in the private queue):
- (void)manageInfoUpdate
{
const char* UpdateInfoQueue = "com.comp.myapp.updateinfo";
dispatch_queue_t queue = dispatch_queue_create(UpdateInfoQueue, NULL);
dispatch_async(queue,^{
// Handle updates from private context:
// Here I compare objects in the main context with the objects
// in the private context and I delete objects from both
// by calling:
[mainContext deleteObject:object];
[self.dataUpdatePrivateContext deleteObject:object];
// This seems to work...
// Save and clear private context
[self saveContext:self.dataUpdatePrivateContext];
[self clearContext:self.dataUpdatePrivateContext];
dispatch_async(dispatch_get_main_queue(), ^{
// Re-fetch from main context to get
// the updated data
// Save main context
[self saveContext:mainContext];
// Notify end of updates
});
});
}
I try to perform the manageInfoUpdate operations in another async thread. I'm getting EXEC_BAD_ACCESS exceptions when trying to clear / save the contexts... Could somebody help me to find why?
Thanks in advance
You won't get an outright "error" for using Core Data in a multi-threaded environment incorrectly. The app just will crash, sometimes.
To confirm you are using it correctly, turn on the debug flag com.apple.CoreData.ConcurrencyDebug 1 in your runtime arguments. Then it will crash EVERY time you touch a MOC or MO from the wrong queue.
As it stands you code is not correct at all with regard to threading. A MO that is created on one queue can only be accessed from that queue. Likewise, a MOC that is configured for the main queue must be accessed on the main queue and a MOC configured as a private queue must be accessed on ITS private queue.
Your "UpdateInfoQueue" is violating the threading rules completely.
Turn on the debug flag, correct the errors it shows you and your save issue will be corrected.
Related
I have a really heavy task to perform, and I dont want to block the main thread. So I'm starting a separate Concurrent queue for it. There can be 4 instances of the said task.
-(dispatch_queue_t)getConcurrentQueue
{
if(concurrentQueue == nil)
{
concurrentQueue = dispatch_queue_create("com.myself.HeavyTask", DISPATCH_QUEUE_CONCURRENT);
}
return concurrentQueue;
}
Now to start the heavy task I have -
-(void)beginTask
{
//.....
//.....
__weak typeof(self) weakSelf = self;
dispatch_queue_t queue = [self getConcurrentQueue];
dispatch_async(queue, ^{
[weakSelf heavyTask];
});
}
Now the method heavyTask goes like this --
-(void)heavyTask
{
//...
dispatch_sync(dispatch_get_current_queue(), ^{
// Initialising code for heavy task
// This is the critical section. Only one of the 4 concurrent threads can enter this at a time
}
//....
while(condition)
{
// Perform meat of the task
}
//...
dispatch_sync(dispatch_get_current_queue(), ^{
// Teardown code. Freeing memory etc.
// This is also a critical section.
}
//...
}
The initialising code and teardown code uses some 3rd party C methods which are not thread safe. So making them thread safe is not the scope of the question.
Now I've put the "initialising code" and and the "teardown code" within -
dispatch_sync(dispatch_get_current_queue(), ^{
}
My code is crashing and I'm getting error messages stating that there is insufficient thread locking around the critical section code.
I read that dispatch_get_current_queue() is not safe, so I replaced it with concurrentQueue. I also tried replacing with dispatch_get_main_queue(). Still the code crashes, complaining about insufficient thread locking.
I know there is something wrong in my understanding of implementing critical section using GCD.
Can anyone clearly show me how to make my code work properly here??
Side question -- Can I use #synchronized { } blocks here??
There's a lot wrong with your code, including not adhering to naming conventions.
So basically, if you want to execute the same task concurrently with respect to each other, use the global concurrent queue to execute those tasks.
If you want to concurrently access shared resources from within these tasks (or from elsewhere), define a dedicated queue, say "sync_queue" where you exclusively access these resources. This "sync_queue" executes your "critical sections".
The "sync_queue" can be serial or concurrent.
If you use a serial queue, use dispatch_async(sync_queue, block) for write access and dispatch_sync(sync_queue, block) for read access to shared resources.
If you use a concurrent queue, use dispatch_barrier_async(sync_queue, block) for write access and dispatch_barrier_sync(sync_queue, block) for read access to shared resources.
Example:
// Read access using a serial sync_queue:
...
__block int counter;
dispatch_sync(sync_queue, ^{
counter = _counter;
});
// Write access using a serial sync_queue:
...
dispatch_async(sync_queue, ^{
_counter = counter;
});
// Read access using a concurrent sync_queue:
...
__block int counter;
dispatch_barrier_sync(sync_queue, ^{
counter = _counter;
});
// Write access using a concurrent sync_queue:
...
dispatch_barrier_async(sync_queue, ^{
_counter = counter;
});
Example for your "heavy task":
-(void)heavyTask
{
dispatch_barrier_async(sync_queue, ^{
// Initialize heavy task
...
// Continue with the task:
dispatch_async(dispatch_get_global_queue(0,0), ^{
BOOL condition = YES; // condition must be local to the block (it's not a shared resource!)
while(condition)
{
// Perform meat of the task
condition = ...;
}
dispatch_barrier_async(sync_queue, ^{
// Teardown code. Freeing memory etc.
// This is also a critical section.
...
}
});
}
}
You called it "getSerialQueue" but really you are creating a "concurrent" queue in it. Try to fix it substituting DISPATCH_QUEUE_CONCURRENT with DISPATCH_QUEUE_SERIAL in getSerialQueue.
Keep in mind that:
dispatch_sync means: I will wait here until this block finishes
dispatch_async means: I will not wait
This is not related to concurrent or serial. If two tasks in a concurrent queue call dispatch_sync(block), 'block' will be executed concurrently.
Hope this helps.
I am developing a bluetooth app using the EADemo example. Whenever there is data in the bluetooth inputStream, the following event is invoked:
- (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode
{
switch (eventCode) {
case 1:
//here I want to start two tasks
break;
}
And I have a common queue to which I have to write and read parallely.
The queue is actually a NSMutuableArray. In the write part, I use addobject:
and in the read part, I use objectAtIndex:0.
Any one has any idea how to move about this?
As you are using same array you will need to make it safe to access otherwise it may crash app
APPROACH A: (Using Grand Central Dispatch)
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0);//set priority as per requirement
//Read
dispatch_async(queue, ^{
while (//condition to stop)
{
//Perform read operation
}
});
//Write
dispatch_async(queue, ^{
while (//condition to stop)
{
//Perform write operation
}
});
//As operations are performed on same queue your array is safely accessed
APPROACH B: (Using threads)
Create two thread
Read Thread
Write tread
But access your array in synchronised manner.
I am concurrently downloading some information from server and I am using NSOperatioQueue for the same. I have an issue. For instance if a download operation fails for some reason I don't want to remove that operation from queue.
Right now even if its a failure as soon as it gets a response back from server the operation is removed from queue.
Is there any way to tell the queue that a particular operation is not logically finished and it should keep it in queue?
In my case, I am downloading a set of information. For example fetching all places in a County and then all houses for each county. So in certain cases county cannot be downloaded if the user is not logged in with a valid token. In that case server returns a failure message. I want to keep such items in queue so that I can try again when user logs in to the app.
Sample Code
self.downloadQueue.maxConcurrentOperationCount = 1;
for(Campaign *campaign in campaigns)
{
isContentUpdated = false;
if(self.operation)
self.operation = Nil;
self.operation = [[DownloadOutlets alloc] initWithCampaign:campaign];
[self.downloadQueue addOperation:operation];
}
where downloadQueue is an NSOperationQueue and DownloadOutlets extends NSOperation.
Thanks
You should not be keeping your failed operations in the queue. The failed operation has performed its task. You should have your operation controller listen to the state of the operations, via completionBlock or otherwise, and decide what to do next. If it comes to the determination that the operation has failed but a similar operation should be retried, it should add another operation to perform the task again.
Another approach would be to retry your download inside the operation until success, and only then end the operation. This is not optimal design, however, because the operation does not, and should not, have all the information required to decide whether to retry, inform the user, etc.
You shouldn't keep operations that failed in queue, but use the queue for serial fetching data, and stop queueing if the operation fails :
#implementation DataAdapter
// ...
-(void)setup{
// weak reference to self to avoid retain cycle
__weak DataAdapter* selfRef= self;
// create a block that will run inside the operation queue
void(^pullCountriesBlock)(void)= ^{
[[DownloadManager instance] fetchAllCountriesWithCompletionBlock:^(Result* result){
if(result.successful){
// on success
[selfRef didFetchDataForAction:action];
}else{
// on failure
[selfRef failedToFetchDataForAction:action];
}
};
self.actions= [NSMutableArray new];
[self.actions addObject:[DownloadAction actionWithBlock:pullCountriesBlock];
// add other actions
// ...
[self fetchData];
}
}
-(void)fetchData{
if(self.currentActionIndex >= self.actions.count){
[self finishedFetchingData];
return;
}
[self fetchDataForAction: self.actions[self.currentActionIndex] ];
}
-(void)fetchDataForAction:(DownloadAction*)action
[self.myOperationQueueImplementation enqueueOperationWithBlock:action.block];
}
If the download is successful, just enqueue the next action(increment the currentActionIndex and call fetchData). If it fails, you can act accordingly. What I'd do is start listening to interesting NSNotificationCenter events before calling fetchData the first time. You could listen to UserDidLogInNotification or any other that may allow the queue to continue running the downloads.
This has been a hard one to search.
I found a similar question, iOS 5 Wait for delegate to finish before populating a table?, but the accepted answer was 'Refresh the table view,' and that does not help me. The other results I found tended to be in c#.
I have an app that streams from iPhone to Wowza servers. When the user hits record, I generate a unique device id, then send it to a PHP script on the server that returns a JSON document with configuration settings (which includes the rtmp dump link).
The problem is, the delegate methods are asynchronous, but I need to get the config settings before the next lines of code in my - (IBAction)recordButtonPressed method, since that code is what sets the profile settings, and then records based on those settings.
I've realized I could make the NSURLConnection in -recordButtonPressed like I am currently, and then continue the setup code inside the delegate method connectionDidFinishLoading (or just encapsulate the setup and method call it from there) but that's sacrificing coherent design for functionality and that sucks.
Is there not some simple waitUntilDelegateIsFinished:(BOOL)nonAsyncFlag flag I can send to the delegator so I can have sequential operations that pull data from the web?
I've realized I could make the NSURLConnection in -recordButtonPressed like I am currently, and then continue the setup code inside the delegate method connectionDidFinishLoading (or just encapsulate the setup and method call it from there) but that's sacrificing coherent design for functionality and that sucks.
You have analyzed and understood the situation and you have described its possible solutions perfectly. I just don't agree with your conclusions. This kind of thing happens all the time:
- (void) doPart1 {
// do something here that will eventually cause part2 to be called
}
- (void) doPart2 {
}
You can play various games with invocations to make this more elegant and universal, but my advice would be, don't fight the framework, as what you're describing is exactly the nature of being asynchronous. (And do not use a synchronous request on the main thread, since that blocks the main thread, which is a no-no.)
Indeed, in an event-driven framework, the very notion "wait until" is anathema.
Why not to use synchronous request?
Wrap your asynchronous NSURLConnection request in a helper method which has a completion block as a parameter:
-(void) asyncDoSomething:(void(^)(id result)completionHandler ;
This method should be implemented in the NSURLConnectionDelegate. For details see the example implementation and comments below.
Elsewhere, in your action method:
Set the completion handler. The block will dispatch further on the main thread, and then perform anything appropriate to update the table data, unless the result was an error, in which case you should display an alert.
- (IBAction) recordButtonPressed
{
[someController asyncConnectionRequst:^(id result){
if (![result isKindOfClass:[NSError class]]) {
dispatch_async(dispatch_get_main_queue(), ^{
// We are on the main thread!
someController.tableData = result;
});
}
}];
}
The Implementation of the method asyncConnectionRequst: could work as follows: take the block and hold it in an ivar. When it is appropriate call it with the correct parameter. However, having blocks as ivars or properties will increase the risk to inadvertently introduce circular references.
But, there is a better way: a wrapper block will be immediately dispatched to a suspended serial dispatch queue - which is hold as an ivar. Since the queue is suspended, they will not execute any blocks. Only until after the queue will be resumed, the block executes. You resume the queue in your connectionDidFinish: and connectionDidFailWithError: (see below):
In your NSURLConnectionDelegate:
-(void) asyncConnectionRequst:(void(^)(id result)completionHandler
{
// Setup and start the connection:
self.connection = ...
if (!self.connection) {
NSError* error = [[NSError alloc] initWithDomain:#"Me"
code:-1234
userInfo:#{NSLocalizedDescriptionKey: #"Could not create NSURLConnection"}];
completionHandler(error);
});
return;
}
dispatch_suspend(self.handlerQueue); // a serial dispatch queue, now suspended
dispatch_async(self.handlerQueue, ^{
completionHandler(self.result);
});
[self.connection start];
}
Then in the NSURLConnectionDelegate, dispatch a the handler and resume the
handler queue:
- (void) connectionDidFinishLoading:(NSURLConnection*)connection {
self.result = self.responseData;
dispatch_resume(self.handlerQueue);
dispatch_release(_handlerQueue), _handlerQueue = NULL;
}
Likewise when an error occurred:
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
self.result = error;
dispatch_resume(self.handlerQueue);
dispatch_release(_handlerQueue), _handlerQueue = NULL;
}
There are even better ways, which however involve a few more basic helper classes which deal with asynchronous architectures which at the end of the day make your async code look like it were synchronous:
-(void) doFourTasksInAChainWith:(id)input
{
// This runs completely asynchronous!
self.promise = [self asyncWith:input]
.then(^(id result1){return [self auth:result1]);}, nil)
.then(^(id result2){return [self fetch:result2];}, nil)
.then(^(id result3){return [self parse:result3];}, nil)
.then(^(id result){ self.tableView.data = result; return nil;}, ^id(NSError* error){ ... })
// later eventually, self.promise.get should contain the final result
}
I am new to multithreading in iOS. I need to do three things: get information from the api, parse the information and save to my database. I have these three things in a different files(getAPI,parseAPI and savetoDB). getAPI will call parseAPI and it will in return call savetoDB. I want all three of them to work in background thread.
My question is when I call getAPI, will parseAPI and savetoDB run in the background thread as well? How do I ensure that all three of them run in the background? How do I return the call back to main thread after savetoDB?
Example:
dispatch_queue_t backgroundQueue;
backgroundQueue = dispatch_queue_create("lakesh", NULL);
- (void)startprocess {
dispatch_async(backgroundQueue, ^(void) {
[self getAPI];
});
}
Need some guidance.. Thanks...
If you issue a function on a background thread, all execution will continue on that thread until it finishes or you call back another function on the main thread. I had worries like you in the beginning, so I made myself the following macros:
/// Stick this in code you want to assert if run on the main UI thread.
#define DONT_BLOCK_UI() \
NSAssert(![NSThread isMainThread], #"Don't block the UI thread please!")
/// Stick this in code you want to assert if run on a background thread.
#define BLOCK_UI() \
NSAssert([NSThread isMainThread], #"You aren't running in the UI thread!")
As you can see by the comments, I tend to use these macros at the beginning of methods I want to make sure I'm not using by error in the wrong thread. I've put these macros and more random stuff at https://github.com/gradha/ELHASO-iOS-snippets which you may find useful.
With regards to your question on returning to the main thread, since you are using GCD the best would be to call dispatch_get_main_queue() at the end of your savetoDB with the code you want to run there. If savetoDB is a library function, its entry point should allow passing in the success block you want to run on the main thread when everything finished. This is the pattern used by libraries like https://github.com/AFNetworking/AFNetworking. Note how their examples provide an API where stuff runs in the background and then your code gets called back (usually in the main thread).
Yes, parseAPI and savetoDB will run in the new queue you have created. If you need to modify the UI when the operations are finished, that code must run in the main thread. To do that, get a reference to the main queue and send it some code. For example:
- (void)startprocess {
dispatch_async(backgroundQueue, ^(void) {
[self getAPI];
dispatch_async(dispatch_get_main_queue(), ^{
// Refresh the UI with the new information
});
});
}
Don't forget to dispatch_release your new queue when you're done with it.
Another pattern, used by Cocoa itself in many parts of the framework, is to add callback block to the signatures of your API functions that is invoked when the background operation has ended. This Stack Overflow thread explains how to do that.
Yes of course if getAPI calls parseAPI, the code of parseAPI will execute on the same thread than the one getAPI was executed, so in your example on a background queue.
To return the callback to the main thread at the end, use the same techniques as Apple uses with their completionBlock you can see on multiple Apple APIs : simply pass a block (e.g. dispatch_block_t or void(^)(NSError*) or whatever fits your needs) as a parameter to your getAPI: method which will pass it to parseAPI: which will in turn pass it to savetoDB: and at the end savetoDB: can simply use dipatch_async(dispatch_get_main_queue, completionBlock); to call this block of code (passed from method to method) on the main thread.
Note: for your getAPI you can use Apple's sendAsynchronousRequest:queue:completionHandler: method, that will automatically execute the request in the background then call the completion block on the indicated NSOperationQueue (NSOperationQueue uses GCD's dispatch_queue internally). See documentation on NSOperationQueue, GCD and the Concurrency Programming Guide and all the great detailed guide in Apple doc for more info.
-(void)getAPI:( void(^)(NSError*) )completionBlock
{
NSURLRequest* req = ...
NSOperationQueue* queue = [[NSOperationQueue alloc] init]; // the completionHandler will execute on this background queue once the network request is done
[NSURLConnection sendAsynchronousRequest:req queue:queue completionHandler:^(NSURLResponse* resp, NSData* data, NSError* error)
{
if (error) {
// Error occurred, call completionBlock with error on main thread
dispatch_async(dispatch_get_main_queue(), ^{ completionBlock(error); });
} else {
[... parseAPI:data completion:completionBlock];
}
}];
}
-(void)parseAPI:(NSData*)dataToParse completion:( void(^)(NSError*) )completionBlock
{
... parse datatToParse ...
if (parsingError) {
dispatch_async(dispatch_get_main_queue(), ^{ completionBlock(error); });
} else {
[... savetoDB:dataToSave completion:completionBlock];
}
}
-(void)savetoDB:(id)dataToSave completion:( void(^)(NSError*) )completionBlock
{
... save to your DB ...
// Then call the completionBlock on main queue / main thread
dispatch_async(dispatch_get_main_queue(), ^{ completionBlock(dbError); }); // dbError may be nil if no error occurred of course, that will tell you everything worked fine
}
-(void)test
{
[... getAPI:^(NSError* err)
{
// this code will be called on the main queue (main thread)
// err will be nil if everythg went OK and vontain the error otherwise
}];
}