I'm getting occasional crashes in my GCDWebServer handlers, which access mutable dictionaries. The GCDWebServer ReadMe says the handlers "are executed on arbitrary threads within GCD so special attention must be paid to thread-safety and re-entrancy," and I think that's my problem. Is there a best practice or recommended pattern for accessing mutable properties of the parent object from the handlers?
I don't know if I can synchronize between threads since I'm not creating the handler threads. Also, I imagine I could use an asynchronous handler, then call a method on the main thread from there, then do my work in that method, then send the response, but that seems more complicated and less efficient than necessary.
Here's a simplified version of my code:
#property (nonatomic, strong) NSMutableDictionary *data;
#property (nonatomic, strong) GCDWebServer *webServer;
- (void)setup {
self.data = [NSMutableDictionary dictionary];
[self.data setObject:#"1" forKey:#"status"];
self.webServer = [[GCDWebServer alloc] init];
[self.webServer addHandlerForMethod:#"GET" path:#"/getStatus.txt" requestClass:[GCDWebServerRequest class] processBlock:^(GCDWebServerRequest *request) {
return [self handleStatusRequest:request];
}];
}
- (GCDWebServerDataResponse *)handleStatusRequest:(GCDWebServerRequest *)request {
NSString *status = [self.data objectForKey:#"status"]; // crash here
return [GCDWebServerDataResponse responseWithText:status];
}
Are you mutating your data dictionary after creating it? If so that would explain the crashes.
You must prevent concurrent access to your data dictionary by using locks. The easiest way is through GCD e.g.
#property dispatch_queue_t lock;
__block NSString* status;
dispatch_sync(self.lock, ^{
status = [self.data objectForKey:#"status"];
});
NSString* status = #"Hello";
dispatch_async(self.lock, ^{
[self.data setObject:status forKey:#"status"];
}); // Use dispatch_sync() or dispatch_async() here depending on your needs
Related
#interface ViewController ()
#property (nonatomic, strong) NSString *someString;
#end
#implementation ViewController
#synthesize someString = _someString;
- (NSString *)someString {
__block NSString *tmp;
dispatch_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
tmp = _someString;
});
return tmp;
}
- (void)setSomeString:(NSString *)someString {
__block NSString *tmp;
dispatch_barrier_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
tmp = someString;
});
_someString = tmp;
}
#end
some said it's better than #synchronized way because all the locking is handled down in GCD.
First off, your setter makes no sense at all, and using the default concurrent queue is also probably not what you want. Your code should probably look more like:
#interface ViewController ()
#property (nonatomic, copy) NSString *someString;
#end
#implementation ViewController
{
dispatch_queue_t _stateGuardQueue;
}
- (instancetype)init
{
if (self = [super init])
{
_stateGuardQueue = dispatch_queue_create(NULL, DISPATCH_QUEUE_CONCURRENT);
}
return self;
}
#synthesize someString = _someString;
- (NSString *)someString {
__block NSString *tmp;
dispatch_sync(_stateGuardQueue, ^{
tmp = _someString;
});
return tmp;
}
- (void)setSomeString:(NSString *)someString {
NSString* tmp = [someString copy];
dispatch_barrier_async(_stateGuardQueue, ^{
_someString = tmp;
});
}
#end
The changes I made:
Make the setter actually do the mutation inside the critical section
Use a private, per-instance concurrent queue instead of the global default concurrent queue; Submitting barrier blocks to default concurrent queues doesn't do what you think it does. (See the docs)
Change dispatch_barrier_sync to dispatch_barrier_async there's no point at all in waiting synchronously for the setter block to return, as there's no way to get a stale read on the current thread.
Change the property to have copy semantics, which is always good practice with value-semantic types (NSString, etc.) This is especially important in cases where the property might be read concurrently from multiple threads.
The thing to know is that, in isolation, this pattern provides no more "safety" than atomic properties, so you should arguably just use those (less code, etc). As to the performance question, yes, for this particular use, GCD will certainly out-perform #synchronized. For one, it allows concurrent reads, where #synchronized will serialize concurrent reads. Without testing, I would expect atomic properties to out-perform both. That said atomic properties, and protecting single operations in this way in general, are rarely an adequate concurrency strategy.
why not like below ,using async method:
- (NSString *)someString {
__block NSString *tmp;
dispatch_async(_stateGuardQueue, ^{
tmp = _someString;
});
return tmp;
}
I am performing multiple read operations on the same resource stored on disk.
Sometimes the read operation itself takes longer than the time between requests for that same resource. In those cases it would make sense to batch the read operations into one read request from the disk and then return the same result to the various requester.
Initially I tried caching the result from the initial fetch resource request - but this didn't work because the time it took to read the resource was too long, and new requests came in - meaning that they would try to fetch the resource as well.
Is it possible to "append" the additional requests to the ones that are already in progress?
The code I have now follows this basic structure (which isn't good enough):
-(void)fileForKey:(NSString *)key completion:(void(^)(NSData *data) {
NSData *data = [self.cache threadSafeObjectForKey:key];
if (data) {
// resource is cached - so return it - no need to read from the disk
completion(data);
return;
}
// need to read the resource from disk
dispatch_async(self.resourceFetchQueue, ^{
// this could happen multiple times for the same key - because it could take a long time to fetch the resource - all the completion handlers should wait for the resource that is fetched the first time
NSData *fetchedData = [self fetchResourceForKey:key];
[self.cache threadSafeSetObject:fetchedData forKey:key];
dispatch_async(self.completionQueue, ^{
completion(fetchedData);
return;
});
});
}
I think you want to introduce a helper object
#interface CacheHelper{
#property (nonatomic, copy) NSData *data;
#property (nonatomic, assign) dispatch_semaphore_t dataReadSemaphore;
}
Your reader method now becomes something like
CacheHelper *cacheHelper = [self.cache threadSafeObjectForKey:key]
if (cacheHelper && cacheHelper.data)
{
completion(cacheHelper.data);
return;
}
if (cacheHelper)
{
dispatch_semaphore_wait(cacheHelper.dataReadSemaphore, DISPATCH_TIME_FOREVER);
dispatch_semaphore_signal(cacheHelper.dataReadSemaphore);
completion(cacheHelper.data)
return;
}
cacheHelper = [[CacheHelper alloc] init]
cacheHelper.dataReadSemaphore = dispatch_semaphore_create(0);
cacheHelper.data = [self fetchResourceForKey:key];
[self.cache threadSafeSetObject:cacheHelper forKey:key];
dispatch_semaphore_signal(cacheHelper.dataReadSemaphore);
completion(cacheHelper.data)
This is uncompiled code, so please check the spelling and logic, but I hope it explains the idea. I like this post if you want an introduction to semaphores.
While building my app, Marco Polo (getmarcopolo.com), I found that one of the most challenging parts of the app was pulling data from the server without slowing down the interface and without it crashing. I've got it settled now, and wanted to share my knowledge with any other developers having the same issue.
When pulling data from a server, there are a number of factors that need to be taken into consideration:
Data integrity - No data is ever missed from the server
Data persistence - Data is cached and can be accessed even when offline
Lack of interference with the interface (main thread) - Achieved using multithreading
Speed - Achieved using thread concurrency
Lack of thread collisions - Achieved using serial thread queues
So the question is, how do you achieve all 5?
I've answered this below, but would love to hear feedback on how to improve the process (with this example), as I feel it is not very easy to find in one place right now.
I'll be using the example of refreshing the marco's in the notification feed. I'll also be referring to Apple's GCD library (see https://developer.apple.com/library/mac/documentation/Performance/Reference/GCD_libdispatch_Ref/Reference/reference.html). First, we create a singleton (see http://www.galloway.me.uk/tutorials/singleton-classes/):
#implementation MPOMarcoPoloManager
+ (MPOMarcoPoloManager *)instance {
static MPOMarcoPoloManager *_instance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_instance = [[self alloc] init];
});
return _instance;
}
#end
This allows for us to call [MPOMarcoPoloManager instance] at any time, from any file, and access the properties in the the singleton. It also ensures that there is always only one instance of the marco polos. 'static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{' ensures thread stability.
The next step is to add the data structure we will be accessing publicly. In this case, add an NSArray for the marcos to the header file, as well as a public declaration of 'instance':
#interface MPOMarcoPoloManager : NSObject
+ (MPOMarcoPoloManager *)instance;
#property (strong, nonatomic) NSArray *marcoPolos;
#end
Now that the array and the instance are accessible publicly, it's time to ensure data persistence. We will achieve this by adding the ability to cache the data. The following code will
1. Initializes our serverQueue to the global queue, which allows multiple threads to run concurrently
2. Initializes our localQueue to a serial queue, which allows only one thread to be run at a time. All local data manipulation should be done on this thread to ensure no thread collisions
3. Gives us a method to call for caching our NSArray, with objects that conform to NSCoding (see http://nshipster.com/nscoding/)
4. Attempts to pull the data structure from the cache, and initializes a new one if it cannot
#interface MPOMarcoPoloManager()
#property dispatch_queue_t serverQueue;
#property dispatch_queue_t localQueue;
#end
#implementation MPOMarcoPoloManager
+ (MPOMarcoPoloManager *)instance {
static MPOMarcoPoloManager *_instance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_instance = [[self alloc] init];
});
return _instance;
}
- (id)init {
self = [super init];
if (self) {
_marcoPolos = [NSKeyedUnarchiver unarchiveObjectWithFile:self.marcoPolosArchivePath];
if(!self.marcoPolos) {
_marcoPolos = [NSArray array];
}
//serial queue
_localQueue = dispatch_queue_create([[NSBundle mainBundle] bundleIdentifier].UTF8String, NULL);
//Parallel queue
_serverQueue = dispatch_queue_create(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), NULL);
}
return self;
}
- (NSString *)marcoPolosArchivePath {
NSArray *cacheDirectories = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
NSString *cacheDirectory = [cacheDirectories objectAtIndex:0];
return [cacheDirectory stringByAppendingFormat:#"marcoPolos.archive"];
}
- (BOOL)saveChanges {
BOOL success = [NSKeyedArchiver archiveRootObject:self.marcoPolos toFile:[self marcoPolosArchivePath]];
return success;
}
#end
Now that we have the structure of the singleton, It's time to add the ability to refresh our marco's. Add the declarations of refreshMarcoPolosInBackgroundWithCallback:((^)(NSArray *result, NSError *error)) to the header file:
...
- (void)refreshMarcoPolosInBackground:((^)(NSArray *result, NSError *error))callback;
...
Now it's time to implement the refresh. Notice that all server calls are performed on the serverQueue (which is parallel), and any data manipulation is done on the localQueue (which is serial). When the method is completed, we use what is called a C block (see https://developer.apple.com/library/ios/documentation/cocoa/Conceptual/Blocks/Articles/00_Introduction.html) to callback the result to the main thread. Any task that acts on a background thread should have a callback to the main thread to inform the interface that the refresh has completed (whether it be successful or not).
...
- (void)refreshMarcoPolosInBackground:((^)(NSArray *result, NSError *error))callback {
//error checking ommitted
//Run the server call on the global parallel queue
dispatch_async(_serverQueue, ^{
NSArray *objects = nil;
NSError *error = nil;
//This can be any method with the declaration "- (NSArray *)fetchMarcoPolo:(NSError **)callbackError" that connects to a server and returns objects
objects = [self fetchMarcoPolo:&error];
//If something goes wrong, callback the error on the main thread and stop
if(error) {
dispatch_async(dispatch_get_main_queue(), ^{
callback(nil, error);
});
return;
}
//Since the server call was successful, manipulate the data on the serial queue to ensure no thread collisions
dispatch_async(_localQueue, ^{
//Create a mutable copy of our public array to manipulate
NSMutableArray *mutableMarcoPolos = [NSMutableArray arrayWithArray:_marcoPolos];
//PFObject is a class from Parse.com
for(PFObject *parseMarcoPoloObject in objects) {
BOOL shouldAdd = YES;
MPOMarcoPolo *marcoPolo = [[MPOMarcoPolo alloc] initWithParseMarcoPolo:parseMarcoPoloObject];
for(int i = 0; i < _marcoPolos.count; i++) {
MPOMarcoPolo *localMP = _marcoPolos[i];
if([marcoPolo.objectId isEqualToString:localMP.objectId]) {
//Only update the local model if the object pulled from the server was updated more recently than the local object
if((localMP.updatedAt && [marcoPolo.updatedAt timeIntervalSinceDate:localMP.updatedAt] > 0)||
(!localMP.updatedAt)) {
mutableMarcoPolos[i] = marcoPolo;
} else {
NSLog(#"THERE'S NO NEED TO UPDATE THIS MARCO POLO");
}
shouldAdd = NO;
break;
}
}
if(shouldAdd) {
[mutableMarcoPolos addObject:marcoPolo];
}
}
//Perform any sorting on mutableMarcoPolos if needed
//Assign an immutable copy of mutableMarcoPolos to the public data structure
_marcoPolos = [NSArray arrayWithArray:mutableMarcoPolos];
dispatch_async(dispatch_get_main_queue(), ^{
callback(marcoPolos, nil);
});
});
});
}
...
You may be wondering why we would manipulate the data on a queue for something like this, but lets add a method where we can mark the marco as viewed. We don't want to have to wait for the server to update, but we also don't want to manipulate the local object in a manor that can cause a thread collision. So let's add this declaration to the header file:
...
- (void)setMarcoPoloAsViewed:(MPOMarcoPolo *)marcoPolo inBackgroundWithlocalCallback:((^)())localCallback
serverCallback:((^)(NSError *error))serverCallback;
...
Now it's time to implement the method. Notice that the local manipulation is done on the serial queue, then immediately calls back to the main thread, allowing the interface to update without waiting for a server connection. It then updates the server, and calls back to the main thread on a separate callback to inform the interface that the server save was completed.
- (void)setMarcoPoloAsViewed:(MPOMarcoPolo *)marcoPolo inBackgroundWithlocalCallback:(MPOOrderedSetCallback)localCallback
serverCallback:(MPOErrorCallback)serverCallback {
//error checking ommitted
dispatch_async(_localQueue, ^{
//error checking ommitted
//Update local marcoPolo object
for(MPOMarcoPolo *mp in self.marcoPolos) {
if([mp.objectId isEqualToString:marcoPolo.objectId]) {
mp.updatedAt = [NSDate date];
//MPOMarcoPolo objcts have an array viewedUsers that contains all users that have viewed this marco. I use parse, so I'm going to add a MPOUser object that is created from [PFUser currentUser] but this can be any sort of local model manipulation you need
[mp.viewedUsers addObject:[[MPOUser alloc] initWithParseUser:[PFUser currentUser]]];
//callback on the localCallback, so that the interface can update
dispatch_async(dispatch_get_main_queue(), ^{
//code to be executed on the main thread when background task is finished
localCallback(self.marcoPolos, nil);
});
break;
}
}
});
//Update the server on the global parallel queue
dispatch_async(_serverQueue, ^{
NSError *error = nil;
PFObject *marcoPoloParseObject = [marcoPolo parsePointer];
[marcoPoloParseObject addUniqueObject:[PFUser currentUser] forKey:#"viewedUsers"];
//Update marcoPolo object on server
[marcoPoloParseObject save:&error];
if(!error) {
//Marco Polo has been marked as viewed on server. Inform the interface
dispatch_async(dispatch_get_main_queue(), ^{
serverCallback(nil);
});
} else {
//This is a Parse feature that your server's API may not support. If it does not, just callback the error.
[marcoPoloParseObject saveEventually];
NSLog(#"Error: %#", error);
dispatch_async(dispatch_get_main_queue(), ^{
serverCallback(error);
});
}
});
}
With this setup, a refresh can be occuring the background, while setting a marco as viewed at the same time, while ensuring that the local model is not manipulated at the same time. While the necessity of the localQueue may not be obvious with only two methods, when having many different types of manipulation available, it becomes critical.
I use a dataManager that contains two sub managers, core data fetch manager and restkit manager, which maps to core data.
for example:
anywhereInApp.m
[dataManager getData: someSearchPrecate withCompletionBlock: someBlock];
dataManager.m
- (void) getData: somePredicate withCompletionBlock: someblock{
[self.coreDataManager fetchData: somePredicate withCompletionBlock: some block];
[self.restkitManager fetchData: somePredicate withCompletionBlock: some block];
}
and then core data manger runs on a thread to fetch data and executes completion block.
and reskitmanager runs a thread and executes completion block when http request and object mapping complete.
usually the completion block updates the data shown in a collection view.
only need to worry about old data getting removed from core data, but that's another story and can involve comparing the results from the two different calls and taking appropriate action. I try to picture a venn diagram of result sets and it all makes sense or I am too tired & drinking too good of beer.
I would like to do the following:
In a class (shared instance) I will have a method that takes as parameters a data object (nsstring) and a delegate. This will create a new background thread and dispatch some calculations on that thread. The thing is that the method may be called hundreds of times with different data and possibly different delegates passed in . I would like the results to go to the correct delegate (I will need to keep the delegates in an array right? or can I just pass them to the background thread as they come and when that thread finishes it will send the result only to that delegate?).
One more thing... all this methods will use a very large data structure (an array with 10000 nsstring objects,they only need to read from it). How do I make sure this is not duplicated on each thread? And is only allocated when needed and deallocated when no thread uses it?
Here is the code I decided to use:
if (!self.dictPasswords) {
// read everything from text
NSString* fileContents = [NSString stringWithContentsOfFile:fileRoot
encoding:NSUTF8StringEncoding error:nil];
// separate by new line
self.dictPasswords = [fileContents componentsSeparatedByCharactersInSet:
[NSCharacterSet newlineCharacterSet]];
}
__weak id<PSPasswordDictionaryVerificationDelegate> wdelegate = delegate;
dispatch_async(dispatch_get_global_queue(0, 0), ^{
[wdelegate willBeginPasswordVerificationForPassword:password];
for (NSString *posiblePass in self.dictPasswords) {
if ([password isEqualToString:posiblePass]) {
dispatch_async(dispatch_get_main_queue(), ^{
[wdelegate password:password isInDictionary:YES];
});
return;
}
}
dispatch_async(dispatch_get_main_queue(), ^{
[wdelegate password:password isInDictionary:NO];
});
});
However... after this runs I get a permanent 24MB added to the used memory. I would like to detect when no threads are using the self.DIctPasswords array and deallocate it. It will be read from the file again later if somebody calls this method again...
Thanks for the help guys.
Just let the block capture the delegate.
No need to hold it otherwise
Class
#import <Foundation/Foundation.h>
#protocol ProcessorDelegate;
#interface Processor
- (void)process:(id)data forDelegate:(id<ProcessorDelegate>)delegate;
+ (Processor*)sharedInstance;
#end
#protocol ProcessorDelegate
- (void)processor:(Processor*)processor didProcess:(id)data withResult:(id)result;
#end
#implementation Processor
- (void)process:(id)data forDelegate:(id<ProcessorDelegate>)delegate {
__weak id<ProcessorDelegate> wdelegate = delegate; //capture weak to counter potential cycles
__weak id wself = self;
dispatch_async(dispatch_get_global_queue(0,0), ^{
NSLog(#"WORK");
id result = data; //TODO
[wdelegate processor:wself didProcess:data withResult:result];
});
}
+ (Processor*)sharedInstance {
static Processor *p = nil;
if(!p) {
p = [[Processor alloc] init];
}
return p;
}
#end
DEMO
#interface Demo : NSObject <ProcessorDelegate>
- (void)doIt;
#end
#implementation Demo
- (void)doIt {
[Processor sharedInstance] process:#"TEST" forDelegate:self];
}
#end
int main(int argc, char *argv[]) {
#autoreleasepool {
Demo *d1 = [[Demo alloc] init];
Demo *d2 = [[Demo alloc] init];
Demo *d3 = [[Demo alloc] init];
Demo *d4 = [[Demo alloc] init];
[d1 doIt];
[d2 doIt];
[d3 doIt];
[d4 doIt];
[[NSRunLoop currentRunLoop] run];
}
}
It seems more appropriate to encapsulate the calculations plus data and delegate in a class of its own. Then you can have an array of those objects in your singleton. You may want to consider using NSOperation here.
OMT: Simply pass this large array as a pointer (to each calculation object) and use regular strong properties (not copy) if you're using any properties at all, saving a reference to it using an ivar is fine too. One concern is that this data-structure must be read-only; otherwise (when you'd modify it in each thread), you'd need some data locking.
I have done it with blocks : a singleton that have all the functions you needs (like an API) and a delegate class
// singleton.h
typedef void (^request_handler_t)(NSData* data);
- (void) foo:(NSString *)str withBlock:(request_handler_t)callback;
// singleton.m
- (void) foo:(NSString *)str withBlock:(request_handler_t)callback;{
MyDelegate *delegate = [MyDelegate delegateWithBlock:callback];
[yourMethodThatNeedDelegate:delegate];
}
// MyDelegate.h
+ (MyDelegate*) delegateWithBlock:(api_request_handler_t)block;
- (void)delegateMethod1;
//Delegate.m
+ (MyDelegate*) requestWithBlock:(api_request_handler_t)block;{
//... alloc init
_callback = block;
}
- (void)delegateMethod1;{
// delegate finished the job
block(myResultingData);
}
// Usage :
[MySingleton singleton] foo:(NSString *)str withBlock:^(NSData *data){
//do something with the async data
}];
I get this warning in Xcode
warning: Attempting to create USE_BLOCK_IN_FRAME variable with block
that isn't in the frame.
Xcode redirect me to my NSStream
_naturStream = [[NSInputStream alloc] initWithData:natur];
It is random when it does this error, and my application crashes when it is triggered. Anyone tried similar problem ?
thanks
EDIT
in the appDelegate.h
#property (nonatomic, strong) NSInputStream *naturStream;
In the appDelegate.m:
NSData *natur = [NSData dataWithContentsOfURL:[NSURL URLWithString:_locString]];
_naturStream = [[NSInputStream alloc] initWithData:natur];
[_naturStream open];
if (_naturStream) {
NSError *parseError = nil;
id jsonObject = [NSJSONSerialization JSONObjectWithStream:_naturStream options:NSJSONReadingAllowFragments error:&parseError];
if ([jsonObject respondsToSelector:#selector(objectForKey:)]) {
for (NSDictionary *natur in [jsonObject objectForKey:#"results"]) {
_poi = [[POI alloc]init];
[_poi setTitle:[natur objectForKey:#"title"]];
[_poi setLat:[[natur objectForKey:#"lat"]floatValue]];
[_poi setLon:[[natur objectForKey:#"lng"]floatValue]];
[_poi setDistance:[natur objectForKey:#"distance"]];
[_poi setWebUrl:[natur objectForKey:#"webpage"]];
[_naturArray addObject:_poi];
}
}
}
else {
NSLog(#"Failed to open stream.");
}
[_naturStream close];
}
I realized that i forgot [_naturStream close] i don't know if it has solved the problem or not ?
EDIT
Another thing,.... I use a Thread for fetching the JSON data:
dispatch_queue_t jsonParsingQueue = dispatch_queue_create("jsonParsingQueue", NULL);
// execute a task on that queue asynchronously
dispatch_async(jsonParsingQueue, ^{
[self parseJSON];
dispatch_async(dispatch_get_main_queue(), ^{
[_kortvisning updateAnno];
[visListe updateList];
});
});
// release the dispatch queue
dispatch_release(jsonParsingQueue);
Sounds like you're using ARC - if _naturStream is an instance variable for an objective C class, you might need to pull it out and add a __block reference so that ARC knows the scope correctly - but I'm guessing because I don't see how the block is used with the NSInputStream (if you post that part we might know). A good bit is here: http://nachbaur.com/blog/using-gcd-and-blocks-effectively
-- edit --
Ok, now that you posted the rest, I bet it has to do with the _kortvisning and visListe variables. I think you want to pull those out right after you create your queue something like
__block KortVisning *localKortVisning = _kortvisning;
__block NSMutableArray *localVisListe = visListe;
Then access those directly from your final completion handler you're sending back to the main queue.