Asynchronous callback for network in Objective-C Iphone - iphone

I am working with network request - response in Objective-C. There is something with asynchronous model that I don't understand.
In summary, I have a view that will show my statuses from 2 social networks: Twitter and Facebook. When I clicked refresh, it will call a model manager. That model manager will call 2 service helpers to request for latest items. When 2 service helpers receive data, it will pass back to model manager and this model will add all data into a sorted array.
What I don't understand here is that : when response from social networks come back, how many threads will handle the response. From my understanding about multithreading and networking (in Java), there must have 2 threads handle 2 responses and those 2 threads will execute the code to add the responses to the array. So, it can have race condition and the program can go wrong right? Is it the correct working model of iphone objective-C? Or they do it in a different way that it will never have race condition and we don't have to care about locking, synchronize?
Here is my example code:
ModelManager.m
- (void)updateMyItems:(NSArray *)items {
self.helpers = [self authenticatedHelpersForAction:NCHelperActionGetMyItems];
for (id<NCHelper> helper in self.helpers) {
[helper updateMyItems:items]; // NETWORK request here
}
}
- (void)helper:(id <NCHelper>)helper didReturnItems:(NSArray *)items {
[self helperDidFinishGettingMyItems:items callback:#selector(model:didGetMyItems:)];
break;
}
}
// some private attributes
int *_currentSocialNetworkItemsCount = 0; // to count the number of items of a social network
- (void)helperDidFinishGettingMyItems:(NSArray *)items {
for (Item *item in items) {
_currentSocialNetworkItemsCount ++;
}
NSLog(#"count: %d", _currentSocialNetworkItemsCount);
_currentSocialNetworkItemsCount = 0;
}
I want to ask if there is a case that the method helperDidFinishGettingMyItems is called concurrently. That means, for example, faceboook returns 10 items, twitter returns 10 items, will the output of count will ever be larger than 10?
And if there is only one single thread, how can the thread finishes parsing 1 response and jump to the other response because, IMO, thread is only executed sequently, block of code by block of code

Yes, there is probably a thread per network request. The trick is to handle the response on the main thread. You should have something like this:
- (void)helper:(id <NCHelper>)helper didReturnItems:(NSArray *)items;
{
[self performSelectorOnMainThread:#selector(helperDidFinishGettingMyItems:)
withObject:items
waitUntilDone:NO];
}
Putting the response back onto the main thread will avoid a whole bunch of multithreading problems.
Also, the output of count will never be larger than 10. It's just that multiple threads may be running helperDidFinishGettingMyItems: at the same time. They won't automatically combine the two arrays.
The counter could possibly be more than 10, because multiple threads could be increasing that ivar at the same time.

Related

Variable Multithread Access - Corruption

In a nutshell:
I have one counter variable that is accessed from many threads. Although I've implemented multi-thread read/write protections, the variable seems to still -in an inconsistent way- get written to simultaneously, leading to incorrect results from the counter.
Getting into the weeds:
I'm using a "for loop" that triggers roughly 100 URL requests in the background, each in its “DispatchQueue.global(qos: .userInitiated).async” queue.
These processes are async, once they finish they update a “counter” variable. This variable is supposed to be multi-thread protected, meaning it’s always accessed from one thread and it’s accessed syncronously. However, something is wrong, from time to time the variable will be accessed simultaneously by two threads leading to the counter not updating correctly. Here's an example, lets imagine we have 5 URLs to fetch:
We start with the Counter variable at 5.
1 URL Request Finishes -> Counter = 4
2 URL Request Finishes -> Counter = 3
3 URL Request Finishes -> Counter = 2
4 URL Request Finishes (and for some reason – I assume variable is accessed at the same time) -> Counter 2
5 URL Request Finishes -> Counter = 1
As you can see, this leads to the counter being 1, instead of 0, which then affects other parts of the code. This error happens inconsistently.
Here is the multi-thread protection I use for the counter variable:
Dedicated Global Queue
//Background queue to syncronize data access fileprivate let
globalBackgroundSyncronizeDataQueue = DispatchQueue(label:
"globalBackgroundSyncronizeSharedData")
Variable is always accessed via accessor:
var numberOfFeedsToFetch_Value: Int = 0
var numberOfFeedsToFetch: Int {
set (newValue) {
globalBackgroundSyncronizeDataQueue.sync() {
self.numberOfFeedsToFetch_Value = newValue
}
}
get {
return globalBackgroundSyncronizeDataQueue.sync {
numberOfFeedsToFetch_Value
}
}
}
I assume I may be missing something but I've used profiling and all seems to be good, also checked the documentation and I seem to be doing what they recommend. Really appreciate your help.
Thanks!!
Answer from Apple Forums:https://forums.developer.apple.com/message/322332#322332:
The individual accessors are thread safe, but an increment operation
isn't atomic given how you've written the code. That is, while one
thread is getting or setting the value, no other threads can also be
getting or setting the value. However, there's nothing preventing
thread A from reading the current value (say, 2), thread B reading the
same current value (2), each thread adding one to this value in their
private temporary, and then each thread writing their incremented
value (3 for both threads) to the property. So, two threads
incremented but the property did not go from 2 to 4; it only went from
2 to 3. You need to do the whole increment operation (get, increment
the private value, set) in an atomic way such that no other thread can
read or write the property while it's in progress.

Simple waiting for value from async thread

I've got 2 basic methods - viewDidLoad and viewDidAppear. According to my App philosophy, when view controller loads, it fetches data from base and starts to sort it with some predicates. Fetching process is long, so I dispatched it to global queue. When my view appears, it obviously do not get the value from array(which compiles in load method) and crashes. So I need viewDidAppear to wait till at least one object will be appended to array.
Kind of semaphores or temp values?
Thanks in advance!
P.S. Each item in array represent struct with data which composes UI. User interact with this UI, so it has to be loaded once with the first item from array. To switch to next item, user just clicks "next" and UI changes according to next item from array. That's why I want the data to fetch in background and allow user to work immediately. (It's impossible to jump on 5th, 10th or 1001st element immediately, there will be enough time to fetch data before user gets on these page numbers)
P.P.S Still no right decision :(
You should using a nested dispatch block, like so:
func fetch(completion block:(() -> Void)?) {
// Run fetch on background thread, to prevent the main thread (and hence your UI) from being 'blocked'.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), {
//
// Fetch data...
//
dispatch_async(dispatch_get_main_queue(), {
block?()
})
})
}
fetch(completion: {
// Update your UI
})

Play 1.2.3 framework - Right way to commit transaction

We have a HTTP end-point that takes a long time to run and can also be called concurrently by users. As part of this request, we update the model inside a synchronized block so that other (possibly concurrent) requests pick up that change.
E.g.
MyModel m = null;
synchronized (lockObject) {
m = MyModel.findById(id);
if (m.status == PENDING) {
m.status = ACTIVE;
} else {
//render a response back to user that the operation is not allowed
}
m.save(); //Is not expected to be called unless we set m.status = ACTIVE
}
//Long running operation continues here. It can involve further changes to instance "m"
The reason for the synchronized block is to ensure that even concurrent requests get to pick up the latest status. However, the underlying JPA does not commit my changes (m.save()) until the request is complete. Since this is a long-running request, I do not want to wait until the request is complete and still want to ensure that other callers are notified of the change in status. I tried to call "m.em().flush(); JPA.em().getTransaction().commit();" after m.save(), but that makes the transaction unavailable for the subsequent action as part of the same request. Can I just given "JPA.em().getTransaction().begin();" and let Play handle the transaction from then on? If not, what is the best way to handle this use-case?
UPDATE:
Based on the response, I modified my code as follows:
MyModel m = null;
synchronized (lockObject) {
m = MyModel.findById(id);
if (m.status == PENDING) {
m.status = ACTIVE;
} else {
//render a response back to user that the operation is not allowed
}
m.save(); //Is not expected to be called unless we set m.status = ACTIVE
}
new MyModelUpdateJob(m.id).now();
And in my job, I have the following line:
doJob() {
MyModel m = MyModel.findById(id);
print m.status; //This still prints the old status as-if m.save() had no effect...
}
What am I missing?
Put your update code in a job an call
new MyModelUpdateJob(id).now().get();
thus the update will be done in another transaction that is commited at the end of the job
ouch, as soon as you add more play servers, you will be in trouble. You may want to play with optimistic locking in your example or and I advise against it pessimistic locking....ick.
HOWEVER, looking at your code, maybe read the article Building on Quicksand. I am not sure you need a synchronized block in that case at all...try to go after being idempotent.
In your case if
1. user 1 and user 2 both call that method and it is pending, then it goes to active(Idempotent)
If user 1 or user 2 wins, well that would be like you had the synchronization block anyways.
I am sure however you have a more complex scenario not shown here, BUT READ that article Building on Quicksand as it really changes the traditional way of thinking and is how google and amazon and very large scale systems operate.
Another option for distributed transactions across play servers is zookeeper which the big large nosql guys use BUT only as a last resort ;) ;)
later,
Dean

concurrent background downloads on iphone

I am trying to create class that will handle multiple downloads at same time (I need to download a lot of small files) and I have problems with "disappearing" connections.
I have function addDonwload that adds url to list of urls to download, and checks if there is free download slot available. If there is one it starts download immediately. When one of downloads finishes, I pick first url form list and start new download.
I use NSURLConnection for downloading, here is some code
- (bool) TryDownload:(downloadInfo*)info
{
int index;
#synchronized(_asyncConnection)
{
index = [_asyncConnection indexOfObject:nullObject];
if(index != NSNotFound)
{
NSLog(#"downloading %# at index %i", info.url, index);
activeInfo[index] = info;
NSURLRequest *request = [NSURLRequest requestWithURL:info.url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:15];
[_asyncConnection replaceObjectAtIndex:index withObject:[[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:TRUE]];
//[[_asyncConnection objectAtIndex:i] scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
return true;
}
}
return false;
}
- (void)connectionDidFinishLoading:(NSURLConnection*)connection
{
[self performSelectorOnMainThread:#selector(DownloadFinished:) withObject:connection waitUntilDone:false];
}
- (void)DownloadFinished:(id)connection
{
NSInteger index = NSNotFound;
#synchronized(_asyncConnection)
{
index = [_asyncConnection indexOfObject:(NSURLConnection*)connection];
}
[(id)activeInfo[index].delegate performSelectorInBackground:#selector(backgroundDownloadSucceededWithData:) withObject:_data[index]];
[_data[index] release];
[activeInfo[index].delegate release];
#synchronized(_asyncConnection)
{
[[_asyncConnection objectAtIndex:index] release];
[_asyncConnection replaceObjectAtIndex:index withObject:nullObject];
}
#synchronized(downloadQueue)
{
[downloadQueue removeObject:activeInfo[index]];
[self NextDownload];
}
}
- (void)NextDownload
{
NSLog(#"files remaining: %i", downloadQueue.count);
if(downloadQueue.count > 0)
{
if([self TryDownload:[downloadQueue objectAtIndex:0]])
{
[downloadQueue removeObjectAtIndex:0];
}
}
}
_asyncConnection is my array of download slots (NSURLConnections)
downloadQueue is list of urls to download
What happens is, at the beginning everything works ok, but after few downloads my connections start to disappear. Download starts but connection:didReceiveResponse: never gets called. There is one thing in output console that I don't understand I that might help a bit. Normaly there is something like
2010-01-24 21:44:17.504 appName[3057:207]
before my NSLog messages. I guess that number in square brackets is some kind of app:thread id? everything works ok while there is same number, but after some time, "NSLog(#"downloading %# at index %i", info.url, index);" messages starts having different that second number. And when that happens, I stop receiving any callbacks for that urlconnection.
This has been driving me nuts as I have strict deadlines and I can't find problem. I don't have many experiences with iphone dev and multithreaded apps. I have been trying different approaches so my code is kinda messy, but I hope you will see what I am trying to do here :)
btw is anyone of you know about existing class/lib I could use that would be helpful as well. I want parallel downloads with ability o dynamically add new files to download (so initializing downloader at the beginning with all urls is not helpful for me)
You've got a bunch of serious memory issues, and thread synchronization issues in this code.
Rather than go into them all, I'll ask the following question: You are doing this on a background thread of some kind? Why? IIRC NSURLConnection already does it's downloads on a background thread and calls your delegate on the thread that the NSURLConnection was created upon (e.g., your main thread ideally).
Suggest you step back, re-read NSURLConnection documentation and then remove your background threading code and all the complexity you've injected into this unnecessarily.
Further Suggestion: Instead of trying to maintain parallel positioning in two arrays (and some sketchy code in the above relating to that), make one array and have an object that contains both the NSURLConnection AND the object representing the result. Then you can just release the connection instance var when the connection is done. And the parent object (and thus the data) when you are done with the data.
I recommend that you take a look at this:
http://allseeing-i.com/ASIHTTPRequest/
It's a pretty sophisticated set of classes with liberal licensing terms (free too).
It may provide a lot of the functionality that you are wanting.
This snippet can be the source of the bug, you release the object pointed to by the activeInfo[index].delegate pointer right after issuing async method call on that object.
[(id)activeInfo[index].delegate performSelectorInBackground:#selector(backgroundDownloadSucceededWithData:) withObject:_data[index]];
[_data[index] release];
[activeInfo[index].delegate release];
Do you use connection:didFailWithError: ? There may be a timeout that prevents the successful download completion.
Try to get rid of the #synchronized blocks and see what happens.
The string inside the square brackets seems to be thread identifier as you guessed. So maybe you get locked in the #synchronized. Actually, I don't see a reason for switching thread - all the problematic code should run in the main thread (performSelectorOnMainThread)...
Anyhow, there is no need to use both the #synchronized and the performSelectorOnMainThread.
BTW, I didn't see the NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:self]; line. Where do you initiate the connection?
As for the parallel downloads - I think that you can download more than one file in a time with the same code that you use here. Just create a separate connection for each download.
Consider just keeping a download queue along with a count of active connections, popping items off the top of the queue when downloads complete and a slot becomes free. You can then fire off NSURLConnection objects asynchronously and process events on the main thread.
If you find that your parallel approach prohibits doing all of the processing on the main thread, consider having intermediary manager objects between your main thread download code and NSURLConnection. Using that approach, you'd instantiate your manager and get it to use NSURLConnection synchronously on a background thread. That manager then completely deals with the downloading and passes the result back to its main thread delegate using a performSelectorOnMainThread:withObject: call. Each download is then just a case of creating a new manager object when you've a slot free and setting it going.

how to make asynchronous call using NSThread

i have made one list of images + respective data in tableview.
it takes long time while loading
i want to make multithreading two methods
1> parsing of data
2> parsing of images
i want to execute parsing of data first after that i can select any of rows listed even though images not been loaded(/parsed) because the images is parsed after the parsing of data and it takes long time.
from where should i call these both the methods.
and how enable the selection on row after parsing of the data...
how to do multithread both the methods
waiting for your great responce
Thanking in advance
You likely don't want to use NSThreads - at least not directly.
What you do is subclass NSOperation.
There are a few ways to do what you have in mind. If you know the total number of rows in your table right from the start, then things are simpler:
Make a subclass of NSOperation called MyParseDataOperation. Then make one MyParseDataOperation for each row in your table. When the operation is done, you need to message your main thread with the resulting data.
Code below is full of errors, incomplete. etc.
ie in your MyParseDataOperation class:
MyParseDataOperation
-(id)initWithStuff:(NSURL*)stuff forTableRow:(int)row;
{
blah blah -
// here is where I make sure I have all the data I need for main() which is called in the background on some random thread at some future time.
}
-(void)main;
{
// use data like Urls, file names, etc passed in to the initWithStuff method
get stuff
parse stuff
// ok now you have the data
NSMutableDictionary* parsedData = [NSMutableDictionary dictionary];
[parsedData setObject:[NSNumber numberWithInt:row] forKey:#"row"];
[parsedData setObject:stuff i figured out forKey:#parsed];
[tableDataSource performSelectorOnMainThread:#selector(dataParsed) withObject:parsedData];
}