Use an NSRecursive Lock to wait for an operation to finish - iphone

Would it be save to use an NSRecursiveLock to purposefully wait for an operation to complete on a background thread? Here is an example:
I have a class where I want to have a loadParts function that can be asynchronous or synchronous. The asynchronous function will possibly be called early so that the parts can be loaded before the data is actually needed. The synchronous one should check if the data has been loaded, or is being loaded currently. If it has been loaded it can just return the data; if it is currently loaded, then it should wait for it to be loaded and then return; and if it isn't even being loaded, then it should just load it synchronously. This is the code I am trying to use:
// Private function to be run either on main thread or
// background thread
-(void)_loadParts
{
[_loadingPartsLock lock];
_loadingParts = YES;
// Do long loading operation
_loadingParts = NO;
_partsLoaded = YES;
[_loadingPartsLock unlock];
}
// Asynchronous loading of parts
-(void)preloadParts
{
if( _loadingParts || _partsLoaded )
return;
[self performSelectorInBackground:#selector(_loadParts) withObject:nil];
}
// Synchronous loading of parts
-(void)loadParts
{
if( _loadingParts )
{
[_loadingPartsLock lock];
[_loadingPartsLock unlock];
}
if( !_partsLoaded )
{
[self _loadParts];
}
}
Is this safe / an efficient way to do this? I already see some possible problems with it. Is it thread safe to set and test the value of a BOOL without a lock? I am also locking twice in the synchronous function if it is called while the background thread is still loading.
Is there a more common and better way to achieve this functionality?
Thanks!

A far, far, better solution is to use a dispatch_queue or NSOperationQueue (configured for serial operation).
Enqueue your loading operations and then enqueue whatever is supposed to happen when it is done. If the "done" operation is "tell the main thread to update", that's fine -- perform a method on your main thread that is effectively an event that triggers the update in response to the now loaded data.
This avoids the issues and overhead associated with locking entirely while also solving the "is done" notification issue without requiring some kind of polling mechanism.

Inspired by bbum's answer I found a solution that uses NSOperationQueue but not quite in the way he described. In my preloadParts function I create and store a load operation that is an instance of NSInvocationOperation that runs my background thread function. I then add it to the NSOperationQueue.
If at any point, the data is requested by another class. I first check if the data is loaded (the variable is set). If not, I check if the operation is in the queue. If it is, then I call [_loadOperation waitUntilFinished]. Otherwise, I add it to the operation queue with the argument to waitUntilFinished. Here is the code I came up with:
-(void)preloadCategories
{
if( [[_operationQueue operations] containsObject:_loadOperation] )
return;
[_operationQueue addOperation:_loadOperation];
}
-(CCPart*)getCategoryForName:(NSString*)name
{
if( nil == _parts )
{
[self loadCategories];
}
return [_parts objectForKey:name];
}
-(void)loadCategories
{
if( nil != _parts )
return;
if( [[_operationQueue operations] containsObject:_loadOperation] )
{
[_loadOperation waitUntilFinished];
}
else
{
[_operationQueue addOperations:[NSArray arrayWithObject:_loadOperation]
waitUntilFinished:YES];
}
}
-(void)_loadCategories
{
// Function that actually does the loading and sets _parts to be an array of the data
_parts = [NSArray array];
}
In the initialization function I set the _operationQueue and _loadOperation as follows:
_operationQueue = [[NSOperationQueue alloc] init];
_loadOperation = [[NSInvocationOperation alloc] initWithTarget:self
selector:#selector(_loadCategories)
object:nil];

Related

What is preferred: implement method with GCD inside and then just simple call, or implement method and then call it later with GCD?

what's is more prefered way to write multi threaded apps. I see two ways.
Implement method with GCD inside and then just simple call (myMethodA), or just implement method and then call it with GCD? Thanks in advance.
My point:
ClassA / method implementation
- (void)myMethodA
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// doSomething1
// doSomething2
});
}
- (void)myMethodB
{
// doSomething1
// doSomething2
}
ClassB / method call
{
[myClassA methodA];
// or
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[myClassA methodB];
};
}
IMHO, neither.
The preferred way should be having an object which knowns where to execute its actions:
completion_block_t completionHandler = ^(id result) { ... };
AsyncOperation* op = [AsyncOperation alloc] initWithCompletion:completionHandler];
[op start]; // executes its actions on a private execution context
Then, one can wrap those AsyncOperation objects into a convenient method:
- (void) fetchUsersWithCompletion:(completion_block_t)completionHandler
{
NSDictionary* params = ...;
self.currentOperation = [[HTTPOperation alloc] initWithParams:params
completion:completionHandler];
[self.currentOperation start];
}
The client may only be interested in specifying where its completionHandler should be executed. The API may be enhanced as follows:
- (void) fetchUsersWithQueue:(NSOperationQueue*)handlerQueue
withCompletion:(completion_block_t)completionHandler
{
NSDictionary* params = ...;
self.currentOperation = [[HTTPOperation alloc] initWithParams:params
completion:^(id result){
// As per the documentation of HTTPOperation, the handler will be executed
// on an _unspecified_ execution context.
// Ensure to execute the client's handler on the specified operation queue:
[handlerQueue:addOperationWithBlock:^{
completionHandler(result);
}];
}];
[self.currentOperation start];
}
The latter API can be used as this:
[self fetchUsersWithQueue:[NSOperation mainQueue] completion:^(id result){
self.users = result;
[self.tableView reloadData];
}];
Personal preference. Choose whichever makes the code more readable / understandable / obvious. Also, consideration of whether the code should be possible to run on the 'current' thread or whether it should always be run on a background thread. You need to design your threading configuration, describe it and then implement with that in mind. If you're calling methods between classes like in your example then I'd generally say that any threading should be handled inside that class, not inside the calling class. But that's about distribution of knowledge.
It doesn't make much of a difference - it just depends on what you want to do.
If you want to execute the method on different queues each time, then the myMethodB system is more appropriate. If, however, you always want to run the method on the same queue, then myMethodA will save you time writing code (you only have to write the GCD code once).

Dispatch queues, concurrency and completion handling

I have an array of objects to be processed. The objects have a method like below
#interface CustomObject : NSObject
- (void)processWithCompletionBlock:(void (^)(BOOL success))completionBlock;
#end
The processing of each object takes various time and can have different results. And it is known that the processing itself is executing concurrently. To say the truth it would be great to limit the number of concurrent operations because they are pretty intensive.
So I need to enumerate this array of objects and process them. If some object processing fails I need to skip all the rest objects. And of course I need to be notified after all objects will be enumerated and processed.
Should it be solved by the creation of NSOperationQueue and NSOperation subclass? How this class could look to fulfill these requirements? Are there some other elegant approaches?
This is exactly what NSOperation is designed for. Dispatch queues are much lower-level handlers, and you'd have to construct many of the pieces you need for this. You can of course do that (NSOperationQueue is built on top of GCD), but you'd be reinventing NSOperation.
You can handle NSOperation two ways for the most part. If it's simple, you can just create an NSBlockOperation. If it's a bit more complex, you can subclass NSOperation and override the main method to do what you want.
There are several ways to cancel all the other operations. You could have a separate operation queue per group. Then you can easily call cancelAllOperations to shut down everything. Or you could have a separate controller that knows the list of related operations and it could call cancel on them.
Remember that "cancel" just means "don't schedule if it hasn't stared, and set isCancelled if it has." It doesn't abort a running operation. If you want to abort a running operation, the operation needs to periodically check isCancelled.
You typically should limit the number of concurrent operations the queue will run. Use setMaximimumConcurrentOperationCount:.
There are two ways to determine that all the operations are finished. You can make an extra operation (usually a BlockOperation) and use addDependency: to make it depend on all the other operations. That's a nice asynchronous solution. If you can handle a synchronous solution, then you can use waitUntilAllOperationsAreFinished. I typically prefer the former.
Use NSOperationQueue and make your Class an NSOperation
Use this method to queue your work
- (void)addOperations:(NSArray *)ops waitUntilFinished:(BOOL)wait
Add a reference to the operation queue to the NSOperation subclass you create
If an error occurs call
- (void)setSuspended:(BOOL)suspend
on NSOperationQueue
Ok. To help others to understand how this approach can be handled I am sharing my own code.
To limit the number of concurrent threads we can call the -(void)setMaximimumConcurrentOperationCount: method of NSOperationQueue instance.
To iterate objects and provide the completion mechanism we can define the following method:
- (void)enumerateObjects:(NSArray *)objects
{
// define the completion block
NSBlockOperation *completionOperation = [NSBlockOperation blockOperationWithBlock:^{
NSLog(#"Update UI");
}];
// register dependencies
for (CustomObject *obj in objects) {
CustomOperation *operation = [[CustomOperation alloc] initWithCustomObject:obj];
[completionOperation addDependency:operation]; // set dependencies for the completion block
[_operationQueue addOperation:operation];
}
// register completionOperation on main queue to avoid the cancellation
[[NSOperationQueue mainQueue] addOperation:completionOperation];
}
Overwrite the - (void)start method of the NSOperation subclass to start our custom operation:
- (void)start
{
// We need access to the operation queue for canceling other operations if the process fails
_operationQueue = [NSOperationQueue currentQueue];
if ([self isCancelled]) {
// Must move the operation to the finished state if it is canceled.
[self willChangeValueForKey:#"isFinished"];
_finished = YES;
[self didChangeValueForKey:#"isFinished"];
return;
}
[self willChangeValueForKey:#"isExecuting"];
// We do not need thread detaching because our `-(void)processWithCompletionBlock:` method already uses dispatch_async
[self main]; // [NSThread detachNewThreadSelector:#selector(main) toTarget:self withObject:nil];
_executing = YES;
[self didChangeValueForKey:#"isExecuting"];
}
Overwrite the - (void)main method of the NSOperation subclass to process our custom object:
- (void)main
{
#try {
NSLog(#"Processing object %#", _customObject);
[_customObject processWithCompletionBlock:^(BOOL success) {
_processed = success;
if (!success) {
NSLog(#"Cancelling other operations");
[_operationQueue cancelAllOperations];
}
[self completeOperation];
}];
}
#catch (NSException *exception) {
NSLog(#"Exception raised, %#", exception);
}
}
Thanx to #Rob for pointing me out to the missing part.

Thread safety: NSOperationQueue + [array addObject]

I could not find any examples how to deal with the same (class) variable when operation queue is used. In C & threads its about mutexes. So, what happens when NSOperationQueue starts a thread for operation and class variable is modified? Is it thread safe? Thank you.
#interface MyTest {
NSMutableArray *_array;
}
#end
-(id)init
{
...
_array = [NSMutableArray new]; // class variable
// queue time consuming loading
NSOperationQueue *queue = [NSOperationQueue new];
NSInvocationOperation *operation =
[NSInvocationOperation initWithTarget:self
selector:#selector(populate)
object:nil];
[queue addOperation:operation];
// start continuous processing
[NSTimer scheduledTimerWithTimeInterval:0.1
target:self
selector:#selector(processing)
userInfo:nil
repeats:YES];
...
}
-(void)populate
{
while (...)
{
id element = ...; // time consuming
// modify class variable "_array" from operation's thread (?)
[_array addObject:element];
// Ok, I can do instead of addObject
// performSelectorOnMainThread:withObject:waitUntilDone:
// but is it the only way? Is it needed?
}
}
// access and/or modify class variable "_array"
-(void)processing
{
NSLog(#"array.count = %d", array.count);
for (id i in _array)
{
[_array addObject:[NSNumber numberWithInt:rand() % 100]];
// etc...
}
}
No, this is not thread safe, if you start a thread that does some work on a class variable that can be modified by some other thread then its not thread safe, if processing is called from some thread while populate is running on another then you might get an exception when the foreach loop sees that the array has been modified, though you will get that exception anyway as you are modifying the array inside the foreach loop in your example (you shouldnt do that, and the program will throw an exception )... One way to get around this can be with a synchronized block on the array, it will ensure that the synchronized blocks wont be executed at the same time, the thread blocks until one synchronized block finishes, for example
-(void)populate
{
while (...)
{
id element = ...; // time consuming
// modify class variable "_array" from operation's thread (?)
#synchronized(_array)
{
[_array addObject:element];
} // Ok, I can do instead of addObject
// performSelectorOnMainThread:withObject:waitUntilDone:
// but is it the only way? Is it needed?
}
}
// access and/or modify class variable "_array"
-(void)processing
{
#synchronized(_array)
{
NSLog(#"array.count = %d", array.count);
for (id i in _array)
{
//you shouldnt modify the _array here you will get an exception
// etc...
}
}
}

How to get/set a global variable (BOOL) from within a background-thread? (Xcode)

From my main thread, I launch an image loader method method-A (below). The problem is, if method-A is not finished at the time a new method-A call is made, image loading starts from the beginning.
What I want to do is, nullify any new method-A calls that are made while a previous method-A call is still doing work... The way I (attempt to) do it now is having a simple global BOOL variable (BOOL imageLoaderBusy) and using it to keep track if the method-A is still working or not (as shown below).
The problem is, the variable seems to be ignored sometimes, and new method-A calls are undesirably started...I dunno. Maybe there is a special way you need to create global variables to make them accessible / valid across multiple threads?
Can somebody please tell me what I am doing wrong? Thanks.
//Method-A called like this:
[self performSelectorInBackground:#selector(loadPagesWithGraphics:) withObject:nil];
//Method-A
-(IBAction)loadPagesWithGraphics:(id)sender{
NSAutoreleasePool *arPool = [[NSAutoreleasePool alloc] init];
if(!imageLoaderBusy){
imageLoaderBusy = YES;
// Load Images
}
imageLoaderBusy = NO;
[arPool release];
}
Thanks in advance.
Regardless of a variable being an instance variable or a global variable, if multiple threads may write to that variable concurrently, you need to lock that section of code. For instance,
-(IBAction)loadPagesWithGraphics:(id)sender{
#synchronized(self) {
if (imageLoaderBusy) return;
imageLoaderBusy = YES;
}
NSAutoreleasePool *arPool = [[NSAutoreleasePool alloc] init];
// Load Images
imageLoaderBusy = NO;
[arPool release];
}
Let’s say two executions of that method happen simultaneously in threads A and B, and A gets the lock first, so thread B waits for the lock to be released. From A’s perspective, imageLoaderBusy == NO so it doesn’t return, sets imageLoaderBusy = YES, and releases the lock.
Since the lock has been released, thread B can start executing. It checks imageLoaderBusy and, since thread A has set it to YES, the method returns immediately in thread B.
Thread A proceeds to load the images and sets imageLoaderBusy to NO.
Note that this means that if the method is called again in some thread it will be executed and load the images again. I’m not sure if that’s your intended behaviour; if it’s not, you’ll need another check to determine if images have already been loaded. For instance,
-(IBAction)loadPagesWithGraphics:(id)sender{
if (imagesHaveBeenLoaded) return;
#synchronized(self) {
if (imageLoaderBusy) return;
imageLoaderBusy = YES;
}
NSAutoreleasePool *arPool = [[NSAutoreleasePool alloc] init];
// Load Images
[arPool release];
imageLoaderBusy = NO; // not strictly necessary
imagesHaveBeenLoaded = YES;
}
You don’t need to have all the method inside a #synchronize block. In fact, critical sections should usually be kept small, especially if the lock is being applied to the whole object (self). If the entire method were a critical section, thread B would have to wait until all images are loaded before noticing that another thread was already busy/had already loaded the images.
Try to change this way:
-(IBAction)loadPagesWithGraphics:(id)sender{
if( imagesDidLoad ) return;
#synchronized(self) {
NSAutoreleasePool *arPool = [[NSAutoreleasePool alloc] init];
// Load Images
[arPool release];
//set global ivar
imagesDidLoad = YES;
}
}
and in Method-A
add
-(void) methodA {
if( !imagesDidLoad )
[self performSelectorInBackground:#selector(loadPagesWithGraphics:) withObject:nil];
}
in Method-a call a setter on you're main thread to set that BOOL.
The method to do that is : - (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait

Cocoa thread synchronisation when using [ALAssetsLibrary enumerateGroupsWithTypes:]

I have recently, like a few people, discovered that [ALAssetsLibrary enumerateGroupsWithTypes] likes to run its blocks on another thread. What a shame that Apple didn't document that :-)
In my current circumstance I need to wait for the enumeration to complete, before the main thread returns any results. I clearly need some sort of thread synchronisation.
I've read about NSLock & NSConditionLock, but nothing yet seems to fit the requirement of 'signal a blocked thread that this worker thread has completed'. It seems like a simple enough need - can anyone point me in the right direction?
Your clue & boos, are most welcome as always,
M.
The framework doesn't run these blocks on a separate thread. It just runs them as additional events in the same run-loop. To prove it, try this
[library enumerateGroupsWithTypes:ALAssetsGroupAll
usingBlock:[^(ALAssetsGroup * group, BOOL * stop)
{
if([NSThread isMainThread])
{
NSLog(#"main");
}
else
{
NSLog(#"non-main");
}
} copy]
failureBlock:^(NSError * err)
{NSLog(#"Erorr: %#", [err localizedDescription] );}];
[library release];
if([NSThread isMainThread])
{
NSLog(#"main");
}
else
{
NSLog(#"non-main");
}
My output from this was
main
main
main
Meaning that the block was being called in the main thread. It's just a separate event.
To solve your problem, you just need to return your value somehow from within the block when you reach the last step. You can tell it's the last step because your block will be called with nil for the group object.
EDIT: for instance use this block
^(ALAssetsGroup * group, BOOL * stop)
{
if(group == nil)
{
// we've enumerated all the groups
// do something to return a value somehow (maybe send a selector to a delegate)
}
}
The answer is to use the NSConditionLock class thusly ...
typedef enum {
completed = 0,
running = 1
} threadState;
...
NSConditionLock *lock = [[NSConditionLock alloc] initWithCondition:running];
Then spin off your thread, or in my case a call to [ALAssetsLibrary enumerateGroupsWithTypes:]. Then block the parent thread with this ...
// Await completion of the worker threads
[lock lockWhenCondition:completed];
[lock unlockWithCondition:completed];
When all work is done in the child/worker thread, unblock the parent with this ...
// Signal the waiting thread
[lock lockWhenCondition:running];
[lock unlockWithCondition:completed];
Simply use this:
[library enumerateGroupsWithTypes:ALAssetsGroupAll
usingBlock:[^(ALAssetsGroup * group, BOOL * stop)
{
if(group == nil)
{
// this is end of enumeration
}
}
.
.
.